synchronous_compositor_output_surface.cc revision 438599f994082010ffd07abcbecb7a97956451e2
1aa58081902ad31927df02e8537d972eabe29d6dfDmitri Gribenko// Copyright 2013 The Chromium Authors. All rights reserved.
2aa58081902ad31927df02e8537d972eabe29d6dfDmitri Gribenko// Use of this source code is governed by a BSD-style license that can be
3aa58081902ad31927df02e8537d972eabe29d6dfDmitri Gribenko// found in the LICENSE file.
4aa58081902ad31927df02e8537d972eabe29d6dfDmitri Gribenko
5aa58081902ad31927df02e8537d972eabe29d6dfDmitri Gribenko#include "content/browser/android/in_process/synchronous_compositor_output_surface.h"
6aa58081902ad31927df02e8537d972eabe29d6dfDmitri Gribenko
7aa58081902ad31927df02e8537d972eabe29d6dfDmitri Gribenko#include "base/auto_reset.h"
8aa58081902ad31927df02e8537d972eabe29d6dfDmitri Gribenko#include "base/logging.h"
9aa58081902ad31927df02e8537d972eabe29d6dfDmitri Gribenko#include "cc/output/begin_frame_args.h"
10aa58081902ad31927df02e8537d972eabe29d6dfDmitri Gribenko#include "cc/output/compositor_frame.h"
11e4330a302ac20b41b9800267ebd4b5b01f8553f8Dmitri Gribenko#include "cc/output/context_provider.h"
12aa58081902ad31927df02e8537d972eabe29d6dfDmitri Gribenko#include "cc/output/managed_memory_policy.h"
13aa58081902ad31927df02e8537d972eabe29d6dfDmitri Gribenko#include "cc/output/output_surface_client.h"
14aa58081902ad31927df02e8537d972eabe29d6dfDmitri Gribenko#include "cc/output/software_output_device.h"
15aa58081902ad31927df02e8537d972eabe29d6dfDmitri Gribenko#include "content/browser/android/in_process/synchronous_compositor_impl.h"
16e4330a302ac20b41b9800267ebd4b5b01f8553f8Dmitri Gribenko#include "content/common/gpu/client/webgraphicscontext3d_command_buffer_impl.h"
17aa58081902ad31927df02e8537d972eabe29d6dfDmitri Gribenko#include "content/public/browser/browser_thread.h"
186ebf09130479bc7605aa09a3e6c4dc2ba3513495Dmitri Gribenko#include "gpu/command_buffer/client/gl_in_process_context.h"
196ebf09130479bc7605aa09a3e6c4dc2ba3513495Dmitri Gribenko#include "third_party/skia/include/core/SkCanvas.h"
206ebf09130479bc7605aa09a3e6c4dc2ba3513495Dmitri Gribenko#include "third_party/skia/include/core/SkDevice.h"
216ebf09130479bc7605aa09a3e6c4dc2ba3513495Dmitri Gribenko#include "ui/gfx/rect_conversions.h"
226ebf09130479bc7605aa09a3e6c4dc2ba3513495Dmitri Gribenko#include "ui/gfx/skia_util.h"
236ebf09130479bc7605aa09a3e6c4dc2ba3513495Dmitri Gribenko#include "ui/gfx/transform.h"
246ebf09130479bc7605aa09a3e6c4dc2ba3513495Dmitri Gribenko#include "ui/gl/gl_surface.h"
256ebf09130479bc7605aa09a3e6c4dc2ba3513495Dmitri Gribenko#include "webkit/common/gpu/webgraphicscontext3d_in_process_command_buffer_impl.h"
266ebf09130479bc7605aa09a3e6c4dc2ba3513495Dmitri Gribenko
276ebf09130479bc7605aa09a3e6c4dc2ba3513495Dmitri Gribenko
286ebf09130479bc7605aa09a3e6c4dc2ba3513495Dmitri Gribenkonamespace content {
296ebf09130479bc7605aa09a3e6c4dc2ba3513495Dmitri Gribenko
306ebf09130479bc7605aa09a3e6c4dc2ba3513495Dmitri Gribenkonamespace {
316ebf09130479bc7605aa09a3e6c4dc2ba3513495Dmitri Gribenko
326ebf09130479bc7605aa09a3e6c4dc2ba3513495Dmitri Gribenkoscoped_ptr<WebKit::WebGraphicsContext3D> CreateWebGraphicsContext3D(
33aa58081902ad31927df02e8537d972eabe29d6dfDmitri Gribenko    scoped_refptr<gfx::GLSurface> surface) {
34e4330a302ac20b41b9800267ebd4b5b01f8553f8Dmitri Gribenko  using webkit::gpu::WebGraphicsContext3DInProcessCommandBufferImpl;
35e4330a302ac20b41b9800267ebd4b5b01f8553f8Dmitri Gribenko  if (!gfx::GLSurface::InitializeOneOff())
36e4330a302ac20b41b9800267ebd4b5b01f8553f8Dmitri Gribenko    return scoped_ptr<WebKit::WebGraphicsContext3D>();
37e4330a302ac20b41b9800267ebd4b5b01f8553f8Dmitri Gribenko
38aa58081902ad31927df02e8537d972eabe29d6dfDmitri Gribenko  const char* allowed_extensions = "*";
39aa58081902ad31927df02e8537d972eabe29d6dfDmitri Gribenko  const gfx::GpuPreference gpu_preference = gfx::PreferDiscreteGpu;
40e4330a302ac20b41b9800267ebd4b5b01f8553f8Dmitri Gribenko
41e4330a302ac20b41b9800267ebd4b5b01f8553f8Dmitri Gribenko  WebKit::WebGraphicsContext3D::Attributes attributes;
42e4330a302ac20b41b9800267ebd4b5b01f8553f8Dmitri Gribenko  attributes.antialias = false;
43e4330a302ac20b41b9800267ebd4b5b01f8553f8Dmitri Gribenko  attributes.shareResources = true;
44e4330a302ac20b41b9800267ebd4b5b01f8553f8Dmitri Gribenko  attributes.noAutomaticFlushes = true;
45aa58081902ad31927df02e8537d972eabe29d6dfDmitri Gribenko
46ad91e5431f2f2d0a32ccdff8c55e7fa921af42bdFariborz Jahanian  gpu::GLInProcessContextAttribs in_process_attribs;
47ad91e5431f2f2d0a32ccdff8c55e7fa921af42bdFariborz Jahanian  WebGraphicsContext3DInProcessCommandBufferImpl::ConvertAttributes(
48ad91e5431f2f2d0a32ccdff8c55e7fa921af42bdFariborz Jahanian      attributes, &in_process_attribs);
490089bc4ddee6bb309ad25f4c7ad4b7ffe5df4512Fariborz Jahanian  scoped_ptr<gpu::GLInProcessContext> context(
500089bc4ddee6bb309ad25f4c7ad4b7ffe5df4512Fariborz Jahanian      gpu::GLInProcessContext::CreateWithSurface(surface,
51ad91e5431f2f2d0a32ccdff8c55e7fa921af42bdFariborz Jahanian                                                 attributes.shareResources,
52ad91e5431f2f2d0a32ccdff8c55e7fa921af42bdFariborz Jahanian                                                 allowed_extensions,
53ad91e5431f2f2d0a32ccdff8c55e7fa921af42bdFariborz Jahanian                                                 in_process_attribs,
54ad91e5431f2f2d0a32ccdff8c55e7fa921af42bdFariborz Jahanian                                                 gpu_preference));
55ad91e5431f2f2d0a32ccdff8c55e7fa921af42bdFariborz Jahanian
56ad91e5431f2f2d0a32ccdff8c55e7fa921af42bdFariborz Jahanian  if (!context.get())
57ad91e5431f2f2d0a32ccdff8c55e7fa921af42bdFariborz Jahanian    return scoped_ptr<WebKit::WebGraphicsContext3D>();
58ad91e5431f2f2d0a32ccdff8c55e7fa921af42bdFariborz Jahanian
59ad91e5431f2f2d0a32ccdff8c55e7fa921af42bdFariborz Jahanian  return scoped_ptr<WebKit::WebGraphicsContext3D>(
60ad91e5431f2f2d0a32ccdff8c55e7fa921af42bdFariborz Jahanian      WebGraphicsContext3DInProcessCommandBufferImpl::WrapContext(
61ad91e5431f2f2d0a32ccdff8c55e7fa921af42bdFariborz Jahanian          context.Pass(), attributes));
62ad91e5431f2f2d0a32ccdff8c55e7fa921af42bdFariborz Jahanian}
63ad91e5431f2f2d0a32ccdff8c55e7fa921af42bdFariborz Jahanian
64ad91e5431f2f2d0a32ccdff8c55e7fa921af42bdFariborz Jahanianvoid DidActivatePendingTree(int routing_id) {
65ad91e5431f2f2d0a32ccdff8c55e7fa921af42bdFariborz Jahanian  SynchronousCompositorOutputSurfaceDelegate* delegate =
66ad91e5431f2f2d0a32ccdff8c55e7fa921af42bdFariborz Jahanian      SynchronousCompositorImpl::FromRoutingID(routing_id);
67ad91e5431f2f2d0a32ccdff8c55e7fa921af42bdFariborz Jahanian  if (delegate)
68ad91e5431f2f2d0a32ccdff8c55e7fa921af42bdFariborz Jahanian    delegate->DidActivatePendingTree();
69ad91e5431f2f2d0a32ccdff8c55e7fa921af42bdFariborz Jahanian}
70ad91e5431f2f2d0a32ccdff8c55e7fa921af42bdFariborz Jahanian
71f4030ae4638d1831a2a031f1e33b86de8c5fef28Fariborz Jahanian} // namespace
72f4030ae4638d1831a2a031f1e33b86de8c5fef28Fariborz Jahanian
73f4030ae4638d1831a2a031f1e33b86de8c5fef28Fariborz Jahanianclass SynchronousCompositorOutputSurface::SoftwareDevice
74f4030ae4638d1831a2a031f1e33b86de8c5fef28Fariborz Jahanian  : public cc::SoftwareOutputDevice {
75f4030ae4638d1831a2a031f1e33b86de8c5fef28Fariborz Jahanian public:
760089bc4ddee6bb309ad25f4c7ad4b7ffe5df4512Fariborz Jahanian  SoftwareDevice(SynchronousCompositorOutputSurface* surface)
770089bc4ddee6bb309ad25f4c7ad4b7ffe5df4512Fariborz Jahanian    : surface_(surface),
78b960232518a1cd79c5f64ab5ef54c88e34660191Craig Topper      null_device_(SkBitmap::kARGB_8888_Config, 1, 1),
79ad91e5431f2f2d0a32ccdff8c55e7fa921af42bdFariborz Jahanian      null_canvas_(&null_device_) {
80ad91e5431f2f2d0a32ccdff8c55e7fa921af42bdFariborz Jahanian  }
81ad91e5431f2f2d0a32ccdff8c55e7fa921af42bdFariborz Jahanian  virtual void Resize(gfx::Size size) OVERRIDE {
82ad91e5431f2f2d0a32ccdff8c55e7fa921af42bdFariborz Jahanian    // Intentional no-op: canvas size is controlled by the embedder.
83ad91e5431f2f2d0a32ccdff8c55e7fa921af42bdFariborz Jahanian  }
84ad91e5431f2f2d0a32ccdff8c55e7fa921af42bdFariborz Jahanian  virtual SkCanvas* BeginPaint(gfx::Rect damage_rect) OVERRIDE {
85ad91e5431f2f2d0a32ccdff8c55e7fa921af42bdFariborz Jahanian    if (!surface_->current_sw_canvas_) {
860089bc4ddee6bb309ad25f4c7ad4b7ffe5df4512Fariborz Jahanian      NOTREACHED() << "BeginPaint with no canvas set";
870089bc4ddee6bb309ad25f4c7ad4b7ffe5df4512Fariborz Jahanian      return &null_canvas_;
880089bc4ddee6bb309ad25f4c7ad4b7ffe5df4512Fariborz Jahanian    }
896ebf09130479bc7605aa09a3e6c4dc2ba3513495Dmitri Gribenko    LOG_IF(WARNING, surface_->did_swap_buffer_)
90116bb09882bc1c9281cd84dd07496201feb18d18Eli Friedman        << "Mutliple calls to BeginPaint per frame";
91e4330a302ac20b41b9800267ebd4b5b01f8553f8Dmitri Gribenko    return surface_->current_sw_canvas_;
92116bb09882bc1c9281cd84dd07496201feb18d18Eli Friedman  }
93aa58081902ad31927df02e8537d972eabe29d6dfDmitri Gribenko  virtual void EndPaint(cc::SoftwareFrameData* frame_data) OVERRIDE {
94e4330a302ac20b41b9800267ebd4b5b01f8553f8Dmitri Gribenko  }
95e4330a302ac20b41b9800267ebd4b5b01f8553f8Dmitri Gribenko  virtual void CopyToBitmap(gfx::Rect rect, SkBitmap* output) OVERRIDE {
96e4330a302ac20b41b9800267ebd4b5b01f8553f8Dmitri Gribenko    NOTIMPLEMENTED();
97e4330a302ac20b41b9800267ebd4b5b01f8553f8Dmitri Gribenko  }
98aa58081902ad31927df02e8537d972eabe29d6dfDmitri Gribenko
99e4330a302ac20b41b9800267ebd4b5b01f8553f8Dmitri Gribenko private:
100aa58081902ad31927df02e8537d972eabe29d6dfDmitri Gribenko  SynchronousCompositorOutputSurface* surface_;
101e4330a302ac20b41b9800267ebd4b5b01f8553f8Dmitri Gribenko  SkDevice null_device_;
102aa58081902ad31927df02e8537d972eabe29d6dfDmitri Gribenko  SkCanvas null_canvas_;
103aa58081902ad31927df02e8537d972eabe29d6dfDmitri Gribenko
1046ebf09130479bc7605aa09a3e6c4dc2ba3513495Dmitri Gribenko  DISALLOW_COPY_AND_ASSIGN(SoftwareDevice);
1056ebf09130479bc7605aa09a3e6c4dc2ba3513495Dmitri Gribenko};
1066ebf09130479bc7605aa09a3e6c4dc2ba3513495Dmitri Gribenko
1076ebf09130479bc7605aa09a3e6c4dc2ba3513495Dmitri GribenkoSynchronousCompositorOutputSurface::SynchronousCompositorOutputSurface(
1086ebf09130479bc7605aa09a3e6c4dc2ba3513495Dmitri Gribenko    int routing_id)
1096ebf09130479bc7605aa09a3e6c4dc2ba3513495Dmitri Gribenko    : cc::OutputSurface(
1106ebf09130479bc7605aa09a3e6c4dc2ba3513495Dmitri Gribenko          scoped_ptr<cc::SoftwareOutputDevice>(new SoftwareDevice(this))),
1116ebf09130479bc7605aa09a3e6c4dc2ba3513495Dmitri Gribenko      routing_id_(routing_id),
1126ebf09130479bc7605aa09a3e6c4dc2ba3513495Dmitri Gribenko      needs_begin_frame_(false),
1136ebf09130479bc7605aa09a3e6c4dc2ba3513495Dmitri Gribenko      invoking_composite_(false),
1146ebf09130479bc7605aa09a3e6c4dc2ba3513495Dmitri Gribenko      did_swap_buffer_(false),
1156ebf09130479bc7605aa09a3e6c4dc2ba3513495Dmitri Gribenko      current_sw_canvas_(NULL) {
1166ebf09130479bc7605aa09a3e6c4dc2ba3513495Dmitri Gribenko  capabilities_.deferred_gl_initialization = true;
117e4330a302ac20b41b9800267ebd4b5b01f8553f8Dmitri Gribenko  capabilities_.draw_and_swap_full_viewport_every_frame = true;
118e4330a302ac20b41b9800267ebd4b5b01f8553f8Dmitri Gribenko  capabilities_.adjust_deadline_for_parent = false;
119e4330a302ac20b41b9800267ebd4b5b01f8553f8Dmitri Gribenko  // Cannot call out to GetDelegate() here as the output surface is not
120e4330a302ac20b41b9800267ebd4b5b01f8553f8Dmitri Gribenko  // constructed on the correct thread.
121e4330a302ac20b41b9800267ebd4b5b01f8553f8Dmitri Gribenko}
12262290ae569016345b79d4e11dd93abc300e5a681Dmitri Gribenko
12362290ae569016345b79d4e11dd93abc300e5a681Dmitri GribenkoSynchronousCompositorOutputSurface::~SynchronousCompositorOutputSurface() {
124e4330a302ac20b41b9800267ebd4b5b01f8553f8Dmitri Gribenko  DCHECK(CalledOnValidThread());
125e4330a302ac20b41b9800267ebd4b5b01f8553f8Dmitri Gribenko  SynchronousCompositorOutputSurfaceDelegate* delegate = GetDelegate();
126e4330a302ac20b41b9800267ebd4b5b01f8553f8Dmitri Gribenko  if (delegate)
127e4330a302ac20b41b9800267ebd4b5b01f8553f8Dmitri Gribenko    delegate->DidDestroySynchronousOutputSurface(this);
128e4330a302ac20b41b9800267ebd4b5b01f8553f8Dmitri Gribenko}
129e4330a302ac20b41b9800267ebd4b5b01f8553f8Dmitri Gribenko
130e4330a302ac20b41b9800267ebd4b5b01f8553f8Dmitri Gribenkobool SynchronousCompositorOutputSurface::ForcedDrawToSoftwareDevice() const {
131aa58081902ad31927df02e8537d972eabe29d6dfDmitri Gribenko  // |current_sw_canvas_| indicates we're in a DemandDrawSw call. In addition
132aa58081902ad31927df02e8537d972eabe29d6dfDmitri Gribenko  // |invoking_composite_| == false indicates an attempt to draw outside of
133e4330a302ac20b41b9800267ebd4b5b01f8553f8Dmitri Gribenko  // the synchronous compositor's control: force it into SW path and hence to
134e4330a302ac20b41b9800267ebd4b5b01f8553f8Dmitri Gribenko  // the null canvas (and will log a warning there).
135e4330a302ac20b41b9800267ebd4b5b01f8553f8Dmitri Gribenko  return current_sw_canvas_ != NULL || !invoking_composite_;
136aa58081902ad31927df02e8537d972eabe29d6dfDmitri Gribenko}
137aa58081902ad31927df02e8537d972eabe29d6dfDmitri Gribenko
138aa58081902ad31927df02e8537d972eabe29d6dfDmitri Gribenkobool SynchronousCompositorOutputSurface::BindToClient(
139aa58081902ad31927df02e8537d972eabe29d6dfDmitri Gribenko    cc::OutputSurfaceClient* surface_client) {
140aa58081902ad31927df02e8537d972eabe29d6dfDmitri Gribenko  DCHECK(CalledOnValidThread());
141  if (!cc::OutputSurface::BindToClient(surface_client))
142    return false;
143  surface_client->SetTreeActivationCallback(
144      base::Bind(&DidActivatePendingTree, routing_id_));
145  SynchronousCompositorOutputSurfaceDelegate* delegate = GetDelegate();
146  if (delegate)
147    delegate->DidBindOutputSurface(this);
148
149  const int bytes_limit = 64 * 1024 * 1024;
150  const int num_resources_limit = 100;
151  surface_client->SetMemoryPolicy(
152      cc::ManagedMemoryPolicy(bytes_limit,
153                              cc::ManagedMemoryPolicy::CUTOFF_ALLOW_EVERYTHING,
154                              0,
155                              cc::ManagedMemoryPolicy::CUTOFF_ALLOW_NOTHING,
156                              num_resources_limit));
157
158  return true;
159}
160
161void SynchronousCompositorOutputSurface::Reshape(
162    gfx::Size size, float scale_factor) {
163  // Intentional no-op: surface size is controlled by the embedder.
164}
165
166void SynchronousCompositorOutputSurface::SetNeedsBeginFrame(
167    bool enable) {
168  DCHECK(CalledOnValidThread());
169  cc::OutputSurface::SetNeedsBeginFrame(enable);
170  needs_begin_frame_ = enable;
171  SynchronousCompositorOutputSurfaceDelegate* delegate = GetDelegate();
172  if (delegate)
173    delegate->SetContinuousInvalidate(needs_begin_frame_);
174}
175
176void SynchronousCompositorOutputSurface::SwapBuffers(
177    cc::CompositorFrame* frame) {
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  cc::OutputSurface::ReleaseGL();
213}
214
215bool SynchronousCompositorOutputSurface::DemandDrawHw(
216    gfx::Size surface_size,
217    const gfx::Transform& transform,
218    gfx::Rect viewport,
219    gfx::Rect clip,
220    bool stencil_enabled) {
221  DCHECK(CalledOnValidThread());
222  DCHECK(HasClient());
223  DCHECK(context3d());
224
225  surface_size_ = surface_size;
226  SetExternalStencilTest(stencil_enabled);
227  InvokeComposite(transform, viewport, clip, true);
228
229  return did_swap_buffer_;
230}
231
232bool SynchronousCompositorOutputSurface::DemandDrawSw(SkCanvas* canvas) {
233  DCHECK(CalledOnValidThread());
234  DCHECK(canvas);
235  DCHECK(!current_sw_canvas_);
236  base::AutoReset<SkCanvas*> canvas_resetter(&current_sw_canvas_, canvas);
237
238  SkIRect canvas_clip;
239  canvas->getClipDeviceBounds(&canvas_clip);
240  gfx::Rect clip = gfx::SkIRectToRect(canvas_clip);
241
242  gfx::Transform transform(gfx::Transform::kSkipInitialization);
243  transform.matrix() = canvas->getTotalMatrix();  // Converts 3x3 matrix to 4x4.
244
245  surface_size_ = gfx::Size(canvas->getDeviceSize().width(),
246                            canvas->getDeviceSize().height());
247  SetExternalStencilTest(false);
248
249  InvokeComposite(transform, clip, clip, false);
250
251  return did_swap_buffer_;
252}
253
254void SynchronousCompositorOutputSurface::InvokeComposite(
255    const gfx::Transform& transform,
256    gfx::Rect viewport,
257    gfx::Rect clip,
258    bool hardware) {
259  DCHECK(!invoking_composite_);
260  base::AutoReset<bool> invoking_composite_resetter(&invoking_composite_, true);
261  did_swap_buffer_ = false;
262
263  gfx::Transform adjusted_transform = transform;
264  AdjustTransform(&adjusted_transform, viewport, clip);
265  SetExternalDrawConstraints(adjusted_transform, viewport, clip, hardware);
266  SetNeedsRedrawRect(gfx::Rect(viewport.size()));
267
268  if (needs_begin_frame_)
269    BeginFrame(cc::BeginFrameArgs::CreateForSynchronousCompositor());
270
271  if (hardware) {
272    cached_hw_transform_ = adjusted_transform;
273    cached_hw_viewport_ = viewport;
274    cached_hw_clip_ = clip;
275  } else {
276    SetExternalDrawConstraints(
277        cached_hw_transform_, cached_hw_viewport_, cached_hw_clip_, true);
278  }
279
280  if (did_swap_buffer_)
281    OnSwapBuffersComplete(NULL);
282}
283
284void SynchronousCompositorOutputSurface::PostCheckForRetroactiveBeginFrame() {
285  // Synchronous compositor cannot perform retroactive begin frames, so
286  // intentionally no-op here.
287}
288
289// Not using base::NonThreadSafe as we want to enforce a more exacting threading
290// requirement: SynchronousCompositorOutputSurface() must only be used on the UI
291// thread.
292bool SynchronousCompositorOutputSurface::CalledOnValidThread() const {
293  return BrowserThread::CurrentlyOn(BrowserThread::UI);
294}
295
296SynchronousCompositorOutputSurfaceDelegate*
297SynchronousCompositorOutputSurface::GetDelegate() {
298  return SynchronousCompositorImpl::FromRoutingID(routing_id_);
299}
300
301}  // namespace content
302