Merge pull request #1594 from js6i/perf2
Reducing redundant state changes
diff --git a/MoltenVK/MoltenVK/Commands/MVKCommandEncoderState.h b/MoltenVK/MoltenVK/Commands/MVKCommandEncoderState.h
index c6e7280..0def2be 100644
--- a/MoltenVK/MoltenVK/Commands/MVKCommandEncoderState.h
+++ b/MoltenVK/MoltenVK/Commands/MVKCommandEncoderState.h
@@ -373,7 +373,7 @@
// Template function that marks both the vector and all binding elements in the vector as dirty.
template<class T>
void markDirty(T& bindings, bool& bindingsDirtyFlag) {
- for (auto& b : bindings) { b.isDirty = true; }
+ for (auto& b : bindings) { b.markDirty(); }
bindingsDirtyFlag = true;
}
@@ -384,18 +384,18 @@
if ( !b.mtlResource ) { return; }
- T db = b; // Copy that can be marked dirty
MVKCommandEncoderState::markDirty();
bindingsDirtyFlag = true;
- db.isDirty = true;
for (auto iter = bindings.begin(), end = bindings.end(); iter != end; ++iter) {
- if( iter->index == db.index ) {
- *iter = db;
+ if (iter->index == b.index) {
+ iter->update(b);
return;
}
}
- bindings.push_back(db);
+
+ bindings.push_back(b);
+ bindings.back().markDirty();
}
// For texture bindings, we also keep track of whether any bindings need a texture swizzle
diff --git a/MoltenVK/MoltenVK/Commands/MVKCommandEncoderState.mm b/MoltenVK/MoltenVK/Commands/MVKCommandEncoderState.mm
index dfb8c1c..1a36927 100644
--- a/MoltenVK/MoltenVK/Commands/MVKCommandEncoderState.mm
+++ b/MoltenVK/MoltenVK/Commands/MVKCommandEncoderState.mm
@@ -37,8 +37,8 @@
#pragma mark MVKPipelineCommandEncoderState
void MVKPipelineCommandEncoderState::bindPipeline(MVKPipeline* pipeline) {
+ if (pipeline != _pipeline) markDirty();
_pipeline = pipeline;
- markDirty();
}
MVKPipeline* MVKPipelineCommandEncoderState::getPipeline() { return _pipeline; }
@@ -71,14 +71,17 @@
usingViewports.resize(firstViewport + vpCnt);
}
+ bool dirty;
bool mustSetDynamically = _cmdEncoder->supportsDynamicState(VK_DYNAMIC_STATE_VIEWPORT);
if (isSettingDynamically || (!mustSetDynamically && vpCnt > 0)) {
+ dirty = memcmp(&usingViewports[firstViewport], &viewports[0], vpCnt * sizeof(VkViewport)) != 0;
std::copy(viewports.begin(), viewports.end(), usingViewports.begin() + firstViewport);
} else {
+ dirty = !usingViewports.empty();
usingViewports.clear();
}
- markDirty();
+ if (dirty) markDirty();
}
void MVKViewportCommandEncoderState::encodeImpl(uint32_t stage) {
@@ -121,14 +124,17 @@
usingScissors.resize(firstScissor + sCnt);
}
+ bool dirty;
bool mustSetDynamically = _cmdEncoder->supportsDynamicState(VK_DYNAMIC_STATE_SCISSOR);
if (isSettingDynamically || (!mustSetDynamically && sCnt > 0)) {
+ dirty = memcmp(&usingScissors[firstScissor], &scissors[0], sCnt * sizeof(VkRect2D)) != 0;
std::copy(scissors.begin(), scissors.end(), usingScissors.begin() + firstScissor);
} else {
+ dirty = !usingScissors.empty();
usingScissors.clear();
}
- markDirty();
+ if (dirty) markDirty();
}
void MVKScissorCommandEncoderState::encodeImpl(uint32_t stage) {
@@ -248,6 +254,7 @@
#pragma mark MVKDepthStencilCommandEncoderState
void MVKDepthStencilCommandEncoderState:: setDepthStencilState(const VkPipelineDepthStencilStateCreateInfo& vkDepthStencilInfo) {
+ auto oldData = _depthStencilData;
if (vkDepthStencilInfo.depthTestEnable) {
_depthStencilData.depthCompareFunction = mvkMTLCompareFunctionFromVkCompareOp(vkDepthStencilInfo.depthCompareOp);
@@ -260,7 +267,7 @@
setStencilState(_depthStencilData.frontFaceStencilData, vkDepthStencilInfo.front, vkDepthStencilInfo.stencilTestEnable);
setStencilState(_depthStencilData.backFaceStencilData, vkDepthStencilInfo.back, vkDepthStencilInfo.stencilTestEnable);
- markDirty();
+ if (!(oldData == _depthStencilData)) markDirty();
}
void MVKDepthStencilCommandEncoderState::setStencilState(MVKMTLStencilDescriptorData& stencilInfo,
@@ -289,6 +296,8 @@
// it may not be accurate, and if not dynamic, pipeline will override when it is encoded anyway.
void MVKDepthStencilCommandEncoderState::setStencilCompareMask(VkStencilFaceFlags faceMask,
uint32_t stencilCompareMask) {
+ auto oldData = _depthStencilData;
+
if (mvkAreAllFlagsEnabled(faceMask, VK_STENCIL_FACE_FRONT_BIT)) {
_depthStencilData.frontFaceStencilData.readMask = stencilCompareMask;
}
@@ -296,13 +305,15 @@
_depthStencilData.backFaceStencilData.readMask = stencilCompareMask;
}
- markDirty();
+ if (!(oldData == _depthStencilData)) markDirty();
}
// We don't check for dynamic state here, because if this is called before pipeline is set,
// it may not be accurate, and if not dynamic, pipeline will override when it is encoded anyway.
void MVKDepthStencilCommandEncoderState::setStencilWriteMask(VkStencilFaceFlags faceMask,
uint32_t stencilWriteMask) {
+ auto oldData = _depthStencilData;
+
if (mvkAreAllFlagsEnabled(faceMask, VK_STENCIL_FACE_FRONT_BIT)) {
_depthStencilData.frontFaceStencilData.writeMask = stencilWriteMask;
}
@@ -310,10 +321,12 @@
_depthStencilData.backFaceStencilData.writeMask = stencilWriteMask;
}
- markDirty();
+ if (!(oldData == _depthStencilData)) markDirty();
}
void MVKDepthStencilCommandEncoderState::beginMetalRenderPass() {
+ MVKCommandEncoderState::beginMetalRenderPass();
+
MVKRenderSubpass* mvkSubpass = _cmdEncoder->getSubpass();
MVKPixelFormats* pixFmts = _cmdEncoder->getPixelFormats();
MTLPixelFormat mtlDSFormat = pixFmts->getMTLPixelFormat(mvkSubpass->getDepthStencilFormat());
@@ -351,22 +364,27 @@
// If ref values are to be set dynamically, don't set them here.
if (_cmdEncoder->supportsDynamicState(VK_DYNAMIC_STATE_STENCIL_REFERENCE)) { return; }
+ if (_frontFaceValue != vkDepthStencilInfo.front.reference || _backFaceValue != vkDepthStencilInfo.back.reference)
+ markDirty();
+
_frontFaceValue = vkDepthStencilInfo.front.reference;
_backFaceValue = vkDepthStencilInfo.back.reference;
- markDirty();
}
// We don't check for dynamic state here, because if this is called before pipeline is set,
// it may not be accurate, and if not dynamic, pipeline will override when it is encoded anyway.
void MVKStencilReferenceValueCommandEncoderState::setReferenceValues(VkStencilFaceFlags faceMask,
uint32_t stencilReference) {
+ bool dirty = false;
if (mvkAreAllFlagsEnabled(faceMask, VK_STENCIL_FACE_FRONT_BIT)) {
+ dirty |= (_frontFaceValue != stencilReference);
_frontFaceValue = stencilReference;
}
if (mvkAreAllFlagsEnabled(faceMask, VK_STENCIL_FACE_BACK_BIT)) {
+ dirty |= (_backFaceValue != stencilReference);
_backFaceValue = stencilReference;
}
- markDirty();
+ if (dirty) markDirty();
}
void MVKStencilReferenceValueCommandEncoderState::encodeImpl(uint32_t stage) {
@@ -381,16 +399,20 @@
void MVKDepthBiasCommandEncoderState::setDepthBias(const VkPipelineRasterizationStateCreateInfo& vkRasterInfo) {
+ auto wasEnabled = _isEnabled;
_isEnabled = vkRasterInfo.depthBiasEnable;
// If ref values are to be set dynamically, don't set them here.
if (_cmdEncoder->supportsDynamicState(VK_DYNAMIC_STATE_DEPTH_BIAS)) { return; }
- _depthBiasConstantFactor = vkRasterInfo.depthBiasConstantFactor;
- _depthBiasSlopeFactor = vkRasterInfo.depthBiasSlopeFactor;
- _depthBiasClamp = vkRasterInfo.depthBiasClamp;
+ if (_isEnabled != wasEnabled || _depthBiasConstantFactor != vkRasterInfo.depthBiasConstantFactor
+ || _depthBiasSlopeFactor != vkRasterInfo.depthBiasSlopeFactor || _depthBiasClamp != vkRasterInfo.depthBiasClamp) {
- markDirty();
+ markDirty();
+ _depthBiasConstantFactor = vkRasterInfo.depthBiasConstantFactor;
+ _depthBiasSlopeFactor = vkRasterInfo.depthBiasSlopeFactor;
+ _depthBiasClamp = vkRasterInfo.depthBiasClamp;
+ }
}
// We don't check for dynamic state here, because if this is called before pipeline is set,
@@ -398,11 +420,15 @@
void MVKDepthBiasCommandEncoderState::setDepthBias(float depthBiasConstantFactor,
float depthBiasSlopeFactor,
float depthBiasClamp) {
- _depthBiasConstantFactor = depthBiasConstantFactor;
- _depthBiasSlopeFactor = depthBiasSlopeFactor;
- _depthBiasClamp = depthBiasClamp;
- markDirty();
+ if (_depthBiasConstantFactor != depthBiasConstantFactor || _depthBiasSlopeFactor != depthBiasSlopeFactor
+ || _depthBiasClamp != depthBiasClamp) {
+
+ markDirty();
+ _depthBiasConstantFactor = depthBiasConstantFactor;
+ _depthBiasSlopeFactor = depthBiasSlopeFactor;
+ _depthBiasClamp = depthBiasClamp;
+ }
}
void MVKDepthBiasCommandEncoderState::encodeImpl(uint32_t stage) {
@@ -426,12 +452,13 @@
// Abort if we are using dynamic, but call is not dynamic.
if ( !isDynamic && _cmdEncoder->supportsDynamicState(VK_DYNAMIC_STATE_BLEND_CONSTANTS) ) { return; }
- _red = red;
- _green = green;
- _blue = blue;
- _alpha = alpha;
-
- markDirty();
+ if (_red != red || _green != green || _blue != blue || _alpha != alpha) {
+ markDirty();
+ _red = red;
+ _green = green;
+ _blue = blue;
+ _alpha = alpha;
+ }
}
void MVKBlendColorCommandEncoderState::encodeImpl(uint32_t stage) {
@@ -752,6 +779,10 @@
b.mtlBytes,
b.size,
b.index);
+ else if (b.justOffset)
+ [cmdEncoder->getMTLComputeEncoder(kMVKCommandUseTessellationVertexTessCtl)
+ setBufferOffset: b.offset
+ atIndex: b.index];
else
[cmdEncoder->getMTLComputeEncoder(kMVKCommandUseTessellationVertexTessCtl) setBuffer: b.mtlBuffer
offset: b.offset
@@ -785,9 +816,14 @@
b.size,
b.index);
} else {
- [cmdEncoder->_mtlRenderEncoder setVertexBuffer: b.mtlBuffer
- offset: b.offset
- atIndex: b.index];
+ if (b.justOffset) {
+ [cmdEncoder->_mtlRenderEncoder setVertexBufferOffset: b.offset
+ atIndex: b.index];
+ } else {
+ [cmdEncoder->_mtlRenderEncoder setVertexBuffer: b.mtlBuffer
+ offset: b.offset
+ atIndex: b.index];
+ }
// Add any translated vertex bindings for this binding
auto xltdVtxBindings = pipeline->getTranslatedVertexBindings();
@@ -828,6 +864,9 @@
b.mtlBytes,
b.size,
b.index);
+ else if (b.justOffset)
+ [cmdEncoder->getMTLComputeEncoder(kMVKCommandUseTessellationVertexTessCtl) setBufferOffset: b.offset
+ atIndex: b.index];
else
[cmdEncoder->getMTLComputeEncoder(kMVKCommandUseTessellationVertexTessCtl) setBuffer: b.mtlBuffer
offset: b.offset
@@ -858,6 +897,9 @@
b.mtlBytes,
b.size,
b.index);
+ else if (b.justOffset)
+ [cmdEncoder->_mtlRenderEncoder setVertexBufferOffset: b.offset
+ atIndex: b.index];
else
[cmdEncoder->_mtlRenderEncoder setVertexBuffer: b.mtlBuffer
offset: b.offset
@@ -888,6 +930,9 @@
b.mtlBytes,
b.size,
b.index);
+ else if (b.justOffset)
+ [cmdEncoder->_mtlRenderEncoder setFragmentBufferOffset: b.offset
+ atIndex: b.index];
else
[cmdEncoder->_mtlRenderEncoder setFragmentBuffer: b.mtlBuffer
offset: b.offset
@@ -1027,7 +1072,12 @@
b.mtlBytes,
b.size,
b.index);
- } else {
+ } else if (b.justOffset) {
+ [cmdEncoder->getMTLComputeEncoder(kMVKCommandUseDispatch)
+ setBufferOffset: b.offset
+ atIndex: b.index];
+
+ } else {
[cmdEncoder->getMTLComputeEncoder(kMVKCommandUseDispatch) setBuffer: b.mtlBuffer
offset: b.offset
atIndex: b.index];
diff --git a/MoltenVK/MoltenVK/Commands/MVKMTLResourceBindings.h b/MoltenVK/MoltenVK/Commands/MVKMTLResourceBindings.h
index 012600a..a0f71d0 100644
--- a/MoltenVK/MoltenVK/Commands/MVKMTLResourceBindings.h
+++ b/MoltenVK/MoltenVK/Commands/MVKMTLResourceBindings.h
@@ -34,6 +34,16 @@
uint32_t swizzle = 0;
uint16_t index = 0;
bool isDirty = true;
+
+ inline void markDirty() { isDirty = true; }
+
+ inline void update(const MVKMTLTextureBinding &other) {
+ if (mtlTexture != other.mtlTexture || swizzle != other.swizzle) {
+ mtlTexture = other.mtlTexture;
+ swizzle = other.swizzle;
+ markDirty();
+ }
+ }
} MVKMTLTextureBinding;
/** Describes a MTLSamplerState resource binding. */
@@ -41,6 +51,15 @@
union { id<MTLSamplerState> mtlSamplerState = nil; id<MTLSamplerState> mtlResource; }; // aliases
uint16_t index = 0;
bool isDirty = true;
+
+ inline void markDirty() { isDirty = true; }
+
+ inline void update(const MVKMTLSamplerStateBinding &other) {
+ if (mtlSamplerState != other.mtlSamplerState) {
+ mtlSamplerState = other.mtlSamplerState;
+ markDirty();
+ }
+ }
} MVKMTLSamplerStateBinding;
/** Describes a MTLBuffer resource binding. */
@@ -49,8 +68,27 @@
VkDeviceSize offset = 0;
uint32_t size = 0;
uint16_t index = 0;
+ bool justOffset = false;
bool isDirty = true;
bool isInline = false;
+
+ inline void markDirty() { justOffset = false; isDirty = true; }
+
+ inline void update(const MVKMTLBufferBinding &other) {
+ if (mtlBuffer != other.mtlBuffer || size != other.size || isInline != other.isInline) {
+ mtlBuffer = other.mtlBuffer;
+ size = other.size;
+ isInline = other.isInline;
+ offset = other.offset;
+
+ justOffset = false;
+ isDirty = true;
+ } else if (offset != other.offset) {
+ offset = other.offset;
+ justOffset = !isDirty || justOffset;
+ isDirty = true;
+ }
+ }
} MVKMTLBufferBinding;
/** Describes a MTLBuffer resource binding as used for an index buffer. */