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/browser/gpu/compositor_util.h" 16 #include "content/public/browser/browser_thread.h" 17 #include "gpu/command_buffer/client/gles2_interface.h" 18 #include "gpu/command_buffer/common/gpu_memory_allocation.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 24 namespace content { 25 26 namespace { 27 28 void DidActivatePendingTree(int routing_id) { 29 SynchronousCompositorOutputSurfaceDelegate* delegate = 30 SynchronousCompositorImpl::FromRoutingID(routing_id); 31 if (delegate) 32 delegate->DidActivatePendingTree(); 33 } 34 35 } // namespace 36 37 class SynchronousCompositorOutputSurface::SoftwareDevice 38 : public cc::SoftwareOutputDevice { 39 public: 40 SoftwareDevice(SynchronousCompositorOutputSurface* surface) 41 : surface_(surface) { 42 } 43 virtual void Resize(const gfx::Size& pixel_size, 44 float scale_factor) OVERRIDE { 45 // Intentional no-op: canvas size is controlled by the embedder. 46 } 47 virtual SkCanvas* BeginPaint(const gfx::Rect& damage_rect) OVERRIDE { 48 if (!surface_->current_sw_canvas_) { 49 NOTREACHED() << "BeginPaint with no canvas set"; 50 return &null_canvas_; 51 } 52 LOG_IF(WARNING, surface_->frame_holder_.get()) 53 << "Mutliple calls to BeginPaint per frame"; 54 return surface_->current_sw_canvas_; 55 } 56 virtual void EndPaint(cc::SoftwareFrameData* frame_data) OVERRIDE { 57 } 58 virtual void CopyToPixels(const gfx::Rect& rect, void* pixels) OVERRIDE { 59 NOTIMPLEMENTED(); 60 } 61 62 private: 63 SynchronousCompositorOutputSurface* surface_; 64 SkCanvas null_canvas_; 65 66 DISALLOW_COPY_AND_ASSIGN(SoftwareDevice); 67 }; 68 69 SynchronousCompositorOutputSurface::SynchronousCompositorOutputSurface( 70 int routing_id) 71 : cc::OutputSurface( 72 scoped_ptr<cc::SoftwareOutputDevice>(new SoftwareDevice(this))), 73 routing_id_(routing_id), 74 needs_begin_frame_(false), 75 invoking_composite_(false), 76 current_sw_canvas_(NULL), 77 memory_policy_(0), 78 output_surface_client_(NULL) { 79 capabilities_.deferred_gl_initialization = true; 80 capabilities_.draw_and_swap_full_viewport_every_frame = true; 81 capabilities_.adjust_deadline_for_parent = false; 82 capabilities_.delegated_rendering = true; 83 capabilities_.max_frames_pending = 1; 84 // Cannot call out to GetDelegate() here as the output surface is not 85 // constructed on the correct thread. 86 87 memory_policy_.priority_cutoff_when_visible = 88 gpu::MemoryAllocation::CUTOFF_ALLOW_NICE_TO_HAVE; 89 } 90 91 SynchronousCompositorOutputSurface::~SynchronousCompositorOutputSurface() { 92 DCHECK(CalledOnValidThread()); 93 SynchronousCompositorOutputSurfaceDelegate* delegate = GetDelegate(); 94 if (delegate) 95 delegate->DidDestroySynchronousOutputSurface(this); 96 } 97 98 bool SynchronousCompositorOutputSurface::BindToClient( 99 cc::OutputSurfaceClient* surface_client) { 100 DCHECK(CalledOnValidThread()); 101 if (!cc::OutputSurface::BindToClient(surface_client)) 102 return false; 103 104 output_surface_client_ = surface_client; 105 output_surface_client_->SetTreeActivationCallback( 106 base::Bind(&DidActivatePendingTree, routing_id_)); 107 output_surface_client_->SetMemoryPolicy(memory_policy_); 108 109 SynchronousCompositorOutputSurfaceDelegate* delegate = GetDelegate(); 110 if (delegate) 111 delegate->DidBindOutputSurface(this); 112 113 return true; 114 } 115 116 void SynchronousCompositorOutputSurface::Reshape( 117 const gfx::Size& size, float scale_factor) { 118 // Intentional no-op: surface size is controlled by the embedder. 119 } 120 121 void SynchronousCompositorOutputSurface::SetNeedsBeginFrame(bool enable) { 122 DCHECK(CalledOnValidThread()); 123 needs_begin_frame_ = enable; 124 SynchronousCompositorOutputSurfaceDelegate* delegate = GetDelegate(); 125 if (delegate && !invoking_composite_) 126 delegate->SetContinuousInvalidate(needs_begin_frame_); 127 } 128 129 void SynchronousCompositorOutputSurface::SwapBuffers( 130 cc::CompositorFrame* frame) { 131 DCHECK(CalledOnValidThread()); 132 133 frame_holder_.reset(new cc::CompositorFrame); 134 frame->AssignTo(frame_holder_.get()); 135 136 client_->DidSwapBuffers(); 137 } 138 139 namespace { 140 void AdjustTransform(gfx::Transform* transform, gfx::Rect viewport) { 141 // CC's draw origin starts at the viewport. 142 transform->matrix().postTranslate(-viewport.x(), -viewport.y(), 0); 143 } 144 } // namespace 145 146 bool SynchronousCompositorOutputSurface::InitializeHwDraw( 147 scoped_refptr<cc::ContextProvider> onscreen_context_provider) { 148 DCHECK(CalledOnValidThread()); 149 DCHECK(HasClient()); 150 DCHECK(!context_provider_); 151 152 return InitializeAndSetContext3d(onscreen_context_provider); 153 } 154 155 void SynchronousCompositorOutputSurface::ReleaseHwDraw() { 156 DCHECK(CalledOnValidThread()); 157 cc::OutputSurface::ReleaseGL(); 158 } 159 160 scoped_ptr<cc::CompositorFrame> 161 SynchronousCompositorOutputSurface::DemandDrawHw( 162 gfx::Size surface_size, 163 const gfx::Transform& transform, 164 gfx::Rect viewport, 165 gfx::Rect clip, 166 gfx::Rect viewport_rect_for_tile_priority, 167 const gfx::Transform& transform_for_tile_priority) { 168 DCHECK(CalledOnValidThread()); 169 DCHECK(HasClient()); 170 DCHECK(context_provider_); 171 172 surface_size_ = surface_size; 173 InvokeComposite(transform, 174 viewport, 175 clip, 176 viewport_rect_for_tile_priority, 177 transform_for_tile_priority, 178 true); 179 180 return frame_holder_.Pass(); 181 } 182 183 scoped_ptr<cc::CompositorFrame> 184 SynchronousCompositorOutputSurface::DemandDrawSw(SkCanvas* canvas) { 185 DCHECK(CalledOnValidThread()); 186 DCHECK(canvas); 187 DCHECK(!current_sw_canvas_); 188 base::AutoReset<SkCanvas*> canvas_resetter(¤t_sw_canvas_, canvas); 189 190 SkIRect canvas_clip; 191 canvas->getClipDeviceBounds(&canvas_clip); 192 gfx::Rect clip = gfx::SkIRectToRect(canvas_clip); 193 194 gfx::Transform transform(gfx::Transform::kSkipInitialization); 195 transform.matrix() = canvas->getTotalMatrix(); // Converts 3x3 matrix to 4x4. 196 197 surface_size_ = gfx::Size(canvas->getDeviceSize().width(), 198 canvas->getDeviceSize().height()); 199 200 // Resourceless software draw does not need viewport_for_tiling. 201 gfx::Rect empty; 202 InvokeComposite(transform, clip, clip, empty, gfx::Transform(), false); 203 204 return frame_holder_.Pass(); 205 } 206 207 void SynchronousCompositorOutputSurface::InvokeComposite( 208 const gfx::Transform& transform, 209 gfx::Rect viewport, 210 gfx::Rect clip, 211 gfx::Rect viewport_rect_for_tile_priority, 212 gfx::Transform transform_for_tile_priority, 213 bool hardware_draw) { 214 DCHECK(!invoking_composite_); 215 DCHECK(!frame_holder_.get()); 216 base::AutoReset<bool> invoking_composite_resetter(&invoking_composite_, true); 217 218 gfx::Transform adjusted_transform = transform; 219 AdjustTransform(&adjusted_transform, viewport); 220 SetExternalDrawConstraints(adjusted_transform, 221 viewport, 222 clip, 223 viewport_rect_for_tile_priority, 224 transform_for_tile_priority, 225 !hardware_draw); 226 SetNeedsRedrawRect(gfx::Rect(viewport.size())); 227 client_->BeginFrame(cc::BeginFrameArgs::CreateForSynchronousCompositor()); 228 229 // After software draws (which might move the viewport arbitrarily), restore 230 // the previous hardware viewport to allow CC's tile manager to prioritize 231 // properly. 232 if (hardware_draw) { 233 cached_hw_transform_ = adjusted_transform; 234 cached_hw_viewport_ = viewport; 235 cached_hw_clip_ = clip; 236 cached_hw_viewport_rect_for_tile_priority_ = 237 viewport_rect_for_tile_priority; 238 cached_hw_transform_for_tile_priority_ = transform_for_tile_priority; 239 } else { 240 bool resourceless_software_draw = false; 241 SetExternalDrawConstraints(cached_hw_transform_, 242 cached_hw_viewport_, 243 cached_hw_clip_, 244 cached_hw_viewport_rect_for_tile_priority_, 245 cached_hw_transform_for_tile_priority_, 246 resourceless_software_draw); 247 } 248 249 if (frame_holder_.get()) 250 client_->DidSwapBuffersComplete(); 251 252 SynchronousCompositorOutputSurfaceDelegate* delegate = GetDelegate(); 253 if (delegate) 254 delegate->SetContinuousInvalidate(needs_begin_frame_); 255 } 256 257 void SynchronousCompositorOutputSurface::ReturnResources( 258 const cc::CompositorFrameAck& frame_ack) { 259 ReclaimResources(&frame_ack); 260 } 261 262 void SynchronousCompositorOutputSurface::SetMemoryPolicy( 263 const SynchronousCompositorMemoryPolicy& policy) { 264 DCHECK(CalledOnValidThread()); 265 memory_policy_.bytes_limit_when_visible = policy.bytes_limit; 266 memory_policy_.num_resources_limit = policy.num_resources_limit; 267 268 if (output_surface_client_) 269 output_surface_client_->SetMemoryPolicy(memory_policy_); 270 } 271 272 // Not using base::NonThreadSafe as we want to enforce a more exacting threading 273 // requirement: SynchronousCompositorOutputSurface() must only be used on the UI 274 // thread. 275 bool SynchronousCompositorOutputSurface::CalledOnValidThread() const { 276 return BrowserThread::CurrentlyOn(BrowserThread::UI); 277 } 278 279 SynchronousCompositorOutputSurfaceDelegate* 280 SynchronousCompositorOutputSurface::GetDelegate() { 281 return SynchronousCompositorImpl::FromRoutingID(routing_id_); 282 } 283 284 } // namespace content 285