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