Handle vertex attributes with zero stride

This commit is contained in:
Jan Sikorski 2020-06-04 10:03:04 +02:00
parent 6df700f6e7
commit fa63b92d5d

View File

@ -970,9 +970,17 @@ bool MVKGraphicsPipeline::addVertexInputToPipeline(MTLRenderPipelineDescriptor*
maxBinding = max(pVKVB->binding, maxBinding); maxBinding = max(pVKVB->binding, maxBinding);
uint32_t vbIdx = getMetalBufferIndexForVertexAttributeBinding(pVKVB->binding); uint32_t vbIdx = getMetalBufferIndexForVertexAttributeBinding(pVKVB->binding);
MTLVertexBufferLayoutDescriptor* vbDesc = plDesc.vertexDescriptor.layouts[vbIdx]; MTLVertexBufferLayoutDescriptor* vbDesc = plDesc.vertexDescriptor.layouts[vbIdx];
vbDesc.stride = (pVKVB->stride == 0) ? sizeof(simd::float4) : pVKVB->stride; // Vulkan allows zero stride but Metal doesn't. Default to float4 if (pVKVB->stride == 0) {
vbDesc.stepFunction = mvkMTLVertexStepFunctionFromVkVertexInputRate(pVKVB->inputRate); // Stride can't be 0, it will be set later to attributes' maximum offset + size
vbDesc.stepRate = 1; // to prevent it from being larger than the underlying buffer permits.
vbDesc.stride = 0;
vbDesc.stepFunction = MTLVertexStepFunctionConstant;
vbDesc.stepRate = 0;
} else {
vbDesc.stride = pVKVB->stride;
vbDesc.stepFunction = mvkMTLVertexStepFunctionFromVkVertexInputRate(pVKVB->inputRate);
vbDesc.stepRate = 1;
}
} }
} }
@ -1004,26 +1012,31 @@ bool MVKGraphicsPipeline::addVertexInputToPipeline(MTLRenderPipelineDescriptor*
// Vulkan allows offsets to exceed the buffer stride, but Metal doesn't. // Vulkan allows offsets to exceed the buffer stride, but Metal doesn't.
// If this is the case, fetch an a translated artificial buffer binding, using the same MTLBuffer, // If this is the case, fetch an a translated artificial buffer binding, using the same MTLBuffer,
// but that is translated so that the reduced VA offset fits into the binding stride. // but that is translated so that the reduced VA offset fits into the binding stride.
// Only check non-zero offsets, as it's common for both to be zero when step rate is instance. const VkVertexInputBindingDescription* pVKVB = pVI->pVertexBindingDescriptions;
if (vaOffset > 0) { for (uint32_t j = 0; j < vbCnt; j++, pVKVB++) {
const VkVertexInputBindingDescription* pVKVB = pVI->pVertexBindingDescriptions; if (pVKVB->binding == pVKVA->binding) {
for (uint32_t j = 0; j < vbCnt; j++, pVKVB++) { uint32_t attrSize = getPixelFormats()->getBytesPerBlock(pVKVA->format);
if (pVKVB->binding == pVKVA->binding) { if (pVKVB->stride == 0) {
if (pVKVB->stride && vaOffset >= pVKVB->stride) { // The step is set to constant, but we need to change stride to be non-zero for metal.
// Move vertex attribute offset into the stride. This vertex attribute may be // Look for the maximum offset + size to set as the stride.
// combined with other vertex attributes into the same translated buffer binding. uint32_t vbIdx = getMetalBufferIndexForVertexAttributeBinding(pVKVB->binding);
// But if the reduced offset combined with the vertex attribute size still won't MTLVertexBufferLayoutDescriptor* vbDesc = plDesc.vertexDescriptor.layouts[vbIdx];
// fit into the buffer binding stride, force the vertex attribute offset to zero, uint32_t strideLowBound = vaOffset + attrSize;
// effectively dedicating this vertex attribute to its own buffer binding. if (vbDesc.stride < strideLowBound) vbDesc.stride = strideLowBound;
uint32_t origOffset = vaOffset; } else if (vaOffset >= pVKVB->stride) {
vaOffset %= pVKVB->stride; // Move vertex attribute offset into the stride. This vertex attribute may be
if (vaOffset + getPixelFormats()->getBytesPerBlock(pVKVA->format) > pVKVB->stride) { // combined with other vertex attributes into the same translated buffer binding.
vaOffset = 0; // But if the reduced offset combined with the vertex attribute size still won't
} // fit into the buffer binding stride, force the vertex attribute offset to zero,
vaBinding = getTranslatedVertexBinding(vaBinding, origOffset - vaOffset, maxBinding); // effectively dedicating this vertex attribute to its own buffer binding.
uint32_t origOffset = vaOffset;
vaOffset %= pVKVB->stride;
if (vaOffset + attrSize > pVKVB->stride) {
vaOffset = 0;
} }
break; vaBinding = getTranslatedVertexBinding(vaBinding, origOffset - vaOffset, maxBinding);
} }
break;
} }
} }