| // Copyright (c) 2012 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/gpu/compositor_output_surface.h" |
| |
| #include "base/command_line.h" |
| #include "base/message_loop/message_loop_proxy.h" |
| #include "cc/output/compositor_frame.h" |
| #include "cc/output/compositor_frame_ack.h" |
| #include "cc/output/managed_memory_policy.h" |
| #include "cc/output/output_surface_client.h" |
| #include "content/common/gpu/client/command_buffer_proxy_impl.h" |
| #include "content/common/gpu/client/context_provider_command_buffer.h" |
| #include "content/common/gpu/client/webgraphicscontext3d_command_buffer_impl.h" |
| #include "content/common/view_messages.h" |
| #include "content/public/common/content_switches.h" |
| #include "content/renderer/render_thread_impl.h" |
| #include "gpu/command_buffer/client/context_support.h" |
| #include "gpu/command_buffer/client/gles2_interface.h" |
| #include "ipc/ipc_forwarding_message_filter.h" |
| #include "ipc/ipc_sync_channel.h" |
| |
| namespace { |
| // There are several compositor surfaces in a process, but they share the same |
| // compositor thread, so we use a simple int here to track prefer-smoothness. |
| int g_prefer_smoothness_count = 0; |
| } // namespace |
| |
| namespace content { |
| |
| //------------------------------------------------------------------------------ |
| |
| // static |
| IPC::ForwardingMessageFilter* CompositorOutputSurface::CreateFilter( |
| base::TaskRunner* target_task_runner) |
| { |
| uint32 messages_to_filter[] = { |
| ViewMsg_UpdateVSyncParameters::ID, |
| ViewMsg_SwapCompositorFrameAck::ID, |
| ViewMsg_ReclaimCompositorResources::ID, |
| #if defined(OS_ANDROID) |
| ViewMsg_BeginFrame::ID |
| #endif |
| }; |
| |
| return new IPC::ForwardingMessageFilter( |
| messages_to_filter, arraysize(messages_to_filter), |
| target_task_runner); |
| } |
| |
| CompositorOutputSurface::CompositorOutputSurface( |
| int32 routing_id, |
| uint32 output_surface_id, |
| const scoped_refptr<ContextProviderCommandBuffer>& context_provider, |
| scoped_ptr<cc::SoftwareOutputDevice> software_device, |
| bool use_swap_compositor_frame_message) |
| : OutputSurface(context_provider, software_device.Pass()), |
| output_surface_id_(output_surface_id), |
| use_swap_compositor_frame_message_(use_swap_compositor_frame_message), |
| output_surface_filter_( |
| RenderThreadImpl::current()->compositor_output_surface_filter()), |
| routing_id_(routing_id), |
| prefers_smoothness_(false), |
| #if defined(OS_WIN) |
| // TODO(epenner): Implement PlatformThread::CurrentHandle() on windows. |
| main_thread_handle_(base::PlatformThreadHandle()), |
| #else |
| main_thread_handle_(base::PlatformThread::CurrentHandle()), |
| #endif |
| layout_test_mode_(RenderThreadImpl::current()->layout_test_mode()), |
| weak_ptrs_(this) { |
| DCHECK(output_surface_filter_.get()); |
| DetachFromThread(); |
| message_sender_ = RenderThreadImpl::current()->sync_message_filter(); |
| DCHECK(message_sender_.get()); |
| if (OutputSurface::software_device()) |
| capabilities_.max_frames_pending = 1; |
| } |
| |
| CompositorOutputSurface::~CompositorOutputSurface() { |
| DCHECK(CalledOnValidThread()); |
| SetNeedsBeginFrame(false); |
| if (!HasClient()) |
| return; |
| UpdateSmoothnessTakesPriority(false); |
| if (output_surface_proxy_.get()) |
| output_surface_proxy_->ClearOutputSurface(); |
| output_surface_filter_->RemoveRoute(routing_id_); |
| } |
| |
| bool CompositorOutputSurface::BindToClient( |
| cc::OutputSurfaceClient* client) { |
| DCHECK(CalledOnValidThread()); |
| |
| if (!cc::OutputSurface::BindToClient(client)) |
| return false; |
| |
| output_surface_proxy_ = new CompositorOutputSurfaceProxy(this); |
| output_surface_filter_->AddRoute( |
| routing_id_, |
| base::Bind(&CompositorOutputSurfaceProxy::OnMessageReceived, |
| output_surface_proxy_)); |
| |
| if (!context_provider()) { |
| // Without a GPU context, the memory policy otherwise wouldn't be set. |
| client->SetMemoryPolicy(cc::ManagedMemoryPolicy( |
| 128 * 1024 * 1024, |
| gpu::MemoryAllocation::CUTOFF_ALLOW_NICE_TO_HAVE, |
| base::SharedMemory::GetHandleLimit() / 3)); |
| } |
| |
| return true; |
| } |
| |
| void CompositorOutputSurface::ShortcutSwapAck( |
| uint32 output_surface_id, |
| scoped_ptr<cc::GLFrameData> gl_frame_data, |
| scoped_ptr<cc::SoftwareFrameData> software_frame_data) { |
| if (!layout_test_previous_frame_ack_) { |
| layout_test_previous_frame_ack_.reset(new cc::CompositorFrameAck); |
| layout_test_previous_frame_ack_->gl_frame_data.reset(new cc::GLFrameData); |
| } |
| |
| OnSwapAck(output_surface_id, *layout_test_previous_frame_ack_); |
| |
| layout_test_previous_frame_ack_->gl_frame_data = gl_frame_data.Pass(); |
| layout_test_previous_frame_ack_->last_software_frame_id = |
| software_frame_data ? software_frame_data->id : 0; |
| } |
| |
| void CompositorOutputSurface::SwapBuffers(cc::CompositorFrame* frame) { |
| if (layout_test_mode_ && use_swap_compositor_frame_message_) { |
| // This code path is here to support layout tests that are currently |
| // doing a readback in the renderer instead of the browser. So they |
| // are using deprecated code paths in the renderer and don't need to |
| // actually swap anything to the browser. We shortcut the swap to the |
| // browser here and just ack directly within the renderer process. |
| // Once crbug.com/311404 is fixed, this can be removed. |
| |
| // This would indicate that crbug.com/311404 is being fixed, and this |
| // block needs to be removed. |
| DCHECK(!frame->delegated_frame_data); |
| |
| base::Closure closure = |
| base::Bind(&CompositorOutputSurface::ShortcutSwapAck, |
| weak_ptrs_.GetWeakPtr(), |
| output_surface_id_, |
| base::Passed(&frame->gl_frame_data), |
| base::Passed(&frame->software_frame_data)); |
| |
| if (context_provider()) { |
| gpu::gles2::GLES2Interface* context = context_provider()->ContextGL(); |
| context->Flush(); |
| uint32 sync_point = context->InsertSyncPointCHROMIUM(); |
| context_provider()->ContextSupport()->SignalSyncPoint(sync_point, |
| closure); |
| } else { |
| base::MessageLoopProxy::current()->PostTask(FROM_HERE, closure); |
| } |
| client_->DidSwapBuffers(); |
| return; |
| } |
| |
| if (use_swap_compositor_frame_message_) { |
| Send(new ViewHostMsg_SwapCompositorFrame(routing_id_, |
| output_surface_id_, |
| *frame)); |
| client_->DidSwapBuffers(); |
| return; |
| } |
| |
| if (frame->gl_frame_data) { |
| context_provider()->ContextGL()->ShallowFlushCHROMIUM(); |
| ContextProviderCommandBuffer* provider_command_buffer = |
| static_cast<ContextProviderCommandBuffer*>(context_provider().get()); |
| CommandBufferProxyImpl* command_buffer_proxy = |
| provider_command_buffer->GetCommandBufferProxy(); |
| DCHECK(command_buffer_proxy); |
| command_buffer_proxy->SetLatencyInfo(frame->metadata.latency_info); |
| } |
| |
| OutputSurface::SwapBuffers(frame); |
| } |
| |
| void CompositorOutputSurface::OnMessageReceived(const IPC::Message& message) { |
| DCHECK(CalledOnValidThread()); |
| if (!HasClient()) |
| return; |
| IPC_BEGIN_MESSAGE_MAP(CompositorOutputSurface, message) |
| IPC_MESSAGE_HANDLER(ViewMsg_UpdateVSyncParameters, |
| OnUpdateVSyncParametersFromBrowser); |
| IPC_MESSAGE_HANDLER(ViewMsg_SwapCompositorFrameAck, OnSwapAck); |
| IPC_MESSAGE_HANDLER(ViewMsg_ReclaimCompositorResources, OnReclaimResources); |
| #if defined(OS_ANDROID) |
| IPC_MESSAGE_HANDLER(ViewMsg_BeginFrame, OnBeginFrame); |
| #endif |
| IPC_END_MESSAGE_MAP() |
| } |
| |
| void CompositorOutputSurface::OnUpdateVSyncParametersFromBrowser( |
| base::TimeTicks timebase, |
| base::TimeDelta interval) { |
| DCHECK(CalledOnValidThread()); |
| CommitVSyncParameters(timebase, interval); |
| } |
| |
| #if defined(OS_ANDROID) |
| void CompositorOutputSurface::SetNeedsBeginFrame(bool enable) { |
| DCHECK(CalledOnValidThread()); |
| Send(new ViewHostMsg_SetNeedsBeginFrame(routing_id_, enable)); |
| } |
| |
| void CompositorOutputSurface::OnBeginFrame(const cc::BeginFrameArgs& args) { |
| DCHECK(CalledOnValidThread()); |
| client_->BeginFrame(args); |
| } |
| #endif // defined(OS_ANDROID) |
| |
| void CompositorOutputSurface::OnSwapAck(uint32 output_surface_id, |
| const cc::CompositorFrameAck& ack) { |
| // Ignore message if it's a stale one coming from a different output surface |
| // (e.g. after a lost context). |
| if (output_surface_id != output_surface_id_) |
| return; |
| ReclaimResources(&ack); |
| client_->DidSwapBuffersComplete(); |
| } |
| |
| void CompositorOutputSurface::OnReclaimResources( |
| uint32 output_surface_id, |
| const cc::CompositorFrameAck& ack) { |
| // Ignore message if it's a stale one coming from a different output surface |
| // (e.g. after a lost context). |
| if (output_surface_id != output_surface_id_) |
| return; |
| ReclaimResources(&ack); |
| } |
| |
| bool CompositorOutputSurface::Send(IPC::Message* message) { |
| return message_sender_->Send(message); |
| } |
| |
| namespace { |
| #if defined(OS_ANDROID) |
| void SetThreadPriorityToIdle(base::PlatformThreadHandle handle) { |
| base::PlatformThread::SetThreadPriority( |
| handle, base::kThreadPriority_Background); |
| } |
| void SetThreadPriorityToDefault(base::PlatformThreadHandle handle) { |
| base::PlatformThread::SetThreadPriority( |
| handle, base::kThreadPriority_Normal); |
| } |
| #else |
| void SetThreadPriorityToIdle(base::PlatformThreadHandle handle) {} |
| void SetThreadPriorityToDefault(base::PlatformThreadHandle handle) {} |
| #endif |
| } |
| |
| void CompositorOutputSurface::UpdateSmoothnessTakesPriority( |
| bool prefers_smoothness) { |
| #ifndef NDEBUG |
| // If we use different compositor threads, we need to |
| // use an atomic int to track prefer smoothness count. |
| base::PlatformThreadId g_last_thread = base::PlatformThread::CurrentId(); |
| DCHECK_EQ(g_last_thread, base::PlatformThread::CurrentId()); |
| #endif |
| if (prefers_smoothness_ == prefers_smoothness) |
| return; |
| // If this is the first surface to start preferring smoothness, |
| // Throttle the main thread's priority. |
| if (prefers_smoothness_ == false && |
| ++g_prefer_smoothness_count == 1) { |
| SetThreadPriorityToIdle(main_thread_handle_); |
| } |
| // If this is the last surface to stop preferring smoothness, |
| // Reset the main thread's priority to the default. |
| if (prefers_smoothness_ == true && |
| --g_prefer_smoothness_count == 0) { |
| SetThreadPriorityToDefault(main_thread_handle_); |
| } |
| prefers_smoothness_ = prefers_smoothness; |
| } |
| |
| } // namespace content |