blob: e8f6fefac9ac5c49e3b9ea72204de6a1e027ca4f [file] [log] [blame]
[email protected]96449d2c2009-11-25 00:01:321/*
2 * Copyright 2009, Google Inc.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are
7 * met:
8 *
9 * * Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * * Redistributions in binary form must reproduce the above
12 * copyright notice, this list of conditions and the following disclaimer
13 * in the documentation and/or other materials provided with the
14 * distribution.
15 * * Neither the name of Google Inc. nor the names of its
16 * contributors may be used to endorse or promote products derived from
17 * this software without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 */
31
32
33// This file contains the implementation of the command buffer helper class.
34
35#include "gpu/command_buffer/client/cmd_buffer_helper.h"
36#include "gpu/command_buffer/common/command_buffer.h"
37#include "gpu/np_utils/np_utils.h"
38
39namespace command_buffer {
40
41using command_buffer::CommandBuffer;
42using np_utils::NPBrowser;
43using np_utils::NPInvoke;
44using np_utils::NPObjectPointer;
45
46CommandBufferHelper::CommandBufferHelper(CommandBuffer* command_buffer)
47 : command_buffer_(command_buffer),
48 entries_(NULL),
49 entry_count_(0),
50 token_(0),
51 last_token_read_(-1),
52 get_(0),
53 put_(0) {
54}
55
56bool CommandBufferHelper::Initialize() {
57 ring_buffer_ = command_buffer_->GetRingBuffer();
58 if (!ring_buffer_)
59 return false;
60
61 // Map the ring buffer into this process.
62 if (!ring_buffer_->Map(ring_buffer_->max_size()))
63 return false;
64
65 entries_ = static_cast<CommandBufferEntry*>(ring_buffer_->memory());
66 entry_count_ = command_buffer_->GetSize();
67 get_ = command_buffer_->GetGetOffset();
68 put_ = command_buffer_->GetPutOffset();
69 last_token_read_ = command_buffer_->GetToken();
70
71 return true;
72}
73
74CommandBufferHelper::~CommandBufferHelper() {
75}
76
77bool CommandBufferHelper::Flush() {
78 get_ = command_buffer_->SyncOffsets(put_);
79 return !command_buffer_->GetErrorStatus();
80}
81
82// Calls Flush() and then waits until the buffer is empty. Break early if the
83// error is set.
84bool CommandBufferHelper::Finish() {
85 do {
86 // Do not loop forever if the flush fails, meaning the command buffer reader
87 // has shutdown).
88 if (!Flush())
89 return false;
90 } while (put_ != get_);
91
92 return true;
93}
94
95// Inserts a new token into the command stream. It uses an increasing value
96// scheme so that we don't lose tokens (a token has passed if the current token
97// value is higher than that token). Calls Finish() if the token value wraps,
98// which will be rare.
99int32 CommandBufferHelper::InsertToken() {
100 // Increment token as 31-bit integer. Negative values are used to signal an
101 // error.
102 token_ = (token_ + 1) & 0x7FFFFFFF;
103 CommandBufferEntry args;
104 args.value_uint32 = token_;
105 const uint32 kSetToken = 1; // TODO(gman): add a common set of commands.
106 AddCommand(kSetToken, 1, &args);
107 if (token_ == 0) {
108 // we wrapped
109 Finish();
110 last_token_read_ = command_buffer_->GetToken();
111 DCHECK_EQ(token_, last_token_read_);
112 }
113 return token_;
114}
115
116// Waits until the current token value is greater or equal to the value passed
117// in argument.
118void CommandBufferHelper::WaitForToken(int32 token) {
119 // Return immediately if corresponding InsertToken failed.
120 if (token < 0)
121 return;
122 if (last_token_read_ >= token) return; // fast path.
123 if (token > token_) return; // we wrapped
124 Flush();
125 last_token_read_ = command_buffer_->GetToken();
126 while (last_token_read_ < token) {
127 if (get_ == put_) {
128 LOG(FATAL) << "Empty command buffer while waiting on a token.";
129 return;
130 }
131 // Do not loop forever if the flush fails, meaning the command buffer reader
132 // has shutdown.
133 if (!Flush())
134 return;
135 last_token_read_ = command_buffer_->GetToken();
136 }
137}
138
139// Waits for available entries, basically waiting until get >= put + count + 1.
140// It actually waits for contiguous entries, so it may need to wrap the buffer
141// around, adding noops. Thus this function may change the value of put_.
142// The function will return early if an error occurs, in which case the
143// available space may not be available.
144void CommandBufferHelper::WaitForAvailableEntries(int32 count) {
145 CHECK(count < entry_count_);
146 if (put_ + count > entry_count_) {
147 // There's not enough room between the current put and the end of the
148 // buffer, so we need to wrap. We will add noops all the way to the end,
149 // but we need to make sure get wraps first, actually that get is 1 or
150 // more (since put will wrap to 0 after we add the noops).
151 DCHECK_LE(1, put_);
152 Flush();
153 while (get_ > put_ || get_ == 0) {
154 // Do not loop forever if the flush fails, meaning the command buffer
155 // reader has shutdown.
156 if (!Flush())
157 return;
158 }
159 // Add the noops. By convention, a noop is a command 0 with no args.
160 // TODO(apatrick): A noop can have a size. It would be better to add a
161 // single noop with a variable size. Watch out for size limit on
162 // individual commands.
163 CommandHeader header;
164 header.size = 1;
165 header.command = 0;
166 while (put_ < entry_count_) {
167 entries_[put_++].value_header = header;
168 }
169 put_ = 0;
170 }
171 // If we have enough room, return immediatly.
172 if (count <= AvailableEntries()) return;
173 // Otherwise flush, and wait until we do have enough room.
174 Flush();
175 while (AvailableEntries() < count) {
176 // Do not loop forever if the flush fails, meaning the command buffer reader
177 // has shutdown.
178 if (!Flush())
179 return;
180 }
181}
182
183CommandBufferEntry* CommandBufferHelper::GetSpace(uint32 entries) {
184 WaitForAvailableEntries(entries);
185 CommandBufferEntry* space = &entries_[put_];
186 put_ += entries;
187 return space;
188}
189
190parse_error::ParseError CommandBufferHelper::GetParseError() {
191 int32 parse_error = command_buffer_->ResetParseError();
192 return static_cast<parse_error::ParseError>(parse_error);
193}
194
195} // namespace command_buffer