blob: f33f4ef133e232625d1b85d438102bde8c111455 [file] [log] [blame]
/*
* Copyright 2009, Google Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
// This file contains the common parts of command buffer formats.
#ifndef GPU_COMMAND_BUFFER_COMMON_CROSS_CMD_BUFFER_COMMON_H_
#define GPU_COMMAND_BUFFER_COMMON_CROSS_CMD_BUFFER_COMMON_H_
#include "base/basictypes.h"
#include "gpu/command_buffer/common/types.h"
#include "gpu/command_buffer/common/bitfield_helpers.h"
#include "gpu/command_buffer/common/logging.h"
namespace command_buffer {
namespace cmd {
enum ArgFlags {
kFixed = 0x0,
kAtLeastN = 0x1,
};
} // namespace cmd
// Computes the number of command buffer entries needed for a certain size. In
// other words it rounds up to a multiple of entries.
inline uint32 ComputeNumEntries(size_t size_in_bytes) {
return static_cast<uint32>(
(size_in_bytes + sizeof(uint32) - 1) / sizeof(uint32)); // NOLINT
}
// Rounds up to a multiple of entries in bytes.
inline size_t RoundSizeToMultipleOfEntries(size_t size_in_bytes) {
return ComputeNumEntries(size_in_bytes) * sizeof(uint32); // NOLINT
}
// Struct that defines the command header in the command buffer.
struct CommandHeader {
Uint32 size:21;
Uint32 command:11;
void Init(uint32 _command, uint32 _size) {
DCHECK_LE(_size, 1u << 22);
command = _command;
size = _size;
}
// Sets the header based on the passed in command. Can not be used for
// variable sized commands like immediate commands or Noop.
template <typename T>
void SetCmd() {
COMPILE_ASSERT(T::kArgFlags == cmd::kFixed, Cmd_kArgFlags_not_kFixed);
Init(T::kCmdId, ComputeNumEntries(sizeof(T))); // NOLINT
}
// Sets the header by a size in bytes of the immediate data after the command.
template <typename T>
void SetCmdBySize(uint32 size_of_data_in_bytes) {
COMPILE_ASSERT(T::kArgFlags == cmd::kAtLeastN, Cmd_kArgFlags_not_kAtLeastN);
Init(T::kCmdId,
ComputeNumEntries(sizeof(T) + size_of_data_in_bytes)); // NOLINT
}
// Sets the header by a size in bytes.
template <typename T>
void SetCmdByTotalSize(uint32 size_in_bytes) {
COMPILE_ASSERT(T::kArgFlags == cmd::kAtLeastN, Cmd_kArgFlags_not_kAtLeastN);
DCHECK_GE(size_in_bytes, sizeof(T)); // NOLINT
Init(T::kCmdId, ComputeNumEntries(size_in_bytes));
}
};
COMPILE_ASSERT(sizeof(CommandHeader) == 4, Sizeof_CommandHeader_is_not_4);
// Union that defines possible command buffer entries.
union CommandBufferEntry {
CommandHeader value_header;
Uint32 value_uint32;
Int32 value_int32;
float value_float;
};
COMPILE_ASSERT(sizeof(CommandBufferEntry) == 4,
Sizeof_CommandBufferEntry_is_not_4);
// Make sure the compiler does not add extra padding to any of the command
// structures.
#pragma pack(push, 1)
// Gets the address of memory just after a structure in a typesafe way. This is
// used for IMMEDIATE commands to get the address of the place to put the data.
// Immediate command put their data direclty in the command buffer.
// Parameters:
// cmd: Address of command.
template <typename T>
void* ImmediateDataAddress(T* cmd) {
COMPILE_ASSERT(T::kArgFlags == cmd::kAtLeastN, Cmd_kArgFlags_not_kAtLeastN);
return reinterpret_cast<char*>(cmd) + sizeof(*cmd);
}
// Gets the address of the place to put the next command in a typesafe way.
// This can only be used for fixed sized commands.
template <typename T>
// Parameters:
// cmd: Address of command.
void* NextCmdAddress(void* cmd) {
COMPILE_ASSERT(T::kArgFlags == cmd::kFixed, Cmd_kArgFlags_not_kFixed);
return reinterpret_cast<char*>(cmd) + sizeof(T);
}
// Gets the address of the place to put the next command in a typesafe way.
// This can only be used for variable sized command like IMMEDIATE commands.
// Parameters:
// cmd: Address of command.
// size_of_data_in_bytes: Size of the data for the command.
template <typename T>
void* NextImmediateCmdAddress(void* cmd, uint32 size_of_data_in_bytes) {
COMPILE_ASSERT(T::kArgFlags == cmd::kAtLeastN, Cmd_kArgFlags_not_kAtLeastN);
return reinterpret_cast<char*>(cmd) + sizeof(T) + // NOLINT
RoundSizeToMultipleOfEntries(size_of_data_in_bytes);
}
// Gets the address of the place to put the next command in a typesafe way.
// This can only be used for variable sized command like IMMEDIATE commands.
// Parameters:
// cmd: Address of command.
// size_of_cmd_in_bytes: Size of the cmd and data.
template <typename T>
void* NextImmediateCmdAddressTotalSize(void* cmd, uint32 total_size_in_bytes) {
COMPILE_ASSERT(T::kArgFlags == cmd::kAtLeastN, Cmd_kArgFlags_not_kAtLeastN);
DCHECK_GE(total_size_in_bytes, sizeof(T)); // NOLINT
return reinterpret_cast<char*>(cmd) +
RoundSizeToMultipleOfEntries(total_size_in_bytes);
}
namespace cmd {
// This macro is used to safely and convienently expand the list of commnad
// buffer commands in to various lists and never have them get out of sync. To
// add a new command, add it this list, create the corresponding structure below
// and then add a function in gapi_decoder.cc called Handle_COMMAND_NAME where
// COMMAND_NAME is the name of your command structure.
//
// NOTE: THE ORDER OF THESE MUST NOT CHANGE (their id is derived by order)
#define COMMON_COMMAND_BUFFER_CMDS(OP) \
OP(Noop) /* 0 */ \
OP(SetToken) /* 1 */ \
OP(Jump) /* 2 */ \
OP(JumpRelative) /* 3 */ \
OP(Call) /* 4 */ \
OP(CallRelative) /* 5 */ \
OP(Return) /* 6 */ \
OP(SetBucketSize) /* 7 */ \
OP(SetBucketData) /* 8 */ \
OP(SetBucketDataImmediate) /* 9 */ \
OP(GetResultSize) /* 10 */ \
OP(GetResultData) /* 11 */ \
// Common commands.
enum CommandId {
#define COMMON_COMMAND_BUFFER_CMD_OP(name) k ## name,
COMMON_COMMAND_BUFFER_CMDS(COMMON_COMMAND_BUFFER_CMD_OP)
#undef COMMON_COMMAND_BUFFER_CMD_OP
kNumCommands,
kLastCommonId = 255, // reserve 256 spaces for common commands.
};
COMPILE_ASSERT(kNumCommands - 1 <= kLastCommonId, Too_many_common_commands);
const char* GetCommandName(CommandId id);
// A Noop command.
struct Noop {
typedef Noop ValueType;
static const CommandId kCmdId = kNoop;
static const cmd::ArgFlags kArgFlags = cmd::kAtLeastN;
void SetHeader(uint32 skip_count) {
header.Init(kCmdId, skip_count + 1);
}
void Init(uint32 skip_count) {
SetHeader(skip_count);
}
static void* Set(void* cmd, uint32 skip_count) {
static_cast<ValueType*>(cmd)->Init(skip_count);
return NextImmediateCmdAddress<ValueType>(
cmd, skip_count * sizeof(CommandBufferEntry)); // NOLINT
}
CommandHeader header;
};
COMPILE_ASSERT(sizeof(Noop) == 4, Sizeof_Noop_is_not_4);
COMPILE_ASSERT(offsetof(Noop, header) == 0, Offsetof_Noop_header_not_0);
// The SetToken command puts a token in the command stream that you can
// use to check if that token has been passed in the command stream.
struct SetToken {
typedef SetToken ValueType;
static const CommandId kCmdId = kSetToken;
static const cmd::ArgFlags kArgFlags = cmd::kFixed;
void SetHeader() {
header.SetCmd<ValueType>();
}
void Init(uint32 _token) {
SetHeader();
token = _token;
}
static void* Set(void* cmd, uint32 token) {
static_cast<ValueType*>(cmd)->Init(token);
return NextCmdAddress<ValueType>(cmd);
}
CommandHeader header;
uint32 token;
};
COMPILE_ASSERT(sizeof(SetToken) == 8, Sizeof_SetToken_is_not_8);
COMPILE_ASSERT(offsetof(SetToken, header) == 0,
Offsetof_SetToken_header_not_0);
COMPILE_ASSERT(offsetof(SetToken, token) == 4,
Offsetof_SetToken_token_not_4);
// The Jump command jumps to another place in the command buffer.
struct Jump {
typedef Jump ValueType;
static const CommandId kCmdId = kJump;
static const cmd::ArgFlags kArgFlags = cmd::kFixed;
void SetHeader() {
header.SetCmd<ValueType>();
}
void Init(uint32 _shared_memory_id, uint32 _shared_memory_offset) {
SetHeader();
shared_memory_id = _shared_memory_id;
shared_memory_offset = _shared_memory_offset;
}
static void* Set(
void* cmd, uint32 _shared_memory_id, uint32 _shared_memory_offset) {
static_cast<ValueType*>(cmd)->Init(
_shared_memory_id, _shared_memory_offset);
return NextCmdAddress<ValueType>(cmd);
}
CommandHeader header;
uint32 shared_memory_id;
uint32 shared_memory_offset;
};
COMPILE_ASSERT(sizeof(Jump) == 12, Sizeof_Jump_is_not_12);
COMPILE_ASSERT(offsetof(Jump, header) == 0,
Offsetof_Jump_header_not_0);
COMPILE_ASSERT(offsetof(Jump, shared_memory_id) == 4,
Offsetof_Jump_shared_memory_id_not_4);
COMPILE_ASSERT(offsetof(Jump, shared_memory_offset) == 8,
Offsetof_Jump_shared_memory_offset_not_8);
// The JumpRelative command jumps to another place in the command buffer
// relative to the end of this command. In other words. JumpRelative with an
// offset of zero is effectively a noop.
struct JumpRelative {
typedef JumpRelative ValueType;
static const CommandId kCmdId = kJumpRelative;
static const cmd::ArgFlags kArgFlags = cmd::kFixed;
void SetHeader() {
header.SetCmd<ValueType>();
}
void Init(int32 _offset) {
SetHeader();
offset = _offset;
}
static void* Set(void* cmd, int32 _offset) {
static_cast<ValueType*>(cmd)->Init(_offset);
return NextCmdAddress<ValueType>(cmd);
}
CommandHeader header;
int32 offset;
};
COMPILE_ASSERT(sizeof(JumpRelative) == 8, Sizeof_JumpRelative_is_not_8);
COMPILE_ASSERT(offsetof(JumpRelative, header) == 0,
Offsetof_JumpRelative_header_not_0);
COMPILE_ASSERT(offsetof(JumpRelative, offset) == 4,
Offsetof_JumpRelative_offset_4);
// The Call command jumps to a subroutine which can be returned from with the
// Return command.
struct Call {
typedef Call ValueType;
static const CommandId kCmdId = kCall;
static const cmd::ArgFlags kArgFlags = cmd::kFixed;
void SetHeader() {
header.SetCmd<ValueType>();
}
void Init(uint32 _shared_memory_id, uint32 _shared_memory_offset) {
SetHeader();
shared_memory_id = _shared_memory_id;
shared_memory_offset = _shared_memory_offset;
}
static void* Set(
void* cmd, uint32 _shared_memory_id, uint32 _shared_memory_offset) {
static_cast<ValueType*>(cmd)->Init(
_shared_memory_id, _shared_memory_offset);
return NextCmdAddress<ValueType>(cmd);
}
CommandHeader header;
uint32 shared_memory_id;
uint32 shared_memory_offset;
};
COMPILE_ASSERT(sizeof(Call) == 12, Sizeof_Call_is_not_12);
COMPILE_ASSERT(offsetof(Call, header) == 0,
Offsetof_Call_header_not_0);
COMPILE_ASSERT(offsetof(Call, shared_memory_id) == 4,
Offsetof_Call_shared_memory_id_not_4);
COMPILE_ASSERT(offsetof(Call, shared_memory_offset) == 8,
Offsetof_Call_shared_memory_offset_not_8);
// The CallRelative command jumps to a subroutine using a relative offset. The
// offset is relative to the end of this command..
struct CallRelative {
typedef CallRelative ValueType;
static const CommandId kCmdId = kCallRelative;
static const cmd::ArgFlags kArgFlags = cmd::kFixed;
void SetHeader() {
header.SetCmd<ValueType>();
}
void Init(int32 _offset) {
SetHeader();
offset = _offset;
}
static void* Set(void* cmd, int32 _offset) {
static_cast<ValueType*>(cmd)->Init(_offset);
return NextCmdAddress<ValueType>(cmd);
}
CommandHeader header;
int32 offset;
};
COMPILE_ASSERT(sizeof(CallRelative) == 8, Sizeof_CallRelative_is_not_8);
COMPILE_ASSERT(offsetof(CallRelative, header) == 0,
Offsetof_CallRelative_header_not_0);
COMPILE_ASSERT(offsetof(CallRelative, offset) == 4,
Offsetof_CallRelative_offset_4);
// Returns from a subroutine called by the Call or CallRelative commands.
struct Return {
typedef Return ValueType;
static const CommandId kCmdId = kReturn;
static const cmd::ArgFlags kArgFlags = cmd::kFixed;
void SetHeader() {
header.SetCmd<ValueType>();
}
void Init() {
SetHeader();
}
static void* Set(void* cmd) {
static_cast<ValueType*>(cmd)->Init();
return NextCmdAddress<ValueType>(cmd);
}
CommandHeader header;
};
COMPILE_ASSERT(sizeof(Return) == 4, Sizeof_Return_is_not_4);
COMPILE_ASSERT(offsetof(Return, header) == 0,
Offsetof_Return_header_not_0);
// Sets the size of a bucket for collecting data on the service side.
// This is a utility for gathering data on the service side so it can be used
// all at once when some service side API is called. It removes the need to add
// special commands just to support a particular API. For example, any API
// command that needs a string needs a way to send that string to the API over
// the command buffers. While you can require that the command buffer or
// transfer buffer be large enough to hold the largest string you can send,
// using this command removes that restriction by letting you send smaller
// pieces over and build up the data on the service side.
//
// You can clear a bucket on the service side and thereby free memory by sending
// a size of 0.
struct SetBucketSize {
typedef SetBucketSize ValueType;
static const CommandId kCmdId = kSetBucketSize;
static const cmd::ArgFlags kArgFlags = cmd::kFixed;
void SetHeader() {
header.SetCmd<ValueType>();
}
void Init(uint32 _bucket_id, uint32 _size) {
SetHeader();
bucket_id = _bucket_id;
size = _size;
}
static void* Set(void* cmd, uint32 _bucket_id, uint32 _size) {
static_cast<ValueType*>(cmd)->Init(_bucket_id, _size);
return NextCmdAddress<ValueType>(cmd);
}
CommandHeader header;
uint32 bucket_id;
uint32 size;
};
COMPILE_ASSERT(sizeof(SetBucketSize) == 12, Sizeof_SetBucketSize_is_not_8);
COMPILE_ASSERT(offsetof(SetBucketSize, header) == 0,
Offsetof_SetBucketSize_header_not_0);
COMPILE_ASSERT(offsetof(SetBucketSize, bucket_id) == 4,
Offsetof_SetBucketSize_bucket_id_4);
COMPILE_ASSERT(offsetof(SetBucketSize, size) == 8,
Offsetof_SetBucketSize_size_8);
// Sets the contents of a portion of a bucket on the service side from data in
// shared memory.
// See SetBucketSize.
struct SetBucketData {
typedef SetBucketData ValueType;
static const CommandId kCmdId = kSetBucketData;
static const cmd::ArgFlags kArgFlags = cmd::kFixed;
void SetHeader() {
header.SetCmd<ValueType>();
}
void Init(uint32 _bucket_id,
uint32 _offset,
uint32 _size,
uint32 _shared_memory_id,
uint32 _shared_memory_offset) {
SetHeader();
bucket_id = _bucket_id;
offset = _offset;
size = _size;
shared_memory_id = _shared_memory_id;
shared_memory_offset = _shared_memory_offset;
}
static void* Set(void* cmd,
uint32 _bucket_id,
uint32 _offset,
uint32 _size,
uint32 _shared_memory_id,
uint32 _shared_memory_offset) {
static_cast<ValueType*>(cmd)->Init(
_bucket_id,
_offset,
_size,
_shared_memory_id,
_shared_memory_offset);
return NextCmdAddress<ValueType>(cmd);
}
CommandHeader header;
uint32 bucket_id;
uint32 offset;
uint32 size;
uint32 shared_memory_id;
uint32 shared_memory_offset;
};
COMPILE_ASSERT(sizeof(SetBucketData) == 24, Sizeof_SetBucketData_is_not_24);
COMPILE_ASSERT(offsetof(SetBucketData, header) == 0,
Offsetof_SetBucketData_header_not_0);
COMPILE_ASSERT(offsetof(SetBucketData, bucket_id) == 4,
Offsetof_SetBucketData_bucket_id_not_4);
COMPILE_ASSERT(offsetof(SetBucketData, offset) == 8,
Offsetof_SetBucketData_offset_not_8);
COMPILE_ASSERT(offsetof(SetBucketData, size) == 12,
Offsetof_SetBucketData_size_not_12);
COMPILE_ASSERT(offsetof(SetBucketData, shared_memory_id) == 16,
Offsetof_SetBucketData_shared_memory_id_not_16);
COMPILE_ASSERT(offsetof(SetBucketData, shared_memory_offset) == 20,
Offsetof_SetBucketData_shared_memory_offset_not_20);
// Sets the contents of a portion of a bucket on the service side from data in
// the command buffer.
// See SetBucketSize.
struct SetBucketDataImmediate {
typedef SetBucketDataImmediate ValueType;
static const CommandId kCmdId = kSetBucketDataImmediate;
static const cmd::ArgFlags kArgFlags = cmd::kAtLeastN;
void SetHeader(uint32 size) {
header.SetCmdBySize<ValueType>(size);
}
void Init(uint32 _bucket_id,
uint32 _offset,
uint32 _size) {
SetHeader(_size);
bucket_id = _bucket_id;
offset = _offset;
size = _size;
}
static void* Set(void* cmd,
uint32 _bucket_id,
uint32 _offset,
uint32 _size) {
static_cast<ValueType*>(cmd)->Init(
_bucket_id,
_offset,
_size);
return NextImmediateCmdAddress<ValueType>(cmd, _size);
}
CommandHeader header;
uint32 bucket_id;
uint32 offset;
uint32 size;
};
COMPILE_ASSERT(sizeof(SetBucketDataImmediate) == 16,
Sizeof_SetBucketDataImmediate_is_not_24);
COMPILE_ASSERT(offsetof(SetBucketDataImmediate, header) == 0,
Offsetof_SetBucketDataImmediate_header_not_0);
COMPILE_ASSERT(offsetof(SetBucketDataImmediate, bucket_id) == 4,
Offsetof_SetBucketDataImmediate_bucket_id_not_4);
COMPILE_ASSERT(offsetof(SetBucketDataImmediate, offset) == 8,
Offsetof_SetBucketDataImmediate_offset_not_8);
COMPILE_ASSERT(offsetof(SetBucketDataImmediate, size) == 12,
Offsetof_SetBucketDataImmediate_size_not_12);
// Gets the size of a result the service has available.
// Sending a variable size result back to the client, for example any API that
// returns a string, is problematic since the largest thing you can send back is
// the size of your shared memory. This command along with GetResultData
// implement a way to get a result a piece at a time to help solve that problem
// in a generic way.
struct GetResultSize {
typedef GetResultSize ValueType;
static const CommandId kCmdId = kGetResultSize;
static const cmd::ArgFlags kArgFlags = cmd::kFixed;
void SetHeader() {
header.SetCmd<ValueType>();
}
void Init(uint32 _shared_memory_id,
uint32 _shared_memory_offset) {
SetHeader();
shared_memory_id = _shared_memory_id;
shared_memory_offset = _shared_memory_offset;
}
static void* Set(void* cmd,
uint32 _shared_memory_id,
uint32 _shared_memory_offset) {
static_cast<ValueType*>(cmd)->Init(
_shared_memory_id,
_shared_memory_offset);
return NextCmdAddress<ValueType>(cmd);
}
CommandHeader header;
uint32 shared_memory_id;
uint32 shared_memory_offset;
};
COMPILE_ASSERT(sizeof(GetResultSize) == 12, Sizeof_GetResultSize_is_not_12);
COMPILE_ASSERT(offsetof(GetResultSize, header) == 0,
Offsetof_GetResultSize_header_not_0);
COMPILE_ASSERT(offsetof(GetResultSize, shared_memory_id) == 4,
Offsetof_GetResultSize_shared_memory_id_not_4);
COMPILE_ASSERT(offsetof(GetResultSize, shared_memory_offset) == 8,
Offsetof_GetResultSize_shared_memory_offset_not_8);
// Gets a piece of a result the service as available.
// See GetResultSize.
struct GetResultData {
typedef GetResultData ValueType;
static const CommandId kCmdId = kGetResultData;
static const cmd::ArgFlags kArgFlags = cmd::kFixed;
void SetHeader() {
header.SetCmd<ValueType>();
}
void Init(uint32 _offset,
uint32 _size,
uint32 _shared_memory_id,
uint32 _shared_memory_offset) {
SetHeader();
offset = _offset;
size = _size;
shared_memory_id = _shared_memory_id;
shared_memory_offset = _shared_memory_offset;
}
static void* Set(void* cmd,
uint32 _offset,
uint32 _size,
uint32 _shared_memory_id,
uint32 _shared_memory_offset) {
static_cast<ValueType*>(cmd)->Init(
_offset,
_size,
_shared_memory_id,
_shared_memory_offset);
return NextCmdAddress<ValueType>(cmd);
}
CommandHeader header;
uint32 offset;
uint32 size;
uint32 shared_memory_id;
uint32 shared_memory_offset;
};
COMPILE_ASSERT(sizeof(GetResultData) == 20, Sizeof_GetResultData_is_not_20);
COMPILE_ASSERT(offsetof(GetResultData, header) == 0,
Offsetof_GetResultData_header_not_0);
COMPILE_ASSERT(offsetof(GetResultData, offset) == 4,
Offsetof_GetResultData_offset_not_4);
COMPILE_ASSERT(offsetof(GetResultData, size) == 8,
Offsetof_GetResultData_size_not_8);
COMPILE_ASSERT(offsetof(GetResultData, shared_memory_id) == 12,
Offsetof_GetResultData_shared_memory_id_not_12);
COMPILE_ASSERT(offsetof(GetResultData, shared_memory_offset) == 16,
Offsetof_GetResultData_shared_memory_offset_not_16);
} // namespace cmd
#pragma pack(pop)
} // namespace command_buffer
#endif // GPU_COMMAND_BUFFER_COMMON_CROSS_CMD_BUFFER_COMMON_H_