Added optimized support for VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT when MVK_CONFIG_PREFILL_METAL_COMMAND_BUFFERS is used
diff --git a/MoltenVK/MoltenVK/Commands/MVKCommandBuffer.h b/MoltenVK/MoltenVK/Commands/MVKCommandBuffer.h
index 7a38e67..9ad5e61 100644
--- a/MoltenVK/MoltenVK/Commands/MVKCommandBuffer.h
+++ b/MoltenVK/MoltenVK/Commands/MVKCommandBuffer.h
@@ -166,7 +166,9 @@
bool canPrefill();
void prefill();
void clearPrefilledMTLCommandBuffer();
- void releaseCommands();
+ void releaseCommands(MVKCommand* command);
+ void releaseRecordedCommands();
+ void flushImmediateCmdEncoder();
MVKCommand* _head = nullptr;
MVKCommand* _tail = nullptr;
@@ -175,6 +177,8 @@
std::atomic_flag _isExecutingNonConcurrently;
VkCommandBufferInheritanceInfo _secondaryInheritanceInfo;
id<MTLCommandBuffer> _prefilledMTLCmdBuffer = nil;
+ MVKCommandEncodingContext* _immediateCmdEncodingContext = nullptr;
+ MVKCommandEncoder* _immediateCmdEncoder = nullptr;
bool _isSecondary;
bool _doesContinueRenderPass;
bool _canAcceptCommands;
@@ -274,6 +278,10 @@
/** Encode commands from the command buffer onto the Metal command buffer. */
void encode(id<MTLCommandBuffer> mtlCmdBuff, MVKCommandEncodingContext* pEncodingContext);
+
+ void beginEncoding(id<MTLCommandBuffer> mtlCmdBuff, MVKCommandEncodingContext* pEncodingContext);
+ void encodeCommands(MVKCommand* command);
+ void endEncoding();
/** Encode commands from the specified secondary command buffer onto the Metal command buffer. */
void encodeSecondary(MVKCommandBuffer* secondaryCmdBuffer);
diff --git a/MoltenVK/MoltenVK/Commands/MVKCommandBuffer.mm b/MoltenVK/MoltenVK/Commands/MVKCommandBuffer.mm
index ac7124b..574cb98 100644
--- a/MoltenVK/MoltenVK/Commands/MVKCommandBuffer.mm
+++ b/MoltenVK/MoltenVK/Commands/MVKCommandBuffer.mm
@@ -50,24 +50,51 @@
const VkCommandBufferInheritanceInfo* pInheritInfo = (_isSecondary ? pBeginInfo->pInheritanceInfo : NULL);
bool hasInheritInfo = mvkSetOrClear(&_secondaryInheritanceInfo, pInheritInfo);
_doesContinueRenderPass = mvkAreAllFlagsEnabled(usage, VK_COMMAND_BUFFER_USAGE_RENDER_PASS_CONTINUE_BIT) && hasInheritInfo;
-
- return getConfigurationResult();
+
+ if(canPrefill()) {
+ @autoreleasepool {
+ uint32_t qIdx = 0;
+ _prefilledMTLCmdBuffer = _commandPool->newMTLCommandBuffer(qIdx); // retain
+
+ _immediateCmdEncodingContext = new MVKCommandEncodingContext;
+
+ _immediateCmdEncoder = new MVKCommandEncoder(this);
+ _immediateCmdEncoder->beginEncoding(_prefilledMTLCmdBuffer, _immediateCmdEncodingContext);
+ }
+ }
+
+ return getConfigurationResult();
}
-void MVKCommandBuffer::releaseCommands() {
- MVKCommand* cmd = _head;
- while (cmd) {
- MVKCommand* nextCmd = cmd->_next; // Establish next before returning current to pool.
- (cmd->getTypePool(getCommandPool()))->returnObject(cmd);
- cmd = nextCmd;
- }
+void MVKCommandBuffer::releaseCommands(MVKCommand* command) {
+ while(command) {
+ MVKCommand* nextCommand = command->_next; // Establish next before returning current to pool.
+ (command->getTypePool(getCommandPool()))->returnObject(command);
+ command = nextCommand;
+ }
+}
+
+void MVKCommandBuffer::releaseRecordedCommands() {
+ releaseCommands(_head);
_head = nullptr;
_tail = nullptr;
}
+void MVKCommandBuffer::flushImmediateCmdEncoder() {
+ if(_immediateCmdEncoder) {
+ _immediateCmdEncoder->endEncoding();
+ delete _immediateCmdEncoder;
+ _immediateCmdEncoder = nullptr;
+
+ delete _immediateCmdEncodingContext;
+ _immediateCmdEncodingContext = nullptr;
+ }
+}
+
VkResult MVKCommandBuffer::reset(VkCommandBufferResetFlags flags) {
+ flushImmediateCmdEncoder();
clearPrefilledMTLCommandBuffer();
- releaseCommands();
+ releaseRecordedCommands();
_doesContinueRenderPass = false;
_canAcceptCommands = false;
_isReusable = false;
@@ -89,15 +116,26 @@
VkResult MVKCommandBuffer::end() {
_canAcceptCommands = false;
- prefill();
+
+ flushImmediateCmdEncoder();
+
return getConfigurationResult();
}
void MVKCommandBuffer::addCommand(MVKCommand* command) {
- if ( !_canAcceptCommands ) {
- setConfigurationResult(reportError(VK_NOT_READY, "Command buffer cannot accept commands before vkBeginCommandBuffer() is called."));
- return;
- }
+ if ( !_canAcceptCommands ) {
+ setConfigurationResult(reportError(VK_NOT_READY, "Command buffer cannot accept commands before vkBeginCommandBuffer() is called."));
+ return;
+ }
+
+ if(_immediateCmdEncoder) {
+ _immediateCmdEncoder->encodeCommands(command);
+
+ if( !_isReusable ) {
+ releaseCommands(command);
+ return;
+ }
+ }
if (_tail) { _tail->_next = command; }
command->_next = nullptr;
@@ -141,27 +179,6 @@
return true;
}
-// If we can, prefill a MTLCommandBuffer with the commands in this command buffer.
-// Wrap in autorelease pool to capture autoreleased Metal encoding activity.
-void MVKCommandBuffer::prefill() {
- @autoreleasepool {
- clearPrefilledMTLCommandBuffer();
-
- if ( !canPrefill() ) { return; }
-
- uint32_t qIdx = 0;
- _prefilledMTLCmdBuffer = _commandPool->newMTLCommandBuffer(qIdx); // retain
-
- MVKCommandEncodingContext encodingContext;
- MVKCommandEncoder encoder(this);
- encoder.encode(_prefilledMTLCmdBuffer, &encodingContext);
-
- // Once encoded onto Metal, if this command buffer is not reusable, we don't need the
- // MVKCommand instances anymore, so release them in order to reduce memory pressure.
- if ( !_isReusable ) { releaseCommands(); }
- }
-}
-
bool MVKCommandBuffer::canPrefill() {
bool wantPrefill = _device->shouldPrefillMTLCommandBuffers();
return wantPrefill && !(_isSecondary || _supportsConcurrentExecution);
@@ -251,33 +268,44 @@
void MVKCommandEncoder::encode(id<MTLCommandBuffer> mtlCmdBuff,
MVKCommandEncodingContext* pEncodingContext) {
- _framebuffer = nullptr;
- _renderPass = nullptr;
- _subpassContents = VK_SUBPASS_CONTENTS_INLINE;
- _renderSubpassIndex = 0;
- _multiviewPassIndex = 0;
- _canUseLayeredRendering = false;
+ beginEncoding(mtlCmdBuff, pEncodingContext);
+ encodeCommands(_cmdBuffer->_head);
+ endEncoding();
+}
- _pEncodingContext = pEncodingContext;
- _mtlCmdBuffer = mtlCmdBuff; // not retained
+void MVKCommandEncoder::beginEncoding(id<MTLCommandBuffer> mtlCmdBuff, MVKCommandEncodingContext* pEncodingContext) {
+ _framebuffer = nullptr;
+ _renderPass = nullptr;
+ _subpassContents = VK_SUBPASS_CONTENTS_INLINE;
+ _renderSubpassIndex = 0;
+ _multiviewPassIndex = 0;
+ _canUseLayeredRendering = false;
- setLabelIfNotNil(_mtlCmdBuffer, _cmdBuffer->_debugName);
+ _pEncodingContext = pEncodingContext;
+ _mtlCmdBuffer = mtlCmdBuff; // not retained
- MVKCommand* cmd = _cmdBuffer->_head;
- while (cmd) {
- uint32_t prevMVPassIdx = _multiviewPassIndex;
- cmd->encode(this);
- if (_multiviewPassIndex > prevMVPassIdx) {
- // This means we're in a multiview render pass, and we moved on to the
- // next view group. Re-encode all commands in the subpass again for this group.
- cmd = _lastMultiviewPassCmd->_next;
- } else {
- cmd = cmd->_next;
- }
- }
+ setLabelIfNotNil(_mtlCmdBuffer, _cmdBuffer->_debugName);
+}
- endCurrentMetalEncoding();
- finishQueries();
+void MVKCommandEncoder::encodeCommands(MVKCommand* command) {
+ while(command) {
+ uint32_t prevMVPassIdx = _multiviewPassIndex;
+ command->encode(this);
+
+ if(_multiviewPassIndex > prevMVPassIdx) {
+ // This means we're in a multiview render pass, and we moved on to the
+ // next view group. Re-encode all commands in the subpass again for this group.
+
+ command = _lastMultiviewPassCmd->_next;
+ } else {
+ command = command->_next;
+ }
+ }
+}
+
+void MVKCommandEncoder::endEncoding() {
+ endCurrentMetalEncoding();
+ finishQueries();
}
void MVKCommandEncoder::encodeSecondary(MVKCommandBuffer* secondaryCmdBuffer) {