More fixes for Vulkan rendering to be on pair with DirectX when it comes to accessing missing vertex buffer components

This commit is contained in:
Wojtek Figat
2025-01-09 21:46:22 +01:00
parent 3505b8971b
commit 99788e4743
7 changed files with 54 additions and 24 deletions

View File

@@ -169,15 +169,22 @@ GPUBuffer::GPUBuffer()
bool GPUBuffer::Init(const GPUBufferDescription& desc)
{
ASSERT(Math::IsInRange<uint32>(desc.Size, 1, MAX_int32)
&& Math::IsInRange<uint32>(desc.Stride, 0, 1024));
// Validate description
#if !BUILD_RELEASE
#define GET_NAME() GetName()
#else
#define GET_NAME() TEXT("")
#endif
if (Math::IsNotInRange<uint32>(desc.Size, 1, MAX_int32))
{
LOG(Warning, "Cannot create buffer '{}'. Incorrect size {}.", GET_NAME(), desc.Size);
return true;
}
if (Math::IsNotInRange<uint32>(desc.Stride, 0, 1024))
{
LOG(Warning, "Cannot create buffer '{}'. Incorrect stride {}.", GET_NAME(), desc.Stride);
return true;
}
if (EnumHasAnyFlags(desc.Flags, GPUBufferFlags::Structured))
{
if (desc.Stride <= 0)

View File

@@ -96,12 +96,12 @@ GPUVertexLayout::GPUVertexLayout()
void GPUVertexLayout::SetElements(const Elements& elements, bool explicitOffsets)
{
uint32 offsets[GPU_MAX_VB_BINDED] = {};
uint32 offsets[GPU_MAX_VB_BINDED + 1] = {};
_elements = elements;
for (int32 i = 0; i < _elements.Count(); i++)
{
VertexElement& e = _elements[i];
ASSERT(e.Slot < GPU_MAX_VB_BINDED);
ASSERT(e.Slot <= GPU_MAX_VB_BINDED); // One special slot after all VBs for any missing vertex elements binding (on Vulkan)
uint32& offset = offsets[e.Slot];
if (e.Offset != 0 || explicitOffsets)
offset = e.Offset;
@@ -216,7 +216,7 @@ GPUVertexLayout* GPUVertexLayout::Get(const Span<GPUVertexLayout*>& layouts)
return result;
}
GPUVertexLayout* GPUVertexLayout::Merge(GPUVertexLayout* base, GPUVertexLayout* reference, bool removeUnused, bool addMissing)
GPUVertexLayout* GPUVertexLayout::Merge(GPUVertexLayout* base, GPUVertexLayout* reference, bool removeUnused, bool addMissing, int32 missingSlotOverride)
{
GPUVertexLayout* result = base ? base : reference;
if (base && reference && base != reference)
@@ -261,7 +261,7 @@ GPUVertexLayout* GPUVertexLayout::Merge(GPUVertexLayout* base, GPUVertexLayout*
if (missing)
{
// Insert any missing elements
VertexElement ne = { e.Type, e.Slot, 0, e.PerInstance, e.Format };
VertexElement ne = { e.Type, missingSlotOverride != -1 ? (byte)missingSlotOverride : e.Slot, 0, e.PerInstance, e.Format };
if (e.Type == VertexElement::Types::TexCoord1 || e.Type == VertexElement::Types::TexCoord2 || e.Type == VertexElement::Types::TexCoord3)
{
// Alias missing texcoords with existing texcoords

View File

@@ -83,8 +83,9 @@ public:
/// <param name="reference">The list of reference inputs.</param>
/// <param name="removeUnused">True to remove elements from base layout that don't exist in a reference layout.</param>
/// <param name="addMissing">True to add missing elements to base layout that exist in a reference layout.</param>
/// <param name="missingSlotOverride">Allows to override the input slot for missing elements. Use value -1 to inherit slot from the reference layout.</param>
/// <returns>Vertex layout object. Doesn't need to be cleared as it's cached for an application lifetime.</returns>
static GPUVertexLayout* Merge(GPUVertexLayout* base, GPUVertexLayout* reference, bool removeUnused = false, bool addMissing = true);
static GPUVertexLayout* Merge(GPUVertexLayout* base, GPUVertexLayout* reference, bool removeUnused = false, bool addMissing = true, int32 missingSlotOverride = -1);
public:
// [GPUResource]

View File

@@ -529,6 +529,8 @@ void GPUContextVulkan::UpdateDescriptorSets(const SpirvShaderDescriptorInfo& des
auto handle = handles[slot];
if (!handle)
{
if (descriptor.ResourceFormat == PixelFormat::Unknown)
break;
const auto dummy = _device->HelperResources.GetDummyBuffer(descriptor.ResourceFormat);
handle = (DescriptorOwnerResourceVulkan*)dummy->View()->GetNativePtr();
}
@@ -650,23 +652,14 @@ void GPUContextVulkan::OnDrawCall()
}
}
// Bind any missing vertex buffers to null if required by the current state
GPUVertexLayoutVulkan* vertexLayout = _vertexLayout ? _vertexLayout : pipelineState->VertexBufferLayout;
#if GPU_ENABLE_ASSERTION_LOW_LAYERS
// Check for missing vertex buffers layout
GPUVertexLayoutVulkan* vertexLayout = _vertexLayout ? _vertexLayout : pipelineState->VertexBufferLayout;
if (!vertexLayout && pipelineState && !pipelineState->VertexBufferLayout && (pipelineState->UsedStagesMask & (1 << (int32)DescriptorSet::Vertex)) != 0 && !_vertexLayout && _vbCount)
{
LOG(Error, "Missing Vertex Layout (not assigned to GPUBuffer). Vertex Shader won't read valid data resulting incorrect visuals.");
}
#endif
const int32 missingVBs = vertexLayout ? vertexLayout->MaxSlot + 1 - _vbCount : 0;
if (missingVBs > 0)
{
VkBuffer buffers[GPU_MAX_VB_BINDED];
VkDeviceSize offsets[GPU_MAX_VB_BINDED] = {};
for (int32 i = 0; i < missingVBs; i++)
buffers[i] = _device->HelperResources.GetDummyVertexBuffer()->GetHandle();
vkCmdBindVertexBuffers(cmdBuffer->GetHandle(), _vbCount, missingVBs, buffers, offsets);
}
// Start render pass if not during one
if (cmdBuffer->IsOutsideRenderPass())
@@ -734,6 +727,9 @@ void GPUContextVulkan::FrameBegin()
// Init command buffer
const auto cmdBuffer = _cmdBufferManager->GetCmdBuffer();
vkCmdSetStencilReference(cmdBuffer->GetHandle(), VK_STENCIL_FRONT_AND_BACK, _stencilRef);
VkBuffer buffers[1] = { _device->HelperResources.GetDummyVertexBuffer()->GetHandle() };
VkDeviceSize offsets[1] = {};
vkCmdBindVertexBuffers(cmdBuffer->GetHandle(), GPU_MAX_VB_BINDED, 1, buffers, offsets);
#if VULKAN_RESET_QUERY_POOLS
// Reset pending queries

View File

@@ -499,6 +499,7 @@ RenderPassVulkan::RenderPassVulkan(GPUDeviceVulkan* device, const RenderTargetLa
: Device(device)
, Handle(VK_NULL_HANDLE)
, Layout(layout)
, CanDepthWrite(true)
{
const int32 colorAttachmentsCount = layout.RTsCount;
const bool hasDepthStencilAttachment = layout.DepthFormat != PixelFormat::Unknown;
@@ -585,6 +586,8 @@ RenderPassVulkan::RenderPassVulkan(GPUDeviceVulkan* device, const RenderTargetLa
depthStencilReference.attachment = colorAttachmentsCount;
depthStencilReference.layout = depthStencilLayout;
subpassDesc.pDepthStencilAttachment = &depthStencilReference;
if (!layout.WriteDepth && !layout.WriteStencil)
CanDepthWrite = false;
}
VkRenderPassCreateInfo createInfo;

View File

@@ -239,6 +239,7 @@ public:
GPUDeviceVulkan* Device;
VkRenderPass Handle;
RenderTargetLayoutVulkan Layout;
bool CanDepthWrite;
#if VULKAN_USE_DEBUG_DATA
VkRenderPassCreateInfo DebugCreateInfo;
#endif

View File

@@ -224,7 +224,7 @@ VkPipeline GPUPipelineStateVulkan::GetState(RenderPassVulkan* renderPass, GPUVer
// Bind vertex input
VkPipelineVertexInputStateCreateInfo vertexInputCreateInfo;
VkVertexInputBindingDescription vertexInputBindings[GPU_MAX_VB_BINDED];
VkVertexInputBindingDescription vertexInputBindings[GPU_MAX_VB_BINDED + 1];
VkVertexInputAttributeDescription vertexInputAttributes[GPU_MAX_VS_ELEMENTS];
_desc.pVertexInputState = nullptr;
if (!vertexLayout)
@@ -253,12 +253,33 @@ VkPipeline GPUPipelineStateVulkan::GetState(RenderPassVulkan* renderPass, GPUVer
}
// Vertex elements (including any merged elements from reference layout from shader reflection)
vertexLayout = (GPUVertexLayoutVulkan*)GPUVertexLayout::Merge(vertexLayout, VertexInputLayout, true, true);
uint32 missingSlotBinding = bindingsCount;
int32 missingSlotOverride = GPU_MAX_VB_BINDED; // Use additional slot with empty VB
vertexLayout = (GPUVertexLayoutVulkan*)GPUVertexLayout::Merge(vertexLayout, VertexInputLayout, true, true, missingSlotOverride);
for (int32 i = 0; i < vertexLayout->GetElements().Count(); i++)
{
const VertexElement& src = vertexLayout->GetElements().Get()[i];
VkVertexInputAttributeDescription& attribute = vertexInputAttributes[i];
attribute.location = i;
if (VertexInputLayout)
{
// Sync locations with vertex shader inputs to ensure that shader will load correct attributes
const auto& vertexInputLayoutElements = VertexInputLayout->GetElements();
for (int32 j = 0; j < vertexInputLayoutElements.Count(); j++)
{
if (vertexInputLayoutElements.Get()[j].Type == src.Type)
{
attribute.location = j;
break;
}
}
}
if (src.Slot == missingSlotOverride)
{
// Element is missing and uses special empty VB
vertexInputBindings[missingSlotBinding] = { GPU_MAX_VB_BINDED, sizeof(byte[4]), VK_VERTEX_INPUT_RATE_INSTANCE };
bindingsCount = missingSlotBinding + 1;
}
attribute.binding = src.Slot;
attribute.format = RenderToolsVulkan::ToVulkanFormat(src.Format);
attribute.offset = src.Offset;
@@ -277,14 +298,15 @@ VkPipeline GPUPipelineStateVulkan::GetState(RenderPassVulkan* renderPass, GPUVer
_descMultisample.rasterizationSamples = (VkSampleCountFlagBits)renderPass->Layout.MSAA;
_desc.renderPass = renderPass->Handle;
// Check if has missing layout
// Ensure to have valid layout set
if (_desc.layout == VK_NULL_HANDLE)
{
_desc.layout = GetLayout()->Handle;
}
// Create object
auto depthWrite = _descDepthStencil.depthWriteEnable;
_descDepthStencil.depthWriteEnable &= renderPass->CanDepthWrite;
const VkResult result = vkCreateGraphicsPipelines(_device->Device, _device->PipelineCache, 1, &_desc, nullptr, &pipeline);
_descDepthStencil.depthWriteEnable = depthWrite;
LOG_VULKAN_RESULT(result);
if (result != VK_SUCCESS)
{