Fix error on some Apple GPU's where a vkCmdTimestampQuery() after a

renderpass was writing timestamp before renderpass activity was complete.

MVKCommandBuffer tracks whether it contains a stage-based timestamp command,
and MVKCommandEncoder updates the timestamp command fence when ending any
Metal command encoder on such a MVKCommandBuffer.

MVKCommandEncoder reorder member variables to avoid layout gaps (unrelated).
MVKCommandBuffer update _commandCount even for single-use immediate command
encoding (unrelated).
This commit is contained in:
Bill Hollings 2022-05-10 14:51:55 -04:00
parent b8f0828da7
commit e2168a33db
4 changed files with 35 additions and 24 deletions

View File

@ -25,9 +25,11 @@ Released TBD
- `VK_KHR_dynamic_rendering` - `VK_KHR_dynamic_rendering`
- `VK_KHR_separate_depth_stencil_layouts` - `VK_KHR_separate_depth_stencil_layouts`
- `VK_EXT_separate_stencil_usage` - `VK_EXT_separate_stencil_usage`
- Support attachment clearing when some clearing formats are not specified.
- Fix error where previously bound push constants can override a descriptor buffer binding - Fix error where previously bound push constants can override a descriptor buffer binding
used by a subsequent pipeline that does not use push constants. used by a subsequent pipeline that does not use push constants.
- Support attachment clearing when some clearing formats are not specified. - Fix error on some Apple GPU's where a `vkCmdTimestampQuery()` after a renderpass was
writing timestamp before renderpass activity was complete.
- Update to latest SPIRV-Cross: - Update to latest SPIRV-Cross:
- MSL: Emit interface block members of array length 1 as arrays instead of scalars. - MSL: Emit interface block members of array length 1 as arrays instead of scalars.

View File

@ -85,6 +85,8 @@ VkResult MVKCmdWriteTimestamp::setContent(MVKCommandBuffer* cmdBuff,
_pipelineStage = pipelineStage; _pipelineStage = pipelineStage;
cmdBuff->recordTimestampCommand();
return rslt; return rslt;
} }

View File

@ -118,6 +118,10 @@ public:
/** Called when a MVKCmdExecuteCommands is added to this command buffer. */ /** Called when a MVKCmdExecuteCommands is added to this command buffer. */
void recordExecuteCommands(const MVKArrayRef<MVKCommandBuffer*> secondaryCommandBuffers); void recordExecuteCommands(const MVKArrayRef<MVKCommandBuffer*> secondaryCommandBuffers);
/** Called when a timestamp command is added. */
void recordTimestampCommand();
#pragma mark Tessellation constituent command management #pragma mark Tessellation constituent command management
/** Update the last recorded pipeline with tessellation shaders */ /** Update the last recorded pipeline with tessellation shaders */
@ -197,6 +201,7 @@ protected:
bool _isReusable; bool _isReusable;
bool _supportsConcurrentExecution; bool _supportsConcurrentExecution;
bool _wasExecuted; bool _wasExecuted;
bool _hasStageCounterTimestampCommand;
}; };
@ -461,7 +466,6 @@ protected:
NSString* getMTLRenderCommandEncoderName(MVKCommandUse cmdUse); NSString* getMTLRenderCommandEncoderName(MVKCommandUse cmdUse);
void encodeGPUCounterSample(MVKGPUCounterQueryPool* mvkQryPool, uint32_t sampleIndex, MVKCounterSamplingFlags samplingPoints); void encodeGPUCounterSample(MVKGPUCounterQueryPool* mvkQryPool, uint32_t sampleIndex, MVKCounterSamplingFlags samplingPoints);
void encodeTimestampStageCounterSamples(); void encodeTimestampStageCounterSamples();
bool hasTimestampStageCounterQueries() { return !_timestampStageCounterQueries.empty(); }
id<MTLFence> getStageCountersMTLFence(); id<MTLFence> getStageCountersMTLFence();
MVKArrayRef<MTLSamplePosition> getCustomSamplePositions(); MVKArrayRef<MTLSamplePosition> getCustomSamplePositions();
@ -470,11 +474,8 @@ protected:
uint32_t query = 0; uint32_t query = 0;
} GPUCounterQuery; } GPUCounterQuery;
VkSubpassContents _subpassContents;
MVKCommand* _lastMultiviewPassCmd;
uint32_t _renderSubpassIndex;
uint32_t _multiviewPassIndex;
VkRect2D _renderArea; VkRect2D _renderArea;
MVKCommand* _lastMultiviewPassCmd;
MVKActivatedQueries* _pActivatedQueries; MVKActivatedQueries* _pActivatedQueries;
MVKSmallVector<GPUCounterQuery, 16> _timestampStageCounterQueries; MVKSmallVector<GPUCounterQuery, 16> _timestampStageCounterQueries;
MVKSmallVector<VkClearValue, kMVKDefaultAttachmentCount> _clearValues; MVKSmallVector<VkClearValue, kMVKDefaultAttachmentCount> _clearValues;
@ -482,16 +483,19 @@ protected:
MVKSmallVector<MTLSamplePosition> _dynamicSamplePositions; MVKSmallVector<MTLSamplePosition> _dynamicSamplePositions;
MVKSmallVector<MVKSmallVector<MTLSamplePosition>> _subpassSamplePositions; MVKSmallVector<MVKSmallVector<MTLSamplePosition>> _subpassSamplePositions;
id<MTLComputeCommandEncoder> _mtlComputeEncoder; id<MTLComputeCommandEncoder> _mtlComputeEncoder;
MVKCommandUse _mtlComputeEncoderUse;
id<MTLBlitCommandEncoder> _mtlBlitEncoder; id<MTLBlitCommandEncoder> _mtlBlitEncoder;
id<MTLFence> _stageCountersMTLFence; id<MTLFence> _stageCountersMTLFence;
MVKCommandUse _mtlBlitEncoderUse;
MVKPushConstantsCommandEncoderState _vertexPushConstants; MVKPushConstantsCommandEncoderState _vertexPushConstants;
MVKPushConstantsCommandEncoderState _tessCtlPushConstants; MVKPushConstantsCommandEncoderState _tessCtlPushConstants;
MVKPushConstantsCommandEncoderState _tessEvalPushConstants; MVKPushConstantsCommandEncoderState _tessEvalPushConstants;
MVKPushConstantsCommandEncoderState _fragmentPushConstants; MVKPushConstantsCommandEncoderState _fragmentPushConstants;
MVKPushConstantsCommandEncoderState _computePushConstants; MVKPushConstantsCommandEncoderState _computePushConstants;
MVKOcclusionQueryCommandEncoderState _occlusionQueryState; MVKOcclusionQueryCommandEncoderState _occlusionQueryState;
VkSubpassContents _subpassContents;
MVKCommandUse _mtlComputeEncoderUse;
MVKCommandUse _mtlBlitEncoderUse;
uint32_t _renderSubpassIndex;
uint32_t _multiviewPassIndex;
uint32_t _flushCount = 0; uint32_t _flushCount = 0;
bool _isRenderingEntireAttachment; bool _isRenderingEntireAttachment;
}; };

View File

@ -147,6 +147,7 @@ VkResult MVKCommandBuffer::reset(VkCommandBufferResetFlags flags) {
_isExecutingNonConcurrently.clear(); _isExecutingNonConcurrently.clear();
_commandCount = 0; _commandCount = 0;
_needsVisibilityResultMTLBuffer = false; _needsVisibilityResultMTLBuffer = false;
_hasStageCounterTimestampCommand = false;
_lastTessellationPipeline = nullptr; _lastTessellationPipeline = nullptr;
_lastMultiviewSubpass = nullptr; _lastMultiviewSubpass = nullptr;
setConfigurationResult(VK_NOT_READY); setConfigurationResult(VK_NOT_READY);
@ -171,7 +172,9 @@ void MVKCommandBuffer::addCommand(MVKCommand* command) {
setConfigurationResult(reportError(VK_NOT_READY, "Command buffer cannot accept commands before vkBeginCommandBuffer() is called.")); setConfigurationResult(reportError(VK_NOT_READY, "Command buffer cannot accept commands before vkBeginCommandBuffer() is called."));
return; return;
} }
_commandCount++;
if(_immediateCmdEncoder) { if(_immediateCmdEncoder) {
_immediateCmdEncoder->encodeCommands(command); _immediateCmdEncoder->encodeCommands(command);
@ -185,7 +188,6 @@ void MVKCommandBuffer::addCommand(MVKCommand* command) {
command->_next = nullptr; command->_next = nullptr;
_tail = command; _tail = command;
if ( !_head ) { _head = command; } if ( !_head ) { _head = command; }
_commandCount++;
} }
void MVKCommandBuffer::submit(MVKQueueCommandBufferSubmission* cmdBuffSubmit, void MVKCommandBuffer::submit(MVKQueueCommandBufferSubmission* cmdBuffSubmit,
@ -258,20 +260,21 @@ MVKCommandBuffer::~MVKCommandBuffer() {
reset(0); reset(0);
} }
// If the initial visibility result buffer has not been set, promote the first visibility result buffer // Promote the initial visibility buffer and indication of timestamp use from the secondary buffers.
// found among any of the secondary command buffers, to support the case where a render pass is started in
// the primary command buffer but the visibility query is started inside one of the secondary command buffers.
void MVKCommandBuffer::recordExecuteCommands(const MVKArrayRef<MVKCommandBuffer*> secondaryCommandBuffers) { void MVKCommandBuffer::recordExecuteCommands(const MVKArrayRef<MVKCommandBuffer*> secondaryCommandBuffers) {
if (!_needsVisibilityResultMTLBuffer) { for (MVKCommandBuffer* cmdBuff : secondaryCommandBuffers) {
for (MVKCommandBuffer* cmdBuff : secondaryCommandBuffers) { if (cmdBuff->_needsVisibilityResultMTLBuffer) { _needsVisibilityResultMTLBuffer = true; }
if (cmdBuff->_needsVisibilityResultMTLBuffer) { if (cmdBuff->_hasStageCounterTimestampCommand) { _hasStageCounterTimestampCommand = true; }
_needsVisibilityResultMTLBuffer = true;
break;
}
}
} }
} }
// Track whether a stage-based timestamp command has been added, so we know
// to update the timestamp command fence when ending a Metal command encoder.
void MVKCommandBuffer::recordTimestampCommand() {
_hasStageCounterTimestampCommand = mvkIsAnyFlagEnabled(_device->_pMetalFeatures->counterSamplingPoints, MVK_COUNTER_SAMPLING_AT_PIPELINE_STAGE);
}
#pragma mark - #pragma mark -
#pragma mark Tessellation constituent command management #pragma mark Tessellation constituent command management
@ -334,7 +337,7 @@ void MVKCommandEncoder::encodeCommands(MVKCommand* command) {
while(command) { while(command) {
uint32_t prevMVPassIdx = _multiviewPassIndex; uint32_t prevMVPassIdx = _multiviewPassIndex;
command->encode(this); command->encode(this);
if(_multiviewPassIndex > prevMVPassIdx) { if(_multiviewPassIndex > prevMVPassIdx) {
// This means we're in a multiview render pass, and we moved on to the // 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. // next view group. Re-encode all commands in the subpass again for this group.
@ -744,7 +747,7 @@ void MVKCommandEncoder::endRenderpass() {
void MVKCommandEncoder::endMetalRenderEncoding() { void MVKCommandEncoder::endMetalRenderEncoding() {
if (_mtlRenderEncoder == nil) { return; } if (_mtlRenderEncoder == nil) { return; }
if (hasTimestampStageCounterQueries() ) { [_mtlRenderEncoder updateFence: getStageCountersMTLFence() afterStages: MTLRenderStageFragment]; } if (_cmdBuffer->_hasStageCounterTimestampCommand) { [_mtlRenderEncoder updateFence: getStageCountersMTLFence() afterStages: MTLRenderStageFragment]; }
[_mtlRenderEncoder endEncoding]; [_mtlRenderEncoder endEncoding];
_mtlRenderEncoder = nil; // not retained _mtlRenderEncoder = nil; // not retained
@ -772,12 +775,12 @@ void MVKCommandEncoder::endCurrentMetalEncoding() {
_computeResourcesState.markDirty(); _computeResourcesState.markDirty();
_computePushConstants.markDirty(); _computePushConstants.markDirty();
if (_mtlComputeEncoder && hasTimestampStageCounterQueries() ) { [_mtlComputeEncoder updateFence: getStageCountersMTLFence()]; } if (_mtlComputeEncoder && _cmdBuffer->_hasStageCounterTimestampCommand) { [_mtlComputeEncoder updateFence: getStageCountersMTLFence()]; }
[_mtlComputeEncoder endEncoding]; [_mtlComputeEncoder endEncoding];
_mtlComputeEncoder = nil; // not retained _mtlComputeEncoder = nil; // not retained
_mtlComputeEncoderUse = kMVKCommandUseNone; _mtlComputeEncoderUse = kMVKCommandUseNone;
if (_mtlBlitEncoder && hasTimestampStageCounterQueries() ) { [_mtlBlitEncoder updateFence: getStageCountersMTLFence()]; } if (_mtlBlitEncoder && _cmdBuffer->_hasStageCounterTimestampCommand) { [_mtlBlitEncoder updateFence: getStageCountersMTLFence()]; }
[_mtlBlitEncoder endEncoding]; [_mtlBlitEncoder endEncoding];
_mtlBlitEncoder = nil; // not retained _mtlBlitEncoder = nil; // not retained
_mtlBlitEncoderUse = kMVKCommandUseNone; _mtlBlitEncoderUse = kMVKCommandUseNone;