diff options
-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', |