blob: 11219ede664751e3fa92a2dcc800388f8d4d3f5c [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]9498e972012-10-04 03:48:2815const int kCommandsPerFlushCheck = 100;
[email protected]621a27b2013-10-20 05:25:4716
17#if !defined(OS_ANDROID)
[email protected]9498e972012-10-04 03:48:2818const double kFlushDelay = 1.0 / (5.0 * 60.0);
[email protected]621a27b2013-10-20 05:25:4719#endif
[email protected]9498e972012-10-04 03:48:2820
[email protected]96449d2c2009-11-25 00:01:3221CommandBufferHelper::CommandBufferHelper(CommandBuffer* command_buffer)
22 : command_buffer_(command_buffer),
[email protected]503b3a22011-12-12 23:29:4023 ring_buffer_id_(-1),
24 ring_buffer_size_(0),
[email protected]96449d2c2009-11-25 00:01:3225 entries_(NULL),
[email protected]9310b262010-06-03 16:15:4726 total_entry_count_(0),
[email protected]96449d2c2009-11-25 00:01:3227 token_(0),
[email protected]7d5b8d12011-01-14 23:43:1528 put_(0),
[email protected]b36897c12011-07-12 18:04:1029 last_put_sent_(0),
30 commands_issued_(0),
[email protected]c4488402012-01-11 01:05:4931 usable_(true),
[email protected]d35e6a72012-08-25 01:51:1332 context_lost_(false),
[email protected]362e6d602012-10-17 16:55:0633 flush_automatically_(true),
[email protected]b36897c12011-07-12 18:04:1034 last_flush_time_(0) {
[email protected]96449d2c2009-11-25 00:01:3235}
36
[email protected]362e6d602012-10-17 16:55:0637void CommandBufferHelper::SetAutomaticFlushes(bool enabled) {
38 flush_automatically_ = enabled;
39}
40
[email protected]d35e6a72012-08-25 01:51:1341bool CommandBufferHelper::IsContextLost() {
42 if (!context_lost_) {
43 context_lost_ = error::IsError(command_buffer()->GetLastError());
44 }
45 return context_lost_;
46}
47
[email protected]503b3a22011-12-12 23:29:4048bool CommandBufferHelper::AllocateRingBuffer() {
[email protected]c4488402012-01-11 01:05:4949 if (!usable()) {
50 return false;
51 }
52
[email protected]617296e2011-12-15 05:37:5753 if (HaveRingBuffer()) {
54 return true;
55 }
56
[email protected]67c80782012-12-21 01:16:5257 int32 id = -1;
58 Buffer buffer = command_buffer_->CreateTransferBuffer(ring_buffer_size_, &id);
[email protected]503b3a22011-12-12 23:29:4059 if (id < 0) {
[email protected]c4488402012-01-11 01:05:4960 ClearUsable();
[email protected]503b3a22011-12-12 23:29:4061 return false;
62 }
63
[email protected]67c80782012-12-21 01:16:5264 ring_buffer_ = buffer;
[email protected]503b3a22011-12-12 23:29:4065 ring_buffer_id_ = id;
66 command_buffer_->SetGetBuffer(id);
67
68 // TODO(gman): Do we really need to call GetState here? We know get & put = 0
69 // Also do we need to check state.num_entries?
[email protected]c77ea362010-01-29 22:02:3670 CommandBuffer::State state = command_buffer_->GetState();
[email protected]7477ea6f2009-12-22 23:28:1571 entries_ = static_cast<CommandBufferEntry*>(ring_buffer_.ptr);
[email protected]503b3a22011-12-12 23:29:4072 int32 num_ring_buffer_entries =
73 ring_buffer_size_ / sizeof(CommandBufferEntry);
[email protected]b21265f2010-05-12 17:05:1374 if (num_ring_buffer_entries > state.num_entries) {
[email protected]c4488402012-01-11 01:05:4975 ClearUsable();
[email protected]b21265f2010-05-12 17:05:1376 return false;
77 }
[email protected]9310b262010-06-03 16:15:4778
[email protected]9310b262010-06-03 16:15:4779 total_entry_count_ = num_ring_buffer_entries;
[email protected]c77ea362010-01-29 22:02:3680 put_ = state.put_offset;
[email protected]96449d2c2009-11-25 00:01:3281 return true;
82}
83
[email protected]a5cf3cb2012-08-23 01:08:4284void CommandBufferHelper::FreeResources() {
[email protected]617296e2011-12-15 05:37:5785 if (HaveRingBuffer()) {
86 command_buffer_->DestroyTransferBuffer(ring_buffer_id_);
87 ring_buffer_id_ = -1;
88 }
89}
90
[email protected]a5cf3cb2012-08-23 01:08:4291void CommandBufferHelper::FreeRingBuffer() {
[email protected]cf1aa982013-11-05 21:49:3792 CHECK((put_ == get_offset()) ||
[email protected]ea51dbd2013-01-18 10:03:5393 error::IsError(command_buffer_->GetLastState().error));
[email protected]a5cf3cb2012-08-23 01:08:4294 FreeResources();
95}
96
[email protected]503b3a22011-12-12 23:29:4097bool CommandBufferHelper::Initialize(int32 ring_buffer_size) {
98 ring_buffer_size_ = ring_buffer_size;
99 return AllocateRingBuffer();
100}
101
[email protected]96449d2c2009-11-25 00:01:32102CommandBufferHelper::~CommandBufferHelper() {
[email protected]a5cf3cb2012-08-23 01:08:42103 FreeResources();
[email protected]96449d2c2009-11-25 00:01:32104}
105
[email protected]7d5b8d12011-01-14 23:43:15106bool CommandBufferHelper::FlushSync() {
[email protected]c4488402012-01-11 01:05:49107 if (!usable()) {
108 return false;
109 }
[email protected]14eb04b2011-09-08 00:27:07110 last_flush_time_ = clock();
[email protected]7d5b8d12011-01-14 23:43:15111 last_put_sent_ = put_;
[email protected]d0f02c42011-07-21 21:40:48112 CommandBuffer::State state = command_buffer_->FlushSync(put_, get_offset());
[email protected]f7a64ee2010-02-01 22:24:14113 return state.error == error::kNoError;
[email protected]96449d2c2009-11-25 00:01:32114}
115
[email protected]7d5b8d12011-01-14 23:43:15116void CommandBufferHelper::Flush() {
[email protected]9f5ab392013-01-30 12:47:13117 if (usable() && last_put_sent_ != put_) {
[email protected]c4488402012-01-11 01:05:49118 last_flush_time_ = clock();
119 last_put_sent_ = put_;
120 command_buffer_->Flush(put_);
121 }
[email protected]7d5b8d12011-01-14 23:43:15122}
123
[email protected]96449d2c2009-11-25 00:01:32124// Calls Flush() and then waits until the buffer is empty. Break early if the
125// error is set.
126bool CommandBufferHelper::Finish() {
[email protected]366ae242011-05-10 02:23:58127 TRACE_EVENT0("gpu", "CommandBufferHelper::Finish");
[email protected]c4488402012-01-11 01:05:49128 if (!usable()) {
129 return false;
130 }
[email protected]a24e7582012-02-15 23:21:32131 // If there is no work just exit.
132 if (put_ == get_offset()) {
133 return true;
134 }
[email protected]cf1aa982013-11-05 21:49:37135 DCHECK(HaveRingBuffer());
[email protected]96449d2c2009-11-25 00:01:32136 do {
137 // Do not loop forever if the flush fails, meaning the command buffer reader
[email protected]c77ea362010-01-29 22:02:36138 // has shutdown.
[email protected]7d5b8d12011-01-14 23:43:15139 if (!FlushSync())
[email protected]96449d2c2009-11-25 00:01:32140 return false;
[email protected]d0f02c42011-07-21 21:40:48141 } while (put_ != get_offset());
[email protected]96449d2c2009-11-25 00:01:32142
143 return true;
144}
145
146// Inserts a new token into the command stream. It uses an increasing value
147// scheme so that we don't lose tokens (a token has passed if the current token
148// value is higher than that token). Calls Finish() if the token value wraps,
149// which will be rare.
150int32 CommandBufferHelper::InsertToken() {
[email protected]617296e2011-12-15 05:37:57151 AllocateRingBuffer();
[email protected]c4488402012-01-11 01:05:49152 if (!usable()) {
153 return token_;
154 }
[email protected]cf1aa982013-11-05 21:49:37155 DCHECK(HaveRingBuffer());
[email protected]96449d2c2009-11-25 00:01:32156 // Increment token as 31-bit integer. Negative values are used to signal an
157 // error.
158 token_ = (token_ + 1) & 0x7FFFFFFF;
[email protected]c4488402012-01-11 01:05:49159 cmd::SetToken* cmd = GetCmdSpace<cmd::SetToken>();
160 if (cmd) {
161 cmd->Init(token_);
162 if (token_ == 0) {
163 TRACE_EVENT0("gpu", "CommandBufferHelper::InsertToken(wrapped)");
164 // we wrapped
165 Finish();
[email protected]cf1aa982013-11-05 21:49:37166 DCHECK_EQ(token_, last_token_read());
[email protected]c4488402012-01-11 01:05:49167 }
[email protected]96449d2c2009-11-25 00:01:32168 }
169 return token_;
170}
171
172// Waits until the current token value is greater or equal to the value passed
173// in argument.
174void CommandBufferHelper::WaitForToken(int32 token) {
[email protected]ea51dbd2013-01-18 10:03:53175 if (!usable() || !HaveRingBuffer()) {
[email protected]c4488402012-01-11 01:05:49176 return;
177 }
[email protected]96449d2c2009-11-25 00:01:32178 // Return immediately if corresponding InsertToken failed.
179 if (token < 0)
180 return;
[email protected]96449d2c2009-11-25 00:01:32181 if (token > token_) return; // we wrapped
[email protected]d0f02c42011-07-21 21:40:48182 while (last_token_read() < token) {
183 if (get_offset() == put_) {
[email protected]cf1aa982013-11-05 21:49:37184 LOG(FATAL) << "Empty command buffer while waiting on a token.";
[email protected]96449d2c2009-11-25 00:01:32185 return;
186 }
187 // Do not loop forever if the flush fails, meaning the command buffer reader
188 // has shutdown.
[email protected]7d5b8d12011-01-14 23:43:15189 if (!FlushSync())
[email protected]96449d2c2009-11-25 00:01:32190 return;
[email protected]96449d2c2009-11-25 00:01:32191 }
192}
193
194// Waits for available entries, basically waiting until get >= put + count + 1.
195// It actually waits for contiguous entries, so it may need to wrap the buffer
[email protected]47257372013-01-04 18:37:48196// around, adding a noops. Thus this function may change the value of put_. The
[email protected]9310b262010-06-03 16:15:47197// function will return early if an error occurs, in which case the available
198// space may not be available.
[email protected]96449d2c2009-11-25 00:01:32199void CommandBufferHelper::WaitForAvailableEntries(int32 count) {
[email protected]617296e2011-12-15 05:37:57200 AllocateRingBuffer();
[email protected]c4488402012-01-11 01:05:49201 if (!usable()) {
202 return;
203 }
[email protected]cf1aa982013-11-05 21:49:37204 DCHECK(HaveRingBuffer());
205 DCHECK(count < total_entry_count_);
[email protected]47257372013-01-04 18:37:48206 if (put_ + count > total_entry_count_) {
[email protected]96449d2c2009-11-25 00:01:32207 // There's not enough room between the current put and the end of the
[email protected]47257372013-01-04 18:37:48208 // buffer, so we need to wrap. We will add noops all the way to the end,
209 // but we need to make sure get wraps first, actually that get is 1 or
210 // more (since put will wrap to 0 after we add the noops).
[email protected]cf1aa982013-11-05 21:49:37211 DCHECK_LE(1, put_);
[email protected]d0f02c42011-07-21 21:40:48212 if (get_offset() > put_ || get_offset() == 0) {
[email protected]366ae242011-05-10 02:23:58213 TRACE_EVENT0("gpu", "CommandBufferHelper::WaitForAvailableEntries");
[email protected]d0f02c42011-07-21 21:40:48214 while (get_offset() > put_ || get_offset() == 0) {
[email protected]3a69c6fe2011-04-14 22:07:34215 // Do not loop forever if the flush fails, meaning the command buffer
216 // reader has shutdown.
217 if (!FlushSync())
218 return;
219 }
[email protected]96449d2c2009-11-25 00:01:32220 }
[email protected]47257372013-01-04 18:37:48221 // Insert Noops to fill out the buffer.
222 int32 num_entries = total_entry_count_ - put_;
223 while (num_entries > 0) {
224 int32 num_to_skip = std::min(CommandHeader::kMaxSize, num_entries);
225 cmd::Noop::Set(&entries_[put_], num_to_skip);
226 put_ += num_to_skip;
227 num_entries -= num_to_skip;
228 }
[email protected]96449d2c2009-11-25 00:01:32229 put_ = 0;
230 }
[email protected]3a69c6fe2011-04-14 22:07:34231 if (AvailableEntries() < count) {
[email protected]366ae242011-05-10 02:23:58232 TRACE_EVENT0("gpu", "CommandBufferHelper::WaitForAvailableEntries1");
[email protected]3a69c6fe2011-04-14 22:07:34233 while (AvailableEntries() < count) {
234 // Do not loop forever if the flush fails, meaning the command buffer
235 // reader has shutdown.
236 if (!FlushSync())
237 return;
238 }
[email protected]96449d2c2009-11-25 00:01:32239 }
[email protected]14eb04b2011-09-08 00:27:07240 // Force a flush if the buffer is getting half full, or even earlier if the
241 // reader is known to be idle.
242 int32 pending =
[email protected]47257372013-01-04 18:37:48243 (put_ + total_entry_count_ - last_put_sent_) % total_entry_count_;
244 int32 limit = total_entry_count_ /
[email protected]14eb04b2011-09-08 00:27:07245 ((get_offset() == last_put_sent_) ? 16 : 2);
246 if (pending > limit) {
247 Flush();
[email protected]362e6d602012-10-17 16:55:06248 } else if (flush_automatically_ &&
249 (commands_issued_ % kCommandsPerFlushCheck == 0)) {
250#if !defined(OS_ANDROID)
[email protected]9498e972012-10-04 03:48:28251 // Allow this command buffer to be pre-empted by another if a "reasonable"
252 // amount of work has been done. On highend machines, this reduces the
253 // latency of GPU commands. However, on Android, this can cause the
254 // kernel to thrash between generating GPU commands and executing them.
255 clock_t current_time = clock();
256 if (current_time - last_flush_time_ > kFlushDelay * CLOCKS_PER_SEC)
257 Flush();
258#endif
[email protected]7d5b8d12011-01-14 23:43:15259 }
[email protected]96449d2c2009-11-25 00:01:32260}
261
262CommandBufferEntry* CommandBufferHelper::GetSpace(uint32 entries) {
[email protected]617296e2011-12-15 05:37:57263 AllocateRingBuffer();
[email protected]c4488402012-01-11 01:05:49264 if (!usable()) {
265 return NULL;
266 }
[email protected]cf1aa982013-11-05 21:49:37267 DCHECK(HaveRingBuffer());
[email protected]b36897c12011-07-12 18:04:10268 ++commands_issued_;
[email protected]96449d2c2009-11-25 00:01:32269 WaitForAvailableEntries(entries);
270 CommandBufferEntry* space = &entries_[put_];
271 put_ += entries;
[email protected]cf1aa982013-11-05 21:49:37272 DCHECK_LE(put_, total_entry_count_);
[email protected]47257372013-01-04 18:37:48273 if (put_ == total_entry_count_) {
[email protected]7e5bee52010-03-08 18:21:00274 put_ = 0;
275 }
[email protected]96449d2c2009-11-25 00:01:32276 return space;
277}
278
[email protected]a7a27ace2009-12-12 00:11:25279} // namespace gpu