blob: f4bb5d1f4123a8898fc1c7845d09ba8a790ef565 [file] [log] [blame]
[email protected]c4488402012-01-11 01:05:491// Copyright (c) 2012 The Chromium Authors. All rights reserved.
[email protected]1bee3982009-12-17 23:15:282// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
[email protected]96449d2c2009-11-25 00:01:324
5// This file contains the implementation of the command buffer helper class.
6
[email protected]1df19862013-05-24 11:26:297#include "gpu/command_buffer/client/cmd_buffer_helper.h"
[email protected]cf1aa982013-11-05 21:49:378
9#include "base/logging.h"
[email protected]1df19862013-05-24 11:26:2910#include "gpu/command_buffer/common/command_buffer.h"
11#include "gpu/command_buffer/common/trace_event.h"
[email protected]96449d2c2009-11-25 00:01:3212
[email protected]a7a27ace2009-12-12 00:11:2513namespace gpu {
[email protected]96449d2c2009-11-25 00:01:3214
[email protected]96449d2c2009-11-25 00:01:3215CommandBufferHelper::CommandBufferHelper(CommandBuffer* command_buffer)
16 : command_buffer_(command_buffer),
[email protected]503b3a22011-12-12 23:29:4017 ring_buffer_id_(-1),
18 ring_buffer_size_(0),
[email protected]96449d2c2009-11-25 00:01:3219 entries_(NULL),
[email protected]9310b262010-06-03 16:15:4720 total_entry_count_(0),
[email protected]15691b42014-02-12 00:56:0021 immediate_entry_count_(0),
[email protected]96449d2c2009-11-25 00:01:3222 token_(0),
[email protected]7d5b8d12011-01-14 23:43:1523 put_(0),
[email protected]b36897c12011-07-12 18:04:1024 last_put_sent_(0),
[email protected]15691b42014-02-12 00:56:0025#if defined(CMD_HELPER_PERIODIC_FLUSH_CHECK)
[email protected]b36897c12011-07-12 18:04:1026 commands_issued_(0),
[email protected]15691b42014-02-12 00:56:0027#endif
[email protected]c4488402012-01-11 01:05:4928 usable_(true),
[email protected]d35e6a72012-08-25 01:51:1329 context_lost_(false),
[email protected]362e6d602012-10-17 16:55:0630 flush_automatically_(true),
[email protected]3d668fb2014-02-22 00:49:3831 last_flush_time_(0),
32 flush_generation_(0) {
[email protected]96449d2c2009-11-25 00:01:3233}
34
[email protected]362e6d602012-10-17 16:55:0635void CommandBufferHelper::SetAutomaticFlushes(bool enabled) {
36 flush_automatically_ = enabled;
[email protected]15691b42014-02-12 00:56:0037 CalcImmediateEntries(0);
[email protected]362e6d602012-10-17 16:55:0638}
39
[email protected]d35e6a72012-08-25 01:51:1340bool CommandBufferHelper::IsContextLost() {
41 if (!context_lost_) {
42 context_lost_ = error::IsError(command_buffer()->GetLastError());
43 }
44 return context_lost_;
45}
46
[email protected]15691b42014-02-12 00:56:0047void CommandBufferHelper::CalcImmediateEntries(int waiting_count) {
48 DCHECK_GE(waiting_count, 0);
49
50 // Check if usable & allocated.
51 if (!usable() || !HaveRingBuffer()) {
52 immediate_entry_count_ = 0;
53 return;
54 }
55
56 // Get maximum safe contiguous entries.
57 const int32 curr_get = get_offset();
58 if (curr_get > put_) {
59 immediate_entry_count_ = curr_get - put_ - 1;
60 } else {
61 immediate_entry_count_ =
62 total_entry_count_ - put_ - (curr_get == 0 ? 1 : 0);
63 }
64
65 // Limit entry count to force early flushing.
66 if (flush_automatically_) {
67 int32 limit =
68 total_entry_count_ /
69 ((curr_get == last_put_sent_) ? kAutoFlushSmall : kAutoFlushBig);
70
71 int32 pending =
72 (put_ + total_entry_count_ - last_put_sent_) % total_entry_count_;
73
74 if (pending > 0 && pending >= limit) {
75 // Time to force flush.
76 immediate_entry_count_ = 0;
77 } else {
78 // Limit remaining entries, but not lower than waiting_count entries to
79 // prevent deadlock when command size is greater than the flush limit.
80 limit -= pending;
81 limit = limit < waiting_count ? waiting_count : limit;
82 immediate_entry_count_ =
83 immediate_entry_count_ > limit ? limit : immediate_entry_count_;
84 }
85 }
86}
87
[email protected]503b3a22011-12-12 23:29:4088bool CommandBufferHelper::AllocateRingBuffer() {
[email protected]c4488402012-01-11 01:05:4989 if (!usable()) {
90 return false;
91 }
92
[email protected]617296e2011-12-15 05:37:5793 if (HaveRingBuffer()) {
94 return true;
95 }
96
[email protected]67c80782012-12-21 01:16:5297 int32 id = -1;
98 Buffer buffer = command_buffer_->CreateTransferBuffer(ring_buffer_size_, &id);
[email protected]503b3a22011-12-12 23:29:4099 if (id < 0) {
[email protected]c4488402012-01-11 01:05:49100 ClearUsable();
[email protected]503b3a22011-12-12 23:29:40101 return false;
102 }
103
[email protected]67c80782012-12-21 01:16:52104 ring_buffer_ = buffer;
[email protected]503b3a22011-12-12 23:29:40105 ring_buffer_id_ = id;
106 command_buffer_->SetGetBuffer(id);
107
108 // TODO(gman): Do we really need to call GetState here? We know get & put = 0
109 // Also do we need to check state.num_entries?
[email protected]c77ea362010-01-29 22:02:36110 CommandBuffer::State state = command_buffer_->GetState();
[email protected]7477ea6f2009-12-22 23:28:15111 entries_ = static_cast<CommandBufferEntry*>(ring_buffer_.ptr);
[email protected]503b3a22011-12-12 23:29:40112 int32 num_ring_buffer_entries =
113 ring_buffer_size_ / sizeof(CommandBufferEntry);
[email protected]b21265f2010-05-12 17:05:13114 if (num_ring_buffer_entries > state.num_entries) {
[email protected]c4488402012-01-11 01:05:49115 ClearUsable();
[email protected]b21265f2010-05-12 17:05:13116 return false;
117 }
[email protected]9310b262010-06-03 16:15:47118
[email protected]9310b262010-06-03 16:15:47119 total_entry_count_ = num_ring_buffer_entries;
[email protected]c77ea362010-01-29 22:02:36120 put_ = state.put_offset;
[email protected]15691b42014-02-12 00:56:00121 CalcImmediateEntries(0);
[email protected]96449d2c2009-11-25 00:01:32122 return true;
123}
124
[email protected]a5cf3cb2012-08-23 01:08:42125void CommandBufferHelper::FreeResources() {
[email protected]617296e2011-12-15 05:37:57126 if (HaveRingBuffer()) {
127 command_buffer_->DestroyTransferBuffer(ring_buffer_id_);
128 ring_buffer_id_ = -1;
[email protected]15691b42014-02-12 00:56:00129 CalcImmediateEntries(0);
[email protected]617296e2011-12-15 05:37:57130 }
131}
132
[email protected]a5cf3cb2012-08-23 01:08:42133void CommandBufferHelper::FreeRingBuffer() {
[email protected]cf1aa982013-11-05 21:49:37134 CHECK((put_ == get_offset()) ||
[email protected]ea51dbd2013-01-18 10:03:53135 error::IsError(command_buffer_->GetLastState().error));
[email protected]a5cf3cb2012-08-23 01:08:42136 FreeResources();
137}
138
[email protected]503b3a22011-12-12 23:29:40139bool CommandBufferHelper::Initialize(int32 ring_buffer_size) {
140 ring_buffer_size_ = ring_buffer_size;
141 return AllocateRingBuffer();
142}
143
[email protected]96449d2c2009-11-25 00:01:32144CommandBufferHelper::~CommandBufferHelper() {
[email protected]a5cf3cb2012-08-23 01:08:42145 FreeResources();
[email protected]96449d2c2009-11-25 00:01:32146}
147
[email protected]7d5b8d12011-01-14 23:43:15148bool CommandBufferHelper::FlushSync() {
[email protected]c4488402012-01-11 01:05:49149 if (!usable()) {
150 return false;
151 }
[email protected]15691b42014-02-12 00:56:00152
153 // Wrap put_ before flush.
154 if (put_ == total_entry_count_)
155 put_ = 0;
156
[email protected]14eb04b2011-09-08 00:27:07157 last_flush_time_ = clock();
[email protected]7d5b8d12011-01-14 23:43:15158 last_put_sent_ = put_;
[email protected]d0f02c42011-07-21 21:40:48159 CommandBuffer::State state = command_buffer_->FlushSync(put_, get_offset());
[email protected]cbe0ded2014-02-21 20:42:52160 ++flush_generation_;
[email protected]15691b42014-02-12 00:56:00161 CalcImmediateEntries(0);
[email protected]f7a64ee2010-02-01 22:24:14162 return state.error == error::kNoError;
[email protected]96449d2c2009-11-25 00:01:32163}
164
[email protected]7d5b8d12011-01-14 23:43:15165void CommandBufferHelper::Flush() {
[email protected]15691b42014-02-12 00:56:00166 // Wrap put_ before flush.
167 if (put_ == total_entry_count_)
168 put_ = 0;
169
[email protected]9f5ab392013-01-30 12:47:13170 if (usable() && last_put_sent_ != put_) {
[email protected]c4488402012-01-11 01:05:49171 last_flush_time_ = clock();
172 last_put_sent_ = put_;
173 command_buffer_->Flush(put_);
[email protected]cbe0ded2014-02-21 20:42:52174 ++flush_generation_;
[email protected]15691b42014-02-12 00:56:00175 CalcImmediateEntries(0);
[email protected]c4488402012-01-11 01:05:49176 }
[email protected]7d5b8d12011-01-14 23:43:15177}
178
[email protected]15691b42014-02-12 00:56:00179#if defined(CMD_HELPER_PERIODIC_FLUSH_CHECK)
180void CommandBufferHelper::PeriodicFlushCheck() {
181 clock_t current_time = clock();
182 if (current_time - last_flush_time_ > kPeriodicFlushDelay * CLOCKS_PER_SEC)
183 Flush();
184}
185#endif
186
[email protected]96449d2c2009-11-25 00:01:32187// Calls Flush() and then waits until the buffer is empty. Break early if the
188// error is set.
189bool CommandBufferHelper::Finish() {
[email protected]366ae242011-05-10 02:23:58190 TRACE_EVENT0("gpu", "CommandBufferHelper::Finish");
[email protected]c4488402012-01-11 01:05:49191 if (!usable()) {
192 return false;
193 }
[email protected]a24e7582012-02-15 23:21:32194 // If there is no work just exit.
195 if (put_ == get_offset()) {
196 return true;
197 }
[email protected]cf1aa982013-11-05 21:49:37198 DCHECK(HaveRingBuffer());
[email protected]96449d2c2009-11-25 00:01:32199 do {
200 // Do not loop forever if the flush fails, meaning the command buffer reader
[email protected]c77ea362010-01-29 22:02:36201 // has shutdown.
[email protected]7d5b8d12011-01-14 23:43:15202 if (!FlushSync())
[email protected]96449d2c2009-11-25 00:01:32203 return false;
[email protected]d0f02c42011-07-21 21:40:48204 } while (put_ != get_offset());
[email protected]96449d2c2009-11-25 00:01:32205
206 return true;
207}
208
209// Inserts a new token into the command stream. It uses an increasing value
210// scheme so that we don't lose tokens (a token has passed if the current token
211// value is higher than that token). Calls Finish() if the token value wraps,
212// which will be rare.
213int32 CommandBufferHelper::InsertToken() {
[email protected]617296e2011-12-15 05:37:57214 AllocateRingBuffer();
[email protected]c4488402012-01-11 01:05:49215 if (!usable()) {
216 return token_;
217 }
[email protected]cf1aa982013-11-05 21:49:37218 DCHECK(HaveRingBuffer());
[email protected]96449d2c2009-11-25 00:01:32219 // Increment token as 31-bit integer. Negative values are used to signal an
220 // error.
221 token_ = (token_ + 1) & 0x7FFFFFFF;
[email protected]c4488402012-01-11 01:05:49222 cmd::SetToken* cmd = GetCmdSpace<cmd::SetToken>();
223 if (cmd) {
224 cmd->Init(token_);
225 if (token_ == 0) {
226 TRACE_EVENT0("gpu", "CommandBufferHelper::InsertToken(wrapped)");
227 // we wrapped
228 Finish();
[email protected]cf1aa982013-11-05 21:49:37229 DCHECK_EQ(token_, last_token_read());
[email protected]c4488402012-01-11 01:05:49230 }
[email protected]96449d2c2009-11-25 00:01:32231 }
232 return token_;
233}
234
235// Waits until the current token value is greater or equal to the value passed
236// in argument.
237void CommandBufferHelper::WaitForToken(int32 token) {
[email protected]ea51dbd2013-01-18 10:03:53238 if (!usable() || !HaveRingBuffer()) {
[email protected]c4488402012-01-11 01:05:49239 return;
240 }
[email protected]96449d2c2009-11-25 00:01:32241 // Return immediately if corresponding InsertToken failed.
242 if (token < 0)
243 return;
[email protected]96449d2c2009-11-25 00:01:32244 if (token > token_) return; // we wrapped
[email protected]d0f02c42011-07-21 21:40:48245 while (last_token_read() < token) {
246 if (get_offset() == put_) {
[email protected]cf1aa982013-11-05 21:49:37247 LOG(FATAL) << "Empty command buffer while waiting on a token.";
[email protected]96449d2c2009-11-25 00:01:32248 return;
249 }
250 // Do not loop forever if the flush fails, meaning the command buffer reader
251 // has shutdown.
[email protected]7d5b8d12011-01-14 23:43:15252 if (!FlushSync())
[email protected]96449d2c2009-11-25 00:01:32253 return;
[email protected]96449d2c2009-11-25 00:01:32254 }
255}
256
257// Waits for available entries, basically waiting until get >= put + count + 1.
258// It actually waits for contiguous entries, so it may need to wrap the buffer
[email protected]47257372013-01-04 18:37:48259// around, adding a noops. Thus this function may change the value of put_. The
[email protected]9310b262010-06-03 16:15:47260// function will return early if an error occurs, in which case the available
261// space may not be available.
[email protected]3110b122013-11-19 23:25:54262void CommandBufferHelper::WaitForAvailableEntries(int32 count) {
263 AllocateRingBuffer();
264 if (!usable()) {
265 return;
266 }
267 DCHECK(HaveRingBuffer());
[email protected]cf1aa982013-11-05 21:49:37268 DCHECK(count < total_entry_count_);
[email protected]47257372013-01-04 18:37:48269 if (put_ + count > total_entry_count_) {
[email protected]96449d2c2009-11-25 00:01:32270 // There's not enough room between the current put and the end of the
[email protected]47257372013-01-04 18:37:48271 // buffer, so we need to wrap. We will add noops all the way to the end,
272 // but we need to make sure get wraps first, actually that get is 1 or
273 // more (since put will wrap to 0 after we add the noops).
[email protected]cf1aa982013-11-05 21:49:37274 DCHECK_LE(1, put_);
[email protected]15691b42014-02-12 00:56:00275 int32 curr_get = get_offset();
276 if (curr_get > put_ || curr_get == 0) {
[email protected]366ae242011-05-10 02:23:58277 TRACE_EVENT0("gpu", "CommandBufferHelper::WaitForAvailableEntries");
[email protected]15691b42014-02-12 00:56:00278 while (curr_get > put_ || curr_get == 0) {
[email protected]3a69c6fe2011-04-14 22:07:34279 // Do not loop forever if the flush fails, meaning the command buffer
280 // reader has shutdown.
281 if (!FlushSync())
[email protected]3110b122013-11-19 23:25:54282 return;
[email protected]15691b42014-02-12 00:56:00283 curr_get = get_offset();
[email protected]3a69c6fe2011-04-14 22:07:34284 }
[email protected]96449d2c2009-11-25 00:01:32285 }
[email protected]47257372013-01-04 18:37:48286 // Insert Noops to fill out the buffer.
287 int32 num_entries = total_entry_count_ - put_;
288 while (num_entries > 0) {
289 int32 num_to_skip = std::min(CommandHeader::kMaxSize, num_entries);
290 cmd::Noop::Set(&entries_[put_], num_to_skip);
291 put_ += num_to_skip;
292 num_entries -= num_to_skip;
293 }
[email protected]96449d2c2009-11-25 00:01:32294 put_ = 0;
295 }
[email protected]15691b42014-02-12 00:56:00296
297 // Try to get 'count' entries without flushing.
298 CalcImmediateEntries(count);
299 if (immediate_entry_count_ < count) {
300 // Try again with a shallow Flush().
[email protected]3110b122013-11-19 23:25:54301 Flush();
[email protected]15691b42014-02-12 00:56:00302 CalcImmediateEntries(count);
303 if (immediate_entry_count_ < count) {
304 // Buffer is full. Need to wait for entries.
305 TRACE_EVENT0("gpu", "CommandBufferHelper::WaitForAvailableEntries1");
306 while (immediate_entry_count_ < count) {
307 // Do not loop forever if the flush fails, meaning the command buffer
308 // reader has shutdown.
309 if (!FlushSync())
310 return;
311 CalcImmediateEntries(count);
312 }
313 }
[email protected]3110b122013-11-19 23:25:54314 }
[email protected]96449d2c2009-11-25 00:01:32315}
316
[email protected]96449d2c2009-11-25 00:01:32317
[email protected]a7a27ace2009-12-12 00:11:25318} // namespace gpu