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/gl_in_process_context.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"
23 #include "ui/gl/gl_surface.h"
24 #include "webkit/common/gpu/context_provider_in_process.h"
25 #include "webkit/common/gpu/webgraphicscontext3d_in_process_command_buffer_impl.h"
31 scoped_ptr<webkit::gpu::WebGraphicsContext3DInProcessCommandBufferImpl>
32 CreateWebGraphicsContext3D(scoped_refptr<gfx::GLSurface> surface) {
33 using webkit::gpu::WebGraphicsContext3DInProcessCommandBufferImpl;
34 if (!gfx::GLSurface::InitializeOneOff())
35 return scoped_ptr<WebGraphicsContext3DInProcessCommandBufferImpl>();
37 const gfx::GpuPreference gpu_preference = gfx::PreferDiscreteGpu;
39 WebKit::WebGraphicsContext3D::Attributes attributes;
40 attributes.antialias = false;
41 attributes.shareResources = true;
42 attributes.noAutomaticFlushes = true;
44 gpu::GLInProcessContextAttribs in_process_attribs;
45 WebGraphicsContext3DInProcessCommandBufferImpl::ConvertAttributes(
46 attributes, &in_process_attribs);
47 scoped_ptr<gpu::GLInProcessContext> context(
48 gpu::GLInProcessContext::CreateWithSurface(surface,
49 attributes.shareResources,
54 return scoped_ptr<WebGraphicsContext3DInProcessCommandBufferImpl>();
56 return WebGraphicsContext3DInProcessCommandBufferImpl::WrapContext(
57 context.Pass(), attributes).Pass();
60 void DidActivatePendingTree(int routing_id) {
61 SynchronousCompositorOutputSurfaceDelegate* delegate =
62 SynchronousCompositorImpl::FromRoutingID(routing_id);
64 delegate->DidActivatePendingTree();
69 class SynchronousCompositorOutputSurface::SoftwareDevice
70 : public cc::SoftwareOutputDevice {
72 SoftwareDevice(SynchronousCompositorOutputSurface* surface)
74 null_device_(SkBitmap::kARGB_8888_Config, 1, 1),
75 null_canvas_(&null_device_) {
77 virtual void Resize(gfx::Size size) OVERRIDE {
78 // Intentional no-op: canvas size is controlled by the embedder.
80 virtual SkCanvas* BeginPaint(gfx::Rect damage_rect) OVERRIDE {
81 if (!surface_->current_sw_canvas_) {
82 NOTREACHED() << "BeginPaint with no canvas set";
85 LOG_IF(WARNING, surface_->did_swap_buffer_)
86 << "Mutliple calls to BeginPaint per frame";
87 return surface_->current_sw_canvas_;
89 virtual void EndPaint(cc::SoftwareFrameData* frame_data) OVERRIDE {
91 virtual void CopyToBitmap(gfx::Rect rect, SkBitmap* output) OVERRIDE {
96 SynchronousCompositorOutputSurface* surface_;
97 SkBitmapDevice null_device_;
98 SkCanvas null_canvas_;
100 DISALLOW_COPY_AND_ASSIGN(SoftwareDevice);
103 SynchronousCompositorOutputSurface::SynchronousCompositorOutputSurface(
106 scoped_ptr<cc::SoftwareOutputDevice>(new SoftwareDevice(this))),
107 routing_id_(routing_id),
108 needs_begin_impl_frame_(false),
109 invoking_composite_(false),
110 did_swap_buffer_(false),
111 current_sw_canvas_(NULL),
113 output_surface_client_(NULL) {
114 capabilities_.deferred_gl_initialization = true;
115 capabilities_.draw_and_swap_full_viewport_every_frame = true;
116 capabilities_.adjust_deadline_for_parent = false;
117 // Cannot call out to GetDelegate() here as the output surface is not
118 // constructed on the correct thread.
120 memory_policy_.priority_cutoff_when_visible =
121 gpu::MemoryAllocation::CUTOFF_ALLOW_NICE_TO_HAVE;
124 SynchronousCompositorOutputSurface::~SynchronousCompositorOutputSurface() {
125 DCHECK(CalledOnValidThread());
126 SynchronousCompositorOutputSurfaceDelegate* delegate = GetDelegate();
128 delegate->DidDestroySynchronousOutputSurface(this);
131 bool SynchronousCompositorOutputSurface::ForcedDrawToSoftwareDevice() const {
132 // |current_sw_canvas_| indicates we're in a DemandDrawSw call. In addition
133 // |invoking_composite_| == false indicates an attempt to draw outside of
134 // the synchronous compositor's control: force it into SW path and hence to
135 // the null canvas (and will log a warning there).
136 return current_sw_canvas_ != NULL || !invoking_composite_;
139 bool SynchronousCompositorOutputSurface::BindToClient(
140 cc::OutputSurfaceClient* surface_client) {
141 DCHECK(CalledOnValidThread());
142 if (!cc::OutputSurface::BindToClient(surface_client))
145 output_surface_client_ = surface_client;
146 output_surface_client_->SetTreeActivationCallback(
147 base::Bind(&DidActivatePendingTree, routing_id_));
148 output_surface_client_->SetMemoryPolicy(memory_policy_);
150 SynchronousCompositorOutputSurfaceDelegate* delegate = GetDelegate();
152 delegate->DidBindOutputSurface(this);
157 void SynchronousCompositorOutputSurface::Reshape(
158 gfx::Size size, float scale_factor) {
159 // Intentional no-op: surface size is controlled by the embedder.
162 void SynchronousCompositorOutputSurface::SetNeedsBeginImplFrame(
164 DCHECK(CalledOnValidThread());
165 cc::OutputSurface::SetNeedsBeginImplFrame(enable);
166 needs_begin_impl_frame_ = enable;
167 SynchronousCompositorOutputSurfaceDelegate* delegate = GetDelegate();
169 delegate->SetContinuousInvalidate(needs_begin_impl_frame_);
172 void SynchronousCompositorOutputSurface::SwapBuffers(
173 cc::CompositorFrame* frame) {
174 DCHECK(CalledOnValidThread());
175 if (!ForcedDrawToSoftwareDevice()) {
176 DCHECK(context_provider_);
177 context_provider_->Context3d()->shallowFlushCHROMIUM();
179 SynchronousCompositorOutputSurfaceDelegate* delegate = GetDelegate();
181 delegate->UpdateFrameMetaData(frame->metadata);
183 did_swap_buffer_ = true;
188 void AdjustTransform(gfx::Transform* transform, gfx::Rect viewport) {
189 // CC's draw origin starts at the viewport.
190 transform->matrix().postTranslate(-viewport.x(), -viewport.y(), 0);
194 bool SynchronousCompositorOutputSurface::InitializeHwDraw(
195 scoped_refptr<gfx::GLSurface> surface,
196 scoped_refptr<cc::ContextProvider> offscreen_context_provider) {
197 DCHECK(CalledOnValidThread());
199 DCHECK(!context_provider_);
202 scoped_refptr<cc::ContextProvider> onscreen_context_provider =
203 webkit::gpu::ContextProviderInProcess::Create(
204 CreateWebGraphicsContext3D(surface), "SynchronousCompositor");
205 return InitializeAndSetContext3d(onscreen_context_provider,
206 offscreen_context_provider);
209 void SynchronousCompositorOutputSurface::ReleaseHwDraw() {
210 DCHECK(CalledOnValidThread());
211 cc::OutputSurface::ReleaseGL();
214 bool SynchronousCompositorOutputSurface::DemandDrawHw(
215 gfx::Size surface_size,
216 const gfx::Transform& transform,
219 bool stencil_enabled) {
220 DCHECK(CalledOnValidThread());
222 DCHECK(context_provider_);
224 surface_size_ = surface_size;
225 SetExternalStencilTest(stencil_enabled);
226 InvokeComposite(transform, viewport, clip, true);
228 return did_swap_buffer_;
231 bool SynchronousCompositorOutputSurface::DemandDrawSw(SkCanvas* canvas) {
232 DCHECK(CalledOnValidThread());
234 DCHECK(!current_sw_canvas_);
235 base::AutoReset<SkCanvas*> canvas_resetter(¤t_sw_canvas_, canvas);
238 canvas->getClipDeviceBounds(&canvas_clip);
239 gfx::Rect clip = gfx::SkIRectToRect(canvas_clip);
241 gfx::Transform transform(gfx::Transform::kSkipInitialization);
242 transform.matrix() = canvas->getTotalMatrix(); // Converts 3x3 matrix to 4x4.
244 surface_size_ = gfx::Size(canvas->getDeviceSize().width(),
245 canvas->getDeviceSize().height());
246 SetExternalStencilTest(false);
248 InvokeComposite(transform, clip, clip, false);
250 return did_swap_buffer_;
253 void SynchronousCompositorOutputSurface::InvokeComposite(
254 const gfx::Transform& transform,
257 bool valid_for_tile_management) {
258 DCHECK(!invoking_composite_);
259 base::AutoReset<bool> invoking_composite_resetter(&invoking_composite_, true);
260 did_swap_buffer_ = false;
262 gfx::Transform adjusted_transform = transform;
263 AdjustTransform(&adjusted_transform, viewport);
264 SetExternalDrawConstraints(
265 adjusted_transform, viewport, clip, valid_for_tile_management);
266 SetNeedsRedrawRect(gfx::Rect(viewport.size()));
268 if (needs_begin_impl_frame_)
269 BeginImplFrame(cc::BeginFrameArgs::CreateForSynchronousCompositor());
271 // After software draws (which might move the viewport arbitrarily), restore
272 // the previous hardware viewport to allow CC's tile manager to prioritize
274 if (valid_for_tile_management) {
275 cached_hw_transform_ = adjusted_transform;
276 cached_hw_viewport_ = viewport;
277 cached_hw_clip_ = clip;
279 SetExternalDrawConstraints(
280 cached_hw_transform_, cached_hw_viewport_, cached_hw_clip_, true);
283 if (did_swap_buffer_)
284 OnSwapBuffersComplete();
288 SynchronousCompositorOutputSurface::PostCheckForRetroactiveBeginImplFrame() {
289 // Synchronous compositor cannot perform retroactive BeginImplFrames, so
290 // intentionally no-op here.
293 void SynchronousCompositorOutputSurface::SetMemoryPolicy(
294 const SynchronousCompositorMemoryPolicy& policy) {
295 DCHECK(CalledOnValidThread());
296 memory_policy_.bytes_limit_when_visible = policy.bytes_limit;
297 memory_policy_.num_resources_limit = policy.num_resources_limit;
299 if (output_surface_client_)
300 output_surface_client_->SetMemoryPolicy(memory_policy_);
303 // Not using base::NonThreadSafe as we want to enforce a more exacting threading
304 // requirement: SynchronousCompositorOutputSurface() must only be used on the UI
306 bool SynchronousCompositorOutputSurface::CalledOnValidThread() const {
307 return BrowserThread::CurrentlyOn(BrowserThread::UI);
310 SynchronousCompositorOutputSurfaceDelegate*
311 SynchronousCompositorOutputSurface::GetDelegate() {
312 return SynchronousCompositorImpl::FromRoutingID(routing_id_);
315 } // namespace content