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