summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.gitignore1
-rw-r--r--build/visual-studio/gfx/gfx.vcxproj4
-rw-r--r--build/visual-studio/gfx/gfx.vcxproj.filters12
-rw-r--r--tests/compute/array-param.slang2
-rw-r--r--tools/gfx/d3d11/render-d3d11.cpp13
-rw-r--r--tools/gfx/d3d12/render-d3d12.cpp13
-rw-r--r--tools/gfx/open-gl/render-gl.cpp13
-rw-r--r--tools/gfx/render-graphics-common.cpp1289
-rw-r--r--tools/gfx/render-graphics-common.h22
-rw-r--r--tools/gfx/render.h81
-rw-r--r--tools/gfx/shader-cursor.cpp249
-rw-r--r--tools/gfx/shader-cursor.h123
-rw-r--r--tools/gfx/vulkan/render-vk.cpp13
-rw-r--r--tools/render-test/render-test-main.cpp1544
14 files changed, 1833 insertions, 1546 deletions
diff --git a/.gitignore b/.gitignore
index 22349e8d4..9d3adf5e3 100644
--- a/.gitignore
+++ b/.gitignore
@@ -7,6 +7,7 @@
*.sdf
*.ilk
*.obj
+*.slang-module
.clang-format
bin/
intermediate/
diff --git a/build/visual-studio/gfx/gfx.vcxproj b/build/visual-studio/gfx/gfx.vcxproj
index 9a188c070..d04ac9c2a 100644
--- a/build/visual-studio/gfx/gfx.vcxproj
+++ b/build/visual-studio/gfx/gfx.vcxproj
@@ -183,7 +183,9 @@
<ClInclude Include="..\..\..\tools\gfx\nvapi\nvapi-include.h" />
<ClInclude Include="..\..\..\tools\gfx\nvapi\nvapi-util.h" />
<ClInclude Include="..\..\..\tools\gfx\open-gl\render-gl.h" />
+ <ClInclude Include="..\..\..\tools\gfx\render-graphics-common.h" />
<ClInclude Include="..\..\..\tools\gfx\render.h" />
+ <ClInclude Include="..\..\..\tools\gfx\shader-cursor.h" />
<ClInclude Include="..\..\..\tools\gfx\surface.h" />
<ClInclude Include="..\..\..\tools\gfx\vector-math.h" />
<ClInclude Include="..\..\..\tools\gfx\vulkan\render-vk.h" />
@@ -206,7 +208,9 @@
<ClCompile Include="..\..\..\tools\gfx\model.cpp" />
<ClCompile Include="..\..\..\tools\gfx\nvapi\nvapi-util.cpp" />
<ClCompile Include="..\..\..\tools\gfx\open-gl\render-gl.cpp" />
+ <ClCompile Include="..\..\..\tools\gfx\render-graphics-common.cpp" />
<ClCompile Include="..\..\..\tools\gfx\render.cpp" />
+ <ClCompile Include="..\..\..\tools\gfx\shader-cursor.cpp" />
<ClCompile Include="..\..\..\tools\gfx\surface.cpp" />
<ClCompile Include="..\..\..\tools\gfx\vulkan\render-vk.cpp" />
<ClCompile Include="..\..\..\tools\gfx\vulkan\vk-api.cpp" />
diff --git a/build/visual-studio/gfx/gfx.vcxproj.filters b/build/visual-studio/gfx/gfx.vcxproj.filters
index 2044ede93..ada84b968 100644
--- a/build/visual-studio/gfx/gfx.vcxproj.filters
+++ b/build/visual-studio/gfx/gfx.vcxproj.filters
@@ -45,9 +45,15 @@
<ClInclude Include="..\..\..\tools\gfx\open-gl\render-gl.h">
<Filter>Header Files</Filter>
</ClInclude>
+ <ClInclude Include="..\..\..\tools\gfx\render-graphics-common.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
<ClInclude Include="..\..\..\tools\gfx\render.h">
<Filter>Header Files</Filter>
</ClInclude>
+ <ClInclude Include="..\..\..\tools\gfx\shader-cursor.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
<ClInclude Include="..\..\..\tools\gfx\surface.h">
<Filter>Header Files</Filter>
</ClInclude>
@@ -110,9 +116,15 @@
<ClCompile Include="..\..\..\tools\gfx\open-gl\render-gl.cpp">
<Filter>Source Files</Filter>
</ClCompile>
+ <ClCompile Include="..\..\..\tools\gfx\render-graphics-common.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
<ClCompile Include="..\..\..\tools\gfx\render.cpp">
<Filter>Source Files</Filter>
</ClCompile>
+ <ClCompile Include="..\..\..\tools\gfx\shader-cursor.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
<ClCompile Include="..\..\..\tools\gfx\surface.cpp">
<Filter>Source Files</Filter>
</ClCompile>
diff --git a/tests/compute/array-param.slang b/tests/compute/array-param.slang
index f1b06b352..ceac34e78 100644
--- a/tests/compute/array-param.slang
+++ b/tests/compute/array-param.slang
@@ -1,5 +1,5 @@
//TEST(compute):COMPARE_COMPUTE_EX:-cpu -compute -compile-arg -O3
-//TEST(compute):COMPARE_COMPUTE_EX:-slang -compute
+//TEST(compute):COMPARE_COMPUTE_EX:-slang -compute -shaderobj
//TEST(compute):COMPARE_COMPUTE_EX:-slang -compute -dx12
//TEST(compute, vulkan):COMPARE_COMPUTE_EX:-vk -compute
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;
diff --git a/tools/render-test/render-test-main.cpp b/tools/render-test/render-test-main.cpp
index 197ee2b46..5903c49f7 100644
--- a/tools/render-test/render-test-main.cpp
+++ b/tools/render-test/render-test-main.cpp
@@ -4,7 +4,7 @@
#include "options.h"
#include "render.h"
-
+#include "shader-cursor.h"
#include "slang-support.h"
#include "surface.h"
#include "png-serialize-util.h"
@@ -165,1495 +165,13 @@ public:
virtual Result writeBindingOutput(BindRoot* bindRoot, const char* fileName) override;
protected:
- RefPtr<ProgramVars> m_programVars;
+ RefPtr<ShaderObject> m_programVars;
ShaderOutputPlan m_outputPlan;
};
-struct ShaderOffset
-{
- SlangInt uniformOffset = 0;
- SlangInt bindingRangeIndex = 0;
- SlangInt bindingArrayIndex = 0;
-};
-
-class ShaderObjectLayout : public RefObject
-{
-public:
- struct BindingRangeInfo
- {
- slang::BindingType bindingType;
- Index count;
- Index baseIndex;
- Index descriptorSetIndex;
- Index rangeIndexInDescriptorSet;
-// Index subObjectRangeIndex = -1;
- };
-
- struct SubObjectRangeInfo
- {
- RefPtr<ShaderObjectLayout> 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<ShaderObjectLayout> subObjectLayout;
- if(slangBindingType != slang::BindingType::ExistentialValue)
- {
- ShaderObjectLayout::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(ShaderObjectLayout** outLayout)
- {
- RefPtr<ShaderObjectLayout> layout = new ShaderObjectLayout();
- 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, ShaderObjectLayout** 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 ShaderObjectLayout
-{
- typedef ShaderObjectLayout 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 ProgramLayout : public ShaderObjectLayout
-{
- typedef ShaderObjectLayout Super;
-public:
-
- struct EntryPointInfo
- {
- RefPtr<EntryPointLayout> layout;
- Index rangeOffset;
- };
-
-
- struct Builder : Super::Builder
- {
- Builder(Renderer* renderer)
- : Super::Builder(renderer)
- {}
-
- Result build(ProgramLayout** outLayout)
- {
- RefPtr<ProgramLayout> layout = new ProgramLayout();
- 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(ShaderObjectLayout* 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 ShaderObject : public RefObject
-{
-public:
- static Result create(Renderer* renderer, ShaderObjectLayout* layout, ShaderObject** outShaderObject)
- {
- RefPtr<ShaderObject> object = new ShaderObject();
- SLANG_RETURN_ON_FAIL(object->init(renderer, layout));
-
- *outShaderObject = object.detach();
- return SLANG_OK;
- }
-
- Renderer* getRenderer()
- {
- return m_layout->getRenderer();
- }
-
- ShaderObjectLayout* 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;
- }
-
- SlangResult setObject(ShaderOffset const& offset, ShaderObject* object)
- {
- 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] = 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
- }
-
- SlangResult getObject(ShaderOffset const& offset, ShaderObject** outObject)
- {
- 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, ShaderObjectLayout* 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<ShaderObjectLayout> 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<ShaderObject> subObject;
- SLANG_RETURN_ON_FAIL(ShaderObject::create(renderer, subObjectLayout, subObject.writeRef()));
- m_objects[bindingRangeInfo.baseIndex + i] = subObject;
- }
- }
-
- return SLANG_OK;
- }
-
- Result apply(Renderer* renderer, PipelineType pipelineType, PipelineLayout* pipelineLayout, Index& ioRootIndex)
- {
- ShaderObjectLayout* 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)
- {
- ShaderObjectLayout* 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)
- {
- ShaderObjectLayout* 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)
- {
- ShaderObject* 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)
- {
- ShaderObject* 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<ShaderObjectLayout> 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<ShaderObject>> m_objects;
-};
-
-class EntryPointVars : public ShaderObject
-{
- typedef ShaderObject 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 ShaderObject
-{
- typedef ShaderObject Super;
-
-public:
- static Result create(Renderer* renderer, ProgramLayout* layout, ProgramVars** outShaderObject)
- {
- RefPtr<ProgramVars> object = new ProgramVars();
- SLANG_RETURN_ON_FAIL(object->init(renderer, layout));
-
- *outShaderObject = object.detach();
- return SLANG_OK;
- }
-
- ProgramLayout* getLayout()
- {
- return static_cast<ProgramLayout*>(m_layout.Ptr());
- }
-
- void apply(Renderer* renderer, PipelineType pipelineType)
- {
- auto pipelineLayout = getLayout()->getPipelineLayout();
-
- Index rootIndex = 0;
- ShaderObject::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; }
-
-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, ProgramLayout* 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;
-};
-
- /// 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
- {
- 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;
- }
-
- }
- }
-
- 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(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 getField(UnownedStringSlice const& name)
- {
- ShaderCursor cursor;
- getField(name, cursor);
- return cursor;
- }
-
- ShaderCursor getField(String const& name)
- {
- return getField(name.getUnownedSlice());
- }
-
- ShaderCursor getElement(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(UnownedStringSlice const& slice)
- {
- const char* b = slice.begin();
- const char* e = slice.end();
- if(b == e) return -1;
- return *b;
- }
-
- static int _get(UnownedStringSlice& slice)
- {
- const char* b = slice.begin();
- const char* e = slice.end();
- if(b == e) return -1;
- auto result = *b++;
- slice = UnownedStringSlice(b, e);
- return result;
- }
-
- static Result followPath(UnownedStringSlice const& path, ShaderCursor& ioCursor)
- {
- ShaderCursor cursor = ioCursor;
-
- enum
- {
- ALLOW_NAME = 0x1,
- ALLOW_SUBSCRIPT = 0x2,
- ALLOW_DOT = 0x4,
- };
- int state = ALLOW_NAME | ALLOW_SUBSCRIPT;
-
- 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);
- 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();
- UnownedStringSlice name(nameBegin, nameEnd);
- cursor = cursor.getField(name);
- state = ALLOW_DOT | ALLOW_SUBSCRIPT;
- continue;
- }
- }
-
- ioCursor = cursor;
- return SLANG_OK;
- }
-
- static Result followPath(String const& path, ShaderCursor& ioCursor)
- {
- return followPath(path.getUnownedSlice(), ioCursor);
- }
-
- ShaderCursor getPath(UnownedStringSlice const& path)
- {
- ShaderCursor result(*this);
- followPath(path, result);
- return result;
- }
-
-
- ShaderCursor getPath(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);
- }
-};
-
SlangResult _assignVarsFromLayout(
Renderer* renderer,
- ProgramVars* shaderObject,
+ ShaderObject* shaderObject,
ShaderInputLayout const& layout,
ShaderOutputPlan& ioOutputPlan,
slang::ProgramLayout* slangReflection)
@@ -1676,9 +194,9 @@ SlangResult _assignVarsFromLayout(
if(!entryCursor.isValid())
{
- for(auto entryPoint : shaderObject->getEntryPoints())
+ for(Index i = 0; i < shaderObject->getEntryPointCount(); i++)
{
- entryCursor = ShaderCursor(entryPoint).getPath(entry.name);
+ entryCursor = ShaderCursor(shaderObject->getEntryPoint(i)).getPath(entry.name);
if(entryCursor.isValid())
break;
}
@@ -1877,11 +395,8 @@ SlangResult _assignVarsFromLayout(
auto slangType = slangReflection->findTypeByName(typeName.getBuffer());
auto slangTypeLayout = slangReflection->getTypeLayout(slangType);
- RefPtr<ShaderObjectLayout> shaderObjectLayout;
- SLANG_RETURN_ON_FAIL(ShaderObjectLayout::createForElementType(renderer, slangTypeLayout, shaderObjectLayout.writeRef()));
-
- RefPtr<ShaderObject> shaderObject;
- SLANG_RETURN_ON_FAIL(ShaderObject::create(renderer, shaderObjectLayout, shaderObject.writeRef()));
+ RefPtr<ShaderObjectLayout> shaderObjectLayout = renderer->createShaderObjectLayout(slangTypeLayout);
+ RefPtr<ShaderObject> shaderObject = renderer->createShaderObject(shaderObjectLayout);
entryCursor.setObject(shaderObject);
}
@@ -1911,7 +426,7 @@ void LegacyRenderTestApp::applyBinding(PipelineType pipelineType)
void ShaderObjectRenderTestApp::applyBinding(PipelineType pipelineType)
{
- m_programVars->apply(m_renderer.Ptr(), pipelineType);
+ m_renderer->bindRootShaderObject(pipelineType, m_programVars);
}
SlangResult LegacyRenderTestApp::initialize(
@@ -2039,49 +554,19 @@ SlangResult ShaderObjectRenderTestApp::initialize(
// If we are doing a non-pass-through compilation, then we will rely on
// Slang's reflection API to tell us what the parameters of the program are.
//
- RefPtr<ProgramLayout> programLayout = nullptr;
-
- // Okay, we will use Slang reflection to determine what the parameters of the shader are.
- //
auto slangReflection = (slang::ProgramLayout*) spGetReflection(m_compilationOutput.output.getRequestForReflection());
- {
- ProgramLayout::Builder builder(renderer);
- 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(renderer);
- 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()));
- }
-
- // The shape of the parameters will determine the pipeline layout that we use.
- //
- RefPtr<PipelineLayout> pipelineLayout = programLayout->getPipelineLayout();
+ RefPtr<ShaderObjectLayout> programLayout = renderer->createRootShaderObjectLayout(slangReflection);
// Once we have determined the layout of all the parameters we need to bind,
// we will create a shader object to use for storing and binding those parameters.
//
- RefPtr<ProgramVars> programVars;
- SLANG_RETURN_ON_FAIL(ProgramVars::create(renderer, programLayout, programVars.writeRef()));
- m_programVars = programVars;
+ m_programVars = renderer->createRootShaderObject(programLayout);
// Now we need to assign from the input parameter data that was parsed into
// the program vars we allocated.
//
- SLANG_RETURN_ON_FAIL(_assignVarsFromLayout(renderer, programVars, m_compilationOutput.layout, m_outputPlan, slangReflection));
+ SLANG_RETURN_ON_FAIL(_assignVarsFromLayout(
+ renderer, m_programVars, m_compilationOutput.layout, m_outputPlan, slangReflection));
m_renderer = renderer;
@@ -2098,7 +583,7 @@ SlangResult ShaderObjectRenderTestApp::initialize(
case Options::ShaderProgramType::Compute:
{
ComputePipelineStateDesc desc;
- desc.pipelineLayout = pipelineLayout;
+ desc.rootShaderObjectLayout = programLayout.Ptr();
desc.program = m_shaderProgram;
m_pipelineState = renderer->createComputePipelineState(desc);
@@ -2132,10 +617,9 @@ SlangResult ShaderObjectRenderTestApp::initialize(
SLANG_RETURN_ON_FAIL(renderer->createBufferResource(Resource::Usage::VertexBuffer, vertexBufferDesc, kVertexData, m_vertexBuffer.writeRef()));
GraphicsPipelineStateDesc desc;
- desc.pipelineLayout = pipelineLayout;
+ desc.rootShaderObjectLayout = programLayout.Ptr();
desc.program = m_shaderProgram;
desc.inputLayout = inputLayout;
- desc.renderTargetCount = programLayout->getRenderTargetCount();
m_pipelineState = renderer->createGraphicsPipelineState(desc);
}