From 5587710848f42789c7569435cf9bdce572286598 Mon Sep 17 00:00:00 2001 From: assiduous Date: Thu, 22 Aug 2024 22:15:43 -0700 Subject: [PATCH] PBR Renderer: implemented primitive array without multi-draw through instance id --- Hydrogent/src/HnRenderDelegate.cpp | 8 ++- Hydrogent/src/HnRenderPass.cpp | 61 ++++++++++++++---- PBR/src/PBR_Renderer.cpp | 99 +++++++++++++++++------------- 3 files changed, 115 insertions(+), 53 deletions(-) diff --git a/Hydrogent/src/HnRenderDelegate.cpp b/Hydrogent/src/HnRenderDelegate.cpp index 5359f12b..8e2c7e71 100644 --- a/Hydrogent/src/HnRenderDelegate.cpp +++ b/Hydrogent/src/HnRenderDelegate.cpp @@ -191,8 +191,14 @@ static std::shared_ptr CreateUSDRenderer(const HnRenderDelegate::C }; const auto& DeviceInfo = RenderDelegateCI.pDevice->GetDeviceInfo(); - if (DeviceInfo.Features.NativeMultiDraw && (DeviceInfo.IsVulkanDevice() || DeviceInfo.IsGLDevice())) + if (DeviceInfo.IsVulkanDevice() || + DeviceInfo.IsWebGPUDevice() || + (DeviceInfo.IsGLDevice() && DeviceInfo.Features.NativeMultiDraw)) + { + // When native multi-draw is not supported, we use instance id as a primitive id. + // Direct3D is currently not supported because SV_InstanceID is not offset by base instance. USDRendererCI.PrimitiveArraySize = RenderDelegateCI.MultiDrawBatchSize; + } USDRendererCI.InputLayout.LayoutElements = Inputs; USDRendererCI.InputLayout.NumElements = _countof(Inputs); diff --git a/Hydrogent/src/HnRenderPass.cpp b/Hydrogent/src/HnRenderPass.cpp index af04ea23..212495d4 100644 --- a/Hydrogent/src/HnRenderPass.cpp +++ b/Hydrogent/src/HnRenderPass.cpp @@ -113,6 +113,7 @@ struct HnRenderPass::RenderState const USD_Renderer::ALPHA_MODE AlphaMode; const Uint32 ConstantBufferOffsetAlignment; + const bool NativeMultiDrawSupported; RenderState(const HnRenderPass& _RenderPass, const HnRenderPassState& _RPState) : @@ -124,7 +125,8 @@ struct HnRenderPass::RenderState USDRenderer{*RenderDelegate.GetUSDRenderer()}, pCtx{RenderDelegate.GetDeviceContext()}, AlphaMode{MaterialTagToPbrAlphaMode(RenderPass.m_MaterialTag)}, - ConstantBufferOffsetAlignment{RenderDelegate.GetDevice()->GetAdapterInfo().Buffer.ConstantBufferOffsetAlignment} + ConstantBufferOffsetAlignment{RenderDelegate.GetDevice()->GetAdapterInfo().Buffer.ConstantBufferOffsetAlignment}, + NativeMultiDrawSupported{RenderDelegate.GetDevice()->GetDeviceInfo().Features.NativeMultiDraw == DEVICE_FEATURE_STATE_ENABLED} { } @@ -1034,23 +1036,60 @@ void HnRenderPass::RenderPendingDrawItems(RenderState& State) if (ListItem.IndexBuffer != nullptr) { - MultiDrawIndexedItem* pMultiDrawItems = reinterpret_cast(m_ScratchSpace.data()); - for (size_t i = 0; i < PendingItem.DrawCount; ++i) + if (State.NativeMultiDrawSupported) { - const auto& BatchItem = m_PendingDrawItems[item_idx + i].ListItem; - pMultiDrawItems[i] = {BatchItem.NumVertices, BatchItem.StartIndex, 0}; + MultiDrawIndexedItem* pMultiDrawItems = reinterpret_cast(m_ScratchSpace.data()); + for (size_t i = 0; i < PendingItem.DrawCount; ++i) + { + const DrawListItem& BatchItem = m_PendingDrawItems[item_idx + i].ListItem; + pMultiDrawItems[i] = {BatchItem.NumVertices, BatchItem.StartIndex, 0}; + } + State.pCtx->MultiDrawIndexed({PendingItem.DrawCount, pMultiDrawItems, VT_UINT32, DRAW_FLAG_VERIFY_ALL}); + } + else + { + for (size_t i = 0; i < PendingItem.DrawCount; ++i) + { + // When native multi-draw is not supported, we pass primitive ID as instance ID. + const DrawListItem& BatchItem = m_PendingDrawItems[item_idx + i].ListItem; + DrawIndexedAttribs Attribs{BatchItem.NumVertices, VT_UINT32, DRAW_FLAG_VERIFY_ALL}; + if (i > 0) + { + Attribs.Flags |= DRAW_FLAG_DYNAMIC_RESOURCE_BUFFERS_INTACT; + } + Attribs.FirstIndexLocation = BatchItem.StartIndex; + Attribs.FirstInstanceLocation = i; + State.pCtx->DrawIndexed(Attribs); + } } - State.pCtx->MultiDrawIndexed({PendingItem.DrawCount, pMultiDrawItems, VT_UINT32, DRAW_FLAG_VERIFY_ALL}); } else { - MultiDrawItem* pMultiDrawItems = reinterpret_cast(m_ScratchSpace.data()); - for (size_t i = 0; i < PendingItem.DrawCount; ++i) + if (State.NativeMultiDrawSupported) { - const auto& BatchItem = m_PendingDrawItems[item_idx + i].ListItem; - pMultiDrawItems[i] = {BatchItem.NumVertices, 0}; + MultiDrawItem* pMultiDrawItems = reinterpret_cast(m_ScratchSpace.data()); + for (size_t i = 0; i < PendingItem.DrawCount; ++i) + { + const DrawListItem& BatchItem = m_PendingDrawItems[item_idx + i].ListItem; + pMultiDrawItems[i] = {BatchItem.NumVertices, 0}; + } + State.pCtx->MultiDraw({PendingItem.DrawCount, pMultiDrawItems, DRAW_FLAG_VERIFY_ALL}); + } + else + { + // When native multi-draw is not supported, we pass primitive ID as instance ID. + for (size_t i = 0; i < PendingItem.DrawCount; ++i) + { + const DrawListItem& BatchItem = m_PendingDrawItems[item_idx + i].ListItem; + DrawAttribs Attribs{BatchItem.NumVertices, DRAW_FLAG_VERIFY_ALL}; + if (i > 0) + { + Attribs.Flags |= DRAW_FLAG_DYNAMIC_RESOURCE_BUFFERS_INTACT; + } + Attribs.FirstInstanceLocation = i; + State.pCtx->Draw(Attribs); + } } - State.pCtx->MultiDraw({PendingItem.DrawCount, pMultiDrawItems, DRAW_FLAG_VERIFY_ALL}); } } else diff --git a/PBR/src/PBR_Renderer.cpp b/PBR/src/PBR_Renderer.cpp index 2e7c188b..286e2d86 100644 --- a/PBR/src/PBR_Renderer.cpp +++ b/PBR/src/PBR_Renderer.cpp @@ -1104,27 +1104,35 @@ ShaderMacroHelper PBR_Renderer::DefineMacros(const PSOKey& Key) const if (m_Settings.PrimitiveArraySize > 0) { const char* PrimitiveID = nullptr; - if (m_Device.GetDeviceInfo().IsGLDevice()) + if (m_Device.GetDeviceInfo().Features.NativeMultiDraw) { + if (m_Device.GetDeviceInfo().IsGLDevice()) + { #if PLATFORM_EMSCRIPTEN - PrimitiveID = "gl_DrawID"; + PrimitiveID = "gl_DrawID"; #else - PrimitiveID = "gl_DrawIDARB"; + PrimitiveID = "gl_DrawIDARB"; #endif - } - else if (m_Device.GetDeviceInfo().IsVulkanDevice()) - { + } + else if (m_Device.GetDeviceInfo().IsVulkanDevice()) + { #ifdef HLSL2GLSL_CONVERTER_SUPPORTED - PrimitiveID = "gl_DrawID"; + PrimitiveID = "gl_DrawID"; #else - UNSUPPORTED("Primitive ID on Vulkan requires HLSL2GLSL converter"); - PrimitiveID = "0"; + UNSUPPORTED("Primitive ID on Vulkan requires HLSL2GLSL converter"); + PrimitiveID = "0"; #endif + } + else + { + UNEXPECTED("Native multi-draw is only expected in GL and Vulkan"); + PrimitiveID = "0"; + } } else { - UNSUPPORTED("Primitive ID is only supported in GL and Vulkan"); - PrimitiveID = "0"; + // Use instance ID as primitive ID + PrimitiveID = "int(VSIn.InstanceID)"; } Macros.Add("PRIMITIVE_ID", PrimitiveID); } @@ -1402,7 +1410,7 @@ void PBR_Renderer::GetVSInputStructAndLayout(PSO_FLAGS PSOFlags, } #endif VERIFY_EXPR(Attrib.Type == VT_FLOAT32); - ss << " " << std::setw(7) << "float" << Attrib.NumComponents << std::setw(9) << Attrib.Name << ": ATTRIB" << Attrib.Index << ";" << std::endl; + ss << " float" << Attrib.NumComponents << std::setw(9) << Attrib.Name << " : ATTRIB" << Attrib.Index << ";" << std::endl; } else { @@ -1419,6 +1427,12 @@ void PBR_Renderer::GetVSInputStructAndLayout(PSO_FLAGS PSOFlags, } } + if (m_Settings.PrimitiveArraySize > 0 && !m_Device.GetDeviceInfo().Features.NativeMultiDraw) + { + // Draw id is emulated using instance id + ss << " uint InstanceID : SV_InstanceID;" << std::endl; + } + ss << "};" << std::endl; VSInputStruct = ss.str(); @@ -1473,7 +1487,7 @@ std::string PBR_Renderer::GetVSOutputStruct(PSO_FLAGS PSOFlags, bool UseVkPointS } if (UsePrimitiveId) { - ss << " int PrimitiveID : PRIMITIVE_ID;" << std::endl; + ss << " int PrimitiveID : PRIM_ID;" << std::endl; } ss << "};" << std::endl; return ss.str(); @@ -1630,39 +1644,42 @@ void PBR_Renderer::CreatePSO(PsoHashMapType& PsoHashMap, std::string GLSLSource; if (m_Settings.PrimitiveArraySize > 0) { - if (m_Device.GetDeviceInfo().IsGLDevice()) + if (m_Device.GetDeviceInfo().Features.NativeMultiDraw) { - ShaderCI.GLSLExtensions = MultiDrawGLSLExtension; - } - else if (m_Device.GetDeviceInfo().IsVulkanDevice()) - { -#ifdef HLSL2GLSL_CONVERTER_SUPPORTED - // Since we use gl_DrawID in HLSL, we need to manually convert the shader to GLSL - HLSL2GLSLConverterImpl::ConversionAttribs Attribs; - Attribs.pSourceStreamFactory = ShaderCI.pShaderSourceStreamFactory; - Attribs.EntryPoint = ShaderCI.EntryPoint; - Attribs.ShaderType = ShaderCI.Desc.ShaderType; - Attribs.InputFileName = ShaderCI.FilePath; - Attribs.SamplerSuffix = UseCombinedSamplers ? ShaderCI.Desc.CombinedSamplerSuffix : ShaderDesc{}.CombinedSamplerSuffix; - Attribs.UseInOutLocationQualifiers = true; - Attribs.IncludeDefinitions = true; - - GLSLSource = HLSL2GLSLConverterImpl::GetInstance().Convert(Attribs); - if (GLSLSource.empty()) + if (m_Device.GetDeviceInfo().IsGLDevice()) { - UNEXPECTED("Failed to convert HLSL source to GLSL"); + ShaderCI.GLSLExtensions = MultiDrawGLSLExtension; } - ShaderCI.FilePath = nullptr; - ShaderCI.Source = GLSLSource.c_str(); - ShaderCI.SourceLength = GLSLSource.length(); - ShaderCI.SourceLanguage = SHADER_SOURCE_LANGUAGE_GLSL; + else if (m_Device.GetDeviceInfo().IsVulkanDevice()) + { +#ifdef HLSL2GLSL_CONVERTER_SUPPORTED + // Since we use gl_DrawID in HLSL, we need to manually convert the shader to GLSL + HLSL2GLSLConverterImpl::ConversionAttribs Attribs; + Attribs.pSourceStreamFactory = ShaderCI.pShaderSourceStreamFactory; + Attribs.EntryPoint = ShaderCI.EntryPoint; + Attribs.ShaderType = ShaderCI.Desc.ShaderType; + Attribs.InputFileName = ShaderCI.FilePath; + Attribs.SamplerSuffix = UseCombinedSamplers ? ShaderCI.Desc.CombinedSamplerSuffix : ShaderDesc{}.CombinedSamplerSuffix; + Attribs.UseInOutLocationQualifiers = true; + Attribs.IncludeDefinitions = true; + + GLSLSource = HLSL2GLSLConverterImpl::GetInstance().Convert(Attribs); + if (GLSLSource.empty()) + { + UNEXPECTED("Failed to convert HLSL source to GLSL"); + } + ShaderCI.FilePath = nullptr; + ShaderCI.Source = GLSLSource.c_str(); + ShaderCI.SourceLength = GLSLSource.length(); + ShaderCI.SourceLanguage = SHADER_SOURCE_LANGUAGE_GLSL; #else - UNSUPPORTED("Primitive array on Vulkan requires HLSL2GLSL converter"); + UNSUPPORTED("Primitive array on Vulkan requires HLSL2GLSL converter"); #endif - } - else - { - UNSUPPORTED("Primitive array is only supported in GL and Vulkan"); + } + else + { + UNEXPECTED("Native multi-draw is only expected in GL and Vulkan"); + } } }