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
18 namespace gpu {
19
20 namespace 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.
ComputeNumEntries(size_t size_in_bytes)33 inline 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.
RoundSizeToMultipleOfEntries(size_t size_in_bytes)39 inline 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.
44 struct CommandHeader {
45 uint32_t size:21;
46 uint32_t command:11;
47
48 GPU_EXPORT static const int32_t kMaxSize = (1 << 21) - 1;
49
InitCommandHeader50 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>
SetCmdCommandHeader59 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>
SetCmdBySizeCommandHeader66 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>
SetCmdByTotalSizeCommandHeader74 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
81 COMPILE_ASSERT(sizeof(CommandHeader) == 4, Sizeof_CommandHeader_is_not_4);
82
83 // Union that defines possible command buffer entries.
84 union 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
92 const size_t kCommandBufferEntrySize = GPU_COMMAND_BUFFER_ENTRY_ALIGNMENT;
93
94 COMPILE_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.
105 template <typename T>
ImmediateDataAddress(T * cmd)106 void* 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.
113 template <typename T>
114 // Parameters:
115 // cmd: Address of command.
NextCmdAddress(void * cmd)116 void* 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.
126 template <typename T>
NextImmediateCmdAddress(void * cmd,uint32_t size_of_data_in_bytes)127 void* 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.
138 template <typename T>
NextImmediateCmdAddressTotalSize(void * cmd,uint32_t total_size_in_bytes)139 void* 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
147 namespace 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.
166 enum 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
177 COMPILE_ASSERT(kNumCommands - 1 <= kLastCommonId, Too_many_common_commands);
178
179 const char* GetCommandName(CommandId id);
180
181 // A Noop command.
182 struct 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
SetHeaderNoop188 void SetHeader(uint32_t skip_count) {
189 DCHECK_GT(skip_count, 0u);
190 header.Init(kCmdId, skip_count);
191 }
192
InitNoop193 void Init(uint32_t skip_count) {
194 SetHeader(skip_count);
195 }
196
SetNoop197 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
206 COMPILE_ASSERT(sizeof(Noop) == 4, Sizeof_Noop_is_not_4);
207 COMPILE_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.
211 struct 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
SetHeaderSetToken217 void SetHeader() {
218 header.SetCmd<ValueType>();
219 }
220
InitSetToken221 void Init(uint32_t _token) {
222 SetHeader();
223 token = _token;
224 }
SetSetToken225 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
234 COMPILE_ASSERT(sizeof(SetToken) == 8, Sizeof_SetToken_is_not_8);
235 COMPILE_ASSERT(offsetof(SetToken, header) == 0,
236 Offsetof_SetToken_header_not_0);
237 COMPILE_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.
252 struct 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
SetHeaderSetBucketSize258 void SetHeader() {
259 header.SetCmd<ValueType>();
260 }
261
InitSetBucketSize262 void Init(uint32_t _bucket_id, uint32_t _size) {
263 SetHeader();
264 bucket_id = _bucket_id;
265 size = _size;
266 }
SetSetBucketSize267 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
277 COMPILE_ASSERT(sizeof(SetBucketSize) == 12, Sizeof_SetBucketSize_is_not_8);
278 COMPILE_ASSERT(offsetof(SetBucketSize, header) == 0,
279 Offsetof_SetBucketSize_header_not_0);
280 COMPILE_ASSERT(offsetof(SetBucketSize, bucket_id) == 4,
281 Offsetof_SetBucketSize_bucket_id_4);
282 COMPILE_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.
288 struct 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
SetHeaderSetBucketData294 void SetHeader() {
295 header.SetCmd<ValueType>();
296 }
297
InitSetBucketData298 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 }
SetSetBucketData310 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
333 COMPILE_ASSERT(sizeof(SetBucketData) == 24, Sizeof_SetBucketData_is_not_24);
334 COMPILE_ASSERT(offsetof(SetBucketData, header) == 0,
335 Offsetof_SetBucketData_header_not_0);
336 COMPILE_ASSERT(offsetof(SetBucketData, bucket_id) == 4,
337 Offsetof_SetBucketData_bucket_id_not_4);
338 COMPILE_ASSERT(offsetof(SetBucketData, offset) == 8,
339 Offsetof_SetBucketData_offset_not_8);
340 COMPILE_ASSERT(offsetof(SetBucketData, size) == 12,
341 Offsetof_SetBucketData_size_not_12);
342 COMPILE_ASSERT(offsetof(SetBucketData, shared_memory_id) == 16,
343 Offsetof_SetBucketData_shared_memory_id_not_16);
344 COMPILE_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.
350 struct 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
SetHeaderSetBucketDataImmediate356 void SetHeader(uint32_t size) {
357 header.SetCmdBySize<ValueType>(size);
358 }
359
InitSetBucketDataImmediate360 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 }
SetSetBucketDataImmediate368 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
385 COMPILE_ASSERT(sizeof(SetBucketDataImmediate) == 16,
386 Sizeof_SetBucketDataImmediate_is_not_24);
387 COMPILE_ASSERT(offsetof(SetBucketDataImmediate, header) == 0,
388 Offsetof_SetBucketDataImmediate_header_not_0);
389 COMPILE_ASSERT(offsetof(SetBucketDataImmediate, bucket_id) == 4,
390 Offsetof_SetBucketDataImmediate_bucket_id_not_4);
391 COMPILE_ASSERT(offsetof(SetBucketDataImmediate, offset) == 8,
392 Offsetof_SetBucketDataImmediate_offset_not_8);
393 COMPILE_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.
406 struct 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
SetHeaderGetBucketStart414 void SetHeader() {
415 header.SetCmd<ValueType>();
416 }
417
InitGetBucketStart418 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 }
SetGetBucketStart432 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
458 COMPILE_ASSERT(sizeof(GetBucketStart) == 28, Sizeof_GetBucketStart_is_not_28);
459 COMPILE_ASSERT(offsetof(GetBucketStart, header) == 0,
460 Offsetof_GetBucketStart_header_not_0);
461 COMPILE_ASSERT(offsetof(GetBucketStart, bucket_id) == 4,
462 Offsetof_GetBucketStart_bucket_id_not_4);
463 COMPILE_ASSERT(offsetof(GetBucketStart, result_memory_id) == 8,
464 Offsetof_GetBucketStart_result_memory_id_not_8);
465 COMPILE_ASSERT(offsetof(GetBucketStart, result_memory_offset) == 12,
466 Offsetof_GetBucketStart_result_memory_offset_not_12);
467 COMPILE_ASSERT(offsetof(GetBucketStart, data_memory_size) == 16,
468 Offsetof_GetBucketStart_data_memory_size_not_16);
469 COMPILE_ASSERT(offsetof(GetBucketStart, data_memory_id) == 20,
470 Offsetof_GetBucketStart_data_memory_id_not_20);
471 COMPILE_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.
476 struct 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
SetHeaderGetBucketData482 void SetHeader() {
483 header.SetCmd<ValueType>();
484 }
485
InitGetBucketData486 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 }
SetGetBucketData498 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
521 COMPILE_ASSERT(sizeof(GetBucketData) == 24, Sizeof_GetBucketData_is_not_20);
522 COMPILE_ASSERT(offsetof(GetBucketData, header) == 0,
523 Offsetof_GetBucketData_header_not_0);
524 COMPILE_ASSERT(offsetof(GetBucketData, bucket_id) == 4,
525 Offsetof_GetBucketData_bucket_id_not_4);
526 COMPILE_ASSERT(offsetof(GetBucketData, offset) == 8,
527 Offsetof_GetBucketData_offset_not_8);
528 COMPILE_ASSERT(offsetof(GetBucketData, size) == 12,
529 Offsetof_GetBucketData_size_not_12);
530 COMPILE_ASSERT(offsetof(GetBucketData, shared_memory_id) == 16,
531 Offsetof_GetBucketData_shared_memory_id_not_16);
532 COMPILE_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