Merge pull request #1068 from cdavis5e/zero-divisor-base-instance

Offset vertex buffers for attribute bindings with zero divisors.
This commit is contained in:
Bill Hollings 2020-09-24 17:37:19 -04:00 committed by GitHub
commit 18dd6fa7aa
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 57 additions and 1 deletions

View File

@ -145,6 +145,9 @@ void MVKCmdDraw::encode(MVKCommandEncoder* cmdEncoder) {
atIndex: pipeline->getOutputBufferIndex().stages[kMVKShaderStageVertex]];
}
[mtlTessCtlEncoder setStageInRegion: MTLRegionMake2D(_firstVertex, _firstInstance, _vertexCount, _instanceCount)];
// If there are vertex bindings with a zero vertex divisor, I need to offset them by
// _firstInstance * stride, since that is the expected behaviour for a divisor of 0.
cmdEncoder->_graphicsResourcesState.offsetZeroDivisorVertexBuffers(stage, pipeline, _firstInstance);
id<MTLComputePipelineState> vtxState = pipeline->getTessVertexStageState();
if (cmdEncoder->getDevice()->_pMetalFeatures->nonUniformThreadgroups) {
#if MVK_MACOS_OR_IOS
@ -245,6 +248,7 @@ void MVKCmdDraw::encode(MVKCommandEncoder* cmdEncoder) {
MVKRenderSubpass* subpass = cmdEncoder->getSubpass();
uint32_t viewCount = subpass->isMultiview() ? subpass->getViewCountInMetalPass(cmdEncoder->getMultiviewPassIndex()) : 1;
uint32_t instanceCount = _instanceCount * viewCount;
cmdEncoder->_graphicsResourcesState.offsetZeroDivisorVertexBuffers(stage, pipeline, _firstInstance);
if (cmdEncoder->_pDeviceMetalFeatures->baseVertexInstanceDrawing) {
[cmdEncoder->_mtlRenderEncoder drawPrimitives: cmdEncoder->_mtlPrimitiveType
vertexStart: _firstVertex
@ -341,6 +345,9 @@ void MVKCmdDrawIndexed::encode(MVKCommandEncoder* cmdEncoder) {
offset: idxBuffOffset
atIndex: pipeline->getIndirectParamsIndex().stages[kMVKShaderStageVertex]];
[mtlTessCtlEncoder setStageInRegion: MTLRegionMake2D(_vertexOffset, _firstInstance, _indexCount, _instanceCount)];
// If there are vertex bindings with a zero vertex divisor, I need to offset them by
// _firstInstance * stride, since that is the expected behaviour for a divisor of 0.
cmdEncoder->_graphicsResourcesState.offsetZeroDivisorVertexBuffers(stage, pipeline, _firstInstance);
id<MTLComputePipelineState> vtxState = ibb.mtlIndexType == MTLIndexTypeUInt16 ? pipeline->getTessVertexStageIndex16State() : pipeline->getTessVertexStageIndex32State();
if (cmdEncoder->getDevice()->_pMetalFeatures->nonUniformThreadgroups) {
#if MVK_MACOS_OR_IOS
@ -444,6 +451,7 @@ void MVKCmdDrawIndexed::encode(MVKCommandEncoder* cmdEncoder) {
MVKRenderSubpass* subpass = cmdEncoder->getSubpass();
uint32_t viewCount = subpass->isMultiview() ? subpass->getViewCountInMetalPass(cmdEncoder->getMultiviewPassIndex()) : 1;
uint32_t instanceCount = _instanceCount * viewCount;
cmdEncoder->_graphicsResourcesState.offsetZeroDivisorVertexBuffers(stage, pipeline, _firstInstance);
if (cmdEncoder->_pDeviceMetalFeatures->baseVertexInstanceDrawing) {
[cmdEncoder->_mtlRenderEncoder drawIndexedPrimitives: cmdEncoder->_mtlPrimitiveType
indexCount: _indexCount

View File

@ -25,6 +25,7 @@
#include <unordered_map>
class MVKCommandEncoder;
class MVKGraphicsPipeline;
class MVKOcclusionQueryPool;
struct MVKShaderImplicitRezBinding;
@ -508,6 +509,9 @@ public:
std::function<void(MVKCommandEncoder*, MVKMTLTextureBinding&)> bindTexture,
std::function<void(MVKCommandEncoder*, MVKMTLSamplerStateBinding&)> bindSampler);
/** 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);
#pragma mark Construction
/** Constructs this instance for the specified command encoder. */

View File

@ -610,6 +610,30 @@ void MVKGraphicsResourcesCommandEncoderState::encodeBindings(MVKShaderStage stag
encodeBinding<MVKMTLSamplerStateBinding>(shaderStage.samplerStateBindings, shaderStage.areSamplerStateBindingsDirty, bindSampler);
}
void MVKGraphicsResourcesCommandEncoderState::offsetZeroDivisorVertexBuffers(MVKGraphicsStage stage,
MVKGraphicsPipeline* pipeline,
uint32_t firstInstance) {
auto& shaderStage = _shaderStageResourceBindings[kMVKShaderStageVertex];
for (auto& binding : pipeline->getZeroDivisorVertexBindings()) {
uint32_t mtlBuffIdx = pipeline->getMetalBufferIndexForVertexAttributeBinding(binding.first);
auto iter = std::find_if(shaderStage.bufferBindings.begin(), shaderStage.bufferBindings.end(), [mtlBuffIdx](const MVKMTLBufferBinding& b) { return b.index == mtlBuffIdx; });
if (!iter) { continue; }
switch (stage) {
case kMVKGraphicsStageVertex:
[_cmdEncoder->getMTLComputeEncoder(kMVKCommandUseTessellationVertexTessCtl) setBufferOffset: iter->offset + firstInstance * binding.second
atIndex: mtlBuffIdx];
break;
case kMVKGraphicsStageRasterization:
[_cmdEncoder->_mtlRenderEncoder setVertexBufferOffset: iter->offset + firstInstance * binding.second
atIndex: mtlBuffIdx];
break;
default:
assert(false); // If we hit this, something went wrong.
break;
}
}
}
// Mark everything as dirty
void MVKGraphicsResourcesCommandEncoderState::markDirty() {
MVKCommandEncoderState::markDirty();

View File

@ -195,6 +195,9 @@ struct MVKTranslatedVertexBinding {
uint32_t translationOffset;
};
/** Describes a vertex buffer binding whose divisor is zero. */
typedef std::pair<uint32_t, uint32_t> MVKZeroDivisorVertexBinding;
typedef MVKSmallVector<MVKGraphicsStage, 4> MVKPiplineStages;
/** The number of dynamic states possible in Vulkan. */
@ -259,6 +262,9 @@ public:
/** Returns the collection of translated vertex bindings. */
MVKArrayRef<MVKTranslatedVertexBinding> getTranslatedVertexBindings() { return _translatedVertexBindings.contents(); }
/** Returns the collection of instance-rate vertex bindings whose divisor is zero, along with their strides. */
MVKArrayRef<MVKZeroDivisorVertexBinding> getZeroDivisorVertexBindings() { return _zeroDivisorVertexBindings.contents(); }
/** Constructs an instance for the device and parent (which may be NULL). */
MVKGraphicsPipeline(MVKDevice* device,
MVKPipelineCache* pipelineCache,
@ -306,6 +312,7 @@ protected:
MVKSmallVector<VkViewport, kMVKCachedViewportScissorCount> _viewports;
MVKSmallVector<VkRect2D, kMVKCachedViewportScissorCount> _scissors;
MVKSmallVector<MVKTranslatedVertexBinding> _translatedVertexBindings;
MVKSmallVector<MVKZeroDivisorVertexBinding> _zeroDivisorVertexBindings;
MTLComputePipelineDescriptor* _mtlTessVertexStageDesc = nil;
id<MTLFunction> _mtlTessVertexFunctions[3] = {nil, nil, nil};

View File

@ -1135,6 +1135,7 @@ bool MVKGraphicsPipeline::addVertexInputToPipeline(T* inputDesc,
}
// Vertex buffer divisors (step rates)
std::unordered_set<uint32_t> zeroDivisorBindings;
if (pVertexInputDivisorState) {
vbCnt = pVertexInputDivisorState->vertexBindingDivisorCount;
for (uint32_t i = 0; i < vbCnt; i++) {
@ -1143,8 +1144,10 @@ bool MVKGraphicsPipeline::addVertexInputToPipeline(T* inputDesc,
uint32_t vbIdx = getMetalBufferIndexForVertexAttributeBinding(pVKVB->binding);
if ((NSUInteger)inputDesc.layouts[vbIdx].stepFunction == MTLStepFunctionPerInstance ||
(NSUInteger)inputDesc.layouts[vbIdx].stepFunction == MTLStepFunctionThreadPositionInGridY) {
if (pVKVB->divisor == 0)
if (pVKVB->divisor == 0) {
inputDesc.layouts[vbIdx].stepFunction = (decltype(inputDesc.layouts[vbIdx].stepFunction))MTLStepFunctionConstant;
zeroDivisorBindings.insert(pVKVB->binding);
}
inputDesc.layouts[vbIdx].stepRate = pVKVB->divisor;
}
}
@ -1185,6 +1188,9 @@ bool MVKGraphicsPipeline::addVertexInputToPipeline(T* inputDesc,
vaOffset = 0;
}
vaBinding = getTranslatedVertexBinding(vaBinding, origOffset - vaOffset, maxBinding);
if (zeroDivisorBindings.count(pVKVB->binding)) {
zeroDivisorBindings.insert(vaBinding);
}
}
break;
}
@ -1224,6 +1230,13 @@ bool MVKGraphicsPipeline::addVertexInputToPipeline(T* inputDesc,
}
}
// Collect all bindings with zero divisors. We need to remember them so we can offset
// the vertex buffers during a draw.
for (uint32_t binding : zeroDivisorBindings) {
uint32_t stride = (uint32_t)inputDesc.layouts[getMetalBufferIndexForVertexAttributeBinding(binding)].stride;
_zeroDivisorVertexBindings.emplace_back(binding, stride);
}
return true;
}