| // Copyright 2013 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "content/renderer/android/synchronous_compositor_output_surface.h" |
| |
| #include "base/auto_reset.h" |
| #include "base/logging.h" |
| #include "cc/output/compositor_frame.h" |
| #include "cc/output/context_provider.h" |
| #include "cc/output/output_surface_client.h" |
| #include "cc/output/software_output_device.h" |
| #include "content/renderer/android/synchronous_compositor_external_begin_frame_source.h" |
| #include "content/renderer/android/synchronous_compositor_registry.h" |
| #include "content/renderer/gpu/frame_swap_message_queue.h" |
| #include "gpu/command_buffer/client/context_support.h" |
| #include "gpu/command_buffer/client/gles2_interface.h" |
| #include "gpu/command_buffer/common/gpu_memory_allocation.h" |
| #include "third_party/skia/include/core/SkCanvas.h" |
| #include "ui/gfx/geometry/rect_conversions.h" |
| #include "ui/gfx/skia_util.h" |
| #include "ui/gfx/transform.h" |
| |
| namespace content { |
| |
| namespace { |
| |
| // Do not limit number of resources, so use an unrealistically high value. |
| const size_t kNumResourcesLimit = 10 * 1000 * 1000; |
| |
| } // namespace |
| |
| class SynchronousCompositorOutputSurface::SoftwareDevice |
| : public cc::SoftwareOutputDevice { |
| public: |
| SoftwareDevice(SynchronousCompositorOutputSurface* surface) |
| : surface_(surface) { |
| } |
| void Resize(const gfx::Size& pixel_size, float scale_factor) override { |
| // Intentional no-op: canvas size is controlled by the embedder. |
| } |
| SkCanvas* BeginPaint(const gfx::Rect& damage_rect) override { |
| if (!surface_->current_sw_canvas_) { |
| NOTREACHED() << "BeginPaint with no canvas set"; |
| return &null_canvas_; |
| } |
| LOG_IF(WARNING, surface_->frame_holder_.get()) |
| << "Mutliple calls to BeginPaint per frame"; |
| return surface_->current_sw_canvas_; |
| } |
| void EndPaint() override {} |
| |
| private: |
| SynchronousCompositorOutputSurface* surface_; |
| SkCanvas null_canvas_; |
| |
| DISALLOW_COPY_AND_ASSIGN(SoftwareDevice); |
| }; |
| |
| SynchronousCompositorOutputSurface::SynchronousCompositorOutputSurface( |
| const scoped_refptr<cc::ContextProvider>& context_provider, |
| const scoped_refptr<cc::ContextProvider>& worker_context_provider, |
| int routing_id, |
| SynchronousCompositorRegistry* registry, |
| scoped_refptr<FrameSwapMessageQueue> frame_swap_message_queue) |
| : cc::OutputSurface( |
| context_provider, |
| worker_context_provider, |
| scoped_ptr<cc::SoftwareOutputDevice>(new SoftwareDevice(this))), |
| routing_id_(routing_id), |
| registry_(registry), |
| registered_(false), |
| sync_client_(nullptr), |
| next_hardware_draw_needs_damage_(false), |
| current_sw_canvas_(nullptr), |
| memory_policy_(0u), |
| frame_swap_message_queue_(frame_swap_message_queue) { |
| thread_checker_.DetachFromThread(); |
| DCHECK(registry_); |
| capabilities_.adjust_deadline_for_parent = false; |
| capabilities_.delegated_rendering = true; |
| memory_policy_.priority_cutoff_when_visible = |
| gpu::MemoryAllocation::CUTOFF_ALLOW_NICE_TO_HAVE; |
| } |
| |
| SynchronousCompositorOutputSurface::~SynchronousCompositorOutputSurface() {} |
| |
| void SynchronousCompositorOutputSurface::SetSyncClient( |
| SynchronousCompositorOutputSurfaceClient* compositor) { |
| DCHECK(CalledOnValidThread()); |
| sync_client_ = compositor; |
| } |
| |
| bool SynchronousCompositorOutputSurface::BindToClient( |
| cc::OutputSurfaceClient* surface_client) { |
| DCHECK(CalledOnValidThread()); |
| if (!cc::OutputSurface::BindToClient(surface_client)) |
| return false; |
| |
| client_->SetMemoryPolicy(memory_policy_); |
| registry_->RegisterOutputSurface(routing_id_, this); |
| registered_ = true; |
| return true; |
| } |
| |
| void SynchronousCompositorOutputSurface::DetachFromClient() { |
| DCHECK(CalledOnValidThread()); |
| if (registered_) { |
| registry_->UnregisterOutputSurface(routing_id_, this); |
| } |
| cc::OutputSurface::DetachFromClient(); |
| } |
| |
| void SynchronousCompositorOutputSurface::Reshape( |
| const gfx::Size& size, float scale_factor) { |
| // Intentional no-op: surface size is controlled by the embedder. |
| } |
| |
| void SynchronousCompositorOutputSurface::SwapBuffers( |
| cc::CompositorFrame* frame) { |
| DCHECK(CalledOnValidThread()); |
| frame_holder_.reset(new cc::CompositorFrame); |
| frame->AssignTo(frame_holder_.get()); |
| client_->DidSwapBuffers(); |
| } |
| |
| void SynchronousCompositorOutputSurface::Invalidate() { |
| DCHECK(CalledOnValidThread()); |
| if (sync_client_) |
| sync_client_->Invalidate(); |
| } |
| |
| scoped_ptr<cc::CompositorFrame> |
| SynchronousCompositorOutputSurface::DemandDrawHw( |
| const gfx::Size& surface_size, |
| const gfx::Transform& transform, |
| const gfx::Rect& viewport, |
| const gfx::Rect& clip, |
| const gfx::Rect& viewport_rect_for_tile_priority, |
| const gfx::Transform& transform_for_tile_priority) { |
| DCHECK(CalledOnValidThread()); |
| DCHECK(HasClient()); |
| DCHECK(context_provider_.get()); |
| |
| surface_size_ = surface_size; |
| InvokeComposite(transform, viewport, clip, viewport_rect_for_tile_priority, |
| transform_for_tile_priority, true); |
| |
| return frame_holder_.Pass(); |
| } |
| |
| scoped_ptr<cc::CompositorFrame> |
| SynchronousCompositorOutputSurface::DemandDrawSw(SkCanvas* canvas) { |
| DCHECK(CalledOnValidThread()); |
| DCHECK(canvas); |
| DCHECK(!current_sw_canvas_); |
| |
| base::AutoReset<SkCanvas*> canvas_resetter(¤t_sw_canvas_, canvas); |
| |
| SkIRect canvas_clip; |
| canvas->getClipDeviceBounds(&canvas_clip); |
| gfx::Rect clip = gfx::SkIRectToRect(canvas_clip); |
| |
| gfx::Transform transform(gfx::Transform::kSkipInitialization); |
| transform.matrix() = canvas->getTotalMatrix(); // Converts 3x3 matrix to 4x4. |
| |
| surface_size_ = gfx::Size(canvas->getBaseLayerSize().width(), |
| canvas->getBaseLayerSize().height()); |
| |
| // Pass in the cached hw viewport and transform for tile priority to avoid |
| // tile thrashing when the WebView is alternating between hardware and |
| // software draws. |
| InvokeComposite(transform, |
| clip, |
| clip, |
| cached_hw_viewport_rect_for_tile_priority_, |
| cached_hw_transform_for_tile_priority_, |
| false); |
| |
| return frame_holder_.Pass(); |
| } |
| |
| void SynchronousCompositorOutputSurface::InvokeComposite( |
| const gfx::Transform& transform, |
| const gfx::Rect& viewport, |
| const gfx::Rect& clip, |
| const gfx::Rect& viewport_rect_for_tile_priority, |
| const gfx::Transform& transform_for_tile_priority, |
| bool hardware_draw) { |
| DCHECK(!frame_holder_.get()); |
| |
| gfx::Transform adjusted_transform = transform; |
| adjusted_transform.matrix().postTranslate(-viewport.x(), -viewport.y(), 0); |
| SetExternalDrawConstraints(adjusted_transform, viewport, clip, |
| viewport_rect_for_tile_priority, |
| transform_for_tile_priority, !hardware_draw); |
| if (!hardware_draw || next_hardware_draw_needs_damage_) { |
| next_hardware_draw_needs_damage_ = false; |
| SetNeedsRedrawRect(gfx::Rect(viewport.size())); |
| } |
| |
| client_->OnDraw(); |
| |
| // After software draws (which might move the viewport arbitrarily), restore |
| // the previous hardware viewport to allow CC's tile manager to prioritize |
| // properly. |
| if (hardware_draw) { |
| cached_hw_transform_ = adjusted_transform; |
| cached_hw_viewport_ = viewport; |
| cached_hw_clip_ = clip; |
| cached_hw_viewport_rect_for_tile_priority_ = |
| viewport_rect_for_tile_priority; |
| cached_hw_transform_for_tile_priority_ = transform_for_tile_priority; |
| } else { |
| bool resourceless_software_draw = false; |
| SetExternalDrawConstraints(cached_hw_transform_, |
| cached_hw_viewport_, |
| cached_hw_clip_, |
| cached_hw_viewport_rect_for_tile_priority_, |
| cached_hw_transform_for_tile_priority_, |
| resourceless_software_draw); |
| // This draw may have reset all damage, which would lead to subsequent |
| // incorrect hardware draw, so explicitly set damage for next hardware |
| // draw as well. |
| next_hardware_draw_needs_damage_ = true; |
| } |
| |
| if (frame_holder_.get()) |
| client_->DidSwapBuffersComplete(); |
| } |
| |
| void SynchronousCompositorOutputSurface::ReturnResources( |
| const cc::CompositorFrameAck& frame_ack) { |
| ReclaimResources(&frame_ack); |
| } |
| |
| void SynchronousCompositorOutputSurface::SetMemoryPolicy(size_t bytes_limit) { |
| DCHECK(CalledOnValidThread()); |
| bool became_zero = memory_policy_.bytes_limit_when_visible && !bytes_limit; |
| bool became_non_zero = |
| !memory_policy_.bytes_limit_when_visible && bytes_limit; |
| memory_policy_.bytes_limit_when_visible = bytes_limit; |
| memory_policy_.num_resources_limit = kNumResourcesLimit; |
| |
| if (client_) |
| client_->SetMemoryPolicy(memory_policy_); |
| |
| if (became_zero) { |
| // This is small hack to drop context resources without destroying it |
| // when this compositor is put into the background. |
| context_provider()->ContextSupport()->SetAggressivelyFreeResources( |
| true /* aggressively_free_resources */); |
| } else if (became_non_zero) { |
| context_provider()->ContextSupport()->SetAggressivelyFreeResources( |
| false /* aggressively_free_resources */); |
| } |
| } |
| |
| void SynchronousCompositorOutputSurface::SetTreeActivationCallback( |
| const base::Closure& callback) { |
| DCHECK(client_); |
| client_->SetTreeActivationCallback(callback); |
| } |
| |
| void SynchronousCompositorOutputSurface::GetMessagesToDeliver( |
| ScopedVector<IPC::Message>* messages) { |
| DCHECK(CalledOnValidThread()); |
| scoped_ptr<FrameSwapMessageQueue::SendMessageScope> send_message_scope = |
| frame_swap_message_queue_->AcquireSendMessageScope(); |
| frame_swap_message_queue_->DrainMessages(messages); |
| } |
| |
| bool SynchronousCompositorOutputSurface::CalledOnValidThread() const { |
| return thread_checker_.CalledOnValidThread(); |
| } |
| |
| } // namespace content |