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 common parts of command buffer formats. 6 7#ifndef GPU_COMMAND_BUFFER_COMMON_CMD_BUFFER_COMMON_H_ 8#define GPU_COMMAND_BUFFER_COMMON_CMD_BUFFER_COMMON_H_ 9 10#include <stddef.h> 11#include <stdint.h> 12 13#include "base/logging.h" 14#include "base/macros.h" 15#include "gpu/command_buffer/common/bitfield_helpers.h" 16#include "gpu/gpu_export.h" 17 18namespace gpu { 19 20namespace cmd { 21 enum ArgFlags { 22 kFixed = 0x0, 23 kAtLeastN = 0x1 24 }; 25} // namespace cmd 26 27// Pack & unpack Command cmd_flags 28#define CMD_FLAG_SET_TRACE_LEVEL(level) ((level & 3) << 0) 29#define CMD_FLAG_GET_TRACE_LEVEL(cmd_flags) ((cmd_flags >> 0) & 3) 30 31// Computes the number of command buffer entries needed for a certain size. In 32// other words it rounds up to a multiple of entries. 33inline uint32_t ComputeNumEntries(size_t size_in_bytes) { 34 return static_cast<uint32_t>( 35 (size_in_bytes + sizeof(uint32_t) - 1) / sizeof(uint32_t)); // NOLINT 36} 37 38// Rounds up to a multiple of entries in bytes. 39inline size_t RoundSizeToMultipleOfEntries(size_t size_in_bytes) { 40 return ComputeNumEntries(size_in_bytes) * sizeof(uint32_t); // NOLINT 41} 42 43// Struct that defines the command header in the command buffer. 44struct CommandHeader { 45 uint32_t size:21; 46 uint32_t command:11; 47 48 GPU_EXPORT static const int32_t kMaxSize = (1 << 21) - 1; 49 50 void Init(uint32_t _command, int32_t _size) { 51 DCHECK_LE(_size, kMaxSize); 52 command = _command; 53 size = _size; 54 } 55 56 // Sets the header based on the passed in command. Can not be used for 57 // variable sized commands like immediate commands or Noop. 58 template <typename T> 59 void SetCmd() { 60 COMPILE_ASSERT(T::kArgFlags == cmd::kFixed, Cmd_kArgFlags_not_kFixed); 61 Init(T::kCmdId, ComputeNumEntries(sizeof(T))); // NOLINT 62 } 63 64 // Sets the header by a size in bytes of the immediate data after the command. 65 template <typename T> 66 void SetCmdBySize(uint32_t size_of_data_in_bytes) { 67 COMPILE_ASSERT(T::kArgFlags == cmd::kAtLeastN, Cmd_kArgFlags_not_kAtLeastN); 68 Init(T::kCmdId, 69 ComputeNumEntries(sizeof(T) + size_of_data_in_bytes)); // NOLINT 70 } 71 72 // Sets the header by a size in bytes. 73 template <typename T> 74 void SetCmdByTotalSize(uint32_t size_in_bytes) { 75 COMPILE_ASSERT(T::kArgFlags == cmd::kAtLeastN, Cmd_kArgFlags_not_kAtLeastN); 76 DCHECK_GE(size_in_bytes, sizeof(T)); // NOLINT 77 Init(T::kCmdId, ComputeNumEntries(size_in_bytes)); 78 } 79}; 80 81COMPILE_ASSERT(sizeof(CommandHeader) == 4, Sizeof_CommandHeader_is_not_4); 82 83// Union that defines possible command buffer entries. 84union CommandBufferEntry { 85 CommandHeader value_header; 86 uint32_t value_uint32; 87 int32_t value_int32; 88 float value_float; 89}; 90 91#define GPU_COMMAND_BUFFER_ENTRY_ALIGNMENT 4 92const size_t kCommandBufferEntrySize = GPU_COMMAND_BUFFER_ENTRY_ALIGNMENT; 93 94COMPILE_ASSERT(sizeof(CommandBufferEntry) == kCommandBufferEntrySize, 95 Sizeof_CommandBufferEntry_is_not_4); 96 97// Command buffer is GPU_COMMAND_BUFFER_ENTRY_ALIGNMENT byte aligned. 98#pragma pack(push, GPU_COMMAND_BUFFER_ENTRY_ALIGNMENT) 99 100// Gets the address of memory just after a structure in a typesafe way. This is 101// used for IMMEDIATE commands to get the address of the place to put the data. 102// Immediate command put their data direclty in the command buffer. 103// Parameters: 104// cmd: Address of command. 105template <typename T> 106void* ImmediateDataAddress(T* cmd) { 107 COMPILE_ASSERT(T::kArgFlags == cmd::kAtLeastN, Cmd_kArgFlags_not_kAtLeastN); 108 return reinterpret_cast<char*>(cmd) + sizeof(*cmd); 109} 110 111// Gets the address of the place to put the next command in a typesafe way. 112// This can only be used for fixed sized commands. 113template <typename T> 114// Parameters: 115// cmd: Address of command. 116void* NextCmdAddress(void* cmd) { 117 COMPILE_ASSERT(T::kArgFlags == cmd::kFixed, Cmd_kArgFlags_not_kFixed); 118 return reinterpret_cast<char*>(cmd) + sizeof(T); 119} 120 121// Gets the address of the place to put the next command in a typesafe way. 122// This can only be used for variable sized command like IMMEDIATE commands. 123// Parameters: 124// cmd: Address of command. 125// size_of_data_in_bytes: Size of the data for the command. 126template <typename T> 127void* NextImmediateCmdAddress(void* cmd, uint32_t size_of_data_in_bytes) { 128 COMPILE_ASSERT(T::kArgFlags == cmd::kAtLeastN, Cmd_kArgFlags_not_kAtLeastN); 129 return reinterpret_cast<char*>(cmd) + sizeof(T) + // NOLINT 130 RoundSizeToMultipleOfEntries(size_of_data_in_bytes); 131} 132 133// Gets the address of the place to put the next command in a typesafe way. 134// This can only be used for variable sized command like IMMEDIATE commands. 135// Parameters: 136// cmd: Address of command. 137// size_of_cmd_in_bytes: Size of the cmd and data. 138template <typename T> 139void* NextImmediateCmdAddressTotalSize(void* cmd, 140 uint32_t total_size_in_bytes) { 141 COMPILE_ASSERT(T::kArgFlags == cmd::kAtLeastN, Cmd_kArgFlags_not_kAtLeastN); 142 DCHECK_GE(total_size_in_bytes, sizeof(T)); // NOLINT 143 return reinterpret_cast<char*>(cmd) + 144 RoundSizeToMultipleOfEntries(total_size_in_bytes); 145} 146 147namespace cmd { 148 149// This macro is used to safely and convienently expand the list of commnad 150// buffer commands in to various lists and never have them get out of sync. To 151// add a new command, add it this list, create the corresponding structure below 152// and then add a function in gapi_decoder.cc called Handle_COMMAND_NAME where 153// COMMAND_NAME is the name of your command structure. 154// 155// NOTE: THE ORDER OF THESE MUST NOT CHANGE (their id is derived by order) 156#define COMMON_COMMAND_BUFFER_CMDS(OP) \ 157 OP(Noop) /* 0 */ \ 158 OP(SetToken) /* 1 */ \ 159 OP(SetBucketSize) /* 2 */ \ 160 OP(SetBucketData) /* 3 */ \ 161 OP(SetBucketDataImmediate) /* 4 */ \ 162 OP(GetBucketStart) /* 5 */ \ 163 OP(GetBucketData) /* 6 */ \ 164 165// Common commands. 166enum CommandId { 167 #define COMMON_COMMAND_BUFFER_CMD_OP(name) k ## name, 168 169 COMMON_COMMAND_BUFFER_CMDS(COMMON_COMMAND_BUFFER_CMD_OP) 170 171 #undef COMMON_COMMAND_BUFFER_CMD_OP 172 173 kNumCommands, 174 kLastCommonId = 255 // reserve 256 spaces for common commands. 175}; 176 177COMPILE_ASSERT(kNumCommands - 1 <= kLastCommonId, Too_many_common_commands); 178 179const char* GetCommandName(CommandId id); 180 181// A Noop command. 182struct Noop { 183 typedef Noop ValueType; 184 static const CommandId kCmdId = kNoop; 185 static const cmd::ArgFlags kArgFlags = cmd::kAtLeastN; 186 static const uint8_t cmd_flags = CMD_FLAG_SET_TRACE_LEVEL(3); 187 188 void SetHeader(uint32_t skip_count) { 189 DCHECK_GT(skip_count, 0u); 190 header.Init(kCmdId, skip_count); 191 } 192 193 void Init(uint32_t skip_count) { 194 SetHeader(skip_count); 195 } 196 197 static void* Set(void* cmd, uint32_t skip_count) { 198 static_cast<ValueType*>(cmd)->Init(skip_count); 199 return NextImmediateCmdAddress<ValueType>( 200 cmd, skip_count * sizeof(CommandBufferEntry)); // NOLINT 201 } 202 203 CommandHeader header; 204}; 205 206COMPILE_ASSERT(sizeof(Noop) == 4, Sizeof_Noop_is_not_4); 207COMPILE_ASSERT(offsetof(Noop, header) == 0, Offsetof_Noop_header_not_0); 208 209// The SetToken command puts a token in the command stream that you can 210// use to check if that token has been passed in the command stream. 211struct SetToken { 212 typedef SetToken ValueType; 213 static const CommandId kCmdId = kSetToken; 214 static const cmd::ArgFlags kArgFlags = cmd::kFixed; 215 static const uint8_t cmd_flags = CMD_FLAG_SET_TRACE_LEVEL(3); 216 217 void SetHeader() { 218 header.SetCmd<ValueType>(); 219 } 220 221 void Init(uint32_t _token) { 222 SetHeader(); 223 token = _token; 224 } 225 static void* Set(void* cmd, uint32_t token) { 226 static_cast<ValueType*>(cmd)->Init(token); 227 return NextCmdAddress<ValueType>(cmd); 228 } 229 230 CommandHeader header; 231 uint32_t token; 232}; 233 234COMPILE_ASSERT(sizeof(SetToken) == 8, Sizeof_SetToken_is_not_8); 235COMPILE_ASSERT(offsetof(SetToken, header) == 0, 236 Offsetof_SetToken_header_not_0); 237COMPILE_ASSERT(offsetof(SetToken, token) == 4, 238 Offsetof_SetToken_token_not_4); 239 240// Sets the size of a bucket for collecting data on the service side. 241// This is a utility for gathering data on the service side so it can be used 242// all at once when some service side API is called. It removes the need to add 243// special commands just to support a particular API. For example, any API 244// command that needs a string needs a way to send that string to the API over 245// the command buffers. While you can require that the command buffer or 246// transfer buffer be large enough to hold the largest string you can send, 247// using this command removes that restriction by letting you send smaller 248// pieces over and build up the data on the service side. 249// 250// You can clear a bucket on the service side and thereby free memory by sending 251// a size of 0. 252struct SetBucketSize { 253 typedef SetBucketSize ValueType; 254 static const CommandId kCmdId = kSetBucketSize; 255 static const cmd::ArgFlags kArgFlags = cmd::kFixed; 256 static const uint8_t cmd_flags = CMD_FLAG_SET_TRACE_LEVEL(3); 257 258 void SetHeader() { 259 header.SetCmd<ValueType>(); 260 } 261 262 void Init(uint32_t _bucket_id, uint32_t _size) { 263 SetHeader(); 264 bucket_id = _bucket_id; 265 size = _size; 266 } 267 static void* Set(void* cmd, uint32_t _bucket_id, uint32_t _size) { 268 static_cast<ValueType*>(cmd)->Init(_bucket_id, _size); 269 return NextCmdAddress<ValueType>(cmd); 270 } 271 272 CommandHeader header; 273 uint32_t bucket_id; 274 uint32_t size; 275}; 276 277COMPILE_ASSERT(sizeof(SetBucketSize) == 12, Sizeof_SetBucketSize_is_not_8); 278COMPILE_ASSERT(offsetof(SetBucketSize, header) == 0, 279 Offsetof_SetBucketSize_header_not_0); 280COMPILE_ASSERT(offsetof(SetBucketSize, bucket_id) == 4, 281 Offsetof_SetBucketSize_bucket_id_4); 282COMPILE_ASSERT(offsetof(SetBucketSize, size) == 8, 283 Offsetof_SetBucketSize_size_8); 284 285// Sets the contents of a portion of a bucket on the service side from data in 286// shared memory. 287// See SetBucketSize. 288struct SetBucketData { 289 typedef SetBucketData ValueType; 290 static const CommandId kCmdId = kSetBucketData; 291 static const cmd::ArgFlags kArgFlags = cmd::kFixed; 292 static const uint8_t cmd_flags = CMD_FLAG_SET_TRACE_LEVEL(3); 293 294 void SetHeader() { 295 header.SetCmd<ValueType>(); 296 } 297 298 void Init(uint32_t _bucket_id, 299 uint32_t _offset, 300 uint32_t _size, 301 uint32_t _shared_memory_id, 302 uint32_t _shared_memory_offset) { 303 SetHeader(); 304 bucket_id = _bucket_id; 305 offset = _offset; 306 size = _size; 307 shared_memory_id = _shared_memory_id; 308 shared_memory_offset = _shared_memory_offset; 309 } 310 static void* Set(void* cmd, 311 uint32_t _bucket_id, 312 uint32_t _offset, 313 uint32_t _size, 314 uint32_t _shared_memory_id, 315 uint32_t _shared_memory_offset) { 316 static_cast<ValueType*>(cmd)->Init( 317 _bucket_id, 318 _offset, 319 _size, 320 _shared_memory_id, 321 _shared_memory_offset); 322 return NextCmdAddress<ValueType>(cmd); 323 } 324 325 CommandHeader header; 326 uint32_t bucket_id; 327 uint32_t offset; 328 uint32_t size; 329 uint32_t shared_memory_id; 330 uint32_t shared_memory_offset; 331}; 332 333COMPILE_ASSERT(sizeof(SetBucketData) == 24, Sizeof_SetBucketData_is_not_24); 334COMPILE_ASSERT(offsetof(SetBucketData, header) == 0, 335 Offsetof_SetBucketData_header_not_0); 336COMPILE_ASSERT(offsetof(SetBucketData, bucket_id) == 4, 337 Offsetof_SetBucketData_bucket_id_not_4); 338COMPILE_ASSERT(offsetof(SetBucketData, offset) == 8, 339 Offsetof_SetBucketData_offset_not_8); 340COMPILE_ASSERT(offsetof(SetBucketData, size) == 12, 341 Offsetof_SetBucketData_size_not_12); 342COMPILE_ASSERT(offsetof(SetBucketData, shared_memory_id) == 16, 343 Offsetof_SetBucketData_shared_memory_id_not_16); 344COMPILE_ASSERT(offsetof(SetBucketData, shared_memory_offset) == 20, 345 Offsetof_SetBucketData_shared_memory_offset_not_20); 346 347// Sets the contents of a portion of a bucket on the service side from data in 348// the command buffer. 349// See SetBucketSize. 350struct SetBucketDataImmediate { 351 typedef SetBucketDataImmediate ValueType; 352 static const CommandId kCmdId = kSetBucketDataImmediate; 353 static const cmd::ArgFlags kArgFlags = cmd::kAtLeastN; 354 static const uint8_t cmd_flags = CMD_FLAG_SET_TRACE_LEVEL(3); 355 356 void SetHeader(uint32_t size) { 357 header.SetCmdBySize<ValueType>(size); 358 } 359 360 void Init(uint32_t _bucket_id, 361 uint32_t _offset, 362 uint32_t _size) { 363 SetHeader(_size); 364 bucket_id = _bucket_id; 365 offset = _offset; 366 size = _size; 367 } 368 static void* Set(void* cmd, 369 uint32_t _bucket_id, 370 uint32_t _offset, 371 uint32_t _size) { 372 static_cast<ValueType*>(cmd)->Init( 373 _bucket_id, 374 _offset, 375 _size); 376 return NextImmediateCmdAddress<ValueType>(cmd, _size); 377 } 378 379 CommandHeader header; 380 uint32_t bucket_id; 381 uint32_t offset; 382 uint32_t size; 383}; 384 385COMPILE_ASSERT(sizeof(SetBucketDataImmediate) == 16, 386 Sizeof_SetBucketDataImmediate_is_not_24); 387COMPILE_ASSERT(offsetof(SetBucketDataImmediate, header) == 0, 388 Offsetof_SetBucketDataImmediate_header_not_0); 389COMPILE_ASSERT(offsetof(SetBucketDataImmediate, bucket_id) == 4, 390 Offsetof_SetBucketDataImmediate_bucket_id_not_4); 391COMPILE_ASSERT(offsetof(SetBucketDataImmediate, offset) == 8, 392 Offsetof_SetBucketDataImmediate_offset_not_8); 393COMPILE_ASSERT(offsetof(SetBucketDataImmediate, size) == 12, 394 Offsetof_SetBucketDataImmediate_size_not_12); 395 396// Gets the start of a bucket the service has available. Sending a variable size 397// result back to the client and the portion of that result that fits in the 398// supplied shared memory. If the size of the result is larger than the supplied 399// shared memory the rest of the bucket's contents can be retrieved with 400// GetBucketData. 401// 402// This is used for example for any API that returns a string. The problem is 403// the largest thing you can send back in 1 command is the size of your shared 404// memory. This command along with GetBucketData implements a way to get a 405// result a piece at a time to help solve that problem in a generic way. 406struct GetBucketStart { 407 typedef GetBucketStart ValueType; 408 static const CommandId kCmdId = kGetBucketStart; 409 static const cmd::ArgFlags kArgFlags = cmd::kFixed; 410 static const uint8_t cmd_flags = CMD_FLAG_SET_TRACE_LEVEL(3); 411 412 typedef uint32_t Result; 413 414 void SetHeader() { 415 header.SetCmd<ValueType>(); 416 } 417 418 void Init(uint32_t _bucket_id, 419 uint32_t _result_memory_id, 420 uint32_t _result_memory_offset, 421 uint32_t _data_memory_size, 422 uint32_t _data_memory_id, 423 uint32_t _data_memory_offset) { 424 SetHeader(); 425 bucket_id = _bucket_id; 426 result_memory_id = _result_memory_id; 427 result_memory_offset = _result_memory_offset; 428 data_memory_size = _data_memory_size; 429 data_memory_id = _data_memory_id; 430 data_memory_offset = _data_memory_offset; 431 } 432 static void* Set(void* cmd, 433 uint32_t _bucket_id, 434 uint32_t _result_memory_id, 435 uint32_t _result_memory_offset, 436 uint32_t _data_memory_size, 437 uint32_t _data_memory_id, 438 uint32_t _data_memory_offset) { 439 static_cast<ValueType*>(cmd)->Init( 440 _bucket_id, 441 _result_memory_id, 442 _result_memory_offset, 443 _data_memory_size, 444 _data_memory_id, 445 _data_memory_offset); 446 return NextCmdAddress<ValueType>(cmd); 447 } 448 449 CommandHeader header; 450 uint32_t bucket_id; 451 uint32_t result_memory_id; 452 uint32_t result_memory_offset; 453 uint32_t data_memory_size; 454 uint32_t data_memory_id; 455 uint32_t data_memory_offset; 456}; 457 458COMPILE_ASSERT(sizeof(GetBucketStart) == 28, Sizeof_GetBucketStart_is_not_28); 459COMPILE_ASSERT(offsetof(GetBucketStart, header) == 0, 460 Offsetof_GetBucketStart_header_not_0); 461COMPILE_ASSERT(offsetof(GetBucketStart, bucket_id) == 4, 462 Offsetof_GetBucketStart_bucket_id_not_4); 463COMPILE_ASSERT(offsetof(GetBucketStart, result_memory_id) == 8, 464 Offsetof_GetBucketStart_result_memory_id_not_8); 465COMPILE_ASSERT(offsetof(GetBucketStart, result_memory_offset) == 12, 466 Offsetof_GetBucketStart_result_memory_offset_not_12); 467COMPILE_ASSERT(offsetof(GetBucketStart, data_memory_size) == 16, 468 Offsetof_GetBucketStart_data_memory_size_not_16); 469COMPILE_ASSERT(offsetof(GetBucketStart, data_memory_id) == 20, 470 Offsetof_GetBucketStart_data_memory_id_not_20); 471COMPILE_ASSERT(offsetof(GetBucketStart, data_memory_offset) == 24, 472 Offsetof_GetBucketStart_data_memory_offset_not_24); 473 474// Gets a piece of a result the service as available. 475// See GetBucketSize. 476struct GetBucketData { 477 typedef GetBucketData ValueType; 478 static const CommandId kCmdId = kGetBucketData; 479 static const cmd::ArgFlags kArgFlags = cmd::kFixed; 480 static const uint8_t cmd_flags = CMD_FLAG_SET_TRACE_LEVEL(3); 481 482 void SetHeader() { 483 header.SetCmd<ValueType>(); 484 } 485 486 void Init(uint32_t _bucket_id, 487 uint32_t _offset, 488 uint32_t _size, 489 uint32_t _shared_memory_id, 490 uint32_t _shared_memory_offset) { 491 SetHeader(); 492 bucket_id = _bucket_id; 493 offset = _offset; 494 size = _size; 495 shared_memory_id = _shared_memory_id; 496 shared_memory_offset = _shared_memory_offset; 497 } 498 static void* Set(void* cmd, 499 uint32_t _bucket_id, 500 uint32_t _offset, 501 uint32_t _size, 502 uint32_t _shared_memory_id, 503 uint32_t _shared_memory_offset) { 504 static_cast<ValueType*>(cmd)->Init( 505 _bucket_id, 506 _offset, 507 _size, 508 _shared_memory_id, 509 _shared_memory_offset); 510 return NextCmdAddress<ValueType>(cmd); 511 } 512 513 CommandHeader header; 514 uint32_t bucket_id; 515 uint32_t offset; 516 uint32_t size; 517 uint32_t shared_memory_id; 518 uint32_t shared_memory_offset; 519}; 520 521COMPILE_ASSERT(sizeof(GetBucketData) == 24, Sizeof_GetBucketData_is_not_20); 522COMPILE_ASSERT(offsetof(GetBucketData, header) == 0, 523 Offsetof_GetBucketData_header_not_0); 524COMPILE_ASSERT(offsetof(GetBucketData, bucket_id) == 4, 525 Offsetof_GetBucketData_bucket_id_not_4); 526COMPILE_ASSERT(offsetof(GetBucketData, offset) == 8, 527 Offsetof_GetBucketData_offset_not_8); 528COMPILE_ASSERT(offsetof(GetBucketData, size) == 12, 529 Offsetof_GetBucketData_size_not_12); 530COMPILE_ASSERT(offsetof(GetBucketData, shared_memory_id) == 16, 531 Offsetof_GetBucketData_shared_memory_id_not_16); 532COMPILE_ASSERT(offsetof(GetBucketData, shared_memory_offset) == 20, 533 Offsetof_GetBucketData_shared_memory_offset_not_20); 534 535} // namespace cmd 536 537#pragma pack(pop) 538 539} // namespace gpu 540 541#endif // GPU_COMMAND_BUFFER_COMMON_CMD_BUFFER_COMMON_H_ 542 543