blob: 54df89ed3da91ed5fb2997498111e9d25333e827 [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]9f427322010-03-08 22:58:587#include "../client/cmd_buffer_helper.h"
8#include "../common/command_buffer.h"
[email protected]366ae242011-05-10 02:23:589#include "../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),
24 usable_entry_count_(0),
[email protected]96449d2c2009-11-25 00:01:3225 token_(0),
[email protected]7d5b8d12011-01-14 23:43:1526 put_(0),
[email protected]b36897c12011-07-12 18:04:1027 last_put_sent_(0),
28 commands_issued_(0),
[email protected]c4488402012-01-11 01:05:4929 usable_(true),
[email protected]d35e6a72012-08-25 01:51:1330 context_lost_(false),
[email protected]362e6d602012-10-17 16:55:0631 flush_automatically_(true),
[email protected]b36897c12011-07-12 18:04:1032 last_flush_time_(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;
37}
38
[email protected]d35e6a72012-08-25 01:51:1339bool CommandBufferHelper::IsContextLost() {
40 if (!context_lost_) {
41 context_lost_ = error::IsError(command_buffer()->GetLastError());
42 }
43 return context_lost_;
44}
45
[email protected]503b3a22011-12-12 23:29:4046bool CommandBufferHelper::AllocateRingBuffer() {
[email protected]c4488402012-01-11 01:05:4947 if (!usable()) {
48 return false;
49 }
50
[email protected]617296e2011-12-15 05:37:5751 if (HaveRingBuffer()) {
52 return true;
53 }
54
[email protected]503b3a22011-12-12 23:29:4055 int32 id = command_buffer_->CreateTransferBuffer(ring_buffer_size_, -1);
56 if (id < 0) {
[email protected]c4488402012-01-11 01:05:4957 ClearUsable();
[email protected]503b3a22011-12-12 23:29:4058 return false;
59 }
60
61 ring_buffer_ = command_buffer_->GetTransferBuffer(id);
[email protected]c4488402012-01-11 01:05:4962 if (!ring_buffer_.ptr) {
63 ClearUsable();
[email protected]96449d2c2009-11-25 00:01:3264 return false;
[email protected]c4488402012-01-11 01:05:4965 }
[email protected]96449d2c2009-11-25 00:01:3266
[email protected]503b3a22011-12-12 23:29:4067 ring_buffer_id_ = id;
68 command_buffer_->SetGetBuffer(id);
69
70 // TODO(gman): Do we really need to call GetState here? We know get & put = 0
71 // Also do we need to check state.num_entries?
[email protected]c77ea362010-01-29 22:02:3672 CommandBuffer::State state = command_buffer_->GetState();
[email protected]7477ea6f2009-12-22 23:28:1573 entries_ = static_cast<CommandBufferEntry*>(ring_buffer_.ptr);
[email protected]503b3a22011-12-12 23:29:4074 int32 num_ring_buffer_entries =
75 ring_buffer_size_ / sizeof(CommandBufferEntry);
[email protected]b21265f2010-05-12 17:05:1376 if (num_ring_buffer_entries > state.num_entries) {
[email protected]c4488402012-01-11 01:05:4977 ClearUsable();
[email protected]b21265f2010-05-12 17:05:1378 return false;
79 }
[email protected]9310b262010-06-03 16:15:4780
81 const int32 kJumpEntries =
82 sizeof(cmd::Jump) / sizeof(*entries_); // NOLINT
83
84 total_entry_count_ = num_ring_buffer_entries;
85 usable_entry_count_ = total_entry_count_ - kJumpEntries;
[email protected]c77ea362010-01-29 22:02:3686 put_ = state.put_offset;
[email protected]96449d2c2009-11-25 00:01:3287 return true;
88}
89
[email protected]a5cf3cb2012-08-23 01:08:4290void CommandBufferHelper::FreeResources() {
[email protected]617296e2011-12-15 05:37:5791 if (HaveRingBuffer()) {
92 command_buffer_->DestroyTransferBuffer(ring_buffer_id_);
93 ring_buffer_id_ = -1;
94 }
95}
96
[email protected]a5cf3cb2012-08-23 01:08:4297void CommandBufferHelper::FreeRingBuffer() {
98 GPU_CHECK_EQ(put_, get_offset());
99 FreeResources();
100}
101
[email protected]503b3a22011-12-12 23:29:40102bool CommandBufferHelper::Initialize(int32 ring_buffer_size) {
103 ring_buffer_size_ = ring_buffer_size;
104 return AllocateRingBuffer();
105}
106
[email protected]96449d2c2009-11-25 00:01:32107CommandBufferHelper::~CommandBufferHelper() {
[email protected]a5cf3cb2012-08-23 01:08:42108 FreeResources();
[email protected]96449d2c2009-11-25 00:01:32109}
110
[email protected]7d5b8d12011-01-14 23:43:15111bool CommandBufferHelper::FlushSync() {
[email protected]c4488402012-01-11 01:05:49112 if (!usable()) {
113 return false;
114 }
[email protected]14eb04b2011-09-08 00:27:07115 last_flush_time_ = clock();
[email protected]7d5b8d12011-01-14 23:43:15116 last_put_sent_ = put_;
[email protected]d0f02c42011-07-21 21:40:48117 CommandBuffer::State state = command_buffer_->FlushSync(put_, get_offset());
[email protected]f7a64ee2010-02-01 22:24:14118 return state.error == error::kNoError;
[email protected]96449d2c2009-11-25 00:01:32119}
120
[email protected]7d5b8d12011-01-14 23:43:15121void CommandBufferHelper::Flush() {
[email protected]c4488402012-01-11 01:05:49122 if (usable()) {
123 last_flush_time_ = clock();
124 last_put_sent_ = put_;
125 command_buffer_->Flush(put_);
126 }
[email protected]7d5b8d12011-01-14 23:43:15127}
128
[email protected]96449d2c2009-11-25 00:01:32129// Calls Flush() and then waits until the buffer is empty. Break early if the
130// error is set.
131bool CommandBufferHelper::Finish() {
[email protected]366ae242011-05-10 02:23:58132 TRACE_EVENT0("gpu", "CommandBufferHelper::Finish");
[email protected]c4488402012-01-11 01:05:49133 if (!usable()) {
134 return false;
135 }
[email protected]a24e7582012-02-15 23:21:32136 // If there is no work just exit.
137 if (put_ == get_offset()) {
138 return true;
139 }
[email protected]617296e2011-12-15 05:37:57140 GPU_DCHECK(HaveRingBuffer());
[email protected]96449d2c2009-11-25 00:01:32141 do {
142 // Do not loop forever if the flush fails, meaning the command buffer reader
[email protected]c77ea362010-01-29 22:02:36143 // has shutdown.
[email protected]7d5b8d12011-01-14 23:43:15144 if (!FlushSync())
[email protected]96449d2c2009-11-25 00:01:32145 return false;
[email protected]d0f02c42011-07-21 21:40:48146 } while (put_ != get_offset());
[email protected]96449d2c2009-11-25 00:01:32147
148 return true;
149}
150
151// Inserts a new token into the command stream. It uses an increasing value
152// scheme so that we don't lose tokens (a token has passed if the current token
153// value is higher than that token). Calls Finish() if the token value wraps,
154// which will be rare.
155int32 CommandBufferHelper::InsertToken() {
[email protected]617296e2011-12-15 05:37:57156 AllocateRingBuffer();
[email protected]c4488402012-01-11 01:05:49157 if (!usable()) {
158 return token_;
159 }
[email protected]617296e2011-12-15 05:37:57160 GPU_DCHECK(HaveRingBuffer());
[email protected]96449d2c2009-11-25 00:01:32161 // Increment token as 31-bit integer. Negative values are used to signal an
162 // error.
163 token_ = (token_ + 1) & 0x7FFFFFFF;
[email protected]c4488402012-01-11 01:05:49164 cmd::SetToken* cmd = GetCmdSpace<cmd::SetToken>();
165 if (cmd) {
166 cmd->Init(token_);
167 if (token_ == 0) {
168 TRACE_EVENT0("gpu", "CommandBufferHelper::InsertToken(wrapped)");
169 // we wrapped
170 Finish();
171 GPU_DCHECK_EQ(token_, last_token_read());
172 }
[email protected]96449d2c2009-11-25 00:01:32173 }
174 return token_;
175}
176
177// Waits until the current token value is greater or equal to the value passed
178// in argument.
179void CommandBufferHelper::WaitForToken(int32 token) {
[email protected]73e5f8152011-05-13 23:30:35180 TRACE_EVENT_IF_LONGER_THAN0(50, "gpu", "CommandBufferHelper::WaitForToken");
[email protected]c4488402012-01-11 01:05:49181 if (!usable()) {
182 return;
183 }
184 GPU_DCHECK(HaveRingBuffer());
[email protected]96449d2c2009-11-25 00:01:32185 // Return immediately if corresponding InsertToken failed.
186 if (token < 0)
187 return;
[email protected]96449d2c2009-11-25 00:01:32188 if (token > token_) return; // we wrapped
[email protected]d0f02c42011-07-21 21:40:48189 while (last_token_read() < token) {
190 if (get_offset() == put_) {
[email protected]20407e92010-09-08 18:31:08191 GPU_LOG(FATAL) << "Empty command buffer while waiting on a token.";
[email protected]96449d2c2009-11-25 00:01:32192 return;
193 }
194 // Do not loop forever if the flush fails, meaning the command buffer reader
195 // has shutdown.
[email protected]7d5b8d12011-01-14 23:43:15196 if (!FlushSync())
[email protected]96449d2c2009-11-25 00:01:32197 return;
[email protected]96449d2c2009-11-25 00:01:32198 }
199}
200
201// Waits for available entries, basically waiting until get >= put + count + 1.
202// It actually waits for contiguous entries, so it may need to wrap the buffer
[email protected]9310b262010-06-03 16:15:47203// around, adding a jump. Thus this function may change the value of put_. The
204// function will return early if an error occurs, in which case the available
205// space may not be available.
[email protected]96449d2c2009-11-25 00:01:32206void CommandBufferHelper::WaitForAvailableEntries(int32 count) {
[email protected]617296e2011-12-15 05:37:57207 AllocateRingBuffer();
[email protected]c4488402012-01-11 01:05:49208 if (!usable()) {
209 return;
210 }
[email protected]617296e2011-12-15 05:37:57211 GPU_DCHECK(HaveRingBuffer());
[email protected]827467f2011-05-11 20:37:38212 GPU_DCHECK(count < usable_entry_count_);
[email protected]9310b262010-06-03 16:15:47213 if (put_ + count > usable_entry_count_) {
[email protected]96449d2c2009-11-25 00:01:32214 // There's not enough room between the current put and the end of the
[email protected]9310b262010-06-03 16:15:47215 // buffer, so we need to wrap. We will add a jump back to the start, but we
216 // need to make sure get wraps first, actually that get is 1 or more (since
217 // put will wrap to 0 after we add the jump).
[email protected]20407e92010-09-08 18:31:08218 GPU_DCHECK_LE(1, put_);
[email protected]d0f02c42011-07-21 21:40:48219 if (get_offset() > put_ || get_offset() == 0) {
[email protected]366ae242011-05-10 02:23:58220 TRACE_EVENT0("gpu", "CommandBufferHelper::WaitForAvailableEntries");
[email protected]d0f02c42011-07-21 21:40:48221 while (get_offset() > put_ || get_offset() == 0) {
[email protected]3a69c6fe2011-04-14 22:07:34222 // Do not loop forever if the flush fails, meaning the command buffer
223 // reader has shutdown.
224 if (!FlushSync())
225 return;
226 }
[email protected]96449d2c2009-11-25 00:01:32227 }
[email protected]9310b262010-06-03 16:15:47228 // Insert a jump back to the beginning.
229 cmd::Jump::Set(&entries_[put_], 0);
[email protected]96449d2c2009-11-25 00:01:32230 put_ = 0;
231 }
[email protected]3a69c6fe2011-04-14 22:07:34232 if (AvailableEntries() < count) {
[email protected]366ae242011-05-10 02:23:58233 TRACE_EVENT0("gpu", "CommandBufferHelper::WaitForAvailableEntries1");
[email protected]3a69c6fe2011-04-14 22:07:34234 while (AvailableEntries() < count) {
235 // Do not loop forever if the flush fails, meaning the command buffer
236 // reader has shutdown.
237 if (!FlushSync())
238 return;
239 }
[email protected]96449d2c2009-11-25 00:01:32240 }
[email protected]14eb04b2011-09-08 00:27:07241 // Force a flush if the buffer is getting half full, or even earlier if the
242 // reader is known to be idle.
243 int32 pending =
244 (put_ + usable_entry_count_ - last_put_sent_) % usable_entry_count_;
245 int32 limit = usable_entry_count_ /
246 ((get_offset() == last_put_sent_) ? 16 : 2);
247 if (pending > limit) {
248 Flush();
[email protected]362e6d602012-10-17 16:55:06249 } else if (flush_automatically_ &&
250 (commands_issued_ % kCommandsPerFlushCheck == 0)) {
251#if !defined(OS_ANDROID)
[email protected]9498e972012-10-04 03:48:28252 // Allow this command buffer to be pre-empted by another if a "reasonable"
253 // amount of work has been done. On highend machines, this reduces the
254 // latency of GPU commands. However, on Android, this can cause the
255 // kernel to thrash between generating GPU commands and executing them.
256 clock_t current_time = clock();
257 if (current_time - last_flush_time_ > kFlushDelay * CLOCKS_PER_SEC)
258 Flush();
259#endif
[email protected]7d5b8d12011-01-14 23:43:15260 }
[email protected]96449d2c2009-11-25 00:01:32261}
262
263CommandBufferEntry* CommandBufferHelper::GetSpace(uint32 entries) {
[email protected]617296e2011-12-15 05:37:57264 AllocateRingBuffer();
[email protected]c4488402012-01-11 01:05:49265 if (!usable()) {
266 return NULL;
267 }
[email protected]617296e2011-12-15 05:37:57268 GPU_DCHECK(HaveRingBuffer());
[email protected]b36897c12011-07-12 18:04:10269 ++commands_issued_;
[email protected]96449d2c2009-11-25 00:01:32270 WaitForAvailableEntries(entries);
271 CommandBufferEntry* space = &entries_[put_];
272 put_ += entries;
[email protected]20407e92010-09-08 18:31:08273 GPU_DCHECK_LE(put_, usable_entry_count_);
[email protected]9310b262010-06-03 16:15:47274 if (put_ == usable_entry_count_) {
275 cmd::Jump::Set(&entries_[put_], 0);
[email protected]7e5bee52010-03-08 18:21:00276 put_ = 0;
277 }
[email protected]96449d2c2009-11-25 00:01:32278 return space;
279}
280
[email protected]f7a64ee2010-02-01 22:24:14281error::Error CommandBufferHelper::GetError() {
[email protected]c77ea362010-01-29 22:02:36282 CommandBuffer::State state = command_buffer_->GetState();
[email protected]f7a64ee2010-02-01 22:24:14283 return static_cast<error::Error>(state.error);
[email protected]c77ea362010-01-29 22:02:36284}
285
[email protected]a7a27ace2009-12-12 00:11:25286} // namespace gpu