Divide Framework 0.1
A free and open-source 3D Framework under heavy development
Loading...
Searching...
No Matches
CommandBuffer.cpp
Go to the documentation of this file.
1
2
5
12
14
15namespace Divide::GFX
16{
17
18namespace
19{
20 [[nodiscard]] FORCE_INLINE bool ShouldSkipBatch( const CommandType type ) noexcept
21 {
22 switch ( type )
23 {
27 return true;
28 default: break;
29 }
30 return false;
31 }
32
33 [[nodiscard]] inline bool RemoveEmptyDrawCommands( GenericDrawCommandContainer& commands )
34 {
35 return dvd_erase_if( commands, []( const GenericDrawCommand& cmd ) noexcept { return cmd._drawCount == 0u; } );
36 }
37
38 [[nodiscard]] inline bool EraseEmptyCommands( CommandBuffer::CommandList& commands )
39 {
40 return dvd_erase_if( commands, []( const CommandBase* entry ) noexcept { return entry == nullptr; } );
41 }
42
43 [[nodiscard]] inline bool RemoveEmptyLocks( GFX::MemoryBarrierCommand* memCmd )
44 {
45 bool ret = false;
46 if ( dvd_erase_if( memCmd->_bufferLocks, []( const BufferLock& lock )
47 {
48 return lock._buffer == nullptr || lock._range._length == 0u;
49 } ) )
50 {
51 ret = true;
52 }
53
54 if (dvd_erase_if( memCmd->_textureLayoutChanges, []( const TextureLayoutChange& lock )
55 {
56 return lock._sourceLayout == lock._targetLayout || lock._targetView._srcTexture == nullptr;
57 } ) )
58 {
59 ret = true;
60 }
61
62 return ret;
63 }
64}; //namespace
65
67 {
69 {
70 if (entry._owning)
71 {
73 }
74 }
75
77 }
78
80 {
81 queue._commandBuffers.emplace_back(
83 {
84 ._buffer = commandBuffer,
85 ._owning = false
86 });
87 }
88
90 {
91 queue._commandBuffers.emplace_back(
93 {
94 ._buffer = MOV(commandBuffer),
95 ._owning = true
96 });
97 }
98
100 {
101 clear();
102 }
103
105 {
106 for (CommandBase*& cmd : _commands)
107 {
108 if (cmd != nullptr)
109 {
110 cmd->DeleteCmd( cmd );
111 }
112 }
113
114 efficient_clear( _commands );
115
116 _batched = true;
117 }
118
119 void CommandBuffer::clear( const char* name, const size_t reservedCmdCount )
120 {
121 clear();
122 _name = name;
123 if ( _commands.max_size() < reservedCmdCount )
124 {
125 _commands.reserve( reservedCmdCount );
126 }
127 }
128
130 {
132
133 _commands.reserve( _commands.size() + other._commands.size() );
134
135 for ( CommandBase* cmd : other._commands )
136 {
137 cmd->addToBuffer( this );
138 }
139 _batched = false;
140 }
141
143 {
144 if(other != INVALID_HANDLE<GFX::CommandBuffer> )
145 {
146 GFX::CommandBuffer* buf = Get(other);
147 if (buf != nullptr )
148 {
149 add( *buf );
150 }
151 }
152 }
153
155 {
156 if ( _batched ) [[unlikely]]
157 {
158 return;
159 }
160
162
163 clean();
164
165 CommandBase* prevCommand = nullptr;
166 do
167 {
168 PROFILE_SCOPE( "TRY_MERGE_LOOP", Profiler::Category::Graphics );
169
170 bool tryMerge = true;
171 while ( tryMerge )
172 {
173 tryMerge = false;
174 prevCommand = nullptr;
176 for ( CommandBase*& cmd : _commands )
177 {
178 if ( cmd == nullptr || ShouldSkipBatch( cmd->type() ) )
179 {
180 continue;
181 }
182
183 PROFILE_SCOPE( "TRY_MERGE_LOOP_STEP", Profiler::Category::Graphics );
184
185 if ( prevCommand != nullptr &&
186 prevType == cmd->type() &&
187 TryMergeCommands( cmd->type(), prevCommand, cmd ) )
188 {
189 cmd->DeleteCmd( cmd );
190 tryMerge = true;
191 }
192 else
193 {
194 prevType = cmd->type();
195 prevCommand = cmd;
196 }
197 }
198 }
199 }
200 while ( EraseEmptyCommands( _commands ) );
201
202 // If we don't have any actual work to do, clear everything
203 bool hasWork = false;
204 for ( CommandBase* cmd : _commands )
205 {
206 if ( hasWork )
207 {
208 break;
209 }
210
211 switch ( cmd->type() )
212 {
231 {
232 hasWork = true;
233 } break;
235 {
236 const ReadTextureCommand* crtCmd = cmd->As<ReadTextureCommand>();
237 hasWork = crtCmd->_texture != INVALID_HANDLE<Texture> && crtCmd->_callback;
238 } break;
240 {
241 const CopyTextureCommand* crtCmd = cmd->As<CopyTextureCommand>();
242 hasWork = crtCmd->_source != INVALID_HANDLE<Texture> && crtCmd->_destination != INVALID_HANDLE<Texture>;
243 }break;
245 {
246 const ClearTextureCommand* crtCmd = cmd->As<ClearTextureCommand>();
247 hasWork = crtCmd->_texture != INVALID_HANDLE<Texture>;
248 }break;
250 {
251 const BindPipelineCommand* crtCmd = cmd->As<BindPipelineCommand>();
252 hasWork = crtCmd->_pipeline != nullptr && crtCmd->_pipeline->stateHash() != 0u;
253 }break;
254 default: break;
255 };
256 }
257
258 if ( hasWork )
259 {
260 const auto [error, lastCmdIndex] = validate();
261 if ( error != GFX::ErrorType::NONE )
262 {
263 Console::errorfn( LOCALE_STR( "ERROR_GFX_INVALID_COMMAND_BUFFER" ), lastCmdIndex, toString().c_str() );
264 DIVIDE_UNEXPECTED_CALL_MSG( Util::StringFormat( "GFX::CommandBuffer::batch error [ {} ]: Invalid command buffer. Check error log!", GFX::Names::errorType[to_base( error )] ).c_str() );
265 }
266 }
267 else
268 {
269 clear();
270 }
271
272 _batched = true;
273 }
274
276 {
277 if ( _commands.empty() ) [[unlikely]]
278 {
279 return;
280 }
281
283 while( cleanInternal() )
284 {
286 }
287 }
288
290 {
291 const Pipeline* prevPipeline = nullptr;
292 const Rect<I32>* prevScissorRect = nullptr;
293 const Rect<I32>* prevViewportRect = nullptr;
294 const DescriptorSet* prevDescriptorSet = nullptr;
295
296 bool ret = false;
297
298 for ( CommandBase*& cmd : _commands )
299 {
300 bool erase = false;
301
302 switch ( cmd->type() )
303 {
305 {
306 PROFILE_SCOPE( "Clean Draw Commands", Profiler::Category::Graphics );
307
308 GenericDrawCommandContainer& cmds = cmd->As<DrawCommand>()->_drawCommands;
309 if ( cmds.size() == 1 )
310 {
311 erase = cmds.begin()->_drawCount == 0u;
312 }
313 else
314 {
315 erase = RemoveEmptyDrawCommands( cmds ) && cmds.empty();
316 }
317 } break;
319 {
320 PROFILE_SCOPE( "Clean Pipelines", Profiler::Category::Graphics );
321
322 const Pipeline* pipeline = cmd->As<BindPipelineCommand>()->_pipeline;
323 // If the current pipeline is identical to the previous one, remove it
324 if ( prevPipeline == nullptr || prevPipeline->stateHash() != pipeline->stateHash() )
325 {
326 prevPipeline = pipeline;
327 }
328 else
329 {
330 erase = true;
331 }
332 }break;
334 {
335 PROFILE_SCOPE( "Clean Push Constants", Profiler::Category::Graphics );
336
337 SendPushConstantsCommand* sendPushConstantsCommand = cmd->As<SendPushConstantsCommand>();
338 if ( !sendPushConstantsCommand->_fastData.set() &&
339 (sendPushConstantsCommand->_uniformData == nullptr || sendPushConstantsCommand->_uniformData->entries().empty()))
340 {
341 erase = true;
342 }
343 }break;
345 {
346 PROFILE_SCOPE( "Clean Descriptor Sets", Profiler::Category::Graphics );
347
348 auto bindCmd = cmd->As<BindShaderResourcesCommand>();
349 if ( bindCmd->_usage != DescriptorSetUsage::COUNT && (prevDescriptorSet == nullptr || *prevDescriptorSet != bindCmd->_set) )
350 {
351 prevDescriptorSet = &bindCmd->_set;
352 }
353 else
354 {
355 erase = true;
356 }
357 }break;
359 {
360 auto memCmd = cmd->As<MemoryBarrierCommand>();
361 if (RemoveEmptyLocks( memCmd ))
362 {
363 erase = IsEmpty( memCmd->_bufferLocks ) &&
364 IsEmpty( memCmd->_textureLayoutChanges);
365 }
366 } break;
368 {
370
371 const Rect<I32>& scissorRect = cmd->As<SetScissorCommand>()->_rect;
372 if ( prevScissorRect == nullptr || *prevScissorRect != scissorRect )
373 {
374 prevScissorRect = &scissorRect;
375 }
376 else
377 {
378 erase = true;
379 }
380 } break;
382 {
383 auto readCmd = cmd->As<ReadBufferDataCommand>();
384 erase = readCmd->_buffer == nullptr ||
385 readCmd->_target.first == nullptr ||
386 readCmd->_elementCount == 0u ||
387 readCmd->_target.second == 0u;
388 } break;
390 {
391 PROFILE_SCOPE( "Clean Viewport", Profiler::Category::Graphics );
392
393 const Rect<I32>& viewportRect = cmd->As<SetViewportCommand>()->_viewport;
394
395 if ( prevViewportRect == nullptr || *prevViewportRect != viewportRect )
396 {
397 prevViewportRect = &viewportRect;
398 }
399 else
400 {
401 erase = true;
402 }
403 } break;
404 default: break;
405 };
406
407 if ( erase )
408 {
409 cmd->DeleteCmd(cmd);
410 ret = true;
411 }
412 }
413
414 {
415 PROFILE_SCOPE( "Remove redundant Pipelines", Profiler::Category::Graphics );
416
417 // Remove redundant pipeline changes
418 CommandBase* prev = nullptr;
419 for ( CommandBase* cmd : _commands )
420 {
421 if ( (cmd != nullptr && cmd->type() == CommandType::BIND_PIPELINE) && // current command is a bind pipeline request
422 (prev != nullptr && prev->type() == CommandType::BIND_PIPELINE)) // previous command was also a bind pipeline request
423 {
424 prev->DeleteCmd(prev); //Remove the previous bind pipeline request as it's redundant
425 ret = true;
426 }
427
428 prev = cmd;
429 }
430 }
431
432 {
433 PROFILE_SCOPE( "Remove invalid commands", Profiler::Category::Graphics );
434 if (EraseEmptyCommands( _commands ))
435 {
436 ret = true;
437 }
438 }
439
440 return ret;
441 }
442
443 // New use cases that emerge from production work should be checked here.
444 std::pair<ErrorType, size_t> CommandBuffer::validate() const
445 {
446 if constexpr( !Config::ENABLE_GPU_VALIDATION )
447 {
448 return { ErrorType::NONE, 0u };
449 }
450
452
453 size_t cmdIndex = 0u;
454 bool pushedPass = false, pushedQuery = false, hasPipeline = false;
455
456 I32 pushedDebugScope = 0, pushedCamera = 0, pushedViewport = 0;
457
458 for ( CommandBase* cmd : _commands )
459 {
460 cmdIndex++;
461 switch ( cmd->type() )
462 {
464 {
465 if ( pushedPass )
466 {
467 return { ErrorType::MISSING_END_RENDER_PASS, cmdIndex };
468 }
469 pushedPass = true;
470
471 auto beginRenderPassCmd = cmd->As<GFX::BeginRenderPassCommand>();
472 for ( const auto& it : beginRenderPassCmd->_descriptor._writeLayers )
473 {
474 if ( it._layer == INVALID_INDEX )
475 {
476 return { ErrorType::INVALID_BEGIN_RENDER_PASS, cmdIndex };
477 }
478 }
479 } break;
481 {
482 if ( !pushedPass )
483 {
484 return { ErrorType::MISSING_BEGIN_RENDER_PASS, cmdIndex };
485 }
486 pushedPass = false;
487 } break;
489 {
490 if ( pushedQuery )
491 {
492 return { ErrorType::MISSING_END_GPU_QUERY, cmdIndex };
493 }
494 pushedQuery = true;
495 } break;
497 {
498 if ( !pushedQuery )
499 {
500 return { ErrorType::MISSING_BEGIN_GPU_QUERY, cmdIndex };
501 }
502 pushedQuery = false;
503 } break;
505 {
506 ++pushedDebugScope;
507 } break;
509 {
510 if ( pushedDebugScope == 0 )
511 {
512 return { ErrorType::MISSING_PUSH_DEBUG_SCOPE, cmdIndex };
513 }
514 --pushedDebugScope;
515 } break;
517 {
518 ++pushedCamera;
519 }break;
521 {
522 --pushedCamera;
523 }break;
525 {
526 ++pushedViewport;
527 }break;
529 {
530 --pushedViewport;
531 }break;
533 {
534 if ( !pushedPass )
535 {
536 if ( cmd->As<GFX::BindPipelineCommand>()->_pipeline->descriptor()._primitiveTopology != PrimitiveTopology::COMPUTE)
537 {
539 }
540 }
541 hasPipeline = true;
542 } break;
544 {
545 if ( !hasPipeline )
546 {
547 return { ErrorType::MISSING_VALID_PIPELINE, cmdIndex };
548 }
549 const vec3<U32>& workGroupCount = cmd->As<GFX::DispatchComputeCommand>()->_computeGroupSize;
550 if ( !(workGroupCount.x > 0 &&
554 {
555 return { ErrorType::INVALID_DISPATCH_COUNT, cmdIndex };
556 }
557
558 } break;
560 {
561 if ( !hasPipeline )
562 {
563 return { ErrorType::MISSING_VALID_PIPELINE, cmdIndex };
564 }
565 }break;
567 {
569 if ( resCmd->_usage == DescriptorSetUsage::COUNT )
570 {
571 return { ErrorType::INVALID_DESCRIPTOR_SET, cmdIndex };
572 }
573 for ( U8 i = 0u; i < resCmd->_set._bindingCount; ++i )
574 {
575 const DescriptorSetBinding& binding = resCmd->_set._bindings[i];
577 {
578 return { ErrorType::INVALID_DESCRIPTOR_SET, cmdIndex };
579 }
580 }
581 }break;
582 default:
583 {
584 // no requirements yet
585 }break;
586 };
587 }
588
589 if ( pushedPass )
590 {
591 return { ErrorType::MISSING_END_RENDER_PASS, cmdIndex };
592 }
593 if ( pushedDebugScope != 0 )
594 {
595 return { ErrorType::MISSING_POP_DEBUG_SCOPE, cmdIndex };
596 }
597 if ( pushedCamera != 0 )
598 {
599 return { ErrorType::MISSING_POP_CAMERA, cmdIndex };
600 }
601 if ( pushedViewport != 0 )
602 {
603 return { ErrorType::MISSING_POP_VIEWPORT, cmdIndex };
604 }
605
606 return { ErrorType::NONE, cmdIndex };
607 }
608
609 void ToString( const CommandBase& cmd, const CommandType type, I32& crtIndent, string& out )
610 {
611 const auto append = []( string& target, const string& text, const I32 indent )
612 {
613 for ( I32 i = 0; i < indent; ++i )
614 {
615 target.append( " " );
616 }
617 target.append( text );
618 };
619
620 switch ( type )
621 {
624 {
625 append( out, GFX::ToString( cmd, type, to_U16( crtIndent ) ), crtIndent );
626 ++crtIndent;
627 }break;
630 {
631 --crtIndent;
632 append( out, GFX::ToString( cmd, type, to_U16( crtIndent ) ), crtIndent );
633 }break;
634 default:
635 {
636 append( out, GFX::ToString( cmd, type, to_U16( crtIndent ) ), crtIndent );
637 }break;
638 }
639 }
640
642 {
643 I32 crtIndent = 0;
644 string out = "\n\n\n\n";
645 size_t idx = 0u;
646 for ( CommandBase* cmd : _commands )
647 {
648 out.append( "[ " + std::to_string( idx++ ) + " ]: " );
649 ToString( *cmd, cmd->type(), crtIndent, out );
650 out.append( "\n" );
651 }
652 out.append( "\n\n\n\n" );
653
654 return out;
655 }
656
657 bool BatchDrawCommands( GenericDrawCommand& previousGDC, GenericDrawCommand& currentGDC ) noexcept
658 {
659 // Instancing is not compatible with MDI. Well, it might be, but I can't be bothered a.t.m. to implement it -Ionut
660 if ( previousGDC._cmd.instanceCount != currentGDC._cmd.instanceCount && (previousGDC._cmd.instanceCount > 1 || currentGDC._cmd.instanceCount > 1) )
661 {
662 return false;
663 }
664
665 // Batch-able commands must share the same buffer and other various state
666 if ( !Compatible( previousGDC, currentGDC ) )
667 {
668 return false;
669 }
670 const U32 offsetCrt = to_U32( currentGDC._commandOffset );
671 const U32 offsetPrev = to_U32( previousGDC._commandOffset );
672 if ( offsetCrt - offsetPrev == previousGDC._drawCount )
673 {
674 // If the rendering commands are batch-able, increase the draw count for the previous one
675 previousGDC._drawCount += currentGDC._drawCount;
676 // And set the current command's draw count to zero so it gets removed from the list later on
677 currentGDC._drawCount = 0;
678 return true;
679 }
680
681 return false;
682 }
683
684 bool Merge( DrawCommand* prevCommand, DrawCommand* crtCommand )
685 {
687
688 auto& commands = prevCommand->_drawCommands;
689 commands.insert( cend( commands ),
690 eastl::make_move_iterator( begin( crtCommand->_drawCommands ) ),
691 eastl::make_move_iterator( end( crtCommand->_drawCommands ) ) );
692 efficient_clear( crtCommand->_drawCommands );
693
694 {
695 PROFILE_SCOPE( "Merge by offset", Profiler::Category::Graphics );
696 eastl::sort( begin( commands ),
697 end( commands ),
698 []( const GenericDrawCommand& a, const GenericDrawCommand& b ) noexcept -> bool
699 {
700 return a._commandOffset < b._commandOffset;
701 } );
702
703 do
704 {
705 const size_t commandCount = commands.size();
706 for ( size_t previousCommandIndex = 0, currentCommandIndex = 1;
707 currentCommandIndex < commandCount;
708 ++currentCommandIndex )
709 {
710 if ( !BatchDrawCommands( commands[previousCommandIndex], commands[currentCommandIndex] ) )
711 {
712 previousCommandIndex = currentCommandIndex;
713 }
714 }
715 }
716 while ( RemoveEmptyDrawCommands( commands ) );
717 }
718
719 return true;
720 }
721
723 {
724 for ( const BufferLock& otherLock : rhs->_bufferLocks )
725 {
726 bool found = false;
727 for ( BufferLock& ourLock : lhs->_bufferLocks )
728 {
729 if ( ourLock._buffer &&
730 otherLock._buffer &&
731 ourLock._buffer->getGUID() == otherLock._buffer->getGUID() )
732 {
733 found = true;
734 }
735
736 if ( found)
737 {
738 if ( ourLock._type == otherLock._type )
739 {
740 Merge( ourLock._range, otherLock._range );
741 break;
742 }
743 else
744 {
745 found = false;
746 }
747 }
748 }
749 if ( !found )
750 {
751 lhs->_bufferLocks.push_back( otherLock );
752 }
753 }
754
755 for ( const TextureLayoutChange& otherChange : rhs->_textureLayoutChanges )
756 {
757 bool found = false;
758 for ( TextureLayoutChange& ourChange : lhs->_textureLayoutChanges )
759 {
760 if ( ourChange._targetView == otherChange._targetView)
761 {
762 found = true;
763 ourChange._targetLayout = otherChange._targetLayout;
764 break;
765 }
766 }
767 if ( !found )
768 {
769 lhs->_textureLayoutChanges.push_back(otherChange);
770 }
771 }
772
773 return true;
774 }
775
777 {
778 if ( lhs->_fastData.set() )
779 {
780 if ( rhs->_fastData.set() && lhs->_fastData != rhs->_fastData)
781 {
782 return false;
783 }
784 }
785 else if ( rhs->_fastData.set() )
786 {
787 lhs->_fastData = rhs->_fastData;
788 }
789
790 bool partial = false;
791
792 UniformData* lhsUniforms = static_cast<SendPushConstantsCommand*>(lhs)->_uniformData;
793 UniformData* rhsUniforms = static_cast<SendPushConstantsCommand*>(rhs)->_uniformData;
794 if ( lhsUniforms == nullptr )
795 {
796 lhsUniforms = rhsUniforms;
797 return true;
798 }
799 else if ( rhsUniforms == nullptr )
800 {
801 return true;
802 }
803
804 return Merge(*lhsUniforms, *rhsUniforms, partial);
805 }
806
807}; //namespace Divide::GFX
#define LOCALE_STR(X)
Definition: Localization.h:91
#define MOV(...)
#define DIVIDE_UNEXPECTED_CALL_MSG(X)
#define FORCE_INLINE
#define PROFILE_SCOPE_AUTO(CATEGORY)
Definition: Profiler.h:87
#define PROFILE_SCOPE(NAME, CATEGORY)
Definition: Profiler.h:86
eastl::fixed_vector< CommandBase *, COMMAND_BUFFER_INIT_SIZE, true > CommandList
string toString() const
Multi-line. indented list of all commands (and params for some of them)
std::pair< ErrorType, size_t > validate() const
Verify that the commands in the buffer are valid and in the right order.
static const DeviceInformation & GetDeviceInformation() noexcept
Definition: GFXDevice.inl:158
FORCE_INLINE I64 getGUID() const noexcept
Definition: GUIDWrapper.h:51
constexpr bool ENABLE_GPU_VALIDATION
Error callbacks, validations, buffer checks, etc. are controlled by this flag. Heavy performance impa...
Definition: config.h:192
static const char * errorType[]
Definition: CommandBuffer.h:63
bool EraseEmptyCommands(CommandBuffer::CommandList &commands)
bool RemoveEmptyDrawCommands(GenericDrawCommandContainer &commands)
FORCE_INLINE bool ShouldSkipBatch(const CommandType type) noexcept
bool RemoveEmptyLocks(GFX::MemoryBarrierCommand *memCmd)
void ToString(const CommandBase &cmd, const CommandType type, I32 &crtIndent, string &out)
CommandBuffer * Get(Handle< CommandBuffer > handle)
void AddCommandBufferToQueue(CommandBufferQueue &queue, const Handle< GFX::CommandBuffer > &commandBuffer)
void ResetCommandBufferQueue(CommandBufferQueue &queue)
bool BatchDrawCommands(GenericDrawCommand &previousGDC, GenericDrawCommand &currentGDC) noexcept
bool Merge(DrawCommand *prevCommand, DrawCommand *crtCommand)
void DeallocateCommandBuffer(Handle< CommandBuffer > &buffer)
bool TryMergeCommands(CommandType type, T *prevCommand, T *crtCommand)
constexpr Optick::Category::Type Graphics
Definition: Profiler.h:60
Str StringFormat(const char *fmt, Args &&...args)
constexpr U8 INVALID_INDEX
constexpr U32 to_U32(const T value)
constexpr U16 to_U16(const T value)
int32_t I32
uint8_t U8
void efficient_clear(eastl::fixed_vector< T, nodeCount, bEnableOverflow, OverflowAllocator > &fixed_vector)
Definition: Vector.h:52
bool IsEmpty(const BufferLocks &locks) noexcept
Definition: BufferLocks.cpp:8
eastl::fixed_vector< GenericDrawCommand, 1, true > GenericDrawCommandContainer
bool Compatible(const GenericDrawCommand &lhs, const GenericDrawCommand &rhs) noexcept
bool dvd_erase_if(eastl::vector< T, A > &vec, Predicate &&pred)
Definition: Vector.h:109
uint32_t U32
Project const SceneEntry & entry
Definition: DefaultScene.h:41
constexpr auto to_base(const Type value) -> Type
BufferRange _range
Definition: BufferLocks.h:57
BufferSyncUsage _type
Definition: BufferLocks.h:58
LockableBuffer * _buffer
Definition: BufferLocks.h:60
static NO_INLINE void errorfn(const char *format, T &&... args)
std::array< DescriptorSetBinding, MAX_BINDINGS_PER_DESCRIPTOR_SET > _bindings
virtual void addToBuffer(CommandBuffer *buffer) const =0
FORCE_INLINE T * As()
Definition: Commands.h:60
virtual void DeleteCmd(CommandBase *&cmd) const =0
Definition: CommandBuffer.h:97
Handle< GFX::CommandBuffer > _buffer
Definition: CommandBuffer.h:98
eastl::fixed_vector< Entry, COMMAND_BUFFER_INIT_SIZE, true > _commandBuffers
Handle< Texture > _destination
Definition: Commands.inl:122
GenericDrawCommandContainer _drawCommands
Definition: Commands.inl:81
TextureLayoutChanges _textureLayoutChanges
Definition: Commands.inl:191
Handle< Texture > _texture
Definition: Commands.inl:129
DELEGATE_STD< void, const ImageReadbackData & > _callback
Definition: Commands.inl:132
bool set() const noexcept
Definition: PushConstants.h:44
ImageUsage _targetLayout
Definition: Texture.h:74
const UniformDataContainer & entries() const noexcept