1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 // This file contains the implementation of the command buffer helper class.
7 #include "gpu/command_buffer/client/cmd_buffer_helper.h"
9 #include "base/logging.h"
10 #include "gpu/command_buffer/common/command_buffer.h"
11 #include "gpu/command_buffer/common/trace_event.h"
15 CommandBufferHelper::CommandBufferHelper(CommandBuffer* command_buffer)
16 : command_buffer_(command_buffer),
20 total_entry_count_(0),
21 immediate_entry_count_(0),
25 #if defined(CMD_HELPER_PERIODIC_FLUSH_CHECK)
30 flush_automatically_(true),
34 void CommandBufferHelper::SetAutomaticFlushes(bool enabled) {
35 flush_automatically_ = enabled;
36 CalcImmediateEntries(0);
39 bool CommandBufferHelper::IsContextLost() {
41 context_lost_ = error::IsError(command_buffer()->GetLastError());
46 void CommandBufferHelper::CalcImmediateEntries(int waiting_count) {
47 DCHECK_GE(waiting_count, 0);
49 // Check if usable & allocated.
50 if (!usable() || !HaveRingBuffer()) {
51 immediate_entry_count_ = 0;
55 // Get maximum safe contiguous entries.
56 const int32 curr_get = get_offset();
57 if (curr_get > put_) {
58 immediate_entry_count_ = curr_get - put_ - 1;
60 immediate_entry_count_ =
61 total_entry_count_ - put_ - (curr_get == 0 ? 1 : 0);
64 // Limit entry count to force early flushing.
65 if (flush_automatically_) {
68 ((curr_get == last_put_sent_) ? kAutoFlushSmall : kAutoFlushBig);
71 (put_ + total_entry_count_ - last_put_sent_) % total_entry_count_;
73 if (pending > 0 && pending >= limit) {
74 // Time to force flush.
75 immediate_entry_count_ = 0;
77 // Limit remaining entries, but not lower than waiting_count entries to
78 // prevent deadlock when command size is greater than the flush limit.
80 limit = limit < waiting_count ? waiting_count : limit;
81 immediate_entry_count_ =
82 immediate_entry_count_ > limit ? limit : immediate_entry_count_;
87 bool CommandBufferHelper::AllocateRingBuffer() {
92 if (HaveRingBuffer()) {
97 Buffer buffer = command_buffer_->CreateTransferBuffer(ring_buffer_size_, &id);
103 ring_buffer_ = buffer;
104 ring_buffer_id_ = id;
105 command_buffer_->SetGetBuffer(id);
107 // TODO(gman): Do we really need to call GetState here? We know get & put = 0
108 // Also do we need to check state.num_entries?
109 CommandBuffer::State state = command_buffer_->GetState();
110 entries_ = static_cast<CommandBufferEntry*>(ring_buffer_.ptr);
111 int32 num_ring_buffer_entries =
112 ring_buffer_size_ / sizeof(CommandBufferEntry);
113 if (num_ring_buffer_entries > state.num_entries) {
118 total_entry_count_ = num_ring_buffer_entries;
119 put_ = state.put_offset;
120 CalcImmediateEntries(0);
124 void CommandBufferHelper::FreeResources() {
125 if (HaveRingBuffer()) {
126 command_buffer_->DestroyTransferBuffer(ring_buffer_id_);
127 ring_buffer_id_ = -1;
128 CalcImmediateEntries(0);
132 void CommandBufferHelper::FreeRingBuffer() {
133 CHECK((put_ == get_offset()) ||
134 error::IsError(command_buffer_->GetLastState().error));
138 bool CommandBufferHelper::Initialize(int32 ring_buffer_size) {
139 ring_buffer_size_ = ring_buffer_size;
140 return AllocateRingBuffer();
143 CommandBufferHelper::~CommandBufferHelper() {
147 bool CommandBufferHelper::FlushSync() {
152 // Wrap put_ before flush.
153 if (put_ == total_entry_count_)
156 last_flush_time_ = clock();
157 last_put_sent_ = put_;
158 CommandBuffer::State state = command_buffer_->FlushSync(put_, get_offset());
159 CalcImmediateEntries(0);
160 return state.error == error::kNoError;
163 void CommandBufferHelper::Flush() {
164 // Wrap put_ before flush.
165 if (put_ == total_entry_count_)
168 if (usable() && last_put_sent_ != put_) {
169 last_flush_time_ = clock();
170 last_put_sent_ = put_;
171 command_buffer_->Flush(put_);
172 CalcImmediateEntries(0);
176 #if defined(CMD_HELPER_PERIODIC_FLUSH_CHECK)
177 void CommandBufferHelper::PeriodicFlushCheck() {
178 clock_t current_time = clock();
179 if (current_time - last_flush_time_ > kPeriodicFlushDelay * CLOCKS_PER_SEC)
184 // Calls Flush() and then waits until the buffer is empty. Break early if the
186 bool CommandBufferHelper::Finish() {
187 TRACE_EVENT0("gpu", "CommandBufferHelper::Finish");
191 // If there is no work just exit.
192 if (put_ == get_offset()) {
195 DCHECK(HaveRingBuffer());
197 // Do not loop forever if the flush fails, meaning the command buffer reader
201 } while (put_ != get_offset());
206 // Inserts a new token into the command stream. It uses an increasing value
207 // scheme so that we don't lose tokens (a token has passed if the current token
208 // value is higher than that token). Calls Finish() if the token value wraps,
209 // which will be rare.
210 int32 CommandBufferHelper::InsertToken() {
211 AllocateRingBuffer();
215 DCHECK(HaveRingBuffer());
216 // Increment token as 31-bit integer. Negative values are used to signal an
218 token_ = (token_ + 1) & 0x7FFFFFFF;
219 cmd::SetToken* cmd = GetCmdSpace<cmd::SetToken>();
223 TRACE_EVENT0("gpu", "CommandBufferHelper::InsertToken(wrapped)");
226 DCHECK_EQ(token_, last_token_read());
232 // Waits until the current token value is greater or equal to the value passed
234 void CommandBufferHelper::WaitForToken(int32 token) {
235 if (!usable() || !HaveRingBuffer()) {
238 // Return immediately if corresponding InsertToken failed.
241 if (token > token_) return; // we wrapped
242 while (last_token_read() < token) {
243 if (get_offset() == put_) {
244 LOG(FATAL) << "Empty command buffer while waiting on a token.";
247 // Do not loop forever if the flush fails, meaning the command buffer reader
254 // Waits for available entries, basically waiting until get >= put + count + 1.
255 // It actually waits for contiguous entries, so it may need to wrap the buffer
256 // around, adding a noops. Thus this function may change the value of put_. The
257 // function will return early if an error occurs, in which case the available
258 // space may not be available.
259 void CommandBufferHelper::WaitForAvailableEntries(int32 count) {
260 AllocateRingBuffer();
264 DCHECK(HaveRingBuffer());
265 DCHECK(count < total_entry_count_);
266 if (put_ + count > total_entry_count_) {
267 // There's not enough room between the current put and the end of the
268 // buffer, so we need to wrap. We will add noops all the way to the end,
269 // but we need to make sure get wraps first, actually that get is 1 or
270 // more (since put will wrap to 0 after we add the noops).
272 int32 curr_get = get_offset();
273 if (curr_get > put_ || curr_get == 0) {
274 TRACE_EVENT0("gpu", "CommandBufferHelper::WaitForAvailableEntries");
275 while (curr_get > put_ || curr_get == 0) {
276 // Do not loop forever if the flush fails, meaning the command buffer
277 // reader has shutdown.
280 curr_get = get_offset();
283 // Insert Noops to fill out the buffer.
284 int32 num_entries = total_entry_count_ - put_;
285 while (num_entries > 0) {
286 int32 num_to_skip = std::min(CommandHeader::kMaxSize, num_entries);
287 cmd::Noop::Set(&entries_[put_], num_to_skip);
289 num_entries -= num_to_skip;
294 // Try to get 'count' entries without flushing.
295 CalcImmediateEntries(count);
296 if (immediate_entry_count_ < count) {
297 // Try again with a shallow Flush().
299 CalcImmediateEntries(count);
300 if (immediate_entry_count_ < count) {
301 // Buffer is full. Need to wait for entries.
302 TRACE_EVENT0("gpu", "CommandBufferHelper::WaitForAvailableEntries1");
303 while (immediate_entry_count_ < count) {
304 // Do not loop forever if the flush fails, meaning the command buffer
305 // reader has shutdown.
308 CalcImmediateEntries(count);