synchronous_compositor_output_surface.cc revision 4e180b6a0b4720a9b8e9e959a882386f690f08ff
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  memory_policy_.priority_cutoff_when_visible =
120      cc::ManagedMemoryPolicy::CUTOFF_ALLOW_NICE_TO_HAVE;
121}
122
123SynchronousCompositorOutputSurface::~SynchronousCompositorOutputSurface() {
124  DCHECK(CalledOnValidThread());
125  SynchronousCompositorOutputSurfaceDelegate* delegate = GetDelegate();
126  if (delegate)
127    delegate->DidDestroySynchronousOutputSurface(this);
128}
129
130bool SynchronousCompositorOutputSurface::ForcedDrawToSoftwareDevice() const {
131  // |current_sw_canvas_| indicates we're in a DemandDrawSw call. In addition
132  // |invoking_composite_| == false indicates an attempt to draw outside of
133  // the synchronous compositor's control: force it into SW path and hence to
134  // the null canvas (and will log a warning there).
135  return current_sw_canvas_ != NULL || !invoking_composite_;
136}
137
138bool SynchronousCompositorOutputSurface::BindToClient(
139    cc::OutputSurfaceClient* surface_client) {
140  DCHECK(CalledOnValidThread());
141  if (!cc::OutputSurface::BindToClient(surface_client))
142    return false;
143
144  output_surface_client_ = surface_client;
145  output_surface_client_->SetTreeActivationCallback(
146      base::Bind(&DidActivatePendingTree, routing_id_));
147  output_surface_client_->SetMemoryPolicy(memory_policy_);
148
149  SynchronousCompositorOutputSurfaceDelegate* delegate = GetDelegate();
150  if (delegate)
151    delegate->DidBindOutputSurface(this);
152
153  return true;
154}
155
156void SynchronousCompositorOutputSurface::Reshape(
157    gfx::Size size, float scale_factor) {
158  // Intentional no-op: surface size is controlled by the embedder.
159}
160
161void SynchronousCompositorOutputSurface::SetNeedsBeginFrame(
162    bool enable) {
163  DCHECK(CalledOnValidThread());
164  cc::OutputSurface::SetNeedsBeginFrame(enable);
165  needs_begin_frame_ = enable;
166  SynchronousCompositorOutputSurfaceDelegate* delegate = GetDelegate();
167  if (delegate)
168    delegate->SetContinuousInvalidate(needs_begin_frame_);
169}
170
171void SynchronousCompositorOutputSurface::SwapBuffers(
172    cc::CompositorFrame* frame) {
173  DCHECK(CalledOnValidThread());
174  if (!ForcedDrawToSoftwareDevice()) {
175    DCHECK(context_provider_);
176    context_provider_->Context3d()->shallowFlushCHROMIUM();
177  }
178  SynchronousCompositorOutputSurfaceDelegate* delegate = GetDelegate();
179  if (delegate)
180    delegate->UpdateFrameMetaData(frame->metadata);
181
182  did_swap_buffer_ = true;
183  DidSwapBuffers();
184}
185
186namespace {
187void AdjustTransform(gfx::Transform* transform, gfx::Rect viewport) {
188  // CC's draw origin starts at the viewport.
189  transform->matrix().postTranslate(-viewport.x(), -viewport.y(), 0);
190}
191} // namespace
192
193bool SynchronousCompositorOutputSurface::InitializeHwDraw(
194    scoped_refptr<gfx::GLSurface> surface,
195    scoped_refptr<cc::ContextProvider> offscreen_context_provider) {
196  DCHECK(CalledOnValidThread());
197  DCHECK(HasClient());
198  DCHECK(!context_provider_);
199  DCHECK(surface);
200
201  scoped_refptr<cc::ContextProvider> onscreen_context_provider =
202      webkit::gpu::ContextProviderInProcess::Create(
203          CreateWebGraphicsContext3D(surface), "SynchronousCompositor");
204  return InitializeAndSetContext3d(onscreen_context_provider,
205                                   offscreen_context_provider);
206}
207
208void SynchronousCompositorOutputSurface::ReleaseHwDraw() {
209  DCHECK(CalledOnValidThread());
210  cc::OutputSurface::ReleaseGL();
211}
212
213bool SynchronousCompositorOutputSurface::DemandDrawHw(
214    gfx::Size surface_size,
215    const gfx::Transform& transform,
216    gfx::Rect viewport,
217    gfx::Rect clip,
218    bool stencil_enabled) {
219  DCHECK(CalledOnValidThread());
220  DCHECK(HasClient());
221  DCHECK(context_provider_);
222
223  surface_size_ = surface_size;
224  SetExternalStencilTest(stencil_enabled);
225  InvokeComposite(transform, viewport, clip, true);
226
227  return did_swap_buffer_;
228}
229
230bool SynchronousCompositorOutputSurface::DemandDrawSw(SkCanvas* canvas) {
231  DCHECK(CalledOnValidThread());
232  DCHECK(canvas);
233  DCHECK(!current_sw_canvas_);
234  base::AutoReset<SkCanvas*> canvas_resetter(&current_sw_canvas_, canvas);
235
236  SkIRect canvas_clip;
237  canvas->getClipDeviceBounds(&canvas_clip);
238  gfx::Rect clip = gfx::SkIRectToRect(canvas_clip);
239
240  gfx::Transform transform(gfx::Transform::kSkipInitialization);
241  transform.matrix() = canvas->getTotalMatrix();  // Converts 3x3 matrix to 4x4.
242
243  surface_size_ = gfx::Size(canvas->getDeviceSize().width(),
244                            canvas->getDeviceSize().height());
245  SetExternalStencilTest(false);
246
247  InvokeComposite(transform, clip, clip, false);
248
249  return did_swap_buffer_;
250}
251
252void SynchronousCompositorOutputSurface::InvokeComposite(
253    const gfx::Transform& transform,
254    gfx::Rect viewport,
255    gfx::Rect clip,
256    bool valid_for_tile_management) {
257  DCHECK(!invoking_composite_);
258  base::AutoReset<bool> invoking_composite_resetter(&invoking_composite_, true);
259  did_swap_buffer_ = false;
260
261  gfx::Transform adjusted_transform = transform;
262  AdjustTransform(&adjusted_transform, viewport);
263  SetExternalDrawConstraints(
264      adjusted_transform, viewport, clip, valid_for_tile_management);
265  SetNeedsRedrawRect(gfx::Rect(viewport.size()));
266
267  if (needs_begin_frame_)
268    BeginFrame(cc::BeginFrameArgs::CreateForSynchronousCompositor());
269
270  // After software draws (which might move the viewport arbitrarily), restore
271  // the previous hardware viewport to allow CC's tile manager to prioritize
272  // properly.
273  if (valid_for_tile_management) {
274    cached_hw_transform_ = adjusted_transform;
275    cached_hw_viewport_ = viewport;
276    cached_hw_clip_ = clip;
277  } else {
278    SetExternalDrawConstraints(
279        cached_hw_transform_, cached_hw_viewport_, cached_hw_clip_, true);
280  }
281
282  if (did_swap_buffer_)
283    OnSwapBuffersComplete();
284}
285
286void SynchronousCompositorOutputSurface::PostCheckForRetroactiveBeginFrame() {
287  // Synchronous compositor cannot perform retroactive begin frames, so
288  // intentionally no-op here.
289}
290
291void SynchronousCompositorOutputSurface::SetMemoryPolicy(
292    const SynchronousCompositorMemoryPolicy& policy) {
293  DCHECK(CalledOnValidThread());
294  memory_policy_.bytes_limit_when_visible = policy.bytes_limit;
295  memory_policy_.num_resources_limit = policy.num_resources_limit;
296
297  if (output_surface_client_)
298    output_surface_client_->SetMemoryPolicy(memory_policy_);
299}
300
301// Not using base::NonThreadSafe as we want to enforce a more exacting threading
302// requirement: SynchronousCompositorOutputSurface() must only be used on the UI
303// thread.
304bool SynchronousCompositorOutputSurface::CalledOnValidThread() const {
305  return BrowserThread::CurrentlyOn(BrowserThread::UI);
306}
307
308SynchronousCompositorOutputSurfaceDelegate*
309SynchronousCompositorOutputSurface::GetDelegate() {
310  return SynchronousCompositorImpl::FromRoutingID(routing_id_);
311}
312
313}  // namespace content
314