Fixes for Vulkan backend after recent changes

This commit is contained in:
Wojtek Figat
2025-01-08 18:09:45 +01:00
parent 7e165d6127
commit 2b2ace0d00
7 changed files with 127 additions and 71 deletions

View File

@@ -206,47 +206,71 @@ GPUVertexLayout* GPUVertexLayout::Get(const Span<GPUVertexLayout*>& layouts)
return result;
}
GPUVertexLayout* GPUVertexLayout::Merge(GPUVertexLayout* base, GPUVertexLayout* reference)
GPUVertexLayout* GPUVertexLayout::Merge(GPUVertexLayout* base, GPUVertexLayout* reference, bool removeUnused, bool addMissing)
{
GPUVertexLayout* result = base ? base : reference;
if (base && reference && base != reference)
{
bool anyMissing = false;
const Elements& baseElements = base->GetElements();
Elements newElements = baseElements;
for (const VertexElement& e : reference->GetElements())
bool elementsModified = false;
Elements newElements = base->GetElements();
if (removeUnused)
{
bool missing = true;
for (const VertexElement& ee : baseElements)
for (int32 i = newElements.Count() - 1; i >= 0; i--)
{
if (ee.Type == e.Type)
bool missing = true;
const VertexElement& e = newElements.Get()[i];
for (const VertexElement& ee : reference->GetElements())
{
missing = false;
break;
}
}
if (missing)
{
// Insert any missing elements
VertexElement ne = { e.Type, 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
for (const VertexElement& ee : newElements)
if (ee.Type == e.Type)
{
if (ee.Type == VertexElement::Types::TexCoord0)
{
ne = ee;
ne.Type = e.Type;
break;
}
missing = false;
break;
}
}
newElements.Add(ne);
anyMissing = true;
if (missing)
{
// Remove unused element
newElements.RemoveAtKeepOrder(i);
elementsModified = true;
}
}
}
if (anyMissing)
if (addMissing)
{
for (const VertexElement& e : reference->GetElements())
{
bool missing = true;
for (const VertexElement& ee : base->GetElements())
{
if (ee.Type == e.Type)
{
missing = false;
break;
}
}
if (missing)
{
// Insert any missing elements
VertexElement ne = { e.Type, 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
for (const VertexElement& ee : newElements)
{
if (ee.Type == VertexElement::Types::TexCoord0)
{
ne = ee;
ne.Type = e.Type;
break;
}
}
}
newElements.Add(ne);
elementsModified = true;
}
}
}
if (elementsModified)
result = Get(newElements, true);
}
return result;

View File

@@ -74,8 +74,10 @@ public:
/// </summary>
/// <param name="base">The list of vertex buffers for the layout.</param>
/// <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>
/// <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);
static GPUVertexLayout* Merge(GPUVertexLayout* base, GPUVertexLayout* reference, bool removeUnused = false, bool addMissing = true);
public:
// [GPUResource]

View File

@@ -651,14 +651,14 @@ void GPUContextVulkan::OnDrawCall()
}
// Bind any missing vertex buffers to null if required by the current state
GPUVertexLayoutVulkan* vertexLayout = _vertexLayout ? _vertexLayout : pipelineState->VertexShaderLayout;
GPUVertexLayoutVulkan* vertexLayout = _vertexLayout ? _vertexLayout : pipelineState->VertexBufferLayout;
#if GPU_ENABLE_ASSERTION_LOW_LAYERS
if (!vertexLayout && pipelineState && !pipelineState->VertexShaderLayout && (pipelineState->UsedStagesMask & (1 << (int32)DescriptorSet::Vertex)) != 0 && !_vertexLayout && _vbCount)
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 ? (int32)vertexLayout->CreateInfo.vertexBindingDescriptionCount - _vbCount : 0;
const int32 missingVBs = vertexLayout ? vertexLayout->MaxSlot + 1 - _vbCount : 0;
if (missingVBs > 0)
{
VkBuffer buffers[GPU_MAX_VB_BINDED];
@@ -1034,7 +1034,13 @@ void GPUContextVulkan::BindUA(int32 slot, GPUResourceView* view)
void GPUContextVulkan::BindVB(const Span<GPUBuffer*>& vertexBuffers, const uint32* vertexBuffersOffsets, GPUVertexLayout* vertexLayout)
{
_vbCount = vertexBuffers.Length();
_vertexLayout = (GPUVertexLayoutVulkan*)(vertexLayout ? vertexLayout : GPUVertexLayout::Get(vertexBuffers));
if (!vertexLayout)
vertexLayout = GPUVertexLayout::Get(vertexBuffers);
if (_vertexLayout != vertexLayout)
{
_vertexLayout = (GPUVertexLayoutVulkan*)vertexLayout;
_psDirtyFlag = true;
}
if (vertexBuffers.Length() == 0)
return;
const auto cmdBuffer = _cmdBufferManager->GetCmdBuffer();

View File

@@ -453,39 +453,12 @@ GPUVertexLayoutVulkan::GPUVertexLayoutVulkan(GPUDeviceVulkan* device, const Elem
: GPUResourceVulkan<GPUVertexLayout>(device, StringView::Empty)
{
SetElements(elements, explicitOffsets);
for (int32 i = 0; i < GPU_MAX_VB_BINDED; i++)
{
VkVertexInputBindingDescription& binding = Bindings[i];
binding.binding = i;
binding.stride = 0;
binding.inputRate = VK_VERTEX_INPUT_RATE_VERTEX;
}
uint32 bindingsCount = 0;
MaxSlot = 0;
for (int32 i = 0; i < elements.Count(); i++)
{
const VertexElement& src = GetElements().Get()[i];
const int32 size = PixelFormatExtensions::SizeInBytes(src.Format);
ASSERT_LOW_LAYER(src.Slot < GPU_MAX_VB_BINDED);
VkVertexInputBindingDescription& binding = Bindings[src.Slot];
binding.binding = src.Slot;
binding.stride = Math::Max(binding.stride, (uint32_t)(src.Offset + size));
binding.inputRate = src.PerInstance ? VK_VERTEX_INPUT_RATE_INSTANCE : VK_VERTEX_INPUT_RATE_VERTEX;
VkVertexInputAttributeDescription& attribute = Attributes[i];
attribute.location = i;
attribute.binding = src.Slot;
attribute.format = RenderToolsVulkan::ToVulkanFormat(src.Format);
attribute.offset = src.Offset;
bindingsCount = Math::Max(bindingsCount, (uint32)src.Slot + 1);
MaxSlot = Math::Max(MaxSlot, (int32)src.Slot);
}
RenderToolsVulkan::ZeroStruct(CreateInfo, VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO);
CreateInfo.vertexBindingDescriptionCount = bindingsCount;
CreateInfo.pVertexBindingDescriptions = Bindings;
CreateInfo.vertexAttributeDescriptionCount = elements.Count();
CreateInfo.pVertexAttributeDescriptions = Attributes;
}
FramebufferVulkan::FramebufferVulkan(GPUDeviceVulkan* device, const Key& key, const VkExtent2D& extent, uint32 layers)
@@ -936,7 +909,8 @@ GPUBufferVulkan* HelperResourcesVulkan::GetDummyVertexBuffer()
if (!_dummyVB)
{
_dummyVB = (GPUBufferVulkan*)_device->CreateBuffer(TEXT("DummyVertexBuffer"));
_dummyVB->Init(GPUBufferDescription::Vertex(nullptr, sizeof(Color32), 1, &Color32::Transparent));
auto* layout = GPUVertexLayout::Get({{ VertexElement::Types::Attribute3, 0, 0, 0, PixelFormat::R8G8B8A8_UNorm }});
_dummyVB->Init(GPUBufferDescription::Vertex(layout, sizeof(Color32), 1, &Color32::Transparent));
}
return _dummyVB;
}

View File

@@ -10,6 +10,7 @@
#include "Engine/Core/Log.h"
#include "Engine/Core/Types/Pair.h"
#include "Engine/Profiler/ProfilerCPU.h"
#include "Engine/Graphics/PixelFormatExtensions.h"
static VkStencilOp ToVulkanStencilOp(const StencilOperation value)
{
@@ -222,8 +223,54 @@ VkPipeline GPUPipelineStateVulkan::GetState(RenderPassVulkan* renderPass, GPUVer
PROFILE_CPU_NAMED("Create Pipeline");
// Bind vertex input
vertexLayout = (GPUVertexLayoutVulkan*)GPUVertexLayout::Merge(vertexLayout, VertexShaderLayout);
_desc.pVertexInputState = vertexLayout ? &vertexLayout->CreateInfo : nullptr;
VkPipelineVertexInputStateCreateInfo vertexInputCreateInfo;
VkVertexInputBindingDescription vertexInputBindings[GPU_MAX_VB_BINDED];
VkVertexInputAttributeDescription vertexInputAttributes[GPU_MAX_VS_ELEMENTS];
_desc.pVertexInputState = nullptr;
if (!vertexLayout)
vertexLayout = VertexBufferLayout; // Fallback to shader-specified layout (if using old APIs)
if (vertexLayout)
{
// Vertex bindings based on vertex buffers assigned
for (int32 i = 0; i < GPU_MAX_VB_BINDED; i++)
{
VkVertexInputBindingDescription& binding = vertexInputBindings[i];
binding.binding = i;
binding.stride = 0;
binding.inputRate = VK_VERTEX_INPUT_RATE_VERTEX;
}
uint32 bindingsCount = 0;
for (int32 i = 0; i < vertexLayout->GetElements().Count(); i++)
{
const VertexElement& src = vertexLayout->GetElements().Get()[i];
const int32 size = PixelFormatExtensions::SizeInBytes(src.Format);
ASSERT_LOW_LAYER(src.Slot < GPU_MAX_VB_BINDED);
VkVertexInputBindingDescription& binding = vertexInputBindings[src.Slot];
binding.binding = src.Slot;
binding.stride = Math::Max(binding.stride, (uint32_t)(src.Offset + size));
binding.inputRate = src.PerInstance ? VK_VERTEX_INPUT_RATE_INSTANCE : VK_VERTEX_INPUT_RATE_VERTEX;
bindingsCount = Math::Max(bindingsCount, (uint32)src.Slot + 1);
}
// Vertex elements (including any merged elements from reference layout from shader reflection)
vertexLayout = (GPUVertexLayoutVulkan*)GPUVertexLayout::Merge(vertexLayout, VertexInputLayout, true, true);
for (int32 i = 0; i < vertexLayout->GetElements().Count(); i++)
{
const VertexElement& src = vertexLayout->GetElements().Get()[i];
VkVertexInputAttributeDescription& attribute = vertexInputAttributes[i];
attribute.location = i;
attribute.binding = src.Slot;
attribute.format = RenderToolsVulkan::ToVulkanFormat(src.Format);
attribute.offset = src.Offset;
}
RenderToolsVulkan::ZeroStruct(vertexInputCreateInfo, VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO);
vertexInputCreateInfo.vertexBindingDescriptionCount = bindingsCount;
vertexInputCreateInfo.pVertexBindingDescriptions = vertexInputBindings;
vertexInputCreateInfo.vertexAttributeDescriptionCount = vertexLayout->GetElements().Count();
vertexInputCreateInfo.pVertexAttributeDescriptions = vertexInputAttributes;
_desc.pVertexInputState = &vertexInputCreateInfo;
}
// Update description to match the pipeline
_descColorBlend.attachmentCount = renderPass->Layout.RTsCount;
@@ -320,7 +367,11 @@ bool GPUPipelineStateVulkan::Init(const Description& desc)
_desc.pStages = _shaderStages;
// Input Assembly
VertexShaderLayout = desc.VS ? (GPUVertexLayoutVulkan*)(desc.VS->Layout ? desc.VS->Layout : desc.VS->InputLayout) : nullptr;
if (desc.VS)
{
VertexInputLayout = (GPUVertexLayoutVulkan*)desc.VS->InputLayout;
VertexBufferLayout = (GPUVertexLayoutVulkan*)desc.VS->Layout;
}
RenderToolsVulkan::ZeroStruct(_descInputAssembly, VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO);;
switch (desc.PrimitiveTopology)
{

View File

@@ -146,7 +146,8 @@ public:
const DescriptorSetLayoutVulkan* DescriptorSetsLayout = nullptr;
TypedDescriptorPoolSetVulkan* CurrentTypedDescriptorPoolSet = nullptr;
GPUVertexLayoutVulkan* VertexShaderLayout = nullptr;
GPUVertexLayoutVulkan* VertexInputLayout = nullptr;
GPUVertexLayoutVulkan* VertexBufferLayout = nullptr;
Array<VkDescriptorSet> DescriptorSetHandles;
Array<uint32> DynamicOffsets;

View File

@@ -15,9 +15,7 @@ class GPUVertexLayoutVulkan : public GPUResourceVulkan<GPUVertexLayout>
public:
GPUVertexLayoutVulkan(GPUDeviceVulkan* device, const Elements& elements, bool explicitOffsets);
VkPipelineVertexInputStateCreateInfo CreateInfo;
VkVertexInputBindingDescription Bindings[GPU_MAX_VB_BINDED];
VkVertexInputAttributeDescription Attributes[GPU_MAX_VS_ELEMENTS];
int32 MaxSlot;
};
#endif