diff --git a/Source/Engine/Graphics/GPUBuffer.cpp b/Source/Engine/Graphics/GPUBuffer.cpp index eca708af9..722518233 100644 --- a/Source/Engine/Graphics/GPUBuffer.cpp +++ b/Source/Engine/Graphics/GPUBuffer.cpp @@ -169,15 +169,22 @@ GPUBuffer::GPUBuffer() bool GPUBuffer::Init(const GPUBufferDescription& desc) { - ASSERT(Math::IsInRange(desc.Size, 1, MAX_int32) - && Math::IsInRange(desc.Stride, 0, 1024)); - // Validate description #if !BUILD_RELEASE #define GET_NAME() GetName() #else #define GET_NAME() TEXT("") #endif + if (Math::IsNotInRange(desc.Size, 1, MAX_int32)) + { + LOG(Warning, "Cannot create buffer '{}'. Incorrect size {}.", GET_NAME(), desc.Size); + return true; + } + if (Math::IsNotInRange(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) diff --git a/Source/Engine/Graphics/Shaders/GPUVertexLayout.cpp b/Source/Engine/Graphics/Shaders/GPUVertexLayout.cpp index 2791acc48..822766e67 100644 --- a/Source/Engine/Graphics/Shaders/GPUVertexLayout.cpp +++ b/Source/Engine/Graphics/Shaders/GPUVertexLayout.cpp @@ -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& 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 diff --git a/Source/Engine/Graphics/Shaders/GPUVertexLayout.h b/Source/Engine/Graphics/Shaders/GPUVertexLayout.h index 9e2c3454f..9de8e417b 100644 --- a/Source/Engine/Graphics/Shaders/GPUVertexLayout.h +++ b/Source/Engine/Graphics/Shaders/GPUVertexLayout.h @@ -83,8 +83,9 @@ public: /// 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. + /// Allows to override the input slot for missing elements. Use value -1 to inherit slot from the 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, bool removeUnused = false, bool addMissing = true); + static GPUVertexLayout* Merge(GPUVertexLayout* base, GPUVertexLayout* reference, bool removeUnused = false, bool addMissing = true, int32 missingSlotOverride = -1); public: // [GPUResource] diff --git a/Source/Engine/GraphicsDevice/Vulkan/GPUContextVulkan.cpp b/Source/Engine/GraphicsDevice/Vulkan/GPUContextVulkan.cpp index 02dc86770..89122a262 100644 --- a/Source/Engine/GraphicsDevice/Vulkan/GPUContextVulkan.cpp +++ b/Source/Engine/GraphicsDevice/Vulkan/GPUContextVulkan.cpp @@ -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 diff --git a/Source/Engine/GraphicsDevice/Vulkan/GPUDeviceVulkan.cpp b/Source/Engine/GraphicsDevice/Vulkan/GPUDeviceVulkan.cpp index efe6a3334..e662f7505 100644 --- a/Source/Engine/GraphicsDevice/Vulkan/GPUDeviceVulkan.cpp +++ b/Source/Engine/GraphicsDevice/Vulkan/GPUDeviceVulkan.cpp @@ -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; diff --git a/Source/Engine/GraphicsDevice/Vulkan/GPUDeviceVulkan.h b/Source/Engine/GraphicsDevice/Vulkan/GPUDeviceVulkan.h index 10a6c1b77..b2fa9a859 100644 --- a/Source/Engine/GraphicsDevice/Vulkan/GPUDeviceVulkan.h +++ b/Source/Engine/GraphicsDevice/Vulkan/GPUDeviceVulkan.h @@ -239,6 +239,7 @@ public: GPUDeviceVulkan* Device; VkRenderPass Handle; RenderTargetLayoutVulkan Layout; + bool CanDepthWrite; #if VULKAN_USE_DEBUG_DATA VkRenderPassCreateInfo DebugCreateInfo; #endif diff --git a/Source/Engine/GraphicsDevice/Vulkan/GPUPipelineStateVulkan.cpp b/Source/Engine/GraphicsDevice/Vulkan/GPUPipelineStateVulkan.cpp index fe96d8f10..573eee70c 100644 --- a/Source/Engine/GraphicsDevice/Vulkan/GPUPipelineStateVulkan.cpp +++ b/Source/Engine/GraphicsDevice/Vulkan/GPUPipelineStateVulkan.cpp @@ -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) {