summaryrefslogtreecommitdiffstats
path: root/Graphics/GraphicsEngine
diff options
context:
space:
mode:
authorassiduous <assiduous@diligentgraphics.com>2021-03-07 18:11:56 +0000
committerassiduous <assiduous@diligentgraphics.com>2021-03-19 00:38:16 +0000
commit176e4de5f14b5332d58f02dc6088ea751189ebba (patch)
treeb2ff0c4bbb60a71c170fa0877bcb93ede42694a7 /Graphics/GraphicsEngine
parentUnified resource signature handling by pipeline state in D3D12, Vk and GL (diff)
downloadDiligentCore-176e4de5f14b5332d58f02dc6088ea751189ebba.tar.gz
DiligentCore-176e4de5f14b5332d58f02dc6088ea751189ebba.zip
Reworked non-separable programs in GL; added more PSO and PRS validation
Diffstat (limited to 'Graphics/GraphicsEngine')
-rw-r--r--Graphics/GraphicsEngine/include/PipelineStateBase.hpp23
-rw-r--r--Graphics/GraphicsEngine/src/PipelineResourceSignatureBase.cpp33
-rw-r--r--Graphics/GraphicsEngine/src/PipelineStateBase.cpp111
3 files changed, 135 insertions, 32 deletions
diff --git a/Graphics/GraphicsEngine/include/PipelineStateBase.hpp b/Graphics/GraphicsEngine/include/PipelineStateBase.hpp
index dcdd82d9..e8743e0f 100644
--- a/Graphics/GraphicsEngine/include/PipelineStateBase.hpp
+++ b/Graphics/GraphicsEngine/include/PipelineStateBase.hpp
@@ -48,9 +48,19 @@
namespace Diligent
{
-void ValidateGraphicsPipelineCreateInfo(const GraphicsPipelineStateCreateInfo& CreateInfo) noexcept(false);
-void ValidateComputePipelineCreateInfo(const ComputePipelineStateCreateInfo& CreateInfo) noexcept(false);
-void ValidateRayTracingPipelineCreateInfo(IRenderDevice* pDevice, Uint32 MaxRecursion, const RayTracingPipelineStateCreateInfo& CreateInfo) noexcept(false);
+// Validates graphics pipeline create attributes and throws an exception in case of an error.
+void ValidateGraphicsPipelineCreateInfo(const GraphicsPipelineStateCreateInfo& CreateInfo,
+ const DeviceFeatures& Features) noexcept(false);
+
+// Validates compute pipeline create attributes and throws an exception in case of an error.
+void ValidateComputePipelineCreateInfo(const ComputePipelineStateCreateInfo& CreateInfo,
+ const DeviceFeatures& Features) noexcept(false);
+
+// Validates ray-tracing pipeline create attributes and throws an exception in case of an error.
+void ValidateRayTracingPipelineCreateInfo(IRenderDevice* pDevice,
+ Uint32 MaxRecursion,
+ const RayTracingPipelineStateCreateInfo& CreateInfo,
+ const DeviceFeatures& Features) noexcept(false);
/// Validates that pipeline resource description 'ResDesc' is compatible with the actual resource
/// attributes and throws an exception in case of an error.
@@ -138,7 +148,7 @@ public:
{
try
{
- ValidateGraphicsPipelineCreateInfo(GraphicsPipelineCI);
+ ValidateGraphicsPipelineCreateInfo(GraphicsPipelineCI, pDevice->GetDeviceCaps().Features);
}
catch (...)
{
@@ -162,7 +172,7 @@ public:
{
try
{
- ValidateComputePipelineCreateInfo(ComputePipelineCI);
+ ValidateComputePipelineCreateInfo(ComputePipelineCI, pDevice->GetDeviceCaps().Features);
}
catch (...)
{
@@ -186,7 +196,8 @@ public:
{
try
{
- ValidateRayTracingPipelineCreateInfo(pDevice, pDevice->GetProperties().MaxRayTracingRecursionDepth, RayTracingPipelineCI);
+ ValidateRayTracingPipelineCreateInfo(pDevice, pDevice->GetProperties().MaxRayTracingRecursionDepth,
+ RayTracingPipelineCI, pDevice->GetDeviceCaps().Features);
}
catch (...)
{
diff --git a/Graphics/GraphicsEngine/src/PipelineResourceSignatureBase.cpp b/Graphics/GraphicsEngine/src/PipelineResourceSignatureBase.cpp
index ae41e2ba..46eb61e6 100644
--- a/Graphics/GraphicsEngine/src/PipelineResourceSignatureBase.cpp
+++ b/Graphics/GraphicsEngine/src/PipelineResourceSignatureBase.cpp
@@ -64,16 +64,16 @@ void ValidatePipelineResourceSignatureDesc(const PipelineResourceSignatureDesc&
const auto& Res = Desc.Resources[i];
if (Res.Name == nullptr)
- LOG_PRS_ERROR_AND_THROW("Desc.Resources[", i, "].Name must not be null");
+ LOG_PRS_ERROR_AND_THROW("Desc.Resources[", i, "].Name must not be null.");
if (Res.Name[0] == '\0')
- LOG_PRS_ERROR_AND_THROW("Desc.Resources[", i, "].Name must not be empty");
+ LOG_PRS_ERROR_AND_THROW("Desc.Resources[", i, "].Name must not be empty.");
if (Res.ShaderStages == SHADER_TYPE_UNKNOWN)
- LOG_PRS_ERROR_AND_THROW("Desc.Resources[", i, "].ShaderStages must not be SHADER_TYPE_UNKNOWN");
+ LOG_PRS_ERROR_AND_THROW("Desc.Resources[", i, "].ShaderStages must not be SHADER_TYPE_UNKNOWN.");
if (Res.ArraySize == 0)
- LOG_PRS_ERROR_AND_THROW("Desc.Resources[", i, "].ArraySize must not be 0");
+ LOG_PRS_ERROR_AND_THROW("Desc.Resources[", i, "].ArraySize must not be 0.");
auto& UsedStages = ResourceShaderStages[Res.Name];
if ((UsedStages & Res.ShaderStages) != 0)
@@ -82,6 +82,15 @@ void ValidatePipelineResourceSignatureDesc(const PipelineResourceSignatureDesc&
"' specify overlapping shader stages. There may be multiple resources with the same name in different shader stages, "
"but the stages must not overlap.");
}
+ if (Features.SeparablePrograms == DEVICE_FEATURE_STATE_DISABLED && UsedStages != SHADER_TYPE_UNKNOWN)
+ {
+ LOG_PRS_ERROR_AND_THROW("This device does not support separable programs, but there are separate resources with the name '",
+ Res.Name, "' in shader stages ",
+ GetShaderStagesString(Res.ShaderStages), " and ",
+ GetShaderStagesString(UsedStages),
+ ". When separable programs are not supported, every resource is always shared between all stages. "
+ "Use distinct resource names for each stage or define a single resource for all stages.");
+ }
UsedStages |= Res.ShaderStages;
if ((Res.Flags & PIPELINE_RESOURCE_FLAG_RUNTIME_ARRAY) != 0 && !Features.ShaderResourceRuntimeArray)
@@ -174,10 +183,13 @@ void ValidatePipelineResourceSignatureDesc(const PipelineResourceSignatureDesc&
{
const auto& SamDesc = Desc.ImmutableSamplers[i];
if (SamDesc.SamplerOrTextureName == nullptr)
- LOG_PRS_ERROR_AND_THROW("Desc.ImmutableSamplers[", i, "].SamplerOrTextureName must not be null");
+ LOG_PRS_ERROR_AND_THROW("Desc.ImmutableSamplers[", i, "].SamplerOrTextureName must not be null.");
if (SamDesc.SamplerOrTextureName[0] == '\0')
- LOG_PRS_ERROR_AND_THROW("Desc.ImmutableSamplers[", i, "].SamplerOrTextureName must not be empty");
+ LOG_PRS_ERROR_AND_THROW("Desc.ImmutableSamplers[", i, "].SamplerOrTextureName must not be empty.");
+
+ if (SamDesc.ShaderStages == SHADER_TYPE_UNKNOWN)
+ LOG_PRS_ERROR_AND_THROW("Desc.ImmutableSamplers[", i, "].ShaderStages must not be SHADER_TYPE_UNKNOWN.");
auto& UsedStages = ImtblSamShaderStages[SamDesc.SamplerOrTextureName];
if ((UsedStages & SamDesc.ShaderStages) != 0)
@@ -186,6 +198,15 @@ void ValidatePipelineResourceSignatureDesc(const PipelineResourceSignatureDesc&
"' specify overlapping shader stages. There may be multiple immutable samplers with the same name in different shader stages, "
"but the stages must not overlap.");
}
+ if (Features.SeparablePrograms == DEVICE_FEATURE_STATE_DISABLED && UsedStages != SHADER_TYPE_UNKNOWN)
+ {
+ LOG_PRS_ERROR_AND_THROW("This device does not support separable programs, but there are separate immutable samplers with the name '",
+ SamDesc.SamplerOrTextureName, "' in shader stages ",
+ GetShaderStagesString(SamDesc.ShaderStages), " and ",
+ GetShaderStagesString(UsedStages),
+ ". When separable programs are not supported, every resource is always shared between all stages. "
+ "Use distinct immutable sampler names for each stage or define a single sampler for all stages.");
+ }
UsedStages |= SamDesc.ShaderStages;
}
}
diff --git a/Graphics/GraphicsEngine/src/PipelineStateBase.cpp b/Graphics/GraphicsEngine/src/PipelineStateBase.cpp
index 4c095669..c7cb1427 100644
--- a/Graphics/GraphicsEngine/src/PipelineStateBase.cpp
+++ b/Graphics/GraphicsEngine/src/PipelineStateBase.cpp
@@ -30,7 +30,6 @@
#include <unordered_set>
#include <unordered_map>
#include <array>
-#include <vector>
#include "HashUtils.hpp"
#include "StringTools.hpp"
@@ -160,7 +159,8 @@ void CorrectBlendStateDesc(GraphicsPipelineDesc& GraphicsPipeline) noexcept
}
-void ValidatePipelineResourceSignatures(const PipelineStateCreateInfo& CreateInfo) noexcept(false)
+void ValidatePipelineResourceSignatures(const PipelineStateCreateInfo& CreateInfo,
+ const DeviceFeatures& Features) noexcept(false)
{
const auto& PSODesc = CreateInfo.PSODesc;
@@ -191,8 +191,8 @@ void ValidatePipelineResourceSignatures(const PipelineStateCreateInfo& CreateInf
}
- std::unordered_map<HashMapStringKey, std::vector<std::pair<SHADER_TYPE, const IPipelineResourceSignature*>>, HashMapStringKey::Hasher> AllResources;
- std::unordered_map<HashMapStringKey, std::vector<std::pair<SHADER_TYPE, const IPipelineResourceSignature*>>, HashMapStringKey::Hasher> AllImtblSamplers;
+ std::unordered_multimap<HashMapStringKey, std::pair<SHADER_TYPE, const IPipelineResourceSignature*>, HashMapStringKey::Hasher> AllResources;
+ std::unordered_multimap<HashMapStringKey, std::pair<SHADER_TYPE, const IPipelineResourceSignature*>, HashMapStringKey::Hasher> AllImtblSamplers;
std::array<const IPipelineResourceSignature*, MAX_RESOURCE_SIGNATURES> ppSignatures = {};
for (Uint32 i = 0; i < CreateInfo.ResourceSignaturesCount; ++i)
@@ -216,10 +216,13 @@ void ValidatePipelineResourceSignatures(const PipelineStateCreateInfo& CreateInf
for (Uint32 res = 0; res < SignDesc.NumResources; ++res)
{
const auto& ResDesc = SignDesc.Resources[res];
+ VERIFY(ResDesc.Name != nullptr && ResDesc.Name[0] != '\0', "Resource name can't be null or empty. This should've been caught by ValidatePipelineResourceSignatureDesc()");
+ VERIFY(ResDesc.ShaderStages != SHADER_TYPE_UNKNOWN, "Shader stages can't be UNKNOWN. This should've been caught by ValidatePipelineResourceSignatureDesc()");
- auto& StageSignatures = AllResources[ResDesc.Name];
- for (auto& StageSig : StageSignatures)
+ auto range = AllResources.equal_range(ResDesc.Name);
+ for (auto it = range.first; it != range.second; ++it)
{
+ const auto& StageSig = it->second;
if ((StageSig.first & ResDesc.ShaderStages) != 0)
{
VERIFY(StageSig.second != pSignature, "Overlapping resources in one signature should've been caught by ValidatePipelineResourceSignatureDesc()");
@@ -228,17 +231,31 @@ void ValidatePipelineResourceSignatures(const PipelineStateCreateInfo& CreateInf
"' and '", StageSig.second->GetDesc().Name,
"') in the same shader stage. Every shader resource in the PSO must be unambiguously defined by only one resource signature.");
}
+
+ if (Features.SeparablePrograms == DEVICE_FEATURE_STATE_DISABLED)
+ {
+ VERIFY_EXPR(StageSig.first != SHADER_TYPE_UNKNOWN);
+ VERIFY(StageSig.second != pSignature, "Resources with the same name in one signature should've been caught by ValidatePipelineResourceSignatureDesc()");
+
+ LOG_PSO_ERROR_AND_THROW("This device does not support separable programs, but shader resource '", ResDesc.Name, "' is found in more than one resource signature ('",
+ SignDesc.Name, "' and '", StageSig.second->GetDesc().Name,
+ "') in different stages. When separable programs are not supported, every resource is always shared between all stages. "
+ "Use distinct resource names for each stage or define a single resource for all stages.");
+ }
}
- StageSignatures.emplace_back(ResDesc.ShaderStages, pSignature);
+ AllResources.emplace(ResDesc.Name, std::make_pair(ResDesc.ShaderStages, pSignature));
}
for (Uint32 res = 0; res < SignDesc.NumImmutableSamplers; ++res)
{
const auto& SamDesc = SignDesc.ImmutableSamplers[res];
+ VERIFY(SamDesc.SamplerOrTextureName != nullptr && SamDesc.SamplerOrTextureName[0] != '\0', "Sampler name can't be null or empty. This should've been caught by ValidatePipelineResourceSignatureDesc()");
+ VERIFY(SamDesc.ShaderStages != SHADER_TYPE_UNKNOWN, "Shader stage can't be UNKNOWN. This should've been caught by ValidatePipelineResourceSignatureDesc()");
- auto& StageSignatures = AllImtblSamplers[SamDesc.SamplerOrTextureName];
- for (auto& StageSig : StageSignatures)
+ auto range = AllImtblSamplers.equal_range(SamDesc.SamplerOrTextureName);
+ for (auto it = range.first; it != range.second; ++it)
{
+ const auto& StageSig = it->second;
if ((StageSig.first & SamDesc.ShaderStages) != 0)
{
VERIFY(StageSig.second != pSignature, "Overlapping immutable samplers in one signature should've been caught by ValidatePipelineResourceSignatureDesc()");
@@ -247,13 +264,24 @@ void ValidatePipelineResourceSignatures(const PipelineStateCreateInfo& CreateInf
"' and '", StageSig.second->GetDesc().Name,
"') in the same stage. Every immutable sampler in the PSO must be unambiguously defined by only one resource signature.");
}
+
+ if (Features.SeparablePrograms == DEVICE_FEATURE_STATE_DISABLED)
+ {
+ VERIFY_EXPR(StageSig.first != SHADER_TYPE_UNKNOWN);
+ VERIFY(StageSig.second != pSignature, "Immutable samplers with the same name in one signature should've been caught by ValidatePipelineResourceSignatureDesc()");
+
+ LOG_PSO_ERROR_AND_THROW("This device does not support separable programs, but immutable sampler '", SamDesc.SamplerOrTextureName, "' is found in more than one resource signature ('",
+ SignDesc.Name, "' and '", StageSig.second->GetDesc().Name,
+ "') in different stages. When separable programs are not supported, every resource is always shared between all stages. "
+ "Use distinct resource names for each stage or define a single immutable sampler for all stages.");
+ }
}
- StageSignatures.emplace_back(SamDesc.ShaderStages, pSignature);
+ AllImtblSamplers.emplace(SamDesc.SamplerOrTextureName, std::make_pair(SamDesc.ShaderStages, pSignature));
}
}
}
-void ValidatePipelineResourceLayoutDesc(const PipelineStateDesc& PSODesc) noexcept(false)
+void ValidatePipelineResourceLayoutDesc(const PipelineStateDesc& PSODesc, const DeviceFeatures& Features) noexcept(false)
{
const auto& Layout = PSODesc.ResourceLayout;
{
@@ -262,6 +290,15 @@ void ValidatePipelineResourceLayoutDesc(const PipelineStateDesc& PSODesc) noexce
{
const auto& Var = Layout.Variables[i];
+ if (Var.Name == nullptr)
+ LOG_PSO_ERROR_AND_THROW("ResourceLayout.Variables[", i, "].Name must not be null.");
+
+ if (Var.Name[0] == '\0')
+ LOG_PSO_ERROR_AND_THROW("ResourceLayout.Variables[", i, "].Name must not be empty.");
+
+ if (Var.ShaderStages == SHADER_TYPE_UNKNOWN)
+ LOG_PSO_ERROR_AND_THROW("ResourceLayout.Variables[", i, "].ShaderStages must not be SHADER_TYPE_UNKNOWN.");
+
auto range = UniqueVariables.equal_range(Var.Name);
for (auto it = range.first; it != range.second; ++it)
{
@@ -271,6 +308,16 @@ void ValidatePipelineResourceLayoutDesc(const PipelineStateDesc& PSODesc) noexce
" and ", GetShaderStagesString(it->second),
"). Multiple variables with the same name are allowed, but shader stages they use must not overlap.");
}
+ if (Features.SeparablePrograms == DEVICE_FEATURE_STATE_DISABLED)
+ {
+ VERIFY_EXPR(it->second != SHADER_TYPE_UNKNOWN);
+ LOG_PSO_ERROR_AND_THROW("This device does not support separable programs, but there are separate resources with the name '",
+ Var.Name, "' in shader stages ",
+ GetShaderStagesString(Var.ShaderStages), " and ",
+ GetShaderStagesString(it->second),
+ ". When separable programs are not supported, every resource is always shared between all stages. "
+ "Use distinct resource names for each stage or define a single resource for all stages.");
+ }
}
UniqueVariables.emplace(Var.Name, Var.ShaderStages);
}
@@ -281,6 +328,15 @@ void ValidatePipelineResourceLayoutDesc(const PipelineStateDesc& PSODesc) noexce
{
const auto& Sam = Layout.ImmutableSamplers[i];
+ if (Sam.SamplerOrTextureName == nullptr)
+ LOG_PSO_ERROR_AND_THROW("ResourceLayout.ImmutableSamplers[", i, "].SamplerOrTextureName must not be null.");
+
+ if (Sam.SamplerOrTextureName[0] == '\0')
+ LOG_PSO_ERROR_AND_THROW("ResourceLayout.ImmutableSamplers[", i, "].SamplerOrTextureName must not be empty.");
+
+ if (Sam.ShaderStages == SHADER_TYPE_UNKNOWN)
+ LOG_PSO_ERROR_AND_THROW("ResourceLayout.ImmutableSamplers[", i, "].ShaderStages must not be SHADER_TYPE_UNKNOWN.");
+
auto range = UniqueSamplers.equal_range(Sam.SamplerOrTextureName);
for (auto it = range.first; it != range.second; ++it)
{
@@ -290,6 +346,16 @@ void ValidatePipelineResourceLayoutDesc(const PipelineStateDesc& PSODesc) noexce
" and ", GetShaderStagesString(it->second),
"). Multiple immutable samplers with the same name are allowed, but shader stages they use must not overlap.");
}
+ if (Features.SeparablePrograms == DEVICE_FEATURE_STATE_DISABLED)
+ {
+ VERIFY_EXPR(it->second != SHADER_TYPE_UNKNOWN);
+ LOG_PSO_ERROR_AND_THROW("This device does not support separable programs, but there are separate immutable samplers with the name '",
+ Sam.SamplerOrTextureName, "' in shader stages ",
+ GetShaderStagesString(Sam.ShaderStages), " and ",
+ GetShaderStagesString(it->second),
+ ". When separable programs are not supported, every resource is always shared between all stages. "
+ "Use distinct immutable sampler names for each stage or define a single sampler for all stages.");
+ }
}
UniqueSamplers.emplace(Sam.SamplerOrTextureName, Sam.ShaderStages);
}
@@ -304,20 +370,21 @@ void ValidatePipelineResourceLayoutDesc(const PipelineStateDesc& PSODesc) noexce
LOG_ERROR_AND_THROW(GetShaderTypeLiteralName(Shader->GetDesc().ShaderType), " is not a valid type for ", ShaderName, " shader"); \
}
-void ValidateGraphicsPipelineCreateInfo(const GraphicsPipelineStateCreateInfo& CreateInfo) noexcept(false)
+void ValidateGraphicsPipelineCreateInfo(const GraphicsPipelineStateCreateInfo& CreateInfo,
+ const DeviceFeatures& Features) noexcept(false)
{
const auto& PSODesc = CreateInfo.PSODesc;
if (PSODesc.PipelineType != PIPELINE_TYPE_GRAPHICS && PSODesc.PipelineType != PIPELINE_TYPE_MESH)
LOG_PSO_ERROR_AND_THROW("Pipeline type must be GRAPHICS or MESH.");
- ValidatePipelineResourceSignatures(CreateInfo);
+ ValidatePipelineResourceSignatures(CreateInfo, Features);
const auto& GraphicsPipeline = CreateInfo.GraphicsPipeline;
ValidateBlendStateDesc(PSODesc, GraphicsPipeline);
ValidateRasterizerStateDesc(PSODesc, GraphicsPipeline);
ValidateDepthStencilDesc(PSODesc, GraphicsPipeline);
- ValidatePipelineResourceLayoutDesc(PSODesc);
+ ValidatePipelineResourceLayoutDesc(PSODesc, Features);
if (PSODesc.PipelineType == PIPELINE_TYPE_GRAPHICS)
@@ -384,14 +451,15 @@ void ValidateGraphicsPipelineCreateInfo(const GraphicsPipelineStateCreateInfo& C
}
}
-void ValidateComputePipelineCreateInfo(const ComputePipelineStateCreateInfo& CreateInfo) noexcept(false)
+void ValidateComputePipelineCreateInfo(const ComputePipelineStateCreateInfo& CreateInfo,
+ const DeviceFeatures& Features) noexcept(false)
{
const auto& PSODesc = CreateInfo.PSODesc;
if (PSODesc.PipelineType != PIPELINE_TYPE_COMPUTE)
LOG_PSO_ERROR_AND_THROW("Pipeline type must be COMPUTE.");
- ValidatePipelineResourceSignatures(CreateInfo);
- ValidatePipelineResourceLayoutDesc(PSODesc);
+ ValidatePipelineResourceSignatures(CreateInfo, Features);
+ ValidatePipelineResourceLayoutDesc(PSODesc, Features);
if (CreateInfo.pCS == nullptr)
LOG_PSO_ERROR_AND_THROW("Compute shader must not be null.");
@@ -399,14 +467,17 @@ void ValidateComputePipelineCreateInfo(const ComputePipelineStateCreateInfo& Cre
VALIDATE_SHADER_TYPE(CreateInfo.pCS, SHADER_TYPE_COMPUTE, "compute")
}
-void ValidateRayTracingPipelineCreateInfo(IRenderDevice* pDevice, Uint32 MaxRecursion, const RayTracingPipelineStateCreateInfo& CreateInfo) noexcept(false)
+void ValidateRayTracingPipelineCreateInfo(IRenderDevice* pDevice,
+ Uint32 MaxRecursion,
+ const RayTracingPipelineStateCreateInfo& CreateInfo,
+ const DeviceFeatures& Features) noexcept(false)
{
const auto& PSODesc = CreateInfo.PSODesc;
if (PSODesc.PipelineType != PIPELINE_TYPE_RAY_TRACING)
LOG_PSO_ERROR_AND_THROW("Pipeline type must be RAY_TRACING.");
- ValidatePipelineResourceSignatures(CreateInfo);
- ValidatePipelineResourceLayoutDesc(PSODesc);
+ ValidatePipelineResourceSignatures(CreateInfo, Features);
+ ValidatePipelineResourceLayoutDesc(PSODesc, Features);
if (pDevice->GetDeviceCaps().DevType == RENDER_DEVICE_TYPE_D3D12)
{