Merge pull request #1677 from billhollings/fix-missing-metal-buffer-binding
Fix occasional missing Metal buffer binding when only offset changes.
diff --git a/Docs/Whats_New.md b/Docs/Whats_New.md
index 9b6287a..ef50c13 100644
--- a/Docs/Whats_New.md
+++ b/Docs/Whats_New.md
@@ -811,6 +811,7 @@
- Fix memory estimates for iOS 13+.
- Broaden conditions for host read sync for image memory barriers on macOS.
- Fix issue of reseting `CAMetalDrawable` and `MTLTexture` of peer swapchain images.
+- Fix occasional missing Metal buffer binding when only offset changes.
- Fix the `make install` build command to overwrite the existing framework in the system
framework library, and update `README.md` to clarify the instructions for using `make install`.
- Update the `README.md` and `MoltenVK_Runtime_UserGuide.md` documents to clarify that
diff --git a/MoltenVK/MoltenVK/Commands/MVKCmdTransfer.mm b/MoltenVK/MoltenVK/Commands/MVKCmdTransfer.mm
index e432115..3253aa9 100644
--- a/MoltenVK/MoltenVK/Commands/MVKCmdTransfer.mm
+++ b/MoltenVK/MoltenVK/Commands/MVKCmdTransfer.mm
@@ -478,8 +478,6 @@
bool isBlittingStencil = mvkIsAnyFlagEnabled(blitKey.srcAspect, (VK_IMAGE_ASPECT_STENCIL_BIT));
id<MTLDepthStencilState> mtlDSS = cmdEncoder->getCommandEncodingPool()->getMTLDepthStencilState(isBlittingDepth, isBlittingStencil);
- uint32_t vtxBuffIdx = cmdEncoder->getDevice()->getMetalBufferIndexForVertexAttributeBinding(kMVKVertexContentBufferIndex);
-
mtlColorAttDesc.level = mvkIBR.region.dstSubresource.mipLevel;
mtlDepthAttDesc.level = mvkIBR.region.dstSubresource.mipLevel;
mtlStencilAttDesc.level = mvkIBR.region.dstSubresource.mipLevel;
@@ -540,7 +538,8 @@
[mtlRendEnc pushDebugGroup: @"vkCmdBlitImage"];
[mtlRendEnc setRenderPipelineState: mtlRPS];
[mtlRendEnc setDepthStencilState: mtlDSS];
- cmdEncoder->setVertexBytes(mtlRendEnc, mvkIBR.vertices, sizeof(mvkIBR.vertices), vtxBuffIdx);
+ cmdEncoder->setVertexBytes(mtlRendEnc, mvkIBR.vertices, sizeof(mvkIBR.vertices),
+ cmdEncoder->getDevice()->getMetalBufferIndexForVertexAttributeBinding(kMVKVertexContentBufferIndex));
if (isLayeredBlit) {
cmdEncoder->setVertexBytes(mtlRendEnc, &zIncr, sizeof(zIncr), 0);
}
@@ -1250,7 +1249,6 @@
MVKPixelFormats* pixFmts = cmdEncoder->getPixelFormats();
MVKRenderSubpass* subpass = cmdEncoder->getSubpass();
- uint32_t vtxBuffIdx = cmdEncoder->getDevice()->getMetalBufferIndexForVertexAttributeBinding(kMVKVertexContentBufferIndex);
// Populate the render pipeline state attachment key with info from the subpass and framebuffer.
_rpsKey.mtlSampleCount = mvkSampleCountFromVkSampleCountFlagBits(subpass->getSampleCount());
@@ -1304,9 +1302,10 @@
[mtlRendEnc setViewport: {0, 0, (double) fbExtent.width, (double) fbExtent.height, 0.0, 1.0}];
[mtlRendEnc setScissorRect: {0, 0, fbExtent.width, fbExtent.height}];
- cmdEncoder->setVertexBytes(mtlRendEnc, clearColors, sizeof(clearColors), 0);
- cmdEncoder->setFragmentBytes(mtlRendEnc, clearColors, sizeof(clearColors), 0);
- cmdEncoder->setVertexBytes(mtlRendEnc, vertices, vtxCnt * sizeof(vertices[0]), vtxBuffIdx);
+ cmdEncoder->setVertexBytes(mtlRendEnc, clearColors, sizeof(clearColors), 0, true);
+ cmdEncoder->setFragmentBytes(mtlRendEnc, clearColors, sizeof(clearColors), 0, true);
+ cmdEncoder->setVertexBytes(mtlRendEnc, vertices, vtxCnt * sizeof(vertices[0]),
+ cmdEncoder->getDevice()->getMetalBufferIndexForVertexAttributeBinding(kMVKVertexContentBufferIndex), true);
[mtlRendEnc drawPrimitives: MTLPrimitiveTypeTriangle vertexStart: 0 vertexCount: vtxCnt];
[mtlRendEnc popDebugGroup];
@@ -1334,7 +1333,6 @@
cmdEncoder->_depthBiasState.markDirty();
cmdEncoder->_viewportState.markDirty();
cmdEncoder->_scissorState.markDirty();
- cmdEncoder->_graphicsResourcesState.beginMetalRenderPass();
}
template class MVKCmdClearAttachments<1>;
diff --git a/MoltenVK/MoltenVK/Commands/MVKCommandBuffer.h b/MoltenVK/MoltenVK/Commands/MVKCommandBuffer.h
index 5502dc2..c5fb691 100644
--- a/MoltenVK/MoltenVK/Commands/MVKCommandBuffer.h
+++ b/MoltenVK/MoltenVK/Commands/MVKCommandBuffer.h
@@ -354,14 +354,29 @@
/** Returns the push constants associated with the specified shader stage. */
MVKPushConstantsCommandEncoderState* getPushConstants(VkShaderStageFlagBits shaderStage);
- /** Copy bytes into the Metal encoder at a Metal vertex buffer index. */
- void setVertexBytes(id<MTLRenderCommandEncoder> mtlEncoder, const void* bytes, NSUInteger length, uint32_t mtlBuffIndex);
+ /**
+ * Copy bytes into the Metal encoder at a Metal vertex buffer index, and optionally indicate
+ * that this binding might override a desriptor binding. If so, the descriptor binding will
+ * be marked dirty so that it will rebind before the next usage.
+ */
+ void setVertexBytes(id<MTLRenderCommandEncoder> mtlEncoder, const void* bytes,
+ NSUInteger length, uint32_t mtlBuffIndex, bool descOverride = false);
- /** Copy bytes into the Metal encoder at a Metal fragment buffer index. */
- void setFragmentBytes(id<MTLRenderCommandEncoder> mtlEncoder, const void* bytes, NSUInteger length, uint32_t mtlBuffIndex);
+ /**
+ * Copy bytes into the Metal encoder at a Metal fragment buffer index, and optionally indicate
+ * that this binding might override a desriptor binding. If so, the descriptor binding will
+ * be marked dirty so that it will rebind before the next usage.
+ */
+ void setFragmentBytes(id<MTLRenderCommandEncoder> mtlEncoder, const void* bytes,
+ NSUInteger length, uint32_t mtlBuffIndex, bool descOverride = false);
- /** Copy bytes into the Metal encoder at a Metal compute buffer index. */
- void setComputeBytes(id<MTLComputeCommandEncoder> mtlEncoder, const void* bytes, NSUInteger length, uint32_t mtlBuffIndex);
+ /**
+ * Copy bytes into the Metal encoder at a Metal compute buffer index, and optionally indicate
+ * that this binding might override a desriptor binding. If so, the descriptor binding will
+ * be marked dirty so that it will rebind before the next usage.
+ */
+ void setComputeBytes(id<MTLComputeCommandEncoder> mtlEncoder, const void* bytes,
+ NSUInteger length, uint32_t mtlBuffIndex, bool descOverride = false);
/** Get a temporary MTLBuffer that will be returned to a pool after the command buffer is finished. */
const MVKMTLBufferAllocation* getTempMTLBuffer(NSUInteger length, bool isPrivate = false, bool isDedicated = false);
diff --git a/MoltenVK/MoltenVK/Commands/MVKCommandBuffer.mm b/MoltenVK/MoltenVK/Commands/MVKCommandBuffer.mm
index e37a89a..0f4c5f5 100644
--- a/MoltenVK/MoltenVK/Commands/MVKCommandBuffer.mm
+++ b/MoltenVK/MoltenVK/Commands/MVKCommandBuffer.mm
@@ -836,37 +836,52 @@
void MVKCommandEncoder::setVertexBytes(id<MTLRenderCommandEncoder> mtlEncoder,
const void* bytes,
NSUInteger length,
- uint32_t mtlBuffIndex) {
+ uint32_t mtlBuffIndex,
+ bool descOverride) {
if (_pDeviceMetalFeatures->dynamicMTLBufferSize && length <= _pDeviceMetalFeatures->dynamicMTLBufferSize) {
[mtlEncoder setVertexBytes: bytes length: length atIndex: mtlBuffIndex];
} else {
const MVKMTLBufferAllocation* mtlBuffAlloc = copyToTempMTLBufferAllocation(bytes, length);
[mtlEncoder setVertexBuffer: mtlBuffAlloc->_mtlBuffer offset: mtlBuffAlloc->_offset atIndex: mtlBuffIndex];
}
+
+ if (descOverride) {
+ _graphicsResourcesState.markBufferIndexDirty(kMVKShaderStageVertex, mtlBuffIndex);
+ }
}
void MVKCommandEncoder::setFragmentBytes(id<MTLRenderCommandEncoder> mtlEncoder,
const void* bytes,
NSUInteger length,
- uint32_t mtlBuffIndex) {
+ uint32_t mtlBuffIndex,
+ bool descOverride) {
if (_pDeviceMetalFeatures->dynamicMTLBufferSize && length <= _pDeviceMetalFeatures->dynamicMTLBufferSize) {
[mtlEncoder setFragmentBytes: bytes length: length atIndex: mtlBuffIndex];
} else {
const MVKMTLBufferAllocation* mtlBuffAlloc = copyToTempMTLBufferAllocation(bytes, length);
[mtlEncoder setFragmentBuffer: mtlBuffAlloc->_mtlBuffer offset: mtlBuffAlloc->_offset atIndex: mtlBuffIndex];
}
+
+ if (descOverride) {
+ _graphicsResourcesState.markBufferIndexDirty(kMVKShaderStageFragment, mtlBuffIndex);
+ }
}
void MVKCommandEncoder::setComputeBytes(id<MTLComputeCommandEncoder> mtlEncoder,
const void* bytes,
NSUInteger length,
- uint32_t mtlBuffIndex) {
+ uint32_t mtlBuffIndex,
+ bool descOverride) {
if (_pDeviceMetalFeatures->dynamicMTLBufferSize && length <= _pDeviceMetalFeatures->dynamicMTLBufferSize) {
[mtlEncoder setBytes: bytes length: length atIndex: mtlBuffIndex];
} else {
const MVKMTLBufferAllocation* mtlBuffAlloc = copyToTempMTLBufferAllocation(bytes, length);
[mtlEncoder setBuffer: mtlBuffAlloc->_mtlBuffer offset: mtlBuffAlloc->_offset atIndex: mtlBuffIndex];
}
+
+ if (descOverride) {
+ _computeResourcesState.markBufferIndexDirty(mtlBuffIndex);
+ }
}
// Return the MTLBuffer allocation to the pool once the command buffer is done with it
diff --git a/MoltenVK/MoltenVK/Commands/MVKCommandEncoderState.h b/MoltenVK/MoltenVK/Commands/MVKCommandEncoderState.h
index 0def2be..166ae74 100644
--- a/MoltenVK/MoltenVK/Commands/MVKCommandEncoderState.h
+++ b/MoltenVK/MoltenVK/Commands/MVKCommandEncoderState.h
@@ -200,8 +200,7 @@
/** Sets the index of the Metal buffer used to hold the push constants. */
void setMTLBufferIndex(uint32_t mtlBufferIndex, bool pipelineStageUsesPushConstants);
- /** Constructs this instance for the specified command encoder. */
- MVKPushConstantsCommandEncoderState(MVKCommandEncoder* cmdEncoder,
+ MVKPushConstantsCommandEncoderState(MVKCommandEncoder* cmdEncoder,
VkShaderStageFlagBits shaderStage)
: MVKCommandEncoderState(cmdEncoder), _shaderStage(shaderStage) {}
@@ -364,11 +363,12 @@
MTLResourceUsage mtlUsage,
MTLRenderStages mtlStages) = 0;
+ void markDirty() override;
+
MVKResourcesCommandEncoderState(MVKCommandEncoder* cmdEncoder) :
MVKCommandEncoderState(cmdEncoder), _boundDescriptorSets{} {}
protected:
- void markDirty() override;
// Template function that marks both the vector and all binding elements in the vector as dirty.
template<class T>
@@ -377,25 +377,40 @@
bindingsDirtyFlag = true;
}
+ // Template function to find and mark dirty the binding that uses the index.
+ template<class T>
+ void markIndexDirty(T& bindings, bool& bindingsDirtyFlag, uint32_t index) {
+ for (auto& b : bindings) {
+ if (b.index == index) {
+ b.markDirty();
+ bindingsDirtyFlag = true;
+ MVKCommandEncoderState::markDirty();
+ return;
+ }
+ }
+ }
+
// Template function that updates an existing binding or adds a new binding to a vector
// of bindings, and marks the binding, the vector, and this instance as dirty
template<class T, class V>
void bind(const T& b, V& bindings, bool& bindingsDirtyFlag) {
-
if ( !b.mtlResource ) { return; }
- MVKCommandEncoderState::markDirty();
- bindingsDirtyFlag = true;
-
- for (auto iter = bindings.begin(), end = bindings.end(); iter != end; ++iter) {
- if (iter->index == b.index) {
- iter->update(b);
+ for (auto& rb : bindings) {
+ if (rb.index == b.index) {
+ rb.update(b);
+ if (rb.isDirty) {
+ bindingsDirtyFlag = true;
+ MVKCommandEncoderState::markDirty();
+ }
return;
}
}
bindings.push_back(b);
bindings.back().markDirty();
+ bindingsDirtyFlag = true;
+ MVKCommandEncoderState::markDirty();
}
// For texture bindings, we also keep track of whether any bindings need a texture swizzle
@@ -533,6 +548,11 @@
/** Offset all buffers for vertex attribute bindings with zero divisors by the given number of strides. */
void offsetZeroDivisorVertexBuffers(MVKGraphicsStage stage, MVKGraphicsPipeline* pipeline, uint32_t firstInstance);
+ /** Marks dirty the buffer binding using the index. */
+ void markBufferIndexDirty(MVKShaderStage stage, uint32_t mtlBufferIndex);
+
+ void markDirty() override;
+
#pragma mark Construction
/** Constructs this instance for the specified command encoder. */
@@ -540,7 +560,6 @@
protected:
void encodeImpl(uint32_t stage) override;
- void markDirty() override;
void bindMetalArgumentBuffer(MVKShaderStage stage, MVKMTLBufferBinding& buffBind) override;
ResourceBindings<8> _shaderStageResourceBindings[4];
@@ -581,6 +600,9 @@
MTLResourceUsage mtlUsage,
MTLRenderStages mtlStages) override;
+ /** Marks dirty the buffer binding using the index. */
+ void markBufferIndexDirty(uint32_t mtlBufferIndex);
+
void markDirty() override;
#pragma mark Construction
diff --git a/MoltenVK/MoltenVK/Commands/MVKCommandEncoderState.mm b/MoltenVK/MoltenVK/Commands/MVKCommandEncoderState.mm
index 1a36927..8cf763a 100644
--- a/MoltenVK/MoltenVK/Commands/MVKCommandEncoderState.mm
+++ b/MoltenVK/MoltenVK/Commands/MVKCommandEncoderState.mm
@@ -194,13 +194,13 @@
_cmdEncoder->setComputeBytes(_cmdEncoder->getMTLComputeEncoder(kMVKCommandUseTessellationVertexTessCtl),
_pushConstants.data(),
_pushConstants.size(),
- _mtlBufferIndex);
+ _mtlBufferIndex, true);
_isDirty = false; // Okay, I changed the encoder
} else if (!isTessellating() && stage == kMVKGraphicsStageRasterization) {
_cmdEncoder->setVertexBytes(_cmdEncoder->_mtlRenderEncoder,
_pushConstants.data(),
_pushConstants.size(),
- _mtlBufferIndex);
+ _mtlBufferIndex, true);
_isDirty = false; // Okay, I changed the encoder
}
break;
@@ -209,7 +209,7 @@
_cmdEncoder->setComputeBytes(_cmdEncoder->getMTLComputeEncoder(kMVKCommandUseTessellationVertexTessCtl),
_pushConstants.data(),
_pushConstants.size(),
- _mtlBufferIndex);
+ _mtlBufferIndex, true);
_isDirty = false; // Okay, I changed the encoder
}
break;
@@ -218,7 +218,7 @@
_cmdEncoder->setVertexBytes(_cmdEncoder->_mtlRenderEncoder,
_pushConstants.data(),
_pushConstants.size(),
- _mtlBufferIndex);
+ _mtlBufferIndex, true);
_isDirty = false; // Okay, I changed the encoder
}
break;
@@ -227,7 +227,7 @@
_cmdEncoder->setFragmentBytes(_cmdEncoder->_mtlRenderEncoder,
_pushConstants.data(),
_pushConstants.size(),
- _mtlBufferIndex);
+ _mtlBufferIndex, true);
_isDirty = false; // Okay, I changed the encoder
}
break;
@@ -235,7 +235,7 @@
_cmdEncoder->setComputeBytes(_cmdEncoder->getMTLComputeEncoder(kMVKCommandUseDispatch),
_pushConstants.data(),
_pushConstants.size(),
- _mtlBufferIndex);
+ _mtlBufferIndex, true);
_isDirty = false; // Okay, I changed the encoder
break;
default:
@@ -982,6 +982,11 @@
}
}
+void MVKGraphicsResourcesCommandEncoderState::markBufferIndexDirty(MVKShaderStage stage, uint32_t mtlBufferIndex) {
+ auto& stageRezBinds = _shaderStageResourceBindings[stage];
+ markIndexDirty(stageRezBinds.bufferBindings, stageRezBinds.areBufferBindingsDirty, mtlBufferIndex);
+}
+
#pragma mark -
#pragma mark MVKComputeResourcesCommandEncoderState
@@ -1115,6 +1120,10 @@
}
}
+void MVKComputeResourcesCommandEncoderState::markBufferIndexDirty(uint32_t mtlBufferIndex) {
+ markIndexDirty(_resourceBindings.bufferBindings, _resourceBindings.areBufferBindingsDirty, mtlBufferIndex);
+}
+
#pragma mark -
#pragma mark MVKOcclusionQueryCommandEncoderState
diff --git a/MoltenVK/MoltenVK/Commands/MVKMTLResourceBindings.h b/MoltenVK/MoltenVK/Commands/MVKMTLResourceBindings.h
index a0f71d0..f250230 100644
--- a/MoltenVK/MoltenVK/Commands/MVKMTLResourceBindings.h
+++ b/MoltenVK/MoltenVK/Commands/MVKMTLResourceBindings.h
@@ -75,12 +75,11 @@
inline void markDirty() { justOffset = false; isDirty = true; }
inline void update(const MVKMTLBufferBinding &other) {
- if (mtlBuffer != other.mtlBuffer || size != other.size || isInline != other.isInline) {
+ if (mtlBuffer != other.mtlBuffer || size != other.size || other.isInline) {
mtlBuffer = other.mtlBuffer;
size = other.size;
isInline = other.isInline;
offset = other.offset;
-
justOffset = false;
isDirty = true;
} else if (offset != other.offset) {