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