synchronous_compositor_output_surface.cc revision 7dbb3d5cf0c15f500944d211057644d6a2f37371
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 "third_party/skia/include/core/SkCanvas.h"
18#include "third_party/skia/include/core/SkDevice.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_context.h"
23#include "webkit/common/gpu/webgraphicscontext3d_in_process_command_buffer_impl.h"
24
25using webkit::gpu::WebGraphicsContext3DInProcessCommandBufferImpl;
26
27namespace content {
28
29namespace {
30
31// TODO(boliu): RenderThreadImpl should create in process contexts as well.
32scoped_ptr<WebKit::WebGraphicsContext3D> CreateWebGraphicsContext3D() {
33  WebKit::WebGraphicsContext3D::Attributes attributes;
34  attributes.antialias = false;
35  attributes.shareResources = true;
36  attributes.noAutomaticFlushes = true;
37
38  return scoped_ptr<WebKit::WebGraphicsContext3D>(
39      WebGraphicsContext3DInProcessCommandBufferImpl
40          ::CreateViewContext(attributes, NULL));
41}
42
43} // namespace
44
45class SynchronousCompositorOutputSurface::SoftwareDevice
46  : public cc::SoftwareOutputDevice {
47 public:
48  SoftwareDevice(SynchronousCompositorOutputSurface* surface)
49    : surface_(surface),
50      null_device_(SkBitmap::kARGB_8888_Config, 1, 1),
51      null_canvas_(&null_device_) {
52  }
53  virtual void Resize(gfx::Size size) OVERRIDE {
54    // Intentional no-op: canvas size is controlled by the embedder.
55  }
56  virtual SkCanvas* BeginPaint(gfx::Rect damage_rect) OVERRIDE {
57    if (!surface_->current_sw_canvas_) {
58      NOTREACHED() << "BeginPaint with no canvas set";
59      return &null_canvas_;
60    }
61    LOG_IF(WARNING, surface_->did_swap_buffer_)
62        << "Mutliple calls to BeginPaint per frame";
63    return surface_->current_sw_canvas_;
64  }
65  virtual void EndPaint(cc::SoftwareFrameData* frame_data) OVERRIDE {
66  }
67  virtual void CopyToBitmap(gfx::Rect rect, SkBitmap* output) OVERRIDE {
68    NOTIMPLEMENTED();
69  }
70
71 private:
72  SynchronousCompositorOutputSurface* surface_;
73  SkDevice null_device_;
74  SkCanvas null_canvas_;
75
76  DISALLOW_COPY_AND_ASSIGN(SoftwareDevice);
77};
78
79SynchronousCompositorOutputSurface::SynchronousCompositorOutputSurface(
80    int routing_id)
81    : cc::OutputSurface(
82          scoped_ptr<cc::SoftwareOutputDevice>(new SoftwareDevice(this))),
83      routing_id_(routing_id),
84      needs_begin_frame_(false),
85      invoking_composite_(false),
86      did_swap_buffer_(false),
87      current_sw_canvas_(NULL) {
88  capabilities_.deferred_gl_initialization = true;
89  capabilities_.adjust_deadline_for_parent = false;
90  // Cannot call out to GetDelegate() here as the output surface is not
91  // constructed on the correct thread.
92}
93
94SynchronousCompositorOutputSurface::~SynchronousCompositorOutputSurface() {
95  DCHECK(CalledOnValidThread());
96  SynchronousCompositorOutputSurfaceDelegate* delegate = GetDelegate();
97  if (delegate)
98    delegate->DidDestroySynchronousOutputSurface(this);
99}
100
101bool SynchronousCompositorOutputSurface::ForcedDrawToSoftwareDevice() const {
102  // |current_sw_canvas_| indicates we're in a DemandDrawSw call. In addition
103  // |invoking_composite_| == false indicates an attempt to draw outside of
104  // the synchronous compositor's control: force it into SW path and hence to
105  // the null canvas (and will log a warning there).
106  return current_sw_canvas_ != NULL || !invoking_composite_;
107}
108
109bool SynchronousCompositorOutputSurface::BindToClient(
110    cc::OutputSurfaceClient* surface_client) {
111  DCHECK(CalledOnValidThread());
112  if (!cc::OutputSurface::BindToClient(surface_client))
113    return false;
114  SynchronousCompositorOutputSurfaceDelegate* delegate = GetDelegate();
115  if (delegate)
116    delegate->DidBindOutputSurface(this);
117  return true;
118}
119
120void SynchronousCompositorOutputSurface::Reshape(
121    gfx::Size size, float scale_factor) {
122  // Intentional no-op: surface size is controlled by the embedder.
123}
124
125void SynchronousCompositorOutputSurface::SetNeedsBeginFrame(
126    bool enable) {
127  DCHECK(CalledOnValidThread());
128  cc::OutputSurface::SetNeedsBeginFrame(enable);
129  needs_begin_frame_ = enable;
130  SynchronousCompositorOutputSurfaceDelegate* delegate = GetDelegate();
131  if (delegate)
132    delegate->SetContinuousInvalidate(needs_begin_frame_);
133}
134
135void SynchronousCompositorOutputSurface::SwapBuffers(
136    cc::CompositorFrame* frame) {
137  if (!ForcedDrawToSoftwareDevice()) {
138    DCHECK(context3d());
139    context3d()->shallowFlushCHROMIUM();
140  }
141  SynchronousCompositorOutputSurfaceDelegate* delegate = GetDelegate();
142  if (delegate)
143    delegate->UpdateFrameMetaData(frame->metadata);
144
145  did_swap_buffer_ = true;
146  DidSwapBuffers();
147}
148
149namespace {
150void AdjustTransformForClip(gfx::Transform* transform, gfx::Rect clip) {
151  // The system-provided transform translates us from the screen origin to the
152  // origin of the clip rect, but CC's draw origin starts at the clip.
153  transform->matrix().postTranslate(-clip.x(), -clip.y(), 0);
154}
155} // namespace
156
157bool SynchronousCompositorOutputSurface::InitializeHwDraw(
158    scoped_refptr<cc::ContextProvider> offscreen_context) {
159  DCHECK(CalledOnValidThread());
160  DCHECK(HasClient());
161  DCHECK(!context3d_);
162
163  return InitializeAndSetContext3D(CreateWebGraphicsContext3D().Pass(),
164                                   offscreen_context);
165}
166
167void SynchronousCompositorOutputSurface::ReleaseHwDraw() {
168  cc::OutputSurface::ReleaseGL();
169}
170
171bool SynchronousCompositorOutputSurface::DemandDrawHw(
172    gfx::Size surface_size,
173    const gfx::Transform& transform,
174    gfx::Rect clip) {
175  DCHECK(CalledOnValidThread());
176  DCHECK(HasClient());
177  DCHECK(context3d());
178
179  gfx::Transform adjusted_transform = transform;
180  AdjustTransformForClip(&adjusted_transform, clip);
181  surface_size_ = surface_size;
182  SetExternalDrawConstraints(adjusted_transform, clip);
183  InvokeComposite(clip.size());
184
185  // TODO(boliu): Check if context is lost here.
186
187  return did_swap_buffer_;
188}
189
190bool SynchronousCompositorOutputSurface::DemandDrawSw(SkCanvas* canvas) {
191  DCHECK(CalledOnValidThread());
192  DCHECK(canvas);
193  DCHECK(!current_sw_canvas_);
194  base::AutoReset<SkCanvas*> canvas_resetter(&current_sw_canvas_, canvas);
195
196  SkIRect canvas_clip;
197  canvas->getClipDeviceBounds(&canvas_clip);
198  gfx::Rect clip = gfx::SkIRectToRect(canvas_clip);
199
200  gfx::Transform transform(gfx::Transform::kSkipInitialization);
201  transform.matrix() = canvas->getTotalMatrix();  // Converts 3x3 matrix to 4x4.
202  AdjustTransformForClip(&transform, clip);
203
204  surface_size_ = gfx::Size(canvas->getDeviceSize().width(),
205                            canvas->getDeviceSize().height());
206  SetExternalDrawConstraints(transform, clip);
207
208  InvokeComposite(clip.size());
209
210  return did_swap_buffer_;
211}
212
213void SynchronousCompositorOutputSurface::InvokeComposite(
214    gfx::Size damage_size) {
215  DCHECK(!invoking_composite_);
216  base::AutoReset<bool> invoking_composite_resetter(&invoking_composite_, true);
217  did_swap_buffer_ = false;
218  SetNeedsRedrawRect(gfx::Rect(damage_size));
219  if (needs_begin_frame_)
220    BeginFrame(cc::BeginFrameArgs::CreateForSynchronousCompositor());
221
222  if (did_swap_buffer_)
223    OnSwapBuffersComplete(NULL);
224}
225
226void SynchronousCompositorOutputSurface::PostCheckForRetroactiveBeginFrame() {
227  // Synchronous compositor cannot perform retroactive begin frames, so
228  // intentionally no-op here.
229}
230
231// Not using base::NonThreadSafe as we want to enforce a more exacting threading
232// requirement: SynchronousCompositorOutputSurface() must only be used on the UI
233// thread.
234bool SynchronousCompositorOutputSurface::CalledOnValidThread() const {
235  return BrowserThread::CurrentlyOn(BrowserThread::UI);
236}
237
238SynchronousCompositorOutputSurfaceDelegate*
239SynchronousCompositorOutputSurface::GetDelegate() {
240  return SynchronousCompositorImpl::FromRoutingID(routing_id_);
241}
242
243}  // namespace content
244