693063dc04bbade3128b2969266b7dd0ee8dcb22
[platform/framework/web/crosswalk.git] / src / gpu / command_buffer / client / cmd_buffer_helper.cc
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 // This file contains the implementation of the command buffer helper class.
6
7 #include "gpu/command_buffer/client/cmd_buffer_helper.h"
8
9 #include "base/logging.h"
10 #include "gpu/command_buffer/common/command_buffer.h"
11 #include "gpu/command_buffer/common/trace_event.h"
12
13 namespace gpu {
14
15 CommandBufferHelper::CommandBufferHelper(CommandBuffer* command_buffer)
16     : command_buffer_(command_buffer),
17       ring_buffer_id_(-1),
18       ring_buffer_size_(0),
19       entries_(NULL),
20       total_entry_count_(0),
21       immediate_entry_count_(0),
22       token_(0),
23       put_(0),
24       last_put_sent_(0),
25 #if defined(CMD_HELPER_PERIODIC_FLUSH_CHECK)
26       commands_issued_(0),
27 #endif
28       usable_(true),
29       context_lost_(false),
30       flush_automatically_(true),
31       last_flush_time_(0) {
32 }
33
34 void CommandBufferHelper::SetAutomaticFlushes(bool enabled) {
35   flush_automatically_ = enabled;
36   CalcImmediateEntries(0);
37 }
38
39 bool CommandBufferHelper::IsContextLost() {
40   if (!context_lost_) {
41     context_lost_ = error::IsError(command_buffer()->GetLastError());
42   }
43   return context_lost_;
44 }
45
46 void CommandBufferHelper::CalcImmediateEntries(int waiting_count) {
47   DCHECK_GE(waiting_count, 0);
48
49   // Check if usable & allocated.
50   if (!usable() || !HaveRingBuffer()) {
51     immediate_entry_count_ = 0;
52     return;
53   }
54
55   // Get maximum safe contiguous entries.
56   const int32 curr_get = get_offset();
57   if (curr_get > put_) {
58     immediate_entry_count_ = curr_get - put_ - 1;
59   } else {
60     immediate_entry_count_ =
61         total_entry_count_ - put_ - (curr_get == 0 ? 1 : 0);
62   }
63
64   // Limit entry count to force early flushing.
65   if (flush_automatically_) {
66     int32 limit =
67         total_entry_count_ /
68         ((curr_get == last_put_sent_) ? kAutoFlushSmall : kAutoFlushBig);
69
70     int32 pending =
71         (put_ + total_entry_count_ - last_put_sent_) % total_entry_count_;
72
73     if (pending > 0 && pending >= limit) {
74       // Time to force flush.
75       immediate_entry_count_ = 0;
76     } else {
77       // Limit remaining entries, but not lower than waiting_count entries to
78       // prevent deadlock when command size is greater than the flush limit.
79       limit -= pending;
80       limit = limit < waiting_count ? waiting_count : limit;
81       immediate_entry_count_ =
82           immediate_entry_count_ > limit ? limit : immediate_entry_count_;
83     }
84   }
85 }
86
87 bool CommandBufferHelper::AllocateRingBuffer() {
88   if (!usable()) {
89     return false;
90   }
91
92   if (HaveRingBuffer()) {
93     return true;
94   }
95
96   int32 id = -1;
97   Buffer buffer = command_buffer_->CreateTransferBuffer(ring_buffer_size_, &id);
98   if (id < 0) {
99     ClearUsable();
100     return false;
101   }
102
103   ring_buffer_ = buffer;
104   ring_buffer_id_ = id;
105   command_buffer_->SetGetBuffer(id);
106
107   // TODO(gman): Do we really need to call GetState here? We know get & put = 0
108   // Also do we need to check state.num_entries?
109   CommandBuffer::State state = command_buffer_->GetState();
110   entries_ = static_cast<CommandBufferEntry*>(ring_buffer_.ptr);
111   int32 num_ring_buffer_entries =
112       ring_buffer_size_ / sizeof(CommandBufferEntry);
113   if (num_ring_buffer_entries > state.num_entries) {
114     ClearUsable();
115     return false;
116   }
117
118   total_entry_count_ = num_ring_buffer_entries;
119   put_ = state.put_offset;
120   CalcImmediateEntries(0);
121   return true;
122 }
123
124 void CommandBufferHelper::FreeResources() {
125   if (HaveRingBuffer()) {
126     command_buffer_->DestroyTransferBuffer(ring_buffer_id_);
127     ring_buffer_id_ = -1;
128     CalcImmediateEntries(0);
129   }
130 }
131
132 void CommandBufferHelper::FreeRingBuffer() {
133   CHECK((put_ == get_offset()) ||
134       error::IsError(command_buffer_->GetLastState().error));
135   FreeResources();
136 }
137
138 bool CommandBufferHelper::Initialize(int32 ring_buffer_size) {
139   ring_buffer_size_ = ring_buffer_size;
140   return AllocateRingBuffer();
141 }
142
143 CommandBufferHelper::~CommandBufferHelper() {
144   FreeResources();
145 }
146
147 bool CommandBufferHelper::FlushSync() {
148   if (!usable()) {
149     return false;
150   }
151
152   // Wrap put_ before flush.
153   if (put_ == total_entry_count_)
154     put_ = 0;
155
156   last_flush_time_ = clock();
157   last_put_sent_ = put_;
158   CommandBuffer::State state = command_buffer_->FlushSync(put_, get_offset());
159   CalcImmediateEntries(0);
160   return state.error == error::kNoError;
161 }
162
163 void CommandBufferHelper::Flush() {
164   // Wrap put_ before flush.
165   if (put_ == total_entry_count_)
166     put_ = 0;
167
168   if (usable() && last_put_sent_ != put_) {
169     last_flush_time_ = clock();
170     last_put_sent_ = put_;
171     command_buffer_->Flush(put_);
172     CalcImmediateEntries(0);
173   }
174 }
175
176 #if defined(CMD_HELPER_PERIODIC_FLUSH_CHECK)
177 void CommandBufferHelper::PeriodicFlushCheck() {
178   clock_t current_time = clock();
179   if (current_time - last_flush_time_ > kPeriodicFlushDelay * CLOCKS_PER_SEC)
180     Flush();
181 }
182 #endif
183
184 // Calls Flush() and then waits until the buffer is empty. Break early if the
185 // error is set.
186 bool CommandBufferHelper::Finish() {
187   TRACE_EVENT0("gpu", "CommandBufferHelper::Finish");
188   if (!usable()) {
189     return false;
190   }
191   // If there is no work just exit.
192   if (put_ == get_offset()) {
193     return true;
194   }
195   DCHECK(HaveRingBuffer());
196   do {
197     // Do not loop forever if the flush fails, meaning the command buffer reader
198     // has shutdown.
199     if (!FlushSync())
200       return false;
201   } while (put_ != get_offset());
202
203   return true;
204 }
205
206 // Inserts a new token into the command stream. It uses an increasing value
207 // scheme so that we don't lose tokens (a token has passed if the current token
208 // value is higher than that token). Calls Finish() if the token value wraps,
209 // which will be rare.
210 int32 CommandBufferHelper::InsertToken() {
211   AllocateRingBuffer();
212   if (!usable()) {
213     return token_;
214   }
215   DCHECK(HaveRingBuffer());
216   // Increment token as 31-bit integer. Negative values are used to signal an
217   // error.
218   token_ = (token_ + 1) & 0x7FFFFFFF;
219   cmd::SetToken* cmd = GetCmdSpace<cmd::SetToken>();
220   if (cmd) {
221     cmd->Init(token_);
222     if (token_ == 0) {
223       TRACE_EVENT0("gpu", "CommandBufferHelper::InsertToken(wrapped)");
224       // we wrapped
225       Finish();
226       DCHECK_EQ(token_, last_token_read());
227     }
228   }
229   return token_;
230 }
231
232 // Waits until the current token value is greater or equal to the value passed
233 // in argument.
234 void CommandBufferHelper::WaitForToken(int32 token) {
235   if (!usable() || !HaveRingBuffer()) {
236     return;
237   }
238   // Return immediately if corresponding InsertToken failed.
239   if (token < 0)
240     return;
241   if (token > token_) return;  // we wrapped
242   while (last_token_read() < token) {
243     if (get_offset() == put_) {
244       LOG(FATAL) << "Empty command buffer while waiting on a token.";
245       return;
246     }
247     // Do not loop forever if the flush fails, meaning the command buffer reader
248     // has shutdown.
249     if (!FlushSync())
250       return;
251   }
252 }
253
254 // Waits for available entries, basically waiting until get >= put + count + 1.
255 // It actually waits for contiguous entries, so it may need to wrap the buffer
256 // around, adding a noops. Thus this function may change the value of put_. The
257 // function will return early if an error occurs, in which case the available
258 // space may not be available.
259 void CommandBufferHelper::WaitForAvailableEntries(int32 count) {
260   AllocateRingBuffer();
261   if (!usable()) {
262     return;
263   }
264   DCHECK(HaveRingBuffer());
265   DCHECK(count < total_entry_count_);
266   if (put_ + count > total_entry_count_) {
267     // There's not enough room between the current put and the end of the
268     // buffer, so we need to wrap. We will add noops all the way to the end,
269     // but we need to make sure get wraps first, actually that get is 1 or
270     // more (since put will wrap to 0 after we add the noops).
271     DCHECK_LE(1, put_);
272     int32 curr_get = get_offset();
273     if (curr_get > put_ || curr_get == 0) {
274       TRACE_EVENT0("gpu", "CommandBufferHelper::WaitForAvailableEntries");
275       while (curr_get > put_ || curr_get == 0) {
276         // Do not loop forever if the flush fails, meaning the command buffer
277         // reader has shutdown.
278         if (!FlushSync())
279           return;
280         curr_get = get_offset();
281       }
282     }
283     // Insert Noops to fill out the buffer.
284     int32 num_entries = total_entry_count_ - put_;
285     while (num_entries > 0) {
286       int32 num_to_skip = std::min(CommandHeader::kMaxSize, num_entries);
287       cmd::Noop::Set(&entries_[put_], num_to_skip);
288       put_ += num_to_skip;
289       num_entries -= num_to_skip;
290     }
291     put_ = 0;
292   }
293
294   // Try to get 'count' entries without flushing.
295   CalcImmediateEntries(count);
296   if (immediate_entry_count_ < count) {
297     // Try again with a shallow Flush().
298     Flush();
299     CalcImmediateEntries(count);
300     if (immediate_entry_count_ < count) {
301       // Buffer is full.  Need to wait for entries.
302       TRACE_EVENT0("gpu", "CommandBufferHelper::WaitForAvailableEntries1");
303       while (immediate_entry_count_ < count) {
304         // Do not loop forever if the flush fails, meaning the command buffer
305         // reader has shutdown.
306         if (!FlushSync())
307           return;
308         CalcImmediateEntries(count);
309       }
310     }
311   }
312 }
313
314
315 }  // namespace gpu