blob: 2b88cc1e7b135b0eb0802a0a98b5855404e22305 [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
martina.kollarovac640df12015-07-10 08:30:389#include <algorithm>
[email protected]cf1aa982013-11-05 21:49:3710#include "base/logging.h"
[email protected]da618e22014-07-11 09:27:1611#include "base/time/time.h"
martina.kollarovac640df12015-07-10 08:30:3812#include "gpu/command_buffer/common/buffer.h"
[email protected]1df19862013-05-24 11:26:2913#include "gpu/command_buffer/common/command_buffer.h"
martina.kollarovac640df12015-07-10 08:30:3814#include "gpu/command_buffer/common/constants.h"
[email protected]1df19862013-05-24 11:26:2915#include "gpu/command_buffer/common/trace_event.h"
[email protected]96449d2c2009-11-25 00:01:3216
[email protected]a7a27ace2009-12-12 00:11:2517namespace gpu {
[email protected]96449d2c2009-11-25 00:01:3218
[email protected]96449d2c2009-11-25 00:01:3219CommandBufferHelper::CommandBufferHelper(CommandBuffer* command_buffer)
20 : command_buffer_(command_buffer),
[email protected]503b3a22011-12-12 23:29:4021 ring_buffer_id_(-1),
22 ring_buffer_size_(0),
[email protected]96449d2c2009-11-25 00:01:3223 entries_(NULL),
[email protected]9310b262010-06-03 16:15:4724 total_entry_count_(0),
[email protected]15691b42014-02-12 00:56:0025 immediate_entry_count_(0),
[email protected]96449d2c2009-11-25 00:01:3226 token_(0),
[email protected]7d5b8d12011-01-14 23:43:1527 put_(0),
[email protected]b36897c12011-07-12 18:04:1028 last_put_sent_(0),
[email protected]15691b42014-02-12 00:56:0029#if defined(CMD_HELPER_PERIODIC_FLUSH_CHECK)
[email protected]b36897c12011-07-12 18:04:1030 commands_issued_(0),
[email protected]15691b42014-02-12 00:56:0031#endif
[email protected]c4488402012-01-11 01:05:4932 usable_(true),
[email protected]d35e6a72012-08-25 01:51:1333 context_lost_(false),
[email protected]362e6d602012-10-17 16:55:0634 flush_automatically_(true),
[email protected]3d668fb2014-02-22 00:49:3835 flush_generation_(0) {
[email protected]96449d2c2009-11-25 00:01:3236}
37
[email protected]362e6d602012-10-17 16:55:0638void CommandBufferHelper::SetAutomaticFlushes(bool enabled) {
39 flush_automatically_ = enabled;
[email protected]15691b42014-02-12 00:56:0040 CalcImmediateEntries(0);
[email protected]362e6d602012-10-17 16:55:0641}
42
[email protected]d35e6a72012-08-25 01:51:1343bool CommandBufferHelper::IsContextLost() {
44 if (!context_lost_) {
45 context_lost_ = error::IsError(command_buffer()->GetLastError());
46 }
47 return context_lost_;
48}
49
[email protected]15691b42014-02-12 00:56:0050void CommandBufferHelper::CalcImmediateEntries(int waiting_count) {
51 DCHECK_GE(waiting_count, 0);
52
53 // Check if usable & allocated.
54 if (!usable() || !HaveRingBuffer()) {
55 immediate_entry_count_ = 0;
56 return;
57 }
58
59 // Get maximum safe contiguous entries.
60 const int32 curr_get = get_offset();
61 if (curr_get > put_) {
62 immediate_entry_count_ = curr_get - put_ - 1;
63 } else {
64 immediate_entry_count_ =
65 total_entry_count_ - put_ - (curr_get == 0 ? 1 : 0);
66 }
67
68 // Limit entry count to force early flushing.
69 if (flush_automatically_) {
70 int32 limit =
71 total_entry_count_ /
72 ((curr_get == last_put_sent_) ? kAutoFlushSmall : kAutoFlushBig);
73
74 int32 pending =
75 (put_ + total_entry_count_ - last_put_sent_) % total_entry_count_;
76
77 if (pending > 0 && pending >= limit) {
78 // Time to force flush.
79 immediate_entry_count_ = 0;
80 } else {
81 // Limit remaining entries, but not lower than waiting_count entries to
82 // prevent deadlock when command size is greater than the flush limit.
83 limit -= pending;
84 limit = limit < waiting_count ? waiting_count : limit;
85 immediate_entry_count_ =
86 immediate_entry_count_ > limit ? limit : immediate_entry_count_;
87 }
88 }
89}
90
[email protected]503b3a22011-12-12 23:29:4091bool CommandBufferHelper::AllocateRingBuffer() {
[email protected]c4488402012-01-11 01:05:4992 if (!usable()) {
93 return false;
94 }
95
[email protected]617296e2011-12-15 05:37:5796 if (HaveRingBuffer()) {
97 return true;
98 }
99
[email protected]67c80782012-12-21 01:16:52100 int32 id = -1;
[email protected]44096602014-03-26 04:53:58101 scoped_refptr<Buffer> buffer =
102 command_buffer_->CreateTransferBuffer(ring_buffer_size_, &id);
[email protected]503b3a22011-12-12 23:29:40103 if (id < 0) {
[email protected]c4488402012-01-11 01:05:49104 ClearUsable();
[email protected]503b3a22011-12-12 23:29:40105 return false;
106 }
107
[email protected]67c80782012-12-21 01:16:52108 ring_buffer_ = buffer;
[email protected]503b3a22011-12-12 23:29:40109 ring_buffer_id_ = id;
110 command_buffer_->SetGetBuffer(id);
[email protected]44096602014-03-26 04:53:58111 entries_ = static_cast<CommandBufferEntry*>(ring_buffer_->memory());
[email protected]bae23772014-04-16 09:50:55112 total_entry_count_ = ring_buffer_size_ / sizeof(CommandBufferEntry);
113 // Call to SetGetBuffer(id) above resets get and put offsets to 0.
114 // No need to query it through IPC.
115 put_ = 0;
[email protected]15691b42014-02-12 00:56:00116 CalcImmediateEntries(0);
[email protected]96449d2c2009-11-25 00:01:32117 return true;
118}
119
[email protected]a5cf3cb2012-08-23 01:08:42120void CommandBufferHelper::FreeResources() {
[email protected]617296e2011-12-15 05:37:57121 if (HaveRingBuffer()) {
122 command_buffer_->DestroyTransferBuffer(ring_buffer_id_);
123 ring_buffer_id_ = -1;
[email protected]15691b42014-02-12 00:56:00124 CalcImmediateEntries(0);
[email protected]617296e2011-12-15 05:37:57125 }
126}
127
[email protected]a5cf3cb2012-08-23 01:08:42128void CommandBufferHelper::FreeRingBuffer() {
[email protected]cf1aa982013-11-05 21:49:37129 CHECK((put_ == get_offset()) ||
[email protected]ea51dbd2013-01-18 10:03:53130 error::IsError(command_buffer_->GetLastState().error));
[email protected]a5cf3cb2012-08-23 01:08:42131 FreeResources();
132}
133
[email protected]503b3a22011-12-12 23:29:40134bool CommandBufferHelper::Initialize(int32 ring_buffer_size) {
135 ring_buffer_size_ = ring_buffer_size;
136 return AllocateRingBuffer();
137}
138
[email protected]96449d2c2009-11-25 00:01:32139CommandBufferHelper::~CommandBufferHelper() {
[email protected]a5cf3cb2012-08-23 01:08:42140 FreeResources();
[email protected]96449d2c2009-11-25 00:01:32141}
142
[email protected]7fe4198b2014-03-18 21:52:36143bool CommandBufferHelper::WaitForGetOffsetInRange(int32 start, int32 end) {
[email protected]c4488402012-01-11 01:05:49144 if (!usable()) {
145 return false;
146 }
[email protected]7fe4198b2014-03-18 21:52:36147 command_buffer_->WaitForGetOffsetInRange(start, end);
148 return command_buffer_->GetLastError() == gpu::error::kNoError;
[email protected]96449d2c2009-11-25 00:01:32149}
150
[email protected]7d5b8d12011-01-14 23:43:15151void CommandBufferHelper::Flush() {
[email protected]15691b42014-02-12 00:56:00152 // Wrap put_ before flush.
153 if (put_ == total_entry_count_)
154 put_ = 0;
155
vmiurab700b432015-02-06 16:42:51156 if (usable()) {
[email protected]da618e22014-07-11 09:27:16157 last_flush_time_ = base::TimeTicks::Now();
[email protected]c4488402012-01-11 01:05:49158 last_put_sent_ = put_;
159 command_buffer_->Flush(put_);
[email protected]cbe0ded2014-02-21 20:42:52160 ++flush_generation_;
[email protected]15691b42014-02-12 00:56:00161 CalcImmediateEntries(0);
[email protected]c4488402012-01-11 01:05:49162 }
[email protected]7d5b8d12011-01-14 23:43:15163}
164
vmiurab700b432015-02-06 16:42:51165void CommandBufferHelper::OrderingBarrier() {
166 // Wrap put_ before setting the barrier.
167 if (put_ == total_entry_count_)
168 put_ = 0;
169
170 if (usable()) {
171 command_buffer_->OrderingBarrier(put_);
172 ++flush_generation_;
173 CalcImmediateEntries(0);
174 }
175}
176
[email protected]15691b42014-02-12 00:56:00177#if defined(CMD_HELPER_PERIODIC_FLUSH_CHECK)
178void CommandBufferHelper::PeriodicFlushCheck() {
[email protected]da618e22014-07-11 09:27:16179 base::TimeTicks current_time = base::TimeTicks::Now();
180 if (current_time - last_flush_time_ >
181 base::TimeDelta::FromMicroseconds(kPeriodicFlushDelayInMicroseconds)) {
[email protected]15691b42014-02-12 00:56:00182 Flush();
[email protected]da618e22014-07-11 09:27:16183 }
[email protected]15691b42014-02-12 00:56:00184}
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]7fe4198b2014-03-18 21:52:36199 Flush();
200 if (!WaitForGetOffsetInRange(put_, put_))
201 return false;
202 DCHECK_EQ(get_offset(), put_);
203
204 CalcImmediateEntries(0);
[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]03e9c1d2014-05-06 18:00:33245 if (last_token_read() >= token)
[email protected]7fe4198b2014-03-18 21:52:36246 return;
247 Flush();
248 command_buffer_->WaitForTokenInRange(token, token_);
[email protected]96449d2c2009-11-25 00:01:32249}
250
251// Waits for available entries, basically waiting until get >= put + count + 1.
252// It actually waits for contiguous entries, so it may need to wrap the buffer
[email protected]47257372013-01-04 18:37:48253// around, adding a noops. Thus this function may change the value of put_. The
[email protected]9310b262010-06-03 16:15:47254// function will return early if an error occurs, in which case the available
255// space may not be available.
[email protected]3110b122013-11-19 23:25:54256void CommandBufferHelper::WaitForAvailableEntries(int32 count) {
257 AllocateRingBuffer();
258 if (!usable()) {
259 return;
260 }
261 DCHECK(HaveRingBuffer());
[email protected]cf1aa982013-11-05 21:49:37262 DCHECK(count < total_entry_count_);
[email protected]47257372013-01-04 18:37:48263 if (put_ + count > total_entry_count_) {
[email protected]96449d2c2009-11-25 00:01:32264 // There's not enough room between the current put and the end of the
[email protected]47257372013-01-04 18:37:48265 // buffer, so we need to wrap. We will add noops all the way to the end,
266 // but we need to make sure get wraps first, actually that get is 1 or
267 // more (since put will wrap to 0 after we add the noops).
[email protected]cf1aa982013-11-05 21:49:37268 DCHECK_LE(1, put_);
[email protected]15691b42014-02-12 00:56:00269 int32 curr_get = get_offset();
270 if (curr_get > put_ || curr_get == 0) {
[email protected]366ae242011-05-10 02:23:58271 TRACE_EVENT0("gpu", "CommandBufferHelper::WaitForAvailableEntries");
[email protected]7fe4198b2014-03-18 21:52:36272 Flush();
273 if (!WaitForGetOffsetInRange(1, put_))
274 return;
275 curr_get = get_offset();
276 DCHECK_LE(curr_get, put_);
277 DCHECK_NE(0, curr_get);
[email protected]96449d2c2009-11-25 00:01:32278 }
[email protected]47257372013-01-04 18:37:48279 // Insert Noops to fill out the buffer.
280 int32 num_entries = total_entry_count_ - put_;
281 while (num_entries > 0) {
282 int32 num_to_skip = std::min(CommandHeader::kMaxSize, num_entries);
283 cmd::Noop::Set(&entries_[put_], num_to_skip);
284 put_ += num_to_skip;
285 num_entries -= num_to_skip;
286 }
[email protected]96449d2c2009-11-25 00:01:32287 put_ = 0;
288 }
[email protected]15691b42014-02-12 00:56:00289
290 // Try to get 'count' entries without flushing.
291 CalcImmediateEntries(count);
292 if (immediate_entry_count_ < count) {
293 // Try again with a shallow Flush().
[email protected]3110b122013-11-19 23:25:54294 Flush();
[email protected]15691b42014-02-12 00:56:00295 CalcImmediateEntries(count);
296 if (immediate_entry_count_ < count) {
297 // Buffer is full. Need to wait for entries.
298 TRACE_EVENT0("gpu", "CommandBufferHelper::WaitForAvailableEntries1");
[email protected]7fe4198b2014-03-18 21:52:36299 if (!WaitForGetOffsetInRange(put_ + count + 1, put_))
300 return;
301 CalcImmediateEntries(count);
302 DCHECK_GE(immediate_entry_count_, count);
[email protected]15691b42014-02-12 00:56:00303 }
[email protected]3110b122013-11-19 23:25:54304 }
[email protected]96449d2c2009-11-25 00:01:32305}
306
[email protected]96449d2c2009-11-25 00:01:32307
[email protected]a7a27ace2009-12-12 00:11:25308} // namespace gpu