diff options
author | jbauman@chromium.org <jbauman@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-02-15 04:12:57 +0000 |
---|---|---|
committer | jbauman@chromium.org <jbauman@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-02-15 04:12:57 +0000 |
commit | 2f9704c73793a303481b5fa49c76903f9e81eb02 (patch) | |
tree | e72dcd9bc380230eccfa3970e83e9a1791fcfc78 | |
parent | e190c9ea6211125a1c836d913d504643678a35c5 (diff) | |
download | chromium_src-2f9704c73793a303481b5fa49c76903f9e81eb02.zip chromium_src-2f9704c73793a303481b5fa49c76903f9e81eb02.tar.gz chromium_src-2f9704c73793a303481b5fa49c76903f9e81eb02.tar.bz2 |
Use shared memory to update the renderer's view of the command buffer state.
The renderer can't receive UpdateState messages while it's executing javascript or NaCl, causing it to eventually flushsync once it fills up the command buffer or transfer buffer. To avoid this, share a piece of memory between the renderer and gpu process that the GPU can asynchronously update the state. A 4-slot asynchronous communication mechanism is used so that the renderer always receives a consistent copy of the state that was put in by the GPU process.
BUG=
TEST=
Review URL: http://codereview.chromium.org/9380037
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@122034 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r-- | content/common/gpu/client/command_buffer_proxy.cc | 30 | ||||
-rw-r--r-- | content/common/gpu/client/command_buffer_proxy.h | 7 | ||||
-rw-r--r-- | content/common/gpu/gpu_command_buffer_stub.cc | 17 | ||||
-rw-r--r-- | content/common/gpu/gpu_command_buffer_stub.h | 1 | ||||
-rw-r--r-- | content/common/gpu/gpu_messages.h | 13 | ||||
-rw-r--r-- | gpu/command_buffer/common/command_buffer_shared.h | 61 | ||||
-rw-r--r-- | gpu/command_buffer/common/command_buffer_shared_test.cc | 97 | ||||
-rw-r--r-- | gpu/command_buffer/service/command_buffer_service.cc | 18 | ||||
-rw-r--r-- | gpu/command_buffer/service/command_buffer_service.h | 8 | ||||
-rw-r--r-- | gpu/gpu.gyp | 1 |
10 files changed, 242 insertions, 11 deletions
diff --git a/content/common/gpu/client/command_buffer_proxy.cc b/content/common/gpu/client/command_buffer_proxy.cc index 1a25c1a..baa4b38 100644 --- a/content/common/gpu/client/command_buffer_proxy.cc +++ b/content/common/gpu/client/command_buffer_proxy.cc @@ -17,6 +17,7 @@ #include "content/common/plugin_messages.h" #include "content/common/view_messages.h" #include "gpu/command_buffer/common/cmd_buffer_common.h" +#include "gpu/command_buffer/common/command_buffer_shared.h" #include "ui/gfx/size.h" using gpu::Buffer; @@ -43,7 +44,6 @@ CommandBufferProxy::~CommandBufferProxy() { bool CommandBufferProxy::OnMessageReceived(const IPC::Message& message) { bool handled = true; IPC_BEGIN_MESSAGE_MAP(CommandBufferProxy, message) - IPC_MESSAGE_HANDLER(GpuCommandBufferMsg_UpdateState, OnUpdateState); IPC_MESSAGE_HANDLER(GpuCommandBufferMsg_Destroyed, OnDestroyed); IPC_MESSAGE_HANDLER(GpuCommandBufferMsg_NotifyRepaint, OnNotifyRepaint); @@ -111,6 +111,28 @@ bool CommandBufferProxy::Initialize() { return false; } + int32 state_buffer = CreateTransferBuffer(sizeof *shared_state_, -1); + + if (state_buffer == -1) { + LOG(ERROR) << "Failed to create shared state transfer buffer."; + return false; + } + + gpu::Buffer buffer = GetTransferBuffer(state_buffer); + if (!buffer.ptr) { + LOG(ERROR) << "Failed to get shared state transfer buffer"; + return false; + } + + shared_state_ = reinterpret_cast<gpu::CommandBufferSharedState*>(buffer.ptr); + shared_state_->Initialize(); + + if (!Send(new GpuCommandBufferMsg_SetSharedStateBuffer(route_id_, + state_buffer))) { + LOG(ERROR) << "Failed to initialize shared command buffer state."; + return false; + } + return true; } @@ -122,6 +144,7 @@ gpu::CommandBuffer::State CommandBufferProxy::GetState() { OnUpdateState(state); } + TryUpdateState(); return last_state_; } @@ -145,6 +168,7 @@ gpu::CommandBuffer::State CommandBufferProxy::FlushSync(int32 put_offset, TRACE_EVENT1("gpu", "CommandBufferProxy::FlushSync", "put_offset", put_offset); Flush(put_offset); + TryUpdateState(); if (last_known_get == last_state_.get_offset) { // Send will flag state with lost context if IPC fails. if (last_state_.error == gpu::error::kNoError) { @@ -153,6 +177,7 @@ gpu::CommandBuffer::State CommandBufferProxy::FlushSync(int32 put_offset, &state))) OnUpdateState(state); } + TryUpdateState(); } return last_state_; @@ -413,3 +438,6 @@ void CommandBufferProxy::SetOnConsoleMessageCallback( console_message_callback_ = callback; } +void CommandBufferProxy::TryUpdateState() { + shared_state_->Read(&last_state_); +} diff --git a/content/common/gpu/client/command_buffer_proxy.h b/content/common/gpu/client/command_buffer_proxy.h index 398f794..37cbe7c 100644 --- a/content/common/gpu/client/command_buffer_proxy.h +++ b/content/common/gpu/client/command_buffer_proxy.h @@ -18,6 +18,7 @@ #include "base/memory/weak_ptr.h" #include "content/common/gpu/client/gpu_video_decode_accelerator_host.h" #include "gpu/command_buffer/common/command_buffer.h" +#include "gpu/command_buffer/common/command_buffer_shared.h" #include "ipc/ipc_channel.h" #include "ipc/ipc_message.h" @@ -116,6 +117,9 @@ class CommandBufferProxy : public gpu::CommandBuffer, void OnEchoAck(); void OnConsoleMessage(const GPUCommandBufferConsoleMessage& message); + // Try to read an updated copy of the state from shared memory. + void TryUpdateState(); + // Local cache of id to transfer buffer mapping. typedef std::map<int32, gpu::Buffer> TransferBufferMap; TransferBufferMap transfer_buffers_; @@ -128,6 +132,9 @@ class CommandBufferProxy : public gpu::CommandBuffer, // The last cached state received from the service. State last_state_; + // The shared memory area used to update state. + gpu::CommandBufferSharedState* shared_state_; + // |*this| is owned by |*channel_| and so is always outlived by it, so using a // raw pointer is ok. GpuChannelHost* channel_; diff --git a/content/common/gpu/gpu_command_buffer_stub.cc b/content/common/gpu/gpu_command_buffer_stub.cc index ed36682..4bfac3b 100644 --- a/content/common/gpu/gpu_command_buffer_stub.cc +++ b/content/common/gpu/gpu_command_buffer_stub.cc @@ -98,6 +98,8 @@ bool GpuCommandBufferStub::OnMessageReceived(const IPC::Message& message) { OnInitialize); IPC_MESSAGE_HANDLER_DELAY_REPLY(GpuCommandBufferMsg_SetGetBuffer, OnSetGetBuffer); + IPC_MESSAGE_HANDLER_DELAY_REPLY(GpuCommandBufferMsg_SetSharedStateBuffer, + OnSetSharedStateBuffer); IPC_MESSAGE_HANDLER_DELAY_REPLY(GpuCommandBufferMsg_SetParent, OnSetParent); IPC_MESSAGE_HANDLER_DELAY_REPLY(GpuCommandBufferMsg_GetState, OnGetState); @@ -294,6 +296,17 @@ void GpuCommandBufferStub::OnSetGetBuffer( Send(reply_message); } +void GpuCommandBufferStub::OnSetSharedStateBuffer( + int32 shm_id, IPC::Message* reply_message) { + if (command_buffer_.get()) { + command_buffer_->SetSharedStateBuffer(shm_id); + } else { + DLOG(ERROR) << "no command_buffer."; + reply_message->set_reply_error(); + } + Send(reply_message); +} + void GpuCommandBufferStub::OnSetParent(int32 parent_route_id, uint32 parent_texture_id, IPC::Message* reply_message) { @@ -476,9 +489,7 @@ void GpuCommandBufferStub::ReportState() { gfx::GLContext::LosesAllContextsOnContextLost()) { channel_->LoseAllContexts(); } else { - IPC::Message* msg = new GpuCommandBufferMsg_UpdateState(route_id_, state); - msg->set_unblock(true); - Send(msg); + command_buffer_->UpdateState(); } } diff --git a/content/common/gpu/gpu_command_buffer_stub.h b/content/common/gpu/gpu_command_buffer_stub.h index 5125767..e194609 100644 --- a/content/common/gpu/gpu_command_buffer_stub.h +++ b/content/common/gpu/gpu_command_buffer_stub.h @@ -152,6 +152,7 @@ class GpuCommandBufferStub // Message handlers: void OnInitialize(IPC::Message* reply_message); void OnSetGetBuffer(int32 shm_id, IPC::Message* reply_message); + void OnSetSharedStateBuffer(int32 shm_id, IPC::Message* reply_message); void OnSetParent(int32 parent_route_id, uint32 parent_texture_id, IPC::Message* reply_message); diff --git a/content/common/gpu/gpu_messages.h b/content/common/gpu/gpu_messages.h index c33b60e..432b629 100644 --- a/content/common/gpu/gpu_messages.h +++ b/content/common/gpu/gpu_messages.h @@ -318,6 +318,11 @@ IPC_MESSAGE_CONTROL0(GpuChannelMsg_CloseChannel) IPC_SYNC_MESSAGE_ROUTED0_1(GpuCommandBufferMsg_Initialize, bool /* result */) +// Sets the shared memory buffer used to hold the CommandBufferSharedState, +// used to transmit the current state. +IPC_SYNC_MESSAGE_ROUTED1_0(GpuCommandBufferMsg_SetSharedStateBuffer, + int32 /* shm_id */) + // Sets the shared memory buffer used for commands. IPC_SYNC_MESSAGE_ROUTED1_0(GpuCommandBufferMsg_SetGetBuffer, int32 /* shm_id */) @@ -339,7 +344,7 @@ IPC_SYNC_MESSAGE_ROUTED0_1(GpuCommandBufferMsg_GetStateFast, // Asynchronously synchronize the put and get offsets of both processes. // Caller passes its current put offset. Current state (including get offset) -// is returned via an UpdateState message. +// is returned in shared memory. IPC_MESSAGE_ROUTED2(GpuCommandBufferMsg_AsyncFlush, int32 /* put_offset */, uint32 /* flush_count */) @@ -350,12 +355,6 @@ IPC_MESSAGE_ROUTED2(GpuCommandBufferMsg_AsyncFlush, // process actually sends it (deferred) to itself. IPC_MESSAGE_ROUTED0(GpuCommandBufferMsg_Rescheduled) -// Return the current state of the command buffer following a request via -// an AsyncGetState or AsyncFlush message. (This message is sent from the -// GPU process to the renderer process.) -IPC_MESSAGE_ROUTED1(GpuCommandBufferMsg_UpdateState, - gpu::CommandBuffer::State /* state */) - // Sent by the GPU process to display messages in the console. IPC_MESSAGE_ROUTED1(GpuCommandBufferMsg_ConsoleMsg, GPUCommandBufferConsoleMessage /* msg */) diff --git a/gpu/command_buffer/common/command_buffer_shared.h b/gpu/command_buffer/common/command_buffer_shared.h new file mode 100644 index 0000000..017a644 --- /dev/null +++ b/gpu/command_buffer/common/command_buffer_shared.h @@ -0,0 +1,61 @@ +// 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. + +#ifndef GPU_COMMAND_BUFFER_COMMON_COMMAND_BUFFER_SHARED_H_ +#define GPU_COMMAND_BUFFER_COMMON_COMMAND_BUFFER_SHARED_H_ + +#include "command_buffer.h" +#include "base/atomicops.h" + +namespace gpu { + +// This is a standard 4-slot asynchronous communication mechanism, used to +// ensure that the reader gets a consistent copy of what the writer wrote. +template<typename T> +class SharedState { + T states_[2][2]; + base::subtle::Atomic32 reading_; + base::subtle::Atomic32 latest_; + base::subtle::Atomic32 slots_[2]; + +public: + + void Initialize() { + for (int i = 0; i < 2; ++i) { + for (int j = 0; j < 2; ++j) { + states_[i][j] = T(); + } + } + base::subtle::NoBarrier_Store(&reading_, 0); + base::subtle::NoBarrier_Store(&latest_, 0); + base::subtle::NoBarrier_Store(&slots_[0], 0); + base::subtle::Acquire_Store(&slots_[1], 0); + } + + void Write(const T& state) { + int towrite = !base::subtle::Acquire_Load(&reading_); + int index = !base::subtle::Acquire_Load(&slots_[towrite]); + states_[towrite][index] = state; + base::subtle::MemoryBarrier(); + base::subtle::Acquire_Store(&slots_[towrite], index); + base::subtle::Acquire_Store(&latest_, towrite); + } + + // Attempt to update the state, updating only if the generation counter is + // newer. + void Read(T* state) { + base::subtle::MemoryBarrier(); + int toread = !!base::subtle::Acquire_Load(&latest_); + base::subtle::Acquire_Store(&reading_, toread); + int index = !!base::subtle::Acquire_Load(&slots_[toread]); + if (states_[toread][index].generation - state->generation < 0x80000000U) + *state = states_[toread][index]; + } +}; + +typedef SharedState<CommandBuffer::State> CommandBufferSharedState; + +} // namespace gpu + +#endif // GPU_COMMAND_BUFFER_COMMON_COMMAND_BUFFER_SHARED_H_ diff --git a/gpu/command_buffer/common/command_buffer_shared_test.cc b/gpu/command_buffer/common/command_buffer_shared_test.cc new file mode 100644 index 0000000..f7ff2f0 --- /dev/null +++ b/gpu/command_buffer/common/command_buffer_shared_test.cc @@ -0,0 +1,97 @@ + +// 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. + +// This file contains the tests for the CommandBufferSharedState class. + +#include "gpu/command_buffer/common/command_buffer_shared.h" +#include "base/bind.h" +#include "base/memory/scoped_ptr.h" +#include "base/threading/thread.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace gpu { + +class CommandBufferSharedTest : public testing::Test { + protected: + + virtual void SetUp() { + shared_state_.reset(new CommandBufferSharedState()); + shared_state_->Initialize(); + } + + scoped_ptr<CommandBufferSharedState> shared_state_; +}; + +TEST_F(CommandBufferSharedTest, TestBasic) { + CommandBuffer::State state; + + shared_state_->Read(&state); + + EXPECT_LT(state.generation, 0x80000000); + EXPECT_EQ(state.get_offset, 0); + EXPECT_EQ(state.put_offset, 0); + EXPECT_EQ(state.token, -1); + EXPECT_EQ(state.error, gpu::error::kNoError); + EXPECT_EQ(state.context_lost_reason, gpu::error::kUnknown); +} + +static const int kSize = 100000; + +void WriteToState(int32 *buffer, + CommandBufferSharedState* shared_state) { + CommandBuffer::State state; + for (int i = 0; i < kSize; i++) { + state.token = i - 1; + state.get_offset = i + 1; + state.generation = i + 2; + state.error = static_cast<gpu::error::Error>(i + 3); + // Ensure that the producer doesn't update the buffer until after the + // consumer reads from it. + EXPECT_EQ(buffer[i], 0); + + shared_state->Write(state); + } +} + +TEST_F(CommandBufferSharedTest, TestConsistency) { + scoped_array<int32> buffer; + buffer.reset(new int32[kSize]); + base::Thread consumer("Reader Thread"); + + memset(buffer.get(), 0, kSize * sizeof(int32)); + + consumer.Start(); + consumer.message_loop()->PostTask( + FROM_HERE, base::Bind(&WriteToState, buffer.get(), + shared_state_.get())); + + CommandBuffer::State last_state; + while (1) { + CommandBuffer::State state = last_state; + + shared_state_->Read(&state); + + if (state.generation < last_state.generation) + continue; + + if (state.get_offset >= 1) { + buffer[state.get_offset - 1] = 1; + // Check that the state is consistent + EXPECT_LE(last_state.token, state.token); + EXPECT_LE(last_state.generation, state.generation); + last_state = state; + EXPECT_EQ(state.token, state.get_offset - 2); + EXPECT_EQ(state.generation, + static_cast<unsigned int>(state.get_offset) + 1); + EXPECT_EQ(state.error, state.get_offset + 2); + + if (state.get_offset == kSize) + break; + } + } +} + +} // namespace gpu + diff --git a/gpu/command_buffer/service/command_buffer_service.cc b/gpu/command_buffer/service/command_buffer_service.cc index c5a1162..13d3098 100644 --- a/gpu/command_buffer/service/command_buffer_service.cc +++ b/gpu/command_buffer/service/command_buffer_service.cc @@ -9,6 +9,7 @@ #include "base/process_util.h" #include "base/debug/trace_event.h" #include "gpu/command_buffer/common/cmd_buffer_common.h" +#include "gpu/command_buffer/common/command_buffer_shared.h" using ::base::SharedMemory; @@ -16,6 +17,7 @@ namespace gpu { CommandBufferService::CommandBufferService() : ring_buffer_id_(-1), + shared_state_(NULL), num_entries_(0), get_offset_(0), put_offset_(0), @@ -59,6 +61,13 @@ CommandBufferService::State CommandBufferService::GetLastState() { return GetState(); } +void CommandBufferService::UpdateState() { + if (shared_state_) { + CommandBufferService::State state = GetState(); + shared_state_->Write(state); + } +} + CommandBufferService::State CommandBufferService::FlushSync( int32 put_offset, int32 last_known_get) { if (put_offset < 0 || put_offset > num_entries_) { @@ -98,6 +107,15 @@ void CommandBufferService::SetGetBuffer(int32 transfer_buffer_id) { if (!get_buffer_change_callback_.is_null()) { get_buffer_change_callback_.Run(ring_buffer_id_); } + + UpdateState(); +} + +void CommandBufferService::SetSharedStateBuffer(int32 transfer_buffer_id) { + gpu::Buffer buffer = GetTransferBuffer(transfer_buffer_id); + shared_state_ = reinterpret_cast<CommandBufferSharedState*>(buffer.ptr); + + UpdateState(); } void CommandBufferService::SetGetOffset(int32 get_offset) { diff --git a/gpu/command_buffer/service/command_buffer_service.h b/gpu/command_buffer/service/command_buffer_service.h index 7704636..2c59678 100644 --- a/gpu/command_buffer/service/command_buffer_service.h +++ b/gpu/command_buffer/service/command_buffer_service.h @@ -13,6 +13,7 @@ #include "base/memory/scoped_ptr.h" #include "base/shared_memory.h" #include "gpu/command_buffer/common/command_buffer.h" +#include "gpu/command_buffer/common/command_buffer_shared.h" namespace gpu { @@ -56,9 +57,16 @@ class CommandBufferService : public CommandBuffer { const GetBufferChangedCallback& callback); virtual void SetParseErrorCallback(const base::Closure& callback); + // Setup the transfer buffer that shared state should be copied into. + void SetSharedStateBuffer(int32 transfer_buffer_id); + + // Copy the current state into the shared state transfer buffer. + void UpdateState(); + private: int32 ring_buffer_id_; Buffer ring_buffer_; + CommandBufferSharedState* shared_state_; int32 num_entries_; int32 get_offset_; int32 put_offset_; diff --git a/gpu/gpu.gyp b/gpu/gpu.gyp index c24c4d1..7af8343 100644 --- a/gpu/gpu.gyp +++ b/gpu/gpu.gyp @@ -317,6 +317,7 @@ 'command_buffer/common/bitfield_helpers_test.cc', 'command_buffer/common/command_buffer_mock.cc', 'command_buffer/common/command_buffer_mock.h', + 'command_buffer/common/command_buffer_shared_test.cc', 'command_buffer/common/gles2_cmd_format_test.cc', 'command_buffer/common/gles2_cmd_format_test_autogen.h', 'command_buffer/common/gles2_cmd_utils_unittest.cc', |