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