diff options
| author | Yong He <yonghe@outlook.com> | 2020-12-10 09:43:09 -0800 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2020-12-10 09:43:09 -0800 |
| commit | e4a8251749cf1fbf005b045e26e25f3ef7cccb8b (patch) | |
| tree | 209bb9617acb91c9cc1a9cc14f9aab5e92ca9871 /tools/gfx | |
| parent | b8e1f62fb9cc66481b35231149448e47f096d257 (diff) | |
Move ShaderObject to be under renderer interface. (#1633)
* Move ShaderObject to be under renderer interface.
* Make `create*PipelineState` take `const PipelineStateDesc&`.
* Move ShaderCursor implementation to a cpp file
Diffstat (limited to 'tools/gfx')
| -rw-r--r-- | tools/gfx/d3d11/render-d3d11.cpp | 13 | ||||
| -rw-r--r-- | tools/gfx/d3d12/render-d3d12.cpp | 13 | ||||
| -rw-r--r-- | tools/gfx/open-gl/render-gl.cpp | 13 | ||||
| -rw-r--r-- | tools/gfx/render-graphics-common.cpp | 1289 | ||||
| -rw-r--r-- | tools/gfx/render-graphics-common.h | 22 | ||||
| -rw-r--r-- | tools/gfx/render.h | 81 | ||||
| -rw-r--r-- | tools/gfx/shader-cursor.cpp | 249 | ||||
| -rw-r--r-- | tools/gfx/shader-cursor.h | 123 | ||||
| -rw-r--r-- | tools/gfx/vulkan/render-vk.cpp | 13 |
9 files changed, 1801 insertions, 15 deletions
diff --git a/tools/gfx/d3d11/render-d3d11.cpp b/tools/gfx/d3d11/render-d3d11.cpp index 6a58501fd..48cf7770d 100644 --- a/tools/gfx/d3d11/render-d3d11.cpp +++ b/tools/gfx/d3d11/render-d3d11.cpp @@ -6,6 +6,7 @@ //WORKING: #include "options.h" #include "../render.h" +#include "../render-graphics-common.h" #include "../d3d/d3d-util.h" #include "../nvapi/nvapi-util.h" @@ -51,7 +52,7 @@ using namespace Slang; namespace gfx { -class D3D11Renderer : public Renderer +class D3D11Renderer : public GraphicsAPIRenderer { public: enum @@ -1776,8 +1777,11 @@ D3D11_COLOR_WRITE_ENABLE translateRenderTargetWriteMask(RenderTargetWriteMaskT m return D3D11_COLOR_WRITE_ENABLE(result); } -Result D3D11Renderer::createGraphicsPipelineState(const GraphicsPipelineStateDesc& desc, PipelineState** outState) +Result D3D11Renderer::createGraphicsPipelineState(const GraphicsPipelineStateDesc& inDesc, PipelineState** outState) { + GraphicsPipelineStateDesc desc = inDesc; + preparePipelineDesc(desc); + auto programImpl = (ShaderProgramImpl*) desc.program; ComPtr<ID3D11DepthStencilState> depthStencilState; @@ -1905,8 +1909,11 @@ Result D3D11Renderer::createGraphicsPipelineState(const GraphicsPipelineStateDes return SLANG_OK; } -Result D3D11Renderer::createComputePipelineState(const ComputePipelineStateDesc& desc, PipelineState** outState) +Result D3D11Renderer::createComputePipelineState(const ComputePipelineStateDesc& inDesc, PipelineState** outState) { + ComputePipelineStateDesc desc = inDesc; + preparePipelineDesc(desc); + auto programImpl = (ShaderProgramImpl*) desc.program; auto pipelineLayoutImpl = (PipelineLayoutImpl*) desc.pipelineLayout; diff --git a/tools/gfx/d3d12/render-d3d12.cpp b/tools/gfx/d3d12/render-d3d12.cpp index 886754f0e..d3f030b88 100644 --- a/tools/gfx/d3d12/render-d3d12.cpp +++ b/tools/gfx/d3d12/render-d3d12.cpp @@ -5,6 +5,7 @@ //WORKING:#include "options.h" #include "../render.h" +#include "../render-graphics-common.h" #include "../surface.h" @@ -61,7 +62,7 @@ struct ID3D12GraphicsCommandList1 {}; namespace gfx { using namespace Slang; -class D3D12Renderer : public Renderer +class D3D12Renderer : public GraphicsAPIRenderer { public: // Renderer implementation @@ -3520,8 +3521,11 @@ Result D3D12Renderer::createDescriptorSet(DescriptorSetLayout* layout, Descripto return SLANG_OK; } -Result D3D12Renderer::createGraphicsPipelineState(const GraphicsPipelineStateDesc& desc, PipelineState** outState) +Result D3D12Renderer::createGraphicsPipelineState(const GraphicsPipelineStateDesc& inDesc, PipelineState** outState) { + GraphicsPipelineStateDesc desc = inDesc; + preparePipelineDesc(desc); + auto pipelineLayoutImpl = (PipelineLayoutImpl*) desc.pipelineLayout; auto programImpl = (ShaderProgramImpl*) desc.program; auto inputLayoutImpl = (InputLayoutImpl*) desc.inputLayout; @@ -3618,8 +3622,11 @@ Result D3D12Renderer::createGraphicsPipelineState(const GraphicsPipelineStateDes return SLANG_OK; } -Result D3D12Renderer::createComputePipelineState(const ComputePipelineStateDesc& desc, PipelineState** outState) +Result D3D12Renderer::createComputePipelineState(const ComputePipelineStateDesc& inDesc, PipelineState** outState) { + ComputePipelineStateDesc desc = inDesc; + preparePipelineDesc(desc); + auto pipelineLayoutImpl = (PipelineLayoutImpl*) desc.pipelineLayout; auto programImpl = (ShaderProgramImpl*) desc.program; diff --git a/tools/gfx/open-gl/render-gl.cpp b/tools/gfx/open-gl/render-gl.cpp index bf873212c..8be8165aa 100644 --- a/tools/gfx/open-gl/render-gl.cpp +++ b/tools/gfx/open-gl/render-gl.cpp @@ -5,6 +5,7 @@ //WORKING:#include "options.h" #include "../render.h" +#include "../render-graphics-common.h" #include <stdio.h> #include <stdlib.h> @@ -76,7 +77,7 @@ using namespace Slang; namespace gfx { -class GLRenderer : public Renderer +class GLRenderer : public GraphicsAPIRenderer { public: @@ -1376,8 +1377,11 @@ Result GLRenderer::createProgram(const ShaderProgram::Desc& desc, ShaderProgram* return SLANG_OK; } -Result GLRenderer::createGraphicsPipelineState(const GraphicsPipelineStateDesc& desc, PipelineState** outState) +Result GLRenderer::createGraphicsPipelineState(const GraphicsPipelineStateDesc& inDesc, PipelineState** outState) { + GraphicsPipelineStateDesc desc = inDesc; + preparePipelineDesc(desc); + auto programImpl = (ShaderProgramImpl*) desc.program; auto pipelineLayoutImpl = (PipelineLayoutImpl*) desc.pipelineLayout; auto inputLayoutImpl = (InputLayoutImpl*) desc.inputLayout; @@ -1390,8 +1394,11 @@ Result GLRenderer::createGraphicsPipelineState(const GraphicsPipelineStateDesc& return SLANG_OK; } -Result GLRenderer::createComputePipelineState(const ComputePipelineStateDesc& desc, PipelineState** outState) +Result GLRenderer::createComputePipelineState(const ComputePipelineStateDesc& inDesc, PipelineState** outState) { + ComputePipelineStateDesc desc = inDesc; + preparePipelineDesc(desc); + auto programImpl = (ShaderProgramImpl*) desc.program; auto pipelineLayoutImpl = (PipelineLayoutImpl*) desc.pipelineLayout; diff --git a/tools/gfx/render-graphics-common.cpp b/tools/gfx/render-graphics-common.cpp new file mode 100644 index 000000000..d3d469fc2 --- /dev/null +++ b/tools/gfx/render-graphics-common.cpp @@ -0,0 +1,1289 @@ +#include "render-graphics-common.h" +using namespace Slang; + +namespace gfx +{ + +gfx::StageType translateStage(SlangStage slangStage) +{ + switch (slangStage) + { + default: + SLANG_ASSERT(!"unhandled case"); + return gfx::StageType::Unknown; + +#define CASE(FROM, TO) \ + case SLANG_STAGE_##FROM: \ + return gfx::StageType::TO + + CASE(VERTEX, Vertex); + CASE(HULL, Hull); + CASE(DOMAIN, Domain); + CASE(GEOMETRY, Geometry); + CASE(FRAGMENT, Fragment); + + CASE(COMPUTE, Compute); + + CASE(RAY_GENERATION, RayGeneration); + CASE(INTERSECTION, Intersection); + CASE(ANY_HIT, AnyHit); + CASE(CLOSEST_HIT, ClosestHit); + CASE(MISS, Miss); + CASE(CALLABLE, Callable); + +#undef CASE + } +} + +class GraphicsCommonShaderObjectLayout : public ShaderObjectLayout +{ +public: + struct BindingRangeInfo + { + slang::BindingType bindingType; + Index count; + Index baseIndex; + Index descriptorSetIndex; + Index rangeIndexInDescriptorSet; + // Index subObjectRangeIndex = -1; + }; + + struct SubObjectRangeInfo + { + RefPtr<GraphicsCommonShaderObjectLayout> layout; + // Index baseIndex; + // Index count; + Index bindingRangeIndex; + }; + + struct DescriptorSetInfo : public RefObject + { + RefPtr<DescriptorSetLayout> layout; + Slang::Int space = -1; + }; + + struct Builder + { + public: + Builder(Renderer* renderer) + : m_renderer(renderer) + {} + + List<BindingRangeInfo> m_bindingRanges; + List<SubObjectRangeInfo> m_subObjectRanges; + + Index m_resourceViewCount = 0; + Index m_samplerCount = 0; + Index m_combinedTextureSamplerCount = 0; + Index m_subObjectCount = 0; + Index m_varyingInputCount = 0; + Index m_varyingOutputCount = 0; + + struct DescriptorSetBuildInfo : public RefObject + { + List<DescriptorSetLayout::SlotRangeDesc> slotRangeDescs; + Index space; + }; + List<RefPtr<DescriptorSetBuildInfo>> m_descriptorSetBuildInfos; + Dictionary<Index, Index> m_mapSpaceToDescriptorSetIndex; + + Index findOrAddDescriptorSet(Index space) + { + Index index; + if (m_mapSpaceToDescriptorSetIndex.TryGetValue(space, index)) + return index; + + RefPtr<DescriptorSetBuildInfo> info = new DescriptorSetBuildInfo(); + info->space = space; + + index = m_descriptorSetBuildInfos.getCount(); + m_descriptorSetBuildInfos.add(info); + + m_mapSpaceToDescriptorSetIndex.Add(space, index); + return index; + } + + static DescriptorSlotType _mapDescriptorType(slang::BindingType slangBindingType) + { + switch (slangBindingType) + { + default: + return DescriptorSlotType::Unknown; + +#define CASE(FROM, TO) \ + case slang::BindingType::FROM: \ + return DescriptorSlotType::TO + + CASE(Sampler, Sampler); + CASE(CombinedTextureSampler, CombinedImageSampler); + CASE(Texture, SampledImage); + CASE(MutableTexture, StorageImage); + CASE(TypedBuffer, UniformTexelBuffer); + CASE(MutableTypedBuffer, StorageTexelBuffer); + CASE(RawBuffer, UniformBuffer); + CASE(MutableRawBuffer, StorageBuffer); + CASE(InputRenderTarget, InputAttachment); + CASE(InlineUniformData, InlineUniformBlock); + CASE(RayTracingAccelerationStructure, RayTracingAccelerationStructure); + CASE(ConstantBuffer, UniformBuffer); + CASE(PushConstant, RootConstant); + +#undef CASE + } + } + + slang::TypeLayoutReflection* unwrapParameterGroups(slang::TypeLayoutReflection* typeLayout) + { + for (;;) + { + if (!typeLayout->getType()) + { + if (auto elementTypeLayout = typeLayout->getElementTypeLayout()) + typeLayout = elementTypeLayout; + } + + switch (typeLayout->getKind()) + { + default: + return typeLayout; + + case slang::TypeReflection::Kind::ConstantBuffer: + case slang::TypeReflection::Kind::ParameterBlock: + typeLayout = typeLayout->getElementTypeLayout(); + continue; + } + } + } + + void _addDescriptorSets( + slang::TypeLayoutReflection* typeLayout, + slang::VariableLayoutReflection* varLayout = nullptr) + { + SlangInt descriptorSetCount = typeLayout->getDescriptorSetCount(); + for (SlangInt s = 0; s < descriptorSetCount; ++s) + { + auto descriptorSetIndex = + findOrAddDescriptorSet(typeLayout->getDescriptorSetSpaceOffset(s)); + auto descriptorSetInfo = m_descriptorSetBuildInfos[descriptorSetIndex]; + + SlangInt descriptorRangeCount = typeLayout->getDescriptorSetDescriptorRangeCount(s); + for (SlangInt r = 0; r < descriptorRangeCount; ++r) + { + auto slangBindingType = typeLayout->getDescriptorSetDescriptorRangeType(s, r); + auto gfxDescriptorType = _mapDescriptorType(slangBindingType); + + DescriptorSetLayout::SlotRangeDesc descriptorRangeDesc; + descriptorRangeDesc.binding = + typeLayout->getDescriptorSetDescriptorRangeIndexOffset(s, r); + descriptorRangeDesc.count = + typeLayout->getDescriptorSetDescriptorRangeDescriptorCount(s, r); + descriptorRangeDesc.type = gfxDescriptorType; + + if (varLayout) + { + auto category = typeLayout->getDescriptorSetDescriptorRangeCategory(s, r); + descriptorRangeDesc.binding += varLayout->getOffset(category); + } + + descriptorSetInfo->slotRangeDescs.add(descriptorRangeDesc); + } + } + } + + Result setElementTypeLayout(slang::TypeLayoutReflection* typeLayout) + { + typeLayout = unwrapParameterGroups(typeLayout); + + m_elementTypeLayout = typeLayout; + + // First we will use the Slang layout information to allocate + // the descriptor set layout(s) required to store values + // of the given type. + // + _addDescriptorSets(typeLayout); + + // Next we will compute the binding ranges that are used to store + // the logical contents of the object in memory. These will relate + // to the descriptor ranges in the various sets, but not always + // in a one-to-one fashion. + + SlangInt bindingRangeCount = typeLayout->getBindingRangeCount(); + for (SlangInt r = 0; r < bindingRangeCount; ++r) + { + slang::BindingType slangBindingType = typeLayout->getBindingRangeType(r); + SlangInt count = typeLayout->getBindingRangeBindingCount(r); + slang::TypeLayoutReflection* slangLeafTypeLayout = + typeLayout->getBindingRangeLeafTypeLayout(r); + + SlangInt descriptorSetIndex = typeLayout->getBindingRangeDescriptorSetIndex(r); + SlangInt rangeIndexInDescriptorSet = + typeLayout->getBindingRangeFirstDescriptorRangeIndex(r); + + Index baseIndex = 0; + switch (slangBindingType) + { + case slang::BindingType::ConstantBuffer: + case slang::BindingType::ParameterBlock: + case slang::BindingType::ExistentialValue: + baseIndex = m_subObjectCount; + m_subObjectCount += count; + break; + + case slang::BindingType::Sampler: + baseIndex = m_samplerCount; + m_samplerCount += count; + break; + + case slang::BindingType::CombinedTextureSampler: + baseIndex = m_combinedTextureSamplerCount; + m_combinedTextureSamplerCount += count; + break; + + case slang::BindingType::VaryingInput: + baseIndex = m_varyingInputCount; + m_varyingInputCount += count; + break; + + case slang::BindingType::VaryingOutput: + baseIndex = m_varyingOutputCount; + m_varyingOutputCount += count; + break; + + default: + baseIndex = m_resourceViewCount; + m_resourceViewCount += count; + break; + } + + BindingRangeInfo bindingRangeInfo; + bindingRangeInfo.bindingType = slangBindingType; + bindingRangeInfo.count = count; + // bindingRangeInfo.descriptorSetIndex = descriptorSetIndex; + // bindingRangeInfo.rangeIndexInDescriptorSet = slotRangeIndex; + // bindingRangeInfo.subObjectRangeIndex = subObjectRangeIndex; + bindingRangeInfo.baseIndex = baseIndex; + bindingRangeInfo.descriptorSetIndex = descriptorSetIndex; + bindingRangeInfo.rangeIndexInDescriptorSet = rangeIndexInDescriptorSet; + + m_bindingRanges.add(bindingRangeInfo); + +#if 0 + SlangInt binding = typeLayout->getBindingRangeIndexOffset(r); + SlangInt space = typeLayout->getBindingRangeSpaceOffset(r); + SlangInt subObjectRangeIndex = typeLayout->getBindingRangeSubObjectRangeIndex(r); + + DescriptorSetLayout::SlotRangeDesc slotRange; + slotRange.type = _mapDescriptorType(slangBindingType); + slotRange.count = count; + slotRange.binding = binding; + + Index descriptorSetIndex = findOrAddDescriptorSet(space); + RefPtr<DescriptorSetBuildInfo> descriptorSetInfo = m_descriptorSetInfos[descriptorSetIndex]; + + Index slotRangeIndex = descriptorSetInfo->slotRanges.getCount(); + descriptorSetInfo->slotRanges.add(slotRange); +#endif + } + + SlangInt subObjectRangeCount = typeLayout->getSubObjectRangeCount(); + for (SlangInt r = 0; r < subObjectRangeCount; ++r) + { + SlangInt bindingRangeIndex = typeLayout->getSubObjectRangeBindingRangeIndex(r); + auto slangBindingType = typeLayout->getBindingRangeType(bindingRangeIndex); + slang::TypeLayoutReflection* slangLeafTypeLayout = + typeLayout->getBindingRangeLeafTypeLayout(bindingRangeIndex); + + // A sub-object range can either represent a sub-object of a known + // type, like a `ConstantBuffer<Foo>` or `ParameterBlock<Foo>` + // (in which case we can pre-compute a layout to use, based on + // the type `Foo`) *or* it can represent a sub-object of some + // existential type (e.g., `IBar`) in which case we cannot + // know the appropraite type/layout of sub-object to allocate. + // + RefPtr<GraphicsCommonShaderObjectLayout> subObjectLayout; + if (slangBindingType != slang::BindingType::ExistentialValue) + { + GraphicsCommonShaderObjectLayout::createForElementType( + m_renderer, + slangLeafTypeLayout->getElementTypeLayout(), + subObjectLayout.writeRef()); + } + + SubObjectRangeInfo subObjectRange; + subObjectRange.bindingRangeIndex = bindingRangeIndex; + subObjectRange.layout = subObjectLayout; + + m_subObjectRanges.add(subObjectRange); + } + +#if 0 + SlangInt subObjectRangeCount = typeLayout->getSubObjectRangeCount(); + for(SlangInt r = 0; r < subObjectRangeCount; ++r) + { + + // TODO: Still need a way to map the binding ranges for + // the sub-object over so that they can be used to + // set/get the sub-object as needed. + } +#endif + return SLANG_OK; + } + + SlangResult build(GraphicsCommonShaderObjectLayout** outLayout) + { + RefPtr<GraphicsCommonShaderObjectLayout> layout = + new GraphicsCommonShaderObjectLayout(); + SLANG_RETURN_ON_FAIL(layout->_init(this)); + + *outLayout = layout.detach(); + return SLANG_OK; + } + + Renderer* m_renderer = nullptr; + slang::TypeLayoutReflection* m_elementTypeLayout = nullptr; + }; + + static Result createForElementType( + Renderer* renderer, + slang::TypeLayoutReflection* elementType, + GraphicsCommonShaderObjectLayout** outLayout) + { + Builder builder(renderer); + builder.setElementTypeLayout(elementType); + return builder.build(outLayout); + } + + List<RefPtr<DescriptorSetInfo>> const& getDescriptorSets() { return m_descriptorSets; } + + List<BindingRangeInfo> const& getBindingRanges() { return m_bindingRanges; } + + Index getBindingRangeCount() { return m_bindingRanges.getCount(); } + + BindingRangeInfo const& getBindingRange(Index index) { return m_bindingRanges[index]; } + + slang::TypeLayoutReflection* getElementTypeLayout() { return m_elementTypeLayout; } + + Index getResourceViewCount() { return m_resourceViewCount; } + Index getSamplerCount() { return m_samplerCount; } + Index getCombinedTextureSamplerCount() { return m_combinedTextureSamplerCount; } + Index getSubObjectCount() { return m_subObjectCount; } + + SubObjectRangeInfo const& getSubObjectRange(Index index) { return m_subObjectRanges[index]; } + List<SubObjectRangeInfo> const& getSubObjectRanges() { return m_subObjectRanges; } + + Renderer* getRenderer() { return m_renderer; } + +protected: + Result _init(Builder const* builder) + { + auto renderer = builder->m_renderer; + m_renderer = renderer; + + m_elementTypeLayout = builder->m_elementTypeLayout; + m_bindingRanges = builder->m_bindingRanges; + + for (auto descriptorSetBuildInfo : builder->m_descriptorSetBuildInfos) + { + auto& slotRangeDescs = descriptorSetBuildInfo->slotRangeDescs; + DescriptorSetLayout::Desc desc; + desc.slotRangeCount = slotRangeDescs.getCount(); + desc.slotRanges = slotRangeDescs.getBuffer(); + + RefPtr<DescriptorSetLayout> descriptorSetLayout; + SLANG_RETURN_ON_FAIL( + m_renderer->createDescriptorSetLayout(desc, descriptorSetLayout.writeRef())); + + RefPtr<DescriptorSetInfo> descriptorSetInfo = new DescriptorSetInfo(); + descriptorSetInfo->layout = descriptorSetLayout; + descriptorSetInfo->space = descriptorSetBuildInfo->space; + + m_descriptorSets.add(descriptorSetInfo); + } + + m_resourceViewCount = builder->m_resourceViewCount; + m_samplerCount = builder->m_samplerCount; + m_combinedTextureSamplerCount = builder->m_combinedTextureSamplerCount; + m_subObjectCount = builder->m_subObjectCount; + + m_subObjectRanges = builder->m_subObjectRanges; + + return SLANG_OK; + } + + Renderer* m_renderer; + List<RefPtr<DescriptorSetInfo>> m_descriptorSets; + List<BindingRangeInfo> m_bindingRanges; + slang::TypeLayoutReflection* m_elementTypeLayout; + Index m_resourceViewCount = 0; + Index m_samplerCount = 0; + Index m_combinedTextureSamplerCount = 0; + Index m_subObjectCount = 0; + List<SubObjectRangeInfo> m_subObjectRanges; +}; + +class EntryPointLayout : public GraphicsCommonShaderObjectLayout +{ + typedef GraphicsCommonShaderObjectLayout Super; + +public: + struct VaryingInputInfo + {}; + + struct VaryingOutputInfo + {}; + + struct Builder : Super::Builder + { + Builder(Renderer* renderer) + : Super::Builder(renderer) + {} + + Result build(EntryPointLayout** outLayout) + { + RefPtr<EntryPointLayout> layout = new EntryPointLayout(); + SLANG_RETURN_ON_FAIL(layout->_init(this)); + + *outLayout = layout.detach(); + return SLANG_OK; + } + + void _addEntryPointParam(slang::VariableLayoutReflection* entryPointParam) + { + auto slangStage = entryPointParam->getStage(); + auto typeLayout = entryPointParam->getTypeLayout(); + + SlangInt bindingRangeCount = typeLayout->getBindingRangeCount(); + for (SlangInt r = 0; r < bindingRangeCount; ++r) + { + slang::BindingType slangBindingType = typeLayout->getBindingRangeType(r); + SlangInt count = typeLayout->getBindingRangeBindingCount(r); + + switch (slangBindingType) + { + default: + break; + + case slang::BindingType::VaryingInput: + { + VaryingInputInfo info; + + m_varyingInputs.add(info); + } + break; + + case slang::BindingType::VaryingOutput: + { + VaryingOutputInfo info; + m_varyingOutputs.add(info); + } + break; + } + } + } + + void addEntryPointParams(slang::EntryPointLayout* entryPointLayout) + { + m_slangEntryPointLayout = entryPointLayout; + + setElementTypeLayout(entryPointLayout->getTypeLayout()); + + m_stage = translateStage(entryPointLayout->getStage()); + _addEntryPointParam(entryPointLayout->getVarLayout()); + _addEntryPointParam(entryPointLayout->getResultVarLayout()); + } + + slang::EntryPointLayout* m_slangEntryPointLayout = nullptr; + + gfx::StageType m_stage; + List<VaryingInputInfo> m_varyingInputs; + List<VaryingOutputInfo> m_varyingOutputs; + }; + + Result _init(Builder const* builder) + { + auto renderer = builder->m_renderer; + + SLANG_RETURN_ON_FAIL(Super::_init(builder)); + + m_slangEntryPointLayout = builder->m_slangEntryPointLayout; + m_stage = builder->m_stage; + m_varyingInputs = builder->m_varyingInputs; + m_varyingOutputs = builder->m_varyingOutputs; + + return SLANG_OK; + } + + List<VaryingInputInfo> const& getVaryingInputs() { return m_varyingInputs; } + List<VaryingOutputInfo> const& getVaryingOutputs() { return m_varyingOutputs; } + + gfx::StageType getStage() const { return m_stage; } + + slang::EntryPointLayout* getSlangLayout() const { return m_slangEntryPointLayout; }; + + slang::EntryPointLayout* m_slangEntryPointLayout; + gfx::StageType m_stage; + List<VaryingInputInfo> m_varyingInputs; + List<VaryingOutputInfo> m_varyingOutputs; +}; + +class GraphicsCommonProgramLayout : public GraphicsCommonShaderObjectLayout +{ + typedef GraphicsCommonShaderObjectLayout Super; + +public: + struct EntryPointInfo + { + RefPtr<EntryPointLayout> layout; + Index rangeOffset; + }; + + struct Builder : Super::Builder + { + Builder(Renderer* renderer) + : Super::Builder(renderer) + {} + + Result build(GraphicsCommonProgramLayout** outLayout) + { + RefPtr<GraphicsCommonProgramLayout> layout = new GraphicsCommonProgramLayout(); + SLANG_RETURN_ON_FAIL(layout->_init(this)); + + *outLayout = layout.detach(); + return SLANG_OK; + } + + void addGlobalParams(slang::VariableLayoutReflection* globalsLayout) + { + setElementTypeLayout(globalsLayout->getTypeLayout()); + } + + void addEntryPoint(EntryPointLayout* entryPointLayout) + { + EntryPointInfo info; + info.layout = entryPointLayout; + + if (m_descriptorSetBuildInfos.getCount()) + { + info.rangeOffset = m_descriptorSetBuildInfos[0]->slotRangeDescs.getCount(); + } + + auto slangEntryPointLayout = entryPointLayout->getSlangLayout(); + _addDescriptorSets( + slangEntryPointLayout->getTypeLayout(), slangEntryPointLayout->getVarLayout()); + + m_entryPoints.add(info); + } + + List<EntryPointInfo> m_entryPoints; + }; + + Slang::Int getRenderTargetCount() { return m_renderTargetCount; } + + PipelineLayout* getPipelineLayout() { return m_pipelineLayout; } + + Index findEntryPointIndex(gfx::StageType stage) + { + auto entryPointCount = m_entryPoints.getCount(); + for (Index i = 0; i < entryPointCount; ++i) + { + auto entryPoint = m_entryPoints[i]; + if (entryPoint.layout->getStage() == stage) + return i; + } + return -1; + } + + EntryPointInfo const& getEntryPoint(Index index) { return m_entryPoints[index]; } + + List<EntryPointInfo> const& getEntryPoints() const { return m_entryPoints; } + +protected: + Result _init(Builder const* builder) + { + auto renderer = builder->m_renderer; + + SLANG_RETURN_ON_FAIL(Super::_init(builder)); + + m_entryPoints = builder->m_entryPoints; + + List<PipelineLayout::DescriptorSetDesc> pipelineDescriptorSets; + _addDescriptorSetsRec(this, pipelineDescriptorSets); + +#if 0 + _createInputLayout(builder); +#endif + + auto fragmentEntryPointIndex = findEntryPointIndex(gfx::StageType::Fragment); + if (fragmentEntryPointIndex != -1) + { + auto fragmentEntryPoint = getEntryPoint(fragmentEntryPointIndex); + m_renderTargetCount = fragmentEntryPoint.layout->getVaryingOutputs().getCount(); + } + + PipelineLayout::Desc pipelineLayoutDesc; + pipelineLayoutDesc.renderTargetCount = m_renderTargetCount; + pipelineLayoutDesc.descriptorSetCount = pipelineDescriptorSets.getCount(); + pipelineLayoutDesc.descriptorSets = pipelineDescriptorSets.getBuffer(); + + SLANG_RETURN_ON_FAIL( + renderer->createPipelineLayout(pipelineLayoutDesc, m_pipelineLayout.writeRef())); + + return SLANG_OK; + } + + static void _addDescriptorSetsRec( + GraphicsCommonShaderObjectLayout* layout, + List<PipelineLayout::DescriptorSetDesc>& ioPipelineDescriptorSets) + { + for (auto descriptorSetInfo : layout->getDescriptorSets()) + { + PipelineLayout::DescriptorSetDesc pipelineDescriptorSet; + pipelineDescriptorSet.layout = descriptorSetInfo->layout; + pipelineDescriptorSet.space = descriptorSetInfo->space; + + ioPipelineDescriptorSets.add(pipelineDescriptorSet); + } + + // TODO: next we need to recurse into the "sub-objects" of `layout` and + // add their descriptor sets as well. + } + +#if 0 + Result _createInputLayout(Builder const* builder) + { + auto renderer = builder->m_renderer; + + List<InputElementDesc> const& inputElements = builder->getInputElements(); + SLANG_RETURN_ON_FAIL(renderer->createInputLayout(inputElements.getBuffer(), inputElements.getCount(), m_inputLayout.writeRef())); + + return SLANG_OK; + } +#endif + + List<EntryPointInfo> m_entryPoints; + gfx::UInt m_renderTargetCount = 0; + + RefPtr<PipelineLayout> m_pipelineLayout; +}; + +class GraphicsCommonShaderObject : public ShaderObject +{ +public: + static Result create( + Renderer* renderer, + GraphicsCommonShaderObjectLayout* layout, + GraphicsCommonShaderObject** outShaderObject) + { + RefPtr<GraphicsCommonShaderObject> object = new GraphicsCommonShaderObject(); + SLANG_RETURN_ON_FAIL(object->init(renderer, layout)); + + *outShaderObject = object.detach(); + return SLANG_OK; + } + + Renderer* getRenderer() { return m_layout->getRenderer(); } + + Index getEntryPointCount() { return 0; } + + ShaderObject* getEntryPoint(Index index) { return nullptr; } + + GraphicsCommonShaderObjectLayout* getLayout() { return m_layout; } + + slang::TypeLayoutReflection* getElementTypeLayout() { return m_layout->getElementTypeLayout(); } + + SlangResult setData(ShaderOffset const& offset, void const* data, size_t size) + { + Renderer* renderer = getRenderer(); + + char* dest = (char*)renderer->map(m_buffer, MapFlavor::HostWrite); + memcpy(dest + offset.uniformOffset, data, size); + renderer->unmap(m_buffer); + + return SLANG_OK; + } + + virtual SlangResult setObject(ShaderOffset const& offset, ShaderObject* object) SLANG_OVERRIDE + { + if (offset.bindingRangeIndex < 0) + return SLANG_E_INVALID_ARG; + if (offset.bindingRangeIndex >= m_layout->getBindingRangeCount()) + return SLANG_E_INVALID_ARG; + auto& bindingRange = m_layout->getBindingRange(offset.bindingRangeIndex); + + // TODO: Is this reasonable to store the base index directly in the binding range? + m_objects[bindingRange.baseIndex + offset.bindingArrayIndex] = + reinterpret_cast<GraphicsCommonShaderObject*>(object); + + // auto& subObjectRange = + // m_layout->getSubObjectRange(bindingRange.subObjectRangeIndex); + // m_objects[subObjectRange.baseIndex + offset.bindingArrayIndex] = object; + +#if 0 + + SLANG_ASSERT(bindingRange.descriptorSetIndex >= 0); + SLANG_ASSERT(bindingRange.descriptorSetIndex < m_descriptorSets.getCount()); + auto& descriptorSet = m_descriptorSets[bindingRange.descriptorSetIndex]; + + descriptorSet->setConstantBuffer(bindingRange.rangeIndexInDescriptorSet, offset.bindingArrayIndex, buffer); + return SLANG_OK; +#else + return SLANG_E_NOT_IMPLEMENTED; +#endif + } + + virtual SlangResult getObject(ShaderOffset const& offset, ShaderObject** outObject) SLANG_OVERRIDE + { + SLANG_ASSERT(outObject); + if (offset.bindingRangeIndex < 0) + return SLANG_E_INVALID_ARG; + if (offset.bindingRangeIndex >= m_layout->getBindingRangeCount()) + return SLANG_E_INVALID_ARG; + auto& bindingRange = m_layout->getBindingRange(offset.bindingRangeIndex); + + *outObject = m_objects[bindingRange.baseIndex + offset.bindingArrayIndex]; + + // auto& subObjectRange = + // m_layout->getSubObjectRange(bindingRange.subObjectRangeIndex); *outObject = + // m_objects[subObjectRange.baseIndex + offset.bindingArrayIndex]; + + return SLANG_OK; + +#if 0 + SLANG_ASSERT(bindingRange.descriptorSetIndex >= 0); + SLANG_ASSERT(bindingRange.descriptorSetIndex < m_descriptorSets.getCount()); + auto& descriptorSet = m_descriptorSets[bindingRange.descriptorSetIndex]; + + descriptorSet->setConstantBuffer(bindingRange.rangeIndexInDescriptorSet, offset.bindingArrayIndex, buffer); + return SLANG_OK; +#endif + } + + ShaderObject* getObject(ShaderOffset const& offset) + { + ShaderObject* object = nullptr; + SLANG_RETURN_NULL_ON_FAIL(getObject(offset, &object)); + return object; + } + + SlangResult setResource(ShaderOffset const& offset, ResourceView* resourceView) + { + if (offset.bindingRangeIndex < 0) + return SLANG_E_INVALID_ARG; + if (offset.bindingRangeIndex >= m_layout->getBindingRangeCount()) + return SLANG_E_INVALID_ARG; + auto& bindingRange = m_layout->getBindingRange(offset.bindingRangeIndex); + + m_resourceViews[bindingRange.baseIndex + offset.bindingArrayIndex] = resourceView; + return SLANG_OK; + } + + SlangResult setSampler(ShaderOffset const& offset, SamplerState* sampler) + { + if (offset.bindingRangeIndex < 0) + return SLANG_E_INVALID_ARG; + if (offset.bindingRangeIndex >= m_layout->getBindingRangeCount()) + return SLANG_E_INVALID_ARG; + auto& bindingRange = m_layout->getBindingRange(offset.bindingRangeIndex); + + m_samplers[bindingRange.baseIndex + offset.bindingArrayIndex] = sampler; + return SLANG_OK; + } + + SlangResult setCombinedTextureSampler( + ShaderOffset const& offset, ResourceView* textureView, SamplerState* sampler) + { + if (offset.bindingRangeIndex < 0) + return SLANG_E_INVALID_ARG; + if (offset.bindingRangeIndex >= m_layout->getBindingRangeCount()) + return SLANG_E_INVALID_ARG; + auto& bindingRange = m_layout->getBindingRange(offset.bindingRangeIndex); + + auto& slot = m_combinedTextureSamplers[bindingRange.baseIndex + offset.bindingArrayIndex]; + slot.textureView = textureView; + slot.sampler = sampler; + return SLANG_OK; + } + +protected: + friend class ProgramVars; + + Result init(Renderer* renderer, GraphicsCommonShaderObjectLayout* layout) + { + m_layout = layout; + + // If the layout tells us that there is any uniform data, + // then we need to allocate a constant buffer to hold that data. + // + // TODO: Do we need to allocate a shadow copy for use from + // the CPU? + // + // TODO: When/where do we bind this constant buffer into + // a descriptor set for later use? + // + size_t uniformSize = layout->getElementTypeLayout()->getSize(); + if (uniformSize) + { + BufferResource::Desc bufferDesc; + bufferDesc.init(uniformSize); + bufferDesc.cpuAccessFlags |= Resource::AccessFlag::Write; + SLANG_RETURN_ON_FAIL(renderer->createBufferResource( + Resource::Usage::ConstantBuffer, bufferDesc, nullptr, m_buffer.writeRef())); + } + +#if 0 + // If the layout tells us there are any descriptor sets to + // allocate, then we do so now. + // + for(auto descriptorSetInfo : layout->getDescriptorSets()) + { + RefPtr<DescriptorSet> descriptorSet; + SLANG_RETURN_ON_FAIL(renderer->createDescriptorSet(descriptorSetInfo->layout, descriptorSet.writeRef())); + m_descriptorSets.add(descriptorSet); + } +#endif + + m_resourceViews.setCount(layout->getResourceViewCount()); + m_samplers.setCount(layout->getSamplerCount()); + m_combinedTextureSamplers.setCount(layout->getCombinedTextureSamplerCount()); + + // If the layout specifies that we have any sub-objects, then + // we need to size the array to account for them. + // + Index subObjectCount = layout->getSubObjectCount(); + m_objects.setCount(subObjectCount); + + for (auto subObjectRangeInfo : layout->getSubObjectRanges()) + { + RefPtr<GraphicsCommonShaderObjectLayout> subObjectLayout = subObjectRangeInfo.layout; + + // In the case where the sub-object range represents an + // existential-type leaf field (e.g., an `IBar`), we + // cannot pre-allocate the objet(s) to go into that + // range, since we can't possibly know what to allocate + // at this point. + // + if (!subObjectLayout) + continue; + // + // Otherwise, we will allocate a sub-object to fill + // in each entry in this range, based on the layout + // information we already have. + + auto& bindingRangeInfo = layout->getBindingRange(subObjectRangeInfo.bindingRangeIndex); + for (Index i = 0; i < bindingRangeInfo.count; ++i) + { + RefPtr<GraphicsCommonShaderObject> subObject; + SLANG_RETURN_ON_FAIL( + GraphicsCommonShaderObject::create(renderer, subObjectLayout, subObject.writeRef())); + m_objects[bindingRangeInfo.baseIndex + i] = subObject; + } + } + + return SLANG_OK; + } + + Result apply( + Renderer* renderer, + PipelineType pipelineType, + PipelineLayout* pipelineLayout, + Index& ioRootIndex) + { + GraphicsCommonShaderObjectLayout* layout = m_layout; + + // Create the descritpor sets required by the layout... + // + List<RefPtr<DescriptorSet>> descriptorSets; + for (auto descriptorSetInfo : layout->getDescriptorSets()) + { + RefPtr<DescriptorSet> descriptorSet; + SLANG_RETURN_ON_FAIL( + renderer->createDescriptorSet(descriptorSetInfo->layout, descriptorSet.writeRef())); + descriptorSets.add(descriptorSet); + } + + SLANG_RETURN_ON_FAIL(_bindIntoDescriptorSets(descriptorSets.getBuffer())); + + for (auto descriptorSet : descriptorSets) + { + renderer->setDescriptorSet(pipelineType, pipelineLayout, ioRootIndex++, descriptorSet); + } + + return SLANG_OK; + } + + Result _bindIntoDescriptorSet( + DescriptorSet* descriptorSet, Index baseRangeIndex, Index subObjectRangeArrayIndex) + { + GraphicsCommonShaderObjectLayout* layout = m_layout; + + if (m_buffer) + { + descriptorSet->setConstantBuffer(baseRangeIndex, subObjectRangeArrayIndex, m_buffer); + baseRangeIndex++; + } + + for (auto bindingRangeInfo : layout->getBindingRanges()) + { + switch (bindingRangeInfo.bindingType) + { + case slang::BindingType::VaryingInput: + case slang::BindingType::VaryingOutput: + continue; + + default: + break; + } + + SLANG_ASSERT(bindingRangeInfo.descriptorSetIndex == 0); + + auto count = bindingRangeInfo.count; + auto baseIndex = bindingRangeInfo.baseIndex; + + auto descriptorRangeIndex = baseRangeIndex + bindingRangeInfo.rangeIndexInDescriptorSet; + auto descriptorArrayBaseIndex = subObjectRangeArrayIndex * count; + + switch (bindingRangeInfo.bindingType) + { + case slang::BindingType::ConstantBuffer: + case slang::BindingType::ParameterBlock: + break; + + case slang::BindingType::ExistentialValue: + // + // TODO: If the existential value is one that "fits" into the storage available, + // then we should write its data directly into that area. Otherwise, we need + // to bind its content as "pending" data which will come after any other data + // beloning to the same set (that is, it's starting descriptorRangeIndex will + // need to be one after the number of ranges accounted for in the original type) + // + break; + + case slang::BindingType::CombinedTextureSampler: + for (Index i = 0; i < count; ++i) + { + auto& slot = m_combinedTextureSamplers[baseIndex + i]; + descriptorSet->setCombinedTextureSampler( + descriptorRangeIndex, + descriptorArrayBaseIndex + i, + slot.textureView, + slot.sampler); + } + break; + + case slang::BindingType::Sampler: + for (Index i = 0; i < count; ++i) + { + descriptorSet->setSampler( + descriptorRangeIndex, + descriptorArrayBaseIndex + i, + m_samplers[baseIndex + i]); + } + break; + + default: + for (Index i = 0; i < count; ++i) + { + descriptorSet->setResource( + descriptorRangeIndex, + descriptorArrayBaseIndex + i, + m_resourceViews[baseIndex + i]); + } + break; + } + } + + return SLANG_OK; + } + +public: + virtual Result _bindIntoDescriptorSets(RefPtr<DescriptorSet>* descriptorSets) + { + GraphicsCommonShaderObjectLayout* layout = m_layout; + + if (m_buffer) + { + // TODO: look up binding infor for default constant buffer... + descriptorSets[0]->setConstantBuffer(0, 0, m_buffer); + } + + // Fill in the descriptor sets based on binding ranges + // + for (auto bindingRangeInfo : layout->getBindingRanges()) + { + DescriptorSet* descriptorSet = descriptorSets[bindingRangeInfo.descriptorSetIndex]; + auto rangeIndex = bindingRangeInfo.rangeIndexInDescriptorSet; + auto baseIndex = bindingRangeInfo.baseIndex; + auto count = bindingRangeInfo.count; + switch (bindingRangeInfo.bindingType) + { + case slang::BindingType::ConstantBuffer: + case slang::BindingType::ParameterBlock: + for (Index i = 0; i < count; ++i) + { + GraphicsCommonShaderObject* subObject = m_objects[baseIndex + i]; + + subObject->_bindIntoDescriptorSet(descriptorSet, rangeIndex, i); + } + break; + + case slang::BindingType::CombinedTextureSampler: + for (Index i = 0; i < count; ++i) + { + auto& slot = m_combinedTextureSamplers[baseIndex + i]; + descriptorSet->setCombinedTextureSampler( + rangeIndex, i, slot.textureView, slot.sampler); + } + break; + + case slang::BindingType::Sampler: + for (Index i = 0; i < count; ++i) + { + descriptorSet->setSampler(rangeIndex, i, m_samplers[baseIndex + i]); + } + break; + + case slang::BindingType::VaryingInput: + case slang::BindingType::VaryingOutput: + break; + + case slang::BindingType::ExistentialValue: + // Here we are binding as if existential value is the same + // as a constant buffer or parameter block, which will lead + // to incorrect results... + for (Index i = 0; i < count; ++i) + { + GraphicsCommonShaderObject* subObject = m_objects[baseIndex + i]; + + subObject->_bindIntoDescriptorSet(descriptorSet, rangeIndex, i); + } + break; + + default: + for (Index i = 0; i < count; ++i) + { + descriptorSet->setResource(rangeIndex, i, m_resourceViews[baseIndex + i]); + } + break; + } + } + return SLANG_OK; + } + + RefPtr<GraphicsCommonShaderObjectLayout> m_layout = nullptr; + RefPtr<BufferResource> m_buffer; + + List<RefPtr<ResourceView>> m_resourceViews; + + List<RefPtr<SamplerState>> m_samplers; + + struct CombinedTextureSamplerSlot + { + RefPtr<ResourceView> textureView; + RefPtr<SamplerState> sampler; + }; + List<CombinedTextureSamplerSlot> m_combinedTextureSamplers; + + // List<RefPtr<DescriptorSet>> m_descriptorSets; + List<RefPtr<GraphicsCommonShaderObject>> m_objects; +}; + +class EntryPointVars : public GraphicsCommonShaderObject +{ + typedef GraphicsCommonShaderObject Super; + +public: + static Result + create(Renderer* renderer, EntryPointLayout* layout, EntryPointVars** outShaderObject) + { + RefPtr<EntryPointVars> object = new EntryPointVars(); + SLANG_RETURN_ON_FAIL(object->init(renderer, layout)); + + *outShaderObject = object.detach(); + return SLANG_OK; + } + + EntryPointLayout* getLayout() { return static_cast<EntryPointLayout*>(m_layout.Ptr()); } + +protected: + Result init(Renderer* renderer, EntryPointLayout* layout) + { + SLANG_RETURN_ON_FAIL(Super::init(renderer, layout)); + return SLANG_OK; + } +}; + +class ProgramVars : public GraphicsCommonShaderObject +{ + typedef GraphicsCommonShaderObject Super; + +public: + static Result create(Renderer* renderer, GraphicsCommonProgramLayout* layout, ProgramVars** outShaderObject) + { + RefPtr<ProgramVars> object = new ProgramVars(); + SLANG_RETURN_ON_FAIL(object->init(renderer, layout)); + + *outShaderObject = object.detach(); + return SLANG_OK; + } + + GraphicsCommonProgramLayout* getLayout() { return static_cast<GraphicsCommonProgramLayout*>(m_layout.Ptr()); } + + void apply(Renderer* renderer, PipelineType pipelineType) + { + auto pipelineLayout = getLayout()->getPipelineLayout(); + + Index rootIndex = 0; + GraphicsCommonShaderObject::apply(renderer, pipelineType, pipelineLayout, rootIndex); + +#if 0 + + Index descriptorSetCount = m_descriptorSets.getCount(); + for(Index descriptorSetIndex = 0; descriptorSetIndex < descriptorSetCount; ++descriptorSetIndex) + { + renderer->setDescriptorSet( + pipelineType, + pipelineLayout, + descriptorSetIndex, + m_descriptorSets[descriptorSetIndex]); + } +#endif + + // TODO: We also need to bind any descriptor sets that are + // part of sub-objects of this object. + } + + List<RefPtr<EntryPointVars>> const& getEntryPoints() const { return m_entryPoints; } + + + Index getEntryPointCount() { return m_entryPoints.getCount(); } + ShaderObject* getEntryPoint(Index index) { return m_entryPoints[index]; } + + +protected: + virtual Result _bindIntoDescriptorSets(RefPtr<DescriptorSet>* descriptorSets) + { + SLANG_RETURN_ON_FAIL(Super::_bindIntoDescriptorSets(descriptorSets)); + + auto entryPointCount = m_entryPoints.getCount(); + for (Index i = 0; i < entryPointCount; ++i) + { + auto entryPoint = m_entryPoints[i]; + auto& entryPointInfo = getLayout()->getEntryPoint(i); + + SLANG_RETURN_ON_FAIL(entryPoint->_bindIntoDescriptorSet( + descriptorSets[0], entryPointInfo.rangeOffset, 0)); + } + + return SLANG_OK; + } + + Result init(Renderer* renderer, GraphicsCommonProgramLayout* layout) + { + SLANG_RETURN_ON_FAIL(Super::init(renderer, layout)); + + for (auto entryPointInfo : layout->getEntryPoints()) + { + RefPtr<EntryPointVars> entryPoint; + SLANG_RETURN_ON_FAIL( + EntryPointVars::create(renderer, entryPointInfo.layout, entryPoint.writeRef())); + m_entryPoints.add(entryPoint); + } + + return SLANG_OK; + } + + List<RefPtr<EntryPointVars>> m_entryPoints; +}; + + +Result GraphicsAPIRenderer::createShaderObjectLayout( + slang::TypeLayoutReflection* typeLayout, ShaderObjectLayout** outLayout) +{ + RefPtr<GraphicsCommonShaderObjectLayout> layout; + SLANG_RETURN_ON_FAIL(GraphicsCommonShaderObjectLayout::createForElementType( + this, typeLayout, layout.writeRef())); + *outLayout = layout.detach(); + return SLANG_OK; +} + +Result GraphicsAPIRenderer::createShaderObject(ShaderObjectLayout* layout, ShaderObject** outObject) +{ + RefPtr<GraphicsCommonShaderObject> shaderObject; + SLANG_RETURN_ON_FAIL(GraphicsCommonShaderObject::create(this, + reinterpret_cast<GraphicsCommonShaderObjectLayout*>(layout), shaderObject.writeRef())); + *outObject = shaderObject.detach(); + return SLANG_OK; +} + +Result GraphicsAPIRenderer::createRootShaderObject( + ShaderObjectLayout* rootLayout, ShaderObject** outObject) +{ + RefPtr<ProgramVars> shaderObject; + SLANG_RETURN_ON_FAIL(ProgramVars::create(this, + dynamic_cast<GraphicsCommonProgramLayout*>(rootLayout), + shaderObject.writeRef())); + *outObject = shaderObject.detach(); + return SLANG_OK; +} + +Result GraphicsAPIRenderer::createRootShaderObjectLayout( + slang::ProgramLayout* layout, ShaderObjectLayout** outLayout) +{ + RefPtr<GraphicsCommonProgramLayout> programLayout; + auto slangReflection = layout; + { + GraphicsCommonProgramLayout::Builder builder(this); + builder.addGlobalParams(slangReflection->getGlobalParamsVarLayout()); + + // TODO: Also need to reflect entry points here. + + SlangInt entryPointCount = slangReflection->getEntryPointCount(); + for (SlangInt e = 0; e < entryPointCount; ++e) + { + auto slangEntryPoint = slangReflection->getEntryPointByIndex(e); + + EntryPointLayout::Builder entryPointBuilder(this); + entryPointBuilder.addEntryPointParams(slangEntryPoint); + + RefPtr<EntryPointLayout> entryPointLayout; + SLANG_RETURN_ON_FAIL(entryPointBuilder.build(entryPointLayout.writeRef())); + + builder.addEntryPoint(entryPointLayout); + } + + SLANG_RETURN_ON_FAIL(builder.build(programLayout.writeRef())); + } + *outLayout = programLayout.detach(); + return SLANG_OK; +} + +Result GraphicsAPIRenderer::bindRootShaderObject(PipelineType pipelineType, ShaderObject* object) +{ + auto programVars = dynamic_cast<ProgramVars*>(object); + if (!programVars) + return SLANG_E_INVALID_HANDLE; + + programVars->apply(this, pipelineType); + return SLANG_OK; +} + +void GraphicsAPIRenderer::preparePipelineDesc(GraphicsPipelineStateDesc& desc) +{ + if (desc.rootShaderObjectLayout) + { + auto rootLayout = dynamic_cast<GraphicsCommonProgramLayout*>(desc.rootShaderObjectLayout); + desc.renderTargetCount = rootLayout->getRenderTargetCount(); + desc.pipelineLayout = rootLayout->getPipelineLayout(); + } +} + +void GraphicsAPIRenderer::preparePipelineDesc(ComputePipelineStateDesc& desc) +{ + if (desc.rootShaderObjectLayout) + { + auto rootLayout = dynamic_cast<GraphicsCommonProgramLayout*>(desc.rootShaderObjectLayout); + desc.pipelineLayout = rootLayout->getPipelineLayout(); + } +} + +} diff --git a/tools/gfx/render-graphics-common.h b/tools/gfx/render-graphics-common.h new file mode 100644 index 000000000..5bb4cb59e --- /dev/null +++ b/tools/gfx/render-graphics-common.h @@ -0,0 +1,22 @@ +#pragma once + +#include "tools/gfx/render.h" + +namespace gfx +{ + +class GraphicsAPIRenderer : public Renderer +{ +public: + virtual Result createShaderObjectLayout( + slang::TypeLayoutReflection* typeLayout, ShaderObjectLayout** outLayout) SLANG_OVERRIDE; + virtual Result createRootShaderObjectLayout( + slang::ProgramLayout* programLayout, ShaderObjectLayout** outLayout) SLANG_OVERRIDE; + virtual Result createShaderObject(ShaderObjectLayout* layout, ShaderObject** outObject) SLANG_OVERRIDE; + virtual Result createRootShaderObject(ShaderObjectLayout* rootLayout, ShaderObject** outObject) SLANG_OVERRIDE; + virtual Result bindRootShaderObject(PipelineType pipelineType, ShaderObject* object) SLANG_OVERRIDE; + void preparePipelineDesc(GraphicsPipelineStateDesc& desc); + void preparePipelineDesc(ComputePipelineStateDesc& desc); +}; + +} diff --git a/tools/gfx/render.h b/tools/gfx/render.h index 5ad338594..fb9e10724 100644 --- a/tools/gfx/render.h +++ b/tools/gfx/render.h @@ -634,6 +634,38 @@ public: void const* data) = 0; }; +struct ShaderOffset +{ + SlangInt uniformOffset = 0; + SlangInt bindingRangeIndex = 0; + SlangInt bindingArrayIndex = 0; +}; + +class ShaderObjectLayout : public Slang::RefObject +{}; + +class ShaderObject : public Slang::RefObject +{ +public: + ShaderObject* getObject(ShaderOffset const& offset) + { + ShaderObject* object = nullptr; + SLANG_RETURN_NULL_ON_FAIL(getObject(offset, &object)); + return object; + } + + virtual slang::TypeLayoutReflection* getElementTypeLayout() = 0; + virtual Slang::Index getEntryPointCount() = 0; + virtual ShaderObject* getEntryPoint(Slang::Index index) = 0; + virtual SlangResult setData(ShaderOffset const& offset, void const* data, size_t size) = 0; + virtual SlangResult getObject(ShaderOffset const& offset, ShaderObject** object) = 0; + virtual SlangResult setObject(ShaderOffset const& offset, ShaderObject* object) = 0; + virtual SlangResult setResource(ShaderOffset const& offset, ResourceView* resourceView) = 0; + virtual SlangResult setSampler(ShaderOffset const& offset, SamplerState* sampler) = 0; + virtual SlangResult setCombinedTextureSampler( + ShaderOffset const& offset, ResourceView* textureView, SamplerState* sampler) = 0; +}; + enum class StencilOp : uint8_t { Keep, @@ -779,11 +811,14 @@ struct BlendDesc struct GraphicsPipelineStateDesc { ShaderProgram* program; - PipelineLayout* pipelineLayout; + // Application should set either pipelineLayout or rootShaderObjectLayout, but not both. + PipelineLayout* pipelineLayout = nullptr; + // Application should set either pipelineLayout or rootShaderObjectLayout, but not both. + ShaderObjectLayout* rootShaderObjectLayout = nullptr; InputLayout* inputLayout; UInt framebufferWidth; UInt framebufferHeight; - UInt renderTargetCount; + UInt renderTargetCount = 0; // Not used if rootShaderObjectLayout is set. DepthStencilDesc depthStencil; RasterizerDesc rasterizer; BlendDesc blend; @@ -792,7 +827,8 @@ struct GraphicsPipelineStateDesc struct ComputePipelineStateDesc { ShaderProgram* program; - PipelineLayout* pipelineLayout; + PipelineLayout* pipelineLayout = nullptr; + ShaderObjectLayout* rootShaderObjectLayout = nullptr; }; class PipelineState : public Slang::RefObject @@ -910,6 +946,45 @@ public: return layout; } + virtual Result createShaderObjectLayout( + slang::TypeLayoutReflection* typeLayout, ShaderObjectLayout** outLayout) = 0; + + inline RefPtr<ShaderObjectLayout> createShaderObjectLayout(slang::TypeLayoutReflection* typeLayout) + { + RefPtr<ShaderObjectLayout> layout; + SLANG_RETURN_NULL_ON_FAIL(createShaderObjectLayout(typeLayout, layout.writeRef())); + return layout; + } + + virtual Result createRootShaderObjectLayout(slang::ProgramLayout* layout, ShaderObjectLayout** outLayout) = 0; + + inline RefPtr<ShaderObjectLayout> createRootShaderObjectLayout(slang::ProgramLayout* layout) + { + RefPtr<ShaderObjectLayout> result; + SLANG_RETURN_NULL_ON_FAIL(createRootShaderObjectLayout(layout, result.writeRef())); + return result; + } + + virtual Result createShaderObject(ShaderObjectLayout* layout, ShaderObject** outObject) = 0; + + inline RefPtr<ShaderObject> createShaderObject(ShaderObjectLayout* layout) + { + RefPtr<ShaderObject> object; + SLANG_RETURN_NULL_ON_FAIL(createShaderObject(layout, object.writeRef())); + return object; + } + + virtual Result createRootShaderObject(ShaderObjectLayout* layout, ShaderObject** outObject) = 0; + + inline RefPtr<ShaderObject> createRootShaderObject(ShaderObjectLayout* layout) + { + RefPtr<ShaderObject> object; + SLANG_RETURN_NULL_ON_FAIL(createRootShaderObject(layout, object.writeRef())); + return object; + } + + virtual Result bindRootShaderObject(PipelineType pipelineType, ShaderObject* object) = 0; + virtual Result createPipelineLayout(const PipelineLayout::Desc& desc, PipelineLayout** outLayout) = 0; inline RefPtr<PipelineLayout> createPipelineLayout(const PipelineLayout::Desc& desc) diff --git a/tools/gfx/shader-cursor.cpp b/tools/gfx/shader-cursor.cpp new file mode 100644 index 000000000..c4a9ac607 --- /dev/null +++ b/tools/gfx/shader-cursor.cpp @@ -0,0 +1,249 @@ +#include "shader-cursor.h" + +namespace gfx +{ + +Result gfx::ShaderCursor::getDereferenced(ShaderCursor& outCursor) const +{ + switch (m_typeLayout->getKind()) + { + default: + return SLANG_E_INVALID_ARG; + + case slang::TypeReflection::Kind::ConstantBuffer: + case slang::TypeReflection::Kind::ParameterBlock: + { + ShaderObject* subObject = m_baseObject->getObject(m_offset); + outCursor = ShaderCursor(subObject); + return SLANG_OK; + } + } +} + +Result ShaderCursor::getField(Slang::UnownedStringSlice const& name, ShaderCursor& outCursor) +{ + // If this cursor is invalid, then can't possible fetch a field. + // + if (!isValid()) + return SLANG_E_INVALID_ARG; + + // If the cursor is valid, we want to consider the type of data + // it is referencing. + // + switch (m_typeLayout->getKind()) + { + // The easy/expected case is when the value has a structure type. + // + case slang::TypeReflection::Kind::Struct: + { + // We start by looking up the index of a field matching `name`. + // + // If there is no such field, we have an error. + // + SlangInt fieldIndex = m_typeLayout->findFieldIndexByName(name.begin(), name.end()); + if (fieldIndex == -1) + return SLANG_E_INVALID_ARG; + + // Once we know the index of the field being referenced, + // we create a cursor to point at the field, based on + // the offset information already in this cursor, plus + // offsets derived from the field's layout. + // + slang::VariableLayoutReflection* fieldLayout = + m_typeLayout->getFieldByIndex((unsigned int)fieldIndex); + ShaderCursor fieldCursor; + + // The field cursorwill point into the same parent object. + // + fieldCursor.m_baseObject = m_baseObject; + + // The type being pointed to is the tyep of the field. + // + fieldCursor.m_typeLayout = fieldLayout->getTypeLayout(); + + // The byte offset is the current offset plus the relative offset of the field. + // The offset in binding ranges is computed similarly. + // + fieldCursor.m_offset.uniformOffset = m_offset.uniformOffset + fieldLayout->getOffset(); + fieldCursor.m_offset.bindingRangeIndex = + m_offset.bindingRangeIndex + m_typeLayout->getFieldBindingRangeOffset(fieldIndex); + + // The index of the field within any binding ranges will be the same + // as the index computed for the parent structure. + // + // Note: this case would arise for an array of structures with texture-type + // fields. Suppose we have: + // + // struct S { Texture2D t; Texture2D u; } + // S g[4]; + // + // In this scenario, `g` holds two binding ranges: + // + // * Range #0 comprises 4 textures, representing `g[...].t` + // * Range #1 comprises 4 textures, representing `g[...].u` + // + // A cursor for `g[2]` would have a `bindingRangeIndex` of zero but + // a `bindingArrayIndex` of 2, iindicating that we could end up + // referencing either range, but no matter what we know the index + // is 2. Thus when we form a cursor for `g[2].u` we want to + // apply the binding range offset to get a `bindingRangeIndex` of + // 1, while the `bindingArrayIndex` is unmodified. + // + // The result is that `g[2].u` is stored in range #1 at array index 2. + // + fieldCursor.m_offset.bindingArrayIndex = m_offset.bindingArrayIndex; + + outCursor = fieldCursor; + return SLANG_OK; + } + break; + + // In some cases the user might be trying to acess a field by name + // from a cursor that references a constant buffer or parameter block, + // and in these cases we want the access to Just Work. + // + case slang::TypeReflection::Kind::ConstantBuffer: + case slang::TypeReflection::Kind::ParameterBlock: + { + // We basically need to "dereference" the current cursor + // to go from a pointer to a constant buffer to a pointer + // to the *contents* of the constant buffer. + // + ShaderCursor d = getDereferenced(); + return d.getField(name, outCursor); + } + break; + } + + return SLANG_E_INVALID_ARG; +} + +ShaderCursor ShaderCursor::getElement(Slang::Index index) +{ + // TODO: need to auto-dereference various buffer types... + + if (m_typeLayout->getKind() == slang::TypeReflection::Kind::Array) + { + ShaderCursor elementCursor; + elementCursor.m_baseObject = m_baseObject; + elementCursor.m_typeLayout = m_typeLayout->getElementTypeLayout(); + elementCursor.m_offset.uniformOffset = + m_offset.uniformOffset + + index * m_typeLayout->getElementStride(SLANG_PARAMETER_CATEGORY_UNIFORM); + elementCursor.m_offset.bindingRangeIndex = m_offset.bindingRangeIndex; + elementCursor.m_offset.bindingArrayIndex = + m_offset.bindingArrayIndex * m_typeLayout->getElementCount() + index; + return elementCursor; + } + + return ShaderCursor(); +} + + +static int _peek(Slang::UnownedStringSlice const& slice) +{ + const char* b = slice.begin(); + const char* e = slice.end(); + if (b == e) + return -1; + return *b; +} + +static int _get(Slang::UnownedStringSlice& slice) +{ + const char* b = slice.begin(); + const char* e = slice.end(); + if (b == e) + return -1; + auto result = *b++; + slice = Slang::UnownedStringSlice(b, e); + return result; +} + +Result ShaderCursor::followPath(Slang::UnownedStringSlice const& path, ShaderCursor& ioCursor) +{ + ShaderCursor cursor = ioCursor; + + enum + { + ALLOW_NAME = 0x1, + ALLOW_SUBSCRIPT = 0x2, + ALLOW_DOT = 0x4, + }; + int state = ALLOW_NAME | ALLOW_SUBSCRIPT; + + Slang::UnownedStringSlice rest = path; + for (;;) + { + int c = _peek(rest); + + if (c == -1) + break; + else if (c == '.') + { + if (!(state & ALLOW_DOT)) + return SLANG_E_INVALID_ARG; + + _get(rest); + state = ALLOW_NAME; + continue; + } + else if (c == '[') + { + if (!(state & ALLOW_SUBSCRIPT)) + return SLANG_E_INVALID_ARG; + + _get(rest); + Slang::Index index = 0; + while (_peek(rest) != ']') + { + int d = _get(rest); + if (d >= '0' && d <= '9') + { + index = index * 10 + (d - '0'); + } + else + { + return SLANG_E_INVALID_ARG; + } + } + + if (_peek(rest) != ']') + return SLANG_E_INVALID_ARG; + _get(rest); + + cursor = cursor.getElement(index); + state = ALLOW_DOT | ALLOW_SUBSCRIPT; + continue; + } + else + { + const char* nameBegin = rest.begin(); + for (;;) + { + switch (_peek(rest)) + { + default: + _get(rest); + continue; + + case -1: + case '.': + case '[': + break; + } + break; + } + char const* nameEnd = rest.begin(); + Slang::UnownedStringSlice name(nameBegin, nameEnd); + cursor = cursor.getField(name); + state = ALLOW_DOT | ALLOW_SUBSCRIPT; + continue; + } + } + + ioCursor = cursor; + return SLANG_OK; +} + +} // namespace gfx diff --git a/tools/gfx/shader-cursor.h b/tools/gfx/shader-cursor.h new file mode 100644 index 000000000..8f48f3581 --- /dev/null +++ b/tools/gfx/shader-cursor.h @@ -0,0 +1,123 @@ +#pragma once + +#include "tools/gfx/render.h" +#include "core/slang-basic.h" + +namespace gfx +{ + +/// Represents a "pointer" to the storage for a shader parameter of a (dynamically) known type. +/// +/// A `ShaderCursor` serves as a pointer-like type for things stored inside a `ShaderObject`. +/// +/// A cursor that points to the entire content of a shader object can be formed as +/// `ShaderCursor(someObject)`. A cursor pointing to a structure field or array element can be +/// formed from another cursor using `getField` or `getElement` respectively. +/// +/// Given a cursor pointing to a value of some "primitive" type, we can set or get the value +/// using operations like `setResource`, `getResource`, etc. +/// +/// Because type information for shader parameters is being reflected dynamically, all type +/// checking for shader cursors occurs at runtime, and errors may occur when attempting to +/// set a parameter using a value of an inappropriate type. As much as possible, `ShaderCursor` +/// attempts to protect against these cases and return an error `Result` or an invalid +/// cursor, rather than allowing operations to proceed with incorrect types. +/// +struct ShaderCursor +{ + ShaderObject* m_baseObject = nullptr; + slang::TypeLayoutReflection* m_typeLayout = nullptr; + ShaderOffset m_offset; + + /// Get the type (layout) of the value being pointed at by the cursor + slang::TypeLayoutReflection* getTypeLayout() const { return m_typeLayout; } + + /// Is this cursor valid (that is, does it seem to point to an actual location)? + /// + /// This check is equivalent to checking whether a pointer is null, so it is + /// a very weak sense of "valid." In particular, it is possible to form a + /// `ShaderCursor` for which `isValid()` is true, but attempting to get or + /// set the value would be an error (like dereferencing a garbage pointer). + /// + bool isValid() const { return m_baseObject != nullptr; } + + Result getDereferenced(ShaderCursor& outCursor) const; + + ShaderCursor getDereferenced() + { + ShaderCursor result; + getDereferenced(result); + return result; + } + + /// Form a cursor pointing to the field with the given `name` within the value this cursor + /// points at. + /// + /// If the operation succeeds, then the field cursor is written to `outCursor`. + Result getField(Slang::UnownedStringSlice const& name, ShaderCursor& outCursor); + + ShaderCursor getField(Slang::UnownedStringSlice const& name) + { + ShaderCursor cursor; + getField(name, cursor); + return cursor; + } + + ShaderCursor getField(Slang::String const& name) { return getField(name.getUnownedSlice()); } + + ShaderCursor getElement(Slang::Index index); + + static Result followPath(Slang::UnownedStringSlice const& path, ShaderCursor& ioCursor); + + static Result followPath(Slang::String const& path, ShaderCursor& ioCursor) + { + return followPath(path.getUnownedSlice(), ioCursor); + } + + ShaderCursor getPath(Slang::UnownedStringSlice const& path) + { + ShaderCursor result(*this); + followPath(path, result); + return result; + } + + ShaderCursor getPath(Slang::String const& path) + { + ShaderCursor result(*this); + followPath(path, result); + return result; + } + + ShaderCursor() {} + + ShaderCursor(ShaderObject* object) + : m_baseObject(object) + , m_typeLayout(object->getElementTypeLayout()) + {} + + SlangResult setData(void const* data, size_t size) + { + return m_baseObject->setData(m_offset, data, size); + } + + SlangResult setObject(ShaderObject* object) + { + return m_baseObject->setObject(m_offset, object); + } + + SlangResult setResource(ResourceView* resourceView) + { + return m_baseObject->setResource(m_offset, resourceView); + } + + SlangResult setSampler(SamplerState* sampler) + { + return m_baseObject->setSampler(m_offset, sampler); + } + + SlangResult setCombinedTextureSampler(ResourceView* textureView, SamplerState* sampler) + { + return m_baseObject->setCombinedTextureSampler(m_offset, textureView, sampler); + } +}; +} diff --git a/tools/gfx/vulkan/render-vk.cpp b/tools/gfx/vulkan/render-vk.cpp index 6f2411f24..0f6e3cdaa 100644 --- a/tools/gfx/vulkan/render-vk.cpp +++ b/tools/gfx/vulkan/render-vk.cpp @@ -3,6 +3,7 @@ //WORKING:#include "options.h" #include "../render.h" +#include "../render-graphics-common.h" #include "../../source/core/slang-smart-pointer.h" @@ -29,7 +30,7 @@ namespace gfx { using namespace Slang; -class VKRenderer : public Renderer +class VKRenderer : public GraphicsAPIRenderer { public: enum @@ -2721,8 +2722,11 @@ Result VKRenderer::createProgram(const ShaderProgram::Desc& desc, ShaderProgram* return SLANG_OK; } -Result VKRenderer::createGraphicsPipelineState(const GraphicsPipelineStateDesc& desc, PipelineState** outState) +Result VKRenderer::createGraphicsPipelineState(const GraphicsPipelineStateDesc& inDesc, PipelineState** outState) { + GraphicsPipelineStateDesc desc = inDesc; + preparePipelineDesc(desc); + VkPipelineCache pipelineCache = VK_NULL_HANDLE; auto programImpl = (ShaderProgramImpl*) desc.program; @@ -2849,8 +2853,11 @@ Result VKRenderer::createGraphicsPipelineState(const GraphicsPipelineStateDesc& return SLANG_OK; } -Result VKRenderer::createComputePipelineState(const ComputePipelineStateDesc& desc, PipelineState** outState) +Result VKRenderer::createComputePipelineState(const ComputePipelineStateDesc& inDesc, PipelineState** outState) { + ComputePipelineStateDesc desc = inDesc; + preparePipelineDesc(desc); + VkPipelineCache pipelineCache = VK_NULL_HANDLE; auto programImpl = (ShaderProgramImpl*) desc.program; |
