Skip to content

Commit

Permalink
PBR Renderer: implemented primitive array without multi-draw through …
Browse files Browse the repository at this point in the history
…instance id
  • Loading branch information
TheMostDiligent committed Aug 23, 2024
1 parent 49d09f6 commit 5587710
Show file tree
Hide file tree
Showing 3 changed files with 115 additions and 53 deletions.
8 changes: 7 additions & 1 deletion Hydrogent/src/HnRenderDelegate.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -191,8 +191,14 @@ static std::shared_ptr<USD_Renderer> 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);
Expand Down
61 changes: 50 additions & 11 deletions Hydrogent/src/HnRenderPass.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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) :
Expand All @@ -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}
{
}

Expand Down Expand Up @@ -1034,23 +1036,60 @@ void HnRenderPass::RenderPendingDrawItems(RenderState& State)

if (ListItem.IndexBuffer != nullptr)
{
MultiDrawIndexedItem* pMultiDrawItems = reinterpret_cast<MultiDrawIndexedItem*>(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<MultiDrawIndexedItem*>(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<MultiDrawItem*>(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<MultiDrawItem*>(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
Expand Down
99 changes: 58 additions & 41 deletions PBR/src/PBR_Renderer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Expand Down Expand Up @@ -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
{
Expand All @@ -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();
Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -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");
}
}
}

Expand Down

0 comments on commit 5587710

Please sign in to comment.