• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 command buffer helper class.
6 
7 #ifndef GPU_COMMAND_BUFFER_CLIENT_CMD_BUFFER_HELPER_H_
8 #define GPU_COMMAND_BUFFER_CLIENT_CMD_BUFFER_HELPER_H_
9 
10 #include <string.h>
11 #include <time.h>
12 
13 #include "gpu/command_buffer/common/cmd_buffer_common.h"
14 #include "gpu/command_buffer/common/command_buffer.h"
15 #include "gpu/command_buffer/common/constants.h"
16 #include "gpu/gpu_export.h"
17 
18 namespace gpu {
19 
20 #if !defined(OS_ANDROID)
21 #define CMD_HELPER_PERIODIC_FLUSH_CHECK
22 const int kCommandsPerFlushCheck = 100;
23 const float kPeriodicFlushDelay = 1.0f / (5.0f * 60.0f);
24 #endif
25 
26 const int kAutoFlushSmall = 16;  // 1/16 of the buffer
27 const int kAutoFlushBig = 2;     // 1/2 of the buffer
28 
29 // Command buffer helper class. This class simplifies ring buffer management:
30 // it will allocate the buffer, give it to the buffer interface, and let the
31 // user add commands to it, while taking care of the synchronization (put and
32 // get). It also provides a way to ensure commands have been executed, through
33 // the token mechanism:
34 //
35 // helper.AddCommand(...);
36 // helper.AddCommand(...);
37 // int32 token = helper.InsertToken();
38 // helper.AddCommand(...);
39 // helper.AddCommand(...);
40 // [...]
41 //
42 // helper.WaitForToken(token);  // this doesn't return until the first two
43 //                              // commands have been executed.
44 class GPU_EXPORT CommandBufferHelper {
45  public:
46   explicit CommandBufferHelper(CommandBuffer* command_buffer);
47   virtual ~CommandBufferHelper();
48 
49   // Initializes the CommandBufferHelper.
50   // Parameters:
51   //   ring_buffer_size: The size of the ring buffer portion of the command
52   //       buffer.
53   bool Initialize(int32 ring_buffer_size);
54 
55   // Sets whether the command buffer should automatically flush periodically
56   // to try to increase performance. Defaults to true.
57   void SetAutomaticFlushes(bool enabled);
58 
59   // True if the context is lost.
60   bool IsContextLost();
61 
62   // Asynchronously flushes the commands, setting the put pointer to let the
63   // buffer interface know that new commands have been added. After a flush
64   // returns, the command buffer service is aware of all pending commands.
65   void Flush();
66 
67   // Waits until all the commands have been executed. Returns whether it
68   // was successful. The function will fail if the command buffer service has
69   // disconnected.
70   bool Finish();
71 
72   // Waits until a given number of available entries are available.
73   // Parameters:
74   //   count: number of entries needed. This value must be at most
75   //     the size of the buffer minus one.
76   void WaitForAvailableEntries(int32 count);
77 
78   // Inserts a new token into the command buffer. This token either has a value
79   // different from previously inserted tokens, or ensures that previously
80   // inserted tokens with that value have already passed through the command
81   // stream.
82   // Returns:
83   //   the value of the new token or -1 if the command buffer reader has
84   //   shutdown.
85   int32 InsertToken();
86 
87   // Returns true if the token has passed.
88   // Parameters:
89   //   the value of the token to check whether it has passed
HasTokenPassed(int32 token)90   bool HasTokenPassed(int32 token) const {
91     if (token > token_)
92       return true;  // we wrapped
93     return last_token_read() >= token;
94   }
95 
96   // Waits until the token of a particular value has passed through the command
97   // stream (i.e. commands inserted before that token have been executed).
98   // NOTE: This will call Flush if it needs to block.
99   // Parameters:
100   //   the value of the token to wait for.
101   void WaitForToken(int32 token);
102 
103   // Called prior to each command being issued. Waits for a certain amount of
104   // space to be available. Returns address of space.
GetSpace(int32 entries)105   void* GetSpace(int32 entries) {
106 #if defined(CMD_HELPER_PERIODIC_FLUSH_CHECK)
107     // Allow this command buffer to be pre-empted by another if a "reasonable"
108     // amount of work has been done. On highend machines, this reduces the
109     // latency of GPU commands. However, on Android, this can cause the
110     // kernel to thrash between generating GPU commands and executing them.
111     ++commands_issued_;
112     if (flush_automatically_ &&
113         (commands_issued_ % kCommandsPerFlushCheck == 0)) {
114       PeriodicFlushCheck();
115     }
116 #endif
117 
118     // Test for immediate entries.
119     if (entries > immediate_entry_count_) {
120       WaitForAvailableEntries(entries);
121       if (entries > immediate_entry_count_)
122         return NULL;
123     }
124 
125     DCHECK_LE(entries, immediate_entry_count_);
126 
127     // Allocate space and advance put_.
128     CommandBufferEntry* space = &entries_[put_];
129     put_ += entries;
130     immediate_entry_count_ -= entries;
131 
132     DCHECK_LE(put_, total_entry_count_);
133     return space;
134   }
135 
136   template <typename T>
ForceNullCheck(T * data)137   void ForceNullCheck(T* data) {
138 #if defined(OS_WIN) && defined(ARCH_CPU_64_BITS)
139     // 64-bit MSVC's alias analysis was determining that the command buffer
140     // entry couldn't be NULL, so it optimized out the NULL check.
141     // Dereferencing the same datatype through a volatile pointer seems to
142     // prevent that from happening. http://crbug.com/361936
143     if (data)
144       static_cast<volatile T*>(data)->header;
145 #endif
146   }
147 
148   // Typed version of GetSpace. Gets enough room for the given type and returns
149   // a reference to it.
150   template <typename T>
GetCmdSpace()151   T* GetCmdSpace() {
152     COMPILE_ASSERT(T::kArgFlags == cmd::kFixed, Cmd_kArgFlags_not_kFixed);
153     int32 space_needed = ComputeNumEntries(sizeof(T));
154     T* data = static_cast<T*>(GetSpace(space_needed));
155     ForceNullCheck(data);
156     return data;
157   }
158 
159   // Typed version of GetSpace for immediate commands.
160   template <typename T>
GetImmediateCmdSpace(size_t data_space)161   T* GetImmediateCmdSpace(size_t data_space) {
162     COMPILE_ASSERT(T::kArgFlags == cmd::kAtLeastN, Cmd_kArgFlags_not_kAtLeastN);
163     int32 space_needed = ComputeNumEntries(sizeof(T) + data_space);
164     T* data = static_cast<T*>(GetSpace(space_needed));
165     ForceNullCheck(data);
166     return data;
167   }
168 
169   // Typed version of GetSpace for immediate commands.
170   template <typename T>
GetImmediateCmdSpaceTotalSize(size_t total_space)171   T* GetImmediateCmdSpaceTotalSize(size_t total_space) {
172     COMPILE_ASSERT(T::kArgFlags == cmd::kAtLeastN, Cmd_kArgFlags_not_kAtLeastN);
173     int32 space_needed = ComputeNumEntries(total_space);
174     T* data = static_cast<T*>(GetSpace(space_needed));
175     ForceNullCheck(data);
176     return data;
177   }
178 
last_token_read()179   int32 last_token_read() const {
180     return command_buffer_->GetLastToken();
181   }
182 
get_offset()183   int32 get_offset() const {
184     return command_buffer_->GetLastState().get_offset;
185   }
186 
187   // Common Commands
Noop(uint32 skip_count)188   void Noop(uint32 skip_count) {
189     cmd::Noop* cmd = GetImmediateCmdSpace<cmd::Noop>(
190         (skip_count - 1) * sizeof(CommandBufferEntry));
191     if (cmd) {
192       cmd->Init(skip_count);
193     }
194   }
195 
SetToken(uint32 token)196   void SetToken(uint32 token) {
197     cmd::SetToken* cmd = GetCmdSpace<cmd::SetToken>();
198     if (cmd) {
199       cmd->Init(token);
200     }
201   }
202 
SetBucketSize(uint32 bucket_id,uint32 size)203   void SetBucketSize(uint32 bucket_id, uint32 size) {
204     cmd::SetBucketSize* cmd = GetCmdSpace<cmd::SetBucketSize>();
205     if (cmd) {
206       cmd->Init(bucket_id, size);
207     }
208   }
209 
SetBucketData(uint32 bucket_id,uint32 offset,uint32 size,uint32 shared_memory_id,uint32 shared_memory_offset)210   void SetBucketData(uint32 bucket_id,
211                      uint32 offset,
212                      uint32 size,
213                      uint32 shared_memory_id,
214                      uint32 shared_memory_offset) {
215     cmd::SetBucketData* cmd = GetCmdSpace<cmd::SetBucketData>();
216     if (cmd) {
217       cmd->Init(bucket_id,
218                 offset,
219                 size,
220                 shared_memory_id,
221                 shared_memory_offset);
222     }
223   }
224 
SetBucketDataImmediate(uint32 bucket_id,uint32 offset,const void * data,uint32 size)225   void SetBucketDataImmediate(
226       uint32 bucket_id, uint32 offset, const void* data, uint32 size) {
227     cmd::SetBucketDataImmediate* cmd =
228         GetImmediateCmdSpace<cmd::SetBucketDataImmediate>(size);
229     if (cmd) {
230       cmd->Init(bucket_id, offset, size);
231       memcpy(ImmediateDataAddress(cmd), data, size);
232     }
233   }
234 
GetBucketStart(uint32 bucket_id,uint32 result_memory_id,uint32 result_memory_offset,uint32 data_memory_size,uint32 data_memory_id,uint32 data_memory_offset)235   void GetBucketStart(uint32 bucket_id,
236                       uint32 result_memory_id,
237                       uint32 result_memory_offset,
238                       uint32 data_memory_size,
239                       uint32 data_memory_id,
240                       uint32 data_memory_offset) {
241     cmd::GetBucketStart* cmd = GetCmdSpace<cmd::GetBucketStart>();
242     if (cmd) {
243       cmd->Init(bucket_id,
244                 result_memory_id,
245                 result_memory_offset,
246                 data_memory_size,
247                 data_memory_id,
248                 data_memory_offset);
249     }
250   }
251 
GetBucketData(uint32 bucket_id,uint32 offset,uint32 size,uint32 shared_memory_id,uint32 shared_memory_offset)252   void GetBucketData(uint32 bucket_id,
253                      uint32 offset,
254                      uint32 size,
255                      uint32 shared_memory_id,
256                      uint32 shared_memory_offset) {
257     cmd::GetBucketData* cmd = GetCmdSpace<cmd::GetBucketData>();
258     if (cmd) {
259       cmd->Init(bucket_id,
260                 offset,
261                 size,
262                 shared_memory_id,
263                 shared_memory_offset);
264     }
265   }
266 
command_buffer()267   CommandBuffer* command_buffer() const {
268     return command_buffer_;
269   }
270 
get_ring_buffer()271   scoped_refptr<Buffer> get_ring_buffer() const { return ring_buffer_; }
272 
flush_generation()273   uint32 flush_generation() const { return flush_generation_; }
274 
275   void FreeRingBuffer();
276 
HaveRingBuffer()277   bool HaveRingBuffer() const {
278     return ring_buffer_id_ != -1;
279   }
280 
usable()281   bool usable () const {
282     return usable_;
283   }
284 
ClearUsable()285   void ClearUsable() {
286     usable_ = false;
287     CalcImmediateEntries(0);
288   }
289 
290  private:
291   // Returns the number of available entries (they may not be contiguous).
AvailableEntries()292   int32 AvailableEntries() {
293     return (get_offset() - put_ - 1 + total_entry_count_) % total_entry_count_;
294   }
295 
296   void CalcImmediateEntries(int waiting_count);
297   bool AllocateRingBuffer();
298   void FreeResources();
299 
300   // Waits for the get offset to be in a specific range, inclusive. Returns
301   // false if there was an error.
302   bool WaitForGetOffsetInRange(int32 start, int32 end);
303 
304 #if defined(CMD_HELPER_PERIODIC_FLUSH_CHECK)
305   // Calls Flush if automatic flush conditions are met.
306   void PeriodicFlushCheck();
307 #endif
308 
309   CommandBuffer* command_buffer_;
310   int32 ring_buffer_id_;
311   int32 ring_buffer_size_;
312   scoped_refptr<gpu::Buffer> ring_buffer_;
313   CommandBufferEntry* entries_;
314   int32 total_entry_count_;  // the total number of entries
315   int32 immediate_entry_count_;
316   int32 token_;
317   int32 put_;
318   int32 last_put_sent_;
319 
320 #if defined(CMD_HELPER_PERIODIC_FLUSH_CHECK)
321   int commands_issued_;
322 #endif
323 
324   bool usable_;
325   bool context_lost_;
326   bool flush_automatically_;
327 
328   // Using C runtime instead of base because this file cannot depend on base.
329   clock_t last_flush_time_;
330 
331   // Incremented every time the helper flushes the command buffer.
332   // Can be used to track when prior commands have been flushed.
333   uint32 flush_generation_;
334 
335   friend class CommandBufferHelperTest;
336   DISALLOW_COPY_AND_ASSIGN(CommandBufferHelper);
337 };
338 
339 }  // namespace gpu
340 
341 #endif  // GPU_COMMAND_BUFFER_CLIENT_CMD_BUFFER_HELPER_H_
342