blob: 3b9599efb8d8ace16ec11d1bb081ec01306f9e81 [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]96449d2c2009-11-25 00:01:3215CommandBufferHelper::CommandBufferHelper(CommandBuffer* command_buffer)
16 : command_buffer_(command_buffer),
[email protected]503b3a22011-12-12 23:29:4017 ring_buffer_id_(-1),
18 ring_buffer_size_(0),
[email protected]96449d2c2009-11-25 00:01:3219 entries_(NULL),
[email protected]9310b262010-06-03 16:15:4720 total_entry_count_(0),
[email protected]96449d2c2009-11-25 00:01:3221 token_(0),
[email protected]7d5b8d12011-01-14 23:43:1522 put_(0),
[email protected]b36897c12011-07-12 18:04:1023 last_put_sent_(0),
24 commands_issued_(0),
[email protected]c4488402012-01-11 01:05:4925 usable_(true),
[email protected]d35e6a72012-08-25 01:51:1326 context_lost_(false),
[email protected]362e6d602012-10-17 16:55:0627 flush_automatically_(true),
[email protected]b36897c12011-07-12 18:04:1028 last_flush_time_(0) {
[email protected]96449d2c2009-11-25 00:01:3229}
30
[email protected]362e6d602012-10-17 16:55:0631void CommandBufferHelper::SetAutomaticFlushes(bool enabled) {
32 flush_automatically_ = enabled;
33}
34
[email protected]d35e6a72012-08-25 01:51:1335bool CommandBufferHelper::IsContextLost() {
36 if (!context_lost_) {
37 context_lost_ = error::IsError(command_buffer()->GetLastError());
38 }
39 return context_lost_;
40}
41
[email protected]503b3a22011-12-12 23:29:4042bool CommandBufferHelper::AllocateRingBuffer() {
[email protected]c4488402012-01-11 01:05:4943 if (!usable()) {
44 return false;
45 }
46
[email protected]617296e2011-12-15 05:37:5747 if (HaveRingBuffer()) {
48 return true;
49 }
50
[email protected]67c80782012-12-21 01:16:5251 int32 id = -1;
52 Buffer buffer = command_buffer_->CreateTransferBuffer(ring_buffer_size_, &id);
[email protected]503b3a22011-12-12 23:29:4053 if (id < 0) {
[email protected]c4488402012-01-11 01:05:4954 ClearUsable();
[email protected]503b3a22011-12-12 23:29:4055 return false;
56 }
57
[email protected]67c80782012-12-21 01:16:5258 ring_buffer_ = buffer;
[email protected]503b3a22011-12-12 23:29:4059 ring_buffer_id_ = id;
60 command_buffer_->SetGetBuffer(id);
61
62 // TODO(gman): Do we really need to call GetState here? We know get & put = 0
63 // Also do we need to check state.num_entries?
[email protected]c77ea362010-01-29 22:02:3664 CommandBuffer::State state = command_buffer_->GetState();
[email protected]7477ea6f2009-12-22 23:28:1565 entries_ = static_cast<CommandBufferEntry*>(ring_buffer_.ptr);
[email protected]503b3a22011-12-12 23:29:4066 int32 num_ring_buffer_entries =
67 ring_buffer_size_ / sizeof(CommandBufferEntry);
[email protected]b21265f2010-05-12 17:05:1368 if (num_ring_buffer_entries > state.num_entries) {
[email protected]c4488402012-01-11 01:05:4969 ClearUsable();
[email protected]b21265f2010-05-12 17:05:1370 return false;
71 }
[email protected]9310b262010-06-03 16:15:4772
[email protected]9310b262010-06-03 16:15:4773 total_entry_count_ = num_ring_buffer_entries;
[email protected]c77ea362010-01-29 22:02:3674 put_ = state.put_offset;
[email protected]96449d2c2009-11-25 00:01:3275 return true;
76}
77
[email protected]a5cf3cb2012-08-23 01:08:4278void CommandBufferHelper::FreeResources() {
[email protected]617296e2011-12-15 05:37:5779 if (HaveRingBuffer()) {
80 command_buffer_->DestroyTransferBuffer(ring_buffer_id_);
81 ring_buffer_id_ = -1;
82 }
83}
84
[email protected]a5cf3cb2012-08-23 01:08:4285void CommandBufferHelper::FreeRingBuffer() {
[email protected]cf1aa982013-11-05 21:49:3786 CHECK((put_ == get_offset()) ||
[email protected]ea51dbd2013-01-18 10:03:5387 error::IsError(command_buffer_->GetLastState().error));
[email protected]a5cf3cb2012-08-23 01:08:4288 FreeResources();
89}
90
[email protected]503b3a22011-12-12 23:29:4091bool CommandBufferHelper::Initialize(int32 ring_buffer_size) {
92 ring_buffer_size_ = ring_buffer_size;
93 return AllocateRingBuffer();
94}
95
[email protected]96449d2c2009-11-25 00:01:3296CommandBufferHelper::~CommandBufferHelper() {
[email protected]a5cf3cb2012-08-23 01:08:4297 FreeResources();
[email protected]96449d2c2009-11-25 00:01:3298}
99
[email protected]7d5b8d12011-01-14 23:43:15100bool CommandBufferHelper::FlushSync() {
[email protected]c4488402012-01-11 01:05:49101 if (!usable()) {
102 return false;
103 }
[email protected]14eb04b2011-09-08 00:27:07104 last_flush_time_ = clock();
[email protected]7d5b8d12011-01-14 23:43:15105 last_put_sent_ = put_;
[email protected]d0f02c42011-07-21 21:40:48106 CommandBuffer::State state = command_buffer_->FlushSync(put_, get_offset());
[email protected]f7a64ee2010-02-01 22:24:14107 return state.error == error::kNoError;
[email protected]96449d2c2009-11-25 00:01:32108}
109
[email protected]7d5b8d12011-01-14 23:43:15110void CommandBufferHelper::Flush() {
[email protected]9f5ab392013-01-30 12:47:13111 if (usable() && last_put_sent_ != put_) {
[email protected]c4488402012-01-11 01:05:49112 last_flush_time_ = clock();
113 last_put_sent_ = put_;
114 command_buffer_->Flush(put_);
115 }
[email protected]7d5b8d12011-01-14 23:43:15116}
117
[email protected]96449d2c2009-11-25 00:01:32118// Calls Flush() and then waits until the buffer is empty. Break early if the
119// error is set.
120bool CommandBufferHelper::Finish() {
[email protected]366ae242011-05-10 02:23:58121 TRACE_EVENT0("gpu", "CommandBufferHelper::Finish");
[email protected]c4488402012-01-11 01:05:49122 if (!usable()) {
123 return false;
124 }
[email protected]a24e7582012-02-15 23:21:32125 // If there is no work just exit.
126 if (put_ == get_offset()) {
127 return true;
128 }
[email protected]cf1aa982013-11-05 21:49:37129 DCHECK(HaveRingBuffer());
[email protected]96449d2c2009-11-25 00:01:32130 do {
131 // Do not loop forever if the flush fails, meaning the command buffer reader
[email protected]c77ea362010-01-29 22:02:36132 // has shutdown.
[email protected]7d5b8d12011-01-14 23:43:15133 if (!FlushSync())
[email protected]96449d2c2009-11-25 00:01:32134 return false;
[email protected]d0f02c42011-07-21 21:40:48135 } while (put_ != get_offset());
[email protected]96449d2c2009-11-25 00:01:32136
137 return true;
138}
139
140// Inserts a new token into the command stream. It uses an increasing value
141// scheme so that we don't lose tokens (a token has passed if the current token
142// value is higher than that token). Calls Finish() if the token value wraps,
143// which will be rare.
144int32 CommandBufferHelper::InsertToken() {
[email protected]617296e2011-12-15 05:37:57145 AllocateRingBuffer();
[email protected]c4488402012-01-11 01:05:49146 if (!usable()) {
147 return token_;
148 }
[email protected]cf1aa982013-11-05 21:49:37149 DCHECK(HaveRingBuffer());
[email protected]96449d2c2009-11-25 00:01:32150 // Increment token as 31-bit integer. Negative values are used to signal an
151 // error.
152 token_ = (token_ + 1) & 0x7FFFFFFF;
[email protected]c4488402012-01-11 01:05:49153 cmd::SetToken* cmd = GetCmdSpace<cmd::SetToken>();
154 if (cmd) {
155 cmd->Init(token_);
156 if (token_ == 0) {
157 TRACE_EVENT0("gpu", "CommandBufferHelper::InsertToken(wrapped)");
158 // we wrapped
159 Finish();
[email protected]cf1aa982013-11-05 21:49:37160 DCHECK_EQ(token_, last_token_read());
[email protected]c4488402012-01-11 01:05:49161 }
[email protected]96449d2c2009-11-25 00:01:32162 }
163 return token_;
164}
165
166// Waits until the current token value is greater or equal to the value passed
167// in argument.
168void CommandBufferHelper::WaitForToken(int32 token) {
[email protected]ea51dbd2013-01-18 10:03:53169 if (!usable() || !HaveRingBuffer()) {
[email protected]c4488402012-01-11 01:05:49170 return;
171 }
[email protected]96449d2c2009-11-25 00:01:32172 // Return immediately if corresponding InsertToken failed.
173 if (token < 0)
174 return;
[email protected]96449d2c2009-11-25 00:01:32175 if (token > token_) return; // we wrapped
[email protected]d0f02c42011-07-21 21:40:48176 while (last_token_read() < token) {
177 if (get_offset() == put_) {
[email protected]cf1aa982013-11-05 21:49:37178 LOG(FATAL) << "Empty command buffer while waiting on a token.";
[email protected]96449d2c2009-11-25 00:01:32179 return;
180 }
181 // Do not loop forever if the flush fails, meaning the command buffer reader
182 // has shutdown.
[email protected]7d5b8d12011-01-14 23:43:15183 if (!FlushSync())
[email protected]96449d2c2009-11-25 00:01:32184 return;
[email protected]96449d2c2009-11-25 00:01:32185 }
186}
187
188// Waits for available entries, basically waiting until get >= put + count + 1.
189// It actually waits for contiguous entries, so it may need to wrap the buffer
[email protected]47257372013-01-04 18:37:48190// around, adding a noops. Thus this function may change the value of put_. The
[email protected]9310b262010-06-03 16:15:47191// function will return early if an error occurs, in which case the available
192// space may not be available.
[email protected]82c66322013-11-15 21:36:56193bool CommandBufferHelper::WaitForAvailableEntries(int32 count) {
[email protected]cf1aa982013-11-05 21:49:37194 DCHECK(count < total_entry_count_);
[email protected]47257372013-01-04 18:37:48195 if (put_ + count > total_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]47257372013-01-04 18:37:48197 // buffer, so we need to wrap. We will add noops all the way to the end,
198 // but we need to make sure get wraps first, actually that get is 1 or
199 // more (since put will wrap to 0 after we add the noops).
[email protected]cf1aa982013-11-05 21:49:37200 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())
[email protected]82c66322013-11-15 21:36:56207 return false;
[email protected]3a69c6fe2011-04-14 22:07:34208 }
[email protected]96449d2c2009-11-25 00:01:32209 }
[email protected]47257372013-01-04 18:37:48210 // Insert Noops to fill out the buffer.
211 int32 num_entries = total_entry_count_ - put_;
212 while (num_entries > 0) {
213 int32 num_to_skip = std::min(CommandHeader::kMaxSize, num_entries);
214 cmd::Noop::Set(&entries_[put_], num_to_skip);
215 put_ += num_to_skip;
216 num_entries -= num_to_skip;
217 }
[email protected]96449d2c2009-11-25 00:01:32218 put_ = 0;
219 }
[email protected]3a69c6fe2011-04-14 22:07:34220 if (AvailableEntries() < count) {
[email protected]366ae242011-05-10 02:23:58221 TRACE_EVENT0("gpu", "CommandBufferHelper::WaitForAvailableEntries1");
[email protected]3a69c6fe2011-04-14 22:07:34222 while (AvailableEntries() < count) {
223 // Do not loop forever if the flush fails, meaning the command buffer
224 // reader has shutdown.
225 if (!FlushSync())
[email protected]82c66322013-11-15 21:36:56226 return false;
[email protected]3a69c6fe2011-04-14 22:07:34227 }
[email protected]96449d2c2009-11-25 00:01:32228 }
[email protected]82c66322013-11-15 21:36:56229 FlushIfNeeded();
230 return true;
[email protected]96449d2c2009-11-25 00:01:32231}
232
233CommandBufferEntry* CommandBufferHelper::GetSpace(uint32 entries) {
[email protected]82c66322013-11-15 21:36:56234 ++commands_issued_;
235 if (HaveRingBuffer() && usable() && entries <= SpaceAvailableImmediately()) {
236 FlushIfNeeded();
237 return GetSpaceForEntries(entries);
238 }
239
[email protected]617296e2011-12-15 05:37:57240 AllocateRingBuffer();
[email protected]c4488402012-01-11 01:05:49241 if (!usable()) {
242 return NULL;
243 }
[email protected]cf1aa982013-11-05 21:49:37244 DCHECK(HaveRingBuffer());
[email protected]82c66322013-11-15 21:36:56245 if (!WaitForAvailableEntries(entries))
246 return NULL;
247
248 return GetSpaceForEntries(entries);
[email protected]96449d2c2009-11-25 00:01:32249}
250
[email protected]a7a27ace2009-12-12 00:11:25251} // namespace gpu