blob: 29770b8e04fd31e56afab6b08c4fd84448a5b280 [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]b36897c12011-07-12 18:04:1013namespace {
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]b36897c12011-07-12 18:04:1030 last_flush_time_(0) {
[email protected]96449d2c2009-11-25 00:01:3231}
32
[email protected]503b3a22011-12-12 23:29:4033bool CommandBufferHelper::AllocateRingBuffer() {
[email protected]c4488402012-01-11 01:05:4934 if (!usable()) {
35 return false;
36 }
37
[email protected]617296e2011-12-15 05:37:5738 if (HaveRingBuffer()) {
39 return true;
40 }
41
[email protected]503b3a22011-12-12 23:29:4042 int32 id = command_buffer_->CreateTransferBuffer(ring_buffer_size_, -1);
43 if (id < 0) {
[email protected]c4488402012-01-11 01:05:4944 ClearUsable();
[email protected]503b3a22011-12-12 23:29:4045 return false;
46 }
47
48 ring_buffer_ = command_buffer_->GetTransferBuffer(id);
[email protected]c4488402012-01-11 01:05:4949 if (!ring_buffer_.ptr) {
50 ClearUsable();
[email protected]96449d2c2009-11-25 00:01:3251 return false;
[email protected]c4488402012-01-11 01:05:4952 }
[email protected]96449d2c2009-11-25 00:01:3253
[email protected]503b3a22011-12-12 23:29:4054 ring_buffer_id_ = id;
55 command_buffer_->SetGetBuffer(id);
56
57 // TODO(gman): Do we really need to call GetState here? We know get & put = 0
58 // Also do we need to check state.num_entries?
[email protected]c77ea362010-01-29 22:02:3659 CommandBuffer::State state = command_buffer_->GetState();
[email protected]7477ea6f2009-12-22 23:28:1560 entries_ = static_cast<CommandBufferEntry*>(ring_buffer_.ptr);
[email protected]503b3a22011-12-12 23:29:4061 int32 num_ring_buffer_entries =
62 ring_buffer_size_ / sizeof(CommandBufferEntry);
[email protected]b21265f2010-05-12 17:05:1363 if (num_ring_buffer_entries > state.num_entries) {
[email protected]c4488402012-01-11 01:05:4964 ClearUsable();
[email protected]b21265f2010-05-12 17:05:1365 return false;
66 }
[email protected]9310b262010-06-03 16:15:4767
68 const int32 kJumpEntries =
69 sizeof(cmd::Jump) / sizeof(*entries_); // NOLINT
70
71 total_entry_count_ = num_ring_buffer_entries;
72 usable_entry_count_ = total_entry_count_ - kJumpEntries;
[email protected]c77ea362010-01-29 22:02:3673 put_ = state.put_offset;
[email protected]96449d2c2009-11-25 00:01:3274 return true;
75}
76
[email protected]617296e2011-12-15 05:37:5777void CommandBufferHelper::FreeRingBuffer() {
78 GPU_CHECK_EQ(put_, get_offset());
79 if (HaveRingBuffer()) {
80 command_buffer_->DestroyTransferBuffer(ring_buffer_id_);
81 ring_buffer_id_ = -1;
82 }
83}
84
[email protected]503b3a22011-12-12 23:29:4085bool CommandBufferHelper::Initialize(int32 ring_buffer_size) {
86 ring_buffer_size_ = ring_buffer_size;
87 return AllocateRingBuffer();
88}
89
[email protected]96449d2c2009-11-25 00:01:3290CommandBufferHelper::~CommandBufferHelper() {
91}
92
[email protected]7d5b8d12011-01-14 23:43:1593bool CommandBufferHelper::FlushSync() {
[email protected]c4488402012-01-11 01:05:4994 if (!usable()) {
95 return false;
96 }
[email protected]14eb04b2011-09-08 00:27:0797 last_flush_time_ = clock();
[email protected]7d5b8d12011-01-14 23:43:1598 last_put_sent_ = put_;
[email protected]d0f02c42011-07-21 21:40:4899 CommandBuffer::State state = command_buffer_->FlushSync(put_, get_offset());
[email protected]f7a64ee2010-02-01 22:24:14100 return state.error == error::kNoError;
[email protected]96449d2c2009-11-25 00:01:32101}
102
[email protected]7d5b8d12011-01-14 23:43:15103void CommandBufferHelper::Flush() {
[email protected]c4488402012-01-11 01:05:49104 if (usable()) {
105 last_flush_time_ = clock();
106 last_put_sent_ = put_;
107 command_buffer_->Flush(put_);
108 }
[email protected]7d5b8d12011-01-14 23:43:15109}
110
[email protected]96449d2c2009-11-25 00:01:32111// Calls Flush() and then waits until the buffer is empty. Break early if the
112// error is set.
113bool CommandBufferHelper::Finish() {
[email protected]366ae242011-05-10 02:23:58114 TRACE_EVENT0("gpu", "CommandBufferHelper::Finish");
[email protected]c4488402012-01-11 01:05:49115 if (!usable()) {
116 return false;
117 }
[email protected]a24e7582012-02-15 23:21:32118 // If there is no work just exit.
119 if (put_ == get_offset()) {
120 return true;
121 }
[email protected]617296e2011-12-15 05:37:57122 GPU_DCHECK(HaveRingBuffer());
[email protected]96449d2c2009-11-25 00:01:32123 do {
124 // Do not loop forever if the flush fails, meaning the command buffer reader
[email protected]c77ea362010-01-29 22:02:36125 // has shutdown.
[email protected]7d5b8d12011-01-14 23:43:15126 if (!FlushSync())
[email protected]96449d2c2009-11-25 00:01:32127 return false;
[email protected]d0f02c42011-07-21 21:40:48128 } while (put_ != get_offset());
[email protected]96449d2c2009-11-25 00:01:32129
130 return true;
131}
132
133// Inserts a new token into the command stream. It uses an increasing value
134// scheme so that we don't lose tokens (a token has passed if the current token
135// value is higher than that token). Calls Finish() if the token value wraps,
136// which will be rare.
137int32 CommandBufferHelper::InsertToken() {
[email protected]617296e2011-12-15 05:37:57138 AllocateRingBuffer();
[email protected]c4488402012-01-11 01:05:49139 if (!usable()) {
140 return token_;
141 }
[email protected]617296e2011-12-15 05:37:57142 GPU_DCHECK(HaveRingBuffer());
[email protected]96449d2c2009-11-25 00:01:32143 // Increment token as 31-bit integer. Negative values are used to signal an
144 // error.
145 token_ = (token_ + 1) & 0x7FFFFFFF;
[email protected]c4488402012-01-11 01:05:49146 cmd::SetToken* cmd = GetCmdSpace<cmd::SetToken>();
147 if (cmd) {
148 cmd->Init(token_);
149 if (token_ == 0) {
150 TRACE_EVENT0("gpu", "CommandBufferHelper::InsertToken(wrapped)");
151 // we wrapped
152 Finish();
153 GPU_DCHECK_EQ(token_, last_token_read());
154 }
[email protected]96449d2c2009-11-25 00:01:32155 }
156 return token_;
157}
158
159// Waits until the current token value is greater or equal to the value passed
160// in argument.
161void CommandBufferHelper::WaitForToken(int32 token) {
[email protected]73e5f8152011-05-13 23:30:35162 TRACE_EVENT_IF_LONGER_THAN0(50, "gpu", "CommandBufferHelper::WaitForToken");
[email protected]c4488402012-01-11 01:05:49163 if (!usable()) {
164 return;
165 }
166 GPU_DCHECK(HaveRingBuffer());
[email protected]96449d2c2009-11-25 00:01:32167 // Return immediately if corresponding InsertToken failed.
168 if (token < 0)
169 return;
[email protected]96449d2c2009-11-25 00:01:32170 if (token > token_) return; // we wrapped
[email protected]d0f02c42011-07-21 21:40:48171 while (last_token_read() < token) {
172 if (get_offset() == put_) {
[email protected]20407e92010-09-08 18:31:08173 GPU_LOG(FATAL) << "Empty command buffer while waiting on a token.";
[email protected]96449d2c2009-11-25 00:01:32174 return;
175 }
176 // Do not loop forever if the flush fails, meaning the command buffer reader
177 // has shutdown.
[email protected]7d5b8d12011-01-14 23:43:15178 if (!FlushSync())
[email protected]96449d2c2009-11-25 00:01:32179 return;
[email protected]96449d2c2009-11-25 00:01:32180 }
181}
182
183// Waits for available entries, basically waiting until get >= put + count + 1.
184// It actually waits for contiguous entries, so it may need to wrap the buffer
[email protected]9310b262010-06-03 16:15:47185// around, adding a jump. Thus this function may change the value of put_. The
186// function will return early if an error occurs, in which case the available
187// space may not be available.
[email protected]96449d2c2009-11-25 00:01:32188void CommandBufferHelper::WaitForAvailableEntries(int32 count) {
[email protected]617296e2011-12-15 05:37:57189 AllocateRingBuffer();
[email protected]c4488402012-01-11 01:05:49190 if (!usable()) {
191 return;
192 }
[email protected]617296e2011-12-15 05:37:57193 GPU_DCHECK(HaveRingBuffer());
[email protected]827467f2011-05-11 20:37:38194 GPU_DCHECK(count < usable_entry_count_);
[email protected]9310b262010-06-03 16:15:47195 if (put_ + count > usable_entry_count_) {
[email protected]96449d2c2009-11-25 00:01:32196 // There's not enough room between the current put and the end of the
[email protected]9310b262010-06-03 16:15:47197 // buffer, so we need to wrap. We will add a jump back to the start, but we
198 // need to make sure get wraps first, actually that get is 1 or more (since
199 // put will wrap to 0 after we add the jump).
[email protected]20407e92010-09-08 18:31:08200 GPU_DCHECK_LE(1, put_);
[email protected]d0f02c42011-07-21 21:40:48201 if (get_offset() > put_ || get_offset() == 0) {
[email protected]366ae242011-05-10 02:23:58202 TRACE_EVENT0("gpu", "CommandBufferHelper::WaitForAvailableEntries");
[email protected]d0f02c42011-07-21 21:40:48203 while (get_offset() > put_ || get_offset() == 0) {
[email protected]3a69c6fe2011-04-14 22:07:34204 // Do not loop forever if the flush fails, meaning the command buffer
205 // reader has shutdown.
206 if (!FlushSync())
207 return;
208 }
[email protected]96449d2c2009-11-25 00:01:32209 }
[email protected]9310b262010-06-03 16:15:47210 // Insert a jump back to the beginning.
211 cmd::Jump::Set(&entries_[put_], 0);
[email protected]96449d2c2009-11-25 00:01:32212 put_ = 0;
213 }
[email protected]3a69c6fe2011-04-14 22:07:34214 if (AvailableEntries() < count) {
[email protected]366ae242011-05-10 02:23:58215 TRACE_EVENT0("gpu", "CommandBufferHelper::WaitForAvailableEntries1");
[email protected]3a69c6fe2011-04-14 22:07:34216 while (AvailableEntries() < count) {
217 // Do not loop forever if the flush fails, meaning the command buffer
218 // reader has shutdown.
219 if (!FlushSync())
220 return;
221 }
[email protected]96449d2c2009-11-25 00:01:32222 }
[email protected]14eb04b2011-09-08 00:27:07223 // Force a flush if the buffer is getting half full, or even earlier if the
224 // reader is known to be idle.
225 int32 pending =
226 (put_ + usable_entry_count_ - last_put_sent_) % usable_entry_count_;
227 int32 limit = usable_entry_count_ /
228 ((get_offset() == last_put_sent_) ? 16 : 2);
229 if (pending > limit) {
230 Flush();
231 } else if (commands_issued_ % kCommandsPerFlushCheck == 0) {
[email protected]d15c01652011-12-15 20:02:45232#if !defined(OS_ANDROID)
[email protected]14eb04b2011-09-08 00:27:07233 // Allow this command buffer to be pre-empted by another if a "reasonable"
[email protected]d15c01652011-12-15 20:02:45234 // amount of work has been done. On highend machines, this reduces the
235 // latency of GPU commands. However, on Android, this can cause the
236 // kernel to thrash between generating GPU commands and executing them.
[email protected]14eb04b2011-09-08 00:27:07237 clock_t current_time = clock();
238 if (current_time - last_flush_time_ > kFlushDelay * CLOCKS_PER_SEC)
[email protected]b36897c12011-07-12 18:04:10239 Flush();
[email protected]d15c01652011-12-15 20:02:45240#endif
[email protected]7d5b8d12011-01-14 23:43:15241 }
[email protected]96449d2c2009-11-25 00:01:32242}
243
244CommandBufferEntry* CommandBufferHelper::GetSpace(uint32 entries) {
[email protected]617296e2011-12-15 05:37:57245 AllocateRingBuffer();
[email protected]c4488402012-01-11 01:05:49246 if (!usable()) {
247 return NULL;
248 }
[email protected]617296e2011-12-15 05:37:57249 GPU_DCHECK(HaveRingBuffer());
[email protected]b36897c12011-07-12 18:04:10250 ++commands_issued_;
[email protected]96449d2c2009-11-25 00:01:32251 WaitForAvailableEntries(entries);
252 CommandBufferEntry* space = &entries_[put_];
253 put_ += entries;
[email protected]20407e92010-09-08 18:31:08254 GPU_DCHECK_LE(put_, usable_entry_count_);
[email protected]9310b262010-06-03 16:15:47255 if (put_ == usable_entry_count_) {
256 cmd::Jump::Set(&entries_[put_], 0);
[email protected]7e5bee52010-03-08 18:21:00257 put_ = 0;
258 }
[email protected]96449d2c2009-11-25 00:01:32259 return space;
260}
261
[email protected]f7a64ee2010-02-01 22:24:14262error::Error CommandBufferHelper::GetError() {
[email protected]c77ea362010-01-29 22:02:36263 CommandBuffer::State state = command_buffer_->GetState();
[email protected]f7a64ee2010-02-01 22:24:14264 return static_cast<error::Error>(state.error);
[email protected]c77ea362010-01-29 22:02:36265}
266
[email protected]a7a27ace2009-12-12 00:11:25267} // namespace gpu