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