diff options
author | jbates@chromium.org <jbates@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-06-03 23:19:30 +0000 |
---|---|---|
committer | jbates@chromium.org <jbates@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-06-03 23:19:30 +0000 |
commit | d6ca9a3a0c76deb0723789d2844e028cf7fa419d (patch) | |
tree | 9373b7e4e04e7e1a79b24aad37d1570f1a1fa0f3 /gpu/command_buffer/service | |
parent | 0f25bfdf7a60ff20d3b3d7572065fe1250bd6bd0 (diff) | |
download | chromium_src-d6ca9a3a0c76deb0723789d2844e028cf7fa419d.zip chromium_src-d6ca9a3a0c76deb0723789d2844e028cf7fa419d.tar.gz chromium_src-d6ca9a3a0c76deb0723789d2844e028cf7fa419d.tar.bz2 |
GpuScheduler fix:
- Ganesh generates a lot of cheap GL commands, so 100 commands were executing in 50us, causing us to spin on PostTask(ProcessCommands) 60+ times per frame. This change simply tracks the elapsed time after each 100 commands - if the time is less than 2ms, it continues on to the next 100 commands.
BUG=83628
TEST=trace FishIE demo with 500 fish, check that ProcessCommands is not called ~60 times per frame.
Review URL: http://codereview.chromium.org/6993032
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@87895 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'gpu/command_buffer/service')
-rw-r--r-- | gpu/command_buffer/service/gpu_scheduler.cc | 68 | ||||
-rw-r--r-- | gpu/command_buffer/service/gpu_scheduler.h | 4 | ||||
-rw-r--r-- | gpu/command_buffer/service/gpu_scheduler_unittest.cc | 7 | ||||
-rw-r--r-- | gpu/command_buffer/service/mocks.cc | 22 | ||||
-rw-r--r-- | gpu/command_buffer/service/mocks.h | 14 |
5 files changed, 87 insertions, 28 deletions
diff --git a/gpu/command_buffer/service/gpu_scheduler.cc b/gpu/command_buffer/service/gpu_scheduler.cc index 151ff9a..5fa9ceb 100644 --- a/gpu/command_buffer/service/gpu_scheduler.cc +++ b/gpu/command_buffer/service/gpu_scheduler.cc @@ -9,6 +9,7 @@ #include "base/compiler_specific.h" #include "base/debug/trace_event.h" #include "base/message_loop.h" +#include "base/time.h" #include "ui/gfx/gl/gl_context.h" #include "ui/gfx/gl/gl_bindings.h" #include "ui/gfx/gl/gl_surface.h" @@ -136,6 +137,7 @@ const unsigned int kMaxOutstandingSwapBuffersCallsPerOnscreenContext = 1; #endif void GpuScheduler::PutChanged(bool sync) { + TRACE_EVENT0("gpu", "GpuScheduler:PutChanged"); CommandBuffer::State state = command_buffer_->GetState(); parser_->set_put(state.put_offset); @@ -151,8 +153,11 @@ void GpuScheduler::ProcessCommands() { if (state.error != error::kNoError) return; - if (unscheduled_count_ > 0) + if (unscheduled_count_ > 0) { + TRACE_EVENT1("gpu", "EarlyOut_Unscheduled", + "unscheduled_count_", unscheduled_count_); return; + } if (decoder_.get()) { if (!decoder_->MakeCurrent()) { @@ -175,32 +180,43 @@ void GpuScheduler::ProcessCommands() { } #endif + base::TimeTicks start_time = base::TimeTicks::Now(); + base::TimeDelta elapsed; + bool is_break = false; error::Error error = error::kNoError; - int commands_processed = 0; - while (commands_processed < commands_per_update_ && - !parser_->IsEmpty()) { - error = parser_->ProcessCommand(); - - // 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. - command_buffer_->SetGetOffset(static_cast<int32>(parser_->get())); - - if (error == error::kWaiting || error == error::kYield) { - break; - } else if (error::IsError(error)) { - command_buffer_->SetParseError(error); - return; + do { + int commands_processed = 0; + while (commands_processed < commands_per_update_ && + !parser_->IsEmpty()) { + error = parser_->ProcessCommand(); + + // 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. + command_buffer_->SetGetOffset(static_cast<int32>(parser_->get())); + + if (error == error::kWaiting || error == error::kYield) { + is_break = true; + break; + } else if (error::IsError(error)) { + command_buffer_->SetParseError(error); + return; + } + + if (unscheduled_count_ > 0) { + is_break = true; + break; + } + + ++commands_processed; + if (command_processed_callback_.get()) { + command_processed_callback_->Run(); + } } - - if (unscheduled_count_ > 0) - break; - - ++commands_processed; - if (command_processed_callback_.get()) { - command_processed_callback_->Run(); - } - } + elapsed = base::TimeTicks::Now() - start_time; + } while(!is_break && + !parser_->IsEmpty() && + elapsed.InMicroseconds() < kMinimumSchedulerQuantumMicros); if (unscheduled_count_ == 0 && error != error::kWaiting && @@ -210,6 +226,8 @@ void GpuScheduler::ProcessCommands() { } void GpuScheduler::SetScheduled(bool scheduled) { + TRACE_EVENT2("gpu", "GpuScheduler:SetScheduled", "scheduled", scheduled, + "unscheduled_count_", unscheduled_count_); if (scheduled) { --unscheduled_count_; DCHECK_GE(unscheduled_count_, 0); diff --git a/gpu/command_buffer/service/gpu_scheduler.h b/gpu/command_buffer/service/gpu_scheduler.h index d75beae..b49713f 100644 --- a/gpu/command_buffer/service/gpu_scheduler.h +++ b/gpu/command_buffer/service/gpu_scheduler.h @@ -39,6 +39,10 @@ class ContextGroup; // posts tasks to the current message loop to do additional work. class GpuScheduler : public CommandBufferEngine { public: + // Scheduler quantum: makes ProcessCommands continue until the specified time + // has passed, or the command buffer yields or runs out of commands. + static const int kMinimumSchedulerQuantumMicros = 2000; + // If a group is not passed in one will be created. GpuScheduler(CommandBuffer* command_buffer, SurfaceManager* surface_manager, diff --git a/gpu/command_buffer/service/gpu_scheduler_unittest.cc b/gpu/command_buffer/service/gpu_scheduler_unittest.cc index 965353c..8cdb361 100644 --- a/gpu/command_buffer/service/gpu_scheduler_unittest.cc +++ b/gpu/command_buffer/service/gpu_scheduler_unittest.cc @@ -44,7 +44,7 @@ class GpuSchedulerTest : public testing::Test { ON_CALL(*command_buffer_.get(), GetState()) .WillByDefault(Return(default_state)); - async_api_.reset(new StrictMock<AsyncAPIMock>); + async_api_.reset(new StrictMock<SpecializedDoCommandAsyncAPIMock>); decoder_ = new gles2::MockGLES2Decoder(); @@ -161,11 +161,12 @@ TEST_F(GpuSchedulerTest, SchedulerSetsTheGLContext) { } TEST_F(GpuSchedulerTest, PostsTaskToFinishRemainingCommands) { + unsigned int pauseCmd = SpecializedDoCommandAsyncAPIMock::kTestQuantumCommand; CommandHeader* header = reinterpret_cast<CommandHeader*>(&buffer_[0]); header[0].command = 7; header[0].size = 2; buffer_[1] = 123; - header[2].command = 8; + header[2].command = pauseCmd; header[2].size = 1; header[3].command = 9; header[3].size = 1; @@ -180,7 +181,7 @@ TEST_F(GpuSchedulerTest, PostsTaskToFinishRemainingCommands) { .WillOnce(Return(error::kNoError)); EXPECT_CALL(*command_buffer_, SetGetOffset(2)); - EXPECT_CALL(*async_api_, DoCommand(8, 0, &buffer_[2])) + EXPECT_CALL(*async_api_, DoCommand(pauseCmd, 0, &buffer_[2])) .WillOnce(Return(error::kNoError)); EXPECT_CALL(*command_buffer_, SetGetOffset(3)); diff --git a/gpu/command_buffer/service/mocks.cc b/gpu/command_buffer/service/mocks.cc index 40b3d6d9..70898b3 100644 --- a/gpu/command_buffer/service/mocks.cc +++ b/gpu/command_buffer/service/mocks.cc @@ -2,6 +2,9 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include "base/threading/thread.h" +#include "base/time.h" +#include "gpu/command_buffer/service/gpu_scheduler.h" #include "gpu/command_buffer/service/mocks.h" namespace gpu { @@ -24,6 +27,25 @@ void AsyncAPIMock::SetToken(unsigned int command, engine_->set_token(args->token); } +SpecializedDoCommandAsyncAPIMock::SpecializedDoCommandAsyncAPIMock() {} + +SpecializedDoCommandAsyncAPIMock::~SpecializedDoCommandAsyncAPIMock() {} + +error::Error SpecializedDoCommandAsyncAPIMock::DoCommand( + unsigned int command, + unsigned int arg_count, + const void* cmd_data) { + if (command == kTestQuantumCommand) { + // Surpass the GpuScheduler scheduling quantum. + base::TimeTicks start_time = base::TimeTicks::Now(); + while ((base::TimeTicks::Now() - start_time).InMicroseconds() < + GpuScheduler::kMinimumSchedulerQuantumMicros) { + base::PlatformThread::Sleep(1); + } + } + return AsyncAPIMock::DoCommand(command, arg_count, cmd_data); +} + namespace gles2 { MockShaderTranslator::MockShaderTranslator() {} diff --git a/gpu/command_buffer/service/mocks.h b/gpu/command_buffer/service/mocks.h index 0d341bd..f526c01 100644 --- a/gpu/command_buffer/service/mocks.h +++ b/gpu/command_buffer/service/mocks.h @@ -69,6 +69,20 @@ class AsyncAPIMock : public AsyncAPIInterface { CommandBufferEngine *engine_; }; +// Allows specialized behavior per command in DoCommand. +class SpecializedDoCommandAsyncAPIMock : public AsyncAPIMock { + public: + // Cause DoCommand to sleep more than the GpuScheduler time quantum. + static const unsigned int kTestQuantumCommand = 333; + + SpecializedDoCommandAsyncAPIMock(); + virtual ~SpecializedDoCommandAsyncAPIMock(); + + virtual error::Error DoCommand(unsigned int command, + unsigned int arg_count, + const void* cmd_data); +}; + namespace gles2 { class MockShaderTranslator : public ShaderTranslatorInterface { |