summaryrefslogtreecommitdiffstats
path: root/gpu/command_buffer/service
diff options
context:
space:
mode:
authorjbates@chromium.org <jbates@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-06-03 23:19:30 +0000
committerjbates@chromium.org <jbates@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-06-03 23:19:30 +0000
commitd6ca9a3a0c76deb0723789d2844e028cf7fa419d (patch)
tree9373b7e4e04e7e1a79b24aad37d1570f1a1fa0f3 /gpu/command_buffer/service
parent0f25bfdf7a60ff20d3b3d7572065fe1250bd6bd0 (diff)
downloadchromium_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.cc68
-rw-r--r--gpu/command_buffer/service/gpu_scheduler.h4
-rw-r--r--gpu/command_buffer/service/gpu_scheduler_unittest.cc7
-rw-r--r--gpu/command_buffer/service/mocks.cc22
-rw-r--r--gpu/command_buffer/service/mocks.h14
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 {