summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--content/common/gpu/client/command_buffer_proxy.cc30
-rw-r--r--content/common/gpu/client/command_buffer_proxy.h7
-rw-r--r--content/common/gpu/gpu_command_buffer_stub.cc17
-rw-r--r--content/common/gpu/gpu_command_buffer_stub.h1
-rw-r--r--content/common/gpu/gpu_messages.h13
-rw-r--r--gpu/command_buffer/common/command_buffer_shared.h61
-rw-r--r--gpu/command_buffer/common/command_buffer_shared_test.cc97
-rw-r--r--gpu/command_buffer/service/command_buffer_service.cc18
-rw-r--r--gpu/command_buffer/service/command_buffer_service.h8
-rw-r--r--gpu/gpu.gyp1
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',