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