diff --git a/Source/Engine/Graphics/Shaders/GPUVertexLayout.cpp b/Source/Engine/Graphics/Shaders/GPUVertexLayout.cpp index 59e40483b..e3afae47e 100644 --- a/Source/Engine/Graphics/Shaders/GPUVertexLayout.cpp +++ b/Source/Engine/Graphics/Shaders/GPUVertexLayout.cpp @@ -206,47 +206,71 @@ GPUVertexLayout* GPUVertexLayout::Get(const Span& 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; diff --git a/Source/Engine/Graphics/Shaders/GPUVertexLayout.h b/Source/Engine/Graphics/Shaders/GPUVertexLayout.h index 9212626a3..e804b087e 100644 --- a/Source/Engine/Graphics/Shaders/GPUVertexLayout.h +++ b/Source/Engine/Graphics/Shaders/GPUVertexLayout.h @@ -74,8 +74,10 @@ public: /// /// The list of vertex buffers for the layout. /// The list of reference inputs. + /// True to remove elements from base layout that don't exist in a reference layout. + /// True to add missing elements to base layout that exist in a reference layout. /// Vertex layout object. Doesn't need to be cleared as it's cached for an application lifetime. - static GPUVertexLayout* Merge(GPUVertexLayout* base, GPUVertexLayout* reference); + static GPUVertexLayout* Merge(GPUVertexLayout* base, GPUVertexLayout* reference, bool removeUnused = false, bool addMissing = true); public: // [GPUResource] diff --git a/Source/Engine/GraphicsDevice/Vulkan/GPUContextVulkan.cpp b/Source/Engine/GraphicsDevice/Vulkan/GPUContextVulkan.cpp index ebf95f7cc..02dc86770 100644 --- a/Source/Engine/GraphicsDevice/Vulkan/GPUContextVulkan.cpp +++ b/Source/Engine/GraphicsDevice/Vulkan/GPUContextVulkan.cpp @@ -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& 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(); diff --git a/Source/Engine/GraphicsDevice/Vulkan/GPUDeviceVulkan.cpp b/Source/Engine/GraphicsDevice/Vulkan/GPUDeviceVulkan.cpp index 21e653a36..efe6a3334 100644 --- a/Source/Engine/GraphicsDevice/Vulkan/GPUDeviceVulkan.cpp +++ b/Source/Engine/GraphicsDevice/Vulkan/GPUDeviceVulkan.cpp @@ -453,39 +453,12 @@ GPUVertexLayoutVulkan::GPUVertexLayoutVulkan(GPUDeviceVulkan* device, const Elem : GPUResourceVulkan(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; } diff --git a/Source/Engine/GraphicsDevice/Vulkan/GPUPipelineStateVulkan.cpp b/Source/Engine/GraphicsDevice/Vulkan/GPUPipelineStateVulkan.cpp index 32b34e4f6..fe96d8f10 100644 --- a/Source/Engine/GraphicsDevice/Vulkan/GPUPipelineStateVulkan.cpp +++ b/Source/Engine/GraphicsDevice/Vulkan/GPUPipelineStateVulkan.cpp @@ -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) { diff --git a/Source/Engine/GraphicsDevice/Vulkan/GPUPipelineStateVulkan.h b/Source/Engine/GraphicsDevice/Vulkan/GPUPipelineStateVulkan.h index 600cb1841..ada069bce 100644 --- a/Source/Engine/GraphicsDevice/Vulkan/GPUPipelineStateVulkan.h +++ b/Source/Engine/GraphicsDevice/Vulkan/GPUPipelineStateVulkan.h @@ -146,7 +146,8 @@ public: const DescriptorSetLayoutVulkan* DescriptorSetsLayout = nullptr; TypedDescriptorPoolSetVulkan* CurrentTypedDescriptorPoolSet = nullptr; - GPUVertexLayoutVulkan* VertexShaderLayout = nullptr; + GPUVertexLayoutVulkan* VertexInputLayout = nullptr; + GPUVertexLayoutVulkan* VertexBufferLayout = nullptr; Array DescriptorSetHandles; Array DynamicOffsets; diff --git a/Source/Engine/GraphicsDevice/Vulkan/GPUVertexLayoutVulkan.h b/Source/Engine/GraphicsDevice/Vulkan/GPUVertexLayoutVulkan.h index 67a05824f..32ab50804 100644 --- a/Source/Engine/GraphicsDevice/Vulkan/GPUVertexLayoutVulkan.h +++ b/Source/Engine/GraphicsDevice/Vulkan/GPUVertexLayoutVulkan.h @@ -15,9 +15,7 @@ class GPUVertexLayoutVulkan : public GPUResourceVulkan 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