Skip to content

Commit

Permalink
gpu: Thread-safe command buffer state lookup.
Browse files Browse the repository at this point in the history
This makes client side (in-process / proxy) command buffer state thread-
safe and adds release count to the client-server shared state. This
allows the compositor thread to monitor worker context sync token in the
service.

Client side command buffer state is synchronized using a lock. Extra
caching of the state is added to command buffer helper so that it
doesn't acquire the lock for every command.

Also fixes command buffer memory tracing so that it happens on the same
thread which the context provider is bound to.

R=piman@chromium.org,jbauman@chromium.org
BUG=514813,638862
CQ_INCLUDE_TRYBOTS=master.tryserver.chromium.linux:linux_optional_gpu_tests_rel;master.tryserver.chromium.mac:mac_optional_gpu_tests_rel;master.tryserver.chromium.win:win_optional_gpu_tests_rel

Review-Url: https://codereview.chromium.org/2550583002
Cr-Commit-Position: refs/heads/master@{#437651}
  • Loading branch information
sunnyps authored and Commit bot committed Dec 9, 2016
1 parent 59dc4be commit 1285660
Show file tree
Hide file tree
Showing 31 changed files with 390 additions and 310 deletions.
4 changes: 4 additions & 0 deletions cc/test/test_context_support.cc
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,10 @@ void TestContextSupport::SignalSyncToken(const gpu::SyncToken& sync_token,
weak_ptr_factory_.GetWeakPtr()));
}

bool TestContextSupport::IsFenceSyncReleased(uint64_t release) {
return true;
}

void TestContextSupport::SignalQuery(uint32_t query,
const base::Closure& callback) {
sync_point_callbacks_.push_back(callback);
Expand Down
1 change: 1 addition & 0 deletions cc/test/test_context_support.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ class TestContextSupport : public gpu::ContextSupport {
// gpu::ContextSupport implementation.
void SignalSyncToken(const gpu::SyncToken& sync_token,
const base::Closure& callback) override;
bool IsFenceSyncReleased(uint64_t release) override;
void SignalQuery(uint32_t query, const base::Closure& callback) override;
void SetAggressivelyFreeResources(bool aggressively_free_resources) override;
void Swap() override;
Expand Down
7 changes: 5 additions & 2 deletions content/common/gpu/client/context_provider_command_buffer.cc
Original file line number Diff line number Diff line change
Expand Up @@ -292,9 +292,9 @@ bool ContextProviderCommandBuffer::BindToCurrentThread() {
return false;
}

if (command_buffer_->GetLastError() != gpu::error::kNoError) {
if (command_buffer_->GetLastState().error != gpu::error::kNoError) {
DLOG(ERROR) << "Context dead on arrival. Last error: "
<< command_buffer_->GetLastError();
<< command_buffer_->GetLastState().error;
return false;
}

Expand Down Expand Up @@ -454,6 +454,9 @@ bool ContextProviderCommandBuffer::OnMemoryDump(
if (support_locking_)
hold.emplace(context_lock_);

gles2_impl_->OnMemoryDump(args, pmd);
gles2_helper_->OnMemoryDump(args, pmd);

context_thread_checker_.DetachFromThread();
SkiaGpuTraceMemoryDump trace_memory_dump(
pmd, gles2_impl_->ShareGroupTracingGUID());
Expand Down
6 changes: 2 additions & 4 deletions content/renderer/pepper/ppb_graphics_3d_impl.cc
Original file line number Diff line number Diff line change
Expand Up @@ -117,15 +117,13 @@ PP_Bool PPB_Graphics3D_Impl::Flush(int32_t put_offset) {
gpu::CommandBuffer::State PPB_Graphics3D_Impl::WaitForTokenInRange(
int32_t start,
int32_t end) {
GetCommandBuffer()->WaitForTokenInRange(start, end);
return GetCommandBuffer()->GetLastState();
return GetCommandBuffer()->WaitForTokenInRange(start, end);
}

gpu::CommandBuffer::State PPB_Graphics3D_Impl::WaitForGetOffsetInRange(
int32_t start,
int32_t end) {
GetCommandBuffer()->WaitForGetOffsetInRange(start, end);
return GetCommandBuffer()->GetLastState();
return GetCommandBuffer()->WaitForGetOffsetInRange(start, end);
}

void PPB_Graphics3D_Impl::EnsureWorkVisible() {
Expand Down
46 changes: 21 additions & 25 deletions gpu/command_buffer/client/client_test_helper.cc
Original file line number Diff line number Diff line change
Expand Up @@ -20,30 +20,33 @@ using ::testing::Invoke;

namespace gpu {

MockCommandBufferBase::MockCommandBufferBase() : put_offset_(0) {
}
MockCommandBufferBase::MockCommandBufferBase() : put_offset_(0) {}

MockCommandBufferBase::~MockCommandBufferBase() {
}
MockCommandBufferBase::~MockCommandBufferBase() {}

CommandBuffer::State MockCommandBufferBase::GetLastState() {
return state_;
}

int32_t MockCommandBufferBase::GetLastToken() {
return state_.token;
}

void MockCommandBufferBase::SetGetOffset(int32_t get_offset) {
state_.get_offset = get_offset;
}

void MockCommandBufferBase::WaitForTokenInRange(int32_t start, int32_t end) {}
void MockCommandBufferBase::SetReleaseCount(uint64_t release_count) {
state_.release_count = release_count;
}

CommandBuffer::State MockCommandBufferBase::WaitForTokenInRange(int32_t start,
int32_t end) {
return state_;
}

void MockCommandBufferBase::WaitForGetOffsetInRange(int32_t start,
int32_t end) {
CommandBuffer::State MockCommandBufferBase::WaitForGetOffsetInRange(
int32_t start,
int32_t end) {
state_.get_offset = put_offset_;
OnFlush();
return state_;
}

void MockCommandBufferBase::SetGetBuffer(int transfer_buffer_id) {
Expand Down Expand Up @@ -125,8 +128,7 @@ MockClientCommandBuffer::MockClientCommandBuffer() {
DelegateToFake();
}

MockClientCommandBuffer::~MockClientCommandBuffer() {
}
MockClientCommandBuffer::~MockClientCommandBuffer() {}

void MockClientCommandBuffer::Flush(int32_t put_offset) {
FlushHelper(put_offset);
Expand All @@ -138,30 +140,24 @@ void MockClientCommandBuffer::OrderingBarrier(int32_t put_offset) {

void MockClientCommandBuffer::DelegateToFake() {
ON_CALL(*this, DestroyTransferBuffer(_))
.WillByDefault(Invoke(
this, &MockCommandBufferBase::DestroyTransferBufferHelper));
.WillByDefault(
Invoke(this, &MockCommandBufferBase::DestroyTransferBufferHelper));
}

MockClientCommandBufferMockFlush::MockClientCommandBufferMockFlush() {
DelegateToFake();
}

MockClientCommandBufferMockFlush::~MockClientCommandBufferMockFlush() {
}
MockClientCommandBufferMockFlush::~MockClientCommandBufferMockFlush() {}

void MockClientCommandBufferMockFlush::DelegateToFake() {
MockClientCommandBuffer::DelegateToFake();
ON_CALL(*this, Flush(_))
.WillByDefault(Invoke(
this, &MockCommandBufferBase::FlushHelper));
.WillByDefault(Invoke(this, &MockCommandBufferBase::FlushHelper));
}

MockClientGpuControl::MockClientGpuControl() {
}
MockClientGpuControl::MockClientGpuControl() {}

MockClientGpuControl::~MockClientGpuControl() {
}
MockClientGpuControl::~MockClientGpuControl() {}

} // namespace gpu


7 changes: 4 additions & 3 deletions gpu/command_buffer/client/client_test_helper.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,11 +33,11 @@ class MockCommandBufferBase : public CommandBufferServiceBase {
~MockCommandBufferBase() override;

State GetLastState() override;
int32_t GetLastToken() override;
void WaitForTokenInRange(int32_t start, int32_t end) override;
void WaitForGetOffsetInRange(int32_t start, int32_t end) override;
State WaitForTokenInRange(int32_t start, int32_t end) override;
State WaitForGetOffsetInRange(int32_t start, int32_t end) override;
void SetGetBuffer(int transfer_buffer_id) override;
void SetGetOffset(int32_t get_offset) override;
void SetReleaseCount(uint64_t release_count) override;
scoped_refptr<gpu::Buffer> CreateTransferBuffer(size_t size,
int32_t* id) override;
scoped_refptr<gpu::Buffer> GetTransferBuffer(int32_t id) override;
Expand Down Expand Up @@ -119,6 +119,7 @@ class MockClientGpuControl : public GpuControl {
MOCK_METHOD1(IsFenceSyncRelease, bool(uint64_t release));
MOCK_METHOD1(IsFenceSyncFlushed, bool(uint64_t release));
MOCK_METHOD1(IsFenceSyncFlushReceived, bool(uint64_t release));
MOCK_METHOD1(IsFenceSyncReleased, bool(uint64_t release));
MOCK_METHOD2(SignalSyncToken, void(const SyncToken& sync_token,
const base::Closure& callback));
MOCK_METHOD1(CanWaitUnverifiedSyncToken, bool(const SyncToken*));
Expand Down
78 changes: 48 additions & 30 deletions gpu/command_buffer/client/cmd_buffer_helper.cc
Original file line number Diff line number Diff line change
Expand Up @@ -27,26 +27,21 @@ CommandBufferHelper::CommandBufferHelper(CommandBuffer* command_buffer)
: command_buffer_(command_buffer),
ring_buffer_id_(-1),
ring_buffer_size_(0),
entries_(NULL),
entries_(nullptr),
total_entry_count_(0),
immediate_entry_count_(0),
token_(0),
put_(0),
last_put_sent_(0),
cached_last_token_read_(0),
cached_get_offset_(0),
#if defined(CMD_HELPER_PERIODIC_FLUSH_CHECK)
commands_issued_(0),
#endif
usable_(true),
context_lost_(false),
flush_automatically_(true),
flush_generation_(0) {
// In certain cases, ThreadTaskRunnerHandle isn't set (Android Webview).
// Don't register a dump provider in these cases.
// TODO(ericrk): Get this working in Android Webview. crbug.com/517156
if (base::ThreadTaskRunnerHandle::IsSet()) {
base::trace_event::MemoryDumpManager::GetInstance()->RegisterDumpProvider(
this, "gpu::CommandBufferHelper", base::ThreadTaskRunnerHandle::Get());
}
}

void CommandBufferHelper::SetAutomaticFlushes(bool enabled) {
Expand All @@ -55,9 +50,8 @@ void CommandBufferHelper::SetAutomaticFlushes(bool enabled) {
}

bool CommandBufferHelper::IsContextLost() {
if (!context_lost_) {
context_lost_ = error::IsError(command_buffer()->GetLastError());
}
if (!context_lost_)
context_lost_ = error::IsError(command_buffer()->GetLastState().error);
return context_lost_;
}

Expand All @@ -71,7 +65,7 @@ void CommandBufferHelper::CalcImmediateEntries(int waiting_count) {
}

// Get maximum safe contiguous entries.
const int32_t curr_get = get_offset();
const int32_t curr_get = cached_get_offset_;
if (curr_get > put_) {
immediate_entry_count_ = curr_get - put_ - 1;
} else {
Expand Down Expand Up @@ -116,7 +110,7 @@ bool CommandBufferHelper::AllocateRingBuffer() {
command_buffer_->CreateTransferBuffer(ring_buffer_size_, &id);
if (id < 0) {
ClearUsable();
DCHECK(error::IsError(command_buffer()->GetLastError()));
DCHECK(context_lost_);
return false;
}

Expand All @@ -128,6 +122,7 @@ bool CommandBufferHelper::AllocateRingBuffer() {
// Call to SetGetBuffer(id) above resets get and put offsets to 0.
// No need to query it through IPC.
put_ = 0;
cached_get_offset_ = 0;
CalcImmediateEntries(0);
return true;
}
Expand All @@ -143,8 +138,8 @@ void CommandBufferHelper::FreeResources() {
}

void CommandBufferHelper::FreeRingBuffer() {
CHECK((put_ == get_offset()) ||
error::IsError(command_buffer_->GetLastState().error));
CHECK((put_ == cached_get_offset_) ||
error::IsError(command_buffer_->GetLastState().error));
FreeResources();
}

Expand All @@ -154,19 +149,25 @@ bool CommandBufferHelper::Initialize(int32_t ring_buffer_size) {
}

CommandBufferHelper::~CommandBufferHelper() {
base::trace_event::MemoryDumpManager::GetInstance()->UnregisterDumpProvider(
this);
FreeResources();
}

void CommandBufferHelper::UpdateCachedState(const CommandBuffer::State& state) {
cached_get_offset_ = state.get_offset;
cached_last_token_read_ = state.token;
context_lost_ = error::IsError(state.error);
}

bool CommandBufferHelper::WaitForGetOffsetInRange(int32_t start, int32_t end) {
DCHECK(start >= 0 && start <= total_entry_count_);
DCHECK(end >= 0 && end <= total_entry_count_);
if (!usable()) {
return false;
}
command_buffer_->WaitForGetOffsetInRange(start, end);
return command_buffer_->GetLastError() == gpu::error::kNoError;
CommandBuffer::State last_state =
command_buffer_->WaitForGetOffsetInRange(start, end);
UpdateCachedState(last_state);
return !context_lost_;
}

void CommandBufferHelper::Flush() {
Expand Down Expand Up @@ -213,15 +214,15 @@ bool CommandBufferHelper::Finish() {
return false;
}
// If there is no work just exit.
if (put_ == get_offset()) {
if (put_ == cached_get_offset_) {
return true;
}
DCHECK(HaveRingBuffer() ||
error::IsError(command_buffer_->GetLastState().error));
Flush();
if (!WaitForGetOffsetInRange(put_, put_))
return false;
DCHECK_EQ(get_offset(), put_);
DCHECK_EQ(cached_get_offset_, put_);

CalcImmediateEntries(0);

Expand All @@ -246,14 +247,25 @@ int32_t CommandBufferHelper::InsertToken() {
cmd->Init(token_);
if (token_ == 0) {
TRACE_EVENT0("gpu", "CommandBufferHelper::InsertToken(wrapped)");
// we wrapped
Finish();
DCHECK_EQ(token_, last_token_read());
bool finished = Finish(); // we wrapped
DCHECK(!finished || (cached_last_token_read_ == 0));
}
}
return token_;
}

bool CommandBufferHelper::HasTokenPassed(int32_t token) {
// If token_ wrapped around we Finish'd.
if (token > token_)
return true;
// Don't update state if we don't have to.
if (token <= cached_last_token_read_)
return true;
CommandBuffer::State last_state = command_buffer_->GetLastState();
UpdateCachedState(last_state);
return token <= cached_last_token_read_;
}

// Waits until the current token value is greater or equal to the value passed
// in argument.
void CommandBufferHelper::WaitForToken(int32_t token) {
Expand All @@ -263,11 +275,17 @@ void CommandBufferHelper::WaitForToken(int32_t token) {
// Return immediately if corresponding InsertToken failed.
if (token < 0)
return;
if (token > token_) return; // we wrapped
if (last_token_read() >= token)
if (token > token_)
return; // we wrapped
if (cached_last_token_read_ >= token)
return;
UpdateCachedState(command_buffer_->GetLastState());
if (cached_last_token_read_ >= token)
return;
Flush();
command_buffer_->WaitForTokenInRange(token, token_);
CommandBuffer::State last_state =
command_buffer_->WaitForTokenInRange(token, token_);
UpdateCachedState(last_state);
}

// Waits for available entries, basically waiting until get >= put + count + 1.
Expand All @@ -288,13 +306,13 @@ void CommandBufferHelper::WaitForAvailableEntries(int32_t count) {
// but we need to make sure get wraps first, actually that get is 1 or
// more (since put will wrap to 0 after we add the noops).
DCHECK_LE(1, put_);
int32_t curr_get = get_offset();
int32_t curr_get = cached_get_offset_;
if (curr_get > put_ || curr_get == 0) {
TRACE_EVENT0("gpu", "CommandBufferHelper::WaitForAvailableEntries");
Flush();
if (!WaitForGetOffsetInRange(1, put_))
return;
curr_get = get_offset();
curr_get = cached_get_offset_;
DCHECK_LE(curr_get, put_);
DCHECK_NE(0, curr_get);
}
Expand Down Expand Up @@ -328,7 +346,7 @@ void CommandBufferHelper::WaitForAvailableEntries(int32_t count) {
}

int32_t CommandBufferHelper::GetTotalFreeEntriesNoWaiting() const {
int32_t current_get_offset = get_offset();
int32_t current_get_offset = cached_get_offset_;
if (current_get_offset > put_) {
return current_get_offset - put_ - 1;
} else {
Expand Down
Loading

0 comments on commit 1285660

Please sign in to comment.