blob: 2fa15aacfefd392c9359d5a70aeda9c753eece05 [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"
8#include "gpu/command_buffer/common/command_buffer.h"
9#include "gpu/command_buffer/common/trace_event.h"
[email protected]96449d2c2009-11-25 00:01:3210
[email protected]a7a27ace2009-12-12 00:11:2511namespace gpu {
[email protected]96449d2c2009-11-25 00:01:3212
[email protected]9498e972012-10-04 03:48:2813namespace {
14const int kCommandsPerFlushCheck = 100;
15const double kFlushDelay = 1.0 / (5.0 * 60.0);
16}
17
[email protected]96449d2c2009-11-25 00:01:3218CommandBufferHelper::CommandBufferHelper(CommandBuffer* command_buffer)
19 : command_buffer_(command_buffer),
[email protected]503b3a22011-12-12 23:29:4020 ring_buffer_id_(-1),
21 ring_buffer_size_(0),
[email protected]96449d2c2009-11-25 00:01:3222 entries_(NULL),
[email protected]9310b262010-06-03 16:15:4723 total_entry_count_(0),
[email protected]96449d2c2009-11-25 00:01:3224 token_(0),
[email protected]7d5b8d12011-01-14 23:43:1525 put_(0),
[email protected]b36897c12011-07-12 18:04:1026 last_put_sent_(0),
27 commands_issued_(0),
[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]b36897c12011-07-12 18:04:1031 last_flush_time_(0) {
[email protected]96449d2c2009-11-25 00:01:3232}
33
[email protected]362e6d602012-10-17 16:55:0634void CommandBufferHelper::SetAutomaticFlushes(bool enabled) {
35 flush_automatically_ = enabled;
36}
37
[email protected]d35e6a72012-08-25 01:51:1338bool CommandBufferHelper::IsContextLost() {
39 if (!context_lost_) {
40 context_lost_ = error::IsError(command_buffer()->GetLastError());
41 }
42 return context_lost_;
43}
44
[email protected]503b3a22011-12-12 23:29:4045bool CommandBufferHelper::AllocateRingBuffer() {
[email protected]c4488402012-01-11 01:05:4946 if (!usable()) {
47 return false;
48 }
49
[email protected]617296e2011-12-15 05:37:5750 if (HaveRingBuffer()) {
51 return true;
52 }
53
[email protected]67c80782012-12-21 01:16:5254 int32 id = -1;
55 Buffer buffer = command_buffer_->CreateTransferBuffer(ring_buffer_size_, &id);
[email protected]503b3a22011-12-12 23:29:4056 if (id < 0) {
[email protected]c4488402012-01-11 01:05:4957 ClearUsable();
[email protected]503b3a22011-12-12 23:29:4058 return false;
59 }
60
[email protected]67c80782012-12-21 01:16:5261 ring_buffer_ = buffer;
[email protected]503b3a22011-12-12 23:29:4062 ring_buffer_id_ = id;
63 command_buffer_->SetGetBuffer(id);
64
65 // TODO(gman): Do we really need to call GetState here? We know get & put = 0
66 // Also do we need to check state.num_entries?
[email protected]c77ea362010-01-29 22:02:3667 CommandBuffer::State state = command_buffer_->GetState();
[email protected]7477ea6f2009-12-22 23:28:1568 entries_ = static_cast<CommandBufferEntry*>(ring_buffer_.ptr);
[email protected]503b3a22011-12-12 23:29:4069 int32 num_ring_buffer_entries =
70 ring_buffer_size_ / sizeof(CommandBufferEntry);
[email protected]b21265f2010-05-12 17:05:1371 if (num_ring_buffer_entries > state.num_entries) {
[email protected]c4488402012-01-11 01:05:4972 ClearUsable();
[email protected]b21265f2010-05-12 17:05:1373 return false;
74 }
[email protected]9310b262010-06-03 16:15:4775
[email protected]9310b262010-06-03 16:15:4776 total_entry_count_ = num_ring_buffer_entries;
[email protected]c77ea362010-01-29 22:02:3677 put_ = state.put_offset;
[email protected]96449d2c2009-11-25 00:01:3278 return true;
79}
80
[email protected]a5cf3cb2012-08-23 01:08:4281void CommandBufferHelper::FreeResources() {
[email protected]617296e2011-12-15 05:37:5782 if (HaveRingBuffer()) {
83 command_buffer_->DestroyTransferBuffer(ring_buffer_id_);
84 ring_buffer_id_ = -1;
85 }
86}
87
[email protected]a5cf3cb2012-08-23 01:08:4288void CommandBufferHelper::FreeRingBuffer() {
[email protected]ea51dbd2013-01-18 10:03:5389 GPU_CHECK((put_ == get_offset()) ||
90 error::IsError(command_buffer_->GetLastState().error));
[email protected]a5cf3cb2012-08-23 01:08:4291 FreeResources();
92}
93
[email protected]503b3a22011-12-12 23:29:4094bool CommandBufferHelper::Initialize(int32 ring_buffer_size) {
95 ring_buffer_size_ = ring_buffer_size;
96 return AllocateRingBuffer();
97}
98
[email protected]96449d2c2009-11-25 00:01:3299CommandBufferHelper::~CommandBufferHelper() {
[email protected]a5cf3cb2012-08-23 01:08:42100 FreeResources();
[email protected]96449d2c2009-11-25 00:01:32101}
102
[email protected]7d5b8d12011-01-14 23:43:15103bool CommandBufferHelper::FlushSync() {
[email protected]c4488402012-01-11 01:05:49104 if (!usable()) {
105 return false;
106 }
[email protected]14eb04b2011-09-08 00:27:07107 last_flush_time_ = clock();
[email protected]7d5b8d12011-01-14 23:43:15108 last_put_sent_ = put_;
[email protected]d0f02c42011-07-21 21:40:48109 CommandBuffer::State state = command_buffer_->FlushSync(put_, get_offset());
[email protected]f7a64ee2010-02-01 22:24:14110 return state.error == error::kNoError;
[email protected]96449d2c2009-11-25 00:01:32111}
112
[email protected]7d5b8d12011-01-14 23:43:15113void CommandBufferHelper::Flush() {
[email protected]9f5ab392013-01-30 12:47:13114 if (usable() && last_put_sent_ != put_) {
[email protected]c4488402012-01-11 01:05:49115 last_flush_time_ = clock();
116 last_put_sent_ = put_;
117 command_buffer_->Flush(put_);
118 }
[email protected]7d5b8d12011-01-14 23:43:15119}
120
[email protected]96449d2c2009-11-25 00:01:32121// Calls Flush() and then waits until the buffer is empty. Break early if the
122// error is set.
123bool CommandBufferHelper::Finish() {
[email protected]366ae242011-05-10 02:23:58124 TRACE_EVENT0("gpu", "CommandBufferHelper::Finish");
[email protected]c4488402012-01-11 01:05:49125 if (!usable()) {
126 return false;
127 }
[email protected]a24e7582012-02-15 23:21:32128 // If there is no work just exit.
129 if (put_ == get_offset()) {
130 return true;
131 }
[email protected]617296e2011-12-15 05:37:57132 GPU_DCHECK(HaveRingBuffer());
[email protected]96449d2c2009-11-25 00:01:32133 do {
134 // Do not loop forever if the flush fails, meaning the command buffer reader
[email protected]c77ea362010-01-29 22:02:36135 // has shutdown.
[email protected]7d5b8d12011-01-14 23:43:15136 if (!FlushSync())
[email protected]96449d2c2009-11-25 00:01:32137 return false;
[email protected]d0f02c42011-07-21 21:40:48138 } while (put_ != get_offset());
[email protected]96449d2c2009-11-25 00:01:32139
140 return true;
141}
142
143// Inserts a new token into the command stream. It uses an increasing value
144// scheme so that we don't lose tokens (a token has passed if the current token
145// value is higher than that token). Calls Finish() if the token value wraps,
146// which will be rare.
147int32 CommandBufferHelper::InsertToken() {
[email protected]617296e2011-12-15 05:37:57148 AllocateRingBuffer();
[email protected]c4488402012-01-11 01:05:49149 if (!usable()) {
150 return token_;
151 }
[email protected]617296e2011-12-15 05:37:57152 GPU_DCHECK(HaveRingBuffer());
[email protected]96449d2c2009-11-25 00:01:32153 // Increment token as 31-bit integer. Negative values are used to signal an
154 // error.
155 token_ = (token_ + 1) & 0x7FFFFFFF;
[email protected]c4488402012-01-11 01:05:49156 cmd::SetToken* cmd = GetCmdSpace<cmd::SetToken>();
157 if (cmd) {
158 cmd->Init(token_);
159 if (token_ == 0) {
160 TRACE_EVENT0("gpu", "CommandBufferHelper::InsertToken(wrapped)");
161 // we wrapped
162 Finish();
163 GPU_DCHECK_EQ(token_, last_token_read());
164 }
[email protected]96449d2c2009-11-25 00:01:32165 }
166 return token_;
167}
168
169// Waits until the current token value is greater or equal to the value passed
170// in argument.
171void CommandBufferHelper::WaitForToken(int32 token) {
[email protected]ea51dbd2013-01-18 10:03:53172 if (!usable() || !HaveRingBuffer()) {
[email protected]c4488402012-01-11 01:05:49173 return;
174 }
[email protected]96449d2c2009-11-25 00:01:32175 // Return immediately if corresponding InsertToken failed.
176 if (token < 0)
177 return;
[email protected]96449d2c2009-11-25 00:01:32178 if (token > token_) return; // we wrapped
[email protected]d0f02c42011-07-21 21:40:48179 while (last_token_read() < token) {
180 if (get_offset() == put_) {
[email protected]20407e92010-09-08 18:31:08181 GPU_LOG(FATAL) << "Empty command buffer while waiting on a token.";
[email protected]96449d2c2009-11-25 00:01:32182 return;
183 }
184 // Do not loop forever if the flush fails, meaning the command buffer reader
185 // has shutdown.
[email protected]7d5b8d12011-01-14 23:43:15186 if (!FlushSync())
[email protected]96449d2c2009-11-25 00:01:32187 return;
[email protected]96449d2c2009-11-25 00:01:32188 }
189}
190
191// Waits for available entries, basically waiting until get >= put + count + 1.
192// It actually waits for contiguous entries, so it may need to wrap the buffer
[email protected]47257372013-01-04 18:37:48193// around, adding a noops. Thus this function may change the value of put_. The
[email protected]9310b262010-06-03 16:15:47194// function will return early if an error occurs, in which case the available
195// space may not be available.
[email protected]96449d2c2009-11-25 00:01:32196void CommandBufferHelper::WaitForAvailableEntries(int32 count) {
[email protected]617296e2011-12-15 05:37:57197 AllocateRingBuffer();
[email protected]c4488402012-01-11 01:05:49198 if (!usable()) {
199 return;
200 }
[email protected]617296e2011-12-15 05:37:57201 GPU_DCHECK(HaveRingBuffer());
[email protected]47257372013-01-04 18:37:48202 GPU_DCHECK(count < total_entry_count_);
203 if (put_ + count > total_entry_count_) {
[email protected]96449d2c2009-11-25 00:01:32204 // There's not enough room between the current put and the end of the
[email protected]47257372013-01-04 18:37:48205 // buffer, so we need to wrap. We will add noops all the way to the end,
206 // but we need to make sure get wraps first, actually that get is 1 or
207 // more (since put will wrap to 0 after we add the noops).
[email protected]20407e92010-09-08 18:31:08208 GPU_DCHECK_LE(1, put_);
[email protected]d0f02c42011-07-21 21:40:48209 if (get_offset() > put_ || get_offset() == 0) {
[email protected]366ae242011-05-10 02:23:58210 TRACE_EVENT0("gpu", "CommandBufferHelper::WaitForAvailableEntries");
[email protected]d0f02c42011-07-21 21:40:48211 while (get_offset() > put_ || get_offset() == 0) {
[email protected]3a69c6fe2011-04-14 22:07:34212 // Do not loop forever if the flush fails, meaning the command buffer
213 // reader has shutdown.
214 if (!FlushSync())
215 return;
216 }
[email protected]96449d2c2009-11-25 00:01:32217 }
[email protected]47257372013-01-04 18:37:48218 // Insert Noops to fill out the buffer.
219 int32 num_entries = total_entry_count_ - put_;
220 while (num_entries > 0) {
221 int32 num_to_skip = std::min(CommandHeader::kMaxSize, num_entries);
222 cmd::Noop::Set(&entries_[put_], num_to_skip);
223 put_ += num_to_skip;
224 num_entries -= num_to_skip;
225 }
[email protected]96449d2c2009-11-25 00:01:32226 put_ = 0;
227 }
[email protected]3a69c6fe2011-04-14 22:07:34228 if (AvailableEntries() < count) {
[email protected]366ae242011-05-10 02:23:58229 TRACE_EVENT0("gpu", "CommandBufferHelper::WaitForAvailableEntries1");
[email protected]3a69c6fe2011-04-14 22:07:34230 while (AvailableEntries() < count) {
231 // Do not loop forever if the flush fails, meaning the command buffer
232 // reader has shutdown.
233 if (!FlushSync())
234 return;
235 }
[email protected]96449d2c2009-11-25 00:01:32236 }
[email protected]14eb04b2011-09-08 00:27:07237 // Force a flush if the buffer is getting half full, or even earlier if the
238 // reader is known to be idle.
239 int32 pending =
[email protected]47257372013-01-04 18:37:48240 (put_ + total_entry_count_ - last_put_sent_) % total_entry_count_;
241 int32 limit = total_entry_count_ /
[email protected]14eb04b2011-09-08 00:27:07242 ((get_offset() == last_put_sent_) ? 16 : 2);
243 if (pending > limit) {
244 Flush();
[email protected]362e6d602012-10-17 16:55:06245 } else if (flush_automatically_ &&
246 (commands_issued_ % kCommandsPerFlushCheck == 0)) {
247#if !defined(OS_ANDROID)
[email protected]9498e972012-10-04 03:48:28248 // Allow this command buffer to be pre-empted by another if a "reasonable"
249 // amount of work has been done. On highend machines, this reduces the
250 // latency of GPU commands. However, on Android, this can cause the
251 // kernel to thrash between generating GPU commands and executing them.
252 clock_t current_time = clock();
253 if (current_time - last_flush_time_ > kFlushDelay * CLOCKS_PER_SEC)
254 Flush();
255#endif
[email protected]7d5b8d12011-01-14 23:43:15256 }
[email protected]96449d2c2009-11-25 00:01:32257}
258
259CommandBufferEntry* CommandBufferHelper::GetSpace(uint32 entries) {
[email protected]617296e2011-12-15 05:37:57260 AllocateRingBuffer();
[email protected]c4488402012-01-11 01:05:49261 if (!usable()) {
262 return NULL;
263 }
[email protected]617296e2011-12-15 05:37:57264 GPU_DCHECK(HaveRingBuffer());
[email protected]b36897c12011-07-12 18:04:10265 ++commands_issued_;
[email protected]96449d2c2009-11-25 00:01:32266 WaitForAvailableEntries(entries);
267 CommandBufferEntry* space = &entries_[put_];
268 put_ += entries;
[email protected]47257372013-01-04 18:37:48269 GPU_DCHECK_LE(put_, total_entry_count_);
270 if (put_ == total_entry_count_) {
[email protected]7e5bee52010-03-08 18:21:00271 put_ = 0;
272 }
[email protected]96449d2c2009-11-25 00:01:32273 return space;
274}
275
[email protected]f7a64ee2010-02-01 22:24:14276error::Error CommandBufferHelper::GetError() {
[email protected]c77ea362010-01-29 22:02:36277 CommandBuffer::State state = command_buffer_->GetState();
[email protected]f7a64ee2010-02-01 22:24:14278 return static_cast<error::Error>(state.error);
[email protected]c77ea362010-01-29 22:02:36279}
280
[email protected]a7a27ace2009-12-12 00:11:25281} // namespace gpu