summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--build/visual-studio/core/core.vcxproj1
-rw-r--r--build/visual-studio/core/core.vcxproj.filters3
-rw-r--r--build/visual-studio/gfx/gfx.vcxproj2
-rw-r--r--build/visual-studio/gfx/gfx.vcxproj.filters6
-rw-r--r--examples/gpu-printing/main.cpp56
-rw-r--r--examples/hello-world/main.cpp1
-rw-r--r--examples/shader-object/main.cpp1
-rw-r--r--examples/shader-toy/main.cpp1
-rw-r--r--slang-gfx.h209
-rw-r--r--source/core/slang-chunked-list.h268
-rw-r--r--tools/gfx/command-writer.h17
-rw-r--r--tools/gfx/cpu/render-cpu.cpp38
-rw-r--r--tools/gfx/cuda/render-cuda.cpp62
-rw-r--r--tools/gfx/d3d11/render-d3d11.cpp36
-rw-r--r--tools/gfx/d3d12/render-d3d12.cpp78
-rw-r--r--tools/gfx/immediate-renderer-base.cpp23
-rw-r--r--tools/gfx/immediate-renderer-base.h5
-rw-r--r--tools/gfx/open-gl/render-gl.cpp36
-rw-r--r--tools/gfx/render-graphics-common.cpp1766
-rw-r--r--tools/gfx/render-graphics-common.h56
-rw-r--r--tools/gfx/renderer-shared.cpp60
-rw-r--r--tools/gfx/renderer-shared.h7
-rw-r--r--tools/gfx/vulkan/render-vk.cpp2965
-rw-r--r--tools/gfx/vulkan/vk-util.cpp47
-rw-r--r--tools/gfx/vulkan/vk-util.h4
-rw-r--r--tools/platform/gui.cpp103
-rw-r--r--tools/platform/gui.h2
-rw-r--r--tools/render-test/slang-support.cpp32
-rw-r--r--tools/render-test/slang-support.h15
29 files changed, 2481 insertions, 3419 deletions
diff --git a/build/visual-studio/core/core.vcxproj b/build/visual-studio/core/core.vcxproj
index 38bcf2ade..2f48f93eb 100644
--- a/build/visual-studio/core/core.vcxproj
+++ b/build/visual-studio/core/core.vcxproj
@@ -178,6 +178,7 @@
<ClInclude Include="..\..\..\source\core\slang-blob.h" />
<ClInclude Include="..\..\..\source\core\slang-byte-encode-util.h" />
<ClInclude Include="..\..\..\source\core\slang-char-util.h" />
+ <ClInclude Include="..\..\..\source\core\slang-chunked-list.h" />
<ClInclude Include="..\..\..\source\core\slang-common.h" />
<ClInclude Include="..\..\..\source\core\slang-compression-system.h" />
<ClInclude Include="..\..\..\source\core\slang-deflate-compression-system.h" />
diff --git a/build/visual-studio/core/core.vcxproj.filters b/build/visual-studio/core/core.vcxproj.filters
index 9c718a6fb..5e63e4681 100644
--- a/build/visual-studio/core/core.vcxproj.filters
+++ b/build/visual-studio/core/core.vcxproj.filters
@@ -33,6 +33,9 @@
<ClInclude Include="..\..\..\source\core\slang-char-util.h">
<Filter>Header Files</Filter>
</ClInclude>
+ <ClInclude Include="..\..\..\source\core\slang-chunked-list.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
<ClInclude Include="..\..\..\source\core\slang-common.h">
<Filter>Header Files</Filter>
</ClInclude>
diff --git a/build/visual-studio/gfx/gfx.vcxproj b/build/visual-studio/gfx/gfx.vcxproj
index 08786b5cf..01faf88bd 100644
--- a/build/visual-studio/gfx/gfx.vcxproj
+++ b/build/visual-studio/gfx/gfx.vcxproj
@@ -195,7 +195,6 @@
<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\renderer-shared.h" />
<ClInclude Include="..\..\..\tools\gfx\simple-render-pass-layout.h" />
<ClInclude Include="..\..\..\tools\gfx\slang-context.h" />
@@ -220,7 +219,6 @@
<ClCompile Include="..\..\..\tools\gfx\immediate-renderer-base.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\renderer-shared.cpp" />
<ClCompile Include="..\..\..\tools\gfx\simple-render-pass-layout.cpp" />
diff --git a/build/visual-studio/gfx/gfx.vcxproj.filters b/build/visual-studio/gfx/gfx.vcxproj.filters
index cff8cc95a..99af1fbbd 100644
--- a/build/visual-studio/gfx/gfx.vcxproj.filters
+++ b/build/visual-studio/gfx/gfx.vcxproj.filters
@@ -57,9 +57,6 @@
<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\renderer-shared.h">
<Filter>Header Files</Filter>
</ClInclude>
@@ -128,9 +125,6 @@
<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>
diff --git a/examples/gpu-printing/main.cpp b/examples/gpu-printing/main.cpp
index 3fd1e460a..0315d4827 100644
--- a/examples/gpu-printing/main.cpp
+++ b/examples/gpu-printing/main.cpp
@@ -56,9 +56,7 @@ ComPtr<slang::ISession> gSlangSession;
ComPtr<slang::IModule> gSlangModule;
ComPtr<gfx::IShaderProgram> gProgram;
-ComPtr<gfx::IPipelineLayout> gPipelineLayout;
ComPtr<gfx::IPipelineState> gPipelineState;
-ComPtr<gfx::IDescriptorSet> gDescriptorSet;
Slang::Dictionary<int, std::string> gHashedStrings;
@@ -74,21 +72,9 @@ ComPtr<gfx::IShaderProgram> loadComputeProgram(slang::IModule* slangModule, char
gGPUPrinting.loadStrings(linkedProgram->getLayout());
- ComPtr<ISlangBlob> codeBlob;
- linkedProgram->getEntryPointCode(0, 0, codeBlob.writeRef());
-
- char const* code = (char const*) codeBlob->getBufferPointer();
- char const* codeEnd = code + codeBlob->getBufferSize();
-
- gfx::IShaderProgram::KernelDesc kernelDescs[] =
- {
- { gfx::StageType::Compute, code, codeEnd },
- };
-
gfx::IShaderProgram::Desc programDesc = {};
programDesc.pipelineType = gfx::PipelineType::Compute;
- programDesc.kernels = &kernelDescs[0];
- programDesc.kernelCount = 2;
+ programDesc.slangProgram = linkedProgram;
auto shaderProgram = gDevice->createProgram(programDesc);
@@ -107,40 +93,7 @@ Result execute()
gProgram = loadComputeProgram(gSlangModule, "computeMain");
if(!gProgram) return SLANG_FAIL;
- IDescriptorSetLayout::SlotRangeDesc slotRanges[] =
- {
- IDescriptorSetLayout::SlotRangeDesc(DescriptorSlotType::StorageBuffer),
- };
- IDescriptorSetLayout::Desc descriptorSetLayoutDesc;
- descriptorSetLayoutDesc.slotRangeCount = 1;
- descriptorSetLayoutDesc.slotRanges = &slotRanges[0];
- auto descriptorSetLayout = gDevice->createDescriptorSetLayout(descriptorSetLayoutDesc);
- if(!descriptorSetLayout) return SLANG_FAIL;
-
- IPipelineLayout::DescriptorSetDesc descriptorSets[] =
- {
- IPipelineLayout::DescriptorSetDesc( descriptorSetLayout ),
- };
- IPipelineLayout::Desc pipelineLayoutDesc;
- pipelineLayoutDesc.renderTargetCount = 1;
- pipelineLayoutDesc.descriptorSetCount = 1;
- pipelineLayoutDesc.descriptorSets = &descriptorSets[0];
- auto pipelineLayout = gDevice->createPipelineLayout(pipelineLayoutDesc);
- if(!pipelineLayout) return SLANG_FAIL;
-
- gPipelineLayout = pipelineLayout;
-
- // Once we have the descriptor set layout, we can allocate
- // and fill in a descriptor set to hold our parameters.
- //
- auto descriptorSet =
- gDevice->createDescriptorSet(descriptorSetLayout, IDescriptorSet::Flag::Transient);
- if(!descriptorSet) return SLANG_FAIL;
-
- gDescriptorSet = descriptorSet;
-
ComputePipelineStateDesc desc;
- desc.pipelineLayout = gPipelineLayout;
desc.program = gProgram;
auto pipelineState = gDevice->createComputePipelineState(desc);
if(!pipelineState) return SLANG_FAIL;
@@ -164,12 +117,9 @@ Result execute()
auto queue = gDevice->createCommandQueue(queueDesc);
auto commandBuffer = queue->createCommandBuffer();
auto encoder = commandBuffer->encodeComputeCommands();
- // TODO: need to copy a zero into the start of the print buffer!
-
- gDescriptorSet->setResource(0, 0, printBufferView);
- encoder->setDescriptorSet(gPipelineLayout, 0, gDescriptorSet);
-
+ auto rootShaderObject = gDevice->createRootShaderObject(gProgram);
encoder->setPipelineState(gPipelineState);
+ encoder->bindRootShaderObject(rootShaderObject);
encoder->dispatchCompute(1, 1, 1);
encoder->endEncoding();
commandBuffer->close();
diff --git a/examples/hello-world/main.cpp b/examples/hello-world/main.cpp
index b3bee9c07..023c16304 100644
--- a/examples/hello-world/main.cpp
+++ b/examples/hello-world/main.cpp
@@ -244,6 +244,7 @@ Slang::Result initialize()
// platforms/APIs.
//
IDevice::Desc deviceDesc = {};
+ deviceDesc.deviceType = DeviceType::Vulkan;
gfx::Result res = gfxCreateDevice(&deviceDesc, gDevice.writeRef());
if(SLANG_FAILED(res)) return res;
diff --git a/examples/shader-object/main.cpp b/examples/shader-object/main.cpp
index aaafc010a..4945de27b 100644
--- a/examples/shader-object/main.cpp
+++ b/examples/shader-object/main.cpp
@@ -136,6 +136,7 @@ int main()
// interacting with the graphics API.
Slang::ComPtr<gfx::IDevice> device;
IDevice::Desc deviceDesc = {};
+ deviceDesc.deviceType = DeviceType::Vulkan;
SLANG_RETURN_ON_FAIL(gfxCreateDevice(&deviceDesc, device.writeRef()));
// Now we can load the shader code.
diff --git a/examples/shader-toy/main.cpp b/examples/shader-toy/main.cpp
index c4424b294..8e377b42f 100644
--- a/examples/shader-toy/main.cpp
+++ b/examples/shader-toy/main.cpp
@@ -315,6 +315,7 @@ Result initialize()
gWindow->events.sizeChanged = Slang::Action<>(this, &ShaderToyApp::windowSizeChanged);
IDevice::Desc deviceDesc;
+ deviceDesc.deviceType = DeviceType::Vulkan;
Result res = gfxCreateDevice(&deviceDesc, gDevice.writeRef());
if(SLANG_FAILED(res)) return res;
diff --git a/slang-gfx.h b/slang-gfx.h
index cb4fcf984..1e13d6699 100644
--- a/slang-gfx.h
+++ b/slang-gfx.h
@@ -113,35 +113,11 @@ enum class BindingStyle
class IShaderProgram: public ISlangUnknown
{
public:
-
- struct KernelDesc
- {
- StageType stage;
- void const* codeBegin;
- void const* codeEnd;
- char const* entryPointName;
-
- UInt getCodeSize() const { return (char const*)codeEnd - (char const*)codeBegin; }
- };
-
struct Desc
{
PipelineType pipelineType;
-
- KernelDesc const* kernels;
- Int kernelCount;
-
/// Use instead of `kernels`/`kernelCount` if loading a Slang program.
slang::IComponentType* slangProgram;
-
- /// Find and return the kernel for `stage`, if present.
- KernelDesc const* findKernel(StageType stage) const
- {
- for(Int ii = 0; ii < kernelCount; ++ii)
- if(kernels[ii].stage == stage)
- return &kernels[ii];
- return nullptr;
- }
};
};
#define SLANG_UUID_IShaderProgram \
@@ -727,109 +703,6 @@ public:
0x8b8055df, 0x9377, 0x401d, { 0x91, 0xff, 0x3f, 0xa3, 0xbf, 0x66, 0x64, 0xf4 } \
}
-
-enum class DescriptorSlotType
-{
- Unknown,
-
- Sampler,
- CombinedImageSampler,
- SampledImage,
- StorageImage,
- UniformTexelBuffer,
- StorageTexelBuffer,
- UniformBuffer,
- ReadOnlyStorageBuffer,
- StorageBuffer,
- DynamicUniformBuffer,
- DynamicStorageBuffer,
- InputAttachment,
- RootConstant,
- InlineUniformBlock,
- RayTracingAccelerationStructure,
-};
-
-class IDescriptorSetLayout : public ISlangUnknown
-{
-public:
- struct SlotRangeDesc
- {
- DescriptorSlotType type = DescriptorSlotType::Unknown;
- UInt count = 1;
-
- /// The underlying API-specific binding/register to use for this slot range.
- ///
- /// A value of `-1` indicates that the implementation should
- /// automatically compute the binding/register to use
- /// based on the preceeding slot range(s).
- ///
- /// Some implementations do not have a concept of bindings/regsiters
- /// for slot ranges, and will ignore this field.
- ///
- Int binding = -1;
-
- SlotRangeDesc()
- {}
-
- SlotRangeDesc(
- DescriptorSlotType type,
- UInt count = 1)
- : type(type)
- , count(count)
- {}
- };
-
- struct Desc
- {
- UInt slotRangeCount = 0;
- SlotRangeDesc const* slotRanges = nullptr;
- };
-};
-#define SLANG_UUID_IDescriptorSetLayout \
- { \
- 0x9fe39a2f, 0xdf8b, 0x4690, { 0x90, 0x6a, 0x10, 0x1e, 0xed, 0xf9, 0xbe, 0xc0 } \
- }
-
-
-class IPipelineLayout : public ISlangUnknown
-{
-public:
- struct DescriptorSetDesc
- {
- IDescriptorSetLayout* layout = nullptr;
-
- /// The underlying API-specific space/set number to use for this set.
- ///
- /// A value of `-1` indicates that the implementation should
- /// automatically compute the space/set to use basd on
- /// the preceeding set(s)
- ///
- /// Some implementations do not have a concept of space/set numbers
- /// for descriptor sets, and will ignore this field.
- ///
- Int space = -1;
-
- DescriptorSetDesc()
- {}
-
- DescriptorSetDesc(
- IDescriptorSetLayout* layout)
- : layout(layout)
- {}
- };
-
- struct Desc
- {
- UInt renderTargetCount = 0;
- UInt descriptorSetCount = 0;
- DescriptorSetDesc const* descriptorSets = nullptr;
- };
-};
-#define SLANG_UUID_IPipelineLayout \
- { \
- 0x9d644a9a, 0x3e6f, 0x4350, { 0xa3, 0x5a, 0xe8, 0xe3, 0xbc, 0xef, 0xb9, 0xcf } \
- }
-
class IResourceView : public ISlangUnknown
{
public:
@@ -867,42 +740,6 @@ public:
0x7b6c4926, 0x884, 0x408c, { 0xad, 0x8a, 0x50, 0x3a, 0x8e, 0x23, 0x98, 0xa4 } \
}
-
-class IDescriptorSet : public ISlangUnknown
-{
-public:
- struct Flag
- {
- enum Enum
- {
- None = 0,
- Transient = 1,
- Persistent = 2
- };
- };
- virtual SLANG_NO_THROW void SLANG_MCALL setConstantBuffer(UInt range, UInt index, IBufferResource* buffer) = 0;
- virtual SLANG_NO_THROW void SLANG_MCALL
- setResource(UInt range, UInt index, IResourceView* view) = 0;
- virtual SLANG_NO_THROW void SLANG_MCALL
- setSampler(UInt range, UInt index, ISamplerState* sampler) = 0;
- virtual SLANG_NO_THROW void SLANG_MCALL setCombinedTextureSampler(
- UInt range,
- UInt index,
- IResourceView* textureView,
- ISamplerState* sampler) = 0;
- virtual SLANG_NO_THROW void SLANG_MCALL
- setRootConstants(
- UInt range,
- UInt offset,
- UInt size,
- void const* data) = 0;
-};
-#define SLANG_UUID_IDescriptorSet \
- { \
- 0x29a881ea, 0xd7, 0x41d4, { 0xa3, 0x2d, 0x6c, 0x78, 0x4b, 0x79, 0xda, 0x2e } \
- }
-
-
struct ShaderOffset
{
SlangInt uniformOffset = 0;
@@ -1116,10 +953,6 @@ struct GraphicsPipelineStateDesc
{
IShaderProgram* program = nullptr;
- // If `pipelineLayout` is null, then layout information will be extracted
- // from `program`, which must have been created with Slang reflection info.
- IPipelineLayout* pipelineLayout = nullptr;
-
IInputLayout* inputLayout = nullptr;
IFramebufferLayout* framebufferLayout = nullptr;
PrimitiveType primitiveType = PrimitiveType::Triangle;
@@ -1131,10 +964,6 @@ struct GraphicsPipelineStateDesc
struct ComputePipelineStateDesc
{
IShaderProgram* program;
-
- // If `pipelineLayout` is null, then layout information will be extracted
- // from `program`, which must have been created with Slang reflection info.
- IPipelineLayout* pipelineLayout = nullptr;
};
class IPipelineState : public ISlangUnknown
@@ -1280,11 +1109,6 @@ public:
virtual SLANG_NO_THROW void SLANG_MCALL
bindRootShaderObject(IShaderObject* object) = 0;
- virtual SLANG_NO_THROW void SLANG_MCALL setDescriptorSet(
- IPipelineLayout* layout,
- UInt index,
- IDescriptorSet* descriptorSet) = 0;
-
virtual SLANG_NO_THROW void
SLANG_MCALL setViewports(uint32_t count, const Viewport* viewports) = 0;
virtual SLANG_NO_THROW void
@@ -1330,11 +1154,6 @@ public:
virtual SLANG_NO_THROW void SLANG_MCALL
bindRootShaderObject(IShaderObject* object) = 0;
- virtual SLANG_NO_THROW void SLANG_MCALL setDescriptorSet(
- IPipelineLayout* layout,
- UInt index,
- IDescriptorSet* descriptorSet) = 0;
-
virtual SLANG_NO_THROW void SLANG_MCALL setPipelineState(IPipelineState* state) = 0;
virtual SLANG_NO_THROW void SLANG_MCALL dispatchCompute(int x, int y, int z) = 0;
};
@@ -1674,16 +1493,6 @@ public:
return queue;
}
- virtual SLANG_NO_THROW Result SLANG_MCALL createDescriptorSetLayout(
- const IDescriptorSetLayout::Desc& desc, IDescriptorSetLayout** outLayout) = 0;
-
- inline ComPtr<IDescriptorSetLayout> createDescriptorSetLayout(const IDescriptorSetLayout::Desc& desc)
- {
- ComPtr<IDescriptorSetLayout> layout;
- SLANG_RETURN_NULL_ON_FAIL(createDescriptorSetLayout(desc, layout.writeRef()));
- return layout;
- }
-
virtual SLANG_NO_THROW Result SLANG_MCALL createShaderObject(slang::TypeReflection* type, IShaderObject** outObject) = 0;
inline ComPtr<IShaderObject> createShaderObject(slang::TypeReflection* type)
@@ -1702,24 +1511,6 @@ public:
return object;
}
- virtual SLANG_NO_THROW Result SLANG_MCALL createPipelineLayout(const IPipelineLayout::Desc& desc, IPipelineLayout** outLayout) = 0;
-
- inline ComPtr<IPipelineLayout> createPipelineLayout(const IPipelineLayout::Desc& desc)
- {
- ComPtr<IPipelineLayout> layout;
- SLANG_RETURN_NULL_ON_FAIL(createPipelineLayout(desc, layout.writeRef()));
- return layout;
- }
-
- virtual SLANG_NO_THROW Result SLANG_MCALL createDescriptorSet(IDescriptorSetLayout* layout, IDescriptorSet::Flag::Enum flag, IDescriptorSet** outDescriptorSet) = 0;
-
- inline ComPtr<IDescriptorSet> createDescriptorSet(IDescriptorSetLayout* layout, IDescriptorSet::Flag::Enum flag)
- {
- ComPtr<IDescriptorSet> descriptorSet;
- SLANG_RETURN_NULL_ON_FAIL(createDescriptorSet(layout, flag, descriptorSet.writeRef()));
- return descriptorSet;
- }
-
virtual SLANG_NO_THROW Result SLANG_MCALL createProgram(const IShaderProgram::Desc& desc, IShaderProgram** outProgram) = 0;
inline ComPtr<IShaderProgram> createProgram(const IShaderProgram::Desc& desc)
diff --git a/source/core/slang-chunked-list.h b/source/core/slang-chunked-list.h
new file mode 100644
index 000000000..ca79fe75a
--- /dev/null
+++ b/source/core/slang-chunked-list.h
@@ -0,0 +1,268 @@
+#ifndef SLANG_CORE_CHUNKED_LIST_H
+#define SLANG_CORE_CHUNKED_LIST_H
+
+#include "../../slang.h"
+
+#include "slang-allocator.h"
+#include "slang-array-view.h"
+#include "slang-math.h"
+
+namespace Slang
+{
+// Items stored in a ChunkedList are guaranteed to have fixed address.
+template <typename T, uint32_t defaultChunkSize = 16, typename TAllocator = StandardAllocator>
+class ChunkedList
+{
+private:
+ TAllocator allocator;
+
+ struct Chunk
+ {
+ uint32_t size = 0;
+ uint32_t capacity = defaultChunkSize;
+ Chunk* next = nullptr;
+ T* begin()
+ {
+ return reinterpret_cast<T*>(this + 1);
+ }
+ T* end() { return begin() + size; }
+ };
+
+ struct FirstChunk : public Chunk
+ {
+ T elements[defaultChunkSize];
+ };
+
+ Chunk* allocateChunk(uint32_t size)
+ {
+ auto resultChunk = (Chunk*)allocator.allocate(sizeof(Chunk) + size * sizeof(T));
+ resultChunk->capacity = size;
+ resultChunk->size = 0;
+ resultChunk->next = nullptr;
+ auto firstItem = resultChunk->begin();
+ Initializer<T, std::is_pod<T>::value>::initialize(firstItem, size);
+ return resultChunk;
+ }
+ void freeChunk(Chunk* chunk)
+ {
+ if (!std::is_trivially_destructible<T>::value)
+ {
+ for (uint32_t i = 0; i < chunk->capacity; i++)
+ chunk->begin()[i].~T();
+ }
+ allocator.deallocate(chunk);
+ }
+
+public:
+ typedef ChunkedList<T, defaultChunkSize, TAllocator> ThisType;
+ ChunkedList()
+ : m_lastChunk(&m_firstChunk)
+ , m_count(0)
+ {}
+ template <typename... Args> ChunkedList(const T& val, Args... args) { _init(val, args...); }
+ ChunkedList(const ThisType& list)
+ : m_lastChunk(&m_firstChunk)
+ , m_count(0)
+ {
+ this->operator=(list);
+ }
+ ChunkedList(ThisType&& list)
+ : m_lastChunk(&m_firstChunk)
+ , m_count(0)
+ {
+ this->operator=(static_cast<ThisType&&>(list));
+ }
+ ~ChunkedList() { _deallocateBuffer(); }
+ template <int _otherShortListSize, typename TOtherAllocator>
+ ThisType& operator=(const ChunkedList<T, _otherShortListSize, TOtherAllocator>& list)
+ {
+ clearAndDeallocate();
+ addRange(list);
+ return *this;
+ }
+
+ ThisType& operator=(const ThisType& other)
+ {
+ clearAndDeallocate();
+ addRange(other);
+ return *this;
+ }
+
+ ThisType& operator=(ThisType&& list)
+ {
+ // Could just do a swap here, and memory would be freed on rhs dtor
+ _deallocateBuffer();
+ m_count = list.m_count;
+ m_firstChunk = _Move(list.m_firstChunk);
+ m_lastChunk = list.m_lastChunk;
+ list.m_count = 0;
+ list.m_firstChunk.next = nullptr;
+ list.m_lastChunk = &list.m_firstChunk;
+ list.m_firstChunk.size = 0;
+ return *this;
+ }
+
+ struct Iterator
+ {
+ Chunk* chunk = nullptr;
+ Index index = -1;
+ Iterator& operator++()
+ {
+ ++index;
+ if (index == chunk->size)
+ {
+ index = 0;
+ chunk = chunk->next;
+ }
+ return *this;
+ }
+ Iterator operator++(int)
+ {
+ Iterator rs = *this;
+ operator++();
+ return rs;
+ }
+ T* operator->()
+ {
+ SLANG_ASSERT(chunk);
+ return chunk->begin() + index;
+ }
+ T& operator*()
+ {
+ SLANG_ASSERT(chunk);
+ return chunk->begin()[index];
+ }
+ bool operator==(Iterator other) { return chunk == other.chunk && index == other.index; }
+ bool operator!=(Iterator other) { return index != other.index || chunk != other.chunk; }
+ };
+
+ Iterator begin()
+ {
+ Iterator rs;
+ rs.chunk = &m_firstChunk;
+ rs.index = 0;
+ return rs;
+ }
+ Iterator end()
+ {
+ Iterator rs;
+ rs.chunk = nullptr;
+ rs.index = 0;
+ return rs;
+ }
+
+ Chunk* _maybeReserveForAdd(uint32_t chunkSize)
+ {
+ if (m_lastChunk->capacity - m_lastChunk->size < chunkSize)
+ {
+ auto chunk = allocateChunk(Math::Max(defaultChunkSize, chunkSize));
+ m_lastChunk->next = chunk;
+ m_lastChunk = chunk;
+ return chunk;
+ }
+ return m_lastChunk;
+ }
+
+ T* add(T&& obj)
+ {
+ auto chunk = _maybeReserveForAdd();
+ auto result = chunk->begin() + chunk->size;
+ chunk->begin()[chunk->size] = static_cast<T&&>(obj);
+ chunk->size++;
+ m_count++;
+ return result;
+ }
+
+ T* add(const T& obj)
+ {
+ auto chunk = _maybeReserveForAdd();
+ auto result = chunk->begin() + chunk->size;
+ chunk->begin()[chunk->size] = obj;
+ chunk->size++;
+ m_count++;
+ return result;
+ }
+
+ Index getCount() const { return m_count; }
+
+ T* addRange(const T* vals, Index n)
+ {
+ Chunk* chunk = _maybeReserveForAdd((uint32_t)n);
+ auto result = chunk->begin() + chunk->size;
+ for (Index i = 0; i < n; i++)
+ {
+ chunk->begin()[chunk->size + i] = vals[i];
+ }
+ chunk->size += (uint32_t)n;
+ m_count += n;
+ return result;
+ }
+
+ T* addRange(ArrayView<T> list) { return addRange(list.m_buffer, list.m_count); }
+
+ T* reserveRange(uint32_t size)
+ {
+ Chunk* chunk = _maybeReserveForAdd((uint32_t)size);
+ auto result = chunk->begin() + chunk->size;
+ chunk->size += size;
+ m_count += size;
+ return result;
+ }
+
+ template <typename TContainer> T* addRange(const TContainer& list)
+ {
+ Chunk* chunk = _maybeReserveForAdd((uint32_t)list.getCount());
+ auto result = chunk->begin() + chunk->size;
+ for (auto& obj : list)
+ {
+ chunk->begin()[chunk->size] = obj;
+ chunk->size++;
+ m_count++;
+ }
+ return result;
+ }
+
+ void clearAndDeallocate()
+ {
+ _deallocateBuffer();
+ m_count = 0;
+ }
+
+private:
+
+ Index m_count = 0; ///< The amount of elements
+ FirstChunk m_firstChunk;
+ Chunk* m_lastChunk = &m_firstChunk;
+
+ void _deallocateBuffer()
+ {
+ auto chunk = m_firstChunk.next;
+ while (chunk)
+ {
+ auto nextChunk = chunk->next;
+ freeChunk(chunk);
+ }
+ m_firstChunk.next = 0;
+ m_firstChunk.size = 0;
+ m_lastChunk = &m_firstChunk;
+ }
+ static inline T* _allocate(Index count)
+ {
+ return AllocateMethod<T, TAllocator>::allocateArray(count);
+ }
+ static inline void _free(T* ptr, Index count)
+ {
+ return AllocateMethod<T, TAllocator>::deallocateArray(ptr, count);
+ }
+
+ template <typename... Args> void _init(const T& val, Args... args)
+ {
+ add(val);
+ _init(args...);
+ }
+
+ void _init() {}
+};
+} // namespace Slang
+
+#endif
diff --git a/tools/gfx/command-writer.h b/tools/gfx/command-writer.h
index 8aba454f4..76a72edc3 100644
--- a/tools/gfx/command-writer.h
+++ b/tools/gfx/command-writer.h
@@ -10,7 +10,6 @@ namespace gfx
enum class CommandName
{
SetPipelineState,
- SetDescriptorSet,
BindRootShaderObject,
SetFramebuffer,
ClearFrame,
@@ -128,22 +127,6 @@ public:
m_commands.add(Command(CommandName::SetPipelineState, offset));
}
- void setDescriptorSet(
- PipelineType pipelineType,
- IPipelineLayout* layout,
- UInt index,
- IDescriptorSet* descriptorSet)
- {
- uint32_t pipelineLayoutOffset = encodeObject(layout);
- uint32_t descSetOffset = encodeObject(descriptorSet);
- m_commands.add(Command(
- CommandName::SetDescriptorSet,
- (uint32_t)pipelineType,
- pipelineLayoutOffset,
- (uint32_t)index,
- descSetOffset));
- }
-
void bindRootShaderObject(PipelineType pipelineType, IShaderObject* object)
{
auto rootOffset = encodeObject(object);
diff --git a/tools/gfx/cpu/render-cpu.cpp b/tools/gfx/cpu/render-cpu.cpp
index ab9224657..fa31f7ee1 100644
--- a/tools/gfx/cpu/render-cpu.cpp
+++ b/tools/gfx/cpu/render-cpu.cpp
@@ -1167,14 +1167,6 @@ private:
m_writer->bindRootShaderObject(PipelineType::Compute, object);
}
- virtual SLANG_NO_THROW void SLANG_MCALL setDescriptorSet(
- IPipelineLayout* layout,
- UInt index,
- IDescriptorSet* descriptorSet) override
- {
- m_writer->setDescriptorSet(PipelineType::Compute, layout, index, descriptorSet);
- }
-
virtual SLANG_NO_THROW void SLANG_MCALL dispatchCompute(int x, int y, int z) override
{
m_writer->dispatchCompute(x, y, z);
@@ -1521,14 +1513,6 @@ public:
virtual SLANG_NO_THROW Result SLANG_MCALL
createProgram(const IShaderProgram::Desc& desc, IShaderProgram** outProgram) override
{
- if( desc.kernelCount == 0 )
- {
- return createProgramFromSlang(this, desc, outProgram);
- }
-
- if (desc.kernelCount != 1)
- return SLANG_E_INVALID_ARG;
-
RefPtr<CPUShaderProgram> cpuProgram = new CPUShaderProgram();
// TODO: stuff?
@@ -1623,28 +1607,6 @@ public:
SLANG_UNUSED(outLayout);
return SLANG_E_NOT_AVAILABLE;
}
- virtual SLANG_NO_THROW Result SLANG_MCALL createDescriptorSetLayout(
- const IDescriptorSetLayout::Desc& desc, IDescriptorSetLayout** outLayout) override
- {
- SLANG_UNUSED(desc);
- SLANG_UNUSED(outLayout);
- return SLANG_E_NOT_AVAILABLE;
- }
- virtual SLANG_NO_THROW Result SLANG_MCALL
- createPipelineLayout(const IPipelineLayout::Desc& desc, IPipelineLayout** outLayout) override
- {
- SLANG_UNUSED(desc);
- SLANG_UNUSED(outLayout);
- return SLANG_E_NOT_AVAILABLE;
- }
- virtual SLANG_NO_THROW Result SLANG_MCALL
- createDescriptorSet(IDescriptorSetLayout* layout, IDescriptorSet::Flag::Enum flags, IDescriptorSet** outDescriptorSet) override
- {
- SLANG_UNUSED(layout);
- SLANG_UNUSED(flags);
- SLANG_UNUSED(outDescriptorSet);
- return SLANG_E_NOT_AVAILABLE;
- }
virtual SLANG_NO_THROW Result SLANG_MCALL createGraphicsPipelineState(
const GraphicsPipelineStateDesc& desc, IPipelineState** outState) override
{
diff --git a/tools/gfx/cuda/render-cuda.cpp b/tools/gfx/cuda/render-cuda.cpp
index dbdc27628..383ccc924 100644
--- a/tools/gfx/cuda/render-cuda.cpp
+++ b/tools/gfx/cuda/render-cuda.cpp
@@ -12,7 +12,6 @@
#include "slang-com-helper.h"
#include "../command-writer.h"
#include "../renderer-shared.h"
-#include "../render-graphics-common.h"
#include "../slang-context.h"
# ifdef RENDER_TEST_OPTIX
@@ -1038,14 +1037,6 @@ public:
m_writer->bindRootShaderObject(PipelineType::Compute, object);
}
- virtual SLANG_NO_THROW void SLANG_MCALL setDescriptorSet(
- IPipelineLayout* layout,
- UInt index,
- IDescriptorSet* descriptorSet) override
- {
- m_writer->setDescriptorSet(PipelineType::Compute, layout, index, descriptorSet);
- }
-
virtual SLANG_NO_THROW void SLANG_MCALL dispatchCompute(int x, int y, int z) override
{
m_writer->dispatchCompute(x, y, z);
@@ -1861,27 +1852,29 @@ public:
// If this is a specializable program, we just keep a reference to the slang program and
// don't actually create any kernels. This program will be specialized later when we know
// the shader object bindings.
- if (desc.slangProgram && desc.slangProgram->getSpecializationParamCount() != 0)
+ RefPtr<CUDAShaderProgram> cudaProgram = new CUDAShaderProgram();
+ cudaProgram->slangProgram = desc.slangProgram;
+ if (desc.slangProgram->getSpecializationParamCount() != 0)
{
- RefPtr<CUDAShaderProgram> cudaProgram = new CUDAShaderProgram();
- cudaProgram->slangProgram = desc.slangProgram;
cudaProgram->layout = new CUDAProgramLayout(this, desc.slangProgram->getLayout());
*outProgram = cudaProgram.detach();
return SLANG_OK;
}
- if( desc.kernelCount == 0 )
+ ComPtr<ISlangBlob> kernelCode;
+ ComPtr<ISlangBlob> diagnostics;
+ auto compileResult = desc.slangProgram->getEntryPointCode(
+ (SlangInt)0, 0, kernelCode.writeRef(), diagnostics.writeRef());
+ if (diagnostics)
{
- return createProgramFromSlang(this, desc, outProgram);
+ // TODO: report compile error.
}
-
- if (desc.kernelCount != 1)
- return SLANG_E_INVALID_ARG;
- RefPtr<CUDAShaderProgram> cudaProgram = new CUDAShaderProgram();
- SLANG_CUDA_RETURN_ON_FAIL(cuModuleLoadData(&cudaProgram->cudaModule, desc.kernels[0].codeBegin));
- SLANG_CUDA_RETURN_ON_FAIL(
- cuModuleGetFunction(&cudaProgram->cudaKernel, cudaProgram->cudaModule, desc.kernels[0].entryPointName));
- cudaProgram->kernelName = desc.kernels[0].entryPointName;
+ SLANG_RETURN_ON_FAIL(compileResult);
+
+ SLANG_CUDA_RETURN_ON_FAIL(cuModuleLoadData(&cudaProgram->cudaModule, kernelCode->getBufferPointer()));
+ cudaProgram->kernelName = desc.slangProgram->getLayout()->getEntryPointByIndex(0)->getName();
+ SLANG_CUDA_RETURN_ON_FAIL(cuModuleGetFunction(
+ &cudaProgram->cudaKernel, cudaProgram->cudaModule, cudaProgram->kernelName.getBuffer()));
auto slangProgram = desc.slangProgram;
if( slangProgram )
@@ -1985,31 +1978,6 @@ public:
return SLANG_E_NOT_AVAILABLE;
}
- virtual SLANG_NO_THROW Result SLANG_MCALL createDescriptorSetLayout(
- const IDescriptorSetLayout::Desc& desc, IDescriptorSetLayout** outLayout) override
- {
- SLANG_UNUSED(desc);
- SLANG_UNUSED(outLayout);
- return SLANG_E_NOT_AVAILABLE;
- }
-
- virtual SLANG_NO_THROW Result SLANG_MCALL
- createPipelineLayout(const IPipelineLayout::Desc& desc, IPipelineLayout** outLayout) override
- {
- SLANG_UNUSED(desc);
- SLANG_UNUSED(outLayout);
- return SLANG_E_NOT_AVAILABLE;
- }
-
- virtual SLANG_NO_THROW Result SLANG_MCALL
- createDescriptorSet(IDescriptorSetLayout* layout, IDescriptorSet::Flag::Enum flags, IDescriptorSet** outDescriptorSet) override
- {
- SLANG_UNUSED(layout);
- SLANG_UNUSED(flags);
- SLANG_UNUSED(outDescriptorSet);
- return SLANG_E_NOT_AVAILABLE;
- }
-
virtual SLANG_NO_THROW Result SLANG_MCALL createGraphicsPipelineState(
const GraphicsPipelineStateDesc& desc, IPipelineState** outState) override
{
diff --git a/tools/gfx/d3d11/render-d3d11.cpp b/tools/gfx/d3d11/render-d3d11.cpp
index 60fd4c7b3..08d159415 100644
--- a/tools/gfx/d3d11/render-d3d11.cpp
+++ b/tools/gfx/d3d11/render-d3d11.cpp
@@ -98,42 +98,6 @@ public:
UInt inputElementCount,
IInputLayout** outLayout) override;
- virtual SLANG_NO_THROW Result SLANG_MCALL createDescriptorSetLayout(
- const IDescriptorSetLayout::Desc& desc, IDescriptorSetLayout** outLayout) override
- {
- SLANG_UNUSED(desc);
- SLANG_UNUSED(outLayout);
- return SLANG_FAIL;
- }
- virtual SLANG_NO_THROW Result SLANG_MCALL
- createPipelineLayout(const IPipelineLayout::Desc& desc, IPipelineLayout** outLayout) override
- {
- SLANG_UNUSED(desc);
- SLANG_UNUSED(outLayout);
- return SLANG_FAIL;
- }
- virtual SLANG_NO_THROW Result SLANG_MCALL createDescriptorSet(
- IDescriptorSetLayout* layout,
- IDescriptorSet::Flag::Enum flag,
- IDescriptorSet** outDescriptorSet) override
- {
- SLANG_UNUSED(layout);
- SLANG_UNUSED(flag);
- SLANG_UNUSED(outDescriptorSet);
- return SLANG_FAIL;
- }
- virtual SLANG_NO_THROW void SLANG_MCALL setDescriptorSet(
- PipelineType pipelineType,
- IPipelineLayout* layout,
- UInt index,
- IDescriptorSet* descriptorSet) override
- {
- SLANG_UNUSED(pipelineType);
- SLANG_UNUSED(layout);
- SLANG_UNUSED(index);
- SLANG_UNUSED(descriptorSet);
- }
-
virtual Result createShaderObjectLayout(
slang::TypeLayoutReflection* typeLayout,
ShaderObjectLayoutBase** outLayout) override;
diff --git a/tools/gfx/d3d12/render-d3d12.cpp b/tools/gfx/d3d12/render-d3d12.cpp
index e0bfea838..e45eaad3a 100644
--- a/tools/gfx/d3d12/render-d3d12.cpp
+++ b/tools/gfx/d3d12/render-d3d12.cpp
@@ -112,33 +112,6 @@ public:
UInt inputElementCount,
IInputLayout** outLayout) override;
- virtual SLANG_NO_THROW Result SLANG_MCALL createDescriptorSetLayout(
- const IDescriptorSetLayout::Desc& desc,
- IDescriptorSetLayout** outLayout) override
- {
- SLANG_UNUSED(desc);
- SLANG_UNUSED(outLayout);
- return SLANG_FAIL;
- }
- virtual SLANG_NO_THROW Result SLANG_MCALL createPipelineLayout(
- const IPipelineLayout::Desc& desc,
- IPipelineLayout** outLayout) override
- {
- SLANG_UNUSED(desc);
- SLANG_UNUSED(outLayout);
- return SLANG_FAIL;
- }
- virtual SLANG_NO_THROW Result SLANG_MCALL createDescriptorSet(
- IDescriptorSetLayout* layout,
- IDescriptorSet::Flag::Enum flag,
- IDescriptorSet** outDescriptorSet) override
- {
- SLANG_UNUSED(layout);
- SLANG_UNUSED(flag);
- SLANG_UNUSED(outDescriptorSet);
- return SLANG_FAIL;
- }
-
virtual Result createShaderObjectLayout(
slang::TypeLayoutReflection* typeLayout,
ShaderObjectLayoutBase** outLayout) override;
@@ -2083,25 +2056,36 @@ public:
}
bindingState->futureRootParamOffset =
Math::Max(descTableIndex, bindingState->futureRootParamOffset);
- for (Index i = 0; i < layout->getSubObjectCount(); i++)
+ auto& subObjectRanges = layout->getSubObjectRanges();
+ for (Index i = 0; i < subObjectRanges.getCount(); i++)
{
+ auto bindingRange =
+ layout->getBindingRange(layout->getSubObjectRange(i).bindingRangeIndex);
switch (layout->getSubObjectRange(i).bindingType)
{
case slang::BindingType::ParameterBlock:
{
- auto newBindingState = *bindingState;
- newBindingState.offset.resource = 0;
- newBindingState.offset.sampler = 0;
- newBindingState.rootParamIndex =
- bindingState->futureRootParamOffset;
- newBindingState.futureRootParamOffset = newBindingState.rootParamIndex;
- m_objects[i]->bindObject(encoder, &newBindingState);
- bindingState->futureRootParamOffset = newBindingState.futureRootParamOffset;
+ auto baseIndex = bindingRange.binding.index;
+ for (uint32_t j = 0; j < bindingRange.count; j++)
+ {
+ auto newBindingState = *bindingState;
+ newBindingState.offset.resource = 0;
+ newBindingState.offset.sampler = 0;
+ newBindingState.rootParamIndex = bindingState->futureRootParamOffset;
+ newBindingState.futureRootParamOffset = newBindingState.rootParamIndex;
+ m_objects[baseIndex + j]->bindObject(encoder, &newBindingState);
+ bindingState->futureRootParamOffset =
+ newBindingState.futureRootParamOffset;
+ }
}
break;
case slang::BindingType::ConstantBuffer:
{
- m_objects[i]->bindObject(encoder, bindingState);
+ auto baseIndex = bindingRange.binding.index;
+ for (uint32_t j = 0; j < bindingRange.count; j++)
+ {
+ m_objects[baseIndex + j]->bindObject(encoder, bindingState);
+ }
}
break;
case slang::BindingType::ExistentialValue:
@@ -2534,16 +2518,6 @@ public:
bindRootShaderObjectImpl(object);
}
- virtual SLANG_NO_THROW void SLANG_MCALL setDescriptorSet(
- IPipelineLayout* layout,
- UInt index,
- IDescriptorSet* descriptorSet) override
- {
- SLANG_UNUSED(layout);
- SLANG_UNUSED(index);
- SLANG_UNUSED(descriptorSet);
- }
-
virtual SLANG_NO_THROW void SLANG_MCALL
setViewports(uint32_t count, const Viewport* viewports) override
{
@@ -2818,16 +2792,6 @@ public:
bindRootShaderObjectImpl(object);
}
- virtual SLANG_NO_THROW void SLANG_MCALL setDescriptorSet(
- IPipelineLayout* layout,
- UInt index,
- IDescriptorSet* descriptorSet) override
- {
- SLANG_UNUSED(layout);
- SLANG_UNUSED(index);
- SLANG_UNUSED(descriptorSet);
- }
-
virtual SLANG_NO_THROW void SLANG_MCALL dispatchCompute(int x, int y, int z) override
{
// Submit binding for compute
diff --git a/tools/gfx/immediate-renderer-base.cpp b/tools/gfx/immediate-renderer-base.cpp
index 92daa19ef..9402f7834 100644
--- a/tools/gfx/immediate-renderer-base.cpp
+++ b/tools/gfx/immediate-renderer-base.cpp
@@ -110,14 +110,6 @@ public:
m_writer->bindRootShaderObject(PipelineType::Graphics, object);
}
- virtual SLANG_NO_THROW void SLANG_MCALL setDescriptorSet(
- IPipelineLayout* layout,
- UInt index,
- IDescriptorSet* descriptorSet) override
- {
- m_writer->setDescriptorSet(PipelineType::Graphics, layout, index, descriptorSet);
- }
-
virtual SLANG_NO_THROW void SLANG_MCALL
setViewports(uint32_t count, const Viewport* viewports) override
{
@@ -218,14 +210,6 @@ public:
m_writer->bindRootShaderObject(PipelineType::Compute, object);
}
- virtual SLANG_NO_THROW void SLANG_MCALL setDescriptorSet(
- IPipelineLayout* layout,
- UInt index,
- IDescriptorSet* descriptorSet) override
- {
- m_writer->setDescriptorSet(PipelineType::Compute, layout, index, descriptorSet);
- }
-
virtual SLANG_NO_THROW void SLANG_MCALL dispatchCompute(int x, int y, int z) override
{
m_writer->dispatchCompute(x, y, z);
@@ -310,13 +294,6 @@ public:
(PipelineType)cmd.operands[0],
m_writer.getObject<IShaderObject>(cmd.operands[1]));
break;
- case CommandName::SetDescriptorSet:
- m_renderer->setDescriptorSet(
- (gfx::PipelineType)cmd.operands[0],
- m_writer.getObject<IPipelineLayout>(cmd.operands[1]),
- (UInt)cmd.operands[2],
- m_writer.getObject<IDescriptorSet>(cmd.operands[3]));
- break;
case CommandName::SetFramebuffer:
m_renderer->setFramebuffer(m_writer.getObject<IFramebuffer>(cmd.operands[0]));
break;
diff --git a/tools/gfx/immediate-renderer-base.h b/tools/gfx/immediate-renderer-base.h
index 311767822..a78671e49 100644
--- a/tools/gfx/immediate-renderer-base.h
+++ b/tools/gfx/immediate-renderer-base.h
@@ -25,11 +25,6 @@ private:
public:
// Immediate commands to be implemented by each target.
virtual SLANG_NO_THROW void SLANG_MCALL setPipelineState(IPipelineState* state) = 0;
- virtual SLANG_NO_THROW void SLANG_MCALL setDescriptorSet(
- PipelineType pipelineType,
- IPipelineLayout* layout,
- UInt index,
- IDescriptorSet* descriptorSet) = 0;
virtual SLANG_NO_THROW void SLANG_MCALL setFramebuffer(IFramebuffer* frameBuffer) = 0;
virtual SLANG_NO_THROW void SLANG_MCALL clearFrame(uint32_t colorBufferMask, bool clearDepth, bool clearStencil) = 0;
virtual SLANG_NO_THROW void SLANG_MCALL setViewports(UInt count, const Viewport* viewports) = 0;
diff --git a/tools/gfx/open-gl/render-gl.cpp b/tools/gfx/open-gl/render-gl.cpp
index 960670e1e..b9be7f1fc 100644
--- a/tools/gfx/open-gl/render-gl.cpp
+++ b/tools/gfx/open-gl/render-gl.cpp
@@ -129,42 +129,6 @@ public:
UInt inputElementCount,
IInputLayout** outLayout) override;
- virtual SLANG_NO_THROW Result SLANG_MCALL createDescriptorSetLayout(
- const IDescriptorSetLayout::Desc& desc, IDescriptorSetLayout** outLayout) override
- {
- SLANG_UNUSED(desc);
- SLANG_UNUSED(outLayout);
- return SLANG_FAIL;
- }
- virtual SLANG_NO_THROW Result SLANG_MCALL
- createPipelineLayout(const IPipelineLayout::Desc& desc, IPipelineLayout** outLayout) override
- {
- SLANG_UNUSED(desc);
- SLANG_UNUSED(outLayout);
- return SLANG_FAIL;
- }
- virtual SLANG_NO_THROW Result SLANG_MCALL createDescriptorSet(
- IDescriptorSetLayout* layout,
- IDescriptorSet::Flag::Enum flag,
- IDescriptorSet** outDescriptorSet) override
- {
- SLANG_UNUSED(layout);
- SLANG_UNUSED(flag);
- SLANG_UNUSED(outDescriptorSet);
- return SLANG_FAIL;
- }
- virtual SLANG_NO_THROW void SLANG_MCALL setDescriptorSet(
- PipelineType pipelineType,
- IPipelineLayout* layout,
- UInt index,
- IDescriptorSet* descriptorSet) override
- {
- SLANG_UNUSED(pipelineType);
- SLANG_UNUSED(layout);
- SLANG_UNUSED(index);
- SLANG_UNUSED(descriptorSet);
- }
-
virtual Result createShaderObjectLayout(
slang::TypeLayoutReflection* typeLayout,
ShaderObjectLayoutBase** outLayout) override;
diff --git a/tools/gfx/render-graphics-common.cpp b/tools/gfx/render-graphics-common.cpp
deleted file mode 100644
index b734bf391..000000000
--- a/tools/gfx/render-graphics-common.cpp
+++ /dev/null
@@ -1,1766 +0,0 @@
-#include "render-graphics-common.h"
-#include "core/slang-basic.h"
-
-using namespace Slang;
-
-namespace gfx
-{
-
-class GraphicsCommonShaderObjectLayout : public ShaderObjectLayoutBase
-{
-public:
- struct BindingRangeInfo
- {
- slang::BindingType bindingType;
- Index count;
- Index baseIndex;
- Index descriptorSetIndex;
- Index rangeIndexInDescriptorSet;
-
- // Returns true if this binding range consumes a specialization argument slot.
- bool isSpecializationArg() const
- {
- return bindingType == slang::BindingType::ExistentialValue;
- }
- };
-
- struct SubObjectRangeInfo
- {
- RefPtr<GraphicsCommonShaderObjectLayout> layout;
- Index bindingRangeIndex;
- };
-
- struct DescriptorSetInfo : public RefObject
- {
- ComPtr<IDescriptorSetLayout> layout;
- Slang::Int space = -1;
- };
-
- struct Builder
- {
- public:
- Builder(RendererBase* renderer)
- : m_renderer(renderer)
- {}
-
- RendererBase* m_renderer;
- slang::TypeLayoutReflection* m_elementTypeLayout;
-
- 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<IDescriptorSetLayout::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, ReadOnlyStorageBuffer);
- CASE(MutableRawBuffer, StorageBuffer);
- CASE(InputRenderTarget, InputAttachment);
- CASE(InlineUniformData, InlineUniformBlock);
- CASE(RayTracingAccelerationStructure, RayTracingAccelerationStructure);
- CASE(ConstantBuffer, UniformBuffer);
- CASE(PushConstant, RootConstant);
-
-#undef CASE
- }
- }
-
- 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);
-
- switch (slangBindingType)
- {
- case slang::BindingType::ExistentialValue:
- case slang::BindingType::InlineUniformData:
- continue;
- default:
- break;
- }
-
- auto gfxDescriptorType = _mapDescriptorType(slangBindingType);
-
- IDescriptorSetLayout::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.baseIndex = baseIndex;
- bindingRangeInfo.descriptorSetIndex = descriptorSetIndex;
- bindingRangeInfo.rangeIndexInDescriptorSet = rangeIndexInDescriptorSet;
-
- m_bindingRanges.add(bindingRangeInfo);
- }
-
- 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);
- }
- return SLANG_OK;
- }
-
- SlangResult build(GraphicsCommonShaderObjectLayout** outLayout)
- {
- auto layout =
- RefPtr<GraphicsCommonShaderObjectLayout>(new GraphicsCommonShaderObjectLayout());
- SLANG_RETURN_ON_FAIL(layout->_init(this));
-
- *outLayout = layout.detach();
- return SLANG_OK;
- }
- };
-
- static Result createForElementType(
- RendererBase* 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; }
-
- RendererBase* getRenderer() { return m_renderer; }
-
- slang::TypeReflection* getType()
- {
- return m_elementTypeLayout->getType();
- }
-protected:
- Result _init(Builder const* builder)
- {
- auto renderer = builder->m_renderer;
-
- initBase(renderer, builder->m_elementTypeLayout);
-
- m_bindingRanges = builder->m_bindingRanges;
-
- for (auto descriptorSetBuildInfo : builder->m_descriptorSetBuildInfos)
- {
- auto& slotRangeDescs = descriptorSetBuildInfo->slotRangeDescs;
- IDescriptorSetLayout::Desc desc;
- desc.slotRangeCount = slotRangeDescs.getCount();
- desc.slotRanges = slotRangeDescs.getBuffer();
-
- ComPtr<IDescriptorSetLayout> 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;
- }
-
- List<RefPtr<DescriptorSetInfo>> m_descriptorSets;
- List<BindingRangeInfo> m_bindingRanges;
- 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(IDevice* device)
- : Super::Builder(static_cast<RendererBase*>(device))
- {}
-
- 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(
- RendererBase* renderer,
- slang::IComponentType* program,
- slang::ProgramLayout* programLayout)
- : Super::Builder(renderer)
- , m_program(program)
- , m_programLayout(programLayout)
- {}
-
- 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();
- }
- else
- {
- info.rangeOffset = 0;
- }
-
- auto slangEntryPointLayout = entryPointLayout->getSlangLayout();
- _addDescriptorSets(
- slangEntryPointLayout->getTypeLayout(), slangEntryPointLayout->getVarLayout());
-
- m_entryPoints.add(info);
- }
-
- slang::IComponentType* m_program;
- slang::ProgramLayout* m_programLayout;
- List<EntryPointInfo> m_entryPoints;
- };
-
- Slang::Int getRenderTargetCount() { return m_renderTargetCount; }
-
- IPipelineLayout* 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; }
-
- static Result create(
- RendererBase* renderer,
- slang::IComponentType* program,
- slang::ProgramLayout* programLayout,
- GraphicsCommonProgramLayout** outLayout)
- {
- GraphicsCommonProgramLayout::Builder builder(renderer, program, programLayout);
- builder.addGlobalParams(programLayout->getGlobalParamsVarLayout());
-
- SlangInt entryPointCount = programLayout->getEntryPointCount();
- for (SlangInt e = 0; e < entryPointCount; ++e)
- {
- auto slangEntryPoint = programLayout->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(outLayout));
-
- return SLANG_OK;
- }
-
- slang::IComponentType* getSlangProgram() const { return m_program; }
- slang::ProgramLayout* getSlangProgramLayout() const { return m_programLayout; }
-
-protected:
- Result _init(Builder const* builder)
- {
- auto renderer = builder->m_renderer;
-
- SLANG_RETURN_ON_FAIL(Super::_init(builder));
-
- m_program = builder->m_program;
- m_programLayout = builder->m_programLayout;
- m_entryPoints = builder->m_entryPoints;
-
- List<IPipelineLayout::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();
- }
-
- if (m_program->getSpecializationParamCount() == 0)
- {
- IPipelineLayout::Desc pipelineLayoutDesc;
-
- // HACK: we set `renderTargetCount` to zero here becasue otherwise the D3D12
- // render back-end will adjust all UAV registers by this value to account
- // for the `SV_Target<N>` outputs implicitly consuming `u<N>` registers for
- // Shader Model 5.0.
- //
- // When using the shader object path, all registers are being set via Slang
- // reflection information, and we do not need/want the automatic adjustment.
- //
- // TODO: Once we eliminate the non-shader-object path, this whole issue should
- // be moot, because the `ProgramLayout` should own/be the pipeline layout anyway.
- //
- pipelineLayoutDesc.renderTargetCount = 0;
-
- 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<IPipelineLayout::DescriptorSetDesc>& ioPipelineDescriptorSets)
- {
- for (auto descriptorSetInfo : layout->getDescriptorSets())
- {
- IPipelineLayout::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
-
- ComPtr<slang::IComponentType> m_program;
- slang::ProgramLayout* m_programLayout = nullptr;
-
- List<EntryPointInfo> m_entryPoints;
- gfx::UInt m_renderTargetCount = 0;
-
- ComPtr<IPipelineLayout> m_pipelineLayout;
-};
-
-class GraphicsCommonShaderObject : public ShaderObjectBase
-{
-public:
- static Result create(
- IDevice* device,
- GraphicsCommonShaderObjectLayout* layout,
- GraphicsCommonShaderObject** outShaderObject)
- {
- auto object = ComPtr<GraphicsCommonShaderObject>(new GraphicsCommonShaderObject());
- SLANG_RETURN_ON_FAIL(object->init(device, layout));
-
- *outShaderObject = object.detach();
- return SLANG_OK;
- }
-
- RendererBase* getDevice() { return m_layout->getDevice(); }
-
- SLANG_NO_THROW UInt SLANG_MCALL getEntryPointCount() SLANG_OVERRIDE { return 0; }
-
- SLANG_NO_THROW Result SLANG_MCALL getEntryPoint(UInt index, IShaderObject** outEntryPoint)
- SLANG_OVERRIDE
- {
- *outEntryPoint = nullptr;
- return SLANG_OK;
- }
-
- GraphicsCommonShaderObjectLayout* getLayout()
- {
- return static_cast<GraphicsCommonShaderObjectLayout*>(m_layout.Ptr());
- }
-
- SLANG_NO_THROW slang::TypeLayoutReflection* SLANG_MCALL getElementTypeLayout() SLANG_OVERRIDE
- {
- return m_layout->getElementTypeLayout();
- }
-
- SLANG_NO_THROW Result SLANG_MCALL
- setData(ShaderOffset const& inOffset, void const* data, size_t inSize) SLANG_OVERRIDE
- {
- Index offset = inOffset.uniformOffset;
- Index size = inSize;
-
- char* dest = m_ordinaryData.getBuffer();
- Index availableSize = m_ordinaryData.getCount();
-
- // TODO: We really should bounds-check access rather than silently ignoring sets
- // that are too large, but we have several test cases that set more data than
- // an object actually stores on several targets...
- //
- if(offset < 0)
- {
- size += offset;
- offset = 0;
- }
- if((offset + size) >= availableSize)
- {
- size = availableSize - offset;
- }
-
- memcpy(dest + offset, data, size);
-
- return SLANG_OK;
- }
-
- virtual SLANG_NO_THROW Result SLANG_MCALL
- setObject(ShaderOffset const& offset, IShaderObject* object)
- SLANG_OVERRIDE
- {
- if (offset.bindingRangeIndex < 0)
- return SLANG_E_INVALID_ARG;
- auto layout = getLayout();
- if (offset.bindingRangeIndex >= layout->getBindingRangeCount())
- return SLANG_E_INVALID_ARG;
-
- auto subObject = static_cast<GraphicsCommonShaderObject*>(object);
-
- auto bindingRangeIndex = offset.bindingRangeIndex;
- auto& bindingRange = layout->getBindingRange(bindingRangeIndex);
-
- m_objects[bindingRange.baseIndex + offset.bindingArrayIndex] = subObject;
-
- // If the range being assigned into represents an interface/existential-type leaf field,
- // then we need to consider how the `object` being assigned here affects specialization.
- // We may also need to assign some data from the sub-object into the ordinary data
- // buffer for the parent object.
- //
- if( bindingRange.bindingType == slang::BindingType::ExistentialValue )
- {
- // A leaf field of interface type is laid out inside of the parent object
- // as a tuple of `(RTTI, WitnessTable, Payload)`. The layout of these fields
- // is a contract between the compiler and any runtime system, so we will
- // need to rely on details of the binary layout.
-
- // We start by querying the layout/type of the concrete value that the application
- // is trying to store into the field, and also the layout/type of the leaf
- // existential-type field itself.
- //
- auto concreteTypeLayout = subObject->getElementTypeLayout();
- auto concreteType = concreteTypeLayout->getType();
- //
- auto existentialTypeLayout = layout->getElementTypeLayout()->getBindingRangeLeafTypeLayout(bindingRangeIndex);
- auto existentialType = existentialTypeLayout->getType();
-
- // The first field of the tuple (offset zero) is the run-time type information (RTTI)
- // ID for the concrete type being stored into the field.
- //
- // TODO: We need to be able to gather the RTTI type ID from `object` and then
- // use `setData(offset, &TypeID, sizeof(TypeID))`.
-
- // The second field of the tuple (offset 8) is the ID of the "witness" for the
- // conformance of the concrete type to the interface used by this field.
- //
- auto witnessTableOffset = offset;
- witnessTableOffset.uniformOffset += 8;
- //
- // Conformances of a type to an interface are computed and then stored by the
- // Slang runtime, so we can look up the ID for this particular conformance (which
- // will create it on demand).
- //
- ComPtr<slang::ISession> slangSession;
- SLANG_RETURN_ON_FAIL(getRenderer()->getSlangSession(slangSession.writeRef()));
- //
- // Note: If the type doesn't actually conform to the required interface for
- // this sub-object range, then this is the point where we will detect that
- // fact and error out.
- //
- uint32_t conformanceID = 0xFFFFFFFF;
- SLANG_RETURN_ON_FAIL(slangSession->getTypeConformanceWitnessSequentialID(
- concreteType, existentialType, &conformanceID));
- //
- // Once we have the conformance ID, then we can write it into the object
- // at the required offset.
- //
- SLANG_RETURN_ON_FAIL(setData(witnessTableOffset, &conformanceID, sizeof(conformanceID)));
-
- // The third field of the tuple (offset 16) is the "payload" that is supposed to
- // hold the data for a value of the given concrete type.
- //
- auto payloadOffset = offset;
- payloadOffset.uniformOffset += 16;
-
- // There are two cases we need to consider here for how the payload might be used:
- //
- // * If the concrete type of the value being bound is one that can "fit" into the
- // available payload space, then it should be stored in the payload.
- //
- // * If the concrete type of the value cannot fit in the payload space, then it
- // will need to be stored somewhere else.
- //
- if(_doesValueFitInExistentialPayload(concreteTypeLayout, existentialTypeLayout))
- {
- // If the value can fit in the payload area, then we will go ahead and copy
- // its bytes into that area.
- //
- setData(payloadOffset, subObject->m_ordinaryData.getBuffer(), subObject->m_ordinaryData.getCount());
- }
- else
- {
- // If the value does *not *fit in the payload area, then there is nothing
- // we can do at this point (beyond saving a reference to the sub-object, which
- // was handled above).
- //
- // Once all the sub-objects have been set into the parent object, we can
- // compute a specialized layout for it, and that specialized layout can tell
- // us where the data for these sub-objects has been laid out.
- }
- }
-
- return SLANG_E_NOT_IMPLEMENTED;
- }
-
- virtual SLANG_NO_THROW Result SLANG_MCALL
- getObject(ShaderOffset const& offset, IShaderObject** outObject)
- SLANG_OVERRIDE
- {
- SLANG_ASSERT(outObject);
- if (offset.bindingRangeIndex < 0)
- return SLANG_E_INVALID_ARG;
- auto layout = getLayout();
- if (offset.bindingRangeIndex >= layout->getBindingRangeCount())
- return SLANG_E_INVALID_ARG;
- auto& bindingRange = layout->getBindingRange(offset.bindingRangeIndex);
-
- auto object = m_objects[bindingRange.baseIndex + offset.bindingArrayIndex].Ptr();
- object->addRef();
- *outObject = object;
-
- // 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
- }
-
- SLANG_NO_THROW Result SLANG_MCALL
- setResource(ShaderOffset const& offset, IResourceView* resourceView) SLANG_OVERRIDE
- {
- if (offset.bindingRangeIndex < 0)
- return SLANG_E_INVALID_ARG;
- auto layout = getLayout();
- if (offset.bindingRangeIndex >= layout->getBindingRangeCount())
- return SLANG_E_INVALID_ARG;
- auto& bindingRange = layout->getBindingRange(offset.bindingRangeIndex);
-
- m_resourceViews[bindingRange.baseIndex + offset.bindingArrayIndex] = resourceView;
- return SLANG_OK;
- }
-
- SLANG_NO_THROW Result SLANG_MCALL setSampler(ShaderOffset const& offset, ISamplerState* sampler)
- SLANG_OVERRIDE
- {
- if (offset.bindingRangeIndex < 0)
- return SLANG_E_INVALID_ARG;
- auto layout = getLayout();
- if (offset.bindingRangeIndex >= layout->getBindingRangeCount())
- return SLANG_E_INVALID_ARG;
- auto& bindingRange = layout->getBindingRange(offset.bindingRangeIndex);
-
- m_samplers[bindingRange.baseIndex + offset.bindingArrayIndex] = sampler;
- return SLANG_OK;
- }
-
- SLANG_NO_THROW Result SLANG_MCALL setCombinedTextureSampler(
- ShaderOffset const& offset, IResourceView* textureView, ISamplerState* sampler) SLANG_OVERRIDE
- {
- if (offset.bindingRangeIndex < 0)
- return SLANG_E_INVALID_ARG;
- auto layout = getLayout();
- if (offset.bindingRangeIndex >= layout->getBindingRangeCount())
- return SLANG_E_INVALID_ARG;
- auto& bindingRange = layout->getBindingRange(offset.bindingRangeIndex);
-
- auto& slot = m_combinedTextureSamplers[bindingRange.baseIndex + offset.bindingArrayIndex];
- slot.textureView = textureView;
- slot.sampler = sampler;
- return SLANG_OK;
- }
-
-public:
- // Appends all types that are used to specialize the element type of this shader object in `args` list.
- virtual Result collectSpecializationArgs(ExtendedShaderObjectTypeList& args) override
- {
- auto& subObjectRanges = getLayout()->getSubObjectRanges();
- // The following logic is built on the assumption that all fields that involve existential types (and
- // therefore require specialization) will results in a sub-object range in the type layout.
- // This allows us to simply scan the sub-object ranges to find out all specialization arguments.
- Index subObjectRangeCount = subObjectRanges.getCount();
- for (Index subObjectRangeIndex = 0; subObjectRangeIndex < subObjectRangeCount; subObjectRangeIndex++)
- {
- auto const& subObjectRange = subObjectRanges[subObjectRangeIndex];
- auto const& bindingRange = getLayout()->getBindingRange(subObjectRange.bindingRangeIndex);
-
- Index count = bindingRange.count;
- SLANG_ASSERT(count == 1);
-
- Index subObjectIndexInRange = 0;
- auto subObject = m_objects[bindingRange.baseIndex + subObjectIndexInRange];
-
- switch (bindingRange.bindingType)
- {
- case slang::BindingType::ExistentialValue:
- {
- // A binding type of `ExistentialValue` means the sub-object represents a interface-typed field.
- // In this case the specialization argument for this field is the actual specialized type of the bound
- // shader object. If the shader object's type is an ordinary type without existential fields, then the
- // type argument will simply be the ordinary type. But if the sub object's type is itself a specialized
- // type, we need to make sure to use that type as the specialization argument.
-
- ExtendedShaderObjectType specializedSubObjType;
- SLANG_RETURN_ON_FAIL(subObject->getSpecializedShaderObjectType(&specializedSubObjType));
- args.add(specializedSubObjType);
- break;
- }
- case slang::BindingType::ParameterBlock:
- case slang::BindingType::ConstantBuffer:
- // Currently we only handle the case where the field's type is
- // `ParameterBlock<SomeStruct>` or `ConstantBuffer<SomeStruct>`, where `SomeStruct` is a struct type
- // (not directly an interface type). In this case, we just recursively collect the specialization arguments
- // from the bound sub object.
- SLANG_RETURN_ON_FAIL(subObject->collectSpecializationArgs(args));
- // TODO: we need to handle the case where the field is of the form `ParameterBlock<IFoo>`. We should treat
- // this case the same way as the `ExistentialValue` case here, but currently we lack a mechanism to distinguish
- // the two scenarios.
- break;
- }
- // TODO: need to handle another case where specialization happens on resources fields e.g. `StructuredBuffer<IFoo>`.
- }
- return SLANG_OK;
- }
-
-
-protected:
- friend class ProgramVars;
-
- Result init(IDevice* device, GraphicsCommonShaderObjectLayout* layout)
- {
- m_layout = layout;
-
- // If the layout tells us that there is any uniform data,
- // then we will allocate a CPU memory buffer to hold that data
- // while it is being set from the host.
- //
- // Once the user is done setting the parameters/fields of this
- // shader object, we will produce a GPU-memory version of the
- // uniform data (which includes values from this object and
- // any existential-type sub-objects).
- //
- size_t uniformSize = layout->getElementTypeLayout()->getSize();
- if (uniformSize)
- {
- m_ordinaryData.setCount(uniformSize);
- memset(m_ordinaryData.getBuffer(), 0, uniformSize);
- }
-
-#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())
- {
- auto 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 object(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(device, subObjectLayout, subObject.writeRef()));
- m_objects[bindingRangeInfo.baseIndex + i] = subObject;
- }
- }
-
- return SLANG_OK;
- }
-
- Result apply(
- RendererBase* renderer,
- GraphicsComputeCommandEncoderBase* encoder,
- PipelineType pipelineType,
- IPipelineLayout* pipelineLayout,
- Index& ioRootIndex)
- {
- GraphicsCommonShaderObjectLayout* layout = getLayout();
-
- // Create the descritpor sets required by the layout...
- //
- List<ComPtr<IDescriptorSet>> descriptorSets;
- for (auto descriptorSetInfo : layout->getDescriptorSets())
- {
- ComPtr<IDescriptorSet> descriptorSet;
- SLANG_RETURN_ON_FAIL(
- renderer->createDescriptorSet(descriptorSetInfo->layout, IDescriptorSet::Flag::Transient, descriptorSet.writeRef()));
- descriptorSets.add(descriptorSet);
- }
-
- SLANG_RETURN_ON_FAIL(_bindIntoDescriptorSets(encoder, descriptorSets.getBuffer()));
-
- for (auto descriptorSet : descriptorSets)
- {
- encoder->setDescriptorSetImpl(pipelineType, pipelineLayout, ioRootIndex++, descriptorSet);
- }
-
- return SLANG_OK;
- }
-
- /// Write the uniform/ordinary data of this object into the given `dest` buffer at the given `offset`
- Result _writeOrdinaryData(
- GraphicsComputeCommandEncoderBase* encoder,
- IBufferResource* buffer,
- size_t offset,
- size_t destSize,
- GraphicsCommonShaderObjectLayout* specializedLayout)
- {
- auto src = m_ordinaryData.getBuffer();
- auto srcSize = size_t(m_ordinaryData.getCount());
-
- SLANG_ASSERT(srcSize <= destSize);
-
- encoder->uploadBufferDataImpl(buffer, offset, srcSize, src);
-
- // In the case where this object has any sub-objects of
- // existential/interface type, we need to recurse on those objects
- // that need to write their state into an appropriate "pending" allocation.
- //
- // Note: Any values that could fit into the "payload" included
- // in the existential-type field itself will have already been
- // written as part of `setObject()`. This loop only needs to handle
- // those sub-objects that do not "fit."
- //
- // An implementers looking at this code might wonder if things could be changed
- // so that *all* writes related to sub-objects for interface-type fields could
- // be handled in this one location, rather than having some in `setObject()` and
- // others handled here.
- //
- Index subObjectRangeCounter = 0;
- for( auto const& subObjectRangeInfo : specializedLayout->getSubObjectRanges() )
- {
- Index subObjectRangeIndex = subObjectRangeCounter++;
- auto const& bindingRangeInfo = specializedLayout->getBindingRange(subObjectRangeInfo.bindingRangeIndex);
-
- // We only need to handle sub-object ranges for interface/existential-type fields,
- // because fields of constant-buffer or parameter-block type are responsible for
- // the ordinary/uniform data of their own existential/interface-type sub-objects.
- //
- if(bindingRangeInfo.bindingType != slang::BindingType::ExistentialValue)
- continue;
-
- // Each sub-object range represents a single "leaf" field, but might be nested
- // under zero or more outer arrays, such that the number of existential values
- // in the same range can be one or more.
- //
- auto count = bindingRangeInfo.count;
-
- // We are not concerned with the case where the existential value(s) in the range
- // git into the payload part of the leaf field.
- //
- // In the case where the value didn't fit, the Slang layout strategy would have
- // considered the requirements of the value as a "pending" allocation, and would
- // allocate storage for the ordinary/uniform part of that pending allocation inside
- // of the parent object's type layout.
- //
- // Here we assume that the Slang reflection API can provide us with a single byte
- // offset and stride for the location of the pending data allocation in the specialized
- // type layout, which will store the values for this sub-object range.
- //
- // TODO: The reflection API functions we are assuming here haven't been implemented
- // yet, so the functions being called here are stubs.
- //
- // TODO: It might not be that a single sub-object range can reliably map to a single
- // contiguous array with a single stride; we need to carefully consider what the layout
- // logic does for complex cases with multiple layers of nested arrays and structures.
- //
- size_t subObjectRangePendingDataOffset = _getSubObjectRangePendingDataOffset(specializedLayout, subObjectRangeIndex);
- size_t subObjectRangePendingDataStride = _getSubObjectRangePendingDataStride(specializedLayout, subObjectRangeIndex);
-
- // If the range doesn't actually need/use the "pending" allocation at all, then
- // we need to detect that case and skip such ranges.
- //
- // TODO: This should probably be handled on a per-object basis by caching a "does it fit?"
- // bit as part of the information for bound sub-objects, given that we already
- // compute the "does it fit?" status as part of `setObject()`.
- //
- if(subObjectRangePendingDataOffset == 0)
- continue;
-
- for( Slang::Index i = 0; i < count; ++i )
- {
- auto subObject = m_objects[bindingRangeInfo.baseIndex + i];
-
- RefPtr<GraphicsCommonShaderObjectLayout> subObjectLayout;
- SLANG_RETURN_ON_FAIL(subObject->_getSpecializedLayout(subObjectLayout.writeRef()));
-
- auto subObjectOffset = subObjectRangePendingDataOffset + i*subObjectRangePendingDataStride;
-
- subObject->_writeOrdinaryData(encoder, buffer, offset + subObjectOffset, destSize - subObjectOffset, subObjectLayout);
- }
- }
-
- return SLANG_OK;
- }
-
- // As discussed in `_writeOrdinaryData()`, these methods are just stubs waiting for
- // the "flat" Slang refelction information to provide access to the relevant data.
- //
- size_t _getSubObjectRangePendingDataOffset(GraphicsCommonShaderObjectLayout* specializedLayout, Index subObjectRangeIndex) { return 0; }
- size_t _getSubObjectRangePendingDataStride(GraphicsCommonShaderObjectLayout* specializedLayout, Index subObjectRangeIndex) { return 0; }
-
- /// Ensure that the `m_ordinaryDataBuffer` has been created, if it is needed
- Result _ensureOrdinaryDataBufferCreatedIfNeeded(GraphicsComputeCommandEncoderBase* encoder)
- {
- // If we have already created a buffer to hold ordinary data, then we should
- // simply re-use that buffer rather than re-create it.
- //
- // TODO: Simply re-using the buffer without any kind of validation checks
- // means that we are assuming that users cannot or will not perform any `set`
- // operations on a shader object once an operation has requested this buffer
- // be created. We need to enforce that rule if we want to rely on it.
- //
- if( m_ordinaryDataBuffer )
- return SLANG_OK;
-
- // Computing the size of the ordinary data buffer is *not* just as simple
- // as using the size of the `m_ordinayData` array that we store. The reason
- // for the added complexity is that interface-type fields may lead to the
- // storage being specialized such that it needs extra appended data to
- // store the concrete values that logically belong in those interface-type
- // fields but wouldn't fit in the fixed-size allocation we gave them.
- //
- // TODO: We need to actually implement that logic by using reflection
- // data computed for the specialized type of this shader object.
- // For now we just make the simple assumption described above despite
- // knowing that it is false.
- //
- RefPtr<GraphicsCommonShaderObjectLayout> specializedLayout;
- SLANG_RETURN_ON_FAIL(_getSpecializedLayout(specializedLayout.writeRef()));
-
- auto specializedOrdinaryDataSize = specializedLayout->getElementTypeLayout()->getSize();
- if(specializedOrdinaryDataSize == 0)
- return SLANG_OK;
-
- // Once we have computed how large the buffer should be, we can allocate
- // it using the existing public `IDevice` API.
- //
- IDevice* device = getRenderer();
- IBufferResource::Desc bufferDesc;
- bufferDesc.init(specializedOrdinaryDataSize);
- bufferDesc.cpuAccessFlags |= IResource::AccessFlag::Write;
- SLANG_RETURN_ON_FAIL(device->createBufferResource(
- IResource::Usage::ConstantBuffer, bufferDesc, nullptr, m_ordinaryDataBuffer.writeRef()));
-
- // Once the buffer is allocated, we can use `_writeOrdinaryData` to fill it in.
- //
- // Note that `_writeOrdinaryData` is potentially recursive in the case
- // where this object contains interface/existential-type fields, so we
- // don't need or want to inline it into this call site.
- //
- SLANG_RETURN_ON_FAIL(_writeOrdinaryData(
- encoder, m_ordinaryDataBuffer, 0, specializedOrdinaryDataSize, specializedLayout));
- return SLANG_OK;
- }
-
- /// Bind the buffer for ordinary/uniform data, if needed
- Result _bindOrdinaryDataBufferIfNeeded(
- GraphicsComputeCommandEncoderBase* encoder,
- IDescriptorSet* descriptorSet,
- Index* ioBaseRangeIndex,
- Index subObjectRangeArrayIndex)
- {
- // We are going to need to tweak the base binding range index
- // used for descriptor-set writes if and only if we actually
- // bind a buffer for ordinary data.
- //
- auto& baseRangeIndex = *ioBaseRangeIndex;
-
- // We start by ensuring that the buffer is created, if it is needed.
- //
- SLANG_RETURN_ON_FAIL(_ensureOrdinaryDataBufferCreatedIfNeeded(encoder));
-
- // If we did indeed need/create a buffer, then we must bind it into
- // the given `descriptorSet` and update the base range index for
- // subsequent binding operations to account for it.
- //
- if (m_ordinaryDataBuffer)
- {
- descriptorSet->setConstantBuffer(baseRangeIndex, subObjectRangeArrayIndex, m_ordinaryDataBuffer);
- baseRangeIndex++;
- }
-
- return SLANG_OK;
- }
-
- Result _bindIntoDescriptorSet(
- GraphicsComputeCommandEncoderBase* encoder,
- IDescriptorSet* descriptorSet,
- Index baseRangeIndex,
- Index subObjectRangeArrayIndex)
- {
- GraphicsCommonShaderObjectLayout* layout = getLayout();
-
- _bindOrdinaryDataBufferIfNeeded(
- encoder, descriptorSet, &baseRangeIndex, subObjectRangeArrayIndex);
-
- 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(GraphicsComputeCommandEncoderBase* encoder, ComPtr<IDescriptorSet>* descriptorSets)
- {
- GraphicsCommonShaderObjectLayout* layout = getLayout();
-
- Index baseRangeIndex = 0;
- _bindOrdinaryDataBufferIfNeeded(encoder, descriptorSets[0], &baseRangeIndex, 0);
-
- // Fill in the descriptor sets based on binding ranges
- //
- for (auto bindingRangeInfo : layout->getBindingRanges())
- {
- auto 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(encoder, 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(encoder, descriptorSet, rangeIndex, i);
- }
- break;
-
- default:
- for (Index i = 0; i < count; ++i)
- {
- descriptorSet->setResource(rangeIndex, i, m_resourceViews[baseIndex + i]);
- }
- break;
- }
- }
- return SLANG_OK;
- }
-
- /// Any "ordinary" / uniform data for this object
- List<char> m_ordinaryData;
-
- List<ComPtr<IResourceView>> m_resourceViews;
-
- List<ComPtr<ISamplerState>> m_samplers;
-
- struct CombinedTextureSamplerSlot
- {
- ComPtr<IResourceView> textureView;
- ComPtr<ISamplerState> sampler;
- };
- List<CombinedTextureSamplerSlot> m_combinedTextureSamplers;
-
- List<RefPtr<GraphicsCommonShaderObject>> m_objects;
-
- /// A constant buffer used to stored ordinary data for this object
- /// and existential-type sub-objects.
- ///
- /// Created on demand with `_createOrdinaryDataBufferIfNeeded()`
- ComPtr<IBufferResource> m_ordinaryDataBuffer;
-
- /// Get the layout of this shader object with specialization arguments considered
- ///
- /// This operation should only be called after the shader object has been
- /// fully filled in and finalized.
- ///
- Result _getSpecializedLayout(GraphicsCommonShaderObjectLayout** outLayout)
- {
- if(!m_specializedLayout)
- {
- SLANG_RETURN_ON_FAIL(_createSpecializedLayout(m_specializedLayout.writeRef()));
- }
- *outLayout = RefPtr<GraphicsCommonShaderObjectLayout>(m_specializedLayout).detach();
- return SLANG_OK;
- }
-
- /// Create the layout for this shader object with specialization arguments considered
- ///
- /// This operation is virtual so that it can be customized by `ProgramVars`.
- ///
- virtual Result _createSpecializedLayout(GraphicsCommonShaderObjectLayout** outLayout)
- {
- ExtendedShaderObjectType extendedType;
- SLANG_RETURN_ON_FAIL(getSpecializedShaderObjectType(&extendedType));
-
- auto renderer = getRenderer();
- RefPtr<ShaderObjectLayoutBase> layout;
- SLANG_RETURN_ON_FAIL(renderer->getShaderObjectLayout(extendedType.slangType, layout.writeRef()));
-
- *outLayout = static_cast<GraphicsCommonShaderObjectLayout*>(layout.detach());
- return SLANG_OK;
- }
-
- RefPtr<GraphicsCommonShaderObjectLayout> m_specializedLayout;
-};
-
-class EntryPointVars : public GraphicsCommonShaderObject
-{
- typedef GraphicsCommonShaderObject Super;
-
-public:
- static Result
- create(IDevice* device, EntryPointLayout* layout, EntryPointVars** outShaderObject)
- {
- RefPtr<EntryPointVars> object = new EntryPointVars();
- SLANG_RETURN_ON_FAIL(object->init(device, layout));
-
- *outShaderObject = object.detach();
- return SLANG_OK;
- }
-
- EntryPointLayout* getLayout() { return static_cast<EntryPointLayout*>(m_layout.Ptr()); }
-
-protected:
- Result init(IDevice* device, EntryPointLayout* layout)
- {
- SLANG_RETURN_ON_FAIL(Super::init(device, layout));
- return SLANG_OK;
- }
-};
-
-class ProgramVars : public GraphicsCommonShaderObject
-{
- typedef GraphicsCommonShaderObject Super;
-
-public:
- static Result create(IDevice* device, GraphicsCommonProgramLayout* layout, ProgramVars** outShaderObject)
- {
- RefPtr<ProgramVars> object = new ProgramVars();
- SLANG_RETURN_ON_FAIL(object->init(device, layout));
-
- *outShaderObject = object.detach();
- return SLANG_OK;
- }
-
- GraphicsCommonProgramLayout* getLayout() { return static_cast<GraphicsCommonProgramLayout*>(m_layout.Ptr()); }
-
- void apply(RendererBase* renderer, GraphicsComputeCommandEncoderBase* encoder, PipelineType pipelineType)
- {
- auto pipelineLayout = encoder->m_currentPipeline->m_pipelineLayout.get();
-
- Index rootIndex = 0;
- GraphicsCommonShaderObject::apply(renderer, encoder, 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; }
-
-
- UInt SLANG_MCALL getEntryPointCount() SLANG_OVERRIDE { return (UInt)m_entryPoints.getCount(); }
- SlangResult SLANG_MCALL getEntryPoint(UInt index, IShaderObject** outEntryPoint) SLANG_OVERRIDE
- {
- *outEntryPoint = m_entryPoints[index];
- m_entryPoints[index]->addRef();
- return SLANG_OK;
- }
-
- virtual Result collectSpecializationArgs(ExtendedShaderObjectTypeList& args) override
- {
- SLANG_RETURN_ON_FAIL(GraphicsCommonShaderObject::collectSpecializationArgs(args));
- for (auto& entryPoint : m_entryPoints)
- {
- SLANG_RETURN_ON_FAIL(entryPoint->collectSpecializationArgs(args));
- }
- return SLANG_OK;
- }
-
-protected:
- virtual Result _bindIntoDescriptorSets(
- GraphicsComputeCommandEncoderBase* encoder, ComPtr<IDescriptorSet>* descriptorSets) override
- {
- SLANG_RETURN_ON_FAIL(Super::_bindIntoDescriptorSets(encoder, 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(
- encoder, descriptorSets[0], entryPointInfo.rangeOffset, 0));
- }
-
- return SLANG_OK;
- }
-
- Result init(IDevice* device, GraphicsCommonProgramLayout* layout)
- {
- SLANG_RETURN_ON_FAIL(Super::init(device, layout));
-
- for (auto entryPointInfo : layout->getEntryPoints())
- {
- RefPtr<EntryPointVars> entryPoint;
- SLANG_RETURN_ON_FAIL(
- EntryPointVars::create(device, entryPointInfo.layout, entryPoint.writeRef()));
- m_entryPoints.add(entryPoint);
- }
-
- return SLANG_OK;
- }
-
- Result _createSpecializedLayout(GraphicsCommonShaderObjectLayout** outLayout) SLANG_OVERRIDE
- {
- ExtendedShaderObjectTypeList specializationArgs;
- SLANG_RETURN_ON_FAIL(collectSpecializationArgs(specializationArgs));
-
- // Note: There is an important policy decision being made here that we need
- // to approach carefully.
- //
- // We are doing two different things that affect the layout of a program:
- //
- // 1. We are *composing* one or more pieces of code (notably the shared global/module
- // stuff and the per-entry-point stuff).
- //
- // 2. We are *specializing* code that includes generic/existential parameters
- // to concrete types/values.
- //
- // We need to decide the relative *order* of these two steps, because of how it impacts
- // layout. The layout for `specialize(compose(A,B), X, Y)` is potentially different
- // form that of `compose(specialize(A,X), speciealize(B,Y))`, even when both are
- // semantically equivalent programs.
- //
- // Right now we are using the first option: we are first generating a full composition
- // of all the code we plan to use (global scope plus all entry points), and then
- // specializing it to the concatenated specialization argumenst for all of that.
- //
- // In some cases, though, this model isn't appropriate. For example, when dealing with
- // ray-tracing shaders and local root signatures, we really want the parameters of each
- // entry point (actually, each entry-point *group*) to be allocated distinct storage,
- // which really means we want to compute something like:
- //
- // SpecializedGlobals = specialize(compose(ModuleA, ModuleB, ...), X, Y, ...)
- //
- // SpecializedEP1 = compose(SpecializedGlobals, specialize(EntryPoint1, T, U, ...))
- // SpecializedEP2 = compose(SpecializedGlobals, specialize(EntryPoint2, A, B, ...))
- //
- // Note how in this case all entry points agree on the layout for the shared/common
- // parmaeters, but their layouts are also independent of one another.
- //
- // Furthermore, in this example, loading another entry point into the system would not
- // rquire re-computing the layouts (or generated kernel code) for any of the entry points
- // that had already been loaded (in contrast to a compose-then-specialize approach).
- //
- ComPtr<slang::IComponentType> specializedComponentType;
- ComPtr<slang::IBlob> diagnosticBlob;
- auto result = getLayout()->getSlangProgram()->specialize(
- specializationArgs.components.getArrayView().getBuffer(),
- specializationArgs.getCount(),
- specializedComponentType.writeRef(),
- diagnosticBlob.writeRef());
-
- // TODO: print diagnostic message via debug output interface.
-
- if (result != SLANG_OK)
- return result;
-
- auto slangSpecializedLayout = specializedComponentType->getLayout();
- RefPtr<GraphicsCommonProgramLayout> specializedLayout;
- GraphicsCommonProgramLayout::create(getRenderer(), specializedComponentType, slangSpecializedLayout, specializedLayout.writeRef());
-
- // Note: Computing the layout for the specialized program will have also computed
- // the layouts for the entry points, and we really need to attach that information
- // to them so that they don't go and try to compute their own specializations.
- //
- // TODO: Well, if we move to the specialization model described above then maybe
- // we *will* want entry points to do their own specialization work...
- //
- auto entryPointCount = m_entryPoints.getCount();
- for(Index i = 0; i < entryPointCount; ++i)
- {
- auto entryPointInfo = specializedLayout->getEntryPoint(i);
- auto entryPointVars = m_entryPoints[i];
-
- entryPointVars->m_specializedLayout = entryPointInfo.layout;
- }
-
- *outLayout = specializedLayout.detach();
- return SLANG_OK;
- }
-
-
- List<RefPtr<EntryPointVars>> m_entryPoints;
-};
-
-
-Result GraphicsAPIRenderer::createShaderObjectLayout(
- slang::TypeLayoutReflection* typeLayout,
- ShaderObjectLayoutBase** outLayout)
-{
- RefPtr<GraphicsCommonShaderObjectLayout> layout;
- SLANG_RETURN_ON_FAIL(GraphicsCommonShaderObjectLayout::createForElementType(
- this, typeLayout, layout.writeRef()));
- *outLayout = layout.detach();
- return SLANG_OK;
-}
-
-Result GraphicsAPIRenderer::createShaderObject(
- ShaderObjectLayoutBase* layout,
- IShaderObject** outObject)
-{
- RefPtr<GraphicsCommonShaderObject> shaderObject;
- SLANG_RETURN_ON_FAIL(GraphicsCommonShaderObject::create(this,
- reinterpret_cast<GraphicsCommonShaderObjectLayout*>(layout), shaderObject.writeRef()));
- *outObject = shaderObject.detach();
- return SLANG_OK;
-}
-
-Result SLANG_MCALL GraphicsAPIRenderer::createRootShaderObject(
- IShaderProgram* program,
- IShaderObject** outObject)
-{
- auto commonProgram = dynamic_cast<GraphicsCommonShaderProgram*>(program);
-
- RefPtr<ProgramVars> shaderObject;
- SLANG_RETURN_ON_FAIL(ProgramVars::create(this,
- commonProgram->getLayout(),
- shaderObject.writeRef()));
- *outObject = shaderObject.detach();
- return SLANG_OK;
-}
-
-Result GraphicsAPIRenderer::initProgramCommon(
- GraphicsCommonShaderProgram* program,
- IShaderProgram::Desc const& desc)
-{
- auto slangProgram = desc.slangProgram;
- if(!slangProgram)
- return SLANG_OK;
-
- auto slangReflection = slangProgram->getLayout(0);
- if(!slangReflection)
- return SLANG_FAIL;
-
- RefPtr<GraphicsCommonProgramLayout> programLayout;
- SLANG_RETURN_ON_FAIL(GraphicsCommonProgramLayout::create(this, slangProgram, slangReflection, programLayout.writeRef()));
-
- program->slangProgram = slangProgram;
- program->m_layout = programLayout;
-
- return SLANG_OK;
-}
-
-Result GraphicsComputeCommandEncoderBase::bindRootShaderObjectImpl(
- PipelineType pipelineType,
- IShaderObject* object)
-{
- auto programVars = dynamic_cast<ProgramVars*>(object);
- if (!programVars)
- return SLANG_E_INVALID_HANDLE;
-
- RefPtr<PipelineStateBase> specializedPipeline;
- SLANG_RETURN_ON_FAIL(m_rendererBase->maybeSpecializePipeline(m_currentPipeline, programVars, specializedPipeline));
- m_currentPipeline = specializedPipeline;
-
- // Apply shader parameter bindings.
- programVars->apply(m_rendererBase, this, pipelineType);
- return SLANG_OK;
-}
-
-GraphicsCommonProgramLayout* gfx::GraphicsCommonShaderProgram::getLayout() const
-{
- return static_cast<GraphicsCommonProgramLayout*>(m_layout.Ptr());
-}
-
-void GraphicsAPIRenderer::preparePipelineDesc(GraphicsPipelineStateDesc& desc)
-{
- if (!desc.pipelineLayout)
- {
- auto program = dynamic_cast<GraphicsCommonShaderProgram*>(desc.program);
- auto rootLayout = program->getLayout();
- desc.pipelineLayout = rootLayout->getPipelineLayout();
- }
-}
-
-void GraphicsAPIRenderer::preparePipelineDesc(ComputePipelineStateDesc& desc)
-{
- if (!desc.pipelineLayout)
- {
- auto program = dynamic_cast<GraphicsCommonShaderProgram*>(desc.program);
- auto rootLayout = program->getLayout();
- desc.pipelineLayout = rootLayout->getPipelineLayout();
- }
-}
-
-}
diff --git a/tools/gfx/render-graphics-common.h b/tools/gfx/render-graphics-common.h
deleted file mode 100644
index 96ab8e831..000000000
--- a/tools/gfx/render-graphics-common.h
+++ /dev/null
@@ -1,56 +0,0 @@
-#pragma once
-
-#include "tools/gfx/renderer-shared.h"
-#include "core/slang-basic.h"
-#include "tools/gfx/slang-context.h"
-
-namespace gfx
-{
-class GraphicsCommonProgramLayout;
-
-class GraphicsCommonShaderProgram : public ShaderProgramBase
-{
-public:
- GraphicsCommonProgramLayout* getLayout() const;
-private:
- friend class GraphicsAPIRenderer;
- Slang::RefPtr<ShaderObjectLayoutBase> m_layout;
-};
-
-class GraphicsComputeCommandEncoderBase
-{
-public:
- RendererBase* m_rendererBase;
- Slang::RefPtr<PipelineStateBase> m_currentPipeline;
-
- virtual SLANG_NO_THROW void SLANG_MCALL setDescriptorSetImpl(
- PipelineType pipelineType,
- IPipelineLayout* layout,
- UInt index,
- IDescriptorSet* descriptorSet) = 0;
- virtual SLANG_NO_THROW void SLANG_MCALL uploadBufferDataImpl(
- IBufferResource* buffer, size_t offset, size_t size, void* data) = 0;
-
- Result bindRootShaderObjectImpl(PipelineType pipelineType, IShaderObject* object);
-};
-
-class GraphicsAPIRenderer : public RendererBase
-{
-public:
- virtual Result createShaderObjectLayout(
- slang::TypeLayoutReflection* typeLayout,
- ShaderObjectLayoutBase** outLayout) SLANG_OVERRIDE;
- virtual Result createShaderObject(
- ShaderObjectLayoutBase* layout,
- IShaderObject** outObject) SLANG_OVERRIDE;
- virtual SLANG_NO_THROW Result SLANG_MCALL createRootShaderObject(
- IShaderProgram* program,
- IShaderObject** outObject) SLANG_OVERRIDE;
- void preparePipelineDesc(GraphicsPipelineStateDesc& desc);
- void preparePipelineDesc(ComputePipelineStateDesc& desc);
-
- Result initProgramCommon(
- GraphicsCommonShaderProgram* program,
- IShaderProgram::Desc const& desc);
-};
-}
diff --git a/tools/gfx/renderer-shared.cpp b/tools/gfx/renderer-shared.cpp
index a628cc997..2e6105793 100644
--- a/tools/gfx/renderer-shared.cpp
+++ b/tools/gfx/renderer-shared.cpp
@@ -1,5 +1,4 @@
#include "renderer-shared.h"
-#include "render-graphics-common.h"
#include "core/slang-io.h"
#include "core/slang-token-reader.h"
@@ -9,10 +8,7 @@ namespace gfx
{
const Slang::Guid GfxGUID::IID_ISlangUnknown = SLANG_UUID_ISlangUnknown;
-const Slang::Guid GfxGUID::IID_IDescriptorSetLayout = SLANG_UUID_IDescriptorSetLayout;
-const Slang::Guid GfxGUID::IID_IDescriptorSet = SLANG_UUID_IDescriptorSet;
const Slang::Guid GfxGUID::IID_IShaderProgram = SLANG_UUID_IShaderProgram;
-const Slang::Guid GfxGUID::IID_IPipelineLayout = SLANG_UUID_IPipelineLayout;
const Slang::Guid GfxGUID::IID_IInputLayout = SLANG_UUID_IInputLayout;
const Slang::Guid GfxGUID::IID_IPipelineState = SLANG_UUID_IPipelineState;
const Slang::Guid GfxGUID::IID_IResourceView = SLANG_UUID_IResourceView;
@@ -113,49 +109,6 @@ gfx::StageType mapStage(SlangStage stage)
}
}
-Result createProgramFromSlang(IDevice* device, IShaderProgram::Desc const& originalDesc, IShaderProgram** outProgram)
-{
- SlangInt targetIndex = 0;
- auto slangProgram = originalDesc.slangProgram;
-
- auto programLayout = slangProgram->getLayout(targetIndex);
- if(!programLayout)
- return SLANG_FAIL;
-
- Int entryPointCount = (Int) programLayout->getEntryPointCount();
- if(entryPointCount == 0)
- return SLANG_FAIL;
-
- List<IShaderProgram::KernelDesc> kernelDescs;
- List<ComPtr<slang::IBlob>> kernelBlobs;
- for( Int i = 0; i < entryPointCount; ++i )
- {
- ComPtr<slang::IBlob> entryPointCodeBlob;
- SLANG_RETURN_ON_FAIL(slangProgram->getEntryPointCode(i, targetIndex, entryPointCodeBlob.writeRef()));
-
- auto entryPointLayout = programLayout->getEntryPointByIndex(i);
-
- kernelBlobs.add(entryPointCodeBlob);
-
- IShaderProgram::KernelDesc kernelDesc;
- kernelDesc.codeBegin = entryPointCodeBlob->getBufferPointer();
- kernelDesc.codeEnd = (const char*) kernelDesc.codeBegin + entryPointCodeBlob->getBufferSize();
- kernelDesc.entryPointName = entryPointLayout->getName();
- kernelDesc.stage = mapStage(entryPointLayout->getStage());
-
- kernelDescs.add(kernelDesc);
- }
- SLANG_ASSERT(kernelDescs.getCount() == entryPointCount);
-
- IShaderProgram::Desc programDesc;
- programDesc.pipelineType = originalDesc.pipelineType;
- programDesc.slangProgram = slangProgram;
- programDesc.kernelCount = kernelDescs.getCount();
- programDesc.kernels = kernelDescs.getBuffer();
-
- return device->createProgram(programDesc, outProgram);
-}
-
IShaderObject* gfx::ShaderObjectBase::getInterface(const Guid& guid)
{
if (guid == GfxGUID::IID_ISlangUnknown || guid == GfxGUID::IID_IShaderObject)
@@ -243,19 +196,6 @@ void PipelineStateBase::initializeBase(const PipelineStateDesc& inDesc)
auto program = desc.getProgram();
m_program = program;
isSpecializable = (program->slangProgram && program->slangProgram->getSpecializationParamCount() != 0);
-
- switch (desc.type)
- {
- case PipelineType::Graphics:
- m_pipelineLayout = inDesc.graphics.pipelineLayout;
- break;
- case PipelineType::Compute:
- m_pipelineLayout = inDesc.compute.pipelineLayout;
- break;
- default:
- assert(!"unknown pipeline type");
- break;
- }
}
IDevice* gfx::RendererBase::getInterface(const Guid& guid)
diff --git a/tools/gfx/renderer-shared.h b/tools/gfx/renderer-shared.h
index 5ca5f064b..41b9a31c9 100644
--- a/tools/gfx/renderer-shared.h
+++ b/tools/gfx/renderer-shared.h
@@ -99,11 +99,6 @@ protected:
Desc m_desc;
};
-Result createProgramFromSlang(
- IDevice* device,
- IShaderProgram::Desc const& desc,
- IShaderProgram** outProgram);
-
class RendererBase;
typedef uint32_t ShaderComponentID;
@@ -279,8 +274,6 @@ public:
bool isSpecializable = false;
ComPtr<IShaderProgram> m_program;
- ComPtr<IPipelineLayout> m_pipelineLayout;
-
protected:
void initializeBase(const PipelineStateDesc& inDesc);
};
diff --git a/tools/gfx/vulkan/render-vk.cpp b/tools/gfx/vulkan/render-vk.cpp
index 21d437921..6fe72d909 100644
--- a/tools/gfx/vulkan/render-vk.cpp
+++ b/tools/gfx/vulkan/render-vk.cpp
@@ -3,10 +3,10 @@
//WORKING:#include "options.h"
#include "../renderer-shared.h"
-#include "../render-graphics-common.h"
#include "core/slang-basic.h"
#include "core/slang-blob.h"
+#include "core/slang-chunked-list.h"
#include "vk-api.h"
#include "vk-util.h"
@@ -38,14 +38,14 @@
namespace gfx {
using namespace Slang;
-class VKDevice : public GraphicsAPIRenderer
+class VKDevice : public RendererBase
{
public:
enum
{
kMaxRenderTargets = 8,
kMaxAttachments = kMaxRenderTargets + 1,
-
+ kMaxPushConstantSize = 256,
kMaxDescriptorSets = 8,
};
// Renderer implementation
@@ -85,13 +85,13 @@ public:
UInt inputElementCount,
IInputLayout** outLayout) override;
- virtual SLANG_NO_THROW Result SLANG_MCALL createDescriptorSetLayout(
- const IDescriptorSetLayout::Desc& desc,
- IDescriptorSetLayout** outLayout) override;
- virtual SLANG_NO_THROW Result SLANG_MCALL
- createPipelineLayout(const IPipelineLayout::Desc& desc, IPipelineLayout** outLayout) override;
+ virtual Result createShaderObjectLayout(
+ slang::TypeLayoutReflection* typeLayout,
+ ShaderObjectLayoutBase** outLayout) override;
+ virtual Result createShaderObject(ShaderObjectLayoutBase* layout, IShaderObject** outObject)
+ override;
virtual SLANG_NO_THROW Result SLANG_MCALL
- createDescriptorSet(IDescriptorSetLayout* layout, IDescriptorSet::Flag::Enum flag, IDescriptorSet** outDescriptorSet) override;
+ createRootShaderObject(IShaderProgram* program, IShaderObject** outObject) override;
virtual SLANG_NO_THROW Result SLANG_MCALL
createProgram(const IShaderProgram::Desc& desc, IShaderProgram** outProgram) override;
@@ -632,15 +632,728 @@ public:
}
};
- class ShaderProgramImpl: public GraphicsCommonShaderProgram
+ struct BoundVertexBuffer
+ {
+ RefPtr<BufferResourceImpl> m_buffer;
+ int m_stride;
+ int m_offset;
+ };
+
+ class PipelineStateImpl : public PipelineStateBase
+ {
+ public:
+ PipelineStateImpl(const VulkanApi& api):
+ m_api(&api)
+ {
+ }
+ ~PipelineStateImpl()
+ {
+ if (m_pipeline != VK_NULL_HANDLE)
+ {
+ m_api->vkDestroyPipeline(m_api->m_device, m_pipeline, nullptr);
+ }
+ }
+
+ void init(const GraphicsPipelineStateDesc& inDesc)
+ {
+ PipelineStateDesc pipelineDesc;
+ pipelineDesc.type = PipelineType::Graphics;
+ pipelineDesc.graphics = inDesc;
+ initializeBase(pipelineDesc);
+ }
+ void init(const ComputePipelineStateDesc& inDesc)
+ {
+ PipelineStateDesc pipelineDesc;
+ pipelineDesc.type = PipelineType::Compute;
+ pipelineDesc.compute = inDesc;
+ initializeBase(pipelineDesc);
+ }
+
+ const VulkanApi* m_api;
+
+ RefPtr<FramebufferLayoutImpl> m_framebufferLayout;
+
+ VkPipeline m_pipeline = VK_NULL_HANDLE;
+ };
+
+ class ShaderObjectLayoutImpl : public ShaderObjectLayoutBase
{
public:
+ struct BindingRangeInfo
+ {
+ slang::BindingType bindingType;
+ Index count;
+ Index baseIndex;
+ Index descriptorSetIndex;
+ Index rangeIndexInDescriptorSet;
+
+ // Returns true if this binding range consumes a specialization argument slot.
+ bool isSpecializationArg() const
+ {
+ return bindingType == slang::BindingType::ExistentialValue;
+ }
+ };
+
+ struct SubObjectRangeInfo
+ {
+ RefPtr<ShaderObjectLayoutImpl> layout;
+ Index bindingRangeIndex;
+ };
+
+ struct DescriptorSetInfo : public RefObject
+ {
+ List<VkDescriptorSetLayoutBinding> vkBindings;
+ Slang::Int space = -1;
+ };
+
+ struct Builder
+ {
+ public:
+ Builder(VKDevice* renderer)
+ : m_renderer(renderer)
+ {}
+
+ VKDevice* m_renderer;
+ slang::TypeLayoutReflection* m_elementTypeLayout;
+
+ 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;
+ uint32_t m_pushConstantSize = 0;
+ List<DescriptorSetInfo> m_descriptorSetBuildInfos;
+ Dictionary<Index, Index> m_mapSpaceToDescriptorSetIndex;
+
+ Index findOrAddDescriptorSet(Index space)
+ {
+ Index index;
+ if (m_mapSpaceToDescriptorSetIndex.TryGetValue(space, index))
+ return index;
+
+ DescriptorSetInfo info = {};
+ info.space = space;
+
+ index = m_descriptorSetBuildInfos.getCount();
+ m_descriptorSetBuildInfos.add(info);
+
+ m_mapSpaceToDescriptorSetIndex.Add(space, index);
+ return index;
+ }
+
+ static VkDescriptorType _mapDescriptorType(slang::BindingType slangBindingType)
+ {
+ switch (slangBindingType)
+ {
+ case slang::BindingType::PushConstant:
+ default:
+ SLANG_ASSERT("unsupported binding type");
+ return VK_DESCRIPTOR_TYPE_MAX_ENUM;
+
+ case slang::BindingType::Sampler:
+ return VK_DESCRIPTOR_TYPE_SAMPLER;
+ case slang::BindingType::CombinedTextureSampler:
+ return VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
+ case slang::BindingType::Texture:
+ return VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE;
+ case slang::BindingType::MutableTexture:
+ return VK_DESCRIPTOR_TYPE_STORAGE_IMAGE;
+ case slang::BindingType::TypedBuffer:
+ return VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER;
+ case slang::BindingType::MutableTypedBuffer:
+ return VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER;
+ case slang::BindingType::RawBuffer:
+ case slang::BindingType::MutableRawBuffer:
+ return VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
+ case slang::BindingType::InputRenderTarget:
+ return VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT;
+ case slang::BindingType::InlineUniformData:
+ return VK_DESCRIPTOR_TYPE_INLINE_UNIFORM_BLOCK_EXT;
+ case slang::BindingType::RayTracingAccelerationStructure:
+ return VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR;
+ case slang::BindingType::ConstantBuffer:
+ return VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
+ }
+ }
+
+ 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);
+
+ switch (slangBindingType)
+ {
+ case slang::BindingType::ExistentialValue:
+ case slang::BindingType::InlineUniformData:
+ case slang::BindingType::PushConstant:
+ continue;
+ default:
+ break;
+ }
+
+ auto vkDescriptorType = _mapDescriptorType(slangBindingType);
+
+ VkDescriptorSetLayoutBinding vkBindingRangeDesc = {};
+ vkBindingRangeDesc.binding =
+ (uint32_t)typeLayout->getDescriptorSetDescriptorRangeIndexOffset(s, r);
+ vkBindingRangeDesc.descriptorCount =
+ (uint32_t)typeLayout->getDescriptorSetDescriptorRangeDescriptorCount(
+ s, r);
+ vkBindingRangeDesc.descriptorType = vkDescriptorType;
+ vkBindingRangeDesc.stageFlags = VK_SHADER_STAGE_ALL;
+ if (varLayout)
+ {
+ auto category =
+ typeLayout->getDescriptorSetDescriptorRangeCategory(s, r);
+ vkBindingRangeDesc.binding += (uint32_t)varLayout->getOffset(category);
+ }
+ descriptorSetInfo.vkBindings.add(vkBindingRangeDesc);
+ }
+ }
+ }
+
+ 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);
+ uint32_t count = (uint32_t)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;
+ case slang::BindingType::PushConstant:
+ baseIndex = m_pushConstantSize;
+ m_pushConstantSize += count;
+ break;
+ default:
+ baseIndex = m_resourceViewCount;
+ m_resourceViewCount += count;
+ break;
+ }
+
+ BindingRangeInfo bindingRangeInfo;
+ bindingRangeInfo.bindingType = slangBindingType;
+ bindingRangeInfo.count = count;
+ bindingRangeInfo.baseIndex = baseIndex;
+ bindingRangeInfo.descriptorSetIndex = descriptorSetIndex;
+ bindingRangeInfo.rangeIndexInDescriptorSet = rangeIndexInDescriptorSet;
+
+ m_bindingRanges.add(bindingRangeInfo);
+ }
+
+ 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<ShaderObjectLayoutImpl> subObjectLayout;
+ if (slangBindingType != slang::BindingType::ExistentialValue)
+ {
+ ShaderObjectLayoutImpl::createForElementType(
+ m_renderer,
+ slangLeafTypeLayout->getElementTypeLayout(),
+ subObjectLayout.writeRef());
+ }
+
+ SubObjectRangeInfo subObjectRange;
+ subObjectRange.bindingRangeIndex = bindingRangeIndex;
+ subObjectRange.layout = subObjectLayout;
+
+ m_subObjectRanges.add(subObjectRange);
+ }
+ return SLANG_OK;
+ }
+
+ SlangResult build(ShaderObjectLayoutImpl** outLayout)
+ {
+ auto layout = RefPtr<ShaderObjectLayoutImpl>(new ShaderObjectLayoutImpl());
+ SLANG_RETURN_ON_FAIL(layout->_init(this));
+
+ *outLayout = layout.detach();
+ return SLANG_OK;
+ }
+ };
+
+ static Result createForElementType(
+ VKDevice* renderer,
+ slang::TypeLayoutReflection* elementType,
+ ShaderObjectLayoutImpl** outLayout)
+ {
+ Builder builder(renderer);
+ builder.setElementTypeLayout(elementType);
+ return builder.build(outLayout);
+ }
+
+ List<DescriptorSetInfo> const& getDescriptorSets() { return m_descriptorSetInfos; }
+
+ uint32_t getPushConstantSize() { return m_pushConstantSize; }
+
+ 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; }
+
+ RendererBase* getRenderer() { return m_renderer; }
+
+ slang::TypeReflection* getType() { return m_elementTypeLayout->getType(); }
+
+ protected:
+ Result _init(Builder const* builder)
+ {
+ auto renderer = builder->m_renderer;
+
+ initBase(renderer, builder->m_elementTypeLayout);
+
+ m_bindingRanges = builder->m_bindingRanges;
+
+ m_descriptorSetInfos = _Move(builder->m_descriptorSetBuildInfos);
+ m_pushConstantSize = builder->m_pushConstantSize;
+ 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;
+ }
+
+ List<DescriptorSetInfo> m_descriptorSetInfos;
+ List<BindingRangeInfo> m_bindingRanges;
+ Index m_resourceViewCount = 0;
+ Index m_samplerCount = 0;
+ Index m_combinedTextureSamplerCount = 0;
+ Index m_subObjectCount = 0;
+ uint32_t m_pushConstantSize = 0;
+ List<SubObjectRangeInfo> m_subObjectRanges;
+ };
+
+ class EntryPointLayout : public ShaderObjectLayoutImpl
+ {
+ typedef ShaderObjectLayoutImpl Super;
+
+ public:
+ struct VaryingInputInfo
+ {};
+
+ struct VaryingOutputInfo
+ {};
+
+ struct Builder : Super::Builder
+ {
+ Builder(VKDevice* device)
+ : Super::Builder(device)
+ {}
+
+ 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 = VulkanUtil::getShaderStage(entryPointLayout->getStage());
+ _addEntryPointParam(entryPointLayout->getVarLayout());
+ _addEntryPointParam(entryPointLayout->getResultVarLayout());
+ }
+
+ slang::EntryPointLayout* m_slangEntryPointLayout = nullptr;
+
+ VkShaderStageFlags 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; }
+
+ VkShaderStageFlags getStage() const { return m_stage; }
+
+ slang::EntryPointLayout* getSlangLayout() const { return m_slangEntryPointLayout; };
+
+ slang::EntryPointLayout* m_slangEntryPointLayout;
+ VkShaderStageFlags m_stage;
+ List<VaryingInputInfo> m_varyingInputs;
+ List<VaryingOutputInfo> m_varyingOutputs;
+ };
+
+ class RootShaderObjectLayout : public ShaderObjectLayoutImpl
+ {
+ typedef ShaderObjectLayoutImpl Super;
+
+ public:
+ ~RootShaderObjectLayout()
+ {
+ for (auto descSetLayout : m_vkDescriptorSetLayouts)
+ {
+ m_renderer->m_api.vkDestroyDescriptorSetLayout(
+ m_renderer->m_api.m_device, descSetLayout, nullptr);
+ }
+ if (m_pipelineLayout)
+ {
+ m_renderer->m_api.vkDestroyPipelineLayout(
+ m_renderer->m_api.m_device, m_pipelineLayout, nullptr);
+ }
+ }
+
+ struct EntryPointInfo
+ {
+ RefPtr<EntryPointLayout> layout;
+ Index rangeOffset;
+ };
+
+ struct Builder : Super::Builder
+ {
+ Builder(
+ VKDevice* renderer,
+ slang::IComponentType* program,
+ slang::ProgramLayout* programLayout)
+ : Super::Builder(renderer)
+ , m_program(program)
+ , m_programLayout(programLayout)
+ {}
+
+ Result build(RootShaderObjectLayout** outLayout)
+ {
+ RefPtr<RootShaderObjectLayout> layout = new RootShaderObjectLayout();
+ 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].vkBindings.getCount();
+ }
+ else
+ {
+ info.rangeOffset = 0;
+ }
+
+ auto slangEntryPointLayout = entryPointLayout->getSlangLayout();
+ _addDescriptorSets(
+ slangEntryPointLayout->getTypeLayout(), slangEntryPointLayout->getVarLayout());
+ m_entryPoints.add(info);
+ }
+
+ slang::IComponentType* m_program;
+ slang::ProgramLayout* m_programLayout;
+ List<EntryPointInfo> m_entryPoints;
+ };
+
+ Index findEntryPointIndex(VkShaderStageFlags 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; }
+
+ static Result create(
+ VKDevice* renderer,
+ slang::IComponentType* program,
+ slang::ProgramLayout* programLayout,
+ RootShaderObjectLayout** outLayout)
+ {
+ RootShaderObjectLayout::Builder builder(renderer, program, programLayout);
+ builder.addGlobalParams(programLayout->getGlobalParamsVarLayout());
+
+ SlangInt entryPointCount = programLayout->getEntryPointCount();
+ for (SlangInt e = 0; e < entryPointCount; ++e)
+ {
+ auto slangEntryPoint = programLayout->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(outLayout));
+
+ return SLANG_OK;
+ }
+
+ slang::IComponentType* getSlangProgram() const { return m_program; }
+ slang::ProgramLayout* getSlangProgramLayout() const { return m_programLayout; }
+
+ protected:
+ Result _init(Builder const* builder)
+ {
+ auto renderer = builder->m_renderer;
+
+ SLANG_RETURN_ON_FAIL(Super::_init(builder));
+
+ m_program = builder->m_program;
+ m_programLayout = builder->m_programLayout;
+ m_entryPoints = builder->m_entryPoints;
+ m_renderer = renderer;
+
+ if (m_program->getSpecializationParamCount() != 0)
+ return SLANG_OK;
+
+ // For fully specialized shader programs, we create a Vulkan pipeline layout now.
+
+ // First, create `VkDescriptorSetLayout`s for the global scope and all sub-objects
+ // referenced via a `ParameterBlock`.
+ SLANG_RETURN_ON_FAIL(addDescriptorSetLayoutRec(this));
+
+ // Next, collect push constant ranges. We will use one descriptor range for each
+ // entry point that has uniform parameters.
+ uint32_t pushConstantOffset = 0;
+ for (auto& entryPoint : m_entryPoints)
+ {
+ auto size = entryPoint.layout->getPushConstantSize();
+ if (size)
+ {
+ VkPushConstantRange pushConstantRange = {};
+ pushConstantRange.offset = pushConstantOffset;
+ pushConstantRange.size = size;
+ pushConstantRange.stageFlags =
+ VulkanUtil::getShaderStage(entryPoint.layout->getStage());
+ m_pushConstantRanges.add(pushConstantRange);
+ pushConstantOffset += size;
+ }
+ }
+
+ // Now call Vulkan API to create a pipeline layout.
+ VkPipelineLayoutCreateInfo pipelineLayoutCreateInfo = {};
+ pipelineLayoutCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
+ pipelineLayoutCreateInfo.setLayoutCount = (uint32_t)m_vkDescriptorSetLayouts.getCount();
+ pipelineLayoutCreateInfo.pSetLayouts = m_vkDescriptorSetLayouts.getBuffer();
+ if (m_pushConstantRanges.getCount())
+ {
+ pipelineLayoutCreateInfo.pushConstantRangeCount =
+ (uint32_t)m_pushConstantRanges.getCount();
+ pipelineLayoutCreateInfo.pPushConstantRanges =
+ m_pushConstantRanges.getBuffer();
+ }
+ SLANG_RETURN_ON_FAIL(m_renderer->m_api.vkCreatePipelineLayout(
+ m_renderer->m_api.m_device, &pipelineLayoutCreateInfo, nullptr, &m_pipelineLayout));
+ return SLANG_OK;
+ }
+
+ // Recusively create `VkDescriptorSetLayout` for all descriptor sets used by this and children
+ // shader objects and add them to `m_vkDescriptorSetLayouts`.
+ Result addDescriptorSetLayoutRec(ShaderObjectLayoutImpl* layout)
+ {
+ for (auto& descSetInfo : layout->getDescriptorSets())
+ {
+ VkDescriptorSetLayoutCreateInfo createInfo = {};
+ createInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
+ createInfo.pBindings = descSetInfo.vkBindings.getBuffer();
+ createInfo.bindingCount = (uint32_t)descSetInfo.vkBindings.getCount();
+ VkDescriptorSetLayout vkDescSetLayout;
+ SLANG_RETURN_ON_FAIL(m_renderer->m_api.vkCreateDescriptorSetLayout(
+ m_renderer->m_api.m_device, &createInfo, nullptr, &vkDescSetLayout));
+ m_vkDescriptorSetLayouts.add(vkDescSetLayout);
+ }
+
+ // Note: entry point parameters in a `RootShaderObject` has already been included
+ // in `layout->getDescriptorSets()` during `RootShaderObjectLayout` construction,
+ // so we do not need to enumerate entry point array here.
+
+ // However, for sub-objects referenced through `ParameterBlock`s, we do need to
+ // add their descriptor sets to our pipeline layout.
+ // Binding ranges for sub-objects referenced through `ConstantBuffer`s are also
+ // included in this object's layout already, so no need to skip those.
+
+ for (auto& subObject : layout->getSubObjectRanges())
+ {
+ auto bindingRange = layout->getBindingRange(subObject.bindingRangeIndex);
+ if (bindingRange.bindingType == slang::BindingType::ParameterBlock)
+ {
+ SLANG_RETURN_ON_FAIL(addDescriptorSetLayoutRec(subObject.layout));
+ }
+ }
+
+ return SLANG_OK;
+ }
- ShaderProgramImpl(const VulkanApi& api, PipelineType pipelineType):
- m_api(&api),
- m_pipelineType(pipelineType)
+ public:
+ ComPtr<slang::IComponentType> m_program;
+ slang::ProgramLayout* m_programLayout = nullptr;
+ List<EntryPointInfo> m_entryPoints;
+ VkPipelineLayout m_pipelineLayout = VK_NULL_HANDLE;
+ Array<VkDescriptorSetLayout, kMaxDescriptorSets> m_vkDescriptorSetLayouts;
+ Array<VkPushConstantRange, 8> m_pushConstantRanges;
+ RefPtr<VKDevice> m_renderer;
+ };
+
+ class ShaderProgramImpl : public ShaderProgramBase
+ {
+ public:
+ ShaderProgramImpl(const VulkanApi& api, PipelineType pipelineType)
+ : m_api(&api)
+ , m_pipelineType(pipelineType)
{
- for (auto& shaderModule : m_modules) shaderModule = VK_NULL_HANDLE;
+ for (auto& shaderModule : m_modules)
+ shaderModule = VK_NULL_HANDLE;
}
~ShaderProgramImpl()
@@ -658,225 +1371,1311 @@ public:
PipelineType m_pipelineType;
- VkPipelineShaderStageCreateInfo m_compute;
- VkPipelineShaderStageCreateInfo m_vertex;
- VkPipelineShaderStageCreateInfo m_fragment;
-
- List<char> m_buffers[2]; //< To keep storage of code in scope
- VkShaderModule m_modules[2];
+ Array<VkPipelineShaderStageCreateInfo, 8> m_stageCreateInfos;
+ Array<ComPtr<ISlangBlob>, 8> m_codeBlobs; //< To keep storage of code in scope
+ Array<VkShaderModule, 8> m_modules;
+ RefPtr<RootShaderObjectLayout> m_rootObjectLayout;
};
- class DescriptorSetLayoutImpl : public IDescriptorSetLayout, public RefObject
+ class CommandBufferImpl;
+
+ class PipelineCommandEncoder : public RefObject
{
public:
- SLANG_REF_OBJECT_IUNKNOWN_ALL
- IDescriptorSetLayout* getInterface(const Guid& guid)
+ bool m_isOpen = false;
+ CommandBufferImpl* m_commandBuffer;
+ VkCommandBuffer m_vkCommandBuffer;
+ VkCommandBuffer m_vkPreCommandBuffer = VK_NULL_HANDLE;
+ VkPipeline m_boundPipelines[3] = {};
+ VKDevice* m_device = nullptr;
+ RefPtr<PipelineStateImpl> m_currentPipeline;
+
+ static int getBindPointIndex(VkPipelineBindPoint bindPoint)
{
- if (guid == GfxGUID::IID_ISlangUnknown || guid == GfxGUID::IID_IDescriptorSetLayout)
- return static_cast<IDescriptorSetLayout*>(this);
- return nullptr;
+ switch (bindPoint)
+ {
+ case VK_PIPELINE_BIND_POINT_GRAPHICS:
+ return 0;
+ case VK_PIPELINE_BIND_POINT_COMPUTE:
+ return 1;
+ case VK_PIPELINE_BIND_POINT_RAY_TRACING_KHR:
+ return 2;
+ default:
+ assert(!"unknown pipeline type.");
+ return -1;
+ }
+ }
+ VulkanApi* m_api;
+
+ RefPtr<ShaderObjectBase> m_rootShaderObject;
+
+ void init(CommandBufferImpl* commandBuffer);
+
+ void endEncodingImpl()
+ {
+ m_isOpen = false;
+ for (auto& pipeline : m_boundPipelines)
+ pipeline = VK_NULL_HANDLE;
+ }
+
+ static void _uploadBufferData(
+ VkCommandBuffer commandBuffer,
+ BufferResourceImpl* buffer,
+ size_t offset,
+ size_t size,
+ void* data)
+ {
+ auto& api = buffer->m_renderer->m_api;
+
+ assert(buffer->m_uploadBuffer.isInitialized());
+
+ void* mappedData = nullptr;
+ SLANG_VK_CHECK(api.vkMapMemory(
+ api.m_device, buffer->m_uploadBuffer.m_memory, offset, size, 0, &mappedData));
+ memcpy(mappedData, data, size);
+ api.vkUnmapMemory(api.m_device, buffer->m_uploadBuffer.m_memory);
+
+ // Copy from staging buffer to real buffer
+ VkBufferCopy copyInfo = {};
+ copyInfo.size = size;
+ copyInfo.dstOffset = offset;
+ copyInfo.srcOffset = offset;
+ api.vkCmdCopyBuffer(
+ commandBuffer,
+ buffer->m_uploadBuffer.m_buffer,
+ buffer->m_buffer.m_buffer,
+ 1,
+ &copyInfo);
+ }
+
+ void uploadBufferDataImpl(IBufferResource* buffer, size_t offset, size_t size, void* data)
+ {
+ m_vkPreCommandBuffer = m_commandBuffer->getPreCommandBuffer();
+ _uploadBufferData(
+ m_vkPreCommandBuffer, static_cast<BufferResourceImpl*>(buffer), offset, size, data);
+ }
+
+ Result bindRootShaderObjectImpl(PipelineType pipelineType, IShaderObject* object);
+
+ void setPipelineStateImpl(IPipelineState* state)
+ {
+ m_currentPipeline = static_cast<PipelineStateImpl*>(state);
+ }
+
+ void flushBindingState(VkPipelineBindPoint pipelineBindPoint)
+ {
+ auto& api = *m_api;
+ // Get specialized pipeline state and bind it.
+ //
+ RefPtr<PipelineStateBase> newPipeline;
+ m_device->maybeSpecializePipeline(m_currentPipeline, m_rootShaderObject, newPipeline);
+ PipelineStateImpl* newPipelineImpl = static_cast<PipelineStateImpl*>(newPipeline.Ptr());
+ auto pipelineBindPointId = getBindPointIndex(pipelineBindPoint);
+ if (m_boundPipelines[pipelineBindPointId] != newPipelineImpl->m_pipeline)
+ {
+ api.vkCmdBindPipeline(
+ m_vkCommandBuffer, pipelineBindPoint, newPipelineImpl->m_pipeline);
+ m_boundPipelines[pipelineBindPointId] = newPipelineImpl->m_pipeline;
+ }
}
+ };
+
+ union VulkanDescriptorInfo
+ {
+ VkDescriptorBufferInfo bufferInfo;
+ VkDescriptorImageInfo imageInfo;
+ };
+ struct RootBindingState
+ {
+ ShortList<VkWriteDescriptorSet, 32> descriptorSetWrites;
+ ChunkedList<VulkanDescriptorInfo, 32> descriptorInfos;
+ ChunkedList<VkBufferView, 8> bufferViews;
+ Array<uint8_t, kMaxPushConstantSize> pushConstantBuffer;
+ Array<VkDescriptorSet, kMaxDescriptorSets> descriptorSets;
+ };
+ struct BindingOffset
+ {
+ uint32_t uniformOffset;
+ uint32_t pushConstantOffset;
+ uint32_t descriptorSetIndexOffset;
+ uint32_t subObjectDescriptorSetIndexOffset;
+ uint32_t descriptorRangeOffset;
+ };
+
+ class ShaderObjectImpl : public ShaderObjectBase
+ {
public:
- DescriptorSetLayoutImpl(const VulkanApi& api)
- : m_api(&api)
+ static Result create(
+ IDevice* device,
+ ShaderObjectLayoutImpl* layout,
+ ShaderObjectImpl** outShaderObject)
{
+ auto object = RefPtr<ShaderObjectImpl>(new ShaderObjectImpl());
+ SLANG_RETURN_ON_FAIL(object->init(device, layout));
+
+ *outShaderObject = object.detach();
+ return SLANG_OK;
}
- ~DescriptorSetLayoutImpl()
+ RendererBase* getDevice() { return m_layout->getDevice(); }
+
+ SLANG_NO_THROW UInt SLANG_MCALL getEntryPointCount() SLANG_OVERRIDE { return 0; }
+
+ SLANG_NO_THROW Result SLANG_MCALL getEntryPoint(UInt index, IShaderObject** outEntryPoint)
+ SLANG_OVERRIDE
{
- if(m_descriptorSetLayout != VK_NULL_HANDLE)
+ *outEntryPoint = nullptr;
+ return SLANG_OK;
+ }
+
+ ShaderObjectLayoutImpl* getLayout()
+ {
+ return static_cast<ShaderObjectLayoutImpl*>(m_layout.Ptr());
+ }
+
+ SLANG_NO_THROW slang::TypeLayoutReflection* SLANG_MCALL getElementTypeLayout()
+ SLANG_OVERRIDE
+ {
+ return m_layout->getElementTypeLayout();
+ }
+
+ SLANG_NO_THROW Result SLANG_MCALL
+ setData(ShaderOffset const& inOffset, void const* data, size_t inSize) SLANG_OVERRIDE
+ {
+ Index offset = inOffset.uniformOffset;
+ Index size = inSize;
+
+ char* dest = m_ordinaryData.getBuffer();
+ Index availableSize = m_ordinaryData.getCount();
+
+ // TODO: We really should bounds-check access rather than silently ignoring sets
+ // that are too large, but we have several test cases that set more data than
+ // an object actually stores on several targets...
+ //
+ if (offset < 0)
{
- m_api->vkDestroyDescriptorSetLayout(m_api->m_device, m_descriptorSetLayout, nullptr);
+ size += offset;
+ offset = 0;
}
+ if ((offset + size) >= availableSize)
+ {
+ size = availableSize - offset;
+ }
+
+ memcpy(dest + offset, data, size);
+
+ return SLANG_OK;
}
- VulkanApi const* m_api;
- VkDescriptorSetLayout m_descriptorSetLayout = VK_NULL_HANDLE;
+ virtual SLANG_NO_THROW Result SLANG_MCALL
+ setObject(ShaderOffset const& offset, IShaderObject* object) SLANG_OVERRIDE
+ {
+ if (offset.bindingRangeIndex < 0)
+ return SLANG_E_INVALID_ARG;
+ auto layout = getLayout();
+ if (offset.bindingRangeIndex >= layout->getBindingRangeCount())
+ return SLANG_E_INVALID_ARG;
- // Vulkan descriptor sets are the closest in design to what
- // the `Renderer` abstraction exposes as a `DescriptorSet`.
- // The main difference is that a `DescriptorSet` can include
- // root constant ranges, while under Vulkan push constant
- // ranges are part of the `VkPipelineLayout`, but not part
- // of any `VkDescriptorSetLayout`.
- //
- // Information about each descriptor slot range in the
- // original `Desc` will be stored as `RangeInfo` values,
- // which store the relevant information from the `Desc`
- // as well as additional information specific to the
- // Vulkan implementation path.
- //
- struct RangeInfo
+ auto subObject = static_cast<ShaderObjectImpl*>(object);
+
+ auto bindingRangeIndex = offset.bindingRangeIndex;
+ auto& bindingRange = layout->getBindingRange(bindingRangeIndex);
+
+ m_objects[bindingRange.baseIndex + offset.bindingArrayIndex] = subObject;
+
+ // If the range being assigned into represents an interface/existential-type leaf field,
+ // then we need to consider how the `object` being assigned here affects specialization.
+ // We may also need to assign some data from the sub-object into the ordinary data
+ // buffer for the parent object.
+ //
+ if (bindingRange.bindingType == slang::BindingType::ExistentialValue)
+ {
+ // A leaf field of interface type is laid out inside of the parent object
+ // as a tuple of `(RTTI, WitnessTable, Payload)`. The layout of these fields
+ // is a contract between the compiler and any runtime system, so we will
+ // need to rely on details of the binary layout.
+
+ // We start by querying the layout/type of the concrete value that the application
+ // is trying to store into the field, and also the layout/type of the leaf
+ // existential-type field itself.
+ //
+ auto concreteTypeLayout = subObject->getElementTypeLayout();
+ auto concreteType = concreteTypeLayout->getType();
+ //
+ auto existentialTypeLayout =
+ layout->getElementTypeLayout()->getBindingRangeLeafTypeLayout(
+ bindingRangeIndex);
+ auto existentialType = existentialTypeLayout->getType();
+
+ // The first field of the tuple (offset zero) is the run-time type information
+ // (RTTI) ID for the concrete type being stored into the field.
+ //
+ // TODO: We need to be able to gather the RTTI type ID from `object` and then
+ // use `setData(offset, &TypeID, sizeof(TypeID))`.
+
+ // The second field of the tuple (offset 8) is the ID of the "witness" for the
+ // conformance of the concrete type to the interface used by this field.
+ //
+ auto witnessTableOffset = offset;
+ witnessTableOffset.uniformOffset += 8;
+ //
+ // Conformances of a type to an interface are computed and then stored by the
+ // Slang runtime, so we can look up the ID for this particular conformance (which
+ // will create it on demand).
+ //
+ ComPtr<slang::ISession> slangSession;
+ SLANG_RETURN_ON_FAIL(getRenderer()->getSlangSession(slangSession.writeRef()));
+ //
+ // Note: If the type doesn't actually conform to the required interface for
+ // this sub-object range, then this is the point where we will detect that
+ // fact and error out.
+ //
+ uint32_t conformanceID = 0xFFFFFFFF;
+ SLANG_RETURN_ON_FAIL(slangSession->getTypeConformanceWitnessSequentialID(
+ concreteType, existentialType, &conformanceID));
+ //
+ // Once we have the conformance ID, then we can write it into the object
+ // at the required offset.
+ //
+ SLANG_RETURN_ON_FAIL(
+ setData(witnessTableOffset, &conformanceID, sizeof(conformanceID)));
+
+ // The third field of the tuple (offset 16) is the "payload" that is supposed to
+ // hold the data for a value of the given concrete type.
+ //
+ auto payloadOffset = offset;
+ payloadOffset.uniformOffset += 16;
+
+ // There are two cases we need to consider here for how the payload might be used:
+ //
+ // * If the concrete type of the value being bound is one that can "fit" into the
+ // available payload space, then it should be stored in the payload.
+ //
+ // * If the concrete type of the value cannot fit in the payload space, then it
+ // will need to be stored somewhere else.
+ //
+ if (_doesValueFitInExistentialPayload(concreteTypeLayout, existentialTypeLayout))
+ {
+ // If the value can fit in the payload area, then we will go ahead and copy
+ // its bytes into that area.
+ //
+ setData(
+ payloadOffset,
+ subObject->m_ordinaryData.getBuffer(),
+ subObject->m_ordinaryData.getCount());
+ }
+ else
+ {
+ // If the value does *not *fit in the payload area, then there is nothing
+ // we can do at this point (beyond saving a reference to the sub-object, which
+ // was handled above).
+ //
+ // Once all the sub-objects have been set into the parent object, we can
+ // compute a specialized layout for it, and that specialized layout can tell
+ // us where the data for these sub-objects has been laid out.
+ }
+ }
+
+ return SLANG_E_NOT_IMPLEMENTED;
+ }
+
+ virtual SLANG_NO_THROW Result SLANG_MCALL
+ getObject(ShaderOffset const& offset, IShaderObject** outObject) SLANG_OVERRIDE
{
- /// The type of descriptor slot range from the original `Desc`
- DescriptorSlotType type;
+ SLANG_ASSERT(outObject);
+ if (offset.bindingRangeIndex < 0)
+ return SLANG_E_INVALID_ARG;
+ auto layout = getLayout();
+ if (offset.bindingRangeIndex >= layout->getBindingRangeCount())
+ return SLANG_E_INVALID_ARG;
+ auto& bindingRange = layout->getBindingRange(offset.bindingRangeIndex);
+
+ auto object = m_objects[bindingRange.baseIndex + offset.bindingArrayIndex].Ptr();
+ object->addRef();
+ *outObject = object;
+
+ // auto& subObjectRange =
+ // m_layout->getSubObjectRange(bindingRange.subObjectRangeIndex); *outObject =
+ // m_objects[subObjectRange.baseIndex + offset.bindingArrayIndex];
- /// The start index of the range in the appropriate type-specific array
- Index arrayIndex;
+ return SLANG_OK;
- /// The equivalent Vulkan descriptor type, where applicable
- VkDescriptorType vkDescriptorType;
+#if 0
+ SLANG_ASSERT(bindingRange.descriptorSetIndex >= 0);
+ SLANG_ASSERT(bindingRange.descriptorSetIndex < m_descriptorSets.getCount());
+ auto& descriptorSet = m_descriptorSets[bindingRange.descriptorSetIndex];
- /// The Vulkan `binding` index for this range
- uint32_t vkBindingIndex;
- };
- List<RangeInfo> m_ranges;
+ descriptorSet->setConstantBuffer(bindingRange.rangeIndexInDescriptorSet, offset.bindingArrayIndex, buffer);
+ return SLANG_OK;
+#endif
+ }
- // Because root constant ranges aren't part of a `VkDescriptorSetLayout`,
- // we store additional data to represent the ranges so that
- // we can store their data on a `DescriptorSetImpl` and then
- // bind it to the API later.
+ SLANG_NO_THROW Result SLANG_MCALL
+ setResource(ShaderOffset const& offset, IResourceView* resourceView) SLANG_OVERRIDE
+ {
+ if (offset.bindingRangeIndex < 0)
+ return SLANG_E_INVALID_ARG;
+ auto layout = getLayout();
+ if (offset.bindingRangeIndex >= layout->getBindingRangeCount())
+ return SLANG_E_INVALID_ARG;
+ auto& bindingRange = layout->getBindingRange(offset.bindingRangeIndex);
+
+ m_resourceViews[bindingRange.baseIndex + offset.bindingArrayIndex] =
+ static_cast<ResourceViewImpl*>(resourceView);
+ return SLANG_OK;
+ }
+
+ SLANG_NO_THROW Result SLANG_MCALL
+ setSampler(ShaderOffset const& offset, ISamplerState* sampler) SLANG_OVERRIDE
+ {
+ if (offset.bindingRangeIndex < 0)
+ return SLANG_E_INVALID_ARG;
+ auto layout = getLayout();
+ if (offset.bindingRangeIndex >= layout->getBindingRangeCount())
+ return SLANG_E_INVALID_ARG;
+ auto& bindingRange = layout->getBindingRange(offset.bindingRangeIndex);
+
+ m_samplers[bindingRange.baseIndex + offset.bindingArrayIndex] =
+ static_cast<SamplerStateImpl*>(sampler);
+ return SLANG_OK;
+ }
+
+ SLANG_NO_THROW Result SLANG_MCALL setCombinedTextureSampler(
+ ShaderOffset const& offset,
+ IResourceView* textureView,
+ ISamplerState* sampler) SLANG_OVERRIDE
+ {
+ if (offset.bindingRangeIndex < 0)
+ return SLANG_E_INVALID_ARG;
+ auto layout = getLayout();
+ if (offset.bindingRangeIndex >= layout->getBindingRangeCount())
+ return SLANG_E_INVALID_ARG;
+ auto& bindingRange = layout->getBindingRange(offset.bindingRangeIndex);
+
+ auto& slot =
+ m_combinedTextureSamplers[bindingRange.baseIndex + offset.bindingArrayIndex];
+ slot.textureView = static_cast<TextureResourceViewImpl*>(textureView);
+ slot.sampler = static_cast<SamplerStateImpl*>(sampler);
+ return SLANG_OK;
+ }
+
+ public:
+ // Appends all types that are used to specialize the element type of this shader object in
+ // `args` list.
+ virtual Result collectSpecializationArgs(ExtendedShaderObjectTypeList& args) override
+ {
+ auto& subObjectRanges = getLayout()->getSubObjectRanges();
+ // The following logic is built on the assumption that all fields that involve
+ // existential types (and therefore require specialization) will results in a sub-object
+ // range in the type layout. This allows us to simply scan the sub-object ranges to find
+ // out all specialization arguments.
+ Index subObjectRangeCount = subObjectRanges.getCount();
+ for (Index subObjectRangeIndex = 0; subObjectRangeIndex < subObjectRangeCount;
+ subObjectRangeIndex++)
+ {
+ auto const& subObjectRange = subObjectRanges[subObjectRangeIndex];
+ auto const& bindingRange =
+ getLayout()->getBindingRange(subObjectRange.bindingRangeIndex);
+
+ Index count = bindingRange.count;
+ SLANG_ASSERT(count == 1);
+
+ Index subObjectIndexInRange = 0;
+ auto subObject = m_objects[bindingRange.baseIndex + subObjectIndexInRange];
+
+ switch (bindingRange.bindingType)
+ {
+ case slang::BindingType::ExistentialValue:
+ {
+ // A binding type of `ExistentialValue` means the sub-object represents a
+ // interface-typed field. In this case the specialization argument for this
+ // field is the actual specialized type of the bound shader object. If the
+ // shader object's type is an ordinary type without existential fields, then
+ // the type argument will simply be the ordinary type. But if the sub
+ // object's type is itself a specialized type, we need to make sure to use
+ // that type as the specialization argument.
+
+ ExtendedShaderObjectType specializedSubObjType;
+ SLANG_RETURN_ON_FAIL(
+ subObject->getSpecializedShaderObjectType(&specializedSubObjType));
+ args.add(specializedSubObjType);
+ break;
+ }
+ case slang::BindingType::ParameterBlock:
+ case slang::BindingType::ConstantBuffer:
+ // Currently we only handle the case where the field's type is
+ // `ParameterBlock<SomeStruct>` or `ConstantBuffer<SomeStruct>`, where
+ // `SomeStruct` is a struct type (not directly an interface type). In this case,
+ // we just recursively collect the specialization arguments from the bound sub
+ // object.
+ SLANG_RETURN_ON_FAIL(subObject->collectSpecializationArgs(args));
+ // TODO: we need to handle the case where the field is of the form
+ // `ParameterBlock<IFoo>`. We should treat this case the same way as the
+ // `ExistentialValue` case here, but currently we lack a mechanism to
+ // distinguish the two scenarios.
+ break;
+ }
+ // TODO: need to handle another case where specialization happens on resources
+ // fields e.g. `StructuredBuffer<IFoo>`.
+ }
+ return SLANG_OK;
+ }
+
+ protected:
+ friend class RootShaderObjectLayout;
+
+ Result init(IDevice* device, ShaderObjectLayoutImpl* layout)
+ {
+ m_layout = layout;
+
+ // If the layout tells us that there is any uniform data,
+ // then we will allocate a CPU memory buffer to hold that data
+ // while it is being set from the host.
+ //
+ // Once the user is done setting the parameters/fields of this
+ // shader object, we will produce a GPU-memory version of the
+ // uniform data (which includes values from this object and
+ // any existential-type sub-objects).
+ //
+ size_t uniformSize = layout->getElementTypeLayout()->getSize();
+ if (uniformSize)
+ {
+ m_ordinaryData.setCount(uniformSize);
+ memset(m_ordinaryData.getBuffer(), 0, uniformSize);
+ }
+
+#if 0
+ // If the layout tells us there are any descriptor sets to
+ // allocate, then we do so now.
//
- struct RootConstantRangeInfo
+ for(auto descriptorSetInfo : layout->getDescriptorSets())
{
- /// The offset of the range's data in the backing storage.
- Index offset;
+ RefPtr<DescriptorSet> descriptorSet;
+ SLANG_RETURN_ON_FAIL(renderer->createDescriptorSet(descriptorSetInfo->layout, descriptorSet.writeRef()));
+ m_descriptorSets.add(descriptorSet);
+ }
+#endif
- /// The size of the range's data.
- Index size;
- };
- Slang::List<RootConstantRangeInfo> m_rootConstantRanges;
+ m_resourceViews.setCount(layout->getResourceViewCount());
+ m_samplers.setCount(layout->getSamplerCount());
+ m_combinedTextureSamplers.setCount(layout->getCombinedTextureSamplerCount());
- /// The total size, in bytes, or root constant data for this descriptor set.
- uint32_t m_rootConstantDataSize = 0;
+ // 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);
- /// The total number of reference counted objects that can be bound
- /// to descriptor sets described by this layout.
- ///
- Index m_totalBoundObjectCount = 0;
+ for (auto subObjectRangeInfo : layout->getSubObjectRanges())
+ {
+ auto subObjectLayout = subObjectRangeInfo.layout;
- /// Vulkan Descriptor set bindings
- Slang::List<VkDescriptorSetLayoutBinding> m_vkBindings;
+ // In the case where the sub-object range represents an
+ // existential-type leaf field (e.g., an `IBar`), we
+ // cannot pre-allocate the object(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<ShaderObjectImpl> subObject;
+ SLANG_RETURN_ON_FAIL(
+ ShaderObjectImpl::create(device, subObjectLayout, subObject.writeRef()));
+ m_objects[bindingRangeInfo.baseIndex + i] = subObject;
+ }
+ }
- class PipelineLayoutImpl : public IPipelineLayout, public RefObject
- {
- public:
- SLANG_REF_OBJECT_IUNKNOWN_ALL
- IPipelineLayout* getInterface(const Guid& guid)
+ return SLANG_OK;
+ }
+
+ /// Write the uniform/ordinary data of this object into the given `dest` buffer at the given
+ /// `offset`
+ Result _writeOrdinaryData(
+ PipelineCommandEncoder* encoder,
+ IBufferResource* buffer,
+ size_t offset,
+ size_t destSize,
+ ShaderObjectLayoutImpl* specializedLayout)
{
- if (guid == GfxGUID::IID_ISlangUnknown || guid == GfxGUID::IID_IPipelineLayout)
+ auto src = m_ordinaryData.getBuffer();
+ auto srcSize = size_t(m_ordinaryData.getCount());
+
+ SLANG_ASSERT(srcSize <= destSize);
+
+ encoder->uploadBufferDataImpl(buffer, offset, srcSize, src);
+
+ // In the case where this object has any sub-objects of
+ // existential/interface type, we need to recurse on those objects
+ // that need to write their state into an appropriate "pending" allocation.
+ //
+ // Note: Any values that could fit into the "payload" included
+ // in the existential-type field itself will have already been
+ // written as part of `setObject()`. This loop only needs to handle
+ // those sub-objects that do not "fit."
+ //
+ // An implementers looking at this code might wonder if things could be changed
+ // so that *all* writes related to sub-objects for interface-type fields could
+ // be handled in this one location, rather than having some in `setObject()` and
+ // others handled here.
+ //
+ Index subObjectRangeCounter = 0;
+ for (auto const& subObjectRangeInfo : specializedLayout->getSubObjectRanges())
{
- return static_cast<IPipelineLayout*>(this);
+ Index subObjectRangeIndex = subObjectRangeCounter++;
+ auto const& bindingRangeInfo =
+ specializedLayout->getBindingRange(subObjectRangeInfo.bindingRangeIndex);
+
+ // We only need to handle sub-object ranges for interface/existential-type fields,
+ // because fields of constant-buffer or parameter-block type are responsible for
+ // the ordinary/uniform data of their own existential/interface-type sub-objects.
+ //
+ if (bindingRangeInfo.bindingType != slang::BindingType::ExistentialValue)
+ continue;
+
+ // Each sub-object range represents a single "leaf" field, but might be nested
+ // under zero or more outer arrays, such that the number of existential values
+ // in the same range can be one or more.
+ //
+ auto count = bindingRangeInfo.count;
+
+ // We are not concerned with the case where the existential value(s) in the range
+ // git into the payload part of the leaf field.
+ //
+ // In the case where the value didn't fit, the Slang layout strategy would have
+ // considered the requirements of the value as a "pending" allocation, and would
+ // allocate storage for the ordinary/uniform part of that pending allocation inside
+ // of the parent object's type layout.
+ //
+ // Here we assume that the Slang reflection API can provide us with a single byte
+ // offset and stride for the location of the pending data allocation in the
+ // specialized type layout, which will store the values for this sub-object range.
+ //
+ // TODO: The reflection API functions we are assuming here haven't been implemented
+ // yet, so the functions being called here are stubs.
+ //
+ // TODO: It might not be that a single sub-object range can reliably map to a single
+ // contiguous array with a single stride; we need to carefully consider what the
+ // layout logic does for complex cases with multiple layers of nested arrays and
+ // structures.
+ //
+ size_t subObjectRangePendingDataOffset =
+ _getSubObjectRangePendingDataOffset(specializedLayout, subObjectRangeIndex);
+ size_t subObjectRangePendingDataStride =
+ _getSubObjectRangePendingDataStride(specializedLayout, subObjectRangeIndex);
+
+ // If the range doesn't actually need/use the "pending" allocation at all, then
+ // we need to detect that case and skip such ranges.
+ //
+ // TODO: This should probably be handled on a per-object basis by caching a "does it
+ // fit?" bit as part of the information for bound sub-objects, given that we already
+ // compute the "does it fit?" status as part of `setObject()`.
+ //
+ if (subObjectRangePendingDataOffset == 0)
+ continue;
+
+ for (Slang::Index i = 0; i < count; ++i)
+ {
+ auto subObject = m_objects[bindingRangeInfo.baseIndex + i];
+
+ RefPtr<ShaderObjectLayoutImpl> subObjectLayout;
+ SLANG_RETURN_ON_FAIL(
+ subObject->_getSpecializedLayout(subObjectLayout.writeRef()));
+
+ auto subObjectOffset =
+ subObjectRangePendingDataOffset + i * subObjectRangePendingDataStride;
+
+ subObject->_writeOrdinaryData(
+ encoder,
+ buffer,
+ offset + subObjectOffset,
+ destSize - subObjectOffset,
+ subObjectLayout);
+ }
}
- return nullptr;
+
+ return SLANG_OK;
+ }
+
+ // As discussed in `_writeOrdinaryData()`, these methods are just stubs waiting for
+ // the "flat" Slang refelction information to provide access to the relevant data.
+ //
+ size_t _getSubObjectRangePendingDataOffset(
+ ShaderObjectLayoutImpl* specializedLayout,
+ Index subObjectRangeIndex)
+ {
+ return 0;
}
+ size_t _getSubObjectRangePendingDataStride(
+ ShaderObjectLayoutImpl* specializedLayout,
+ Index subObjectRangeIndex)
+ {
+ return 0;
+ }
+
public:
- PipelineLayoutImpl(const VulkanApi& api)
- : m_api(&api)
+ struct CombinedTextureSamplerSlot
+ {
+ RefPtr<TextureResourceViewImpl> textureView;
+ RefPtr<SamplerStateImpl> sampler;
+ };
+
+ static void writeBufferDescriptor(
+ RootBindingState* bindingState,
+ BindingOffset offset,
+ VkDescriptorType descriptorType,
+ BufferResourceImpl* buffer)
{
+ auto descriptorSet = bindingState->descriptorSets[offset.descriptorSetIndexOffset];
+ VkWriteDescriptorSet write = {};
+ write.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
+ write.descriptorCount = 1;
+ write.descriptorType = descriptorType;
+ write.dstArrayElement = 0;
+ write.dstBinding = offset.descriptorRangeOffset;
+ write.dstSet = descriptorSet;
+ auto& bufferInfo = bindingState->descriptorInfos.reserveRange(1)->bufferInfo;
+ write.pBufferInfo = &bufferInfo;
+ bufferInfo.buffer = buffer->m_buffer.m_buffer;
+ bufferInfo.offset = 0;
+ bufferInfo.range = buffer->getDesc()->sizeInBytes;
+ bindingState->descriptorSetWrites.add(write);
}
- ~PipelineLayoutImpl()
+ static void writePlainBufferDescriptor(
+ RootBindingState* bindingState,
+ BindingOffset offset,
+ VkDescriptorType descriptorType,
+ ArrayView<RefPtr<ResourceViewImpl>> resourceViews)
{
- if (m_pipelineLayout != VK_NULL_HANDLE)
+ auto descriptorSet = bindingState->descriptorSets[offset.descriptorSetIndexOffset];
+ VkWriteDescriptorSet write = {};
+ write.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
+ write.descriptorCount = (uint32_t)resourceViews.getCount();
+ write.descriptorType = descriptorType;
+ write.dstArrayElement = 0;
+ write.dstBinding = offset.descriptorRangeOffset;
+ write.dstSet = descriptorSet;
+ auto infos = bindingState->descriptorInfos.reserveRange(write.descriptorCount);
+ write.pBufferInfo = (VkDescriptorBufferInfo*)infos;
+ for (Index i = 0; i < resourceViews.getCount(); i++)
{
- m_api->vkDestroyPipelineLayout(m_api->m_device, m_pipelineLayout, nullptr);
+ auto bufferView = static_cast<PlainBufferResourceViewImpl*>(resourceViews[i].Ptr());
+ infos[i].bufferInfo.buffer = bufferView->m_buffer->m_buffer.m_buffer;
+ infos[i].bufferInfo.offset = 0;
+ infos[i].bufferInfo.range = bufferView->m_buffer->getDesc()->sizeInBytes;
}
+ bindingState->descriptorSetWrites.add(write);
}
- VulkanApi const* m_api;
- VkPipelineLayout m_pipelineLayout = VK_NULL_HANDLE;
- UInt m_descriptorSetCount = 0;
+ static void writeTexelBufferDescriptor(
+ RootBindingState* bindingState,
+ BindingOffset offset,
+ VkDescriptorType descriptorType,
+ ArrayView<RefPtr<ResourceViewImpl>> resourceViews)
+ {
+ auto descriptorSet = bindingState->descriptorSets[offset.descriptorSetIndexOffset];
+ VkWriteDescriptorSet write = {};
+ write.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
+ write.descriptorCount = (uint32_t)resourceViews.getCount();
+ write.descriptorType = descriptorType;
+ write.dstArrayElement = 0;
+ write.dstBinding = offset.descriptorRangeOffset;
+ write.dstSet = descriptorSet;
+ auto views = bindingState->bufferViews.reserveRange(write.descriptorCount);
+ write.pTexelBufferView = views;
+ for (Index i = 0; i < resourceViews.getCount(); i++)
+ {
+ views[i] = static_cast<TexelBufferResourceViewImpl*>(resourceViews[i].Ptr())->m_view;
+ }
+ bindingState->descriptorSetWrites.add(write);
+ }
- /// For each descriptor set, stores the start offset of that set's root constant data in the pipeline layout
- List<uint32_t> m_descriptorSetRootConstantOffsets;
- };
+ static void writeTextureSamplerDescriptor(
+ RootBindingState* bindingState,
+ BindingOffset offset,
+ VkDescriptorType descriptorType,
+ ArrayView<CombinedTextureSamplerSlot> slots)
+ {
+ auto descriptorSet = bindingState->descriptorSets[offset.descriptorSetIndexOffset];
+ VkWriteDescriptorSet write = {};
+ write.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
+ write.descriptorCount = (uint32_t)slots.getCount();
+ write.descriptorType = descriptorType;
+ write.dstArrayElement = 0;
+ write.dstBinding = offset.descriptorRangeOffset;
+ write.dstSet = descriptorSet;
+ auto infos = bindingState->descriptorInfos.reserveRange(write.descriptorCount);
+ write.pImageInfo = (VkDescriptorImageInfo*)infos;
+ for (Index i = 0; i < slots.getCount(); i++)
+ {
+ auto texture = slots[i].textureView;
+ auto sampler = slots[i].sampler;
+ auto& imageInfo = ((VkDescriptorImageInfo*)infos)[i];
+ imageInfo.imageView = texture->m_view;
+ imageInfo.imageLayout = texture->m_layout;
+ imageInfo.sampler = sampler->m_sampler;
+ }
+ bindingState->descriptorSetWrites.add(write);
+ }
- class DescriptorSetImpl : public IDescriptorSet, public RefObject
- {
- public:
- SLANG_REF_OBJECT_IUNKNOWN_ALL
- IDescriptorSet* getInterface(const Guid& guid)
+ static void writeTextureDescriptor(
+ RootBindingState* bindingState,
+ BindingOffset offset,
+ VkDescriptorType descriptorType,
+ ArrayView<RefPtr<ResourceViewImpl>> resourceViews)
{
- if (guid == GfxGUID::IID_ISlangUnknown || guid == GfxGUID::IID_IDescriptorSet)
- return static_cast<IDescriptorSet*>(this);
- return nullptr;
+ auto descriptorSet = bindingState->descriptorSets[offset.descriptorSetIndexOffset];
+ VkWriteDescriptorSet write = {};
+ write.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
+ write.descriptorCount = (uint32_t)resourceViews.getCount();
+ write.descriptorType = descriptorType;
+ write.dstArrayElement = 0;
+ write.dstBinding = offset.descriptorRangeOffset;
+ write.dstSet = descriptorSet;
+ auto infos = bindingState->descriptorInfos.reserveRange(write.descriptorCount);
+ write.pImageInfo = (VkDescriptorImageInfo*)infos;
+ for (Index i = 0; i < resourceViews.getCount(); i++)
+ {
+ auto texture = static_cast<TextureResourceViewImpl*>(resourceViews[i].Ptr());
+ auto& imageInfo = ((VkDescriptorImageInfo*)infos)[i];
+ imageInfo.imageView = texture->m_view;
+ imageInfo.imageLayout = texture->m_layout;
+ imageInfo.sampler = 0;
+ }
+ bindingState->descriptorSetWrites.add(write);
+ }
+
+ static void writeSamplerDescriptor(
+ RootBindingState* bindingState,
+ BindingOffset offset,
+ VkDescriptorType descriptorType,
+ ArrayView<RefPtr<SamplerStateImpl>> samplers)
+ {
+ auto descriptorSet = bindingState->descriptorSets[offset.descriptorSetIndexOffset];
+ VkWriteDescriptorSet write = {};
+ write.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
+ write.descriptorCount = (uint32_t)samplers.getCount();
+ write.descriptorType = descriptorType;
+ write.dstArrayElement = 0;
+ write.dstBinding = offset.descriptorRangeOffset;
+ write.dstSet = descriptorSet;
+ auto infos = bindingState->descriptorInfos.reserveRange(write.descriptorCount);
+ write.pImageInfo = (VkDescriptorImageInfo*)infos;
+ for (Index i = 0; i < samplers.getCount(); i++)
+ {
+ auto& imageInfo = ((VkDescriptorImageInfo*)infos)[i];
+ imageInfo.imageView = 0;
+ imageInfo.imageLayout = VK_IMAGE_LAYOUT_GENERAL;
+ imageInfo.sampler = samplers[i]->m_sampler;
+ }
+ bindingState->descriptorSetWrites.add(write);
+ }
+
+ /// Ensure that the `m_ordinaryDataBuffer` has been created, if it is needed
+ Result _ensureOrdinaryDataBufferCreatedIfNeeded(PipelineCommandEncoder* encoder)
+ {
+ // If we have already created a buffer to hold ordinary data, then we should
+ // simply re-use that buffer rather than re-create it.
+ //
+ // TODO: Simply re-using the buffer without any kind of validation checks
+ // means that we are assuming that users cannot or will not perform any `set`
+ // operations on a shader object once an operation has requested this buffer
+ // be created. We need to enforce that rule if we want to rely on it.
+ //
+ if (m_ordinaryDataBuffer)
+ return SLANG_OK;
+
+ // Computing the size of the ordinary data buffer is *not* just as simple
+ // as using the size of the `m_ordinayData` array that we store. The reason
+ // for the added complexity is that interface-type fields may lead to the
+ // storage being specialized such that it needs extra appended data to
+ // store the concrete values that logically belong in those interface-type
+ // fields but wouldn't fit in the fixed-size allocation we gave them.
+ //
+ // TODO: We need to actually implement that logic by using reflection
+ // data computed for the specialized type of this shader object.
+ // For now we just make the simple assumption described above despite
+ // knowing that it is false.
+ //
+ RefPtr<ShaderObjectLayoutImpl> specializedLayout;
+ SLANG_RETURN_ON_FAIL(_getSpecializedLayout(specializedLayout.writeRef()));
+
+ auto specializedOrdinaryDataSize = specializedLayout->getElementTypeLayout()->getSize();
+ if (specializedOrdinaryDataSize == 0)
+ return SLANG_OK;
+
+ // Once we have computed how large the buffer should be, we can allocate
+ // it using the existing public `IDevice` API.
+ //
+ IDevice* device = getRenderer();
+ IBufferResource::Desc bufferDesc;
+ bufferDesc.init(specializedOrdinaryDataSize);
+ bufferDesc.cpuAccessFlags |= IResource::AccessFlag::Write;
+ SLANG_RETURN_ON_FAIL(device->createBufferResource(
+ IResource::Usage::ConstantBuffer,
+ bufferDesc,
+ nullptr,
+ m_ordinaryDataBuffer.writeRef()));
+
+ // Once the buffer is allocated, we can use `_writeOrdinaryData` to fill it in.
+ //
+ // Note that `_writeOrdinaryData` is potentially recursive in the case
+ // where this object contains interface/existential-type fields, so we
+ // don't need or want to inline it into this call site.
+ //
+ SLANG_RETURN_ON_FAIL(_writeOrdinaryData(
+ encoder, m_ordinaryDataBuffer, 0, specializedOrdinaryDataSize, specializedLayout));
+ return SLANG_OK;
+ }
+
+ /// Bind the buffer for ordinary/uniform data, if needed
+ Result _bindOrdinaryDataBufferIfNeeded(
+ PipelineCommandEncoder* encoder,
+ RootBindingState* bindingState,
+ BindingOffset& offset)
+ {
+ // We are going to need to tweak the base binding range index
+ // used for descriptor-set writes if and only if we actually
+ // bind a buffer for ordinary data.
+ //
+ auto& baseRangeIndex = offset.descriptorRangeOffset;
+
+ // We start by ensuring that the buffer is created, if it is needed.
+ //
+ SLANG_RETURN_ON_FAIL(_ensureOrdinaryDataBufferCreatedIfNeeded(encoder));
+
+ // If we did indeed need/create a buffer, then we must bind it into
+ // the given `descriptorSet` and update the base range index for
+ // subsequent binding operations to account for it.
+ //
+ if (m_ordinaryDataBuffer)
+ {
+ auto bufferImpl = static_cast<BufferResourceImpl*>(m_ordinaryDataBuffer.get());
+ writeBufferDescriptor(
+ bindingState, offset, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, bufferImpl);
+ offset.descriptorRangeOffset++;
+ }
+
+ return SLANG_OK;
}
+
public:
- DescriptorSetImpl(VKDevice* renderer)
- : m_renderer(renderer)
+ Result bindDescriptorRanges(
+ PipelineCommandEncoder* encoder,
+ RootBindingState* bindingState,
+ BindingOffset& offset)
{
+ auto layout = getLayout();
+
+ // Fill in the descriptor sets based on binding ranges
+ //
+ for (auto bindingRangeInfo : layout->getBindingRanges())
+ {
+ auto descriptorSet =
+ bindingState->descriptorSets[bindingRangeInfo.descriptorSetIndex];
+ auto rangeIndex =
+ bindingRangeInfo.rangeIndexInDescriptorSet + offset.descriptorRangeOffset;
+ auto baseIndex = bindingRangeInfo.baseIndex;
+ auto count = (uint32_t)bindingRangeInfo.count;
+ switch (bindingRangeInfo.bindingType)
+ {
+ case slang::BindingType::ConstantBuffer:
+ for (uint32_t i = 0; i < count; ++i)
+ {
+ ShaderObjectImpl* subObject = m_objects[baseIndex + i];
+ subObject->bindObject(encoder, bindingState, offset);
+ }
+ break;
+ case slang::BindingType::ParameterBlock:
+ for (uint32_t i = 0; i < count; ++i)
+ {
+ offset.subObjectDescriptorSetIndexOffset++;
+ ShaderObjectImpl* subObject = m_objects[baseIndex + i];
+ auto newOffset = offset;
+ newOffset.descriptorSetIndexOffset =
+ offset.subObjectDescriptorSetIndexOffset;
+ newOffset.uniformOffset = 0;
+ newOffset.descriptorRangeOffset = 0;
+ subObject->bindObject(encoder, bindingState, newOffset);
+ offset.subObjectDescriptorSetIndexOffset =
+ newOffset.subObjectDescriptorSetIndexOffset;
+ offset.pushConstantOffset = newOffset.pushConstantOffset;
+ }
+ break;
+ case slang::BindingType::Texture:
+ writeTextureDescriptor(
+ bindingState,
+ offset,
+ VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE,
+ m_resourceViews.getArrayView(baseIndex, count));
+ offset.descriptorRangeOffset++;
+ break;
+ case slang::BindingType::MutableTexture:
+ writeTextureDescriptor(
+ bindingState,
+ offset,
+ VK_DESCRIPTOR_TYPE_STORAGE_IMAGE,
+ m_resourceViews.getArrayView(baseIndex, count));
+ offset.descriptorRangeOffset++;
+ break;
+ case slang::BindingType::CombinedTextureSampler:
+ writeTextureSamplerDescriptor(
+ bindingState,
+ offset,
+ VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
+ m_combinedTextureSamplers.getArrayView(baseIndex, count));
+ offset.descriptorRangeOffset++;
+ break;
+
+ case slang::BindingType::Sampler:
+ writeSamplerDescriptor(
+ bindingState,
+ offset,
+ VK_DESCRIPTOR_TYPE_SAMPLER,
+ m_samplers.getArrayView(baseIndex, count));
+ offset.descriptorRangeOffset++;
+ break;
+
+ case slang::BindingType::RawBuffer:
+ case slang::BindingType::MutableRawBuffer:
+ writePlainBufferDescriptor(
+ bindingState,
+ offset,
+ VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
+ m_resourceViews.getArrayView(baseIndex, count));
+ offset.descriptorRangeOffset++;
+ break;
+
+ case slang::BindingType::TypedBuffer:
+ writeTexelBufferDescriptor(
+ bindingState,
+ offset,
+ VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER,
+ m_resourceViews.getArrayView(baseIndex, count));
+ offset.descriptorRangeOffset++;
+ break;
+ case slang::BindingType::MutableTypedBuffer:
+ writeTexelBufferDescriptor(
+ bindingState,
+ offset,
+ VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER,
+ m_resourceViews.getArrayView(baseIndex, count));
+ offset.descriptorRangeOffset++;
+ offset.descriptorRangeOffset++;
+ break;
+
+ case slang::BindingType::VaryingInput:
+ case slang::BindingType::VaryingOutput:
+ 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;
+
+ default:
+ SLANG_ASSERT(!"unsupported binding type");
+ return SLANG_FAIL;
+ break;
+ }
+ }
+ return SLANG_OK;
}
- ~DescriptorSetImpl()
+ virtual Result bindObject(
+ PipelineCommandEncoder* encoder,
+ RootBindingState* bindingState,
+ BindingOffset& offset)
{
- m_renderer->descriptorSetAllocator.free(m_descriptorSet);
+ SLANG_RETURN_ON_FAIL(_bindOrdinaryDataBufferIfNeeded(encoder, bindingState, offset));
+
+ SLANG_RETURN_ON_FAIL(bindDescriptorRanges(encoder, bindingState, offset));
+ return SLANG_OK;
}
- virtual SLANG_NO_THROW void SLANG_MCALL setConstantBuffer(UInt range, UInt index, IBufferResource* buffer) override;
- virtual SLANG_NO_THROW void SLANG_MCALL
- setResource(UInt range, UInt index, IResourceView* view) override;
- virtual SLANG_NO_THROW void SLANG_MCALL
- setSampler(UInt range, UInt index, ISamplerState* sampler) override;
- virtual SLANG_NO_THROW void SLANG_MCALL setCombinedTextureSampler(
- UInt range,
- UInt index,
- IResourceView* textureView,
- ISamplerState* sampler) override;
- virtual SLANG_NO_THROW void SLANG_MCALL
- setRootConstants(
- UInt range,
- UInt offset,
- UInt size,
- void const* data) override;
+ /// Any "ordinary" / uniform data for this object
+ List<char> m_ordinaryData;
- VKDevice* m_renderer = nullptr; ///< Weak pointer, can't be strong, because if set will become circular reference
- RefPtr<DescriptorSetLayoutImpl> m_layout;
- VulkanDescriptorSet m_descriptorSet = {};
+ List<RefPtr<ResourceViewImpl>> m_resourceViews;
- /// Records entities that are bound to this descriptor set, and keeps the associated resources/views/state in scope
- List<RefPtr<RefObject>> m_boundObjects;
+ List<RefPtr<SamplerStateImpl>> m_samplers;
- /// Backing storage for root constant ranges belonging to this descriptor set
- List<char> m_rootConstantData;
+ List<CombinedTextureSamplerSlot> m_combinedTextureSamplers;
- bool m_isTransient = false;
+ List<RefPtr<ShaderObjectImpl>> m_objects;
+
+ /// A constant buffer used to stored ordinary data for this object
+ /// and existential-type sub-objects.
+ ///
+ /// Created on demand with `_createOrdinaryDataBufferIfNeeded()`
+ ComPtr<IBufferResource> m_ordinaryDataBuffer;
+
+ /// Get the layout of this shader object with specialization arguments considered
+ ///
+ /// This operation should only be called after the shader object has been
+ /// fully filled in and finalized.
+ ///
+ Result _getSpecializedLayout(ShaderObjectLayoutImpl** outLayout)
+ {
+ if (!m_specializedLayout)
+ {
+ SLANG_RETURN_ON_FAIL(_createSpecializedLayout(m_specializedLayout.writeRef()));
+ }
+ *outLayout = RefPtr<ShaderObjectLayoutImpl>(m_specializedLayout).detach();
+ return SLANG_OK;
+ }
+
+ /// Create the layout for this shader object with specialization arguments considered
+ ///
+ /// This operation is virtual so that it can be customized by `ProgramVars`.
+ ///
+ virtual Result _createSpecializedLayout(ShaderObjectLayoutImpl** outLayout)
+ {
+ ExtendedShaderObjectType extendedType;
+ SLANG_RETURN_ON_FAIL(getSpecializedShaderObjectType(&extendedType));
+
+ auto renderer = getRenderer();
+ RefPtr<ShaderObjectLayoutBase> layout;
+ SLANG_RETURN_ON_FAIL(
+ renderer->getShaderObjectLayout(extendedType.slangType, layout.writeRef()));
+
+ *outLayout = static_cast<ShaderObjectLayoutImpl*>(layout.detach());
+ return SLANG_OK;
+ }
+
+ RefPtr<ShaderObjectLayoutImpl> m_specializedLayout;
};
- struct BoundVertexBuffer
+ class EntryPointShaderObject : public ShaderObjectImpl
{
- RefPtr<BufferResourceImpl> m_buffer;
- int m_stride;
- int m_offset;
+ typedef ShaderObjectImpl Super;
+
+ public:
+ static Result create(
+ IDevice* device,
+ EntryPointLayout* layout,
+ EntryPointShaderObject** outShaderObject)
+ {
+ RefPtr<EntryPointShaderObject> object = new EntryPointShaderObject();
+ SLANG_RETURN_ON_FAIL(object->init(device, layout));
+
+ *outShaderObject = object.detach();
+ return SLANG_OK;
+ }
+
+ EntryPointLayout* getLayout() { return static_cast<EntryPointLayout*>(m_layout.Ptr()); }
+
+ virtual Result bindObject(
+ PipelineCommandEncoder* encoder,
+ RootBindingState* bindingState,
+ BindingOffset& offset) override
+ {
+ // Copy entry point uniform data into global push constant buffer.
+ if (m_ordinaryData.getCount())
+ {
+ Index dstOffset = bindingState->pushConstantBuffer.getCount();
+ ::memcpy(
+ bindingState->pushConstantBuffer.getBuffer() + dstOffset,
+ m_ordinaryData.getBuffer(),
+ m_ordinaryData.getCount());
+ bindingState->pushConstantBuffer.setCount(dstOffset + m_ordinaryData.getCount());
+ }
+
+ SLANG_RETURN_ON_FAIL(bindDescriptorRanges(encoder, bindingState, offset));
+ return SLANG_OK;
+ }
+
+ protected:
+ Result init(IDevice* device, EntryPointLayout* layout)
+ {
+ SLANG_RETURN_ON_FAIL(Super::init(device, layout));
+ return SLANG_OK;
+ }
};
- class PipelineStateImpl : public PipelineStateBase
+ class RootShaderObjectImpl : public ShaderObjectImpl
{
+ typedef ShaderObjectImpl Super;
+
public:
- PipelineStateImpl(const VulkanApi& api):
- m_api(&api)
+ static Result create(
+ IDevice* device,
+ RootShaderObjectLayout* layout,
+ RootShaderObjectImpl** outShaderObject)
{
+ RefPtr<RootShaderObjectImpl> object = new RootShaderObjectImpl();
+ SLANG_RETURN_ON_FAIL(object->init(device, layout));
+
+ *outShaderObject = object.detach();
+ return SLANG_OK;
}
- ~PipelineStateImpl()
+
+ RootShaderObjectLayout* getLayout()
{
- if (m_pipeline != VK_NULL_HANDLE)
+ return static_cast<RootShaderObjectLayout*>(m_layout.Ptr());
+ }
+
+ RootShaderObjectLayout* getSpecializedLayout()
+ {
+ RefPtr<ShaderObjectLayoutImpl> specializedLayout;
+ _getSpecializedLayout(specializedLayout.writeRef());
+ return static_cast<RootShaderObjectLayout*>(m_specializedLayout.Ptr());
+ }
+
+ List<RefPtr<EntryPointShaderObject>> const& getEntryPoints() const { return m_entryPoints; }
+
+ UInt SLANG_MCALL getEntryPointCount() SLANG_OVERRIDE
+ {
+ return (UInt)m_entryPoints.getCount();
+ }
+ SlangResult SLANG_MCALL getEntryPoint(UInt index, IShaderObject** outEntryPoint)
+ SLANG_OVERRIDE
+ {
+ *outEntryPoint = m_entryPoints[index];
+ m_entryPoints[index]->addRef();
+ return SLANG_OK;
+ }
+
+ virtual Result bindObject(
+ PipelineCommandEncoder* encoder,
+ RootBindingState* bindingState,
+ BindingOffset& offset) override
+ {
+ SLANG_RETURN_ON_FAIL(Super::bindObject(encoder, bindingState, offset));
+
+ // Bind all entry points.
+ for (auto& entryPoint : m_entryPoints)
{
- m_api->vkDestroyPipeline(m_api->m_device, m_pipeline, nullptr);
+ entryPoint->bindObject(encoder, bindingState, offset);
}
+ return SLANG_OK;
}
- void init(const GraphicsPipelineStateDesc& inDesc)
+ virtual Result collectSpecializationArgs(ExtendedShaderObjectTypeList& args) override
{
- PipelineStateDesc pipelineDesc;
- pipelineDesc.type = PipelineType::Graphics;
- pipelineDesc.graphics = inDesc;
- initializeBase(pipelineDesc);
+ SLANG_RETURN_ON_FAIL(ShaderObjectImpl::collectSpecializationArgs(args));
+ for (auto& entryPoint : m_entryPoints)
+ {
+ SLANG_RETURN_ON_FAIL(entryPoint->collectSpecializationArgs(args));
+ }
+ return SLANG_OK;
}
- void init(const ComputePipelineStateDesc& inDesc)
+
+ protected:
+ Result init(IDevice* device, RootShaderObjectLayout* layout)
{
- PipelineStateDesc pipelineDesc;
- pipelineDesc.type = PipelineType::Compute;
- pipelineDesc.compute = inDesc;
- initializeBase(pipelineDesc);
+ SLANG_RETURN_ON_FAIL(Super::init(device, layout));
+
+ for (auto entryPointInfo : layout->getEntryPoints())
+ {
+ RefPtr<EntryPointShaderObject> entryPoint;
+ SLANG_RETURN_ON_FAIL(EntryPointShaderObject::create(
+ device, entryPointInfo.layout, entryPoint.writeRef()));
+ m_entryPoints.add(entryPoint);
+ }
+
+ return SLANG_OK;
}
- const VulkanApi* m_api;
+ Result _createSpecializedLayout(ShaderObjectLayoutImpl** outLayout) SLANG_OVERRIDE
+ {
+ ExtendedShaderObjectTypeList specializationArgs;
+ SLANG_RETURN_ON_FAIL(collectSpecializationArgs(specializationArgs));
- RefPtr<FramebufferLayoutImpl> m_framebufferLayout;
+ // Note: There is an important policy decision being made here that we need
+ // to approach carefully.
+ //
+ // We are doing two different things that affect the layout of a program:
+ //
+ // 1. We are *composing* one or more pieces of code (notably the shared global/module
+ // stuff and the per-entry-point stuff).
+ //
+ // 2. We are *specializing* code that includes generic/existential parameters
+ // to concrete types/values.
+ //
+ // We need to decide the relative *order* of these two steps, because of how it impacts
+ // layout. The layout for `specialize(compose(A,B), X, Y)` is potentially different
+ // form that of `compose(specialize(A,X), speciealize(B,Y))`, even when both are
+ // semantically equivalent programs.
+ //
+ // Right now we are using the first option: we are first generating a full composition
+ // of all the code we plan to use (global scope plus all entry points), and then
+ // specializing it to the concatenated specialization argumenst for all of that.
+ //
+ // In some cases, though, this model isn't appropriate. For example, when dealing with
+ // ray-tracing shaders and local root signatures, we really want the parameters of each
+ // entry point (actually, each entry-point *group*) to be allocated distinct storage,
+ // which really means we want to compute something like:
+ //
+ // SpecializedGlobals = specialize(compose(ModuleA, ModuleB, ...), X, Y, ...)
+ //
+ // SpecializedEP1 = compose(SpecializedGlobals, specialize(EntryPoint1, T, U, ...))
+ // SpecializedEP2 = compose(SpecializedGlobals, specialize(EntryPoint2, A, B, ...))
+ //
+ // Note how in this case all entry points agree on the layout for the shared/common
+ // parmaeters, but their layouts are also independent of one another.
+ //
+ // Furthermore, in this example, loading another entry point into the system would not
+ // rquire re-computing the layouts (or generated kernel code) for any of the entry
+ // points that had already been loaded (in contrast to a compose-then-specialize
+ // approach).
+ //
+ ComPtr<slang::IComponentType> specializedComponentType;
+ ComPtr<slang::IBlob> diagnosticBlob;
+ auto result = getLayout()->getSlangProgram()->specialize(
+ specializationArgs.components.getArrayView().getBuffer(),
+ specializationArgs.getCount(),
+ specializedComponentType.writeRef(),
+ diagnosticBlob.writeRef());
+
+ // TODO: print diagnostic message via debug output interface.
+
+ if (result != SLANG_OK)
+ return result;
+
+ auto slangSpecializedLayout = specializedComponentType->getLayout();
+ RefPtr<RootShaderObjectLayout> specializedLayout;
+ RootShaderObjectLayout::create(
+ static_cast<VKDevice*>(getRenderer()),
+ specializedComponentType,
+ slangSpecializedLayout,
+ specializedLayout.writeRef());
+
+ // Note: Computing the layout for the specialized program will have also computed
+ // the layouts for the entry points, and we really need to attach that information
+ // to them so that they don't go and try to compute their own specializations.
+ //
+ // TODO: Well, if we move to the specialization model described above then maybe
+ // we *will* want entry points to do their own specialization work...
+ //
+ auto entryPointCount = m_entryPoints.getCount();
+ for (Index i = 0; i < entryPointCount; ++i)
+ {
+ auto entryPointInfo = specializedLayout->getEntryPoint(i);
+ auto entryPointVars = m_entryPoints[i];
- VkPipeline m_pipeline = VK_NULL_HANDLE;
+ entryPointVars->m_specializedLayout = entryPointInfo.layout;
+ }
+
+ *outLayout = specializedLayout.detach();
+ return SLANG_OK;
+ }
+
+ List<RefPtr<EntryPointShaderObject>> m_entryPoints;
};
class CommandBufferImpl
@@ -955,246 +2754,6 @@ public:
}
public:
- static void _uploadBufferData(
- VkCommandBuffer commandBuffer,
- BufferResourceImpl* buffer,
- size_t offset,
- size_t size,
- void* data)
- {
- auto& api = buffer->m_renderer->m_api;
-
- assert(buffer->m_uploadBuffer.isInitialized());
-
- void* mappedData = nullptr;
- SLANG_VK_CHECK(api.vkMapMemory(
- api.m_device, buffer->m_uploadBuffer.m_memory, offset, size, 0, &mappedData));
- memcpy(mappedData, data, size);
- api.vkUnmapMemory(api.m_device, buffer->m_uploadBuffer.m_memory);
-
- // Copy from staging buffer to real buffer
- VkBufferCopy copyInfo = {};
- copyInfo.size = size;
- copyInfo.dstOffset = offset;
- copyInfo.srcOffset = offset;
- api.vkCmdCopyBuffer(
- commandBuffer,
- buffer->m_uploadBuffer.m_buffer,
- buffer->m_buffer.m_buffer,
- 1,
- &copyInfo);
- }
-
- class PipelineCommandEncoder
- : public GraphicsComputeCommandEncoderBase
- , public RefObject
- {
- public:
- bool m_isOpen = false;
- CommandBufferImpl* m_commandBuffer;
- VkCommandBuffer m_vkCommandBuffer;
- VkCommandBuffer m_vkPreCommandBuffer = VK_NULL_HANDLE;
- VkPipeline m_boundPipelines[3] = {};
- static int getBindPointIndex(VkPipelineBindPoint bindPoint)
- {
- switch (bindPoint)
- {
- case VK_PIPELINE_BIND_POINT_GRAPHICS:
- return 0;
- case VK_PIPELINE_BIND_POINT_COMPUTE:
- return 1;
- case VK_PIPELINE_BIND_POINT_RAY_TRACING_KHR:
- return 2;
- default:
- assert(!"unknown pipeline type.");
- return -1;
- }
- }
- VulkanApi* m_api;
-
- RefPtr<PipelineLayoutImpl> m_currentPipelineLayout;
-
- RefPtr<DescriptorSetImpl> m_currentDescriptorSetImpls[kMaxDescriptorSets];
- VkDescriptorSet m_currentDescriptorSets[kMaxDescriptorSets];
-
- // Temporary list used by flushBindingState to avoid per-frame allocation.
- List<VkCopyDescriptorSet> m_descSetCopies;
-
- void init(CommandBufferImpl* commandBuffer)
- {
- m_commandBuffer = commandBuffer;
- m_rendererBase = static_cast<RendererBase*>(commandBuffer->m_renderer);
- m_vkCommandBuffer = m_commandBuffer->m_commandBuffer;
- m_api = &m_commandBuffer->m_renderer->m_api;
- }
-
- void endEncodingImpl()
- {
- m_isOpen = false;
-
- // Make m_currentDescriptorSets consistent with m_currentDescriptorSetImpls
- // so that we don't mistakenly treat any transient descriptor sets as "copied"
- // later.
- for (uint32_t i = 0; i < kMaxDescriptorSets; i++)
- {
- if (m_currentDescriptorSetImpls[i])
- {
- m_currentDescriptorSets[i] =
- m_currentDescriptorSetImpls[i]->m_descriptorSet.handle;
- }
- }
- for (auto& pipeline : m_boundPipelines)
- pipeline = VK_NULL_HANDLE;
- }
-
- virtual SLANG_NO_THROW void SLANG_MCALL setDescriptorSetImpl(
- PipelineType pipelineType,
- IPipelineLayout* layout,
- UInt index,
- IDescriptorSet* descriptorSet) override
- {
- // Ideally this should eventually be as simple as:
- //
- // m_api.vkCmdBindDescriptorSets(
- // commandBuffer,
- // translatePipelineBindPoint(pipelineType),
- // layout->m_pipelineLayout,
- // index,
- // 1,
- // ((DescriptorSetImpl*) descriptorSet)->m_descriptorSet,
- // 0,
- // nullptr);
- //
- // For now we are lazily flushing state right before drawing, so
- // we will hang onto the parameters that were passed in and then
- // use them later.
- //
-
- auto descriptorSetImpl = (DescriptorSetImpl*)descriptorSet;
- m_currentDescriptorSetImpls[index] = descriptorSetImpl;
- m_currentDescriptorSets[index] = descriptorSetImpl->m_descriptorSet.handle;
- }
-
- virtual SLANG_NO_THROW void SLANG_MCALL uploadBufferDataImpl(
- IBufferResource* buffer,
- size_t offset,
- size_t size,
- void* data) override
- {
- m_vkPreCommandBuffer = m_commandBuffer->getPreCommandBuffer();
- _uploadBufferData(
- m_vkPreCommandBuffer,
- static_cast<BufferResourceImpl*>(buffer),
- offset,
- size,
- data);
- }
-
- void setPipelineStateImpl(IPipelineState* state)
- {
- m_currentPipeline = static_cast<PipelineStateImpl*>(state);
- }
-
- void flushBindingState(VkPipelineBindPoint pipelineBindPoint)
- {
- auto& api = *m_api;
-
- auto pipeline = static_cast<PipelineStateImpl*>(m_currentPipeline.Ptr());
- auto& descSetCopies = m_descSetCopies;
- descSetCopies.clear();
- // We start by binding the pipeline state.
- //
- auto pipelineBindPointId = getBindPointIndex(pipelineBindPoint);
- if (m_boundPipelines[pipelineBindPointId] != pipeline->m_pipeline)
- {
- api.vkCmdBindPipeline(m_vkCommandBuffer, pipelineBindPoint, pipeline->m_pipeline);
- m_boundPipelines[pipelineBindPointId] = pipeline->m_pipeline;
- }
-
- // Next we bind all the descriptor sets that were set in the `VKDevice`.
- //
- auto pipelineLayoutImpl = static_cast<PipelineLayoutImpl*>(pipeline->m_pipelineLayout.get());
- auto vkPipelineLayout = pipelineLayoutImpl->m_pipelineLayout;
- auto descriptorSetCount = pipelineLayoutImpl->m_descriptorSetCount;
- for (uint32_t i = 0; i < (uint32_t)descriptorSetCount; i++)
- {
- if (m_currentDescriptorSetImpls[i]->m_isTransient)
- {
- // A transient descriptor set may go out of life cycle after command list
- // recording, therefore we must make a copy of it in the per-frame
- // descriptor pool.
-
- // If we have already created a transient copy for this descriptor set, skip
- // the copy.
- if (m_currentDescriptorSetImpls[i]->m_descriptorSet.handle !=
- m_currentDescriptorSets[i])
- continue;
-
- auto descSet = m_commandBuffer->m_transientDescSetAllocator->allocate(
- m_currentDescriptorSetImpls[i]->m_layout->m_descriptorSetLayout);
- uint32_t bindingIndex = 0;
- for (auto binding : m_currentDescriptorSetImpls[i]->m_layout->m_vkBindings)
- {
- VkCopyDescriptorSet copy = {};
- copy.sType = VK_STRUCTURE_TYPE_COPY_DESCRIPTOR_SET;
- copy.srcSet = m_currentDescriptorSetImpls[i]->m_descriptorSet.handle;
- copy.dstSet = descSet.handle;
- copy.srcBinding = copy.dstBinding = bindingIndex;
- copy.srcArrayElement = copy.dstArrayElement = 0;
- copy.descriptorCount = binding.descriptorCount;
- descSetCopies.add(copy);
- bindingIndex++;
- }
- m_currentDescriptorSets[i] = descSet.handle;
- }
- }
- if (descSetCopies.getCount())
- {
- api.vkUpdateDescriptorSets(
- api.m_device,
- 0,
- nullptr,
- (uint32_t)descSetCopies.getCount(),
- descSetCopies.getBuffer());
- }
- api.vkCmdBindDescriptorSets(
- m_vkCommandBuffer,
- pipelineBindPoint,
- vkPipelineLayout,
- 0,
- uint32_t(descriptorSetCount),
- &m_currentDescriptorSets[0],
- 0,
- nullptr);
-
- // For any descriptor sets with root-constant ranges, we need to
- // bind the relevant data to the context.
- //
- for (gfx::UInt ii = 0; ii < descriptorSetCount; ++ii)
- {
- auto descriptorSet = m_currentDescriptorSetImpls[ii];
- auto descriptorSetLayout = descriptorSet->m_layout;
- auto size = descriptorSetLayout->m_rootConstantDataSize;
- if (size == 0)
- continue;
- auto data = descriptorSet->m_rootConstantData.getBuffer();
-
- // The absolute offset of the descriptor set's data in
- // the push-constant data for the entire pipeline was
- // computed and cached in the pipeline layout.
- //
- uint32_t offset = pipelineLayoutImpl->m_descriptorSetRootConstantOffsets[ii];
-
- api.vkCmdPushConstants(
- m_vkCommandBuffer,
- vkPipelineLayout,
- VK_SHADER_STAGE_ALL,
- offset,
- size,
- data);
- }
- }
- };
class RenderCommandEncoder
: public IRenderCommandEncoder
, public PipelineCommandEncoder
@@ -1253,14 +2812,6 @@ public:
setPipelineStateImpl(pipelineState);
}
- virtual SLANG_NO_THROW void SLANG_MCALL setDescriptorSet(
- IPipelineLayout* layout,
- UInt index,
- IDescriptorSet* descriptorSet) override
- {
- setDescriptorSetImpl(PipelineType::Graphics, layout, index, descriptorSet);
- }
-
virtual SLANG_NO_THROW void SLANG_MCALL
bindRootShaderObject(IShaderObject* object) override
{
@@ -1488,14 +3039,6 @@ public:
setPipelineStateImpl(pipelineState);
}
- virtual SLANG_NO_THROW void SLANG_MCALL setDescriptorSet(
- IPipelineLayout* layout,
- UInt index,
- IDescriptorSet* descriptorSet) override
- {
- setDescriptorSetImpl(PipelineType::Compute, layout, index, descriptorSet);
- }
-
virtual SLANG_NO_THROW void SLANG_MCALL
bindRootShaderObject(IShaderObject* object) override
{
@@ -1568,7 +3111,7 @@ public:
virtual SLANG_NO_THROW void SLANG_MCALL
uploadBufferData(IBufferResource* buffer, size_t offset, size_t size, void* data) override
{
- _uploadBufferData(
+ PipelineCommandEncoder::_uploadBufferData(
m_commandBuffer->m_commandBuffer,
static_cast<BufferResourceImpl*>(buffer),
offset,
@@ -2161,9 +3704,8 @@ public:
/// Note that the outShaderModule value should be cleaned up when no longer needed by caller
/// via vkShaderModuleDestroy()
VkPipelineShaderStageCreateInfo compileEntryPoint(
- IShaderProgram::KernelDesc const& kernelDesc,
+ ISlangBlob* code,
VkShaderStageFlagBits stage,
- List<char>& outBuffer,
VkShaderModule& outShaderModule);
static VKAPI_ATTR VkBool32 VKAPI_CALL debugMessageCallback(VkDebugReportFlagsEXT flags, VkDebugReportObjectTypeEXT objType, uint64_t srcObject,
@@ -2194,6 +3736,73 @@ public:
uint32_t m_queueAllocCount;
};
+void VKDevice::PipelineCommandEncoder::init(CommandBufferImpl* commandBuffer)
+{
+ m_commandBuffer = commandBuffer;
+ m_device = commandBuffer->m_renderer;
+ m_vkCommandBuffer = m_commandBuffer->m_commandBuffer;
+ m_api = &m_commandBuffer->m_renderer->m_api;
+}
+
+Result VKDevice::PipelineCommandEncoder::bindRootShaderObjectImpl(
+ PipelineType pipelineType,
+ IShaderObject* object)
+{
+ // Obtain specialized root layout.
+ auto rootObjectImpl = static_cast<RootShaderObjectImpl*>(object);
+ m_rootShaderObject = rootObjectImpl;
+
+ auto specializedLayout = rootObjectImpl->getSpecializedLayout();
+ if (!specializedLayout)
+ return SLANG_FAIL;
+
+ RootBindingState bindState = {};
+ // Create transient descriptor tables for binding.
+ for (auto descSetLayout : specializedLayout->m_vkDescriptorSetLayouts)
+ {
+ auto descSet = m_commandBuffer->m_transientDescSetAllocator->allocate(descSetLayout);
+ bindState.descriptorSets.add(descSet.handle);
+ }
+
+ // Write bindings into descriptor sets. This step collects
+ // all `VkWriteDescriptorSet` operations in `bindState.descriptorSetWrites`.
+ BindingOffset offset = {};
+ rootObjectImpl->bindObject(this, &bindState, offset);
+
+ // Execute descriptor writes collected in `bindState.descriptorSetWrites`.
+ m_device->m_api.vkUpdateDescriptorSets(
+ m_device->m_device,
+ (uint32_t)bindState.descriptorSetWrites.getCount(),
+ bindState.descriptorSetWrites.getArrayView().arrayView.getBuffer(),
+ 0,
+ nullptr);
+
+ // Bind descriptor sets.
+ m_device->m_api.vkCmdBindDescriptorSets(
+ m_commandBuffer->m_commandBuffer,
+ VulkanUtil::getPipelineBindPoint(pipelineType),
+ specializedLayout->m_pipelineLayout,
+ 0,
+ (uint32_t)bindState.descriptorSets.getCount(),
+ bindState.descriptorSets.getBuffer(),
+ 0,
+ nullptr);
+
+ // Bind Push Constants.
+ for (auto pushConstantRange : specializedLayout->m_pushConstantRanges)
+ {
+ m_device->m_api.vkCmdPushConstants(
+ m_commandBuffer->m_commandBuffer,
+ specializedLayout->m_pipelineLayout,
+ pushConstantRange.stageFlags,
+ pushConstantRange.offset,
+ pushConstantRange.size,
+ bindState.pushConstantBuffer.getBuffer() + pushConstantRange.offset);
+ }
+ return SLANG_OK;
+}
+
+
/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! VKDevice::Buffer !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */
Result VKDevice::Buffer::init(const VulkanApi& api, size_t bufferSize, VkBufferUsageFlags usage, VkMemoryPropertyFlags reqMemoryProperties)
@@ -2302,25 +3911,19 @@ VkBool32 VKDevice::handleDebugMessage(VkDebugReportFlagsEXT flags, VkDebugReport
}
VkPipelineShaderStageCreateInfo VKDevice::compileEntryPoint(
- IShaderProgram::KernelDesc const& kernelDesc,
+ ISlangBlob* code,
VkShaderStageFlagBits stage,
- List<char>& outBuffer,
VkShaderModule& outShaderModule)
{
- char const* dataBegin = (char const*) kernelDesc.codeBegin;
- char const* dataEnd = (char const*) kernelDesc.codeEnd;
+ char const* dataBegin = (char const*) code->getBufferPointer();
+ char const* dataEnd = (char const*)code->getBufferPointer() + code->getBufferSize();
// We need to make a copy of the code, since the Slang compiler
// will free the memory after a compile request is closed.
- size_t codeSize = dataEnd - dataBegin;
-
- outBuffer.insertRange(0, dataBegin, codeSize);
-
- char* codeBegin = outBuffer.getBuffer();
VkShaderModuleCreateInfo moduleCreateInfo = { VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO };
- moduleCreateInfo.pCode = (uint32_t*)codeBegin;
- moduleCreateInfo.codeSize = codeSize;
+ moduleCreateInfo.pCode = (uint32_t*)code->getBufferPointer();
+ moduleCreateInfo.codeSize = code->getBufferSize();
VkShaderModule module;
SLANG_VK_CHECK(m_api.vkCreateShaderModule(m_device, &moduleCreateInfo, nullptr, &module));
@@ -2638,7 +4241,7 @@ SlangResult VKDevice::initialize(const Desc& desc)
m_desc = desc;
- SLANG_RETURN_ON_FAIL(GraphicsAPIRenderer::initialize(desc));
+ SLANG_RETURN_ON_FAIL(RendererBase::initialize(desc));
SLANG_RETURN_ON_FAIL(m_module.init());
SLANG_RETURN_ON_FAIL(m_api.initGlobalProcs(m_module));
@@ -3668,454 +5271,98 @@ static VkImageViewType _calcImageViewType(ITextureResource::Type type, const ITe
return VK_IMAGE_VIEW_TYPE_MAX_ENUM;
}
-static VkDescriptorType translateDescriptorType(DescriptorSlotType type)
+Result VKDevice::createProgram(const IShaderProgram::Desc& desc, IShaderProgram** outProgram)
{
- switch(type)
+ RefPtr<ShaderProgramImpl> shaderProgram = new ShaderProgramImpl(m_api, desc.pipelineType);
+ shaderProgram->m_pipelineType = desc.pipelineType;
+ shaderProgram->slangProgram = desc.slangProgram;
+ RootShaderObjectLayout::create(
+ this,
+ desc.slangProgram,
+ desc.slangProgram->getLayout(),
+ shaderProgram->m_rootObjectLayout.writeRef());
+ if (desc.slangProgram->getSpecializationParamCount() != 0)
{
- default:
- return VK_DESCRIPTOR_TYPE_MAX_ENUM;
-
-#define CASE(SRC, DST) \
- case DescriptorSlotType::SRC: return VK_DESCRIPTOR_TYPE_##DST
-
- CASE(Sampler, SAMPLER);
- CASE(CombinedImageSampler, COMBINED_IMAGE_SAMPLER);
- CASE(SampledImage, SAMPLED_IMAGE);
- CASE(StorageImage, STORAGE_IMAGE);
- CASE(UniformTexelBuffer, UNIFORM_TEXEL_BUFFER);
- CASE(StorageTexelBuffer, STORAGE_TEXEL_BUFFER);
- CASE(UniformBuffer, UNIFORM_BUFFER);
- CASE(ReadOnlyStorageBuffer, STORAGE_BUFFER);
- CASE(StorageBuffer, STORAGE_BUFFER);
- CASE(DynamicUniformBuffer, UNIFORM_BUFFER_DYNAMIC);
- CASE(DynamicStorageBuffer, STORAGE_BUFFER_DYNAMIC);
- CASE(InputAttachment, INPUT_ATTACHMENT);
-
-#undef CASE
+ // For a specializable program, we don't invoke any actual slang compilation yet.
+ *outProgram = shaderProgram.detach();
+ return SLANG_OK;
}
-}
-
-Result VKDevice::createDescriptorSetLayout(const IDescriptorSetLayout::Desc& desc, IDescriptorSetLayout** outLayout)
-{
- RefPtr<DescriptorSetLayoutImpl> descriptorSetLayoutImpl = new DescriptorSetLayoutImpl(m_api);
-
- auto& dstBindings = descriptorSetLayoutImpl->m_vkBindings;
- Slang::List<uint32_t> descriptorCountForTypes;
-
- UInt rangeCount = desc.slotRangeCount;
- for(UInt rr = 0; rr < rangeCount; ++rr)
+ // For a fully specialized program, create `VkShaderModule`s for each shader stage.
+ auto programReflection = desc.slangProgram->getLayout();
+ for (SlangUInt i = 0; i < programReflection->getEntryPointCount(); i++)
{
- auto& srcRange = desc.slotRanges[rr];
-
- if(srcRange.type == DescriptorSlotType::RootConstant)
+ auto entryPointInfo = programReflection->getEntryPointByIndex(i);
+ auto stage = entryPointInfo->getStage();
+ ComPtr<ISlangBlob> kernelCode;
+ ComPtr<ISlangBlob> diagnostics;
+ auto compileResult = desc.slangProgram->getEntryPointCode(
+ (SlangInt)i, 0, kernelCode.writeRef(), diagnostics.writeRef());
+ if (diagnostics)
{
- // Root constant ranges are a special case, since they
- // don't actually map to `VkDescriptorSetLayoutBinding`s
- // like the other cases.
-
- // We start by computing the offset of the range within
- // the backing storage for the descriptor set, while
- // also updating the computed total size of root constant
- // data needed by the set.
- //
- auto size = uint32_t(srcRange.count);
- auto offset = descriptorSetLayoutImpl->m_rootConstantDataSize;
- descriptorSetLayoutImpl->m_rootConstantDataSize += size;
-
- // We will keep track of the information for this
- // range as part of the descriptor set layout.
- //
- DescriptorSetLayoutImpl::RootConstantRangeInfo rootConstantRangeInfo;
- rootConstantRangeInfo.offset = offset;
- rootConstantRangeInfo.size = size;
-
- auto rootConstantRangeIndex = descriptorSetLayoutImpl->m_rootConstantRanges.getCount();
- descriptorSetLayoutImpl->m_rootConstantRanges.add(rootConstantRangeInfo);
-
- // We will also add a `RangeInfo` to represent this
- // range, even though it doesn't map to a VK-level
- // descriptor range.
- //
- DescriptorSetLayoutImpl::RangeInfo rangeInfo;
- rangeInfo.type = srcRange.type;
- rangeInfo.vkDescriptorType = VkDescriptorType(-1);
- rangeInfo.arrayIndex = rootConstantRangeIndex;
- descriptorSetLayoutImpl->m_ranges.add(rangeInfo);
-
- // Finally, we bail out instead of performing
- // the logic that applies to the other descriptor
- // range types.
- //
- continue;
+ // TODO: report compile error.
}
-
- // Note: Because of the existence of root constant ranges,
- // we cannot assume that the `binding` for a range is
- // the same as its index in the input array of ranges.
- //
- // Instead, the `binding` for a range is its index in
- // the output array of `VkDescriptorSetLayoutBinding`s.
- //
- uint32_t bindingIndex = uint32_t(dstBindings.getCount());
-
- VkDescriptorType dstDescriptorType = translateDescriptorType(srcRange.type);
-
- VkDescriptorSetLayoutBinding dstBinding;
- dstBinding.binding = uint32_t(bindingIndex);
- dstBinding.descriptorType = dstDescriptorType;
- dstBinding.descriptorCount = uint32_t(srcRange.count);
- dstBinding.stageFlags = VK_SHADER_STAGE_ALL;
- dstBinding.pImmutableSamplers = nullptr;
-
- if (descriptorCountForTypes.getCount() <= dstDescriptorType)
- {
- descriptorCountForTypes.setCount(dstDescriptorType + 1);
- }
-
- descriptorCountForTypes[dstDescriptorType] += uint32_t(srcRange.count);
-
- dstBindings.add(dstBinding);
-
- UInt boundObjectCount = srcRange.count;
- if( srcRange.type == DescriptorSlotType::CombinedImageSampler )
- {
- boundObjectCount = 2 * srcRange.count;
- }
-
- auto boundObjectArrayIndex = descriptorSetLayoutImpl->m_totalBoundObjectCount;
- descriptorSetLayoutImpl->m_totalBoundObjectCount += boundObjectCount;
-
- DescriptorSetLayoutImpl::RangeInfo rangeInfo;
- rangeInfo.type = srcRange.type;
- rangeInfo.vkDescriptorType = dstDescriptorType;
- rangeInfo.vkBindingIndex = bindingIndex;
- rangeInfo.arrayIndex = boundObjectArrayIndex;
- descriptorSetLayoutImpl->m_ranges.add(rangeInfo);
+ SLANG_RETURN_ON_FAIL(compileResult);
+ shaderProgram->m_codeBlobs.add(kernelCode);
+ VkShaderModule shaderModule;
+ shaderProgram->m_stageCreateInfos.add(compileEntryPoint(
+ kernelCode,
+ (VkShaderStageFlagBits)VulkanUtil::getShaderStage(stage),
+ shaderModule));
+ shaderProgram->m_modules.add(shaderModule);
}
-
- VkDescriptorSetLayoutCreateInfo descriptorSetLayoutInfo = { VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO };
- descriptorSetLayoutInfo.bindingCount = uint32_t(dstBindings.getCount());
- descriptorSetLayoutInfo.pBindings = dstBindings.getBuffer();
-
- VkDescriptorSetLayout descriptorSetLayout = VK_NULL_HANDLE;
- SLANG_VK_CHECK(m_api.vkCreateDescriptorSetLayout(m_device, &descriptorSetLayoutInfo, nullptr, &descriptorSetLayout));
-
- descriptorSetLayoutImpl->m_descriptorSetLayout = descriptorSetLayout;
-
- *outLayout = descriptorSetLayoutImpl.detach();
+ *outProgram = shaderProgram.detach();
return SLANG_OK;
}
-Result VKDevice::createPipelineLayout(const IPipelineLayout::Desc& desc, IPipelineLayout** outLayout)
+Result VKDevice::createShaderObjectLayout(
+ slang::TypeLayoutReflection* typeLayout,
+ ShaderObjectLayoutBase** outLayout)
{
- UInt descriptorSetCount = desc.descriptorSetCount;
-
- VkDescriptorSetLayout descriptorSetLayouts[kMaxDescriptorSets];
- uint32_t descriptorSetRootConstantOffsets[kMaxDescriptorSets];
- uint32_t totalRootConstantSize = 0;
- for(UInt ii = 0; ii < descriptorSetCount; ++ii)
- {
- auto descriptorSetLayoutImpl = (DescriptorSetLayoutImpl*) desc.descriptorSets[ii].layout;
- descriptorSetLayouts[ii] = descriptorSetLayoutImpl->m_descriptorSetLayout;
-
- descriptorSetRootConstantOffsets[ii] = totalRootConstantSize;
- totalRootConstantSize += descriptorSetLayoutImpl->m_rootConstantDataSize;
- }
-
- VkPipelineLayoutCreateInfo pipelineLayoutInfo = { VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO };
- pipelineLayoutInfo.setLayoutCount = uint32_t(desc.descriptorSetCount);
- pipelineLayoutInfo.pSetLayouts = &descriptorSetLayouts[0];
-
- // Our abstraction allows the user to specify any number of root-constant
- // ranges across all of their descriptor sets, but Vulkan has a restriction
- // that a pipeline layout may only include a single push constant range
- // accessible from a given stage. (In other words, the only situation where
- // multiple push-constant ranges are allowed is if you want to have, say,
- // distinct ranges for the vertex and fragment stages to access).
- //
- // We handle this by declaring at most one push constant range, which
- // represents the concatenation of the data from all ranges that the
- // user might have asked for.
- //
- // Note: The Slang compiler doesn't yet have logic to concatenate multiple
- // push-constant ranges in this way, but if/when it does, it should hopefully
- // Just Work with this logic.
- //
- VkPushConstantRange pushConstantRange;
- if( totalRootConstantSize )
- {
- pushConstantRange.offset = 0;
- pushConstantRange.size = totalRootConstantSize;
- pushConstantRange.stageFlags = VK_SHADER_STAGE_ALL;
-
- pipelineLayoutInfo.pushConstantRangeCount = 1;
- pipelineLayoutInfo.pPushConstantRanges = &pushConstantRange;
- }
-
- VkPipelineLayout pipelineLayout;
- SLANG_VK_CHECK(m_api.vkCreatePipelineLayout(m_device, &pipelineLayoutInfo, nullptr, &pipelineLayout));
- RefPtr<PipelineLayoutImpl> pipelineLayoutImpl = new PipelineLayoutImpl(m_api);
- pipelineLayoutImpl->m_pipelineLayout = pipelineLayout;
- pipelineLayoutImpl->m_descriptorSetCount = descriptorSetCount;
-
- for(UInt ii = 0; ii < descriptorSetCount; ++ii)
- {
- pipelineLayoutImpl->m_descriptorSetRootConstantOffsets.add(
- descriptorSetRootConstantOffsets[ii]);
- }
-
- *outLayout = pipelineLayoutImpl.detach();
+ RefPtr<ShaderObjectLayoutImpl> layout;
+ SLANG_RETURN_ON_FAIL(
+ ShaderObjectLayoutImpl::createForElementType(this, typeLayout, layout.writeRef()));
+ *outLayout = layout.detach();
return SLANG_OK;
}
-Result VKDevice::createDescriptorSet(
- IDescriptorSetLayout* layout,
- IDescriptorSet::Flag::Enum flag,
- IDescriptorSet** outDescriptorSet)
+Result VKDevice::createShaderObject(ShaderObjectLayoutBase* layout, IShaderObject** outObject)
{
- auto layoutImpl = (DescriptorSetLayoutImpl*)layout;
-
- RefPtr<DescriptorSetImpl> descriptorSetImpl = new DescriptorSetImpl(this);
- descriptorSetImpl->m_layout = layoutImpl;
- descriptorSetImpl->m_descriptorSet =
- descriptorSetAllocator.allocate(layoutImpl->m_descriptorSetLayout);
- descriptorSetImpl->m_isTransient = (flag == IDescriptorSet::Flag::Enum::Transient);
- descriptorSetImpl->m_rootConstantData.setCount(layoutImpl->m_rootConstantDataSize);
- descriptorSetImpl->m_boundObjects.setCount(layoutImpl->m_totalBoundObjectCount);
-
- *outDescriptorSet = descriptorSetImpl.detach();
+ RefPtr<ShaderObjectImpl> shaderObject;
+ SLANG_RETURN_ON_FAIL(ShaderObjectImpl::create(
+ this, static_cast<ShaderObjectLayoutImpl*>(layout), shaderObject.writeRef()));
+ *outObject = shaderObject.detach();
return SLANG_OK;
}
-void VKDevice::DescriptorSetImpl::setConstantBuffer(UInt range, UInt index, IBufferResource* buffer)
-{
- auto bufferImpl = (BufferResourceImpl*)buffer;
-
- SLANG_ASSERT(range < UInt(m_layout->m_ranges.getCount()));
- auto& rangeInfo = m_layout->m_ranges[range];
- auto bindingIndex = rangeInfo.vkBindingIndex;
- auto boundObjectIndex = rangeInfo.arrayIndex + index;
-
- VkDescriptorBufferInfo bufferInfo = {};
- bufferInfo.buffer = bufferImpl->m_buffer.m_buffer;
- bufferInfo.offset = 0;
- bufferInfo.range = bufferImpl->getDesc()->sizeInBytes;
-
- VkWriteDescriptorSet writeInfo = { VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET };
- writeInfo.dstSet = m_descriptorSet.handle;
- writeInfo.dstBinding = uint32_t(bindingIndex);
- writeInfo.dstArrayElement = uint32_t(index);
- writeInfo.descriptorCount = 1;
- writeInfo.descriptorType = rangeInfo.vkDescriptorType;
- writeInfo.pBufferInfo = &bufferInfo;
-
- m_renderer->m_api.vkUpdateDescriptorSets(m_renderer->m_device, 1, &writeInfo, 0, nullptr);
- m_boundObjects[boundObjectIndex] = dynamic_cast<RefObject*>(buffer);
-}
-
-void VKDevice::DescriptorSetImpl::setResource(UInt range, UInt index, IResourceView* view)
+Result SLANG_MCALL
+ VKDevice::createRootShaderObject(IShaderProgram* program, IShaderObject** outObject)
{
- SLANG_ASSERT(range < UInt(m_layout->m_ranges.getCount()));
- auto& rangeInfo = m_layout->m_ranges[range];
- auto bindingIndex = rangeInfo.vkBindingIndex;
- auto boundObjectIndex = rangeInfo.arrayIndex + index;
- auto descriptorType = rangeInfo.vkDescriptorType;
-
- auto viewImpl = (ResourceViewImpl*)view;
- if (!viewImpl)
- return;
- switch (viewImpl->m_type)
- {
- case ResourceViewImpl::ViewType::Texture:
- {
- auto textureViewImpl = (TextureResourceViewImpl*)viewImpl;
- VkDescriptorImageInfo imageInfo = {};
- imageInfo.imageView = textureViewImpl->m_view;
- imageInfo.imageLayout = textureViewImpl->m_layout;
-
- VkWriteDescriptorSet writeInfo = { VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET };
- writeInfo.dstSet = m_descriptorSet.handle;
- writeInfo.dstBinding = uint32_t(bindingIndex);
- writeInfo.dstArrayElement = uint32_t(index);
- writeInfo.descriptorCount = 1;
- writeInfo.descriptorType = descriptorType;
- writeInfo.pImageInfo = &imageInfo;
-
- m_renderer->m_api.vkUpdateDescriptorSets(m_renderer->m_device, 1, &writeInfo, 0, nullptr);
- }
- break;
-
- case ResourceViewImpl::ViewType::TexelBuffer:
- {
- auto bufferViewImpl = (TexelBufferResourceViewImpl*)viewImpl;
-
- VkWriteDescriptorSet writeInfo = { VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET };
- writeInfo.dstSet = m_descriptorSet.handle;
- writeInfo.dstBinding = uint32_t(bindingIndex);
- writeInfo.dstArrayElement = uint32_t(index);
- writeInfo.descriptorCount = 1;
- writeInfo.descriptorType = descriptorType;
- writeInfo.pTexelBufferView = &bufferViewImpl->m_view;
-
- m_renderer->m_api.vkUpdateDescriptorSets(m_renderer->m_device, 1, &writeInfo, 0, nullptr);
- }
- break;
-
- case ResourceViewImpl::ViewType::PlainBuffer:
- {
- auto bufferViewImpl = (PlainBufferResourceViewImpl*) viewImpl;
-
- VkDescriptorBufferInfo bufferInfo = {};
- bufferInfo.buffer = bufferViewImpl->m_buffer->m_buffer.m_buffer;
- bufferInfo.offset = bufferViewImpl->offset;
- bufferInfo.range = bufferViewImpl->size;
-
- VkWriteDescriptorSet writeInfo = { VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET };
- writeInfo.dstSet = m_descriptorSet.handle;
- writeInfo.dstBinding = uint32_t(bindingIndex);
- writeInfo.dstArrayElement = uint32_t(index);
- writeInfo.descriptorCount = 1;
- writeInfo.descriptorType = descriptorType;
- writeInfo.pBufferInfo = &bufferInfo;
-
- m_renderer->m_api.vkUpdateDescriptorSets(m_renderer->m_device, 1, &writeInfo, 0, nullptr);
- }
- break;
- }
-
- m_boundObjects[boundObjectIndex] = dynamic_cast<RefObject*>(view);
-}
-
-void VKDevice::DescriptorSetImpl::setSampler(UInt range, UInt index, ISamplerState* sampler)
-{
- SLANG_ASSERT(range < UInt(m_layout->m_ranges.getCount()));
- auto& rangeInfo = m_layout->m_ranges[range];
- SLANG_ASSERT(rangeInfo.type == DescriptorSlotType::Sampler);
- auto bindingIndex = rangeInfo.vkBindingIndex;
- auto boundObjectIndex = rangeInfo.arrayIndex + index;
- auto descriptorType = rangeInfo.vkDescriptorType;
- if (!sampler)
- return;
- VkWriteDescriptorSet writeInfo = { VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET };
- writeInfo.dstSet = m_descriptorSet.handle;
- writeInfo.dstBinding = uint32_t(bindingIndex);
- writeInfo.dstArrayElement = uint32_t(index);
- writeInfo.descriptorCount = 1;
- writeInfo.descriptorType = descriptorType;
- VkDescriptorImageInfo imageInfo = {};
- imageInfo.sampler = static_cast<SamplerStateImpl*>(sampler)->m_sampler;
- writeInfo.pImageInfo = &imageInfo;
- m_renderer->m_api.vkUpdateDescriptorSets(m_renderer->m_device, 1, &writeInfo, 0, nullptr);
-
- m_boundObjects[boundObjectIndex] = dynamic_cast<RefObject*>(sampler);
-}
-
-void VKDevice::DescriptorSetImpl::setCombinedTextureSampler(
- UInt range,
- UInt index,
- IResourceView* textureView,
- ISamplerState* sampler)
-{
- SLANG_ASSERT(range < UInt(m_layout->m_ranges.getCount()));
- auto& rangeInfo = m_layout->m_ranges[range];
- SLANG_ASSERT(rangeInfo.type == DescriptorSlotType::CombinedImageSampler);
- auto bindingIndex = rangeInfo.vkBindingIndex;
- auto descriptorType = rangeInfo.vkDescriptorType;
-
- // TODO: Actually bind it!
-
- // Note: Each entry in a combined texture/sampler range consumes
- // two entries in the `m_boundObjects` array, since we have
- // to keep both the texture view and the sampler object live.
- //
- auto boundObjectIndex = rangeInfo.arrayIndex + 2 * index;
- m_boundObjects[boundObjectIndex + 0] = dynamic_cast<RefObject*>(textureView);
- m_boundObjects[boundObjectIndex + 1] = dynamic_cast<RefObject*>(sampler);
+ auto programImpl = dynamic_cast<ShaderProgramImpl*>(program);
+ RefPtr<RootShaderObjectImpl> shaderObject;
+ SLANG_RETURN_ON_FAIL(RootShaderObjectImpl::create(
+ this, programImpl->m_rootObjectLayout, shaderObject.writeRef()));
+ *outObject = shaderObject.detach();
+ return SLANG_OK;
}
-void VKDevice::DescriptorSetImpl::setRootConstants(
- UInt range,
- UInt offset,
- UInt size,
- void const* data)
+Result VKDevice::createGraphicsPipelineState(const GraphicsPipelineStateDesc& inDesc, IPipelineState** outState)
{
- // The `range` variabel is the index of one of the descriptor
- // slot ranges, which had better be a `RootConstant` range.
- //
- SLANG_ASSERT(range < UInt(m_layout->m_ranges.getCount()));
- auto& rangeInfo = m_layout->m_ranges[range];
- SLANG_ASSERT(rangeInfo.type == DescriptorSlotType::RootConstant);
-
- // The `arrayIndex` for the descriptor slot range will refer
- // to a root constant range, which is the range to be set.
- //
- auto rootConstantIndex = rangeInfo.arrayIndex;
- SLANG_ASSERT(rootConstantIndex >= 0);
- SLANG_ASSERT(rootConstantIndex < m_layout->m_rootConstantRanges.getCount());
- auto& rootConstantRangeInfo = m_layout->m_rootConstantRanges[rootConstantIndex];
- SLANG_ASSERT(offset + size <= UInt(rootConstantRangeInfo.size));
-
- memcpy(m_rootConstantData.getBuffer() + rootConstantRangeInfo.offset + offset, data, size);
-}
+ GraphicsPipelineStateDesc desc = inDesc;
+ auto programImpl = static_cast<ShaderProgramImpl*>(desc.program);
-Result VKDevice::createProgram(const IShaderProgram::Desc& desc, IShaderProgram** outProgram)
-{
- if (desc.slangProgram && desc.slangProgram->getSpecializationParamCount() != 0)
+ if (!programImpl->m_rootObjectLayout->m_pipelineLayout)
{
- // For a specializable program, we don't invoke any actual slang compilation yet.
- RefPtr<ShaderProgramImpl> shaderProgram = new ShaderProgramImpl(m_api, desc.pipelineType);
- initProgramCommon(shaderProgram, desc);
- *outProgram = shaderProgram.detach();
+ RefPtr<PipelineStateImpl> pipelineStateImpl = new PipelineStateImpl(m_api);
+ pipelineStateImpl->init(desc);
+ *outState = pipelineStateImpl.detach();
return SLANG_OK;
}
- if( desc.kernelCount == 0 )
- {
- return createProgramFromSlang(this, desc, outProgram);
- }
-
- RefPtr<ShaderProgramImpl> impl = new ShaderProgramImpl(m_api, desc.pipelineType);
- if( desc.pipelineType == PipelineType::Compute)
- {
- auto computeKernel = desc.findKernel(StageType::Compute);
- impl->m_compute = compileEntryPoint(*computeKernel, VK_SHADER_STAGE_COMPUTE_BIT, impl->m_buffers[0], impl->m_modules[0]);
- }
- else
- {
- auto vertexKernel = desc.findKernel(StageType::Vertex);
- auto fragmentKernel = desc.findKernel(StageType::Fragment);
-
- impl->m_vertex = compileEntryPoint(*vertexKernel, VK_SHADER_STAGE_VERTEX_BIT, impl->m_buffers[0], impl->m_modules[0]);
- impl->m_fragment = compileEntryPoint(*fragmentKernel, VK_SHADER_STAGE_FRAGMENT_BIT, impl->m_buffers[1], impl->m_modules[1]);
- }
- initProgramCommon(impl, desc);
- *outProgram = impl.detach();
- return SLANG_OK;
-}
-
-Result VKDevice::createGraphicsPipelineState(const GraphicsPipelineStateDesc& inDesc, IPipelineState** outState)
-{
- GraphicsPipelineStateDesc desc = inDesc;
- preparePipelineDesc(desc);
-
VkPipelineCache pipelineCache = VK_NULL_HANDLE;
- auto programImpl = (ShaderProgramImpl*) desc.program;
- auto pipelineLayoutImpl = (PipelineLayoutImpl*) desc.pipelineLayout;
auto inputLayoutImpl = (InputLayoutImpl*) desc.inputLayout;
- // Shader Stages
- //
- // Currently only handles vertex/fragment.
-
- static const uint32_t kMaxShaderStages = 2;
- VkPipelineShaderStageCreateInfo shaderStages[kMaxShaderStages];
-
- uint32_t shaderStageCount = 0;
- shaderStages[shaderStageCount++] = programImpl->m_vertex;
- shaderStages[shaderStageCount++] = programImpl->m_fragment;
-
// VertexBuffer/s
// Currently only handles one
@@ -4241,8 +5488,8 @@ Result VKDevice::createGraphicsPipelineState(const GraphicsPipelineStateDesc& in
VkGraphicsPipelineCreateInfo pipelineInfo = { VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO };
pipelineInfo.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO;
- pipelineInfo.stageCount = 2;
- pipelineInfo.pStages = shaderStages;
+ pipelineInfo.stageCount = (uint32_t)programImpl->m_stageCreateInfos.getCount();
+ pipelineInfo.pStages = programImpl->m_stageCreateInfos.getBuffer();
pipelineInfo.pVertexInputState = &vertexInputInfo;
pipelineInfo.pInputAssemblyState = &inputAssembly;
pipelineInfo.pViewportState = &viewportState;
@@ -4250,7 +5497,7 @@ Result VKDevice::createGraphicsPipelineState(const GraphicsPipelineStateDesc& in
pipelineInfo.pMultisampleState = &multisampling;
pipelineInfo.pColorBlendState = &colorBlending;
pipelineInfo.pDepthStencilState = &depthStencilStateInfo;
- pipelineInfo.layout = pipelineLayoutImpl->m_pipelineLayout;
+ pipelineInfo.layout = programImpl->m_rootObjectLayout->m_pipelineLayout;
pipelineInfo.renderPass = static_cast<FramebufferLayoutImpl*>(desc.framebufferLayout)->m_renderPass;
pipelineInfo.subpass = 0;
pipelineInfo.basePipelineHandle = VK_NULL_HANDLE;
@@ -4271,28 +5518,28 @@ Result VKDevice::createGraphicsPipelineState(const GraphicsPipelineStateDesc& in
Result VKDevice::createComputePipelineState(const ComputePipelineStateDesc& inDesc, IPipelineState** outState)
{
ComputePipelineStateDesc desc = inDesc;
- preparePipelineDesc(desc);
+ auto programImpl = static_cast<ShaderProgramImpl*>(desc.program);
+ if (!programImpl->m_rootObjectLayout->m_pipelineLayout)
+ {
+ RefPtr<PipelineStateImpl> pipelineStateImpl = new PipelineStateImpl(m_api);
+ pipelineStateImpl->init(desc);
+ *outState = pipelineStateImpl.detach();
+ return SLANG_OK;
+ }
VkPipelineCache pipelineCache = VK_NULL_HANDLE;
- auto programImpl = (ShaderProgramImpl*) desc.program;
- auto pipelineLayoutImpl = (PipelineLayoutImpl*) desc.pipelineLayout;
-
VkPipeline pipeline = VK_NULL_HANDLE;
- if (!programImpl->slangProgram || programImpl->slangProgram->getSpecializationParamCount() == 0)
- {
- VkComputePipelineCreateInfo computePipelineInfo = {
- VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO};
- computePipelineInfo.stage = programImpl->m_compute;
- computePipelineInfo.layout = pipelineLayoutImpl->m_pipelineLayout;
- SLANG_VK_CHECK(m_api.vkCreateComputePipelines(
- m_device, pipelineCache, 1, &computePipelineInfo, nullptr, &pipeline));
- }
+ VkComputePipelineCreateInfo computePipelineInfo = {
+ VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO };
+ computePipelineInfo.stage = programImpl->m_stageCreateInfos[0];
+ computePipelineInfo.layout = programImpl->m_rootObjectLayout->m_pipelineLayout;
+ SLANG_VK_CHECK(m_api.vkCreateComputePipelines(
+ m_device, pipelineCache, 1, &computePipelineInfo, nullptr, &pipeline));
RefPtr<PipelineStateImpl> pipelineStateImpl = new PipelineStateImpl(m_api);
pipelineStateImpl->m_pipeline = pipeline;
- pipelineStateImpl->m_pipelineLayout = pipelineLayoutImpl;
pipelineStateImpl->init(desc);
*outState = pipelineStateImpl.detach();
return SLANG_OK;
diff --git a/tools/gfx/vulkan/vk-util.cpp b/tools/gfx/vulkan/vk-util.cpp
index 218801d7a..46ffb01fd 100644
--- a/tools/gfx/vulkan/vk-util.cpp
+++ b/tools/gfx/vulkan/vk-util.cpp
@@ -30,6 +30,53 @@ namespace gfx {
return (res == VK_SUCCESS) ? SLANG_OK : SLANG_FAIL;
}
+VkShaderStageFlags VulkanUtil::getShaderStage(SlangStage stage)
+{
+ switch (stage)
+ {
+ case SLANG_STAGE_ANY_HIT:
+ return VK_SHADER_STAGE_ANY_HIT_BIT_KHR;
+ case SLANG_STAGE_CALLABLE:
+ return VK_SHADER_STAGE_CALLABLE_BIT_KHR;
+ case SLANG_STAGE_CLOSEST_HIT:
+ return VK_SHADER_STAGE_CLOSEST_HIT_BIT_KHR;
+ case SLANG_STAGE_COMPUTE:
+ return VK_SHADER_STAGE_COMPUTE_BIT;
+ case SLANG_STAGE_DOMAIN:
+ return VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT;
+ case SLANG_STAGE_FRAGMENT:
+ return VK_SHADER_STAGE_FRAGMENT_BIT;
+ case SLANG_STAGE_GEOMETRY:
+ return VK_SHADER_STAGE_GEOMETRY_BIT;
+ case SLANG_STAGE_HULL:
+ return VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT;
+ case SLANG_STAGE_INTERSECTION:
+ return VK_SHADER_STAGE_INTERSECTION_BIT_KHR;
+ case SLANG_STAGE_RAY_GENERATION:
+ return VK_SHADER_STAGE_RAYGEN_BIT_KHR;
+ case SLANG_STAGE_VERTEX:
+ return VK_SHADER_STAGE_VERTEX_BIT;
+ default:
+ assert(!"unsupported stage.");
+ return VkShaderStageFlags(-1);
+ }
+}
+
+VkPipelineBindPoint VulkanUtil::getPipelineBindPoint(PipelineType pipelineType)
+{
+ switch (pipelineType)
+ {
+ case gfx::PipelineType::Graphics:
+ return VK_PIPELINE_BIND_POINT_GRAPHICS;
+ case gfx::PipelineType::Compute:
+ return VK_PIPELINE_BIND_POINT_COMPUTE;
+ case gfx::PipelineType::RayTracing:
+ return VK_PIPELINE_BIND_POINT_RAY_TRACING_KHR;
+ default:
+ return VkPipelineBindPoint(-1);
+ }
+}
+
/* static */Slang::Result VulkanUtil::handleFail(VkResult res)
{
if (res != VK_SUCCESS)
diff --git a/tools/gfx/vulkan/vk-util.h b/tools/gfx/vulkan/vk-util.h
index f374eea8b..0b14f7f84 100644
--- a/tools/gfx/vulkan/vk-util.h
+++ b/tools/gfx/vulkan/vk-util.h
@@ -38,6 +38,10 @@ struct VulkanUtil
/// Returns Slang::Result equivalent of a VkResult
static Slang::Result toSlangResult(VkResult res);
+
+ static VkShaderStageFlags getShaderStage(SlangStage stage);
+
+ static VkPipelineBindPoint getPipelineBindPoint(PipelineType pipelineType);
};
} // renderer_test
diff --git a/tools/platform/gui.cpp b/tools/platform/gui.cpp
index 8e455cd12..e33a504bb 100644
--- a/tools/platform/gui.cpp
+++ b/tools/platform/gui.cpp
@@ -91,68 +91,16 @@ GUI::GUI(
} \
";
- SlangSession* slangSession = spCreateSession(nullptr);
- SlangCompileRequest* slangRequest = spCreateCompileRequest(slangSession);
-
- // TODO: These two lines need to change based on what the target graphics API
- // is, so we need a way for a `Renderer` to pass back its prefeerred code
- // format and profile name...
- //
- int targetIndex = spAddCodeGenTarget(slangRequest, SLANG_DXBC);
- spSetTargetProfile(slangRequest, targetIndex, spFindProfile(slangSession, "sm_4_0"));
-
- int translationUnitIndex = spAddTranslationUnit(slangRequest, SLANG_SOURCE_LANGUAGE_SLANG, nullptr);
- spAddTranslationUnitSourceString(slangRequest, translationUnitIndex, "gui.cpp.slang", shaderCode);
-
- char const* vertexEntryPointName = "vertexMain";
- char const* fragmentEntryPointName = "fragmentMain";
- int vertexIndex = spAddEntryPoint(slangRequest, translationUnitIndex, vertexEntryPointName, SLANG_STAGE_VERTEX);
- int fragmentIndex = spAddEntryPoint(slangRequest, translationUnitIndex, fragmentEntryPointName, SLANG_STAGE_FRAGMENT);
-
- const SlangResult compileRes = spCompile(slangRequest);
- if(auto diagnostics = spGetDiagnosticOutput(slangRequest))
- {
- printf("%s", diagnostics);
- }
- if(SLANG_FAILED(compileRes))
- {
- spDestroyCompileRequest(slangRequest);
- spDestroySession(slangSession);
- assert(!"unexpected");
- return;
- }
-
- ISlangBlob* vertexShaderBlob = nullptr;
- spGetEntryPointCodeBlob(slangRequest, vertexIndex, 0, &vertexShaderBlob);
-
- ISlangBlob* fragmentShaderBlob = nullptr;
- spGetEntryPointCodeBlob(slangRequest, fragmentIndex, 0, &fragmentShaderBlob);
-
- char const* vertexCode = (char const*) vertexShaderBlob->getBufferPointer();
- char const* vertexCodeEnd = vertexCode + vertexShaderBlob->getBufferSize();
-
- char const* fragmentCode = (char const*) fragmentShaderBlob->getBufferPointer();
- char const* fragmentCodeEnd = fragmentCode + fragmentShaderBlob->getBufferSize();
-
- spDestroyCompileRequest(slangRequest);
- spDestroySession(slangSession);
-
- gfx::IShaderProgram::KernelDesc kernelDescs[] =
- {
- { gfx::StageType::Vertex, vertexCode, vertexCodeEnd },
- { gfx::StageType::Fragment, fragmentCode, fragmentCodeEnd },
- };
+ auto slangSession = inDevice->getSlangSession();
+ // TODO: create slang program.
+ IShaderProgram* program = nullptr;
+#if 0
gfx::IShaderProgram::Desc programDesc = {};
programDesc.pipelineType = gfx::PipelineType::Graphics;
- programDesc.kernels = &kernelDescs[0];
- programDesc.kernelCount = 2;
-
- auto program = device->createProgram(programDesc);
-
- vertexShaderBlob->release();
- fragmentShaderBlob->release();
-
+ programDesc.slangProgram = slangProgram;
+ program = device->createProgram(programDesc);
+#endif
InputElementDesc inputElements[] = {
{"U", 0, Format::RG_Float32, offsetof(ImDrawVert, pos) },
{"U", 1, Format::RG_Float32, offsetof(ImDrawVert, uv) },
@@ -164,27 +112,6 @@ GUI::GUI(
//
- Slang::List<IDescriptorSetLayout::SlotRangeDesc> descriptorSetRanges;
- descriptorSetRanges.add(IDescriptorSetLayout::SlotRangeDesc(DescriptorSlotType::UniformBuffer));
- descriptorSetRanges.add(IDescriptorSetLayout::SlotRangeDesc(DescriptorSlotType::SampledImage));
- descriptorSetRanges.add(IDescriptorSetLayout::SlotRangeDesc(DescriptorSlotType::Sampler));
-
- IDescriptorSetLayout::Desc descriptorSetLayoutDesc;
- descriptorSetLayoutDesc.slotRangeCount = descriptorSetRanges.getCount();
- descriptorSetLayoutDesc.slotRanges = descriptorSetRanges.getBuffer();
-
- descriptorSetLayout = device->createDescriptorSetLayout(descriptorSetLayoutDesc);
-
- Slang::List<IPipelineLayout::DescriptorSetDesc> pipelineDescriptorSets;
- pipelineDescriptorSets.add(IPipelineLayout::DescriptorSetDesc(descriptorSetLayout));
-
- IPipelineLayout::Desc pipelineLayoutDesc;
- pipelineLayoutDesc.descriptorSetCount = pipelineDescriptorSets.getCount();
- pipelineLayoutDesc.descriptorSets = pipelineDescriptorSets.getBuffer();
- pipelineLayoutDesc.renderTargetCount = 1;
-
- pipelineLayout = device->createPipelineLayout(pipelineLayoutDesc);
-
TargetBlendDesc targetBlendDesc;
targetBlendDesc.color.srcFactor = BlendFactor::SrcAlpha;
targetBlendDesc.color.dstFactor = BlendFactor::InvSrcAlpha;
@@ -194,7 +121,6 @@ GUI::GUI(
GraphicsPipelineStateDesc pipelineDesc;
pipelineDesc.framebufferLayout = framebufferLayout;
pipelineDesc.program = program;
- pipelineDesc.pipelineLayout = pipelineLayout;
pipelineDesc.inputLayout = inputLayout;
pipelineDesc.blend.targets = &targetBlendDesc;
pipelineDesc.blend.targetCount = 1;
@@ -379,19 +305,8 @@ void GUI::endFrame(IFramebuffer* framebuffer)
};
renderEncoder->setScissorRects(1, &rect);
- // TODO: This should be a dynamic/transient descriptor set...
- auto descriptorSet = device->createDescriptorSet(descriptorSetLayout, gfx::IDescriptorSet::Flag::Transient);
- descriptorSet->setConstantBuffer(0, 0, constantBuffer);
- descriptorSet->setResource(1, 0,
- (gfx::IResourceView*) command->TextureId);
- descriptorSet->setSampler(2, 0,
- samplerState);
-
- renderEncoder->setDescriptorSet(
- pipelineLayout,
- 0,
- descriptorSet);
-
+ // TODO: set parameter into root shader object.
+
renderEncoder->drawIndexed(command->ElemCount, indexOffset, vertexOffset);
}
indexOffset += command->ElemCount;
diff --git a/tools/platform/gui.h b/tools/platform/gui.h
index 970f3a4e8..ef5310cd4 100644
--- a/tools/platform/gui.h
+++ b/tools/platform/gui.h
@@ -26,8 +26,6 @@ private:
Slang::ComPtr<gfx::ICommandQueue> queue;
Slang::ComPtr<gfx::IRenderPassLayout> renderPass;
Slang::ComPtr<gfx::IPipelineState> pipelineState;
- Slang::ComPtr<gfx::IDescriptorSetLayout> descriptorSetLayout;
- Slang::ComPtr<gfx::IPipelineLayout> pipelineLayout;
Slang::ComPtr<gfx::ISamplerState> samplerState;
};
diff --git a/tools/render-test/slang-support.cpp b/tools/render-test/slang-support.cpp
index c6d2b971e..72732363e 100644
--- a/tools/render-test/slang-support.cpp
+++ b/tools/render-test/slang-support.cpp
@@ -52,16 +52,10 @@ gfx::StageType translateStage(SlangStage slangStage)
void ShaderCompilerUtil::Output::set(
PipelineType pipelineType,
- const IShaderProgram::KernelDesc* inKernelDescs,
- Slang::Index kernelDescCount,
slang::IComponentType* inSlangProgram)
{
- kernelDescs.clear();
- kernelDescs.addRange(inKernelDescs, kernelDescCount);
slangProgram = inSlangProgram;
desc.pipelineType = pipelineType;
- desc.kernels = kernelDescs.getBuffer();
- desc.kernelCount = kernelDescCount;
desc.slangProgram = inSlangProgram;
}
@@ -70,11 +64,8 @@ void ShaderCompilerUtil::Output::reset()
{
desc.pipelineType = PipelineType::Unknown;
desc.slangProgram = nullptr;
- desc.kernels = nullptr;
- desc.kernelCount = 0;
}
- kernelDescs.clear();
if (m_requestForKernels && session)
{
spDestroyCompileRequest(m_requestForKernels);
@@ -245,28 +236,7 @@ void ShaderCompilerUtil::Output::reset()
actualEntryPoints = request.entryPoints;
}
- Slang::List<IShaderProgram::KernelDesc> kernelDescs;
-
- Index actualEntryPointCount = actualEntryPoints.getCount();
- for(Index ee = 0; ee < actualEntryPointCount; ++ee)
- {
- auto& actualEntryPoint = actualEntryPoints[ee];
-
- size_t codeSize = 0;
- char const* code = (char const*) spGetEntryPointCode(slangRequest, int(ee), &codeSize);
-
- auto gfxStage = translateStage(actualEntryPoint.slangStage);
-
- IShaderProgram::KernelDesc kernelDesc;
- kernelDesc.stage = gfxStage;
- kernelDesc.codeBegin = code;
- kernelDesc.codeEnd = code + codeSize;
- kernelDesc.entryPointName = actualEntryPoint.name;
-
- kernelDescs.add(kernelDesc);
- }
-
- out.set(input.pipelineType, kernelDescs.getBuffer(), kernelDescs.getCount(), linkedSlangProgram);
+ out.set(input.pipelineType, linkedSlangProgram);
return SLANG_OK;
}
diff --git a/tools/render-test/slang-support.h b/tools/render-test/slang-support.h
index 6fa850874..7e00a2c72 100644
--- a/tools/render-test/slang-support.h
+++ b/tools/render-test/slang-support.h
@@ -60,8 +60,6 @@ struct ShaderCompilerUtil
{
void set(
PipelineType pipelineType,
- const IShaderProgram::KernelDesc* kernelDescs,
- Slang::Index kernelDescCount,
slang::IComponentType* slangProgram);
void reset();
~Output()
@@ -69,19 +67,6 @@ struct ShaderCompilerUtil
reset();
}
- Slang::Index findKernelDescIndex(gfx::StageType stage) const
- {
- for (Slang::Index i = 0; i < kernelDescs.getCount(); ++i)
- {
- if (kernelDescs[i].stage == stage)
- {
- return i;
- }
- }
- return -1;
- }
-
- Slang::List<IShaderProgram::KernelDesc> kernelDescs;
ComPtr<slang::IComponentType> slangProgram;
IShaderProgram::Desc desc = {};