Skip to content

Commit a909822

Browse files
authored
Merge pull request #1594 from js6i/perf2
Reducing redundant state changes
2 parents 334e0ce + 07780d6 commit a909822

File tree

3 files changed

+120
-32
lines changed

3 files changed

+120
-32
lines changed

MoltenVK/MoltenVK/Commands/MVKCommandEncoderState.h

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -373,7 +373,7 @@ class MVKResourcesCommandEncoderState : public MVKCommandEncoderState {
373373
// Template function that marks both the vector and all binding elements in the vector as dirty.
374374
template<class T>
375375
void markDirty(T& bindings, bool& bindingsDirtyFlag) {
376-
for (auto& b : bindings) { b.isDirty = true; }
376+
for (auto& b : bindings) { b.markDirty(); }
377377
bindingsDirtyFlag = true;
378378
}
379379

@@ -384,18 +384,18 @@ class MVKResourcesCommandEncoderState : public MVKCommandEncoderState {
384384

385385
if ( !b.mtlResource ) { return; }
386386

387-
T db = b; // Copy that can be marked dirty
388387
MVKCommandEncoderState::markDirty();
389388
bindingsDirtyFlag = true;
390-
db.isDirty = true;
391389

392390
for (auto iter = bindings.begin(), end = bindings.end(); iter != end; ++iter) {
393-
if( iter->index == db.index ) {
394-
*iter = db;
391+
if (iter->index == b.index) {
392+
iter->update(b);
395393
return;
396394
}
397395
}
398-
bindings.push_back(db);
396+
397+
bindings.push_back(b);
398+
bindings.back().markDirty();
399399
}
400400

401401
// For texture bindings, we also keep track of whether any bindings need a texture swizzle

MoltenVK/MoltenVK/Commands/MVKCommandEncoderState.mm

Lines changed: 76 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,8 @@
3737
#pragma mark MVKPipelineCommandEncoderState
3838

3939
void MVKPipelineCommandEncoderState::bindPipeline(MVKPipeline* pipeline) {
40+
if (pipeline != _pipeline) markDirty();
4041
_pipeline = pipeline;
41-
markDirty();
4242
}
4343

4444
MVKPipeline* MVKPipelineCommandEncoderState::getPipeline() { return _pipeline; }
@@ -71,14 +71,17 @@
7171
usingViewports.resize(firstViewport + vpCnt);
7272
}
7373

74+
bool dirty;
7475
bool mustSetDynamically = _cmdEncoder->supportsDynamicState(VK_DYNAMIC_STATE_VIEWPORT);
7576
if (isSettingDynamically || (!mustSetDynamically && vpCnt > 0)) {
77+
dirty = memcmp(&usingViewports[firstViewport], &viewports[0], vpCnt * sizeof(VkViewport)) != 0;
7678
std::copy(viewports.begin(), viewports.end(), usingViewports.begin() + firstViewport);
7779
} else {
80+
dirty = !usingViewports.empty();
7881
usingViewports.clear();
7982
}
8083

81-
markDirty();
84+
if (dirty) markDirty();
8285
}
8386

8487
void MVKViewportCommandEncoderState::encodeImpl(uint32_t stage) {
@@ -121,14 +124,17 @@
121124
usingScissors.resize(firstScissor + sCnt);
122125
}
123126

127+
bool dirty;
124128
bool mustSetDynamically = _cmdEncoder->supportsDynamicState(VK_DYNAMIC_STATE_SCISSOR);
125129
if (isSettingDynamically || (!mustSetDynamically && sCnt > 0)) {
130+
dirty = memcmp(&usingScissors[firstScissor], &scissors[0], sCnt * sizeof(VkRect2D)) != 0;
126131
std::copy(scissors.begin(), scissors.end(), usingScissors.begin() + firstScissor);
127132
} else {
133+
dirty = !usingScissors.empty();
128134
usingScissors.clear();
129135
}
130136

131-
markDirty();
137+
if (dirty) markDirty();
132138
}
133139

134140
void MVKScissorCommandEncoderState::encodeImpl(uint32_t stage) {
@@ -248,6 +254,7 @@
248254
#pragma mark MVKDepthStencilCommandEncoderState
249255

250256
void MVKDepthStencilCommandEncoderState:: setDepthStencilState(const VkPipelineDepthStencilStateCreateInfo& vkDepthStencilInfo) {
257+
auto oldData = _depthStencilData;
251258

252259
if (vkDepthStencilInfo.depthTestEnable) {
253260
_depthStencilData.depthCompareFunction = mvkMTLCompareFunctionFromVkCompareOp(vkDepthStencilInfo.depthCompareOp);
@@ -260,7 +267,7 @@
260267
setStencilState(_depthStencilData.frontFaceStencilData, vkDepthStencilInfo.front, vkDepthStencilInfo.stencilTestEnable);
261268
setStencilState(_depthStencilData.backFaceStencilData, vkDepthStencilInfo.back, vkDepthStencilInfo.stencilTestEnable);
262269

263-
markDirty();
270+
if (!(oldData == _depthStencilData)) markDirty();
264271
}
265272

266273
void MVKDepthStencilCommandEncoderState::setStencilState(MVKMTLStencilDescriptorData& stencilInfo,
@@ -289,31 +296,37 @@
289296
// it may not be accurate, and if not dynamic, pipeline will override when it is encoded anyway.
290297
void MVKDepthStencilCommandEncoderState::setStencilCompareMask(VkStencilFaceFlags faceMask,
291298
uint32_t stencilCompareMask) {
299+
auto oldData = _depthStencilData;
300+
292301
if (mvkAreAllFlagsEnabled(faceMask, VK_STENCIL_FACE_FRONT_BIT)) {
293302
_depthStencilData.frontFaceStencilData.readMask = stencilCompareMask;
294303
}
295304
if (mvkAreAllFlagsEnabled(faceMask, VK_STENCIL_FACE_BACK_BIT)) {
296305
_depthStencilData.backFaceStencilData.readMask = stencilCompareMask;
297306
}
298307

299-
markDirty();
308+
if (!(oldData == _depthStencilData)) markDirty();
300309
}
301310

302311
// We don't check for dynamic state here, because if this is called before pipeline is set,
303312
// it may not be accurate, and if not dynamic, pipeline will override when it is encoded anyway.
304313
void MVKDepthStencilCommandEncoderState::setStencilWriteMask(VkStencilFaceFlags faceMask,
305314
uint32_t stencilWriteMask) {
315+
auto oldData = _depthStencilData;
316+
306317
if (mvkAreAllFlagsEnabled(faceMask, VK_STENCIL_FACE_FRONT_BIT)) {
307318
_depthStencilData.frontFaceStencilData.writeMask = stencilWriteMask;
308319
}
309320
if (mvkAreAllFlagsEnabled(faceMask, VK_STENCIL_FACE_BACK_BIT)) {
310321
_depthStencilData.backFaceStencilData.writeMask = stencilWriteMask;
311322
}
312323

313-
markDirty();
324+
if (!(oldData == _depthStencilData)) markDirty();
314325
}
315326

316327
void MVKDepthStencilCommandEncoderState::beginMetalRenderPass() {
328+
MVKCommandEncoderState::beginMetalRenderPass();
329+
317330
MVKRenderSubpass* mvkSubpass = _cmdEncoder->getSubpass();
318331
MVKPixelFormats* pixFmts = _cmdEncoder->getPixelFormats();
319332
MTLPixelFormat mtlDSFormat = pixFmts->getMTLPixelFormat(mvkSubpass->getDepthStencilFormat());
@@ -351,22 +364,27 @@
351364
// If ref values are to be set dynamically, don't set them here.
352365
if (_cmdEncoder->supportsDynamicState(VK_DYNAMIC_STATE_STENCIL_REFERENCE)) { return; }
353366

367+
if (_frontFaceValue != vkDepthStencilInfo.front.reference || _backFaceValue != vkDepthStencilInfo.back.reference)
368+
markDirty();
369+
354370
_frontFaceValue = vkDepthStencilInfo.front.reference;
355371
_backFaceValue = vkDepthStencilInfo.back.reference;
356-
markDirty();
357372
}
358373

359374
// We don't check for dynamic state here, because if this is called before pipeline is set,
360375
// it may not be accurate, and if not dynamic, pipeline will override when it is encoded anyway.
361376
void MVKStencilReferenceValueCommandEncoderState::setReferenceValues(VkStencilFaceFlags faceMask,
362377
uint32_t stencilReference) {
378+
bool dirty = false;
363379
if (mvkAreAllFlagsEnabled(faceMask, VK_STENCIL_FACE_FRONT_BIT)) {
380+
dirty |= (_frontFaceValue != stencilReference);
364381
_frontFaceValue = stencilReference;
365382
}
366383
if (mvkAreAllFlagsEnabled(faceMask, VK_STENCIL_FACE_BACK_BIT)) {
384+
dirty |= (_backFaceValue != stencilReference);
367385
_backFaceValue = stencilReference;
368386
}
369-
markDirty();
387+
if (dirty) markDirty();
370388
}
371389

372390
void MVKStencilReferenceValueCommandEncoderState::encodeImpl(uint32_t stage) {
@@ -381,28 +399,36 @@
381399

382400
void MVKDepthBiasCommandEncoderState::setDepthBias(const VkPipelineRasterizationStateCreateInfo& vkRasterInfo) {
383401

402+
auto wasEnabled = _isEnabled;
384403
_isEnabled = vkRasterInfo.depthBiasEnable;
385404

386405
// If ref values are to be set dynamically, don't set them here.
387406
if (_cmdEncoder->supportsDynamicState(VK_DYNAMIC_STATE_DEPTH_BIAS)) { return; }
388407

389-
_depthBiasConstantFactor = vkRasterInfo.depthBiasConstantFactor;
390-
_depthBiasSlopeFactor = vkRasterInfo.depthBiasSlopeFactor;
391-
_depthBiasClamp = vkRasterInfo.depthBiasClamp;
408+
if (_isEnabled != wasEnabled || _depthBiasConstantFactor != vkRasterInfo.depthBiasConstantFactor
409+
|| _depthBiasSlopeFactor != vkRasterInfo.depthBiasSlopeFactor || _depthBiasClamp != vkRasterInfo.depthBiasClamp) {
392410

393-
markDirty();
411+
markDirty();
412+
_depthBiasConstantFactor = vkRasterInfo.depthBiasConstantFactor;
413+
_depthBiasSlopeFactor = vkRasterInfo.depthBiasSlopeFactor;
414+
_depthBiasClamp = vkRasterInfo.depthBiasClamp;
415+
}
394416
}
395417

396418
// We don't check for dynamic state here, because if this is called before pipeline is set,
397419
// it may not be accurate, and if not dynamic, pipeline will override when it is encoded anyway.
398420
void MVKDepthBiasCommandEncoderState::setDepthBias(float depthBiasConstantFactor,
399421
float depthBiasSlopeFactor,
400422
float depthBiasClamp) {
401-
_depthBiasConstantFactor = depthBiasConstantFactor;
402-
_depthBiasSlopeFactor = depthBiasSlopeFactor;
403-
_depthBiasClamp = depthBiasClamp;
404423

405-
markDirty();
424+
if (_depthBiasConstantFactor != depthBiasConstantFactor || _depthBiasSlopeFactor != depthBiasSlopeFactor
425+
|| _depthBiasClamp != depthBiasClamp) {
426+
427+
markDirty();
428+
_depthBiasConstantFactor = depthBiasConstantFactor;
429+
_depthBiasSlopeFactor = depthBiasSlopeFactor;
430+
_depthBiasClamp = depthBiasClamp;
431+
}
406432
}
407433

408434
void MVKDepthBiasCommandEncoderState::encodeImpl(uint32_t stage) {
@@ -426,12 +452,13 @@
426452
// Abort if we are using dynamic, but call is not dynamic.
427453
if ( !isDynamic && _cmdEncoder->supportsDynamicState(VK_DYNAMIC_STATE_BLEND_CONSTANTS) ) { return; }
428454

429-
_red = red;
430-
_green = green;
431-
_blue = blue;
432-
_alpha = alpha;
433-
434-
markDirty();
455+
if (_red != red || _green != green || _blue != blue || _alpha != alpha) {
456+
markDirty();
457+
_red = red;
458+
_green = green;
459+
_blue = blue;
460+
_alpha = alpha;
461+
}
435462
}
436463

437464
void MVKBlendColorCommandEncoderState::encodeImpl(uint32_t stage) {
@@ -752,6 +779,10 @@
752779
b.mtlBytes,
753780
b.size,
754781
b.index);
782+
else if (b.justOffset)
783+
[cmdEncoder->getMTLComputeEncoder(kMVKCommandUseTessellationVertexTessCtl)
784+
setBufferOffset: b.offset
785+
atIndex: b.index];
755786
else
756787
[cmdEncoder->getMTLComputeEncoder(kMVKCommandUseTessellationVertexTessCtl) setBuffer: b.mtlBuffer
757788
offset: b.offset
@@ -785,9 +816,14 @@
785816
b.size,
786817
b.index);
787818
} else {
788-
[cmdEncoder->_mtlRenderEncoder setVertexBuffer: b.mtlBuffer
789-
offset: b.offset
790-
atIndex: b.index];
819+
if (b.justOffset) {
820+
[cmdEncoder->_mtlRenderEncoder setVertexBufferOffset: b.offset
821+
atIndex: b.index];
822+
} else {
823+
[cmdEncoder->_mtlRenderEncoder setVertexBuffer: b.mtlBuffer
824+
offset: b.offset
825+
atIndex: b.index];
826+
}
791827

792828
// Add any translated vertex bindings for this binding
793829
auto xltdVtxBindings = pipeline->getTranslatedVertexBindings();
@@ -828,6 +864,9 @@
828864
b.mtlBytes,
829865
b.size,
830866
b.index);
867+
else if (b.justOffset)
868+
[cmdEncoder->getMTLComputeEncoder(kMVKCommandUseTessellationVertexTessCtl) setBufferOffset: b.offset
869+
atIndex: b.index];
831870
else
832871
[cmdEncoder->getMTLComputeEncoder(kMVKCommandUseTessellationVertexTessCtl) setBuffer: b.mtlBuffer
833872
offset: b.offset
@@ -858,6 +897,9 @@
858897
b.mtlBytes,
859898
b.size,
860899
b.index);
900+
else if (b.justOffset)
901+
[cmdEncoder->_mtlRenderEncoder setVertexBufferOffset: b.offset
902+
atIndex: b.index];
861903
else
862904
[cmdEncoder->_mtlRenderEncoder setVertexBuffer: b.mtlBuffer
863905
offset: b.offset
@@ -888,6 +930,9 @@
888930
b.mtlBytes,
889931
b.size,
890932
b.index);
933+
else if (b.justOffset)
934+
[cmdEncoder->_mtlRenderEncoder setFragmentBufferOffset: b.offset
935+
atIndex: b.index];
891936
else
892937
[cmdEncoder->_mtlRenderEncoder setFragmentBuffer: b.mtlBuffer
893938
offset: b.offset
@@ -1027,7 +1072,12 @@
10271072
b.mtlBytes,
10281073
b.size,
10291074
b.index);
1030-
} else {
1075+
} else if (b.justOffset) {
1076+
[cmdEncoder->getMTLComputeEncoder(kMVKCommandUseDispatch)
1077+
setBufferOffset: b.offset
1078+
atIndex: b.index];
1079+
1080+
} else {
10311081
[cmdEncoder->getMTLComputeEncoder(kMVKCommandUseDispatch) setBuffer: b.mtlBuffer
10321082
offset: b.offset
10331083
atIndex: b.index];

MoltenVK/MoltenVK/Commands/MVKMTLResourceBindings.h

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,13 +34,32 @@ typedef struct MVKMTLTextureBinding {
3434
uint32_t swizzle = 0;
3535
uint16_t index = 0;
3636
bool isDirty = true;
37+
38+
inline void markDirty() { isDirty = true; }
39+
40+
inline void update(const MVKMTLTextureBinding &other) {
41+
if (mtlTexture != other.mtlTexture || swizzle != other.swizzle) {
42+
mtlTexture = other.mtlTexture;
43+
swizzle = other.swizzle;
44+
markDirty();
45+
}
46+
}
3747
} MVKMTLTextureBinding;
3848

3949
/** Describes a MTLSamplerState resource binding. */
4050
typedef struct MVKMTLSamplerStateBinding {
4151
union { id<MTLSamplerState> mtlSamplerState = nil; id<MTLSamplerState> mtlResource; }; // aliases
4252
uint16_t index = 0;
4353
bool isDirty = true;
54+
55+
inline void markDirty() { isDirty = true; }
56+
57+
inline void update(const MVKMTLSamplerStateBinding &other) {
58+
if (mtlSamplerState != other.mtlSamplerState) {
59+
mtlSamplerState = other.mtlSamplerState;
60+
markDirty();
61+
}
62+
}
4463
} MVKMTLSamplerStateBinding;
4564

4665
/** Describes a MTLBuffer resource binding. */
@@ -49,8 +68,27 @@ typedef struct MVKMTLBufferBinding {
4968
VkDeviceSize offset = 0;
5069
uint32_t size = 0;
5170
uint16_t index = 0;
71+
bool justOffset = false;
5272
bool isDirty = true;
5373
bool isInline = false;
74+
75+
inline void markDirty() { justOffset = false; isDirty = true; }
76+
77+
inline void update(const MVKMTLBufferBinding &other) {
78+
if (mtlBuffer != other.mtlBuffer || size != other.size || isInline != other.isInline) {
79+
mtlBuffer = other.mtlBuffer;
80+
size = other.size;
81+
isInline = other.isInline;
82+
offset = other.offset;
83+
84+
justOffset = false;
85+
isDirty = true;
86+
} else if (offset != other.offset) {
87+
offset = other.offset;
88+
justOffset = !isDirty || justOffset;
89+
isDirty = true;
90+
}
91+
}
5492
} MVKMTLBufferBinding;
5593

5694
/** Describes a MTLBuffer resource binding as used for an index buffer. */

0 commit comments

Comments
 (0)