diff options
author | jbates@chromium.org <jbates@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-06-28 02:11:08 +0000 |
---|---|---|
committer | jbates@chromium.org <jbates@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-06-28 02:11:08 +0000 |
commit | a7266a9dc36aa68143d670eb8646f9d67237fa61 (patch) | |
tree | 783113824e460a2154aec46f401ead1f1ffd8e66 | |
parent | 0390d8c8d8c72dedb6bcb9ef39b96fbb8e6915b2 (diff) | |
download | chromium_src-a7266a9dc36aa68143d670eb8646f9d67237fa61.zip chromium_src-a7266a9dc36aa68143d670eb8646f9d67237fa61.tar.gz chromium_src-a7266a9dc36aa68143d670eb8646f9d67237fa61.tar.bz2 |
Defer descheduling of GPU commands until draw or swap
This CL adds support to Windows and Mac. Linux doesn't gain anything, because the GPU process is blocked on the actual SwapBuffers call (whereas Windows and Mac asynchronously notify the browser to execute the Swap).
The surface gets a chance to defer either Draws or Swaps. The surface can unschedule the GpuScheduler on the first attempt at a draw, for example, and reschedule it later when the draw is safe to execute (ie: when the pending SwapBuffers is acked by the browser).
Platforms with triple-buffered contexts can defer all the way until a Swap, while most platforms will use the draws to defer.
Review URL: https://chromiumcodereview.appspot.com/10389202
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@144648 0039d316-1c4b-4281-b951-d872f2087c98
20 files changed, 246 insertions, 57 deletions
diff --git a/content/browser/gpu/gpu_process_host.cc b/content/browser/gpu/gpu_process_host.cc index 7e55b7b..f716102 100644 --- a/content/browser/gpu/gpu_process_host.cc +++ b/content/browser/gpu/gpu_process_host.cc @@ -27,6 +27,7 @@ #include "content/gpu/gpu_process.h" #include "content/public/browser/browser_thread.h" #include "content/public/browser/content_browser_client.h" +#include "content/public/browser/render_process_host.h" #include "content/public/browser/render_widget_host_view.h" #include "content/public/common/content_switches.h" #include "content/public/common/result_codes.h" @@ -47,6 +48,9 @@ using content::BrowserThread; using content::ChildProcessHost; +using content::RenderProcessHost; +using content::RenderWidgetHost; +using content::RenderWidgetHostImpl; bool GpuProcessHost::gpu_enabled_ = true; bool GpuProcessHost::hardware_gpu_enabled_ = true; @@ -97,27 +101,62 @@ void SendGpuProcessMessage(GpuProcessHost::GpuProcessKind kind, } } +void AcceleratedSurfaceBuffersSwappedCompletedForGPU(int host_id, + int route_id, + bool alive) { + if (!BrowserThread::CurrentlyOn(BrowserThread::IO)) { + BrowserThread::PostTask( + BrowserThread::IO, + FROM_HERE, + base::Bind(&AcceleratedSurfaceBuffersSwappedCompletedForGPU, + host_id, + route_id, + alive)); + return; + } + + GpuProcessHost* host = GpuProcessHost::FromID(host_id); + if (host) { + if (alive) + host->Send(new AcceleratedSurfaceMsg_BufferPresented(route_id, 0)); + else + host->ForceShutdown(); + } +} + +// This sends a ViewMsg_SwapBuffers_ACK directly to the renderer process +// (RenderWidget). This path is currently not used with the threaded compositor. +void AcceleratedSurfaceBuffersSwappedCompletedForRenderer(int surface_id) { + if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) { + BrowserThread::PostTask( + BrowserThread::UI, + FROM_HERE, + base::Bind(&AcceleratedSurfaceBuffersSwappedCompletedForRenderer, + surface_id)); + return; + } + + int render_process_id = 0; + int render_widget_id = 0; + if (!GpuSurfaceTracker::Get()->GetRenderWidgetIDForSurface( + surface_id, &render_process_id, &render_widget_id)) { + return; + } + RenderProcessHost* host = RenderProcessHost::FromID(render_process_id); + if (!host) + return; + RenderWidgetHost* rwh = host->GetRenderWidgetHostByID(render_widget_id); + if (!rwh) + return; + RenderWidgetHostImpl::From(rwh)->AcknowledgeSwapBuffersToRenderer(); +} + void AcceleratedSurfaceBuffersSwappedCompleted(int host_id, int route_id, + int surface_id, bool alive) { - if (BrowserThread::CurrentlyOn(BrowserThread::IO)) { - GpuProcessHost* host = GpuProcessHost::FromID(host_id); - if (host) { - if (alive) - host->Send(new AcceleratedSurfaceMsg_BufferPresented(route_id, 0)); - else { - host->ForceShutdown(); - } - } - } else { - BrowserThread::PostTask( - BrowserThread::IO, - FROM_HERE, - base::Bind(&AcceleratedSurfaceBuffersSwappedCompleted, - host_id, - route_id, - alive)); - } + AcceleratedSurfaceBuffersSwappedCompletedForGPU(host_id, route_id, alive); + AcceleratedSurfaceBuffersSwappedCompletedForRenderer(surface_id); } } // anonymous namespace @@ -569,7 +608,7 @@ void GpuProcessHost::OnAcceleratedSurfaceBuffersSwapped( } base::ScopedClosureRunner scoped_completion_runner( - base::Bind(&AcceleratedSurfaceBuffersSwappedCompleted, + base::Bind(&AcceleratedSurfaceBuffersSwappedCompletedForGPU, host_id_, params.route_id, true)); int render_process_id = 0; @@ -607,6 +646,7 @@ void GpuProcessHost::OnAcceleratedSurfaceBuffersSwapped( base::Bind(&AcceleratedSurfaceBuffersSwappedCompleted, host_id_, params.route_id, + params.surface_id, true)); gfx::PluginWindowHandle handle = @@ -630,7 +670,8 @@ void GpuProcessHost::OnAcceleratedSurfaceBuffersSwapped( params.surface_handle, base::Bind(&AcceleratedSurfaceBuffersSwappedCompleted, host_id_, - params.route_id)); + params.route_id, + params.surface_id)); } void GpuProcessHost::OnAcceleratedSurfacePostSubBuffer( diff --git a/content/browser/renderer_host/render_widget_host_impl.cc b/content/browser/renderer_host/render_widget_host_impl.cc index d4d5982..33652c9 100644 --- a/content/browser/renderer_host/render_widget_host_impl.cc +++ b/content/browser/renderer_host/render_widget_host_impl.cc @@ -1857,6 +1857,16 @@ void RenderWidgetHostImpl::AcknowledgeBufferPresent( sync_point)); } +void RenderWidgetHostImpl::AcknowledgeSwapBuffersToRenderer() { + bool enable_threaded_compositing = + CommandLine::ForCurrentProcess()->HasSwitch( + switches::kEnableThreadedCompositing) && + !CommandLine::ForCurrentProcess()->HasSwitch( + switches::kDisableThreadedCompositing); + if (!enable_threaded_compositing) + Send(new ViewMsg_SwapBuffers_ACK(routing_id_)); +} + void RenderWidgetHostImpl::DelayedAutoResized() { gfx::Size new_size = new_auto_size_; // Clear the new_auto_size_ since the empty value is used as a flag to diff --git a/content/browser/renderer_host/render_widget_host_impl.h b/content/browser/renderer_host/render_widget_host_impl.h index a16eb45..2c97b54 100644 --- a/content/browser/renderer_host/render_widget_host_impl.h +++ b/content/browser/renderer_host/render_widget_host_impl.h @@ -363,6 +363,12 @@ class CONTENT_EXPORT RenderWidgetHostImpl : virtual public RenderWidgetHost, int gpu_host_id, uint32 sync_point); + // Called by the view in response to AcceleratedSurfaceBuffersSwapped for + // platforms that support deferred GPU process descheduling. This does + // nothing if the compositor thread is enabled. + // TODO(jbates) Once the compositor thread is always on, this can be removed. + void AcknowledgeSwapBuffersToRenderer(); + // Signals that the compositing surface was updated, e.g. after a lost context // event. void CompositingSurfaceUpdated(); diff --git a/content/browser/renderer_host/render_widget_host_view_mac.mm b/content/browser/renderer_host/render_widget_host_view_mac.mm index a9a8849..4abbe72 100644 --- a/content/browser/renderer_host/render_widget_host_view_mac.mm +++ b/content/browser/renderer_host/render_widget_host_view_mac.mm @@ -1032,6 +1032,8 @@ void RenderWidgetHostViewMac::AckPendingSwapBuffers() { pending_swap_buffers_acks_.front().first, pending_swap_buffers_acks_.front().second, 0); + if (render_widget_host_) + render_widget_host_->AcknowledgeSwapBuffersToRenderer(); } pending_swap_buffers_acks_.erase(pending_swap_buffers_acks_.begin()); } diff --git a/content/common/gpu/client/webgraphicscontext3d_command_buffer_impl.cc b/content/common/gpu/client/webgraphicscontext3d_command_buffer_impl.cc index 2fa1fa4..a1718e2 100644 --- a/content/common/gpu/client/webgraphicscontext3d_command_buffer_impl.cc +++ b/content/common/gpu/client/webgraphicscontext3d_command_buffer_impl.cc @@ -519,9 +519,20 @@ void WebGraphicsContext3DCommandBufferImpl::prepareTexture() { if (command_buffer_->GetLastState().error == gpu::error::kNoError) gl_->SwapBuffers(); - command_buffer_->Echo(base::Bind( - &WebGraphicsContext3DCommandBufferImpl::OnSwapBuffersComplete, - weak_ptr_factory_.GetWeakPtr())); + bool use_echo_for_swap_ack = true; +#if defined(OS_MACOSX) || defined(OS_WIN) + // Get ViewMsg_SwapBuffers_ACK from browser for single-threaded path. + use_echo_for_swap_ack = + CommandLine::ForCurrentProcess()->HasSwitch( + switches::kEnableThreadedCompositing) && + !CommandLine::ForCurrentProcess()->HasSwitch( + switches::kDisableThreadedCompositing); +#endif + if (use_echo_for_swap_ack) { + command_buffer_->Echo(base::Bind( + &WebGraphicsContext3DCommandBufferImpl::OnSwapBuffersComplete, + weak_ptr_factory_.GetWeakPtr())); + } #if defined(OS_MACOSX) // It appears that making the compositor's on-screen context current on // other platforms implies this flush. TODO(kbr): this means that the diff --git a/content/common/gpu/image_transport_surface_mac.cc b/content/common/gpu/image_transport_surface_mac.cc index 52263e5..eed9e77 100644 --- a/content/common/gpu/image_transport_surface_mac.cc +++ b/content/common/gpu/image_transport_surface_mac.cc @@ -30,6 +30,7 @@ class IOSurfaceImageTransportSurface : public gfx::NoOpGLSurfaceCGL, // GLSurface implementation virtual bool Initialize() OVERRIDE; virtual void Destroy() OVERRIDE; + virtual bool DeferDraws() OVERRIDE; virtual bool IsOffscreen() OVERRIDE; virtual bool SwapBuffers() OVERRIDE; virtual bool PostSubBuffer(int x, int y, int width, int height) OVERRIDE; @@ -75,6 +76,12 @@ class IOSurfaceImageTransportSurface : public gfx::NoOpGLSurfaceCGL, // Whether or not we've successfully made the surface current once. bool made_current_; + // Whether a SwapBuffers is pending. + bool is_swap_buffers_pending_; + + // Whether we unscheduled command buffer because of pending SwapBuffers. + bool did_unschedule_; + scoped_ptr<ImageTransportHelper> helper_; DISALLOW_COPY_AND_ASSIGN(IOSurfaceImageTransportSurface); @@ -106,7 +113,9 @@ IOSurfaceImageTransportSurface::IOSurfaceImageTransportSurface( texture_id_(0), io_surface_handle_(0), context_(NULL), - made_current_(false) { + made_current_(false), + is_swap_buffers_pending_(false), + did_unschedule_(false) { helper_.reset(new ImageTransportHelper(this, manager, stub, handle)); } @@ -142,6 +151,20 @@ void IOSurfaceImageTransportSurface::Destroy() { NoOpGLSurfaceCGL::Destroy(); } +bool IOSurfaceImageTransportSurface::DeferDraws() { + // The command buffer hit a draw/clear command that could clobber the + // IOSurface in use by an earlier SwapBuffers. If a Swap is pending, abort + // processing of the command by returning true and unschedule until the Swap + // Ack arrives. + DCHECK(!did_unschedule_); + if (is_swap_buffers_pending_) { + did_unschedule_ = true; + helper_->SetScheduled(false); + return true; + } + return false; +} + bool IOSurfaceImageTransportSurface::IsOffscreen() { return false; } @@ -199,7 +222,8 @@ bool IOSurfaceImageTransportSurface::SwapBuffers() { params.surface_handle = io_surface_handle_; helper_->SendAcceleratedSurfaceBuffersSwapped(params); - helper_->SetScheduled(false); + DCHECK(!is_swap_buffers_pending_); + is_swap_buffers_pending_ = true; return true; } @@ -218,7 +242,8 @@ bool IOSurfaceImageTransportSurface::PostSubBuffer( params.height = height; helper_->SendAcceleratedSurfacePostSubBuffer(params); - helper_->SetScheduled(false); + DCHECK(!is_swap_buffers_pending_); + is_swap_buffers_pending_ = true; return true; } @@ -235,7 +260,12 @@ gfx::Size IOSurfaceImageTransportSurface::GetSize() { } void IOSurfaceImageTransportSurface::OnBufferPresented(uint32 sync_point) { - helper_->SetScheduled(true); + DCHECK(is_swap_buffers_pending_); + is_swap_buffers_pending_ = false; + if (did_unschedule_) { + did_unschedule_ = false; + helper_->SetScheduled(true); + } } void IOSurfaceImageTransportSurface::OnNewSurfaceACK( diff --git a/content/common/gpu/image_transport_surface_win.cc b/content/common/gpu/image_transport_surface_win.cc index 80630e6..cc6dc87 100644 --- a/content/common/gpu/image_transport_surface_win.cc +++ b/content/common/gpu/image_transport_surface_win.cc @@ -36,6 +36,7 @@ class PbufferImageTransportSurface // gfx::GLSurface implementation virtual bool Initialize() OVERRIDE; virtual void Destroy() OVERRIDE; + virtual bool DeferDraws() OVERRIDE; virtual bool IsOffscreen() OVERRIDE; virtual bool SwapBuffers() OVERRIDE; virtual bool PostSubBuffer(int x, int y, int width, int height) OVERRIDE; @@ -60,6 +61,12 @@ class PbufferImageTransportSurface bool backbuffer_suggested_allocation_; bool frontbuffer_suggested_allocation_; + // Whether a SwapBuffers is pending. + bool is_swap_buffers_pending_; + + // Whether we unscheduled command buffer because of pending SwapBuffers. + bool did_unschedule_; + // Size to resize to when the surface becomes visible. gfx::Size visible_size_; @@ -73,7 +80,9 @@ PbufferImageTransportSurface::PbufferImageTransportSurface( GpuCommandBufferStub* stub) : GLSurfaceAdapter(new gfx::PbufferGLSurfaceEGL(false, gfx::Size(1, 1))), backbuffer_suggested_allocation_(true), - frontbuffer_suggested_allocation_(true) { + frontbuffer_suggested_allocation_(true), + is_swap_buffers_pending_(false), + did_unschedule_(false) { helper_.reset(new ImageTransportHelper(this, manager, stub, @@ -102,6 +111,20 @@ void PbufferImageTransportSurface::Destroy() { GLSurfaceAdapter::Destroy(); } +bool PbufferImageTransportSurface::DeferDraws() { + // The command buffer hit a draw/clear command that could clobber the + // IOSurface in use by an earlier SwapBuffers. If a Swap is pending, abort + // processing of the command by returning true and unschedule until the Swap + // Ack arrives. + DCHECK(!did_unschedule_); + if (is_swap_buffers_pending_) { + did_unschedule_ = true; + helper_->SetScheduled(false); + return true; + } + return false; +} + bool PbufferImageTransportSurface::IsOffscreen() { return false; } @@ -115,6 +138,10 @@ bool PbufferImageTransportSurface::SwapBuffers() { if (!surface_handle) return false; + // Don't send the surface to the browser until we hit the fence that + // indicates the drawing to the surface has been completed. + // TODO(jbates) unscheduling should be deferred until draw commands from the + // next frame -- otherwise the GPU is potentially sitting idle. helper_->DeferToFence(base::Bind( &PbufferImageTransportSurface::SendBuffersSwapped, AsWeakPtr())); @@ -171,11 +198,16 @@ void PbufferImageTransportSurface::SendBuffersSwapped() { params.size = GetSize(); helper_->SendAcceleratedSurfaceBuffersSwapped(params); - helper_->SetScheduled(false); + DCHECK(!is_swap_buffers_pending_); + is_swap_buffers_pending_ = true; } void PbufferImageTransportSurface::OnBufferPresented(uint32 sync_point) { - helper_->SetScheduled(true); + is_swap_buffers_pending_ = false; + if (did_unschedule_) { + did_unschedule_ = false; + helper_->SetScheduled(true); + } } void PbufferImageTransportSurface::OnNewSurfaceACK( diff --git a/content/common/view_messages.h b/content/common/view_messages.h index f160752..1ec6b9b 100644 --- a/content/common/view_messages.h +++ b/content/common/view_messages.h @@ -837,6 +837,14 @@ IPC_MESSAGE_ROUTED4(ViewMsg_PaintAtSize, // This signals the render view that it can send another UpdateRect message. IPC_MESSAGE_ROUTED0(ViewMsg_UpdateRect_ACK) +// Tells the render view that a SwapBuffers was completed. Typically, +// SwapBuffers requests go from renderer -> GPU process -> browser. Most +// platforms still use the GfxCxt3D Echo for receiving the SwapBuffers Ack. +// Using Echo routes the ack from browser -> GPU process -> renderer, while this +// Ack goes directly from browser -> renderer. This is not used for the threaded +// compositor path. +IPC_MESSAGE_ROUTED0(ViewMsg_SwapBuffers_ACK) + // Message payload includes: // 1. A blob that should be cast to WebInputEvent // 2. An optional boolean value indicating if a RawKeyDown event is associated diff --git a/content/renderer/render_widget.cc b/content/renderer/render_widget.cc index ddfde20..fbba3d6 100644 --- a/content/renderer/render_widget.cc +++ b/content/renderer/render_widget.cc @@ -234,6 +234,7 @@ bool RenderWidget::OnMessageReceived(const IPC::Message& message) { IPC_MESSAGE_HANDLER(ViewMsg_WasRestored, OnWasRestored) IPC_MESSAGE_HANDLER(ViewMsg_WasSwappedOut, OnWasSwappedOut) IPC_MESSAGE_HANDLER(ViewMsg_UpdateRect_ACK, OnUpdateRectAck) + IPC_MESSAGE_HANDLER(ViewMsg_SwapBuffers_ACK, OnSwapBuffersComplete) IPC_MESSAGE_HANDLER(ViewMsg_HandleInputEvent, OnHandleInputEvent) IPC_MESSAGE_HANDLER(ViewMsg_MouseCaptureLost, OnMouseCaptureLost) IPC_MESSAGE_HANDLER(ViewMsg_SetFocus, OnSetFocus) diff --git a/gpu/command_buffer/build_gles2_cmd_buffer.py b/gpu/command_buffer/build_gles2_cmd_buffer.py index 3426e3f..af6d353 100755 --- a/gpu/command_buffer/build_gles2_cmd_buffer.py +++ b/gpu/command_buffer/build_gles2_cmd_buffer.py @@ -872,7 +872,10 @@ _FUNCTION_INFO = { 'error_value': 'GL_FRAMEBUFFER_UNSUPPORTED', 'result': ['GLenum'], }, - 'Clear': {'decoder_func': 'DoClear'}, + 'Clear': { + 'type': 'Manual', + 'cmd_args': 'GLbitfield mask' + }, 'ClearColor': {'decoder_func': 'DoClearColor'}, 'ClearDepthf': { 'decoder_func': 'DoClearDepthf', diff --git a/gpu/command_buffer/client/gles2_implementation.cc b/gpu/command_buffer/client/gles2_implementation.cc index 953f87f..ca3a9e7 100644 --- a/gpu/command_buffer/client/gles2_implementation.cc +++ b/gpu/command_buffer/client/gles2_implementation.cc @@ -971,6 +971,12 @@ GLuint GLES2Implementation::GetMaxValueInBufferCHROMIUM( return result; } +void GLES2Implementation::Clear(GLbitfield mask) { + GPU_CLIENT_SINGLE_THREAD_CHECK(); + GPU_CLIENT_LOG("[" << this << "] glClear(" << mask << ")"); + helper_->Clear(mask); +} + void GLES2Implementation::DrawElements( GLenum mode, GLsizei count, GLenum type, const void* indices) { GPU_CLIENT_SINGLE_THREAD_CHECK(); diff --git a/gpu/command_buffer/client/gles2_implementation_autogen.h b/gpu/command_buffer/client/gles2_implementation_autogen.h index ee673ce..5432345 100644 --- a/gpu/command_buffer/client/gles2_implementation_autogen.h +++ b/gpu/command_buffer/client/gles2_implementation_autogen.h @@ -120,11 +120,7 @@ GLenum CheckFramebufferStatus(GLenum target) { return *result; } -void Clear(GLbitfield mask) { - GPU_CLIENT_SINGLE_THREAD_CHECK(); - GPU_CLIENT_LOG("[" << this << "] glClear(" << mask << ")"); - helper_->Clear(mask); -} +void Clear(GLbitfield mask); void ClearColor(GLclampf red, GLclampf green, GLclampf blue, GLclampf alpha) { GPU_CLIENT_SINGLE_THREAD_CHECK(); diff --git a/gpu/command_buffer/common/constants.h b/gpu/command_buffer/common/constants.h index 38a8c32..a2eeb74 100644 --- a/gpu/command_buffer/common/constants.h +++ b/gpu/command_buffer/common/constants.h @@ -1,4 +1,4 @@ -// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// 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. @@ -21,12 +21,13 @@ namespace error { kUnknownCommand, kInvalidArguments, kLostContext, - kGenericError + kGenericError, + kDeferCommandUntilLater }; // Return true if the given error code is an actual error. inline bool IsError(Error error) { - return error != kNoError; + return error != kNoError && error != kDeferCommandUntilLater; } // Provides finer grained information about why the context was lost. diff --git a/gpu/command_buffer/service/cmd_parser.cc b/gpu/command_buffer/service/cmd_parser.cc index a58d5a5..0ca1e61 100644 --- a/gpu/command_buffer/service/cmd_parser.cc +++ b/gpu/command_buffer/service/cmd_parser.cc @@ -79,7 +79,7 @@ error::Error CommandParser::ProcessCommand() { } // If get was not set somewhere else advance it. - if (get == get_) + if (get == get_ && result != error::kDeferCommandUntilLater) get_ = (get + header.size) % entry_count_; if (trace_gl_commands_) diff --git a/gpu/command_buffer/service/gles2_cmd_decoder.cc b/gpu/command_buffer/service/gles2_cmd_decoder.cc index bc7b9b8..0f2ef5c 100644 --- a/gpu/command_buffer/service/gles2_cmd_decoder.cc +++ b/gpu/command_buffer/service/gles2_cmd_decoder.cc @@ -1003,7 +1003,7 @@ class GLES2DecoderImpl : public base::SupportsWeakPtr<GLES2DecoderImpl>, GLenum DoCheckFramebufferStatus(GLenum target); // Wrapper for glClear - void DoClear(GLbitfield mask); + error::Error DoClear(GLbitfield mask); // Wrappers for clear and mask settings functions. void DoClearColor( @@ -1356,6 +1356,12 @@ class GLES2DecoderImpl : public base::SupportsWeakPtr<GLES2DecoderImpl>, void RenderWarning(const std::string& msg); void PerformanceWarning(const std::string& msg); + bool ShouldDeferDraws() { + return !offscreen_target_frame_buffer_.get() && + bound_draw_framebuffer_ == NULL && + surface_->DeferDraws(); + } + // Generate a member function prototype for each command in an automated and // typesafe way. #define GLES2_CMD_OP(name) \ @@ -4243,13 +4249,22 @@ error::Error GLES2DecoderImpl::HandleRegisterSharedIdsCHROMIUM( return error::kNoError; } -void GLES2DecoderImpl::DoClear(GLbitfield mask) { +error::Error GLES2DecoderImpl::DoClear(GLbitfield mask) { + if (ShouldDeferDraws()) + return error::kDeferCommandUntilLater; if (CheckBoundFramebuffersValid("glClear")) { UNSHIPPED_TRACE_EVENT_INSTANT2("test_gpu", "DoClear", "red", clear_red_, "green", clear_green_); ApplyDirtyState(); glClear(mask); } + return error::kNoError; +} + +error::Error GLES2DecoderImpl::HandleClear( + uint32 immediate_data_size, const gles2::Clear& c) { + GLbitfield mask = static_cast<GLbitfield>(c.mask); + return DoClear(mask); } void GLES2DecoderImpl::DoFramebufferRenderbuffer( @@ -5492,6 +5507,8 @@ error::Error GLES2DecoderImpl::DoDrawArrays( GLint first, GLsizei count, GLsizei primcount) { + if (ShouldDeferDraws()) + return error::kDeferCommandUntilLater; if (!validators_->draw_mode.IsValid(mode)) { SetGLError(GL_INVALID_ENUM, function_name, "mode GL_INVALID_ENUM"); return error::kNoError; @@ -5593,6 +5610,8 @@ error::Error GLES2DecoderImpl::DoDrawElements( GLenum type, int32 offset, GLsizei primcount) { + if (ShouldDeferDraws()) + return error::kDeferCommandUntilLater; if (!bound_element_array_buffer_) { SetGLError(GL_INVALID_OPERATION, function_name, "No element array buffer bound"); @@ -8204,6 +8223,10 @@ error::Error GLES2DecoderImpl::HandleShaderBinary( error::Error GLES2DecoderImpl::HandleSwapBuffers( uint32 immediate_data_size, const gles2::SwapBuffers& c) { bool is_offscreen = !!offscreen_target_frame_buffer_.get(); + if (!is_offscreen && surface_->DeferSwapBuffers()) { + return error::kDeferCommandUntilLater; + } + int this_frame_number = frame_number_++; // TRACE_EVENT for gpu tests: TRACE_EVENT_INSTANT2("test_gpu", "SwapBuffers", diff --git a/gpu/command_buffer/service/gles2_cmd_decoder_autogen.h b/gpu/command_buffer/service/gles2_cmd_decoder_autogen.h index ab06503..dc85cc2 100644 --- a/gpu/command_buffer/service/gles2_cmd_decoder_autogen.h +++ b/gpu/command_buffer/service/gles2_cmd_decoder_autogen.h @@ -223,13 +223,6 @@ error::Error GLES2DecoderImpl::HandleCheckFramebufferStatus( return error::kNoError; } -error::Error GLES2DecoderImpl::HandleClear( - uint32 immediate_data_size, const gles2::Clear& c) { - GLbitfield mask = static_cast<GLbitfield>(c.mask); - DoClear(mask); - return error::kNoError; -} - error::Error GLES2DecoderImpl::HandleClearColor( uint32 immediate_data_size, const gles2::ClearColor& c) { GLclampf red = static_cast<GLclampf>(c.red); diff --git a/gpu/command_buffer/service/gles2_cmd_decoder_unittest_1_autogen.h b/gpu/command_buffer/service/gles2_cmd_decoder_unittest_1_autogen.h index 495b16a..bd7dbd7 100644 --- a/gpu/command_buffer/service/gles2_cmd_decoder_unittest_1_autogen.h +++ b/gpu/command_buffer/service/gles2_cmd_decoder_unittest_1_autogen.h @@ -290,15 +290,8 @@ TEST_F(GLES2DecoderTest1, CheckFramebufferStatusInvalidArgsBadSharedMemoryId) { cmd.Init(GL_FRAMEBUFFER, shared_memory_id_, kInvalidSharedMemoryOffset); EXPECT_EQ(error::kOutOfBounds, ExecuteCmd(cmd)); } +// TODO(gman): Clear -TEST_F(GLES2DecoderTest1, ClearValidArgs) { - EXPECT_CALL(*gl_, Clear(1)); - SpecializedSetup<Clear, 0>(true); - Clear cmd; - cmd.Init(1); - EXPECT_EQ(error::kNoError, ExecuteCmd(cmd)); - EXPECT_EQ(GL_NO_ERROR, GetGLError()); -} TEST_F(GLES2DecoderTest1, ClearColorValidArgs) { EXPECT_CALL(*gl_, ClearColor(1, 2, 3, 4)); diff --git a/gpu/command_buffer/service/gpu_scheduler.cc b/gpu/command_buffer/service/gpu_scheduler.cc index 85d4f75..de83617 100644 --- a/gpu/command_buffer/service/gpu_scheduler.cc +++ b/gpu/command_buffer/service/gpu_scheduler.cc @@ -80,6 +80,11 @@ void GpuScheduler::PutChanged() { error = parser_->ProcessCommand(); + if (error == error::kDeferCommandUntilLater) { + DCHECK(unscheduled_count_ > 0); + return; + } + // TODO(piman): various classes duplicate various pieces of state, leading // to needlessly complex update logic. It should be possible to simply // share the state across all of them. diff --git a/ui/gl/gl_surface.cc b/ui/gl/gl_surface.cc index b9040ec..b51d747 100644 --- a/ui/gl/gl_surface.cc +++ b/ui/gl/gl_surface.cc @@ -84,6 +84,14 @@ bool GLSurface::Resize(const gfx::Size& size) { return false; } +bool GLSurface::DeferDraws() { + return false; +} + +bool GLSurface::DeferSwapBuffers() { + return false; +} + std::string GLSurface::GetExtensions() { // Use of GLSurfaceAdapter class means that we can't compare // GetCurrent() and this directly. @@ -166,6 +174,14 @@ bool GLSurfaceAdapter::Resize(const gfx::Size& size) { return surface_->Resize(size); } +bool GLSurfaceAdapter::DeferDraws() { + return surface_->DeferDraws(); +} + +bool GLSurfaceAdapter::DeferSwapBuffers() { + return surface_->DeferSwapBuffers(); +} + bool GLSurfaceAdapter::IsOffscreen() { return surface_->IsOffscreen(); } diff --git a/ui/gl/gl_surface.h b/ui/gl/gl_surface.h index 5872ed0..7b0faee 100644 --- a/ui/gl/gl_surface.h +++ b/ui/gl/gl_surface.h @@ -39,6 +39,16 @@ class GL_EXPORT GLSurface : public base::RefCounted<GLSurface> { virtual bool Resize(const gfx::Size& size); + // Unschedule the GpuScheduler and return true to abort the processing of + // a GL draw call to this surface and defer it until the GpuScheduler is + // rescheduled. + virtual bool DeferDraws(); + + // Unschedule the GpuScheduler and return true to abort the processing of + // a GL SwapBuffers call to this surface and defer it until the GpuScheduler + // is rescheduled. + virtual bool DeferSwapBuffers(); + // Returns true if this surface is offscreen. virtual bool IsOffscreen() = 0; @@ -126,6 +136,8 @@ class GL_EXPORT GLSurfaceAdapter : public GLSurface { virtual bool Initialize() OVERRIDE; virtual void Destroy() OVERRIDE; virtual bool Resize(const gfx::Size& size) OVERRIDE; + virtual bool DeferDraws() OVERRIDE; + virtual bool DeferSwapBuffers() OVERRIDE; virtual bool IsOffscreen() OVERRIDE; virtual bool SwapBuffers() OVERRIDE; virtual bool PostSubBuffer(int x, int y, int width, int height) OVERRIDE; |