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/common/gpu/client/webgraphicscontext3d_command_buffer_impl.h" 16 #include "content/public/browser/browser_thread.h" 17 #include "gpu/command_buffer/client/gl_in_process_context.h" 18 #include "third_party/skia/include/core/SkCanvas.h" 19 #include "third_party/skia/include/core/SkDevice.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/webgraphicscontext3d_in_process_command_buffer_impl.h" 25 26 27 namespace content { 28 29 namespace { 30 31 scoped_ptr<WebKit::WebGraphicsContext3D> CreateWebGraphicsContext3D( 32 scoped_refptr<gfx::GLSurface> surface) { 33 using webkit::gpu::WebGraphicsContext3DInProcessCommandBufferImpl; 34 if (!gfx::GLSurface::InitializeOneOff()) 35 return scoped_ptr<WebKit::WebGraphicsContext3D>(); 36 37 const char* allowed_extensions = "*"; 38 const gfx::GpuPreference gpu_preference = gfx::PreferDiscreteGpu; 39 40 WebKit::WebGraphicsContext3D::Attributes attributes; 41 attributes.antialias = false; 42 attributes.shareResources = true; 43 attributes.noAutomaticFlushes = true; 44 45 gpu::GLInProcessContextAttribs in_process_attribs; 46 WebGraphicsContext3DInProcessCommandBufferImpl::ConvertAttributes( 47 attributes, &in_process_attribs); 48 scoped_ptr<gpu::GLInProcessContext> context( 49 gpu::GLInProcessContext::CreateWithSurface(surface, 50 attributes.shareResources, 51 allowed_extensions, 52 in_process_attribs, 53 gpu_preference)); 54 55 if (!context.get()) 56 return scoped_ptr<WebKit::WebGraphicsContext3D>(); 57 58 return scoped_ptr<WebKit::WebGraphicsContext3D>( 59 WebGraphicsContext3DInProcessCommandBufferImpl::WrapContext( 60 context.Pass(), attributes)); 61 } 62 63 void DidActivatePendingTree(int routing_id) { 64 SynchronousCompositorOutputSurfaceDelegate* delegate = 65 SynchronousCompositorImpl::FromRoutingID(routing_id); 66 if (delegate) 67 delegate->DidActivatePendingTree(); 68 } 69 70 } // namespace 71 72 class SynchronousCompositorOutputSurface::SoftwareDevice 73 : public cc::SoftwareOutputDevice { 74 public: 75 SoftwareDevice(SynchronousCompositorOutputSurface* surface) 76 : surface_(surface), 77 null_device_(SkBitmap::kARGB_8888_Config, 1, 1), 78 null_canvas_(&null_device_) { 79 } 80 virtual void Resize(gfx::Size size) OVERRIDE { 81 // Intentional no-op: canvas size is controlled by the embedder. 82 } 83 virtual SkCanvas* BeginPaint(gfx::Rect damage_rect) OVERRIDE { 84 if (!surface_->current_sw_canvas_) { 85 NOTREACHED() << "BeginPaint with no canvas set"; 86 return &null_canvas_; 87 } 88 LOG_IF(WARNING, surface_->did_swap_buffer_) 89 << "Mutliple calls to BeginPaint per frame"; 90 return surface_->current_sw_canvas_; 91 } 92 virtual void EndPaint(cc::SoftwareFrameData* frame_data) OVERRIDE { 93 } 94 virtual void CopyToBitmap(gfx::Rect rect, SkBitmap* output) OVERRIDE { 95 NOTIMPLEMENTED(); 96 } 97 98 private: 99 SynchronousCompositorOutputSurface* surface_; 100 SkDevice null_device_; 101 SkCanvas null_canvas_; 102 103 DISALLOW_COPY_AND_ASSIGN(SoftwareDevice); 104 }; 105 106 SynchronousCompositorOutputSurface::SynchronousCompositorOutputSurface( 107 int routing_id) 108 : cc::OutputSurface( 109 scoped_ptr<cc::SoftwareOutputDevice>(new SoftwareDevice(this))), 110 routing_id_(routing_id), 111 needs_begin_frame_(false), 112 invoking_composite_(false), 113 did_swap_buffer_(false), 114 current_sw_canvas_(NULL), 115 memory_policy_(0), 116 output_surface_client_(NULL) { 117 capabilities_.deferred_gl_initialization = true; 118 capabilities_.draw_and_swap_full_viewport_every_frame = true; 119 capabilities_.adjust_deadline_for_parent = false; 120 // Cannot call out to GetDelegate() here as the output surface is not 121 // constructed on the correct thread. 122 123 memory_policy_.priority_cutoff_when_visible = 124 cc::ManagedMemoryPolicy::CUTOFF_ALLOW_NICE_TO_HAVE; 125 } 126 127 SynchronousCompositorOutputSurface::~SynchronousCompositorOutputSurface() { 128 DCHECK(CalledOnValidThread()); 129 SynchronousCompositorOutputSurfaceDelegate* delegate = GetDelegate(); 130 if (delegate) 131 delegate->DidDestroySynchronousOutputSurface(this); 132 } 133 134 bool SynchronousCompositorOutputSurface::ForcedDrawToSoftwareDevice() const { 135 // |current_sw_canvas_| indicates we're in a DemandDrawSw call. In addition 136 // |invoking_composite_| == false indicates an attempt to draw outside of 137 // the synchronous compositor's control: force it into SW path and hence to 138 // the null canvas (and will log a warning there). 139 return current_sw_canvas_ != NULL || !invoking_composite_; 140 } 141 142 bool SynchronousCompositorOutputSurface::BindToClient( 143 cc::OutputSurfaceClient* surface_client) { 144 DCHECK(CalledOnValidThread()); 145 if (!cc::OutputSurface::BindToClient(surface_client)) 146 return false; 147 148 output_surface_client_ = surface_client; 149 output_surface_client_->SetTreeActivationCallback( 150 base::Bind(&DidActivatePendingTree, routing_id_)); 151 output_surface_client_->SetMemoryPolicy(memory_policy_); 152 153 SynchronousCompositorOutputSurfaceDelegate* delegate = GetDelegate(); 154 if (delegate) 155 delegate->DidBindOutputSurface(this); 156 157 return true; 158 } 159 160 void SynchronousCompositorOutputSurface::Reshape( 161 gfx::Size size, float scale_factor) { 162 // Intentional no-op: surface size is controlled by the embedder. 163 } 164 165 void SynchronousCompositorOutputSurface::SetNeedsBeginFrame( 166 bool enable) { 167 DCHECK(CalledOnValidThread()); 168 cc::OutputSurface::SetNeedsBeginFrame(enable); 169 needs_begin_frame_ = enable; 170 SynchronousCompositorOutputSurfaceDelegate* delegate = GetDelegate(); 171 if (delegate) 172 delegate->SetContinuousInvalidate(needs_begin_frame_); 173 } 174 175 void SynchronousCompositorOutputSurface::SwapBuffers( 176 cc::CompositorFrame* frame) { 177 DCHECK(CalledOnValidThread()); 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 190 namespace { 191 void 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 199 bool 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 211 void SynchronousCompositorOutputSurface::ReleaseHwDraw() { 212 DCHECK(CalledOnValidThread()); 213 cc::OutputSurface::ReleaseGL(); 214 } 215 216 bool SynchronousCompositorOutputSurface::DemandDrawHw( 217 gfx::Size surface_size, 218 const gfx::Transform& transform, 219 gfx::Rect viewport, 220 gfx::Rect clip, 221 bool stencil_enabled) { 222 DCHECK(CalledOnValidThread()); 223 DCHECK(HasClient()); 224 DCHECK(context3d()); 225 226 surface_size_ = surface_size; 227 SetExternalStencilTest(stencil_enabled); 228 InvokeComposite(transform, viewport, clip, true); 229 230 return did_swap_buffer_; 231 } 232 233 bool SynchronousCompositorOutputSurface::DemandDrawSw(SkCanvas* canvas) { 234 DCHECK(CalledOnValidThread()); 235 DCHECK(canvas); 236 DCHECK(!current_sw_canvas_); 237 base::AutoReset<SkCanvas*> canvas_resetter(¤t_sw_canvas_, canvas); 238 239 SkIRect canvas_clip; 240 canvas->getClipDeviceBounds(&canvas_clip); 241 gfx::Rect clip = gfx::SkIRectToRect(canvas_clip); 242 243 gfx::Transform transform(gfx::Transform::kSkipInitialization); 244 transform.matrix() = canvas->getTotalMatrix(); // Converts 3x3 matrix to 4x4. 245 246 surface_size_ = gfx::Size(canvas->getDeviceSize().width(), 247 canvas->getDeviceSize().height()); 248 SetExternalStencilTest(false); 249 250 InvokeComposite(transform, clip, clip, false); 251 252 return did_swap_buffer_; 253 } 254 255 void SynchronousCompositorOutputSurface::InvokeComposite( 256 const gfx::Transform& transform, 257 gfx::Rect viewport, 258 gfx::Rect clip, 259 bool hardware) { 260 DCHECK(!invoking_composite_); 261 base::AutoReset<bool> invoking_composite_resetter(&invoking_composite_, true); 262 did_swap_buffer_ = false; 263 264 gfx::Transform adjusted_transform = transform; 265 AdjustTransform(&adjusted_transform, viewport, clip); 266 SetExternalDrawConstraints(adjusted_transform, viewport, clip, hardware); 267 SetNeedsRedrawRect(gfx::Rect(viewport.size())); 268 269 if (needs_begin_frame_) 270 BeginFrame(cc::BeginFrameArgs::CreateForSynchronousCompositor()); 271 272 if (hardware) { 273 cached_hw_transform_ = adjusted_transform; 274 cached_hw_viewport_ = viewport; 275 cached_hw_clip_ = clip; 276 } else { 277 SetExternalDrawConstraints( 278 cached_hw_transform_, cached_hw_viewport_, cached_hw_clip_, true); 279 } 280 281 if (did_swap_buffer_) 282 OnSwapBuffersComplete(NULL); 283 } 284 285 void SynchronousCompositorOutputSurface::PostCheckForRetroactiveBeginFrame() { 286 // Synchronous compositor cannot perform retroactive begin frames, so 287 // intentionally no-op here. 288 } 289 290 void SynchronousCompositorOutputSurface::SetMemoryPolicy( 291 const SynchronousCompositorMemoryPolicy& policy) { 292 DCHECK(CalledOnValidThread()); 293 memory_policy_.bytes_limit_when_visible = policy.bytes_limit; 294 memory_policy_.num_resources_limit = policy.num_resources_limit; 295 296 if (output_surface_client_) 297 output_surface_client_->SetMemoryPolicy(memory_policy_); 298 } 299 300 // Not using base::NonThreadSafe as we want to enforce a more exacting threading 301 // requirement: SynchronousCompositorOutputSurface() must only be used on the UI 302 // thread. 303 bool SynchronousCompositorOutputSurface::CalledOnValidThread() const { 304 return BrowserThread::CurrentlyOn(BrowserThread::UI); 305 } 306 307 SynchronousCompositorOutputSurfaceDelegate* 308 SynchronousCompositorOutputSurface::GetDelegate() { 309 return SynchronousCompositorImpl::FromRoutingID(routing_id_); 310 } 311 312 } // namespace content 313