blob: 3eba54fd60801900c7f7f1dcdf12bd0da4e9de93 [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]96449d2c2009-11-25 00:01:3213CommandBufferHelper::CommandBufferHelper(CommandBuffer* command_buffer)
14 : command_buffer_(command_buffer),
[email protected]503b3a22011-12-12 23:29:4015 ring_buffer_id_(-1),
16 ring_buffer_size_(0),
[email protected]96449d2c2009-11-25 00:01:3217 entries_(NULL),
[email protected]9310b262010-06-03 16:15:4718 total_entry_count_(0),
19 usable_entry_count_(0),
[email protected]96449d2c2009-11-25 00:01:3220 token_(0),
[email protected]7d5b8d12011-01-14 23:43:1521 put_(0),
[email protected]b36897c12011-07-12 18:04:1022 last_put_sent_(0),
23 commands_issued_(0),
[email protected]c4488402012-01-11 01:05:4924 usable_(true),
[email protected]b36897c12011-07-12 18:04:1025 last_flush_time_(0) {
[email protected]96449d2c2009-11-25 00:01:3226}
27
[email protected]503b3a22011-12-12 23:29:4028bool CommandBufferHelper::AllocateRingBuffer() {
[email protected]c4488402012-01-11 01:05:4929 if (!usable()) {
30 return false;
31 }
32
[email protected]617296e2011-12-15 05:37:5733 if (HaveRingBuffer()) {
34 return true;
35 }
36
[email protected]503b3a22011-12-12 23:29:4037 int32 id = command_buffer_->CreateTransferBuffer(ring_buffer_size_, -1);
38 if (id < 0) {
[email protected]c4488402012-01-11 01:05:4939 ClearUsable();
[email protected]503b3a22011-12-12 23:29:4040 return false;
41 }
42
43 ring_buffer_ = command_buffer_->GetTransferBuffer(id);
[email protected]c4488402012-01-11 01:05:4944 if (!ring_buffer_.ptr) {
45 ClearUsable();
[email protected]96449d2c2009-11-25 00:01:3246 return false;
[email protected]c4488402012-01-11 01:05:4947 }
[email protected]96449d2c2009-11-25 00:01:3248
[email protected]503b3a22011-12-12 23:29:4049 ring_buffer_id_ = id;
50 command_buffer_->SetGetBuffer(id);
51
52 // TODO(gman): Do we really need to call GetState here? We know get & put = 0
53 // Also do we need to check state.num_entries?
[email protected]c77ea362010-01-29 22:02:3654 CommandBuffer::State state = command_buffer_->GetState();
[email protected]7477ea6f2009-12-22 23:28:1555 entries_ = static_cast<CommandBufferEntry*>(ring_buffer_.ptr);
[email protected]503b3a22011-12-12 23:29:4056 int32 num_ring_buffer_entries =
57 ring_buffer_size_ / sizeof(CommandBufferEntry);
[email protected]b21265f2010-05-12 17:05:1358 if (num_ring_buffer_entries > state.num_entries) {
[email protected]c4488402012-01-11 01:05:4959 ClearUsable();
[email protected]b21265f2010-05-12 17:05:1360 return false;
61 }
[email protected]9310b262010-06-03 16:15:4762
63 const int32 kJumpEntries =
64 sizeof(cmd::Jump) / sizeof(*entries_); // NOLINT
65
66 total_entry_count_ = num_ring_buffer_entries;
67 usable_entry_count_ = total_entry_count_ - kJumpEntries;
[email protected]c77ea362010-01-29 22:02:3668 put_ = state.put_offset;
[email protected]96449d2c2009-11-25 00:01:3269 return true;
70}
71
[email protected]a5cf3cb2012-08-23 01:08:4272void CommandBufferHelper::FreeResources() {
[email protected]617296e2011-12-15 05:37:5773 if (HaveRingBuffer()) {
74 command_buffer_->DestroyTransferBuffer(ring_buffer_id_);
75 ring_buffer_id_ = -1;
76 }
77}
78
[email protected]a5cf3cb2012-08-23 01:08:4279void CommandBufferHelper::FreeRingBuffer() {
80 GPU_CHECK_EQ(put_, get_offset());
81 FreeResources();
82}
83
[email protected]503b3a22011-12-12 23:29:4084bool CommandBufferHelper::Initialize(int32 ring_buffer_size) {
85 ring_buffer_size_ = ring_buffer_size;
86 return AllocateRingBuffer();
87}
88
[email protected]96449d2c2009-11-25 00:01:3289CommandBufferHelper::~CommandBufferHelper() {
[email protected]a5cf3cb2012-08-23 01:08:4290 FreeResources();
[email protected]96449d2c2009-11-25 00:01:3291}
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();
[email protected]7d5b8d12011-01-14 23:43:15231 }
[email protected]96449d2c2009-11-25 00:01:32232}
233
234CommandBufferEntry* CommandBufferHelper::GetSpace(uint32 entries) {
[email protected]617296e2011-12-15 05:37:57235 AllocateRingBuffer();
[email protected]c4488402012-01-11 01:05:49236 if (!usable()) {
237 return NULL;
238 }
[email protected]617296e2011-12-15 05:37:57239 GPU_DCHECK(HaveRingBuffer());
[email protected]b36897c12011-07-12 18:04:10240 ++commands_issued_;
[email protected]96449d2c2009-11-25 00:01:32241 WaitForAvailableEntries(entries);
242 CommandBufferEntry* space = &entries_[put_];
243 put_ += entries;
[email protected]20407e92010-09-08 18:31:08244 GPU_DCHECK_LE(put_, usable_entry_count_);
[email protected]9310b262010-06-03 16:15:47245 if (put_ == usable_entry_count_) {
246 cmd::Jump::Set(&entries_[put_], 0);
[email protected]7e5bee52010-03-08 18:21:00247 put_ = 0;
248 }
[email protected]96449d2c2009-11-25 00:01:32249 return space;
250}
251
[email protected]f7a64ee2010-02-01 22:24:14252error::Error CommandBufferHelper::GetError() {
[email protected]c77ea362010-01-29 22:02:36253 CommandBuffer::State state = command_buffer_->GetState();
[email protected]f7a64ee2010-02-01 22:24:14254 return static_cast<error::Error>(state.error);
[email protected]c77ea362010-01-29 22:02:36255}
256
[email protected]a7a27ace2009-12-12 00:11:25257} // namespace gpu