diff options
| author | azhirnov <zh1dron@gmail.com> | 2020-10-21 22:15:55 +0000 |
|---|---|---|
| committer | azhirnov <zh1dron@gmail.com> | 2020-10-25 10:50:53 +0000 |
| commit | 42217cc1ec7f81c028dcc5c1f845a2fc3b3da497 (patch) | |
| tree | 2efcb7c0fdeae582ecb5c8d13c7bedd593ab9a3c /Graphics/GraphicsEngine | |
| parent | Fixed Mac/iOS build (diff) | |
| parent | Updated Metal testing environment (diff) | |
| download | DiligentCore-42217cc1ec7f81c028dcc5c1f845a2fc3b3da497.tar.gz DiligentCore-42217cc1ec7f81c028dcc5c1f845a2fc3b3da497.zip | |
Merge branch 'master' into ray_tracing
# Conflicts:
# Graphics/GraphicsEngine/interface/PipelineState.h
# Graphics/GraphicsEngineVulkan/include/VulkanTypeConversions.hpp
# Graphics/GraphicsEngineVulkan/src/PipelineLayout.cpp
# Graphics/GraphicsEngineVulkan/src/VulkanTypeConversions.cpp
Diffstat (limited to 'Graphics/GraphicsEngine')
| -rw-r--r-- | Graphics/GraphicsEngine/CMakeLists.txt | 1 | ||||
| -rw-r--r-- | Graphics/GraphicsEngine/include/BufferBase.hpp | 10 | ||||
| -rw-r--r-- | Graphics/GraphicsEngine/include/DeviceContextBase.hpp | 10 | ||||
| -rw-r--r-- | Graphics/GraphicsEngine/include/PipelineStateBase.hpp | 753 | ||||
| -rw-r--r-- | Graphics/GraphicsEngine/include/ShaderResourceVariableBase.hpp | 78 | ||||
| -rw-r--r-- | Graphics/GraphicsEngine/include/TextureBase.hpp | 3 | ||||
| -rw-r--r-- | Graphics/GraphicsEngine/interface/APIInfo.h | 119 | ||||
| -rw-r--r-- | Graphics/GraphicsEngine/interface/GraphicsTypes.h | 43 | ||||
| -rw-r--r-- | Graphics/GraphicsEngine/interface/InputLayout.h | 2 | ||||
| -rw-r--r-- | Graphics/GraphicsEngine/interface/PipelineState.h | 138 | ||||
| -rw-r--r-- | Graphics/GraphicsEngine/interface/RenderDevice.h | 53 | ||||
| -rw-r--r-- | Graphics/GraphicsEngine/interface/Shader.h | 4 | ||||
| -rw-r--r-- | Graphics/GraphicsEngine/src/APIInfo.cpp | 5 | ||||
| -rw-r--r-- | Graphics/GraphicsEngine/src/BufferBase.cpp | 6 | ||||
| -rw-r--r-- | Graphics/GraphicsEngine/src/PipelineStateBase.cpp | 260 |
15 files changed, 874 insertions, 611 deletions
diff --git a/Graphics/GraphicsEngine/CMakeLists.txt b/Graphics/GraphicsEngine/CMakeLists.txt index 3162b6a0..47692deb 100644 --- a/Graphics/GraphicsEngine/CMakeLists.txt +++ b/Graphics/GraphicsEngine/CMakeLists.txt @@ -72,6 +72,7 @@ set(SOURCE src/DefaultShaderSourceStreamFactory.cpp src/EngineMemory.cpp src/FramebufferBase.cpp + src/PipelineStateBase.cpp src/ResourceMappingBase.cpp src/RenderPassBase.cpp src/TextureBase.cpp diff --git a/Graphics/GraphicsEngine/include/BufferBase.hpp b/Graphics/GraphicsEngine/include/BufferBase.hpp index 9ae6db92..605bc2ed 100644 --- a/Graphics/GraphicsEngine/include/BufferBase.hpp +++ b/Graphics/GraphicsEngine/include/BufferBase.hpp @@ -30,12 +30,14 @@ /// \file /// Implementation of the Diligent::BufferBase template class +#include <memory> + #include "Buffer.h" #include "GraphicsTypes.h" #include "DeviceObjectBase.hpp" #include "GraphicsAccessories.hpp" #include "STDAllocator.hpp" -#include <memory> +#include "FormatString.hpp" namespace Diligent { @@ -232,6 +234,9 @@ void BufferBase<BaseInterface, RenderDeviceImplType, BufferViewImplType, TBuffVi { BufferViewDesc ViewDesc; ViewDesc.ViewType = BUFFER_VIEW_UNORDERED_ACCESS; + auto UAVName = FormatString("Default UAV of buffer '", this->m_Desc.Name, '\''); + ViewDesc.Name = UAVName.c_str(); + IBufferView* pUAV = nullptr; CreateViewInternal(ViewDesc, &pUAV, true); m_pDefaultUAV.reset(static_cast<BufferViewImplType*>(pUAV)); @@ -242,6 +247,9 @@ void BufferBase<BaseInterface, RenderDeviceImplType, BufferViewImplType, TBuffVi { BufferViewDesc ViewDesc; ViewDesc.ViewType = BUFFER_VIEW_SHADER_RESOURCE; + auto SRVName = FormatString("Default SRV of buffer '", this->m_Desc.Name, '\''); + ViewDesc.Name = SRVName.c_str(); + IBufferView* pSRV = nullptr; CreateViewInternal(ViewDesc, &pSRV, true); m_pDefaultSRV.reset(static_cast<BufferViewImplType*>(pSRV)); diff --git a/Graphics/GraphicsEngine/include/DeviceContextBase.hpp b/Graphics/GraphicsEngine/include/DeviceContextBase.hpp index dd8851c7..c16219da 100644 --- a/Graphics/GraphicsEngine/include/DeviceContextBase.hpp +++ b/Graphics/GraphicsEngine/include/DeviceContextBase.hpp @@ -1672,6 +1672,13 @@ inline void DeviceContextBase<BaseInterface, ImplementationTraits>:: return; } + const auto& PSODesc = m_pPipelineState->GetDesc(); + if (!PSODesc.IsAnyGraphicsPipeline()) + { + LOG_ERROR_MESSAGE("Pipeline state '", PSODesc.Name, "' is not a graphics pipeline"); + return; + } + TEXTURE_FORMAT BoundRTVFormats[8] = {TEX_FORMAT_UNKNOWN}; TEXTURE_FORMAT BoundDSVFormat = TEX_FORMAT_UNKNOWN; @@ -1685,8 +1692,7 @@ inline void DeviceContextBase<BaseInterface, ImplementationTraits>:: BoundDSVFormat = m_pBoundDepthStencil ? m_pBoundDepthStencil->GetDesc().Format : TEX_FORMAT_UNKNOWN; - const auto& PSODesc = m_pPipelineState->GetDesc(); - const auto& GraphicsPipeline = PSODesc.GraphicsPipeline; + const auto& GraphicsPipeline = m_pPipelineState->GetGraphicsPipelineDesc(); if (GraphicsPipeline.NumRenderTargets != m_NumBoundRenderTargets) { LOG_WARNING_MESSAGE("The number of currently bound render targets (", m_NumBoundRenderTargets, diff --git a/Graphics/GraphicsEngine/include/PipelineStateBase.hpp b/Graphics/GraphicsEngine/include/PipelineStateBase.hpp index b54a2f76..1effd32f 100644 --- a/Graphics/GraphicsEngine/include/PipelineStateBase.hpp +++ b/Graphics/GraphicsEngine/include/PipelineStateBase.hpp @@ -38,11 +38,16 @@ #include "STDAllocator.hpp" #include "EngineMemory.h" #include "GraphicsAccessories.hpp" -#include "StringPool.hpp" +#include "LinearAllocator.hpp" namespace Diligent { +void ValidateGraphicsPipelineCreateInfo(const GraphicsPipelineStateCreateInfo& CreateInfo) noexcept(false); +void ValidateComputePipelineCreateInfo(const ComputePipelineStateCreateInfo& CreateInfo) noexcept(false); + +void CorrectGraphicsPipelineDesc(GraphicsPipelineDesc& GraphicsPipeline) noexcept; + /// Template class implementing base functionality for a pipeline state object. /// \tparam BaseInterface - base interface that this class will inheret @@ -57,298 +62,17 @@ class PipelineStateBase : public DeviceObjectBase<BaseInterface, RenderDeviceImp public: using TDeviceObjectBase = DeviceObjectBase<BaseInterface, RenderDeviceImplType, PipelineStateDesc>; - /// \param pRefCounters - reference counters object that controls the lifetime of this PSO - /// \param pDevice - pointer to the device. - /// \param PSODesc - pipeline state description. - /// \param bIsDeviceInternal - flag indicating if the blend state is an internal device object and + /// \param pRefCounters - Reference counters object that controls the lifetime of this PSO + /// \param pDevice - Pointer to the device. + /// \param CreateInfo - Pipeline state create info. + /// \param bIsDeviceInternal - Flag indicating if the pipeline state is an internal device object and /// must not keep a strong reference to the device. PipelineStateBase(IReferenceCounters* pRefCounters, RenderDeviceImplType* pDevice, const PipelineStateDesc& PSODesc, bool bIsDeviceInternal = false) : - TDeviceObjectBase{pRefCounters, pDevice, PSODesc, bIsDeviceInternal}, - m_NumShaders{0} + TDeviceObjectBase{pRefCounters, pDevice, PSODesc, bIsDeviceInternal} { - ValidateDesc(); - - const auto& SrcLayout = PSODesc.ResourceLayout; - size_t StringPoolSize = 0; - if (SrcLayout.Variables != nullptr) - { - for (Uint32 i = 0; i < SrcLayout.NumVariables; ++i) - StringPoolSize += strlen(SrcLayout.Variables[i].Name) + 1; - } - - if (SrcLayout.StaticSamplers != nullptr) - { - for (Uint32 i = 0; i < SrcLayout.NumStaticSamplers; ++i) - StringPoolSize += strlen(SrcLayout.StaticSamplers[i].SamplerOrTextureName) + 1; - } - - if (PSODesc.IsAnyGraphicsPipeline()) - { - CheckAndCorrectBlendStateDesc(); - CheckRasterizerStateDesc(); - CheckAndCorrectDepthStencilDesc(); - - const auto& InputLayout = PSODesc.GraphicsPipeline.InputLayout; - for (Uint32 i = 0; i < InputLayout.NumElements; ++i) - StringPoolSize += strlen(InputLayout.LayoutElements[i].HLSLSemantic) + 1; - } - else - { - DEV_CHECK_ERR(PSODesc.GraphicsPipeline.InputLayout.NumElements == 0, "Compute pipelines must not have input layout elements"); - } - - m_StringPool.Reserve(StringPoolSize, GetRawAllocator()); - - auto& DstLayout = this->m_Desc.ResourceLayout; - if (SrcLayout.Variables != nullptr) - { - ShaderResourceVariableDesc* Variables = - ALLOCATE(GetRawAllocator(), "Memory for ShaderResourceVariableDesc array", ShaderResourceVariableDesc, SrcLayout.NumVariables); - DstLayout.Variables = Variables; - for (Uint32 i = 0; i < SrcLayout.NumVariables; ++i) - { - VERIFY(SrcLayout.Variables[i].Name != nullptr, "Variable name can't be null"); - Variables[i] = SrcLayout.Variables[i]; - Variables[i].Name = m_StringPool.CopyString(SrcLayout.Variables[i].Name); - } - } - - if (SrcLayout.StaticSamplers != nullptr) - { - StaticSamplerDesc* StaticSamplers = - ALLOCATE(GetRawAllocator(), "Memory for StaticSamplerDesc array", StaticSamplerDesc, SrcLayout.NumStaticSamplers); - DstLayout.StaticSamplers = StaticSamplers; - for (Uint32 i = 0; i < SrcLayout.NumStaticSamplers; ++i) - { - VERIFY(SrcLayout.StaticSamplers[i].SamplerOrTextureName != nullptr, "Static sampler or texture name can't be null"); -#ifdef DILIGENT_DEVELOPMENT - { - const auto& BorderColor = SrcLayout.StaticSamplers[i].Desc.BorderColor; - if (!((BorderColor[0] == 0 && BorderColor[1] == 0 && BorderColor[2] == 0 && BorderColor[3] == 0) || - (BorderColor[0] == 0 && BorderColor[1] == 0 && BorderColor[2] == 0 && BorderColor[3] == 1) || - (BorderColor[0] == 1 && BorderColor[1] == 1 && BorderColor[2] == 1 && BorderColor[3] == 1))) - { - LOG_WARNING_MESSAGE("Static sampler for variable \"", SrcLayout.StaticSamplers[i].SamplerOrTextureName, "\" specifies border color (", - BorderColor[0], ", ", BorderColor[1], ", ", BorderColor[2], ", ", BorderColor[3], - "). D3D12 static samplers only allow transparent black (0,0,0,0), opaque black (0,0,0,1) or opaque white (1,1,1,1) as border colors"); - } - } -#endif - - StaticSamplers[i] = SrcLayout.StaticSamplers[i]; - StaticSamplers[i].SamplerOrTextureName = m_StringPool.CopyString(SrcLayout.StaticSamplers[i].SamplerOrTextureName); - } - } - - - if (this->m_Desc.IsComputePipeline()) - { - const auto& ComputePipeline = PSODesc.ComputePipeline; - if (ComputePipeline.pCS == nullptr) - { - LOG_ERROR_AND_THROW("Compute shader is not provided"); - } - -#define VALIDATE_SHADER_TYPE(Shader, ExpectedType, ShaderName) \ - if (Shader && Shader->GetDesc().ShaderType != ExpectedType) \ - { \ - LOG_ERROR_AND_THROW(GetShaderTypeLiteralName(Shader->GetDesc().ShaderType), " is not a valid type for ", ShaderName, " shader"); \ - } - - VALIDATE_SHADER_TYPE(ComputePipeline.pCS, SHADER_TYPE_COMPUTE, "compute") - - m_pCS = ComputePipeline.pCS; - m_ppShaders[0] = ComputePipeline.pCS; - m_NumShaders = 1; - } - else - { - const auto& GraphicsPipeline = PSODesc.GraphicsPipeline; - - VALIDATE_SHADER_TYPE(GraphicsPipeline.pVS, SHADER_TYPE_VERTEX, "vertex") - VALIDATE_SHADER_TYPE(GraphicsPipeline.pPS, SHADER_TYPE_PIXEL, "pixel") - VALIDATE_SHADER_TYPE(GraphicsPipeline.pGS, SHADER_TYPE_GEOMETRY, "geometry") - VALIDATE_SHADER_TYPE(GraphicsPipeline.pHS, SHADER_TYPE_HULL, "hull") - VALIDATE_SHADER_TYPE(GraphicsPipeline.pDS, SHADER_TYPE_DOMAIN, "domain") - VALIDATE_SHADER_TYPE(GraphicsPipeline.pAS, SHADER_TYPE_AMPLIFICATION, "amplification") - VALIDATE_SHADER_TYPE(GraphicsPipeline.pMS, SHADER_TYPE_MESH, "mesh") -#undef VALIDATE_SHADER_TYPE - - if (PSODesc.PipelineType == PIPELINE_TYPE_GRAPHICS) - { - DEV_CHECK_ERR(GraphicsPipeline.pVS, "Vertex shader must be defined"); - DEV_CHECK_ERR(!GraphicsPipeline.pAS && !GraphicsPipeline.pMS, "Mesh shaders are not supported in graphics pipeline"); - m_pVS = GraphicsPipeline.pVS; - m_pPS = GraphicsPipeline.pPS; - m_pGS = GraphicsPipeline.pGS; - m_pDS = GraphicsPipeline.pDS; - m_pHS = GraphicsPipeline.pHS; - } - else if (PSODesc.PipelineType == PIPELINE_TYPE_MESH) - { - DEV_CHECK_ERR(GraphicsPipeline.pMS, "Mesh shader must be defined"); - DEV_CHECK_ERR(!GraphicsPipeline.pVS && !GraphicsPipeline.pGS && !GraphicsPipeline.pDS && !GraphicsPipeline.pHS, - "Vertex, geometry and tessellation shaders are not supported in a mesh pipeline"); - DEV_CHECK_ERR(GraphicsPipeline.InputLayout.NumElements == 0, "Input layout ignored in mesh shader"); - DEV_CHECK_ERR(GraphicsPipeline.PrimitiveTopology == PRIMITIVE_TOPOLOGY_TRIANGLE_LIST || - GraphicsPipeline.PrimitiveTopology == PRIMITIVE_TOPOLOGY_UNDEFINED, - "Primitive topology is ignored in a mesh pipeline, set it to undefined or keep default value (triangle list)"); - m_pAS = GraphicsPipeline.pAS; - m_pMS = GraphicsPipeline.pMS; - m_pPS = GraphicsPipeline.pPS; - } - - if (GraphicsPipeline.pVS) m_ppShaders[m_NumShaders++] = GraphicsPipeline.pVS; - if (GraphicsPipeline.pPS) m_ppShaders[m_NumShaders++] = GraphicsPipeline.pPS; - if (GraphicsPipeline.pGS) m_ppShaders[m_NumShaders++] = GraphicsPipeline.pGS; - if (GraphicsPipeline.pHS) m_ppShaders[m_NumShaders++] = GraphicsPipeline.pHS; - if (GraphicsPipeline.pDS) m_ppShaders[m_NumShaders++] = GraphicsPipeline.pDS; - if (GraphicsPipeline.pAS) m_ppShaders[m_NumShaders++] = GraphicsPipeline.pAS; - if (GraphicsPipeline.pMS) m_ppShaders[m_NumShaders++] = GraphicsPipeline.pMS; - - DEV_CHECK_ERR(m_NumShaders > 0, "There must be at least one shader in the Pipeline State"); - - m_pRenderPass = PSODesc.GraphicsPipeline.pRenderPass; - - for (Uint32 rt = GraphicsPipeline.NumRenderTargets; rt < _countof(GraphicsPipeline.RTVFormats); ++rt) - { - auto RTVFmt = GraphicsPipeline.RTVFormats[rt]; - if (RTVFmt != TEX_FORMAT_UNKNOWN) - { - LOG_ERROR_MESSAGE("Render target format (", GetTextureFormatAttribs(RTVFmt).Name, ") of unused slot ", rt, - " must be set to TEX_FORMAT_UNKNOWN"); - } - } - - if (m_pRenderPass) - { - const auto& RPDesc = m_pRenderPass->GetDesc(); - VERIFY_EXPR(GraphicsPipeline.SubpassIndex < RPDesc.SubpassCount); - const auto& Subpass = RPDesc.pSubpasses[GraphicsPipeline.SubpassIndex]; - - this->m_Desc.GraphicsPipeline.NumRenderTargets = static_cast<Uint8>(Subpass.RenderTargetAttachmentCount); - for (Uint32 rt = 0; rt < Subpass.RenderTargetAttachmentCount; ++rt) - { - const auto& RTAttachmentRef = Subpass.pRenderTargetAttachments[rt]; - if (RTAttachmentRef.AttachmentIndex != ATTACHMENT_UNUSED) - { - VERIFY_EXPR(RTAttachmentRef.AttachmentIndex < RPDesc.AttachmentCount); - this->m_Desc.GraphicsPipeline.RTVFormats[rt] = RPDesc.pAttachments[RTAttachmentRef.AttachmentIndex].Format; - } - } - - if (Subpass.pDepthStencilAttachment != nullptr) - { - const auto& DSAttachmentRef = *Subpass.pDepthStencilAttachment; - if (DSAttachmentRef.AttachmentIndex != ATTACHMENT_UNUSED) - { - VERIFY_EXPR(DSAttachmentRef.AttachmentIndex < RPDesc.AttachmentCount); - this->m_Desc.GraphicsPipeline.DSVFormat = RPDesc.pAttachments[DSAttachmentRef.AttachmentIndex].Format; - } - } - } - - const auto& InputLayout = PSODesc.GraphicsPipeline.InputLayout; - LayoutElement* pLayoutElements = nullptr; - if (InputLayout.NumElements > 0) - { - pLayoutElements = ALLOCATE(GetRawAllocator(), "Raw memory for input layout elements", LayoutElement, InputLayout.NumElements); - } - this->m_Desc.GraphicsPipeline.InputLayout.LayoutElements = pLayoutElements; - for (size_t Elem = 0; Elem < InputLayout.NumElements; ++Elem) - { - pLayoutElements[Elem] = InputLayout.LayoutElements[Elem]; - pLayoutElements[Elem].HLSLSemantic = m_StringPool.CopyString(InputLayout.LayoutElements[Elem].HLSLSemantic); - } - - - // Correct description and compute offsets and tight strides - std::array<Uint32, MAX_BUFFER_SLOTS> Strides, TightStrides = {}; - // Set all strides to an invalid value because an application may want to use 0 stride - for (auto& Stride : Strides) - Stride = LAYOUT_ELEMENT_AUTO_STRIDE; - - for (Uint32 i = 0; i < InputLayout.NumElements; ++i) - { - auto& LayoutElem = pLayoutElements[i]; - - if (LayoutElem.ValueType == VT_FLOAT32 || LayoutElem.ValueType == VT_FLOAT16) - LayoutElem.IsNormalized = false; // Floating point values cannot be normalized - - auto BuffSlot = LayoutElem.BufferSlot; - if (BuffSlot >= Strides.size()) - { - UNEXPECTED("Buffer slot (", BuffSlot, ") exceeds maximum allowed value (", Strides.size() - 1, ")"); - continue; - } - m_BufferSlotsUsed = std::max(m_BufferSlotsUsed, BuffSlot + 1); - - auto& CurrAutoStride = TightStrides[BuffSlot]; - // If offset is not explicitly specified, use current auto stride value - if (LayoutElem.RelativeOffset == LAYOUT_ELEMENT_AUTO_OFFSET) - { - LayoutElem.RelativeOffset = CurrAutoStride; - } - - // If stride is explicitly specified, use it for the current buffer slot - if (LayoutElem.Stride != LAYOUT_ELEMENT_AUTO_STRIDE) - { - // Verify that the value is consistent with the previously specified stride, if any - if (Strides[BuffSlot] != LAYOUT_ELEMENT_AUTO_STRIDE && Strides[BuffSlot] != LayoutElem.Stride) - { - LOG_ERROR_MESSAGE("Inconsistent strides are specified for buffer slot ", BuffSlot, - ". Input element at index ", LayoutElem.InputIndex, " explicitly specifies stride ", - LayoutElem.Stride, ", while current value is ", Strides[BuffSlot], - ". Specify consistent strides or use LAYOUT_ELEMENT_AUTO_STRIDE to allow " - "the engine compute strides automatically."); - } - Strides[BuffSlot] = LayoutElem.Stride; - } - - CurrAutoStride = std::max(CurrAutoStride, LayoutElem.RelativeOffset + LayoutElem.NumComponents * GetValueSize(LayoutElem.ValueType)); - } - - for (Uint32 i = 0; i < InputLayout.NumElements; ++i) - { - auto& LayoutElem = pLayoutElements[i]; - - auto BuffSlot = LayoutElem.BufferSlot; - // If no input elements explicitly specified stride for this buffer slot, use automatic stride - if (Strides[BuffSlot] == LAYOUT_ELEMENT_AUTO_STRIDE) - { - Strides[BuffSlot] = TightStrides[BuffSlot]; - } - else - { - if (Strides[BuffSlot] < TightStrides[BuffSlot]) - { - LOG_ERROR_MESSAGE("Stride ", Strides[BuffSlot], " explicitly specified for slot ", BuffSlot, - " is smaller than the minimum stride ", TightStrides[BuffSlot], - " required to accomodate all input elements."); - } - } - if (LayoutElem.Stride == LAYOUT_ELEMENT_AUTO_STRIDE) - LayoutElem.Stride = Strides[BuffSlot]; - } - - if (m_BufferSlotsUsed > 0) - { - m_pStrides = ALLOCATE(GetRawAllocator(), "Raw memory for buffer strides", Uint32, m_BufferSlotsUsed); - - // Set strides for all unused slots to 0 - for (Uint32 i = 0; i < m_BufferSlotsUsed; ++i) - { - auto Stride = Strides[i]; - m_pStrides[i] = Stride != LAYOUT_ELEMENT_AUTO_STRIDE ? Stride : 0; - } - } - } - - VERIFY_EXPR(m_StringPool.GetRemainingSize() == 0); - Uint64 DeviceQueuesMask = pDevice->GetCommandQueueMask(); DEV_CHECK_ERR((this->m_Desc.CommandQueueMask & DeviceQueuesMask) != 0, "No bits in the command queue mask (0x", std::hex, this->m_Desc.CommandQueueMask, @@ -356,6 +80,35 @@ public: this->m_Desc.CommandQueueMask &= DeviceQueuesMask; } + /// \param pRefCounters - Reference counters object that controls the lifetime of this PSO + /// \param pDevice - Pointer to the device. + /// \param GraphicsPipelineCI - Graphics pipeline create information. + /// \param bIsDeviceInternal - Flag indicating if the pipeline state is an internal device object and + /// must not keep a strong reference to the device. + PipelineStateBase(IReferenceCounters* pRefCounters, + RenderDeviceImplType* pDevice, + const GraphicsPipelineStateCreateInfo& GraphicsPipelineCI, + bool bIsDeviceInternal = false) : + PipelineStateBase{pRefCounters, pDevice, GraphicsPipelineCI.PSODesc, bIsDeviceInternal} + { + ValidateGraphicsPipelineCreateInfo(GraphicsPipelineCI); + } + + /// \param pRefCounters - Reference counters object that controls the lifetime of this PSO + /// \param pDevice - Pointer to the device. + /// \param ComputePipelineCI - Compute pipeline create information. + /// \param bIsDeviceInternal - Flag indicating if the pipeline state is an internal device object and + /// must not keep a strong reference to the device. + PipelineStateBase(IReferenceCounters* pRefCounters, + RenderDeviceImplType* pDevice, + const ComputePipelineStateCreateInfo& ComputePipelineCI, + bool bIsDeviceInternal = false) : + PipelineStateBase{pRefCounters, pDevice, ComputePipelineCI.PSODesc, bIsDeviceInternal} + { + ValidateComputePipelineCreateInfo(ComputePipelineCI); + } + + ~PipelineStateBase() { /* @@ -375,16 +128,6 @@ public: RasterizerStateRegistry.ReportDeletedObject(); DSSRegistry.ReportDeletedObject(); */ - - auto& RawAllocator = GetRawAllocator(); - if (this->m_Desc.ResourceLayout.Variables != nullptr) - RawAllocator.Free(const_cast<ShaderResourceVariableDesc*>(this->m_Desc.ResourceLayout.Variables)); - if (this->m_Desc.ResourceLayout.StaticSamplers != nullptr) - RawAllocator.Free(const_cast<StaticSamplerDesc*>(this->m_Desc.ResourceLayout.StaticSamplers)); - if (this->m_Desc.GraphicsPipeline.InputLayout.LayoutElements != nullptr) - RawAllocator.Free(const_cast<LayoutElement*>(this->m_Desc.GraphicsPipeline.InputLayout.LayoutElements)); - if (m_pStrides != nullptr) - RawAllocator.Free(m_pStrides); } IMPLEMENT_QUERY_INTERFACE_IN_PLACE(IID_PipelineState, TDeviceObjectBase) @@ -399,28 +142,8 @@ public: return m_BufferSlotsUsed; } - IShader* GetVS() { return m_pVS; } - IShader* GetPS() { return m_pPS; } - IShader* GetGS() { return m_pGS; } - IShader* GetDS() { return m_pDS; } - IShader* GetHS() { return m_pHS; } - IShader* GetCS() { return m_pCS; } - - IShader* const* GetShaders() const { return m_ppShaders.data(); } - Uint32 GetNumShaders() const { return m_NumShaders; } - - template <typename ShaderType> - ShaderType* GetShader(Uint32 ShaderInd) - { - VERIFY_EXPR(ShaderInd < m_NumShaders); - return ValidatedCast<ShaderType>(m_ppShaders[ShaderInd]); - } - template <typename ShaderType> - ShaderType* GetShader(Uint32 ShaderInd) const - { - VERIFY_EXPR(ShaderInd < m_NumShaders); - return ValidatedCast<ShaderType>(m_ppShaders[ShaderInd]); - } + SHADER_TYPE GetShaderStageType(Uint32 Stage) const { return m_ShaderStageTypes[Stage]; } + Uint32 GetNumShaderStages() const { return m_NumShaderStages; } // This function only compares shader resource layout hashes, so // it can potentially give false negatives @@ -429,27 +152,12 @@ public: return m_ShaderResourceLayoutHash != ValidatedCast<const PipelineStateBase>(pPSO)->m_ShaderResourceLayoutHash; } -protected: - Uint32 m_BufferSlotsUsed = 0; - Uint32 m_NumShaders = 0; ///< Number of shaders that this PSO uses - Uint32* m_pStrides = nullptr; - - StringPool m_StringPool; - - RefCntAutoPtr<IShader> m_pVS; ///< Strong reference to the vertex shader - RefCntAutoPtr<IShader> m_pPS; ///< Strong reference to the pixel shader - RefCntAutoPtr<IShader> m_pGS; ///< Strong reference to the geometry shader - RefCntAutoPtr<IShader> m_pDS; ///< Strong reference to the domain shader - RefCntAutoPtr<IShader> m_pHS; ///< Strong reference to the hull shader - RefCntAutoPtr<IShader> m_pCS; ///< Strong reference to the compute shader - RefCntAutoPtr<IShader> m_pAS; ///< Strong reference to the amplification shader - RefCntAutoPtr<IShader> m_pMS; ///< Strong reference to the mesh shader - - RefCntAutoPtr<IRenderPass> m_pRenderPass; ///< Strong reference to the render pass object - - std::array<IShader*, MAX_SHADERS_IN_PIPELINE> m_ppShaders = {}; ///< Array of pointers to the shaders used by this PSO - size_t m_ShaderResourceLayoutHash = 0; ///< Hash computed from the shader resource layout - + virtual const GraphicsPipelineDesc& DILIGENT_CALL_TYPE GetGraphicsPipelineDesc() const override final + { + VERIFY_EXPR(this->m_Desc.IsAnyGraphicsPipeline()); + VERIFY_EXPR(m_pGraphicsPipelineDesc != nullptr); + return *m_pGraphicsPipelineDesc; + } protected: Int8 GetStaticVariableCountHelper(SHADER_TYPE ShaderType, const std::array<Int8, MAX_SHADERS_IN_PIPELINE>& ResourceLayoutIndex) const @@ -512,143 +220,312 @@ protected: return LayoutInd; } -private: -#define LOG_PSO_ERROR_AND_THROW(...) LOG_ERROR_AND_THROW("Description of ", GetPipelineTypeString(this->m_Desc.PipelineType), " PSO '", this->m_Desc.Name, "' is invalid: ", ##__VA_ARGS__) - void ValidateDesc() const + void ReserveSpaceForPipelineDesc(const GraphicsPipelineStateCreateInfo& CreateInfo, + LinearAllocator& MemPool) noexcept { - if (this->m_Desc.IsComputePipeline()) + MemPool.AddSpace<GraphicsPipelineDesc>(); + ReserveResourceLayout(CreateInfo.PSODesc.ResourceLayout, MemPool); + + const auto& InputLayout = CreateInfo.GraphicsPipeline.InputLayout; + MemPool.AddSpace<LayoutElement>(InputLayout.NumElements); + for (Uint32 i = 0; i < InputLayout.NumElements; ++i) { - if (this->m_Desc.GraphicsPipeline.pRenderPass != nullptr) + auto& LayoutElem = InputLayout.LayoutElements[i]; + MemPool.AddSpaceForString(LayoutElem.HLSLSemantic); + m_BufferSlotsUsed = std::max(m_BufferSlotsUsed, static_cast<Uint8>(LayoutElem.BufferSlot + 1)); + } + + MemPool.AddSpace<Uint32>(m_BufferSlotsUsed); + } + + void ReserveSpaceForPipelineDesc(const ComputePipelineStateCreateInfo& CreateInfo, + LinearAllocator& MemPool) const noexcept + { + ReserveResourceLayout(CreateInfo.PSODesc.ResourceLayout, MemPool); + } + + + template <typename ShaderImplType, typename TShaderStages> + void ExtractShaders(const GraphicsPipelineStateCreateInfo& CreateInfo, + TShaderStages& ShaderStages) + { + VERIFY(m_NumShaderStages == 0, "The number of shader stages is not zero! ExtractShaders must only be called once."); + + ShaderStages.clear(); + auto AddShaderStage = [&](IShader* pShader) { + if (pShader != nullptr) + { + auto ShaderType = pShader->GetDesc().ShaderType; + ShaderStages.emplace_back(ShaderType, ValidatedCast<ShaderImplType>(pShader)); + VERIFY(m_ShaderStageTypes[m_NumShaderStages] == SHADER_TYPE_UNKNOWN, "This shader stage is already initialized."); + m_ShaderStageTypes[m_NumShaderStages++] = ShaderType; + } + }; + + switch (CreateInfo.PSODesc.PipelineType) + { + case PIPELINE_TYPE_GRAPHICS: { - LOG_PSO_ERROR_AND_THROW("GraphicsPipeline.pRenderPass must be null for compute pipelines"); + AddShaderStage(CreateInfo.pVS); + AddShaderStage(CreateInfo.pHS); + AddShaderStage(CreateInfo.pDS); + AddShaderStage(CreateInfo.pGS); + AddShaderStage(CreateInfo.pPS); + VERIFY(CreateInfo.pVS != nullptr, "Vertex shader must not be null"); + break; } + + case PIPELINE_TYPE_MESH: + { + AddShaderStage(CreateInfo.pAS); + AddShaderStage(CreateInfo.pMS); + AddShaderStage(CreateInfo.pPS); + VERIFY(CreateInfo.pMS != nullptr, "Mesh shader must not be null"); + break; + } + + default: + UNEXPECTED("unknown pipeline type"); } - else + + VERIFY_EXPR(!ShaderStages.empty() && ShaderStages.size() == m_NumShaderStages); + } + + template <typename ShaderImplType, typename TShaderStages> + void ExtractShaders(const ComputePipelineStateCreateInfo& CreateInfo, + TShaderStages& ShaderStages) + { + VERIFY(m_NumShaderStages == 0, "The number of shader stages is not zero! ExtractShaders must only be called once."); + + ShaderStages.clear(); + + VERIFY_EXPR(CreateInfo.PSODesc.PipelineType == PIPELINE_TYPE_COMPUTE); + VERIFY_EXPR(CreateInfo.pCS != nullptr); + VERIFY_EXPR(CreateInfo.pCS->GetDesc().ShaderType == SHADER_TYPE_COMPUTE); + + ShaderStages.emplace_back(SHADER_TYPE_COMPUTE, ValidatedCast<ShaderImplType>(CreateInfo.pCS)); + m_ShaderStageTypes[m_NumShaderStages++] = SHADER_TYPE_COMPUTE; + + VERIFY_EXPR(!ShaderStages.empty() && ShaderStages.size() == m_NumShaderStages); + } + + + void InitializePipelineDesc(const GraphicsPipelineStateCreateInfo& CreateInfo, + LinearAllocator& MemPool) + { + this->m_pGraphicsPipelineDesc = MemPool.Copy(CreateInfo.GraphicsPipeline); + + auto& GraphicsPipeline = *this->m_pGraphicsPipelineDesc; + CorrectGraphicsPipelineDesc(GraphicsPipeline); + + CopyResourceLayout(CreateInfo.PSODesc.ResourceLayout, this->m_Desc.ResourceLayout, MemPool); + + m_pRenderPass = GraphicsPipeline.pRenderPass; + if (m_pRenderPass) { - const auto& GraphicsPipeline = this->m_Desc.GraphicsPipeline; - if (GraphicsPipeline.pRenderPass != nullptr) + const auto& RPDesc = m_pRenderPass->GetDesc(); + VERIFY_EXPR(GraphicsPipeline.SubpassIndex < RPDesc.SubpassCount); + const auto& Subpass = RPDesc.pSubpasses[GraphicsPipeline.SubpassIndex]; + + GraphicsPipeline.NumRenderTargets = static_cast<Uint8>(Subpass.RenderTargetAttachmentCount); + for (Uint32 rt = 0; rt < Subpass.RenderTargetAttachmentCount; ++rt) { - if (GraphicsPipeline.NumRenderTargets != 0) - LOG_PSO_ERROR_AND_THROW("NumRenderTargets must be 0 when explicit render pass is used"); - if (GraphicsPipeline.DSVFormat != TEX_FORMAT_UNKNOWN) - LOG_PSO_ERROR_AND_THROW("DSVFormat must be TEX_FORMAT_UNKNOWN when explicit render pass is used"); + const auto& RTAttachmentRef = Subpass.pRenderTargetAttachments[rt]; + if (RTAttachmentRef.AttachmentIndex != ATTACHMENT_UNUSED) + { + VERIFY_EXPR(RTAttachmentRef.AttachmentIndex < RPDesc.AttachmentCount); + GraphicsPipeline.RTVFormats[rt] = RPDesc.pAttachments[RTAttachmentRef.AttachmentIndex].Format; + } + } - for (Uint32 rt = 0; rt < MAX_RENDER_TARGETS; ++rt) + if (Subpass.pDepthStencilAttachment != nullptr) + { + const auto& DSAttachmentRef = *Subpass.pDepthStencilAttachment; + if (DSAttachmentRef.AttachmentIndex != ATTACHMENT_UNUSED) { - if (GraphicsPipeline.RTVFormats[rt] != TEX_FORMAT_UNKNOWN) - LOG_PSO_ERROR_AND_THROW("RTVFormats[", rt, "] must be TEX_FORMAT_UNKNOWN when explicit render pass is used"); + VERIFY_EXPR(DSAttachmentRef.AttachmentIndex < RPDesc.AttachmentCount); + GraphicsPipeline.DSVFormat = RPDesc.pAttachments[DSAttachmentRef.AttachmentIndex].Format; } + } + } + + const auto& InputLayout = GraphicsPipeline.InputLayout; + LayoutElement* pLayoutElements = MemPool.Allocate<LayoutElement>(InputLayout.NumElements); + for (size_t Elem = 0; Elem < InputLayout.NumElements; ++Elem) + { + const auto& SrcElem = InputLayout.LayoutElements[Elem]; + pLayoutElements[Elem] = SrcElem; + VERIFY_EXPR(SrcElem.HLSLSemantic != nullptr); + pLayoutElements[Elem].HLSLSemantic = MemPool.CopyString(SrcElem.HLSLSemantic); + } + GraphicsPipeline.InputLayout.LayoutElements = pLayoutElements; + + + // Correct description and compute offsets and tight strides + std::array<Uint32, MAX_BUFFER_SLOTS> Strides, TightStrides = {}; + // Set all strides to an invalid value because an application may want to use 0 stride + for (auto& Stride : Strides) + Stride = LAYOUT_ELEMENT_AUTO_STRIDE; + + for (Uint32 i = 0; i < InputLayout.NumElements; ++i) + { + auto& LayoutElem = pLayoutElements[i]; - const auto& RPDesc = GraphicsPipeline.pRenderPass->GetDesc(); - if (GraphicsPipeline.SubpassIndex >= RPDesc.SubpassCount) - LOG_PSO_ERROR_AND_THROW("Subpass index (", Uint32{GraphicsPipeline.SubpassIndex}, ") exceeds the number of subpasses (", Uint32{RPDesc.SubpassCount}, ") in render pass '", RPDesc.Name, "'"); + if (LayoutElem.ValueType == VT_FLOAT32 || LayoutElem.ValueType == VT_FLOAT16) + LayoutElem.IsNormalized = false; // Floating point values cannot be normalized + + auto BuffSlot = LayoutElem.BufferSlot; + if (BuffSlot >= Strides.size()) + { + UNEXPECTED("Buffer slot (", BuffSlot, ") exceeds the maximum allowed value (", Strides.size() - 1, ")"); + continue; + } + VERIFY_EXPR(BuffSlot < m_BufferSlotsUsed); + + auto& CurrAutoStride = TightStrides[BuffSlot]; + // If offset is not explicitly specified, use current auto stride value + if (LayoutElem.RelativeOffset == LAYOUT_ELEMENT_AUTO_OFFSET) + { + LayoutElem.RelativeOffset = CurrAutoStride; + } + + // If stride is explicitly specified, use it for the current buffer slot + if (LayoutElem.Stride != LAYOUT_ELEMENT_AUTO_STRIDE) + { + // Verify that the value is consistent with the previously specified stride, if any + if (Strides[BuffSlot] != LAYOUT_ELEMENT_AUTO_STRIDE && Strides[BuffSlot] != LayoutElem.Stride) + { + LOG_ERROR_MESSAGE("Inconsistent strides are specified for buffer slot ", BuffSlot, + ". Input element at index ", LayoutElem.InputIndex, " explicitly specifies stride ", + LayoutElem.Stride, ", while current value is ", Strides[BuffSlot], + ". Specify consistent strides or use LAYOUT_ELEMENT_AUTO_STRIDE to allow " + "the engine compute strides automatically."); + } + Strides[BuffSlot] = LayoutElem.Stride; + } + + CurrAutoStride = std::max(CurrAutoStride, LayoutElem.RelativeOffset + LayoutElem.NumComponents * GetValueSize(LayoutElem.ValueType)); + } + + for (Uint32 i = 0; i < InputLayout.NumElements; ++i) + { + auto& LayoutElem = pLayoutElements[i]; + + auto BuffSlot = LayoutElem.BufferSlot; + // If no input elements explicitly specified stride for this buffer slot, use automatic stride + if (Strides[BuffSlot] == LAYOUT_ELEMENT_AUTO_STRIDE) + { + Strides[BuffSlot] = TightStrides[BuffSlot]; } else { - if (GraphicsPipeline.SubpassIndex != 0) - LOG_PSO_ERROR_AND_THROW("Subpass index (", Uint32{GraphicsPipeline.SubpassIndex}, ") must be 0 when explicit render pass is not used"); + if (Strides[BuffSlot] < TightStrides[BuffSlot]) + { + LOG_ERROR_MESSAGE("Stride ", Strides[BuffSlot], " explicitly specified for slot ", BuffSlot, + " is smaller than the minimum stride ", TightStrides[BuffSlot], + " required to accomodate all input elements."); + } } + if (LayoutElem.Stride == LAYOUT_ELEMENT_AUTO_STRIDE) + LayoutElem.Stride = Strides[BuffSlot]; + } + + m_pStrides = MemPool.Allocate<Uint32>(m_BufferSlotsUsed); + + // Set strides for all unused slots to 0 + for (Uint32 i = 0; i < m_BufferSlotsUsed; ++i) + { + auto Stride = Strides[i]; + m_pStrides[i] = Stride != LAYOUT_ELEMENT_AUTO_STRIDE ? Stride : 0; } } - void CheckRasterizerStateDesc() const + void InitializePipelineDesc(const ComputePipelineStateCreateInfo& CreateInfo, + LinearAllocator& MemPool) { - const auto& RSDesc = this->m_Desc.GraphicsPipeline.RasterizerDesc; - if (RSDesc.FillMode == FILL_MODE_UNDEFINED) - LOG_PSO_ERROR_AND_THROW("RasterizerDesc.FillMode must not be FILL_MODE_UNDEFINED"); - if (RSDesc.CullMode == CULL_MODE_UNDEFINED) - LOG_PSO_ERROR_AND_THROW("RasterizerDesc.CullMode must not be CULL_MODE_UNDEFINED"); + CopyResourceLayout(CreateInfo.PSODesc.ResourceLayout, this->m_Desc.ResourceLayout, MemPool); } - void CheckAndCorrectDepthStencilDesc() +private: + static void ReserveResourceLayout(const PipelineResourceLayoutDesc& SrcLayout, LinearAllocator& MemPool) noexcept { - auto& DSSDesc = this->m_Desc.GraphicsPipeline.DepthStencilDesc; - if (DSSDesc.DepthFunc == COMPARISON_FUNC_UNKNOWN) + if (SrcLayout.Variables != nullptr) { - if (DSSDesc.DepthEnable) - LOG_PSO_ERROR_AND_THROW("DepthStencilDesc.DepthFunc must not be COMPARISON_FUNC_UNKNOWN when depth is enabled"); - else - DSSDesc.DepthFunc = DepthStencilStateDesc{}.DepthFunc; + MemPool.AddSpace<ShaderResourceVariableDesc>(SrcLayout.NumVariables); + for (Uint32 i = 0; i < SrcLayout.NumVariables; ++i) + { + VERIFY(SrcLayout.Variables[i].Name != nullptr, "Variable name can't be null"); + MemPool.AddSpaceForString(SrcLayout.Variables[i].Name); + } } - auto CheckAndCorrectStencilOpDesc = [&](StencilOpDesc& OpDesc, const char* FaceName) // + if (SrcLayout.ImmutableSamplers != nullptr) { - if (DSSDesc.StencilEnable) - { - if (OpDesc.StencilFailOp == STENCIL_OP_UNDEFINED) - LOG_PSO_ERROR_AND_THROW("DepthStencilDesc.", FaceName, ".StencilFailOp must not be STENCIL_OP_UNDEFINED when stencil is enabled"); - if (OpDesc.StencilDepthFailOp == STENCIL_OP_UNDEFINED) - LOG_PSO_ERROR_AND_THROW("DepthStencilDesc.", FaceName, ".StencilDepthFailOp must not be STENCIL_OP_UNDEFINED when stencil is enabled"); - if (OpDesc.StencilPassOp == STENCIL_OP_UNDEFINED) - LOG_PSO_ERROR_AND_THROW("DepthStencilDesc.", FaceName, ".StencilPassOp must not be STENCIL_OP_UNDEFINED when stencil is enabled"); - if (OpDesc.StencilFunc == COMPARISON_FUNC_UNKNOWN) - LOG_PSO_ERROR_AND_THROW("DepthStencilDesc.", FaceName, ".StencilFunc must not be COMPARISON_FUNC_UNKNOWN when stencil is enabled"); - } - else + MemPool.AddSpace<ImmutableSamplerDesc>(SrcLayout.NumImmutableSamplers); + for (Uint32 i = 0; i < SrcLayout.NumImmutableSamplers; ++i) { - if (OpDesc.StencilFailOp == STENCIL_OP_UNDEFINED) - OpDesc.StencilFailOp = StencilOpDesc{}.StencilFailOp; - if (OpDesc.StencilDepthFailOp == STENCIL_OP_UNDEFINED) - OpDesc.StencilDepthFailOp = StencilOpDesc{}.StencilDepthFailOp; - if (OpDesc.StencilPassOp == STENCIL_OP_UNDEFINED) - OpDesc.StencilPassOp = StencilOpDesc{}.StencilPassOp; - if (OpDesc.StencilFunc == COMPARISON_FUNC_UNKNOWN) - OpDesc.StencilFunc = StencilOpDesc{}.StencilFunc; + VERIFY(SrcLayout.ImmutableSamplers[i].SamplerOrTextureName != nullptr, "Immutable sampler or texture name can't be null"); + MemPool.AddSpaceForString(SrcLayout.ImmutableSamplers[i].SamplerOrTextureName); } - }; - CheckAndCorrectStencilOpDesc(DSSDesc.FrontFace, "FrontFace"); - CheckAndCorrectStencilOpDesc(DSSDesc.BackFace, "BackFace"); + } } - void CheckAndCorrectBlendStateDesc() + static void CopyResourceLayout(const PipelineResourceLayoutDesc& SrcLayout, PipelineResourceLayoutDesc& DstLayout, LinearAllocator& MemPool) { - auto& BlendDesc = this->m_Desc.GraphicsPipeline.BlendDesc; - for (Uint32 rt = 0; rt < MAX_RENDER_TARGETS; ++rt) + if (SrcLayout.Variables != nullptr) { - auto& RTDesc = BlendDesc.RenderTargets[rt]; - // clang-format off - const auto BlendEnable = RTDesc.BlendEnable && (rt == 0 || (BlendDesc.IndependentBlendEnable && rt > 0)); - const auto LogicOpEnable = RTDesc.LogicOperationEnable && (rt == 0 || (BlendDesc.IndependentBlendEnable && rt > 0)); - // clang-format on - if (BlendEnable) + auto* Variables = MemPool.Allocate<ShaderResourceVariableDesc>(SrcLayout.NumVariables); + DstLayout.Variables = Variables; + for (Uint32 i = 0; i < SrcLayout.NumVariables; ++i) { - if (RTDesc.SrcBlend == BLEND_FACTOR_UNDEFINED) - LOG_PSO_ERROR_AND_THROW("BlendDesc.RenderTargets[", rt, "].SrcBlend must not be BLEND_FACTOR_UNDEFINED"); - if (RTDesc.DestBlend == BLEND_FACTOR_UNDEFINED) - LOG_PSO_ERROR_AND_THROW("BlendDesc.RenderTargets[", rt, "].DestBlend must not be BLEND_FACTOR_UNDEFINED"); - if (RTDesc.BlendOp == BLEND_OPERATION_UNDEFINED) - LOG_PSO_ERROR_AND_THROW("BlendDesc.RenderTargets[", rt, "].BlendOp must not be BLEND_OPERATION_UNDEFINED"); - - if (RTDesc.SrcBlendAlpha == BLEND_FACTOR_UNDEFINED) - LOG_PSO_ERROR_AND_THROW("BlendDesc.RenderTargets[", rt, "].SrcBlendAlpha must not be BLEND_FACTOR_UNDEFINED"); - if (RTDesc.DestBlendAlpha == BLEND_FACTOR_UNDEFINED) - LOG_PSO_ERROR_AND_THROW("BlendDesc.RenderTargets[", rt, "].DestBlendAlpha must not be BLEND_FACTOR_UNDEFINED"); - if (RTDesc.BlendOpAlpha == BLEND_OPERATION_UNDEFINED) - LOG_PSO_ERROR_AND_THROW("BlendDesc.RenderTargets[", rt, "].BlendOpAlpha must not be BLEND_OPERATION_UNDEFINED"); + const auto& SrcVar = SrcLayout.Variables[i]; + Variables[i] = SrcVar; + Variables[i].Name = MemPool.CopyString(SrcVar.Name); } - else + } + + if (SrcLayout.ImmutableSamplers != nullptr) + { + auto* ImmutableSamplers = MemPool.Allocate<ImmutableSamplerDesc>(SrcLayout.NumImmutableSamplers); + DstLayout.ImmutableSamplers = ImmutableSamplers; + for (Uint32 i = 0; i < SrcLayout.NumImmutableSamplers; ++i) { - if (RTDesc.SrcBlend == BLEND_FACTOR_UNDEFINED) - RTDesc.SrcBlend = RenderTargetBlendDesc{}.SrcBlend; - if (RTDesc.DestBlend == BLEND_FACTOR_UNDEFINED) - RTDesc.DestBlend = RenderTargetBlendDesc{}.DestBlend; - if (RTDesc.BlendOp == BLEND_OPERATION_UNDEFINED) - RTDesc.BlendOp = RenderTargetBlendDesc{}.BlendOp; - - if (RTDesc.SrcBlendAlpha == BLEND_FACTOR_UNDEFINED) - RTDesc.SrcBlendAlpha = RenderTargetBlendDesc{}.SrcBlendAlpha; - if (RTDesc.DestBlendAlpha == BLEND_FACTOR_UNDEFINED) - RTDesc.DestBlendAlpha = RenderTargetBlendDesc{}.DestBlendAlpha; - if (RTDesc.BlendOpAlpha == BLEND_OPERATION_UNDEFINED) - RTDesc.BlendOpAlpha = RenderTargetBlendDesc{}.BlendOpAlpha; - } + const auto& SrcSmplr = SrcLayout.ImmutableSamplers[i]; +#ifdef DILIGENT_DEVELOPMENT + { + const auto& BorderColor = SrcSmplr.Desc.BorderColor; + if (!((BorderColor[0] == 0 && BorderColor[1] == 0 && BorderColor[2] == 0 && BorderColor[3] == 0) || + (BorderColor[0] == 0 && BorderColor[1] == 0 && BorderColor[2] == 0 && BorderColor[3] == 1) || + (BorderColor[0] == 1 && BorderColor[1] == 1 && BorderColor[2] == 1 && BorderColor[3] == 1))) + { + LOG_WARNING_MESSAGE("Immutable sampler for variable \"", SrcSmplr.SamplerOrTextureName, "\" specifies border color (", + BorderColor[0], ", ", BorderColor[1], ", ", BorderColor[2], ", ", BorderColor[3], + "). D3D12 static samplers only allow transparent black (0,0,0,0), opaque black (0,0,0,1) or opaque white (1,1,1,1) as border colors"); + } + } +#endif - if (!LogicOpEnable) - RTDesc.LogicOp = RenderTargetBlendDesc{}.LogicOp; + ImmutableSamplers[i] = SrcSmplr; + ImmutableSamplers[i].SamplerOrTextureName = MemPool.CopyString(SrcSmplr.SamplerOrTextureName); + } } } -#undef LOG_PSO_ERROR_AND_THROW + +protected: + size_t m_ShaderResourceLayoutHash = 0; ///< Hash computed from the shader resource layout + + Uint32* m_pStrides = nullptr; + Uint8 m_BufferSlotsUsed = 0; + + Uint8 m_NumShaderStages = 0; ///< Number of shader stages in this PSO + + /// Array of shader types for every shader stage used by this PSO + std::array<SHADER_TYPE, MAX_SHADERS_IN_PIPELINE> m_ShaderStageTypes = {}; + + RefCntAutoPtr<IRenderPass> m_pRenderPass; ///< Strong reference to the render pass object + + GraphicsPipelineDesc* m_pGraphicsPipelineDesc = nullptr; }; } // namespace Diligent diff --git a/Graphics/GraphicsEngine/include/ShaderResourceVariableBase.hpp b/Graphics/GraphicsEngine/include/ShaderResourceVariableBase.hpp index 5648e39d..1130fbff 100644 --- a/Graphics/GraphicsEngine/include/ShaderResourceVariableBase.hpp +++ b/Graphics/GraphicsEngine/include/ShaderResourceVariableBase.hpp @@ -32,6 +32,7 @@ #include <vector> +#include "Atomics.hpp" #include "ShaderResourceVariable.h" #include "PipelineState.h" #include "StringTools.hpp" @@ -114,15 +115,15 @@ inline Uint32 GetAllowedTypeBits(const SHADER_RESOURCE_VARIABLE_TYPE* AllowedVar return AllowedTypeBits; } -inline Int32 FindStaticSampler(const StaticSamplerDesc* StaticSamplers, - Uint32 NumStaticSamplers, - SHADER_TYPE ShaderType, - const char* ResourceName, - const char* SamplerSuffix) +inline Int32 FindImmutableSampler(const ImmutableSamplerDesc* ImtblSamplers, + Uint32 NumImtblSamplers, + SHADER_TYPE ShaderType, + const char* ResourceName, + const char* SamplerSuffix) { - for (Uint32 s = 0; s < NumStaticSamplers; ++s) + for (Uint32 s = 0; s < NumImtblSamplers; ++s) { - const auto& StSam = StaticSamplers[s]; + const auto& StSam = ImtblSamplers[s]; if (((StSam.ShaderStages & ShaderType) != 0) && StreqSuff(ResourceName, StSam.SamplerOrTextureName, SamplerSuffix)) return s; } @@ -214,6 +215,28 @@ inline const char* GetResourceTypeName<BUFFER_VIEW_TYPE>() return "buffer view"; } +inline RESOURCE_DIMENSION GetResourceViewDimension(const ITextureView* pTexView) +{ + VERIFY_EXPR(pTexView != nullptr); + return pTexView->GetDesc().TextureDim; +} + +inline RESOURCE_DIMENSION GetResourceViewDimension(const IBufferView* /*pBuffView*/) +{ + return RESOURCE_DIM_BUFFER; +} + +inline Uint32 GetResourceSampleCount(const ITextureView* pTexView) +{ + VERIFY_EXPR(pTexView != nullptr); + return const_cast<ITextureView*>(pTexView)->GetTexture()->GetDesc().SampleCount; +} + +inline Uint32 GetResourceSampleCount(const IBufferView* /*pBuffView*/) +{ + return 0; +} + template <typename ResourceAttribsType, typename ResourceViewImplType, typename ViewTypeEnumType> @@ -254,8 +277,6 @@ bool VerifyResourceViewBinding(const ResourceAttribsType& Attribs, if (!IsExpectedViewType) { - std::string ExpectedViewTypeName; - std::stringstream ss; ss << "Error binding " << ExpectedResourceType << " '" << pViewImpl->GetDesc().Name << "' to variable '" << Attribs.GetPrintName(ArrayIndex) << '\''; @@ -279,11 +300,45 @@ bool VerifyResourceViewBinding(const ResourceAttribsType& Attribs, BindingOK = false; } + + const auto ExpectedResourceDim = Attribs.GetResourceDimension(); + if (ExpectedResourceDim != RESOURCE_DIM_UNDEFINED) + { + auto ResourceDim = GetResourceViewDimension(pViewImpl); + if (ResourceDim != ExpectedResourceDim) + { + LOG_ERROR_MESSAGE("Error binding ", ExpectedResourceType, " '", pViewImpl->GetDesc().Name, "' to variable '", + Attribs.GetPrintName(ArrayIndex), "': incorrect resource dimension: ", + GetResourceDimString(ExpectedResourceDim), " is expected, but the actual dimension is ", + GetResourceDimString(ResourceDim)); + + BindingOK = false; + } + + if (ResourceDim == RESOURCE_DIM_TEX_2D || ResourceDim == RESOURCE_DIM_TEX_2D_ARRAY) + { + auto SampleCount = GetResourceSampleCount(pViewImpl); + auto IsMS = Attribs.IsMultisample(); + if (IsMS && SampleCount == 1) + { + LOG_ERROR_MESSAGE("Error binding ", ExpectedResourceType, " '", pViewImpl->GetDesc().Name, "' to variable '", + Attribs.GetPrintName(ArrayIndex), "': multisample texture is expected."); + BindingOK = false; + } + else if (!IsMS && SampleCount > 1) + { + LOG_ERROR_MESSAGE("Error binding ", ExpectedResourceType, " '", pViewImpl->GetDesc().Name, "' to variable '", + Attribs.GetPrintName(ArrayIndex), "': single-sample texture is expected."); + BindingOK = false; + } + } + } } if (VarType != SHADER_RESOURCE_VARIABLE_TYPE_DYNAMIC && pCachedView != nullptr && pCachedView != pViewImpl) { - auto VarTypeStr = GetShaderVariableTypeLiteralName(VarType); + const auto* VarTypeStr = GetShaderVariableTypeLiteralName(VarType); + std::stringstream ss; ss << "Non-null resource '" << pCachedView->GetDesc().Name << "' is already bound to " << VarTypeStr << " shader variable '" << Attribs.GetPrintName(ArrayIndex) << '\''; @@ -305,6 +360,7 @@ bool VerifyResourceViewBinding(const ResourceAttribsType& Attribs, BindingOK = false; } + return BindingOK; } @@ -340,7 +396,7 @@ template <typename ResourceLayoutType, struct ShaderVariableBase : public ResourceVariableBaseInterface { ShaderVariableBase(ResourceLayoutType& ParentResLayout) : - m_ParentResLayout(ParentResLayout) + m_ParentResLayout{ParentResLayout} { } diff --git a/Graphics/GraphicsEngine/include/TextureBase.hpp b/Graphics/GraphicsEngine/include/TextureBase.hpp index 1805f7d3..559df1ea 100644 --- a/Graphics/GraphicsEngine/include/TextureBase.hpp +++ b/Graphics/GraphicsEngine/include/TextureBase.hpp @@ -30,13 +30,14 @@ /// \file /// Implementation of the Diligent::TextureBase template class +#include <memory> + #include "Texture.h" #include "GraphicsTypes.h" #include "DeviceObjectBase.hpp" #include "GraphicsAccessories.hpp" #include "STDAllocator.hpp" #include "FormatString.hpp" -#include <memory> namespace Diligent { diff --git a/Graphics/GraphicsEngine/interface/APIInfo.h b/Graphics/GraphicsEngine/interface/APIInfo.h index a7596e3f..0ecbd92b 100644 --- a/Graphics/GraphicsEngine/interface/APIInfo.h +++ b/Graphics/GraphicsEngine/interface/APIInfo.h @@ -30,7 +30,7 @@ /// \file /// Diligent API information -#define DILIGENT_API_VERSION 240074 +#define DILIGENT_API_VERSION 240078 #include "../../../Primitives/interface/BasicTypes.h" @@ -39,64 +39,65 @@ DILIGENT_BEGIN_NAMESPACE(Diligent) /// Diligent API Info. This tructure can be used to verify API compatibility. struct APIInfo { - size_t StructSize DEFAULT_INITIALIZER(0); - int APIVersion DEFAULT_INITIALIZER(0); - size_t RenderTargetBlendDescSize DEFAULT_INITIALIZER(0); - size_t BlendStateDescSize DEFAULT_INITIALIZER(0); - size_t BufferDescSize DEFAULT_INITIALIZER(0); - size_t BufferDataSize DEFAULT_INITIALIZER(0); - size_t BufferFormatSize DEFAULT_INITIALIZER(0); - size_t BufferViewDescSize DEFAULT_INITIALIZER(0); - size_t StencilOpDescSize DEFAULT_INITIALIZER(0); - size_t DepthStencilStateDescSize DEFAULT_INITIALIZER(0); - size_t SamplerCapsSize DEFAULT_INITIALIZER(0); - size_t TextureCapsSize DEFAULT_INITIALIZER(0); - size_t DeviceCapsSize DEFAULT_INITIALIZER(0); - size_t DrawAttribsSize DEFAULT_INITIALIZER(0); - size_t DispatchComputeAttribsSize DEFAULT_INITIALIZER(0); - size_t ViewportSize DEFAULT_INITIALIZER(0); - size_t RectSize DEFAULT_INITIALIZER(0); - size_t CopyTextureAttribsSize DEFAULT_INITIALIZER(0); - size_t DeviceObjectAttribsSize DEFAULT_INITIALIZER(0); - size_t GraphicsAdapterInfoSize DEFAULT_INITIALIZER(0); - size_t DisplayModeAttribsSize DEFAULT_INITIALIZER(0); - size_t SwapChainDescSize DEFAULT_INITIALIZER(0); - size_t FullScreenModeDescSize DEFAULT_INITIALIZER(0); - size_t EngineCreateInfoSize DEFAULT_INITIALIZER(0); - size_t EngineGLCreateInfoSize DEFAULT_INITIALIZER(0); - size_t EngineD3D11CreateInfoSize DEFAULT_INITIALIZER(0); - size_t EngineD3D12CreateInfoSize DEFAULT_INITIALIZER(0); - size_t EngineVkCreateInfoSize DEFAULT_INITIALIZER(0); - size_t EngineMtlCreateInfoSize DEFAULT_INITIALIZER(0); - size_t BoxSize DEFAULT_INITIALIZER(0); - size_t TextureFormatAttribsSize DEFAULT_INITIALIZER(0); - size_t TextureFormatInfoSize DEFAULT_INITIALIZER(0); - size_t TextureFormatInfoExtSize DEFAULT_INITIALIZER(0); - size_t StateTransitionDescSize DEFAULT_INITIALIZER(0); - size_t LayoutElementSize DEFAULT_INITIALIZER(0); - size_t InputLayoutDescSize DEFAULT_INITIALIZER(0); - size_t SampleDescSize DEFAULT_INITIALIZER(0); - size_t ShaderResourceVariableDescSize DEFAULT_INITIALIZER(0); - size_t StaticSamplerDescSize DEFAULT_INITIALIZER(0); - size_t PipelineResourceLayoutDescSize DEFAULT_INITIALIZER(0); - size_t GraphicsPipelineDescSize DEFAULT_INITIALIZER(0); - size_t ComputePipelineDescSize DEFAULT_INITIALIZER(0); - size_t PipelineStateDescSize DEFAULT_INITIALIZER(0); - size_t RasterizerStateDescSize DEFAULT_INITIALIZER(0); - size_t ResourceMappingEntrySize DEFAULT_INITIALIZER(0); - size_t ResourceMappingDescSize DEFAULT_INITIALIZER(0); - size_t SamplerDescSize DEFAULT_INITIALIZER(0); - size_t ShaderDescSize DEFAULT_INITIALIZER(0); - size_t ShaderMacroSize DEFAULT_INITIALIZER(0); - size_t ShaderCreateInfoSize DEFAULT_INITIALIZER(0); - size_t ShaderResourceDescSize DEFAULT_INITIALIZER(0); - size_t DepthStencilClearValueSize DEFAULT_INITIALIZER(0); - size_t OptimizedClearValueSize DEFAULT_INITIALIZER(0); - size_t TextureDescSize DEFAULT_INITIALIZER(0); - size_t TextureSubResDataSize DEFAULT_INITIALIZER(0); - size_t TextureDataSize DEFAULT_INITIALIZER(0); - size_t MappedTextureSubresourceSize DEFAULT_INITIALIZER(0); - size_t TextureViewDescSize DEFAULT_INITIALIZER(0); + size_t StructSize DEFAULT_INITIALIZER(0); + int APIVersion DEFAULT_INITIALIZER(0); + size_t RenderTargetBlendDescSize DEFAULT_INITIALIZER(0); + size_t BlendStateDescSize DEFAULT_INITIALIZER(0); + size_t BufferDescSize DEFAULT_INITIALIZER(0); + size_t BufferDataSize DEFAULT_INITIALIZER(0); + size_t BufferFormatSize DEFAULT_INITIALIZER(0); + size_t BufferViewDescSize DEFAULT_INITIALIZER(0); + size_t StencilOpDescSize DEFAULT_INITIALIZER(0); + size_t DepthStencilStateDescSize DEFAULT_INITIALIZER(0); + size_t SamplerCapsSize DEFAULT_INITIALIZER(0); + size_t TextureCapsSize DEFAULT_INITIALIZER(0); + size_t DeviceCapsSize DEFAULT_INITIALIZER(0); + size_t DrawAttribsSize DEFAULT_INITIALIZER(0); + size_t DispatchComputeAttribsSize DEFAULT_INITIALIZER(0); + size_t ViewportSize DEFAULT_INITIALIZER(0); + size_t RectSize DEFAULT_INITIALIZER(0); + size_t CopyTextureAttribsSize DEFAULT_INITIALIZER(0); + size_t DeviceObjectAttribsSize DEFAULT_INITIALIZER(0); + size_t GraphicsAdapterInfoSize DEFAULT_INITIALIZER(0); + size_t DisplayModeAttribsSize DEFAULT_INITIALIZER(0); + size_t SwapChainDescSize DEFAULT_INITIALIZER(0); + size_t FullScreenModeDescSize DEFAULT_INITIALIZER(0); + size_t EngineCreateInfoSize DEFAULT_INITIALIZER(0); + size_t EngineGLCreateInfoSize DEFAULT_INITIALIZER(0); + size_t EngineD3D11CreateInfoSize DEFAULT_INITIALIZER(0); + size_t EngineD3D12CreateInfoSize DEFAULT_INITIALIZER(0); + size_t EngineVkCreateInfoSize DEFAULT_INITIALIZER(0); + size_t EngineMtlCreateInfoSize DEFAULT_INITIALIZER(0); + size_t BoxSize DEFAULT_INITIALIZER(0); + size_t TextureFormatAttribsSize DEFAULT_INITIALIZER(0); + size_t TextureFormatInfoSize DEFAULT_INITIALIZER(0); + size_t TextureFormatInfoExtSize DEFAULT_INITIALIZER(0); + size_t StateTransitionDescSize DEFAULT_INITIALIZER(0); + size_t LayoutElementSize DEFAULT_INITIALIZER(0); + size_t InputLayoutDescSize DEFAULT_INITIALIZER(0); + size_t SampleDescSize DEFAULT_INITIALIZER(0); + size_t ShaderResourceVariableDescSize DEFAULT_INITIALIZER(0); + size_t ImmutableSamplerDescSize DEFAULT_INITIALIZER(0); + size_t PipelineResourceLayoutDescSize DEFAULT_INITIALIZER(0); + size_t GraphicsPipelineDescSize DEFAULT_INITIALIZER(0); + size_t GraphicsPipelineStateCreateInfoSize DEFAULT_INITIALIZER(0); + size_t ComputePipelineStateCreateInfoSize DEFAULT_INITIALIZER(0); + size_t PipelineStateDescSize DEFAULT_INITIALIZER(0); + size_t RasterizerStateDescSize DEFAULT_INITIALIZER(0); + size_t ResourceMappingEntrySize DEFAULT_INITIALIZER(0); + size_t ResourceMappingDescSize DEFAULT_INITIALIZER(0); + size_t SamplerDescSize DEFAULT_INITIALIZER(0); + size_t ShaderDescSize DEFAULT_INITIALIZER(0); + size_t ShaderMacroSize DEFAULT_INITIALIZER(0); + size_t ShaderCreateInfoSize DEFAULT_INITIALIZER(0); + size_t ShaderResourceDescSize DEFAULT_INITIALIZER(0); + size_t DepthStencilClearValueSize DEFAULT_INITIALIZER(0); + size_t OptimizedClearValueSize DEFAULT_INITIALIZER(0); + size_t TextureDescSize DEFAULT_INITIALIZER(0); + size_t TextureSubResDataSize DEFAULT_INITIALIZER(0); + size_t TextureDataSize DEFAULT_INITIALIZER(0); + size_t MappedTextureSubresourceSize DEFAULT_INITIALIZER(0); + size_t TextureViewDescSize DEFAULT_INITIALIZER(0); }; typedef struct APIInfo APIInfo; diff --git a/Graphics/GraphicsEngine/interface/GraphicsTypes.h b/Graphics/GraphicsEngine/interface/GraphicsTypes.h index 56db9e61..85dbcff6 100644 --- a/Graphics/GraphicsEngine/interface/GraphicsTypes.h +++ b/Graphics/GraphicsEngine/interface/GraphicsTypes.h @@ -105,7 +105,7 @@ DILIGENT_TYPED_ENUM(USAGE, Uint8) /// when it is created, since it cannot be changed after creation. \n /// D3D11 Counterpart: D3D11_USAGE_IMMUTABLE. OpenGL counterpart: GL_STATIC_DRAW /// \remarks Static buffers do not allow CPU access and must use CPU_ACCESS_NONE flag. - USAGE_STATIC = 0, + USAGE_IMMUTABLE = 0, /// A resource that requires read and write access by the GPU and can also be occasionally /// written by the CPU. \n @@ -213,16 +213,16 @@ DEFINE_FLAG_ENUM_OPERATORS(MAP_FLAGS) /// - TextureViewDesc to describe texture view type DILIGENT_TYPED_ENUM(RESOURCE_DIMENSION, Uint8) { - RESOURCE_DIM_UNDEFINED = 0, ///< Texture type undefined - RESOURCE_DIM_BUFFER, ///< Buffer - RESOURCE_DIM_TEX_1D, ///< One-dimensional texture - RESOURCE_DIM_TEX_1D_ARRAY, ///< One-dimensional texture array - RESOURCE_DIM_TEX_2D, ///< Two-dimensional texture - RESOURCE_DIM_TEX_2D_ARRAY, ///< Two-dimensional texture array - RESOURCE_DIM_TEX_3D, ///< Three-dimensional texture - RESOURCE_DIM_TEX_CUBE, ///< Cube-map texture - RESOURCE_DIM_TEX_CUBE_ARRAY, ///< Cube-map array texture - RESOURCE_DIM_NUM_DIMENSIONS ///< Helper value that stores the total number of texture types in the enumeration + RESOURCE_DIM_UNDEFINED = 0, ///< Texture type undefined + RESOURCE_DIM_BUFFER, ///< Buffer + RESOURCE_DIM_TEX_1D, ///< One-dimensional texture + RESOURCE_DIM_TEX_1D_ARRAY, ///< One-dimensional texture array + RESOURCE_DIM_TEX_2D, ///< Two-dimensional texture + RESOURCE_DIM_TEX_2D_ARRAY, ///< Two-dimensional texture array + RESOURCE_DIM_TEX_3D, ///< Three-dimensional texture + RESOURCE_DIM_TEX_CUBE, ///< Cube-map texture + RESOURCE_DIM_TEX_CUBE_ARRAY, ///< Cube-map array texture + RESOURCE_DIM_NUM_DIMENSIONS ///< Helper value that stores the total number of texture types in the enumeration }; /// Texture view type @@ -1517,6 +1517,17 @@ struct DeviceFeatures /// Indicates if device supports separable programs DEVICE_FEATURE_STATE SeparablePrograms DEFAULT_INITIALIZER(DEVICE_FEATURE_STATE_DISABLED); + /// Indicates if device supports resource queries from shader objects. + + /// \ note This feature indicates if IShader::GetResourceCount() and IShader::GetResourceDesc() methods + /// can be used to query the list of resources of individual shader objects. + /// Shader variable queries from pipeline state and shader resource binding objects are always + /// available. + /// + /// The feature is always enabled in Direct3D11, Direct3D12 and Vulkan. It is enabled in + /// OpenGL when separable programs are available, and it is always disabled in Metal. + DEVICE_FEATURE_STATE ShaderResourceQueries DEFAULT_INITIALIZER(DEVICE_FEATURE_STATE_DISABLED); + /// Indicates if device supports indirect draw commands DEVICE_FEATURE_STATE IndirectRendering DEFAULT_INITIALIZER(DEVICE_FEATURE_STATE_DISABLED); @@ -1621,6 +1632,7 @@ struct DeviceFeatures explicit DeviceFeatures(DEVICE_FEATURE_STATE State) noexcept : SeparablePrograms {State}, + ShaderResourceQueries {State}, IndirectRendering {State}, WireframeFill {State}, MultithreadedResourceCreation {State}, @@ -1653,7 +1665,7 @@ struct DeviceFeatures UniformBuffer8BitAccess {State} { # if defined(_MSC_VER) && defined(_WIN64) - static_assert(sizeof(*this) == 31, "Did you add a new feature to DeviceFeatures? Please handle its status above."); + static_assert(sizeof(*this) == 32, "Did you add a new feature to DeviceFeatures? Please handle its status above."); # endif } #endif @@ -1712,7 +1724,7 @@ struct GraphicsAdapterInfo /// The amount of local video memory that is inaccessible by CPU, in bytes. - /// \note Device-local memory is where USAGE_DEFAULT and USAGE_STATIC resources + /// \note Device-local memory is where USAGE_DEFAULT and USAGE_IMMUTABLE resources /// are typically allocated. /// /// On some devices it may not be possible to query the memory size, @@ -1881,6 +1893,11 @@ struct EngineGLCreateInfo DILIGENT_DERIVE(EngineCreateInfo) /// provide additional runtime checking, validation, and logging /// functionality while possibly incurring performance penalties bool CreateDebugContext DEFAULT_INITIALIZER(false); + + /// Force using non-separable programs. + + /// Setting this to true is typically needed for testing purposes only. + bool ForceNonSeparablePrograms DEFAULT_INITIALIZER(false); }; typedef struct EngineGLCreateInfo EngineGLCreateInfo; diff --git a/Graphics/GraphicsEngine/interface/InputLayout.h b/Graphics/GraphicsEngine/interface/InputLayout.h index 19904974..c7b18965 100644 --- a/Graphics/GraphicsEngine/interface/InputLayout.h +++ b/Graphics/GraphicsEngine/interface/InputLayout.h @@ -179,7 +179,7 @@ typedef struct LayoutElement LayoutElement; /// Layout description -/// This structure is used by IRenderDevice::CreatePipelineState(). +/// This structure is used by IRenderDevice::CreateGraphicsPipelineState(). struct InputLayoutDesc { /// Array of layout elements diff --git a/Graphics/GraphicsEngine/interface/PipelineState.h b/Graphics/GraphicsEngine/interface/PipelineState.h index 2ea0d224..f953b851 100644 --- a/Graphics/GraphicsEngine/interface/PipelineState.h +++ b/Graphics/GraphicsEngine/interface/PipelineState.h @@ -96,32 +96,36 @@ struct ShaderResourceVariableDesc typedef struct ShaderResourceVariableDesc ShaderResourceVariableDesc; -/// Static sampler description -struct StaticSamplerDesc +/// Immutable sampler description. + +/// An immutable sampler is compiled into the pipeline state and can't be changed. +/// It is generally more efficient than a regular sampler and should be used +/// whenever possible. +struct ImmutableSamplerDesc { - /// Shader stages that this static sampler applies to. More than one shader stage can be specified. + /// Shader stages that this immutable sampler applies to. More than one shader stage can be specified. SHADER_TYPE ShaderStages DEFAULT_INITIALIZER(SHADER_TYPE_UNKNOWN); /// The name of the sampler itself or the name of the texture variable that - /// this static sampler is assigned to if combined texture samplers are used. + /// this immutable sampler is assigned to if combined texture samplers are used. const Char* SamplerOrTextureName DEFAULT_INITIALIZER(nullptr); /// Sampler description struct SamplerDesc Desc; #if DILIGENT_CPP_INTERFACE - StaticSamplerDesc()noexcept{} + ImmutableSamplerDesc()noexcept{} - StaticSamplerDesc(SHADER_TYPE _ShaderStages, - const Char* _SamplerOrTextureName, - const SamplerDesc& _Desc)noexcept : + ImmutableSamplerDesc(SHADER_TYPE _ShaderStages, + const Char* _SamplerOrTextureName, + const SamplerDesc& _Desc)noexcept : ShaderStages {_ShaderStages }, SamplerOrTextureName{_SamplerOrTextureName}, Desc {_Desc } {} #endif }; -typedef struct StaticSamplerDesc StaticSamplerDesc; +typedef struct ImmutableSamplerDesc ImmutableSamplerDesc; /// Pipeline layout description struct PipelineResourceLayoutDesc @@ -129,19 +133,19 @@ struct PipelineResourceLayoutDesc /// Default shader resource variable type. This type will be used if shader /// variable description is not found in the Variables array /// or if Variables == nullptr - SHADER_RESOURCE_VARIABLE_TYPE DefaultVariableType DEFAULT_INITIALIZER(SHADER_RESOURCE_VARIABLE_TYPE_STATIC); + SHADER_RESOURCE_VARIABLE_TYPE DefaultVariableType DEFAULT_INITIALIZER(SHADER_RESOURCE_VARIABLE_TYPE_STATIC); /// Number of elements in Variables array - Uint32 NumVariables DEFAULT_INITIALIZER(0); + Uint32 NumVariables DEFAULT_INITIALIZER(0); /// Array of shader resource variable descriptions - const ShaderResourceVariableDesc* Variables DEFAULT_INITIALIZER(nullptr); + const ShaderResourceVariableDesc* Variables DEFAULT_INITIALIZER(nullptr); - /// Number of static samplers in StaticSamplers array - Uint32 NumStaticSamplers DEFAULT_INITIALIZER(0); + /// Number of immutable samplers in ImmutableSamplers array + Uint32 NumImmutableSamplers DEFAULT_INITIALIZER(0); - /// Array of static sampler descriptions - const StaticSamplerDesc* StaticSamplers DEFAULT_INITIALIZER(nullptr); + /// Array of immutable sampler descriptions + const ImmutableSamplerDesc* ImmutableSamplers DEFAULT_INITIALIZER(nullptr); }; typedef struct PipelineResourceLayoutDesc PipelineResourceLayoutDesc; @@ -151,29 +155,6 @@ typedef struct PipelineResourceLayoutDesc PipelineResourceLayoutDesc; /// This structure describes the graphics pipeline state and is part of the PipelineStateDesc structure. struct GraphicsPipelineDesc { - /// Vertex shader to be used with the pipeline. - IShader* pVS DEFAULT_INITIALIZER(nullptr); - - /// Pixel shader to be used with the pipeline. - IShader* pPS DEFAULT_INITIALIZER(nullptr); - - /// Domain shader to be used with the pipeline. - IShader* pDS DEFAULT_INITIALIZER(nullptr); - - /// Hull shader to be used with the pipeline. - IShader* pHS DEFAULT_INITIALIZER(nullptr); - - /// Geometry shader to be used with the pipeline. - IShader* pGS DEFAULT_INITIALIZER(nullptr); - - /// Amplification shader to be used with the pipeline. - IShader* pAS DEFAULT_INITIALIZER(nullptr); - - /// Mesh shader to be used with the pipeline. - IShader* pMS DEFAULT_INITIALIZER(nullptr); - - //D3D12_STREAM_OUTPUT_DESC StreamOutput; - /// Blend state description. BlendStateDesc BlendDesc; @@ -348,19 +329,9 @@ struct PipelineStateDesc DILIGENT_DERIVE(DeviceObjectAttribs) /// Pipeline layout description PipelineResourceLayoutDesc ResourceLayout; - /// Graphics pipeline state description. This memeber is ignored if PipelineType is not PIPELINE_TYPE_GRAPHICS or PIPELINE_TYPE_MESH - GraphicsPipelineDesc GraphicsPipeline; - - /// Compute pipeline state description. This memeber is ignored if PipelineType is not PIPELINE_TYPE_COMPUTE - ComputePipelineDesc ComputePipeline; - - // TODO (AZ): use pointer - /// Ray tracing pipeline state description. This memeber is ignored if PipelineType is not PIPELINE_TYPE_RAY_TRACING. - RayTracingPipelineDesc RayTracingPipeline; - #if DILIGENT_CPP_INTERFACE bool IsAnyGraphicsPipeline() const { return PipelineType == PIPELINE_TYPE_GRAPHICS || PipelineType == PIPELINE_TYPE_MESH; } - bool IsComputePipeline () const { return PipelineType == PIPELINE_TYPE_COMPUTE; } + bool IsComputePipeline() const { return PipelineType == PIPELINE_TYPE_COMPUTE; } #endif }; typedef struct PipelineStateDesc PipelineStateDesc; @@ -370,7 +341,7 @@ typedef struct PipelineStateDesc PipelineStateDesc; DILIGENT_TYPED_ENUM(PSO_CREATE_FLAGS, Uint32) { /// Null flag. - PSO_CREATE_FLAG_NONE = 0x00, + PSO_CREATE_FLAG_NONE = 0x00, /// Ignore missing variables. @@ -378,15 +349,15 @@ DILIGENT_TYPED_ENUM(PSO_CREATE_FLAGS, Uint32) /// provided as part of the pipeline resource layout description /// that is not found in any of the designated shader stages. /// Use this flag to silence these warnings. - PSO_CREATE_FLAG_IGNORE_MISSING_VARIABLES = 0x01, + PSO_CREATE_FLAG_IGNORE_MISSING_VARIABLES = 0x01, - /// Ignore missing static samplers. + /// Ignore missing immutable samplers. - /// By default, the engine outputs a warning for every static sampler + /// By default, the engine outputs a warning for every immutable sampler /// provided as part of the pipeline resource layout description /// that is not found in any of the designated shader stages. /// Use this flag to silence these warnings. - PSO_CREATE_FLAG_IGNORE_MISSING_STATIC_SAMPLERS = 0x02, + PSO_CREATE_FLAG_IGNORE_MISSING_IMMUTABLE_SAMPLERS = 0x02, }; DEFINE_FLAG_ENUM_OPERATORS(PSO_CREATE_FLAGS); @@ -398,10 +369,57 @@ struct PipelineStateCreateInfo PipelineStateDesc PSODesc; /// Pipeline state creation flags, see Diligent::PSO_CREATE_FLAGS. - PSO_CREATE_FLAGS Flags DEFAULT_INITIALIZER(PSO_CREATE_FLAG_NONE); + PSO_CREATE_FLAGS Flags DEFAULT_INITIALIZER(PSO_CREATE_FLAG_NONE); }; typedef struct PipelineStateCreateInfo PipelineStateCreateInfo; + +/// Graphics pipeline state creation attributes +struct GraphicsPipelineStateCreateInfo DILIGENT_DERIVE(PipelineStateCreateInfo) + + /// Graphics pipeline state description. + GraphicsPipelineDesc GraphicsPipeline; + + /// Vertex shader to be used with the pipeline. + IShader* pVS DEFAULT_INITIALIZER(nullptr); + + /// Pixel shader to be used with the pipeline. + IShader* pPS DEFAULT_INITIALIZER(nullptr); + + /// Domain shader to be used with the pipeline. + IShader* pDS DEFAULT_INITIALIZER(nullptr); + + /// Hull shader to be used with the pipeline. + IShader* pHS DEFAULT_INITIALIZER(nullptr); + + /// Geometry shader to be used with the pipeline. + IShader* pGS DEFAULT_INITIALIZER(nullptr); + + /// Amplification shader to be used with the pipeline. + IShader* pAS DEFAULT_INITIALIZER(nullptr); + + /// Mesh shader to be used with the pipeline. + IShader* pMS DEFAULT_INITIALIZER(nullptr); +}; +typedef struct GraphicsPipelineStateCreateInfo GraphicsPipelineStateCreateInfo; + + +/// Compute pipeline state description. +struct ComputePipelineStateCreateInfo DILIGENT_DERIVE(PipelineStateCreateInfo) + + /// Compute shader to be used with the pipeline + IShader* pCS DEFAULT_INITIALIZER(nullptr); + +#if DILIGENT_CPP_INTERFACE + ComputePipelineStateCreateInfo() noexcept + { + PSODesc.PipelineType = PIPELINE_TYPE_COMPUTE; + } +#endif +}; +typedef struct ComputePipelineStateCreateInfo ComputePipelineStateCreateInfo; + + // {06084AE5-6A71-4FE8-84B9-395DD489A28C} static const struct INTERFACE_ID IID_PipelineState = {0x6084ae5, 0x6a71, 0x4fe8, {0x84, 0xb9, 0x39, 0x5d, 0xd4, 0x89, 0xa2, 0x8c}}; @@ -419,10 +437,13 @@ static const struct INTERFACE_ID IID_PipelineState = DILIGENT_BEGIN_INTERFACE(IPipelineState, IDeviceObject) { #if DILIGENT_CPP_INTERFACE - /// Returns the blend state description used to create the object - virtual const PipelineStateDesc& METHOD(GetDesc)()const override = 0; + /// Returns the pipeline description used to create the object + virtual const PipelineStateDesc& METHOD(GetDesc)() const override = 0; #endif + /// Returns the graphics pipeline description used to create the object. + /// This method must only be called for a graphics or mesh pipeline. + VIRTUAL const GraphicsPipelineDesc REF METHOD(GetGraphicsPipelineDesc)(THIS) CONST PURE; /// Binds resources for all shaders in the pipeline state @@ -512,6 +533,7 @@ DILIGENT_END_INTERFACE # define IPipelineState_GetDesc(This) (const struct PipelineStateDesc*)IDeviceObject_GetDesc(This) +# define IPipelineState_GetGraphicsPipelineDesc(This) CALL_IFACE_METHOD(PipelineState, GetGraphicsPipelineDesc, This) # define IPipelineState_BindStaticResources(This, ...) CALL_IFACE_METHOD(PipelineState, BindStaticResources, This, __VA_ARGS__) # define IPipelineState_GetStaticVariableCount(This, ...) CALL_IFACE_METHOD(PipelineState, GetStaticVariableCount, This, __VA_ARGS__) # define IPipelineState_GetStaticVariableByName(This, ...) CALL_IFACE_METHOD(PipelineState, GetStaticVariableByName, This, __VA_ARGS__) diff --git a/Graphics/GraphicsEngine/interface/RenderDevice.h b/Graphics/GraphicsEngine/interface/RenderDevice.h index 027d0f5c..19d98a94 100644 --- a/Graphics/GraphicsEngine/interface/RenderDevice.h +++ b/Graphics/GraphicsEngine/interface/RenderDevice.h @@ -78,7 +78,7 @@ DILIGENT_BEGIN_INTERFACE(IRenderDevice, IObject) /// \param [in] BuffDesc - Buffer description, see Diligent::BufferDesc for details. /// \param [in] pBuffData - Pointer to Diligent::BufferData structure that describes /// initial buffer data or nullptr if no data is provided. - /// Static buffers (USAGE_STATIC) must be initialized at creation time. + /// Immutable buffers (USAGE_IMMUTABLE) must be initialized at creation time. /// \param [out] ppBuffer - Address of the memory location where the pointer to the /// buffer interface will be stored. The function calls AddRef(), /// so that the new buffer will contain one reference and must be @@ -109,7 +109,7 @@ DILIGENT_BEGIN_INTERFACE(IRenderDevice, IObject) /// \param [in] TexDesc - Texture description, see Diligent::TextureDesc for details. /// \param [in] pData - Pointer to Diligent::TextureData structure that describes /// initial texture data or nullptr if no data is provided. - /// Static textures (USAGE_STATIC) must be initialized at creation time. + /// Immutable textures (USAGE_IMMUTABLE) must be initialized at creation time. /// /// \param [out] ppTexture - Address of the memory location where the pointer to the /// texture interface will be stored. @@ -156,17 +156,27 @@ DILIGENT_BEGIN_INTERFACE(IRenderDevice, IObject) const ResourceMappingDesc REF MappingDesc, IResourceMapping** ppMapping) PURE; - /// Creates a new pipeline state object + /// Creates a new graphics pipeline state object - /// \param [in] PSOCreateInfo - Pipeline state create info, see Diligent::PipelineStateCreateInfo for details. + /// \param [in] PSOCreateInfo - Graphics pipeline state create info, see Diligent::GraphicsPipelineStateCreateInfo for details. /// \param [out] ppPipelineState - Address of the memory location where the pointer to the /// pipeline state interface will be stored. /// The function calls AddRef(), so that the new object will contain /// one reference. - VIRTUAL void METHOD(CreatePipelineState)(THIS_ - const PipelineStateCreateInfo REF PSOCreateInfo, - IPipelineState** ppPipelineState) PURE; + VIRTUAL void METHOD(CreateGraphicsPipelineState)(THIS_ + const GraphicsPipelineStateCreateInfo REF PSOCreateInfo, + IPipelineState** ppPipelineState) PURE; + + /// Creates a new compute pipeline state object + /// \param [in] PSOCreateInfo - Compute pipeline state create info, see Diligent::ComputePipelineStateCreateInfo for details. + /// \param [out] ppPipelineState - Address of the memory location where the pointer to the + /// pipeline state interface will be stored. + /// The function calls AddRef(), so that the new object will contain + /// one reference. + VIRTUAL void METHOD(CreateComputePipelineState)(THIS_ + const ComputePipelineStateCreateInfo REF PSOCreateInfo, + IPipelineState** ppPipelineState) PURE; /// Creates a new fence object @@ -312,20 +322,21 @@ DILIGENT_END_INTERFACE // clang-format off -# define IRenderDevice_CreateBuffer(This, ...) CALL_IFACE_METHOD(RenderDevice, CreateBuffer, This, __VA_ARGS__) -# define IRenderDevice_CreateShader(This, ...) CALL_IFACE_METHOD(RenderDevice, CreateShader, This, __VA_ARGS__) -# define IRenderDevice_CreateTexture(This, ...) CALL_IFACE_METHOD(RenderDevice, CreateTexture, This, __VA_ARGS__) -# define IRenderDevice_CreateSampler(This, ...) CALL_IFACE_METHOD(RenderDevice, CreateSampler, This, __VA_ARGS__) -# define IRenderDevice_CreateResourceMapping(This, ...) CALL_IFACE_METHOD(RenderDevice, CreateResourceMapping, This, __VA_ARGS__) -# define IRenderDevice_CreatePipelineState(This, ...) CALL_IFACE_METHOD(RenderDevice, CreatePipelineState, This, __VA_ARGS__) -# define IRenderDevice_CreateFence(This, ...) CALL_IFACE_METHOD(RenderDevice, CreateFence, This, __VA_ARGS__) -# define IRenderDevice_CreateQuery(This, ...) CALL_IFACE_METHOD(RenderDevice, CreateQuery, This, __VA_ARGS__) -# define IRenderDevice_GetDeviceCaps(This) CALL_IFACE_METHOD(RenderDevice, GetDeviceCaps, This) -# define IRenderDevice_GetTextureFormatInfo(This, ...) CALL_IFACE_METHOD(RenderDevice, GetTextureFormatInfo, This, __VA_ARGS__) -# define IRenderDevice_GetTextureFormatInfoExt(This, ...) CALL_IFACE_METHOD(RenderDevice, GetTextureFormatInfoExt,This, __VA_ARGS__) -# define IRenderDevice_ReleaseStaleResources(This, ...) CALL_IFACE_METHOD(RenderDevice, ReleaseStaleResources, This, __VA_ARGS__) -# define IRenderDevice_IdleGPU(This) CALL_IFACE_METHOD(RenderDevice, IdleGPU, This) -# define IRenderDevice_GetEngineFactory(This) CALL_IFACE_METHOD(RenderDevice, GetEngineFactory, This) +# define IRenderDevice_CreateBuffer(This, ...) CALL_IFACE_METHOD(RenderDevice, CreateBuffer, This, __VA_ARGS__) +# define IRenderDevice_CreateShader(This, ...) CALL_IFACE_METHOD(RenderDevice, CreateShader, This, __VA_ARGS__) +# define IRenderDevice_CreateTexture(This, ...) CALL_IFACE_METHOD(RenderDevice, CreateTexture, This, __VA_ARGS__) +# define IRenderDevice_CreateSampler(This, ...) CALL_IFACE_METHOD(RenderDevice, CreateSampler, This, __VA_ARGS__) +# define IRenderDevice_CreateResourceMapping(This, ...) CALL_IFACE_METHOD(RenderDevice, CreateResourceMapping, This, __VA_ARGS__) +# define IRenderDevice_CreateGraphicsPipelineState(This, ...) CALL_IFACE_METHOD(RenderDevice, CreateGraphicsPipelineState, This, __VA_ARGS__) +# define IRenderDevice_CreateComputePipelineState(This, ...) CALL_IFACE_METHOD(RenderDevice, CreateComputePipelineState, This, __VA_ARGS__) +# define IRenderDevice_CreateFence(This, ...) CALL_IFACE_METHOD(RenderDevice, CreateFence, This, __VA_ARGS__) +# define IRenderDevice_CreateQuery(This, ...) CALL_IFACE_METHOD(RenderDevice, CreateQuery, This, __VA_ARGS__) +# define IRenderDevice_GetDeviceCaps(This) CALL_IFACE_METHOD(RenderDevice, GetDeviceCaps, This) +# define IRenderDevice_GetTextureFormatInfo(This, ...) CALL_IFACE_METHOD(RenderDevice, GetTextureFormatInfo, This, __VA_ARGS__) +# define IRenderDevice_GetTextureFormatInfoExt(This, ...) CALL_IFACE_METHOD(RenderDevice, GetTextureFormatInfoExt, This, __VA_ARGS__) +# define IRenderDevice_ReleaseStaleResources(This, ...) CALL_IFACE_METHOD(RenderDevice, ReleaseStaleResources, This, __VA_ARGS__) +# define IRenderDevice_IdleGPU(This) CALL_IFACE_METHOD(RenderDevice, IdleGPU, This) +# define IRenderDevice_GetEngineFactory(This) CALL_IFACE_METHOD(RenderDevice, GetEngineFactory, This) // clang-format on diff --git a/Graphics/GraphicsEngine/interface/Shader.h b/Graphics/GraphicsEngine/interface/Shader.h index 03dc06f0..950ecc77 100644 --- a/Graphics/GraphicsEngine/interface/Shader.h +++ b/Graphics/GraphicsEngine/interface/Shader.h @@ -352,7 +352,9 @@ DILIGENT_TYPED_ENUM(SHADER_RESOURCE_TYPE, Uint8) SHADER_RESOURCE_TYPE_SAMPLER, /// Input attachment in a render pass - SHADER_RESOURCE_TYPE_INPUT_ATTACHMENT + SHADER_RESOURCE_TYPE_INPUT_ATTACHMENT, + + SHADER_RESOURCE_TYPE_LAST = SHADER_RESOURCE_TYPE_INPUT_ATTACHMENT }; // clang-format on diff --git a/Graphics/GraphicsEngine/src/APIInfo.cpp b/Graphics/GraphicsEngine/src/APIInfo.cpp index f71b943e..e4111327 100644 --- a/Graphics/GraphicsEngine/src/APIInfo.cpp +++ b/Graphics/GraphicsEngine/src/APIInfo.cpp @@ -87,10 +87,11 @@ static APIInfo InitAPIInfo() INIT_STRUCTURE_SIZE(InputLayoutDesc); INIT_STRUCTURE_SIZE(SampleDesc); INIT_STRUCTURE_SIZE(ShaderResourceVariableDesc); - INIT_STRUCTURE_SIZE(StaticSamplerDesc); + INIT_STRUCTURE_SIZE(ImmutableSamplerDesc); INIT_STRUCTURE_SIZE(PipelineResourceLayoutDesc); INIT_STRUCTURE_SIZE(GraphicsPipelineDesc); - INIT_STRUCTURE_SIZE(ComputePipelineDesc); + INIT_STRUCTURE_SIZE(GraphicsPipelineStateCreateInfo); + INIT_STRUCTURE_SIZE(ComputePipelineStateCreateInfo); INIT_STRUCTURE_SIZE(PipelineStateDesc); INIT_STRUCTURE_SIZE(RasterizerStateDesc); INIT_STRUCTURE_SIZE(ResourceMappingEntry); diff --git a/Graphics/GraphicsEngine/src/BufferBase.cpp b/Graphics/GraphicsEngine/src/BufferBase.cpp index c42fd698..aab770fd 100644 --- a/Graphics/GraphicsEngine/src/BufferBase.cpp +++ b/Graphics/GraphicsEngine/src/BufferBase.cpp @@ -74,7 +74,7 @@ void ValidateBufferDesc(const BufferDesc& Desc, const DeviceCaps& deviceCaps) switch (Desc.Usage) { - case USAGE_STATIC: + case USAGE_IMMUTABLE: case USAGE_DEFAULT: VERIFY_BUFFER(Desc.CPUAccessFlags == CPU_ACCESS_NONE, "static and default buffers can't have any CPU access flags set."); break; @@ -117,8 +117,8 @@ void ValidateBufferDesc(const BufferDesc& Desc, const DeviceCaps& deviceCaps) void ValidateBufferInitData(const BufferDesc& Desc, const BufferData* pBuffData) { - if (Desc.Usage == USAGE_STATIC && (pBuffData == nullptr || pBuffData->pData == nullptr)) - LOG_BUFFER_ERROR_AND_THROW("initial data must not be null as static buffers must be initialized at creation time."); + if (Desc.Usage == USAGE_IMMUTABLE && (pBuffData == nullptr || pBuffData->pData == nullptr)) + LOG_BUFFER_ERROR_AND_THROW("initial data must not be null as immutable buffers must be initialized at creation time."); if (Desc.Usage == USAGE_DYNAMIC && pBuffData != nullptr && pBuffData->pData != nullptr) LOG_BUFFER_ERROR_AND_THROW("initial data must be null for dynamic buffers."); diff --git a/Graphics/GraphicsEngine/src/PipelineStateBase.cpp b/Graphics/GraphicsEngine/src/PipelineStateBase.cpp new file mode 100644 index 00000000..fedc332d --- /dev/null +++ b/Graphics/GraphicsEngine/src/PipelineStateBase.cpp @@ -0,0 +1,260 @@ +/* + * Copyright 2019-2020 Diligent Graphics LLC + * Copyright 2015-2019 Egor Yusov + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * In no event and under no legal theory, whether in tort (including negligence), + * contract, or otherwise, unless required by applicable law (such as deliberate + * and grossly negligent acts) or agreed to in writing, shall any Contributor be + * liable for any damages, including any direct, indirect, special, incidental, + * or consequential damages of any character arising as a result of this License or + * out of the use or inability to use the software (including but not limited to damages + * for loss of goodwill, work stoppage, computer failure or malfunction, or any and + * all other commercial damages or losses), even if such Contributor has been advised + * of the possibility of such damages. + */ + +#include "pch.h" +#include "PipelineStateBase.hpp" + +namespace Diligent +{ + +#define LOG_PSO_ERROR_AND_THROW(...) LOG_ERROR_AND_THROW("Description of ", GetPipelineTypeString(PSODesc.PipelineType), " PSO '", PSODesc.Name, "' is invalid: ", ##__VA_ARGS__) + +namespace +{ + +void ValidateRasterizerStateDesc(const PipelineStateDesc& PSODesc, const GraphicsPipelineDesc& GraphicsPipeline) noexcept(false) +{ + const auto& RSDesc = GraphicsPipeline.RasterizerDesc; + if (RSDesc.FillMode == FILL_MODE_UNDEFINED) + LOG_PSO_ERROR_AND_THROW("RasterizerDesc.FillMode must not be FILL_MODE_UNDEFINED"); + if (RSDesc.CullMode == CULL_MODE_UNDEFINED) + LOG_PSO_ERROR_AND_THROW("RasterizerDesc.CullMode must not be CULL_MODE_UNDEFINED"); +} + +void ValidateDepthStencilDesc(const PipelineStateDesc& PSODesc, const GraphicsPipelineDesc& GraphicsPipeline) noexcept(false) +{ + const auto& DSSDesc = GraphicsPipeline.DepthStencilDesc; + if (DSSDesc.DepthEnable && DSSDesc.DepthFunc == COMPARISON_FUNC_UNKNOWN) + LOG_PSO_ERROR_AND_THROW("DepthStencilDesc.DepthFunc must not be COMPARISON_FUNC_UNKNOWN when depth is enabled"); + + auto CheckStencilOpDesc = [&](const StencilOpDesc& OpDesc, const char* FaceName) // + { + if (DSSDesc.StencilEnable) + { + if (OpDesc.StencilFailOp == STENCIL_OP_UNDEFINED) + LOG_PSO_ERROR_AND_THROW("DepthStencilDesc.", FaceName, ".StencilFailOp must not be STENCIL_OP_UNDEFINED when stencil is enabled"); + if (OpDesc.StencilDepthFailOp == STENCIL_OP_UNDEFINED) + LOG_PSO_ERROR_AND_THROW("DepthStencilDesc.", FaceName, ".StencilDepthFailOp must not be STENCIL_OP_UNDEFINED when stencil is enabled"); + if (OpDesc.StencilPassOp == STENCIL_OP_UNDEFINED) + LOG_PSO_ERROR_AND_THROW("DepthStencilDesc.", FaceName, ".StencilPassOp must not be STENCIL_OP_UNDEFINED when stencil is enabled"); + if (OpDesc.StencilFunc == COMPARISON_FUNC_UNKNOWN) + LOG_PSO_ERROR_AND_THROW("DepthStencilDesc.", FaceName, ".StencilFunc must not be COMPARISON_FUNC_UNKNOWN when stencil is enabled"); + } + }; + CheckStencilOpDesc(DSSDesc.FrontFace, "FrontFace"); + CheckStencilOpDesc(DSSDesc.BackFace, "BackFace"); +} + +void CorrectDepthStencilDesc(GraphicsPipelineDesc& GraphicsPipeline) noexcept +{ + auto& DSSDesc = GraphicsPipeline.DepthStencilDesc; + if (!DSSDesc.DepthEnable && DSSDesc.DepthFunc == COMPARISON_FUNC_UNKNOWN) + DSSDesc.DepthFunc = DepthStencilStateDesc{}.DepthFunc; + + auto CorrectStencilOpDesc = [&](StencilOpDesc& OpDesc) // + { + if (!DSSDesc.StencilEnable) + { + if (OpDesc.StencilFailOp == STENCIL_OP_UNDEFINED) + OpDesc.StencilFailOp = StencilOpDesc{}.StencilFailOp; + if (OpDesc.StencilDepthFailOp == STENCIL_OP_UNDEFINED) + OpDesc.StencilDepthFailOp = StencilOpDesc{}.StencilDepthFailOp; + if (OpDesc.StencilPassOp == STENCIL_OP_UNDEFINED) + OpDesc.StencilPassOp = StencilOpDesc{}.StencilPassOp; + if (OpDesc.StencilFunc == COMPARISON_FUNC_UNKNOWN) + OpDesc.StencilFunc = StencilOpDesc{}.StencilFunc; + } + }; + CorrectStencilOpDesc(DSSDesc.FrontFace); + CorrectStencilOpDesc(DSSDesc.BackFace); +} + +void ValidateBlendStateDesc(const PipelineStateDesc& PSODesc, const GraphicsPipelineDesc& GraphicsPipeline) noexcept(false) +{ + const auto& BlendDesc = GraphicsPipeline.BlendDesc; + for (Uint32 rt = 0; rt < MAX_RENDER_TARGETS; ++rt) + { + auto& RTDesc = BlendDesc.RenderTargets[rt]; + + const auto BlendEnable = RTDesc.BlendEnable && (rt == 0 || (BlendDesc.IndependentBlendEnable && rt > 0)); + if (BlendEnable) + { + if (RTDesc.SrcBlend == BLEND_FACTOR_UNDEFINED) + LOG_PSO_ERROR_AND_THROW("BlendDesc.RenderTargets[", rt, "].SrcBlend must not be BLEND_FACTOR_UNDEFINED"); + if (RTDesc.DestBlend == BLEND_FACTOR_UNDEFINED) + LOG_PSO_ERROR_AND_THROW("BlendDesc.RenderTargets[", rt, "].DestBlend must not be BLEND_FACTOR_UNDEFINED"); + if (RTDesc.BlendOp == BLEND_OPERATION_UNDEFINED) + LOG_PSO_ERROR_AND_THROW("BlendDesc.RenderTargets[", rt, "].BlendOp must not be BLEND_OPERATION_UNDEFINED"); + + if (RTDesc.SrcBlendAlpha == BLEND_FACTOR_UNDEFINED) + LOG_PSO_ERROR_AND_THROW("BlendDesc.RenderTargets[", rt, "].SrcBlendAlpha must not be BLEND_FACTOR_UNDEFINED"); + if (RTDesc.DestBlendAlpha == BLEND_FACTOR_UNDEFINED) + LOG_PSO_ERROR_AND_THROW("BlendDesc.RenderTargets[", rt, "].DestBlendAlpha must not be BLEND_FACTOR_UNDEFINED"); + if (RTDesc.BlendOpAlpha == BLEND_OPERATION_UNDEFINED) + LOG_PSO_ERROR_AND_THROW("BlendDesc.RenderTargets[", rt, "].BlendOpAlpha must not be BLEND_OPERATION_UNDEFINED"); + } + } +} + +void CorrectBlendStateDesc(GraphicsPipelineDesc& GraphicsPipeline) noexcept +{ + auto& BlendDesc = GraphicsPipeline.BlendDesc; + for (Uint32 rt = 0; rt < MAX_RENDER_TARGETS; ++rt) + { + auto& RTDesc = BlendDesc.RenderTargets[rt]; + // clang-format off + const auto BlendEnable = RTDesc.BlendEnable && (rt == 0 || (BlendDesc.IndependentBlendEnable && rt > 0)); + const auto LogicOpEnable = RTDesc.LogicOperationEnable && (rt == 0 || (BlendDesc.IndependentBlendEnable && rt > 0)); + // clang-format on + if (!BlendEnable) + { + if (RTDesc.SrcBlend == BLEND_FACTOR_UNDEFINED) + RTDesc.SrcBlend = RenderTargetBlendDesc{}.SrcBlend; + if (RTDesc.DestBlend == BLEND_FACTOR_UNDEFINED) + RTDesc.DestBlend = RenderTargetBlendDesc{}.DestBlend; + if (RTDesc.BlendOp == BLEND_OPERATION_UNDEFINED) + RTDesc.BlendOp = RenderTargetBlendDesc{}.BlendOp; + + if (RTDesc.SrcBlendAlpha == BLEND_FACTOR_UNDEFINED) + RTDesc.SrcBlendAlpha = RenderTargetBlendDesc{}.SrcBlendAlpha; + if (RTDesc.DestBlendAlpha == BLEND_FACTOR_UNDEFINED) + RTDesc.DestBlendAlpha = RenderTargetBlendDesc{}.DestBlendAlpha; + if (RTDesc.BlendOpAlpha == BLEND_OPERATION_UNDEFINED) + RTDesc.BlendOpAlpha = RenderTargetBlendDesc{}.BlendOpAlpha; + } + + if (!LogicOpEnable) + RTDesc.LogicOp = RenderTargetBlendDesc{}.LogicOp; + } +} + +} // namespace + +#define VALIDATE_SHADER_TYPE(Shader, ExpectedType, ShaderName) \ + if (Shader != nullptr && Shader->GetDesc().ShaderType != ExpectedType) \ + { \ + LOG_ERROR_AND_THROW(GetShaderTypeLiteralName(Shader->GetDesc().ShaderType), " is not a valid type for ", ShaderName, " shader"); \ + } + +void ValidateGraphicsPipelineCreateInfo(const GraphicsPipelineStateCreateInfo& CreateInfo) 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"); + + const auto& GraphicsPipeline = CreateInfo.GraphicsPipeline; + + ValidateBlendStateDesc(PSODesc, GraphicsPipeline); + ValidateRasterizerStateDesc(PSODesc, GraphicsPipeline); + ValidateDepthStencilDesc(PSODesc, GraphicsPipeline); + + + if (PSODesc.PipelineType == PIPELINE_TYPE_GRAPHICS) + { + if (CreateInfo.pVS == nullptr) + LOG_ERROR_AND_THROW("Vertex shader must not be null"); + + DEV_CHECK_ERR(CreateInfo.pAS == nullptr && CreateInfo.pMS == nullptr, "Mesh shaders are not supported in graphics pipeline"); + } + else if (PSODesc.PipelineType == PIPELINE_TYPE_MESH) + { + if (CreateInfo.pMS == nullptr) + LOG_ERROR_AND_THROW("Mesh shader must not be null"); + + DEV_CHECK_ERR(CreateInfo.pVS == nullptr && CreateInfo.pGS == nullptr && CreateInfo.pDS == nullptr && CreateInfo.pHS == nullptr, + "Vertex, geometry and tessellation shaders are not supported in a mesh pipeline"); + DEV_CHECK_ERR(GraphicsPipeline.InputLayout.NumElements == 0, "Input layout is ignored in a mesh pipeline"); + DEV_CHECK_ERR(GraphicsPipeline.PrimitiveTopology == PRIMITIVE_TOPOLOGY_TRIANGLE_LIST || + GraphicsPipeline.PrimitiveTopology == PRIMITIVE_TOPOLOGY_UNDEFINED, + "Primitive topology is ignored in a mesh pipeline, set it to undefined or keep default value (triangle list)"); + } + + + VALIDATE_SHADER_TYPE(CreateInfo.pVS, SHADER_TYPE_VERTEX, "vertex") + VALIDATE_SHADER_TYPE(CreateInfo.pPS, SHADER_TYPE_PIXEL, "pixel") + VALIDATE_SHADER_TYPE(CreateInfo.pGS, SHADER_TYPE_GEOMETRY, "geometry") + VALIDATE_SHADER_TYPE(CreateInfo.pHS, SHADER_TYPE_HULL, "hull") + VALIDATE_SHADER_TYPE(CreateInfo.pDS, SHADER_TYPE_DOMAIN, "domain") + VALIDATE_SHADER_TYPE(CreateInfo.pAS, SHADER_TYPE_AMPLIFICATION, "amplification") + VALIDATE_SHADER_TYPE(CreateInfo.pMS, SHADER_TYPE_MESH, "mesh") + + + if (GraphicsPipeline.pRenderPass != nullptr) + { + if (GraphicsPipeline.NumRenderTargets != 0) + LOG_PSO_ERROR_AND_THROW("NumRenderTargets must be 0 when explicit render pass is used"); + if (GraphicsPipeline.DSVFormat != TEX_FORMAT_UNKNOWN) + LOG_PSO_ERROR_AND_THROW("DSVFormat must be TEX_FORMAT_UNKNOWN when explicit render pass is used"); + + for (Uint32 rt = 0; rt < MAX_RENDER_TARGETS; ++rt) + { + if (GraphicsPipeline.RTVFormats[rt] != TEX_FORMAT_UNKNOWN) + LOG_PSO_ERROR_AND_THROW("RTVFormats[", rt, "] must be TEX_FORMAT_UNKNOWN when explicit render pass is used"); + } + + const auto& RPDesc = GraphicsPipeline.pRenderPass->GetDesc(); + if (GraphicsPipeline.SubpassIndex >= RPDesc.SubpassCount) + LOG_PSO_ERROR_AND_THROW("Subpass index (", Uint32{GraphicsPipeline.SubpassIndex}, ") exceeds the number of subpasses (", Uint32{RPDesc.SubpassCount}, ") in render pass '", RPDesc.Name, "'"); + } + else + { + for (Uint32 rt = GraphicsPipeline.NumRenderTargets; rt < _countof(GraphicsPipeline.RTVFormats); ++rt) + { + auto RTVFmt = GraphicsPipeline.RTVFormats[rt]; + if (RTVFmt != TEX_FORMAT_UNKNOWN) + { + LOG_ERROR_MESSAGE("Render target format (", GetTextureFormatAttribs(RTVFmt).Name, ") of unused slot ", rt, + " must be set to TEX_FORMAT_UNKNOWN"); + } + } + + if (GraphicsPipeline.SubpassIndex != 0) + LOG_PSO_ERROR_AND_THROW("Subpass index (", Uint32{GraphicsPipeline.SubpassIndex}, ") must be 0 when explicit render pass is not used"); + } +} + +void ValidateComputePipelineCreateInfo(const ComputePipelineStateCreateInfo& CreateInfo) noexcept(false) +{ + const auto& PSODesc = CreateInfo.PSODesc; + if (PSODesc.PipelineType != PIPELINE_TYPE_COMPUTE) + LOG_PSO_ERROR_AND_THROW("Pipeline type must be COMPUTE"); + + if (CreateInfo.pCS == nullptr) + LOG_ERROR_AND_THROW("Compute shader must not be null"); + + VALIDATE_SHADER_TYPE(CreateInfo.pCS, SHADER_TYPE_COMPUTE, "compute"); +} +#undef VALIDATE_SHADER_TYPE +#undef LOG_PSO_ERROR_AND_THROW + +void CorrectGraphicsPipelineDesc(GraphicsPipelineDesc& GraphicsPipeline) noexcept +{ + CorrectBlendStateDesc(GraphicsPipeline); + CorrectDepthStencilDesc(GraphicsPipeline); +} + +} // namespace Diligent |
