1// Copyright 2013 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 "content/browser/android/in_process/synchronous_compositor_output_surface.h"
6
7#include "base/auto_reset.h"
8#include "base/logging.h"
9#include "cc/output/begin_frame_args.h"
10#include "cc/output/compositor_frame.h"
11#include "cc/output/context_provider.h"
12#include "cc/output/output_surface_client.h"
13#include "cc/output/software_output_device.h"
14#include "content/browser/android/in_process/synchronous_compositor_impl.h"
15#include "content/common/gpu/client/webgraphicscontext3d_command_buffer_impl.h"
16#include "content/public/browser/browser_thread.h"
17#include "gpu/command_buffer/client/gl_in_process_context.h"
18#include "third_party/skia/include/core/SkCanvas.h"
19#include "third_party/skia/include/core/SkDevice.h"
20#include "ui/gfx/rect_conversions.h"
21#include "ui/gfx/skia_util.h"
22#include "ui/gfx/transform.h"
23#include "ui/gl/gl_surface.h"
24#include "webkit/common/gpu/webgraphicscontext3d_in_process_command_buffer_impl.h"
25
26
27namespace content {
28
29namespace {
30
31scoped_ptr<WebKit::WebGraphicsContext3D> CreateWebGraphicsContext3D(
32    scoped_refptr<gfx::GLSurface> surface) {
33  using webkit::gpu::WebGraphicsContext3DInProcessCommandBufferImpl;
34  if (!gfx::GLSurface::InitializeOneOff())
35    return scoped_ptr<WebKit::WebGraphicsContext3D>();
36
37  const char* allowed_extensions = "*";
38  const gfx::GpuPreference gpu_preference = gfx::PreferDiscreteGpu;
39
40  WebKit::WebGraphicsContext3D::Attributes attributes;
41  attributes.antialias = false;
42  attributes.shareResources = true;
43  attributes.noAutomaticFlushes = true;
44
45  gpu::GLInProcessContextAttribs in_process_attribs;
46  WebGraphicsContext3DInProcessCommandBufferImpl::ConvertAttributes(
47      attributes, &in_process_attribs);
48  scoped_ptr<gpu::GLInProcessContext> context(
49      gpu::GLInProcessContext::CreateWithSurface(surface,
50                                                 attributes.shareResources,
51                                                 allowed_extensions,
52                                                 in_process_attribs,
53                                                 gpu_preference));
54
55  if (!context.get())
56    return scoped_ptr<WebKit::WebGraphicsContext3D>();
57
58  return scoped_ptr<WebKit::WebGraphicsContext3D>(
59      WebGraphicsContext3DInProcessCommandBufferImpl::WrapContext(
60          context.Pass(), attributes));
61}
62
63void DidActivatePendingTree(int routing_id) {
64  SynchronousCompositorOutputSurfaceDelegate* delegate =
65      SynchronousCompositorImpl::FromRoutingID(routing_id);
66  if (delegate)
67    delegate->DidActivatePendingTree();
68}
69
70} // namespace
71
72class SynchronousCompositorOutputSurface::SoftwareDevice
73  : public cc::SoftwareOutputDevice {
74 public:
75  SoftwareDevice(SynchronousCompositorOutputSurface* surface)
76    : surface_(surface),
77      null_device_(SkBitmap::kARGB_8888_Config, 1, 1),
78      null_canvas_(&null_device_) {
79  }
80  virtual void Resize(gfx::Size size) OVERRIDE {
81    // Intentional no-op: canvas size is controlled by the embedder.
82  }
83  virtual SkCanvas* BeginPaint(gfx::Rect damage_rect) OVERRIDE {
84    if (!surface_->current_sw_canvas_) {
85      NOTREACHED() << "BeginPaint with no canvas set";
86      return &null_canvas_;
87    }
88    LOG_IF(WARNING, surface_->did_swap_buffer_)
89        << "Mutliple calls to BeginPaint per frame";
90    return surface_->current_sw_canvas_;
91  }
92  virtual void EndPaint(cc::SoftwareFrameData* frame_data) OVERRIDE {
93  }
94  virtual void CopyToBitmap(gfx::Rect rect, SkBitmap* output) OVERRIDE {
95    NOTIMPLEMENTED();
96  }
97
98 private:
99  SynchronousCompositorOutputSurface* surface_;
100  SkDevice null_device_;
101  SkCanvas null_canvas_;
102
103  DISALLOW_COPY_AND_ASSIGN(SoftwareDevice);
104};
105
106SynchronousCompositorOutputSurface::SynchronousCompositorOutputSurface(
107    int routing_id)
108    : cc::OutputSurface(
109          scoped_ptr<cc::SoftwareOutputDevice>(new SoftwareDevice(this))),
110      routing_id_(routing_id),
111      needs_begin_frame_(false),
112      invoking_composite_(false),
113      did_swap_buffer_(false),
114      current_sw_canvas_(NULL),
115      memory_policy_(0),
116      output_surface_client_(NULL) {
117  capabilities_.deferred_gl_initialization = true;
118  capabilities_.draw_and_swap_full_viewport_every_frame = true;
119  capabilities_.adjust_deadline_for_parent = false;
120  // Cannot call out to GetDelegate() here as the output surface is not
121  // constructed on the correct thread.
122
123  memory_policy_.priority_cutoff_when_visible =
124      cc::ManagedMemoryPolicy::CUTOFF_ALLOW_NICE_TO_HAVE;
125}
126
127SynchronousCompositorOutputSurface::~SynchronousCompositorOutputSurface() {
128  DCHECK(CalledOnValidThread());
129  SynchronousCompositorOutputSurfaceDelegate* delegate = GetDelegate();
130  if (delegate)
131    delegate->DidDestroySynchronousOutputSurface(this);
132}
133
134bool SynchronousCompositorOutputSurface::ForcedDrawToSoftwareDevice() const {
135  // |current_sw_canvas_| indicates we're in a DemandDrawSw call. In addition
136  // |invoking_composite_| == false indicates an attempt to draw outside of
137  // the synchronous compositor's control: force it into SW path and hence to
138  // the null canvas (and will log a warning there).
139  return current_sw_canvas_ != NULL || !invoking_composite_;
140}
141
142bool SynchronousCompositorOutputSurface::BindToClient(
143    cc::OutputSurfaceClient* surface_client) {
144  DCHECK(CalledOnValidThread());
145  if (!cc::OutputSurface::BindToClient(surface_client))
146    return false;
147
148  output_surface_client_ = surface_client;
149  output_surface_client_->SetTreeActivationCallback(
150      base::Bind(&DidActivatePendingTree, routing_id_));
151  output_surface_client_->SetMemoryPolicy(memory_policy_);
152
153  SynchronousCompositorOutputSurfaceDelegate* delegate = GetDelegate();
154  if (delegate)
155    delegate->DidBindOutputSurface(this);
156
157  return true;
158}
159
160void SynchronousCompositorOutputSurface::Reshape(
161    gfx::Size size, float scale_factor) {
162  // Intentional no-op: surface size is controlled by the embedder.
163}
164
165void SynchronousCompositorOutputSurface::SetNeedsBeginFrame(
166    bool enable) {
167  DCHECK(CalledOnValidThread());
168  cc::OutputSurface::SetNeedsBeginFrame(enable);
169  needs_begin_frame_ = enable;
170  SynchronousCompositorOutputSurfaceDelegate* delegate = GetDelegate();
171  if (delegate)
172    delegate->SetContinuousInvalidate(needs_begin_frame_);
173}
174
175void SynchronousCompositorOutputSurface::SwapBuffers(
176    cc::CompositorFrame* frame) {
177  DCHECK(CalledOnValidThread());
178  if (!ForcedDrawToSoftwareDevice()) {
179    DCHECK(context3d());
180    context3d()->shallowFlushCHROMIUM();
181  }
182  SynchronousCompositorOutputSurfaceDelegate* delegate = GetDelegate();
183  if (delegate)
184    delegate->UpdateFrameMetaData(frame->metadata);
185
186  did_swap_buffer_ = true;
187  DidSwapBuffers();
188}
189
190namespace {
191void AdjustTransform(gfx::Transform* transform,
192                     gfx::Rect viewport,
193                     gfx::Rect clip) {
194  // CC's draw origin starts at the viewport.
195  transform->matrix().postTranslate(-viewport.x(), -viewport.y(), 0);
196}
197} // namespace
198
199bool SynchronousCompositorOutputSurface::InitializeHwDraw(
200    scoped_refptr<gfx::GLSurface> surface,
201    scoped_refptr<cc::ContextProvider> offscreen_context) {
202  DCHECK(CalledOnValidThread());
203  DCHECK(HasClient());
204  DCHECK(!context3d_);
205  DCHECK(surface);
206
207  return InitializeAndSetContext3D(
208      CreateWebGraphicsContext3D(surface).Pass(), offscreen_context);
209}
210
211void SynchronousCompositorOutputSurface::ReleaseHwDraw() {
212  DCHECK(CalledOnValidThread());
213  cc::OutputSurface::ReleaseGL();
214}
215
216bool SynchronousCompositorOutputSurface::DemandDrawHw(
217    gfx::Size surface_size,
218    const gfx::Transform& transform,
219    gfx::Rect viewport,
220    gfx::Rect clip,
221    bool stencil_enabled) {
222  DCHECK(CalledOnValidThread());
223  DCHECK(HasClient());
224  DCHECK(context3d());
225
226  surface_size_ = surface_size;
227  SetExternalStencilTest(stencil_enabled);
228  InvokeComposite(transform, viewport, clip, true);
229
230  return did_swap_buffer_;
231}
232
233bool SynchronousCompositorOutputSurface::DemandDrawSw(SkCanvas* canvas) {
234  DCHECK(CalledOnValidThread());
235  DCHECK(canvas);
236  DCHECK(!current_sw_canvas_);
237  base::AutoReset<SkCanvas*> canvas_resetter(&current_sw_canvas_, canvas);
238
239  SkIRect canvas_clip;
240  canvas->getClipDeviceBounds(&canvas_clip);
241  gfx::Rect clip = gfx::SkIRectToRect(canvas_clip);
242
243  gfx::Transform transform(gfx::Transform::kSkipInitialization);
244  transform.matrix() = canvas->getTotalMatrix();  // Converts 3x3 matrix to 4x4.
245
246  surface_size_ = gfx::Size(canvas->getDeviceSize().width(),
247                            canvas->getDeviceSize().height());
248  SetExternalStencilTest(false);
249
250  InvokeComposite(transform, clip, clip, false);
251
252  return did_swap_buffer_;
253}
254
255void SynchronousCompositorOutputSurface::InvokeComposite(
256    const gfx::Transform& transform,
257    gfx::Rect viewport,
258    gfx::Rect clip,
259    bool hardware) {
260  DCHECK(!invoking_composite_);
261  base::AutoReset<bool> invoking_composite_resetter(&invoking_composite_, true);
262  did_swap_buffer_ = false;
263
264  gfx::Transform adjusted_transform = transform;
265  AdjustTransform(&adjusted_transform, viewport, clip);
266  SetExternalDrawConstraints(adjusted_transform, viewport, clip, hardware);
267  SetNeedsRedrawRect(gfx::Rect(viewport.size()));
268
269  if (needs_begin_frame_)
270    BeginFrame(cc::BeginFrameArgs::CreateForSynchronousCompositor());
271
272  if (hardware) {
273    cached_hw_transform_ = adjusted_transform;
274    cached_hw_viewport_ = viewport;
275    cached_hw_clip_ = clip;
276  } else {
277    SetExternalDrawConstraints(
278        cached_hw_transform_, cached_hw_viewport_, cached_hw_clip_, true);
279  }
280
281  if (did_swap_buffer_)
282    OnSwapBuffersComplete(NULL);
283}
284
285void SynchronousCompositorOutputSurface::PostCheckForRetroactiveBeginFrame() {
286  // Synchronous compositor cannot perform retroactive begin frames, so
287  // intentionally no-op here.
288}
289
290void SynchronousCompositorOutputSurface::SetMemoryPolicy(
291    const SynchronousCompositorMemoryPolicy& policy) {
292  DCHECK(CalledOnValidThread());
293  memory_policy_.bytes_limit_when_visible = policy.bytes_limit;
294  memory_policy_.num_resources_limit = policy.num_resources_limit;
295
296  if (output_surface_client_)
297    output_surface_client_->SetMemoryPolicy(memory_policy_);
298}
299
300// Not using base::NonThreadSafe as we want to enforce a more exacting threading
301// requirement: SynchronousCompositorOutputSurface() must only be used on the UI
302// thread.
303bool SynchronousCompositorOutputSurface::CalledOnValidThread() const {
304  return BrowserThread::CurrentlyOn(BrowserThread::UI);
305}
306
307SynchronousCompositorOutputSurfaceDelegate*
308SynchronousCompositorOutputSurface::GetDelegate() {
309  return SynchronousCompositorImpl::FromRoutingID(routing_id_);
310}
311
312}  // namespace content
313