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.
4
5#include "android_webview/browser/hardware_renderer.h"
6
7#include "android_webview/browser/aw_gl_surface.h"
8#include "android_webview/browser/deferred_gpu_command_service.h"
9#include "android_webview/browser/parent_output_surface.h"
10#include "android_webview/browser/shared_renderer_state.h"
11#include "android_webview/public/browser/draw_gl.h"
12#include "base/auto_reset.h"
13#include "base/debug/trace_event.h"
14#include "base/strings/string_number_conversions.h"
15#include "cc/layers/delegated_frame_provider.h"
16#include "cc/layers/delegated_renderer_layer.h"
17#include "cc/layers/layer.h"
18#include "cc/output/compositor_frame.h"
19#include "cc/output/output_surface.h"
20#include "cc/trees/layer_tree_host.h"
21#include "cc/trees/layer_tree_settings.h"
22#include "gpu/command_buffer/client/gl_in_process_context.h"
23#include "ui/gfx/frame_time.h"
24#include "ui/gfx/geometry/rect_conversions.h"
25#include "ui/gfx/geometry/rect_f.h"
26#include "ui/gfx/transform.h"
27#include "ui/gl/gl_bindings.h"
28#include "webkit/common/gpu/context_provider_in_process.h"
29#include "webkit/common/gpu/webgraphicscontext3d_in_process_command_buffer_impl.h"
30
31namespace android_webview {
32
33namespace {
34
35using webkit::gpu::WebGraphicsContext3DInProcessCommandBufferImpl;
36
37scoped_refptr<cc::ContextProvider> CreateContext(
38    scoped_refptr<gfx::GLSurface> surface,
39    scoped_refptr<gpu::InProcessCommandBuffer::Service> service,
40    gpu::GLInProcessContext* share_context) {
41  const gfx::GpuPreference gpu_preference = gfx::PreferDiscreteGpu;
42
43  blink::WebGraphicsContext3D::Attributes attributes;
44  attributes.antialias = false;
45  attributes.depth = false;
46  attributes.stencil = false;
47  attributes.shareResources = true;
48  attributes.noAutomaticFlushes = true;
49  gpu::GLInProcessContextAttribs in_process_attribs;
50  WebGraphicsContext3DInProcessCommandBufferImpl::ConvertAttributes(
51      attributes, &in_process_attribs);
52  in_process_attribs.lose_context_when_out_of_memory = 1;
53
54  scoped_ptr<gpu::GLInProcessContext> context(
55      gpu::GLInProcessContext::Create(service,
56                                      surface,
57                                      surface->IsOffscreen(),
58                                      gfx::kNullAcceleratedWidget,
59                                      surface->GetSize(),
60                                      share_context,
61                                      false /* share_resources */,
62                                      in_process_attribs,
63                                      gpu_preference));
64  DCHECK(context.get());
65
66  return webkit::gpu::ContextProviderInProcess::Create(
67      WebGraphicsContext3DInProcessCommandBufferImpl::WrapContext(
68          context.Pass(), attributes),
69      "Parent-Compositor");
70}
71
72}  // namespace
73
74HardwareRenderer::HardwareRenderer(SharedRendererState* state)
75    : shared_renderer_state_(state),
76      last_egl_context_(eglGetCurrentContext()),
77      stencil_enabled_(false),
78      viewport_clip_valid_for_dcheck_(false),
79      gl_surface_(new AwGLSurface),
80      root_layer_(cc::Layer::Create()),
81      resource_collection_(new cc::DelegatedFrameResourceCollection),
82      output_surface_(NULL) {
83  DCHECK(last_egl_context_);
84
85  resource_collection_->SetClient(this);
86
87  cc::LayerTreeSettings settings;
88
89  // Should be kept in sync with compositor_impl_android.cc.
90  settings.allow_antialiasing = false;
91  settings.highp_threshold_min = 2048;
92
93  // Webview does not own the surface so should not clear it.
94  settings.should_clear_root_render_pass = false;
95
96  layer_tree_host_ =
97      cc::LayerTreeHost::CreateSingleThreaded(this, this, NULL, settings);
98  layer_tree_host_->SetRootLayer(root_layer_);
99  layer_tree_host_->SetLayerTreeHostClientReady();
100  layer_tree_host_->set_has_transparent_background(true);
101}
102
103HardwareRenderer::~HardwareRenderer() {
104  // Must reset everything before |resource_collection_| to ensure all
105  // resources are returned before resetting |resource_collection_| client.
106  layer_tree_host_.reset();
107  root_layer_ = NULL;
108  delegated_layer_ = NULL;
109  frame_provider_ = NULL;
110#if DCHECK_IS_ON
111  // Check collection is empty.
112  cc::ReturnedResourceArray returned_resources;
113  resource_collection_->TakeUnusedResourcesForChildCompositor(
114      &returned_resources);
115  DCHECK_EQ(0u, returned_resources.size());
116#endif  // DCHECK_IS_ON
117
118  resource_collection_->SetClient(NULL);
119
120  // Reset draw constraints.
121  shared_renderer_state_->UpdateDrawConstraints(
122      ParentCompositorDrawConstraints());
123}
124
125void HardwareRenderer::DidBeginMainFrame() {
126  // This is called after OutputSurface is created, but before the impl frame
127  // starts. We set the draw constraints here.
128  DCHECK(output_surface_);
129  DCHECK(viewport_clip_valid_for_dcheck_);
130  output_surface_->SetExternalStencilTest(stencil_enabled_);
131  output_surface_->SetDrawConstraints(viewport_, clip_);
132}
133
134void HardwareRenderer::CommitFrame() {
135  scoped_ptr<DrawGLInput> input = shared_renderer_state_->PassDrawGLInput();
136  // Happens with empty global visible rect.
137  if (!input.get())
138    return;
139
140  DCHECK(!input->frame.gl_frame_data);
141  DCHECK(!input->frame.software_frame_data);
142
143  // DelegatedRendererLayerImpl applies the inverse device_scale_factor of the
144  // renderer frame, assuming that the browser compositor will scale
145  // it back up to device scale.  But on Android we put our browser layers in
146  // physical pixels and set our browser CC device_scale_factor to 1, so this
147  // suppresses the transform.
148  input->frame.delegated_frame_data->device_scale_factor = 1.0f;
149
150  gfx::Size frame_size =
151      input->frame.delegated_frame_data->render_pass_list.back()
152          ->output_rect.size();
153  bool size_changed = frame_size != frame_size_;
154  frame_size_ = frame_size;
155  scroll_offset_ = input->scroll_offset;
156
157  if (!frame_provider_ || size_changed) {
158    if (delegated_layer_) {
159      delegated_layer_->RemoveFromParent();
160    }
161
162    frame_provider_ = new cc::DelegatedFrameProvider(
163        resource_collection_.get(), input->frame.delegated_frame_data.Pass());
164
165    delegated_layer_ = cc::DelegatedRendererLayer::Create(frame_provider_);
166    delegated_layer_->SetBounds(gfx::Size(input->width, input->height));
167    delegated_layer_->SetIsDrawable(true);
168
169    root_layer_->AddChild(delegated_layer_);
170  } else {
171    frame_provider_->SetFrameData(input->frame.delegated_frame_data.Pass());
172  }
173}
174
175void HardwareRenderer::DrawGL(bool stencil_enabled,
176                              int framebuffer_binding_ext,
177                              AwDrawGLInfo* draw_info) {
178  TRACE_EVENT0("android_webview", "HardwareRenderer::DrawGL");
179
180  // We need to watch if the current Android context has changed and enforce
181  // a clean-up in the compositor.
182  EGLContext current_context = eglGetCurrentContext();
183  if (!current_context) {
184    DLOG(ERROR) << "DrawGL called without EGLContext";
185    return;
186  }
187
188  // TODO(boliu): Handle context loss.
189  if (last_egl_context_ != current_context)
190    DLOG(WARNING) << "EGLContextChanged";
191
192  gfx::Transform transform(gfx::Transform::kSkipInitialization);
193  transform.matrix().setColMajorf(draw_info->transform);
194  transform.Translate(scroll_offset_.x(), scroll_offset_.y());
195
196  // Need to post the new transform matrix back to child compositor
197  // because there is no onDraw during a Render Thread animation, and child
198  // compositor might not have the tiles rasterized as the animation goes on.
199  ParentCompositorDrawConstraints draw_constraints(
200      draw_info->is_layer, transform, gfx::Rect(viewport_));
201
202  draw_constraints_ = draw_constraints;
203  shared_renderer_state_->PostExternalDrawConstraintsToChildCompositor(
204      draw_constraints);
205
206  if (!delegated_layer_.get())
207    return;
208
209  viewport_.SetSize(draw_info->width, draw_info->height);
210  layer_tree_host_->SetViewportSize(viewport_);
211  clip_.SetRect(draw_info->clip_left,
212                draw_info->clip_top,
213                draw_info->clip_right - draw_info->clip_left,
214                draw_info->clip_bottom - draw_info->clip_top);
215  stencil_enabled_ = stencil_enabled;
216
217  delegated_layer_->SetTransform(transform);
218
219  gl_surface_->SetBackingFrameBufferObject(framebuffer_binding_ext);
220  {
221    base::AutoReset<bool> frame_resetter(&viewport_clip_valid_for_dcheck_,
222                                         true);
223    layer_tree_host_->SetNeedsRedrawRect(clip_);
224    layer_tree_host_->Composite(gfx::FrameTime::Now());
225  }
226  gl_surface_->ResetBackingFrameBufferObject();
227}
228
229scoped_ptr<cc::OutputSurface> HardwareRenderer::CreateOutputSurface(
230    bool fallback) {
231  // Android webview does not support losing output surface.
232  DCHECK(!fallback);
233  scoped_refptr<cc::ContextProvider> context_provider =
234      CreateContext(gl_surface_,
235                    DeferredGpuCommandService::GetInstance(),
236                    shared_renderer_state_->GetSharedContext());
237  scoped_ptr<ParentOutputSurface> output_surface_holder(
238      new ParentOutputSurface(context_provider));
239  output_surface_ = output_surface_holder.get();
240  return output_surface_holder.PassAs<cc::OutputSurface>();
241}
242
243void HardwareRenderer::UnusedResourcesAreAvailable() {
244  cc::ReturnedResourceArray returned_resources;
245  resource_collection_->TakeUnusedResourcesForChildCompositor(
246      &returned_resources);
247  shared_renderer_state_->InsertReturnedResources(returned_resources);
248}
249
250}  // namespace android_webview
251