blob: 655dc635d273a906246881fe1de0105f1847f07d [file] [log] [blame]
[email protected]3a69c6fe2011-04-14 22:07:341// Copyright (c) 2011 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),
15 entries_(NULL),
[email protected]9310b262010-06-03 16:15:4716 total_entry_count_(0),
17 usable_entry_count_(0),
[email protected]96449d2c2009-11-25 00:01:3218 token_(0),
19 last_token_read_(-1),
20 get_(0),
[email protected]7d5b8d12011-01-14 23:43:1521 put_(0),
22 last_put_sent_(0) {
[email protected]96449d2c2009-11-25 00:01:3223}
24
[email protected]b21265f2010-05-12 17:05:1325bool CommandBufferHelper::Initialize(int32 ring_buffer_size) {
[email protected]96449d2c2009-11-25 00:01:3226 ring_buffer_ = command_buffer_->GetRingBuffer();
[email protected]7477ea6f2009-12-22 23:28:1527 if (!ring_buffer_.ptr)
[email protected]96449d2c2009-11-25 00:01:3228 return false;
29
[email protected]c77ea362010-01-29 22:02:3630 CommandBuffer::State state = command_buffer_->GetState();
[email protected]7477ea6f2009-12-22 23:28:1531 entries_ = static_cast<CommandBufferEntry*>(ring_buffer_.ptr);
[email protected]b21265f2010-05-12 17:05:1332 int32 num_ring_buffer_entries = ring_buffer_size / sizeof(CommandBufferEntry);
33 if (num_ring_buffer_entries > state.num_entries) {
34 return false;
35 }
[email protected]9310b262010-06-03 16:15:4736
37 const int32 kJumpEntries =
38 sizeof(cmd::Jump) / sizeof(*entries_); // NOLINT
39
40 total_entry_count_ = num_ring_buffer_entries;
41 usable_entry_count_ = total_entry_count_ - kJumpEntries;
[email protected]c77ea362010-01-29 22:02:3642 put_ = state.put_offset;
43 SynchronizeState(state);
[email protected]96449d2c2009-11-25 00:01:3244 return true;
45}
46
47CommandBufferHelper::~CommandBufferHelper() {
48}
49
[email protected]7d5b8d12011-01-14 23:43:1550bool CommandBufferHelper::FlushSync() {
51 last_put_sent_ = put_;
[email protected]ef16c172011-04-28 23:37:1452 CommandBuffer::State state = command_buffer_->FlushSync(put_, get_);
[email protected]c77ea362010-01-29 22:02:3653 SynchronizeState(state);
[email protected]f7a64ee2010-02-01 22:24:1454 return state.error == error::kNoError;
[email protected]96449d2c2009-11-25 00:01:3255}
56
[email protected]7d5b8d12011-01-14 23:43:1557void CommandBufferHelper::Flush() {
58 last_put_sent_ = put_;
59 command_buffer_->Flush(put_);
60}
61
[email protected]96449d2c2009-11-25 00:01:3262// Calls Flush() and then waits until the buffer is empty. Break early if the
63// error is set.
64bool CommandBufferHelper::Finish() {
[email protected]366ae242011-05-10 02:23:5865 TRACE_EVENT0("gpu", "CommandBufferHelper::Finish");
[email protected]96449d2c2009-11-25 00:01:3266 do {
67 // Do not loop forever if the flush fails, meaning the command buffer reader
[email protected]c77ea362010-01-29 22:02:3668 // has shutdown.
[email protected]7d5b8d12011-01-14 23:43:1569 if (!FlushSync())
[email protected]96449d2c2009-11-25 00:01:3270 return false;
71 } while (put_ != get_);
72
73 return true;
74}
75
76// Inserts a new token into the command stream. It uses an increasing value
77// scheme so that we don't lose tokens (a token has passed if the current token
78// value is higher than that token). Calls Finish() if the token value wraps,
79// which will be rare.
80int32 CommandBufferHelper::InsertToken() {
81 // Increment token as 31-bit integer. Negative values are used to signal an
82 // error.
83 token_ = (token_ + 1) & 0x7FFFFFFF;
[email protected]67e50772010-01-28 21:50:2484 cmd::SetToken& cmd = GetCmdSpace<cmd::SetToken>();
85 cmd.Init(token_);
[email protected]96449d2c2009-11-25 00:01:3286 if (token_ == 0) {
[email protected]366ae242011-05-10 02:23:5887 TRACE_EVENT0("gpu", "CommandBufferHelper::InsertToken(wrapped)");
[email protected]96449d2c2009-11-25 00:01:3288 // we wrapped
89 Finish();
[email protected]20407e92010-09-08 18:31:0890 GPU_DCHECK_EQ(token_, last_token_read_);
[email protected]96449d2c2009-11-25 00:01:3291 }
92 return token_;
93}
94
95// Waits until the current token value is greater or equal to the value passed
96// in argument.
97void CommandBufferHelper::WaitForToken(int32 token) {
[email protected]366ae242011-05-10 02:23:5898 TRACE_EVENT0("gpu", "CommandBufferHelper::WaitForToken");
[email protected]96449d2c2009-11-25 00:01:3299 // Return immediately if corresponding InsertToken failed.
100 if (token < 0)
101 return;
[email protected]96449d2c2009-11-25 00:01:32102 if (token > token_) return; // we wrapped
[email protected]96449d2c2009-11-25 00:01:32103 while (last_token_read_ < token) {
104 if (get_ == put_) {
[email protected]20407e92010-09-08 18:31:08105 GPU_LOG(FATAL) << "Empty command buffer while waiting on a token.";
[email protected]96449d2c2009-11-25 00:01:32106 return;
107 }
108 // Do not loop forever if the flush fails, meaning the command buffer reader
109 // has shutdown.
[email protected]7d5b8d12011-01-14 23:43:15110 if (!FlushSync())
[email protected]96449d2c2009-11-25 00:01:32111 return;
[email protected]96449d2c2009-11-25 00:01:32112 }
113}
114
[email protected]6b7a63f2011-04-23 00:57:35115void CommandBufferHelper::YieldScheduler() {
116 cmd::YieldScheduler& cmd = GetCmdSpace<cmd::YieldScheduler>();
117 cmd.Init();
118}
119
[email protected]96449d2c2009-11-25 00:01:32120// Waits for available entries, basically waiting until get >= put + count + 1.
121// It actually waits for contiguous entries, so it may need to wrap the buffer
[email protected]9310b262010-06-03 16:15:47122// around, adding a jump. Thus this function may change the value of put_. The
123// function will return early if an error occurs, in which case the available
124// space may not be available.
[email protected]96449d2c2009-11-25 00:01:32125void CommandBufferHelper::WaitForAvailableEntries(int32 count) {
[email protected]827467f2011-05-11 20:37:38126 GPU_DCHECK(count < usable_entry_count_);
[email protected]9310b262010-06-03 16:15:47127 if (put_ + count > usable_entry_count_) {
[email protected]96449d2c2009-11-25 00:01:32128 // There's not enough room between the current put and the end of the
[email protected]9310b262010-06-03 16:15:47129 // buffer, so we need to wrap. We will add a jump back to the start, but we
130 // need to make sure get wraps first, actually that get is 1 or more (since
131 // put will wrap to 0 after we add the jump).
[email protected]20407e92010-09-08 18:31:08132 GPU_DCHECK_LE(1, put_);
[email protected]3a69c6fe2011-04-14 22:07:34133 if (get_ > put_ || get_ == 0) {
[email protected]366ae242011-05-10 02:23:58134 TRACE_EVENT0("gpu", "CommandBufferHelper::WaitForAvailableEntries");
[email protected]3a69c6fe2011-04-14 22:07:34135 while (get_ > put_ || get_ == 0) {
136 // Do not loop forever if the flush fails, meaning the command buffer
137 // reader has shutdown.
138 if (!FlushSync())
139 return;
140 }
[email protected]96449d2c2009-11-25 00:01:32141 }
[email protected]9310b262010-06-03 16:15:47142 // Insert a jump back to the beginning.
143 cmd::Jump::Set(&entries_[put_], 0);
[email protected]96449d2c2009-11-25 00:01:32144 put_ = 0;
145 }
[email protected]3a69c6fe2011-04-14 22:07:34146 if (AvailableEntries() < count) {
[email protected]366ae242011-05-10 02:23:58147 TRACE_EVENT0("gpu", "CommandBufferHelper::WaitForAvailableEntries1");
[email protected]3a69c6fe2011-04-14 22:07:34148 while (AvailableEntries() < count) {
149 // Do not loop forever if the flush fails, meaning the command buffer
150 // reader has shutdown.
151 if (!FlushSync())
152 return;
153 }
[email protected]96449d2c2009-11-25 00:01:32154 }
[email protected]7d5b8d12011-01-14 23:43:15155 // Force a flush if the buffer is getting half full, or even earlier if the
156 // reader is known to be idle.
157 int32 pending =
158 (put_ + usable_entry_count_ - last_put_sent_) % usable_entry_count_;
159 int32 limit = usable_entry_count_ / ((get_ == last_put_sent_) ? 16 : 2);
160 if (pending > limit) {
161 Flush();
162 }
[email protected]96449d2c2009-11-25 00:01:32163}
164
165CommandBufferEntry* CommandBufferHelper::GetSpace(uint32 entries) {
166 WaitForAvailableEntries(entries);
167 CommandBufferEntry* space = &entries_[put_];
168 put_ += entries;
[email protected]20407e92010-09-08 18:31:08169 GPU_DCHECK_LE(put_, usable_entry_count_);
[email protected]9310b262010-06-03 16:15:47170 if (put_ == usable_entry_count_) {
171 cmd::Jump::Set(&entries_[put_], 0);
[email protected]7e5bee52010-03-08 18:21:00172 put_ = 0;
173 }
[email protected]96449d2c2009-11-25 00:01:32174 return space;
175}
176
[email protected]f7a64ee2010-02-01 22:24:14177error::Error CommandBufferHelper::GetError() {
[email protected]c77ea362010-01-29 22:02:36178 CommandBuffer::State state = command_buffer_->GetState();
179 SynchronizeState(state);
[email protected]f7a64ee2010-02-01 22:24:14180 return static_cast<error::Error>(state.error);
[email protected]c77ea362010-01-29 22:02:36181}
182
183void CommandBufferHelper::SynchronizeState(CommandBuffer::State state) {
184 get_ = state.get_offset;
185 last_token_read_ = state.token;
[email protected]96449d2c2009-11-25 00:01:32186}
187
[email protected]a7a27ace2009-12-12 00:11:25188} // namespace gpu