diff options
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, + ©Info); + } + + 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, - ©Info); - } - - 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 = {}; |
