1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "android_webview/browser/shared_renderer_state.h"
7 #include "android_webview/browser/browser_view_renderer.h"
8 #include "android_webview/browser/child_frame.h"
9 #include "android_webview/browser/deferred_gpu_command_service.h"
10 #include "android_webview/browser/hardware_renderer.h"
11 #include "android_webview/browser/scoped_app_gl_state_restore.h"
12 #include "android_webview/public/browser/draw_gl.h"
13 #include "base/bind.h"
14 #include "base/lazy_instance.h"
15 #include "base/location.h"
16 #include "base/time/time.h"
17 #include "base/trace_event/trace_event_argument.h"
19 namespace android_webview
{
23 class RequestDrawGLTracker
{
25 RequestDrawGLTracker();
26 bool ShouldRequestOnNonUiThread(SharedRendererState
* state
);
27 bool ShouldRequestOnUiThread(SharedRendererState
* state
);
29 void SetQueuedFunctorOnUi(SharedRendererState
* state
);
33 SharedRendererState
* pending_ui_
;
34 SharedRendererState
* pending_non_ui_
;
37 RequestDrawGLTracker::RequestDrawGLTracker()
38 : pending_ui_(NULL
), pending_non_ui_(NULL
) {
41 bool RequestDrawGLTracker::ShouldRequestOnNonUiThread(
42 SharedRendererState
* state
) {
43 base::AutoLock
lock(lock_
);
44 if (pending_ui_
|| pending_non_ui_
)
46 pending_non_ui_
= state
;
50 bool RequestDrawGLTracker::ShouldRequestOnUiThread(SharedRendererState
* state
) {
51 base::AutoLock
lock(lock_
);
52 if (pending_non_ui_
) {
53 pending_non_ui_
->ResetRequestDrawGLCallback();
54 pending_non_ui_
= NULL
;
56 // At this time, we could have already called RequestDrawGL on the UI thread,
57 // but the corresponding GL mode process hasn't happened yet. In this case,
58 // don't schedule another requestDrawGL on the UI thread.
65 void RequestDrawGLTracker::ResetPending() {
66 base::AutoLock
lock(lock_
);
67 pending_non_ui_
= NULL
;
71 void RequestDrawGLTracker::SetQueuedFunctorOnUi(SharedRendererState
* state
) {
72 base::AutoLock
lock(lock_
);
75 pending_non_ui_
= NULL
;
78 } // namespace internal
82 base::LazyInstance
<internal::RequestDrawGLTracker
> g_request_draw_gl_tracker
=
83 LAZY_INSTANCE_INITIALIZER
;
87 SharedRendererState::SharedRendererState(
88 const scoped_refptr
<base::SingleThreadTaskRunner
>& ui_loop
,
89 BrowserViewRenderer
* browser_view_renderer
)
91 browser_view_renderer_(browser_view_renderer
),
92 renderer_manager_key_(GLViewRendererManager::GetInstance()->NullKey()),
93 inside_hardware_release_(false),
94 weak_factory_on_ui_thread_(this) {
95 DCHECK(ui_loop_
->BelongsToCurrentThread());
96 DCHECK(browser_view_renderer_
);
97 ui_thread_weak_ptr_
= weak_factory_on_ui_thread_
.GetWeakPtr();
98 ResetRequestDrawGLCallback();
101 SharedRendererState::~SharedRendererState() {
102 DCHECK(ui_loop_
->BelongsToCurrentThread());
103 DCHECK(!hardware_renderer_
.get());
106 void SharedRendererState::ClientRequestDrawGL(bool for_idle
) {
107 if (ui_loop_
->BelongsToCurrentThread()) {
108 if (!g_request_draw_gl_tracker
.Get().ShouldRequestOnUiThread(this))
110 ClientRequestDrawGLOnUI();
112 if (!g_request_draw_gl_tracker
.Get().ShouldRequestOnNonUiThread(this))
114 base::Closure callback
;
116 base::AutoLock
lock(lock_
);
117 callback
= request_draw_gl_closure_
;
119 // 17ms is slightly longer than a frame, hoping that it will come
120 // after the next frame so that the idle work is taken care off by
121 // the next frame instead.
122 ui_loop_
->PostDelayedTask(
124 for_idle
? base::TimeDelta::FromMilliseconds(17) : base::TimeDelta());
128 void SharedRendererState::DidDrawGLProcess() {
129 g_request_draw_gl_tracker
.Get().ResetPending();
132 void SharedRendererState::ResetRequestDrawGLCallback() {
133 DCHECK(ui_loop_
->BelongsToCurrentThread());
134 base::AutoLock
lock(lock_
);
135 request_draw_gl_cancelable_closure_
.Reset(base::Bind(
136 &SharedRendererState::ClientRequestDrawGLOnUI
, base::Unretained(this)));
137 request_draw_gl_closure_
= request_draw_gl_cancelable_closure_
.callback();
140 void SharedRendererState::ClientRequestDrawGLOnUI() {
141 DCHECK(ui_loop_
->BelongsToCurrentThread());
142 ResetRequestDrawGLCallback();
143 g_request_draw_gl_tracker
.Get().SetQueuedFunctorOnUi(this);
144 if (!browser_view_renderer_
->RequestDrawGL(false)) {
145 g_request_draw_gl_tracker
.Get().ResetPending();
146 LOG(ERROR
) << "Failed to request GL process. Deadlock likely";
150 void SharedRendererState::UpdateParentDrawConstraintsOnUI() {
151 DCHECK(ui_loop_
->BelongsToCurrentThread());
152 browser_view_renderer_
->UpdateParentDrawConstraints();
155 void SharedRendererState::SetScrollOffsetOnUI(gfx::Vector2d scroll_offset
) {
156 base::AutoLock
lock(lock_
);
157 scroll_offset_
= scroll_offset
;
160 gfx::Vector2d
SharedRendererState::GetScrollOffsetOnRT() {
161 base::AutoLock
lock(lock_
);
162 return scroll_offset_
;
165 void SharedRendererState::SetCompositorFrameOnUI(scoped_ptr
<ChildFrame
> frame
) {
166 base::AutoLock
lock(lock_
);
167 DCHECK(!child_frame_
.get());
168 child_frame_
= frame
.Pass();
171 scoped_ptr
<ChildFrame
> SharedRendererState::PassCompositorFrameOnRT() {
172 base::AutoLock
lock(lock_
);
173 return child_frame_
.Pass();
176 scoped_ptr
<ChildFrame
> SharedRendererState::PassUncommittedFrameOnUI() {
177 base::AutoLock
lock(lock_
);
178 return child_frame_
.Pass();
181 void SharedRendererState::PostExternalDrawConstraintsToChildCompositorOnRT(
182 const ParentCompositorDrawConstraints
& parent_draw_constraints
) {
184 base::AutoLock
lock(lock_
);
185 parent_draw_constraints_
= parent_draw_constraints
;
188 // No need to hold the lock_ during the post task.
191 base::Bind(&SharedRendererState::UpdateParentDrawConstraintsOnUI
,
192 ui_thread_weak_ptr_
));
195 ParentCompositorDrawConstraints
196 SharedRendererState::GetParentDrawConstraintsOnUI() const {
197 base::AutoLock
lock(lock_
);
198 return parent_draw_constraints_
;
201 void SharedRendererState::SetInsideHardwareRelease(bool inside
) {
202 base::AutoLock
lock(lock_
);
203 inside_hardware_release_
= inside
;
206 bool SharedRendererState::IsInsideHardwareRelease() const {
207 base::AutoLock
lock(lock_
);
208 return inside_hardware_release_
;
211 void SharedRendererState::InsertReturnedResourcesOnRT(
212 const cc::ReturnedResourceArray
& resources
) {
213 base::AutoLock
lock(lock_
);
214 returned_resources_
.insert(
215 returned_resources_
.end(), resources
.begin(), resources
.end());
218 void SharedRendererState::SwapReturnedResourcesOnUI(
219 cc::ReturnedResourceArray
* resources
) {
220 DCHECK(resources
->empty());
221 base::AutoLock
lock(lock_
);
222 resources
->swap(returned_resources_
);
225 bool SharedRendererState::ReturnedResourcesEmptyOnUI() const {
226 base::AutoLock
lock(lock_
);
227 return returned_resources_
.empty();
230 void SharedRendererState::DrawGL(AwDrawGLInfo
* draw_info
) {
231 TRACE_EVENT0("android_webview", "DrawFunctor");
232 if (draw_info
->mode
== AwDrawGLInfo::kModeSync
) {
233 TRACE_EVENT_INSTANT0("android_webview", "kModeSync",
234 TRACE_EVENT_SCOPE_THREAD
);
235 if (hardware_renderer_
)
236 hardware_renderer_
->CommitFrame();
240 // kModeProcessNoContext should never happen because we tear down hardware
241 // in onTrimMemory. However that guarantee is maintained outside of chromium
242 // code. Not notifying shared state in kModeProcessNoContext can lead to
243 // immediate deadlock, which is slightly more catastrophic than leaks or
245 if (draw_info
->mode
== AwDrawGLInfo::kModeProcess
||
246 draw_info
->mode
== AwDrawGLInfo::kModeProcessNoContext
) {
251 GLViewRendererManager
* manager
= GLViewRendererManager::GetInstance();
252 base::AutoLock
lock(lock_
);
253 if (renderer_manager_key_
!= manager
->NullKey()) {
254 manager
->DidDrawGL(renderer_manager_key_
);
258 ScopedAppGLStateRestore
state_restore(
259 draw_info
->mode
== AwDrawGLInfo::kModeDraw
260 ? ScopedAppGLStateRestore::MODE_DRAW
261 : ScopedAppGLStateRestore::MODE_RESOURCE_MANAGEMENT
);
262 // Set the correct FBO before kModeDraw. The GL commands run in kModeDraw
263 // require a correctly bound FBO. The FBO remains until the next kModeDraw.
264 // So kModeProcess between kModeDraws has correctly bound FBO, too.
265 if (draw_info
->mode
== AwDrawGLInfo::kModeDraw
) {
266 if (!hardware_renderer_
) {
267 hardware_renderer_
.reset(new HardwareRenderer(this));
268 hardware_renderer_
->CommitFrame();
270 hardware_renderer_
->SetBackingFrameBufferObject(
271 state_restore
.framebuffer_binding_ext());
274 ScopedAllowGL allow_gl
;
276 if (draw_info
->mode
== AwDrawGLInfo::kModeProcessNoContext
) {
277 LOG(ERROR
) << "Received unexpected kModeProcessNoContext";
280 if (IsInsideHardwareRelease()) {
281 hardware_renderer_
.reset();
282 // Flush the idle queue in tear down.
283 DeferredGpuCommandService::GetInstance()->PerformAllIdleWork();
287 if (draw_info
->mode
!= AwDrawGLInfo::kModeDraw
) {
288 if (draw_info
->mode
== AwDrawGLInfo::kModeProcess
) {
289 DeferredGpuCommandService::GetInstance()->PerformIdleWork(true);
294 hardware_renderer_
->DrawGL(state_restore
.stencil_enabled(), draw_info
);
295 DeferredGpuCommandService::GetInstance()->PerformIdleWork(false);
298 void SharedRendererState::ReleaseHardwareDrawIfNeededOnUI() {
299 ReleaseCompositorResourcesIfNeededOnUI(true);
302 void SharedRendererState::DeleteHardwareRendererOnUI() {
303 ReleaseCompositorResourcesIfNeededOnUI(false);
306 void SharedRendererState::ReleaseCompositorResourcesIfNeededOnUI(
307 bool release_hardware_draw
) {
308 DCHECK(ui_loop_
->BelongsToCurrentThread());
309 InsideHardwareReleaseReset
auto_inside_hardware_release_reset(this);
311 browser_view_renderer_
->DetachFunctorFromView();
312 bool hardware_initialized
= browser_view_renderer_
->hardware_enabled();
313 // If the WebView gets onTrimMemory >= MODERATE twice in a row, the 2nd
314 // onTrimMemory will result in an unnecessary Render Thread DrawGL call.
315 if (hardware_initialized
) {
316 bool draw_functor_succeeded
= browser_view_renderer_
->RequestDrawGL(true);
317 if (!draw_functor_succeeded
) {
318 LOG(ERROR
) << "Unable to free GL resources. Has the Window leaked?";
319 // Calling release on wrong thread intentionally.
321 info
.mode
= AwDrawGLInfo::kModeProcess
;
325 if (release_hardware_draw
)
326 browser_view_renderer_
->ReleaseHardware();
329 GLViewRendererManager
* manager
= GLViewRendererManager::GetInstance();
332 base::AutoLock
lock(lock_
);
333 if (renderer_manager_key_
!= manager
->NullKey()) {
334 manager
->Remove(renderer_manager_key_
);
335 renderer_manager_key_
= manager
->NullKey();
339 if (hardware_initialized
) {
340 // Flush any invoke functors that's caused by ReleaseHardware.
341 browser_view_renderer_
->RequestDrawGL(true);
345 void SharedRendererState::InitializeHardwareDrawIfNeededOnUI() {
346 DCHECK(ui_loop_
->BelongsToCurrentThread());
347 GLViewRendererManager
* manager
= GLViewRendererManager::GetInstance();
349 base::AutoLock
lock(lock_
);
350 if (renderer_manager_key_
== manager
->NullKey()) {
351 renderer_manager_key_
= manager
->PushBack(this);
355 SharedRendererState::InsideHardwareReleaseReset::InsideHardwareReleaseReset(
356 SharedRendererState
* shared_renderer_state
)
357 : shared_renderer_state_(shared_renderer_state
) {
358 DCHECK(!shared_renderer_state_
->IsInsideHardwareRelease());
359 shared_renderer_state_
->SetInsideHardwareRelease(true);
362 SharedRendererState::InsideHardwareReleaseReset::~InsideHardwareReleaseReset() {
363 shared_renderer_state_
->SetInsideHardwareRelease(false);
366 } // namespace android_webview