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.
5 #include "content/browser/android/in_process/synchronous_compositor_output_surface.h"
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/gles2_interface.h"
17 #include "gpu/command_buffer/common/gpu_memory_allocation.h"
18 #include "third_party/skia/include/core/SkBitmapDevice.h"
19 #include "third_party/skia/include/core/SkCanvas.h"
20 #include "ui/gfx/rect_conversions.h"
21 #include "ui/gfx/skia_util.h"
22 #include "ui/gfx/transform.h"
28 void DidActivatePendingTree(int routing_id) {
29 SynchronousCompositorOutputSurfaceDelegate* delegate =
30 SynchronousCompositorImpl::FromRoutingID(routing_id);
32 delegate->DidActivatePendingTree();
37 class SynchronousCompositorOutputSurface::SoftwareDevice
38 : public cc::SoftwareOutputDevice {
40 SoftwareDevice(SynchronousCompositorOutputSurface* surface)
42 null_device_(SkBitmap::kARGB_8888_Config, 1, 1),
43 null_canvas_(&null_device_) {
45 virtual void Resize(const gfx::Size& size) OVERRIDE {
46 // Intentional no-op: canvas size is controlled by the embedder.
48 virtual SkCanvas* BeginPaint(const gfx::Rect& damage_rect) OVERRIDE {
49 if (!surface_->current_sw_canvas_) {
50 NOTREACHED() << "BeginPaint with no canvas set";
53 LOG_IF(WARNING, surface_->did_swap_buffer_)
54 << "Mutliple calls to BeginPaint per frame";
55 return surface_->current_sw_canvas_;
57 virtual void EndPaint(cc::SoftwareFrameData* frame_data) OVERRIDE {
59 virtual void CopyToBitmap(const gfx::Rect& rect, SkBitmap* output) OVERRIDE {
64 SynchronousCompositorOutputSurface* surface_;
65 SkBitmapDevice null_device_;
66 SkCanvas null_canvas_;
68 DISALLOW_COPY_AND_ASSIGN(SoftwareDevice);
71 SynchronousCompositorOutputSurface::SynchronousCompositorOutputSurface(
74 scoped_ptr<cc::SoftwareOutputDevice>(new SoftwareDevice(this))),
75 routing_id_(routing_id),
76 needs_begin_impl_frame_(false),
77 invoking_composite_(false),
78 did_swap_buffer_(false),
79 current_sw_canvas_(NULL),
81 output_surface_client_(NULL) {
82 capabilities_.deferred_gl_initialization = true;
83 capabilities_.draw_and_swap_full_viewport_every_frame = true;
84 capabilities_.adjust_deadline_for_parent = false;
85 // Cannot call out to GetDelegate() here as the output surface is not
86 // constructed on the correct thread.
88 memory_policy_.priority_cutoff_when_visible =
89 gpu::MemoryAllocation::CUTOFF_ALLOW_NICE_TO_HAVE;
92 SynchronousCompositorOutputSurface::~SynchronousCompositorOutputSurface() {
93 DCHECK(CalledOnValidThread());
94 SynchronousCompositorOutputSurfaceDelegate* delegate = GetDelegate();
96 delegate->DidDestroySynchronousOutputSurface(this);
99 bool SynchronousCompositorOutputSurface::ForcedDrawToSoftwareDevice() const {
100 // |current_sw_canvas_| indicates we're in a DemandDrawSw call. In addition
101 // |invoking_composite_| == false indicates an attempt to draw outside of
102 // the synchronous compositor's control: force it into SW path and hence to
103 // the null canvas (and will log a warning there).
104 return current_sw_canvas_ != NULL || !invoking_composite_;
107 bool SynchronousCompositorOutputSurface::BindToClient(
108 cc::OutputSurfaceClient* surface_client) {
109 DCHECK(CalledOnValidThread());
110 if (!cc::OutputSurface::BindToClient(surface_client))
113 output_surface_client_ = surface_client;
114 output_surface_client_->SetTreeActivationCallback(
115 base::Bind(&DidActivatePendingTree, routing_id_));
116 output_surface_client_->SetMemoryPolicy(memory_policy_);
118 SynchronousCompositorOutputSurfaceDelegate* delegate = GetDelegate();
120 delegate->DidBindOutputSurface(this);
125 void SynchronousCompositorOutputSurface::Reshape(
126 const gfx::Size& size, float scale_factor) {
127 // Intentional no-op: surface size is controlled by the embedder.
130 void SynchronousCompositorOutputSurface::SetNeedsBeginImplFrame(
132 DCHECK(CalledOnValidThread());
133 cc::OutputSurface::SetNeedsBeginImplFrame(enable);
134 needs_begin_impl_frame_ = enable;
135 SynchronousCompositorOutputSurfaceDelegate* delegate = GetDelegate();
137 delegate->SetContinuousInvalidate(needs_begin_impl_frame_);
140 void SynchronousCompositorOutputSurface::SwapBuffers(
141 cc::CompositorFrame* frame) {
142 DCHECK(CalledOnValidThread());
143 if (!ForcedDrawToSoftwareDevice()) {
144 DCHECK(context_provider_);
145 context_provider_->ContextGL()->ShallowFlushCHROMIUM();
147 SynchronousCompositorOutputSurfaceDelegate* delegate = GetDelegate();
149 delegate->UpdateFrameMetaData(frame->metadata);
151 did_swap_buffer_ = true;
156 void AdjustTransform(gfx::Transform* transform, gfx::Rect viewport) {
157 // CC's draw origin starts at the viewport.
158 transform->matrix().postTranslate(-viewport.x(), -viewport.y(), 0);
162 bool SynchronousCompositorOutputSurface::InitializeHwDraw(
163 scoped_refptr<cc::ContextProvider> onscreen_context_provider,
164 scoped_refptr<cc::ContextProvider> offscreen_context_provider) {
165 DCHECK(CalledOnValidThread());
167 DCHECK(!context_provider_);
169 return InitializeAndSetContext3d(onscreen_context_provider,
170 offscreen_context_provider);
173 void SynchronousCompositorOutputSurface::ReleaseHwDraw() {
174 DCHECK(CalledOnValidThread());
175 cc::OutputSurface::ReleaseGL();
178 bool SynchronousCompositorOutputSurface::DemandDrawHw(
179 gfx::Size surface_size,
180 const gfx::Transform& transform,
183 bool stencil_enabled) {
184 DCHECK(CalledOnValidThread());
186 DCHECK(context_provider_);
188 surface_size_ = surface_size;
189 SetExternalStencilTest(stencil_enabled);
190 InvokeComposite(transform, viewport, clip, true);
192 return did_swap_buffer_;
195 bool SynchronousCompositorOutputSurface::DemandDrawSw(SkCanvas* canvas) {
196 DCHECK(CalledOnValidThread());
198 DCHECK(!current_sw_canvas_);
199 base::AutoReset<SkCanvas*> canvas_resetter(¤t_sw_canvas_, canvas);
202 canvas->getClipDeviceBounds(&canvas_clip);
203 gfx::Rect clip = gfx::SkIRectToRect(canvas_clip);
205 gfx::Transform transform(gfx::Transform::kSkipInitialization);
206 transform.matrix() = canvas->getTotalMatrix(); // Converts 3x3 matrix to 4x4.
208 surface_size_ = gfx::Size(canvas->getDeviceSize().width(),
209 canvas->getDeviceSize().height());
210 SetExternalStencilTest(false);
212 InvokeComposite(transform, clip, clip, false);
214 return did_swap_buffer_;
217 void SynchronousCompositorOutputSurface::InvokeComposite(
218 const gfx::Transform& transform,
221 bool valid_for_tile_management) {
222 DCHECK(!invoking_composite_);
223 base::AutoReset<bool> invoking_composite_resetter(&invoking_composite_, true);
224 did_swap_buffer_ = false;
226 gfx::Transform adjusted_transform = transform;
227 AdjustTransform(&adjusted_transform, viewport);
228 SetExternalDrawConstraints(
229 adjusted_transform, viewport, clip, valid_for_tile_management);
230 SetNeedsRedrawRect(gfx::Rect(viewport.size()));
232 if (needs_begin_impl_frame_)
233 BeginImplFrame(cc::BeginFrameArgs::CreateForSynchronousCompositor());
235 // After software draws (which might move the viewport arbitrarily), restore
236 // the previous hardware viewport to allow CC's tile manager to prioritize
238 if (valid_for_tile_management) {
239 cached_hw_transform_ = adjusted_transform;
240 cached_hw_viewport_ = viewport;
241 cached_hw_clip_ = clip;
243 SetExternalDrawConstraints(
244 cached_hw_transform_, cached_hw_viewport_, cached_hw_clip_, true);
247 if (did_swap_buffer_)
248 OnSwapBuffersComplete();
252 SynchronousCompositorOutputSurface::PostCheckForRetroactiveBeginImplFrame() {
253 // Synchronous compositor cannot perform retroactive BeginImplFrames, so
254 // intentionally no-op here.
257 void SynchronousCompositorOutputSurface::SetMemoryPolicy(
258 const SynchronousCompositorMemoryPolicy& policy) {
259 DCHECK(CalledOnValidThread());
260 memory_policy_.bytes_limit_when_visible = policy.bytes_limit;
261 memory_policy_.num_resources_limit = policy.num_resources_limit;
263 if (output_surface_client_)
264 output_surface_client_->SetMemoryPolicy(memory_policy_);
267 // Not using base::NonThreadSafe as we want to enforce a more exacting threading
268 // requirement: SynchronousCompositorOutputSurface() must only be used on the UI
270 bool SynchronousCompositorOutputSurface::CalledOnValidThread() const {
271 return BrowserThread::CurrentlyOn(BrowserThread::UI);
274 SynchronousCompositorOutputSurfaceDelegate*
275 SynchronousCompositorOutputSurface::GetDelegate() {
276 return SynchronousCompositorImpl::FromRoutingID(routing_id_);
279 } // namespace content