summaryrefslogtreecommitdiff
path: root/tools
diff options
context:
space:
mode:
Diffstat (limited to 'tools')
-rw-r--r--tools/gfx/d3d11/render-d3d11.cpp166
-rw-r--r--tools/gfx/d3d12/render-d3d12.cpp254
-rw-r--r--tools/gfx/open-gl/render-gl.cpp16
-rw-r--r--tools/gfx/render.h6
-rw-r--r--tools/gfx/vulkan/render-vk.cpp395
-rw-r--r--tools/gfx/vulkan/vk-api.h1
-rw-r--r--tools/render-test/shader-input-layout.cpp5
-rw-r--r--tools/render-test/shader-input-layout.h3
-rw-r--r--tools/render-test/shader-renderer-util.cpp26
9 files changed, 749 insertions, 123 deletions
diff --git a/tools/gfx/d3d11/render-d3d11.cpp b/tools/gfx/d3d11/render-d3d11.cpp
index cf2ae75e2..4eba4edaf 100644
--- a/tools/gfx/d3d11/render-d3d11.cpp
+++ b/tools/gfx/d3d11/render-d3d11.cpp
@@ -139,14 +139,66 @@ public:
class DescriptorSetLayoutImpl : public DescriptorSetLayout
{
public:
+ // Each descriptor set for the D3D11 renderer stores distinct
+ // arrays for each kind of shader-visible entity D3D11 understands:
+ // shader resource views (SRVs), unordered access views (UAVs),
+ // constant buffers (CBs), and samplers.
+ //
+ // (This description will ignore compiled image/sampler pairs,
+ // since they aren't really well supported at present)
+ //
+ // Each descriptor range in an input `DescriptorSetLayout::Desc`
+ // will map to a range of entries in one of those arrays, but
+ // in general there can be multiple `DescriptorSlotType`s that
+ // map to the same `D3D11DescriptorSlotType`.
+ //
+ // Each `RangeInfo` in a D3D11 descriptor set layout represents
+ // of of the descriptor slot ranges in the original `Desc`,
+ // and stores the information that is relevant to its layout
+ // in our D3D11 implementation.
+
struct RangeInfo
{
+ /// The type of descriptors in the range, in D3D11 terms (SRVs, UAVs, etc.)
D3D11DescriptorSlotType type;
+
+ /// The start index of this range in the relevant descriptor-type-specific array.
+ ///
+ /// Note: This is *not* the same as the index of the range, both because multiple
+ /// `DescriptorSlotType`s might map to the same array in the D3D11 implementation,
+ /// and also because a given range might store multiple descriptors (so a 3-texture
+ /// range that comes after a 5-texture range will have an `arrayIndex` of 5 but
+ /// a range index of 1).
+ ///
UInt arrayIndex;
+
+ /// For the case of a combined image/sampler pair, the `arrayIndex` is an index
+ /// into the array of SRVs, and we store a separate index into the array of
+ /// samplers.
+ ///
UInt pairedSamplerArrayIndex;
};
List<RangeInfo> m_ranges;
+ // Because D3D11 does not support root constants as they appear in
+ // D3D12 and Vulkan, we need to map root-constant ranges in the original `Desc`
+ // over to ordinary constant buffers. Each root-constant range (of whatever
+ // size) will map to a constant-buffer range of a single buffer.
+ //
+ // In order to be able to properly allocate/initialize these root constant
+ // buffers, we store additional information about them in a flattened array
+ // that only stores information for root constant ranges.
+
+ struct RootConstantRangeInfo
+ {
+ /// Index of the `RangeInfo` corresponding to this root-constant range
+ Index rangeIndex;
+
+ /// Size of the original root-constant range, in bytes.
+ UInt size;
+ };
+ List<RootConstantRangeInfo> m_rootConstantRanges;
+
UInt m_counts[int(D3D11DescriptorSlotType::CountOf)];
};
@@ -174,6 +226,13 @@ public:
UInt index,
ResourceView* textureView,
SamplerState* sampler) override;
+ virtual void setRootConstants(
+ UInt range,
+ UInt offset,
+ UInt size,
+ void const* data) override;
+
+ D3D11Renderer* m_renderer = nullptr;
RefPtr<DescriptorSetLayoutImpl> m_layout;
@@ -1762,6 +1821,7 @@ Result D3D11Renderer::createDescriptorSetLayout(const DescriptorSetLayout::Desc&
DescriptorSetLayoutImpl::RangeInfo rangeInfo;
+ UInt slotCount = rangeDesc.count;
switch(rangeDesc.type)
{
default:
@@ -1776,6 +1836,11 @@ Result D3D11Renderer::createDescriptorSetLayout(const DescriptorSetLayout::Desc&
rangeInfo.type = D3D11DescriptorSlotType::CombinedTextureSampler;
break;
+ case DescriptorSlotType::RootConstant:
+ // A root-constant range will be treated as if it were
+ // a constant-buffer range with a single buffer in it.
+ //
+ slotCount = 1;
case DescriptorSlotType::UniformBuffer:
case DescriptorSlotType::DynamicUniformBuffer:
rangeInfo.type = D3D11DescriptorSlotType::ConstantBuffer;
@@ -1803,17 +1868,31 @@ Result D3D11Renderer::createDescriptorSetLayout(const DescriptorSetLayout::Desc&
rangeInfo.arrayIndex = counts[srvTypeIndex];
rangeInfo.pairedSamplerArrayIndex = counts[samplerTypeIndex];
- counts[srvTypeIndex] += rangeDesc.count;
- counts[samplerTypeIndex] += rangeDesc.count;
+ counts[srvTypeIndex] += slotCount;
+ counts[samplerTypeIndex] += slotCount;
}
else
{
auto typeIndex = int(rangeInfo.type);
rangeInfo.arrayIndex = counts[typeIndex];
- counts[typeIndex] += rangeDesc.count;
+ counts[typeIndex] += slotCount;
}
+ Index rangeIndex = descriptorSetLayoutImpl->m_ranges.getCount();
descriptorSetLayoutImpl->m_ranges.add(rangeInfo);
+
+ if(rangeDesc.type == DescriptorSlotType::RootConstant)
+ {
+ // If the range represents a root constant range, then
+ // we need to also store the information we will need when
+ // allocating a constant buffer to provide backing storage
+ // for the range.
+ //
+ DescriptorSetLayoutImpl::RootConstantRangeInfo rootConstantRangeInfo;
+ rootConstantRangeInfo.rangeIndex = rangeIndex;
+ rootConstantRangeInfo.size = rangeDesc.count;
+ descriptorSetLayoutImpl->m_rootConstantRanges.add(rootConstantRangeInfo);
+ }
}
for(int ii = 0; ii < int(D3D11DescriptorSlotType::CountOf); ++ii)
@@ -1860,12 +1939,55 @@ Result D3D11Renderer::createDescriptorSet(DescriptorSetLayout* layout, Descripto
RefPtr<DescriptorSetImpl> descriptorSetImpl = new DescriptorSetImpl();
+ descriptorSetImpl->m_renderer = this;
descriptorSetImpl->m_layout = layoutImpl;
descriptorSetImpl->m_cbs .setCount(layoutImpl->m_counts[int(D3D11DescriptorSlotType::ConstantBuffer)]);
descriptorSetImpl->m_srvs .setCount(layoutImpl->m_counts[int(D3D11DescriptorSlotType::ShaderResourceView)]);
descriptorSetImpl->m_uavs .setCount(layoutImpl->m_counts[int(D3D11DescriptorSlotType::UnorderedAccessView)]);
descriptorSetImpl->m_samplers.setCount(layoutImpl->m_counts[int(D3D11DescriptorSlotType::Sampler)]);
+ // If the layout includes any root constant ranges, then
+ // we will need to allocate a constant buffer for each
+ // range to provide "backing storage" for its data.
+ //
+ for(auto rootConstantRange : layoutImpl->m_rootConstantRanges)
+ {
+ // The root constant range will refer to a descriptor slot
+ // range that represents a range with a single constant
+ // buffer in it. We need to grab that range so that we
+ // know what constant-buffer bindign slot to fill in.
+ //
+ auto rangeIndex = rootConstantRange.rangeIndex;
+ auto bufferRange = layoutImpl->m_ranges[rangeIndex];
+
+ // We will allocate the constant buffer that provides
+ // backing storage directly using D3D11 API calls,
+ // rather than allocate it as a buffer resource.
+ //
+ // TODO: We could revisit that decision if allocating
+ // a buffer resource proves easier down the line.
+
+ // Note: A D3D11 constant buffer must be a multiple of 16 bytes
+ // in size, so we will round up the allocation size to match
+ // the requirement.
+ //
+ UINT size = (UINT) rootConstantRange.size;
+ size = (size + 15) & ~15;
+
+ D3D11_BUFFER_DESC bufferDesc;
+ bufferDesc.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
+ bufferDesc.ByteWidth = size;
+ bufferDesc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
+ bufferDesc.MiscFlags = 0;
+ bufferDesc.StructureByteStride = 0;
+ bufferDesc.Usage = D3D11_USAGE_DYNAMIC;
+
+ Slang::ComPtr<ID3D11Buffer> buffer;
+ SLANG_RETURN_ON_FAIL(m_device->CreateBuffer(&bufferDesc, nullptr, buffer.writeRef()));
+
+ descriptorSetImpl->m_cbs[bufferRange.arrayIndex] = buffer;
+ }
+
*outDescriptorSet = descriptorSetImpl.detach();
return SLANG_OK;
}
@@ -2277,6 +2399,44 @@ void D3D11Renderer::DescriptorSetImpl::setCombinedTextureSampler(
m_srvs[rangeInfo.pairedSamplerArrayIndex + index] = srvImpl->m_srv;
}
+void D3D11Renderer::DescriptorSetImpl::setRootConstants(
+ UInt range,
+ UInt offset,
+ UInt size,
+ void const* data)
+{
+ // The `range` parameter represents the index of a descriptor
+ // slot range in the layout of this descriptor set.
+ //
+ // A root constant range will have been translated into
+ // a constnat buffer range at creation time for the layout.
+ //
+ auto& rangeInfo = m_layout->m_ranges[range];
+ assert(rangeInfo.type == D3D11DescriptorSlotType::ConstantBuffer);
+
+ // At the time the descriptor set was allocated, a
+ // constant buffer will have been created and bound
+ // into `m_cbs` to provide backing storage for the
+ // root constant range.
+ //
+ auto dxBuffer = m_cbs[rangeInfo.arrayIndex];
+ auto dxContext = m_renderer->m_immediateContext;
+
+ // Once we have the buffer that provides backing
+ // storage we simply need to map it and write
+ // the user-provided data into it.
+ //
+ D3D11_MAPPED_SUBRESOURCE mapped;
+ HRESULT hr = dxContext->Map(dxBuffer, 0, D3D11_MAP_WRITE_NO_OVERWRITE, 0, &mapped);
+ if( FAILED(hr) )
+ {
+ SLANG_ASSERT(!"failed to map backing storage for root constant range");
+ return;
+ }
+ memcpy((char*)mapped.pData + offset, data, size);
+ dxContext->Unmap(dxBuffer, 0);
+}
+
void D3D11Renderer::setDescriptorSet(PipelineType pipelineType, PipelineLayout* layout, UInt index, DescriptorSet* descriptorSet)
{
auto pipelineLayoutImpl = (PipelineLayoutImpl*)layout;
diff --git a/tools/gfx/d3d12/render-d3d12.cpp b/tools/gfx/d3d12/render-d3d12.cpp
index ad7b898f5..ba9bde63a 100644
--- a/tools/gfx/d3d12/render-d3d12.cpp
+++ b/tools/gfx/d3d12/render-d3d12.cpp
@@ -135,6 +135,7 @@ protected:
virtual void setRootConstantBufferView(int index, D3D12_GPU_VIRTUAL_ADDRESS gpuBufferLocation) = 0;
virtual void setRootDescriptorTable(int index, D3D12_GPU_DESCRIPTOR_HANDLE BaseDescriptor) = 0;
virtual void setRootSignature(ID3D12RootSignature* rootSignature) = 0;
+ virtual void setRootConstants(Index rootParamIndex, Index dstOffsetIn32BitValues, Index countOf32BitValues, void const* srcData) = 0;
};
struct FrameInfo
@@ -301,19 +302,90 @@ protected:
class DescriptorSetLayoutImpl : public DescriptorSetLayout
{
public:
+ // A "descriptor set" at the level of the `Renderer` API
+ // is similar to a D3D12 "descriptor table," but the match
+ // isn't perfect for a few reasons:
+ //
+ // * Our descriptor sets can contain both resources and
+ // samplers, while D3D12 descriptor tables are always
+ // resource-only or sampler-only.
+ //
+ // * Our descriptor sets can include root constant ranges,
+ // while under D3D12 a root constant range is thought
+ // of as belonging to the root signature directly.
+ //
+ // We navigate this mismatch in our implementation with
+ // the idea that a single `Renderer`-level descriptor set
+ // maps to zero or more D3D12 root parameters, which can
+ // include:
+ //
+ // * Zero or one root parameter that is used to bind a
+ // descriptor table of resources.
+ //
+ // * Zero or one root parameter that is used to bind a
+ // descriptor table of samplers.
+ //
+ // * Zero or more root parameters that represent ranges
+ // of root constants.
+ //
+ // Binding a descriptor set will band all of its associated
+ // root parameters.
+ //
+ // (Note: this representation could in theory be extended
+ // to also support root resources that are not table-bound)
+ //
+ // Each descriptor slot range in the original `Desc` maps
+ // to a single `RangeInfo` stored here, which captures
+ // derived information used when binding values into
+ // a descriptor table.
+ //
struct RangeInfo
{
+ /// The type of descriptor slot in the original `Desc`
DescriptorSlotType type;
+
+ /// The number of slots in this range
Int count;
+
+ /// The start index of this range in the appropriate type-specific array.
+ ///
+ /// E.g., for a sampler slot range, this would be the start index
+ /// for the range in the descriptor table used to store all the samplers.
Int arrayIndex;
};
-
List<RangeInfo> m_ranges;
+ // We need to track additional information about
+ // root cosntant ranges that isn't captured in
+ // `RangeInfo`, so we store an additional array
+ // that just captures the root constant ranges.
+ //
+ struct RootConstantRangeInfo
+ {
+ /// The D3D12 "root parameter index" for this range
+ Int rootParamIndex;
+
+ /// The size in bytes of this range
+ Int size;
+
+ /// The byte offset of this range's data in the backing storage for a descriptor set
+ Int offset;
+ };
+ List<RootConstantRangeInfo> m_rootConstantRanges;
+
+ /// The total size (in bytes) of root constant data across all contained ranged.
+ Int m_rootConstantDataSize = 0;
+
+ /// The D3D12-format descriptions of the descriptor ranges in this set
List<D3D12_DESCRIPTOR_RANGE> m_dxRanges;
+
+ /// The D3D12-format description of the root parameters introduced by this set
List<D3D12_ROOT_PARAMETER> m_dxRootParameters;
+ /// How many resource slots (total) were introduced by ranges?
Int m_resourceCount;
+
+ /// How many sampler slots (total) were introduce by ranges?
Int m_samplerCount;
};
@@ -335,6 +407,11 @@ protected:
UInt index,
ResourceView* textureView,
SamplerState* sampler) override;
+ virtual void setRootConstants(
+ UInt range,
+ UInt offset,
+ UInt size,
+ void const* data) override;
D3D12Renderer* m_renderer = nullptr; ///< Weak pointer - must be because if set on Renderer, will have a circular reference
RefPtr<DescriptorSetLayoutImpl> m_layout;
@@ -355,6 +432,9 @@ protected:
//
List<RefPtr<RefObject>> m_resourceObjects;
List<RefPtr<SamplerStateImpl>> m_samplerObjects;
+
+ /// Backing storage for root constant ranges in this descriptor set.
+ List<char> m_rootConstantData;
};
@@ -436,6 +516,14 @@ protected:
{
m_commandList->SetGraphicsRootSignature(rootSignature);
}
+ void setRootConstants(
+ Index rootParamIndex,
+ Index dstOffsetIn32BitValues,
+ Index countOf32BitValues,
+ void const* srcData) override
+ {
+ m_commandList->SetGraphicsRoot32BitConstants(UINT(rootParamIndex), UINT(countOf32BitValues), srcData, UINT(dstOffsetIn32BitValues));
+ }
GraphicsSubmitter(ID3D12GraphicsCommandList* commandList):
m_commandList(commandList)
@@ -459,6 +547,14 @@ protected:
{
m_commandList->SetComputeRootSignature(rootSignature);
}
+ void setRootConstants(
+ Index rootParamIndex,
+ Index dstOffsetIn32BitValues,
+ Index countOf32BitValues,
+ void const* srcData) override
+ {
+ m_commandList->SetComputeRoot32BitConstants(UINT(rootParamIndex), UINT(countOf32BitValues), srcData, UINT(dstOffsetIn32BitValues));
+ }
ComputeSubmitter(ID3D12GraphicsCommandList* commandList) :
m_commandList(commandList)
@@ -1304,6 +1400,16 @@ Result D3D12Renderer::_bindRenderState(PipelineStateImpl* pipelineStateImpl, ID3
submitter->setRootDescriptorTable(int(rootParameterIndex++), gpuHeap.getGpuHandle(gpuDescriptorTable));
}
}
+ if(auto rootConstantRangeCount = descriptorSetLayout->m_rootConstantRanges.getCount())
+ {
+ auto srcData = descriptorSet->m_rootConstantData.getBuffer();
+
+ for(auto& rootConstantRangeInfo : descriptorSetLayout->m_rootConstantRanges)
+ {
+ auto countOf32bitValues = rootConstantRangeInfo.size / sizeof(uint32_t);
+ submitter->setRootConstants(rootConstantRangeInfo.rootParamIndex, 0, countOf32bitValues, srcData + rootConstantRangeInfo.offset);
+ }
+ }
}
return SLANG_OK;
@@ -3092,6 +3198,35 @@ void D3D12Renderer::DescriptorSetImpl::setCombinedTextureSampler(
D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER);
}
+void D3D12Renderer::DescriptorSetImpl::setRootConstants(
+ UInt range,
+ UInt offset,
+ UInt size,
+ void const* data)
+{
+ // The `range` parameter is the index of the range in
+ // the original `DescriptorSetLayout::Desc`, which must
+ // have been a root-constant range for this call to be
+ // valid.
+ //
+ SLANG_ASSERT(range < m_layout->m_ranges.getCount());
+ auto& rangeInfo = m_layout->m_ranges[range];
+ SLANG_ASSERT(rangeInfo.type == DescriptorSlotType::RootConstant);
+
+ // The `arrayIndex` in that descriptor slot range is the "flat"
+ // index of the root constant range that the user is trying
+ // to write into. The root constant range represents a range
+ // of bytes in the `m_rootConstantData` buffer.
+ //
+ 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 <= rootConstantRangeInfo.size);
+
+ memcpy((char*)m_rootConstantData.getBuffer() + rootConstantRangeInfo.offset + offset, data, size);
+}
+
void D3D12Renderer::setDescriptorSet(PipelineType pipelineType, PipelineLayout* layout, UInt index, DescriptorSet* descriptorSet)
{
// In D3D12, unlike Vulkan, binding a root signature invalidates *all* descriptor table
@@ -3174,6 +3309,11 @@ Result D3D12Renderer::createDescriptorSetLayout(const DescriptorSetLayout::Desc&
combinedRangeCount++;
break;
+ case DescriptorSlotType::RootConstant:
+ // A root constant slot range doesn't contribute
+ // to the toal number of resources or samplers.
+ break;
+
default:
dedicatedResourceCount += rangeDesc.count;
dedicatedResourceRangeCount++;
@@ -3270,6 +3410,54 @@ Result D3D12Renderer::createDescriptorSetLayout(const DescriptorSetLayout::Desc&
rangeInfo.arrayIndex = combinedCounter;
combinedCounter += rangeInfo.count;
break;
+
+ case DescriptorSlotType::RootConstant:
+ {
+ // A root constant range is a bit different than
+ // the other cases because it does *not* introduce
+ // any descriptor rangess into D3D12 descriptor tables,
+ // while it *does* introduce a distinct root parameter.
+ //
+ D3D12_ROOT_PARAMETER dxRootParameter = {};
+ dxRootParameter.ParameterType = D3D12_ROOT_PARAMETER_TYPE_32BIT_CONSTANTS;
+ dxRootParameter.Constants.Num32BitValues = UINT(rangeInfo.count) / UINT(sizeof(uint32_t));
+
+ // When binding the data for the range to the pipeline,
+ // we will need to know the "root parameter index" in
+ // order to identify the range to D3D12.
+ //
+ auto rootParameterIndex = descriptorSetLayoutImpl->m_dxRootParameters.getCount();
+ descriptorSetLayoutImpl->m_dxRootParameters.add(dxRootParameter);
+
+ // We need to create and store additional tracking data
+ // to remember this root constant range and how to set it.
+ //
+ // The additional data includes the D3D12 root parameter index,
+ // and the size of the range (in bytes).
+ //
+ DescriptorSetLayoutImpl::RootConstantRangeInfo rootConstantRangeInfo;
+ rootConstantRangeInfo.rootParamIndex = rootParameterIndex;
+ rootConstantRangeInfo.size = rangeDesc.count;
+ //
+ // We also need to compute an offset for the data in the backing
+ // storage of a particular descriptor set; we also use this as
+ // a place to update the total size of the root constant data.
+ //
+ // Note: We don't deal with alignment issues here. D3D12 requires
+ // all root-constant data to be in multiples of 4 bytes and to be
+ // 4-byte aligned, and that should mean that alignment works
+ // out without extra effort on our part.
+ //
+ rootConstantRangeInfo.offset = descriptorSetLayoutImpl->m_rootConstantDataSize;
+ descriptorSetLayoutImpl->m_rootConstantDataSize += rootConstantRangeInfo.size;
+
+ auto rootConstantIndex = descriptorSetLayoutImpl->m_rootConstantRanges.getCount();
+ descriptorSetLayoutImpl->m_rootConstantRanges.add(rootConstantRangeInfo);
+
+ rangeInfo.arrayIndex = rootConstantIndex;
+ rangeInfo.count = 1;
+ }
+ break;
}
descriptorSetLayoutImpl->m_ranges.add(rangeInfo);
@@ -3326,6 +3514,36 @@ Result D3D12Renderer::createDescriptorSetLayout(const DescriptorSetLayout::Desc&
dxPairedSamplerRangeIndex = totalResourceRangeCount + combinedRangeCounter;
combinedRangeCounter++;
break;
+
+
+ case DescriptorSlotType::RootConstant:
+ {
+ // A root constant range consumes a `b` register binding
+ // under the D3D12 rules, because it is represented as
+ // a `cbuffer` or `ConstantBuffer` declaration in HLSL.
+ //
+ // We need to allocate a register for the root constant
+ // buffer here to make the bindings line up, but we
+ // will skip out of the rest of the logic (via a `continue`
+ // so that this range doesn't turn into a descriptor
+ // range in one of the D3D12 descriptor tables.
+ //
+ UInt bindingIndex = cbvCounter; cbvCounter += bindingCount;
+
+ auto rootConstantRangeIndex = descriptorSetLayoutImpl->m_ranges[rr].arrayIndex;
+ auto rootParamIndex = descriptorSetLayoutImpl->m_rootConstantRanges[rootConstantRangeIndex].rootParamIndex;
+
+ // The root constant range is represented in the D3D12
+ // root signature as its own root parameter (not in any
+ // table), and that root parameter needs to be set up
+ // to reference the correct binding space and index.
+ //
+ auto& dxRootParam = descriptorSetLayoutImpl->m_dxRootParameters[rootParamIndex];
+ dxRootParam.Constants.RegisterSpace = UINT(bindingSpace);
+ dxRootParam.Constants.ShaderRegister = UINT(bindingIndex);
+ continue;
+ }
+ break;
}
D3D12_DESCRIPTOR_RANGE& dxRange = descriptorSetLayoutImpl->m_dxRanges[dxRangeIndex];
@@ -3509,6 +3727,15 @@ Result D3D12Renderer::createPipelineLayout(const PipelineLayout::Desc& desc, Pip
auto& descriptorSetInfo = desc.descriptorSets[dd];
auto descriptorSetLayout = (DescriptorSetLayoutImpl*) descriptorSetInfo.layout;
+ // For now we assume that the register space used for
+ // logical descriptor set #N will be space N.
+ //
+ // Note: this is the same assumption made in the first
+ // loop, and any change/fix will need to be made to
+ // both places consistently.
+ //
+ UInt bindingSpace = dd;
+
// Copy root parameter information from the set layout to our
// overall pipeline layout.
for( auto setRootParameter : descriptorSetLayout->m_dxRootParameters )
@@ -3516,14 +3743,27 @@ Result D3D12Renderer::createPipelineLayout(const PipelineLayout::Desc& desc, Pip
auto& rootParameter = rootParameters[rootParameterCount++];
rootParameter = setRootParameter;
- // In the case where this parameter is a descriptor table, it
- // needs to point into our array of ranges (with offsets applied),
- // so we will fix up those pointers here.
- //
- if(rootParameter.ParameterType == D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE)
+ switch( rootParameter.ParameterType )
{
+ default:
+ break;
+
+ case D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE:
+ // In the case where this parameter is a descriptor table, it
+ // needs to point into our array of ranges (with offsets applied),
+ // so we will fix up those pointers here.
+ //
rootParameter.DescriptorTable.pDescriptorRanges = rangePtr;
rangePtr += rootParameter.DescriptorTable.NumDescriptorRanges;
+ break;
+
+ case D3D12_ROOT_PARAMETER_TYPE_32BIT_CONSTANTS:
+ // In the case where the parameter is a root constant range,
+ // it needs to reflect the register space for the descriptor
+ // set, as computed based on sets specified.
+ //
+ rootParameter.Constants.RegisterSpace = UINT(bindingSpace);
+ break;
}
}
}
@@ -3596,6 +3836,8 @@ Result D3D12Renderer::createDescriptorSet(DescriptorSetLayout* layout, Descripto
descriptorSetImpl->m_samplerObjects.setCount(samplerCount);
}
+ descriptorSetImpl->m_rootConstantData.setCount(layoutImpl->m_rootConstantDataSize);
+
*outDescriptorSet = descriptorSetImpl.detach();
return SLANG_OK;
}
diff --git a/tools/gfx/open-gl/render-gl.cpp b/tools/gfx/open-gl/render-gl.cpp
index 91c31b71d..ee3977a74 100644
--- a/tools/gfx/open-gl/render-gl.cpp
+++ b/tools/gfx/open-gl/render-gl.cpp
@@ -275,6 +275,11 @@ public:
UInt index,
ResourceView* textureView,
SamplerState* sampler) override;
+ virtual void setRootConstants(
+ UInt range,
+ UInt offset,
+ UInt size,
+ void const* data) override;
RefPtr<DescriptorSetLayoutImpl> m_layout;
List<RefPtr<BufferResourceImpl>> m_constantBuffers;
@@ -1304,6 +1309,15 @@ void GLRenderer::DescriptorSetImpl::setCombinedTextureSampler(
m_samplers[arrayIndex] = samplerImpl;
}
+void GLRenderer::DescriptorSetImpl::setRootConstants(
+ UInt range,
+ UInt offset,
+ UInt size,
+ void const* data)
+{
+ SLANG_UNEXPECTED("unimplemented: setRootConstants for GlRenderer");
+}
+
void GLRenderer::setDescriptorSet(PipelineType pipelineType, PipelineLayout* layout, UInt index, DescriptorSet* descriptorSet)
{
auto descriptorSetImpl = (DescriptorSetImpl*)descriptorSet;
@@ -1339,6 +1353,8 @@ Result GLRenderer::createDescriptorSetLayout(const DescriptorSetLayout::Desc& de
glSlotType = GLDescriptorSlotType::CombinedTextureSampler;
break;
+ case DescriptorSlotType::RootConstant:
+ rangeDesc.count = 1;
case DescriptorSlotType::UniformBuffer:
case DescriptorSlotType::DynamicUniformBuffer:
glSlotType = GLDescriptorSlotType::ConstantBuffer;
diff --git a/tools/gfx/render.h b/tools/gfx/render.h
index 1aa932e0e..051b19742 100644
--- a/tools/gfx/render.h
+++ b/tools/gfx/render.h
@@ -519,6 +519,7 @@ enum class DescriptorSlotType
DynamicUniformBuffer,
DynamicStorageBuffer,
InputAttachment,
+ RootConstant,
};
class DescriptorSetLayout : public Slang::RefObject
@@ -602,6 +603,11 @@ public:
UInt index,
ResourceView* textureView,
SamplerState* sampler) = 0;
+ virtual void setRootConstants(
+ UInt range,
+ UInt offset,
+ UInt size,
+ void const* data) = 0;
};
enum class StencilOp : uint8_t
diff --git a/tools/gfx/vulkan/render-vk.cpp b/tools/gfx/vulkan/render-vk.cpp
index 53c210a20..b9cc82469 100644
--- a/tools/gfx/vulkan/render-vk.cpp
+++ b/tools/gfx/vulkan/render-vk.cpp
@@ -91,6 +91,9 @@ public:
protected:
+ /// Flush state from descriptor set bindings into `commandBuffer`
+ void _flushBindingState(VkCommandBuffer commandBuffer, VkPipelineBindPoint pipelineBindPoint);
+
class Buffer
{
public:
@@ -280,11 +283,57 @@ public:
VkDescriptorSetLayout m_descriptorSetLayout = VK_NULL_HANDLE;
VkDescriptorPool m_descriptorPool = VK_NULL_HANDLE;
+ // 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
{
- VkDescriptorType descriptorType;
+ /// The type of descriptor slot range from the original `Desc`
+ DescriptorSlotType type;
+
+ /// The start index of the range in the appropriate type-specific array
+ Index arrayIndex;
+
+ /// The equivalent Vulkan descriptor type, where applicable
+ VkDescriptorType vkDescriptorType;
+
+ /// The Vulkan `binding` index for this range
+ uint32_t vkBindingIndex;
};
List<RangeInfo> m_ranges;
+
+ // 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.
+ //
+ struct RootConstantRangeInfo
+ {
+ /// The offset of the range's data in the backing storage.
+ Index offset;
+
+ /// The size of the range's data.
+ Index size;
+ };
+ Slang::List<RootConstantRangeInfo> m_rootConstantRanges;
+
+ /// The total size, in bytes, or root constant data for this descriptor set.
+ uint32_t m_rootConstantDataSize = 0;
+
+ /// The total number of reference counted objects that can be bound
+ /// to descriptor sets described by this layout.
+ ///
+ Index m_totalBoundObjectCount = 0;
};
class PipelineLayoutImpl : public PipelineLayout
@@ -306,28 +355,14 @@ public:
VulkanApi const* m_api;
VkPipelineLayout m_pipelineLayout = VK_NULL_HANDLE;
UInt m_descriptorSetCount = 0;
+
+ /// For each descriptor set, stores the start offset of that set's root constant data in the pipeline layout
+ List<uint32_t> m_descriptorSetRootConstantOffsets;
};
class DescriptorSetImpl : public DescriptorSet
{
public:
- // Record the view binding
- struct Binding
- {
- enum class Type : uint8_t
- {
- Unknown,
- ResourceView,
- SamplerState,
- BufferResource,
- CountOf,
- };
- Type type;
- uint32_t range;
- uint32_t index;
- RefPtr<RefObject> obj;
- };
-
DescriptorSetImpl(VKRenderer* renderer)
: m_renderer(renderer)
{
@@ -345,15 +380,21 @@ public:
UInt index,
ResourceView* textureView,
SamplerState* sampler) override;
-
- static Binding::Type _getBindingType(RefObject* ptr);
- void _setBinding(Binding::Type type, UInt range, UInt index, RefObject* ptr);
+ virtual void setRootConstants(
+ UInt range,
+ UInt offset,
+ UInt size,
+ void const* data) override;
VKRenderer* m_renderer = nullptr; ///< Weak pointer, can't be strong, because if set will become circular reference
RefPtr<DescriptorSetLayoutImpl> m_layout;
VkDescriptorSet m_descriptorSet = VK_NULL_HANDLE;
- List<Binding> m_bindings; ///< Records entities are bound to this descriptor set, and keeps the associated resources/views/state in scope
+ /// Records entities that are bound to this descriptor set, and keeps the associated resources/views/state in scope
+ List<RefPtr<RefObject>> m_boundObjects;
+
+ /// Backing storage for root constant ranges belonging to this descriptor set
+ List<char> m_rootConstantData;
};
struct BoundVertexBuffer
@@ -2019,6 +2060,46 @@ void VKRenderer::setPipelineState(PipelineType pipelineType, PipelineState* stat
m_currentPipeline = (PipelineStateImpl*)state;
}
+void VKRenderer::_flushBindingState(VkCommandBuffer commandBuffer, VkPipelineBindPoint pipelineBindPoint)
+{
+ auto pipeline = m_currentPipeline;
+
+ // We start by binding the pipeline state.
+ //
+ m_api.vkCmdBindPipeline(commandBuffer, pipelineBindPoint, pipeline->m_pipeline);
+
+ // Next we bind all the descriptor sets that were set in the `VKRenderer`.
+ //
+ auto pipelineLayoutImpl = pipeline->m_pipelineLayout.Ptr();
+ auto vkPipelineLayout = pipelineLayoutImpl->m_pipelineLayout;
+ auto descriptorSetCount = pipelineLayoutImpl->m_descriptorSetCount;
+ m_api.vkCmdBindDescriptorSets(commandBuffer, 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];
+
+ m_api.vkCmdPushConstants(commandBuffer, vkPipelineLayout, VK_SHADER_STAGE_ALL, offset, size, data);
+ }
+}
+
void VKRenderer::draw(UInt vertexCount, UInt startVertex = 0)
{
auto pipeline = m_currentPipeline;
@@ -2033,13 +2114,7 @@ void VKRenderer::draw(UInt vertexCount, UInt startVertex = 0)
// Also create descriptor sets based on the given pipeline layout
VkCommandBuffer commandBuffer = m_deviceQueue.getCommandBuffer();
- m_api.vkCmdBindPipeline(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline->m_pipeline);
-
- auto pipelineLayoutImpl = pipeline->m_pipelineLayout.Ptr();
- m_api.vkCmdBindDescriptorSets(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayoutImpl->m_pipelineLayout,
- 0, uint32_t(pipelineLayoutImpl->m_descriptorSetCount),
- &m_currentDescriptorSets[0],
- 0, nullptr);
+ _flushBindingState(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS);
// Bind the vertex buffer
if (m_boundVertexBuffers.getCount() > 0 && m_boundVertexBuffers[0].m_buffer)
@@ -2073,13 +2148,7 @@ void VKRenderer::dispatchCompute(int x, int y, int z)
// Also create descriptor sets based on the given pipeline layout
VkCommandBuffer commandBuffer = m_deviceQueue.getCommandBuffer();
- m_api.vkCmdBindPipeline(commandBuffer, VK_PIPELINE_BIND_POINT_COMPUTE, pipeline->m_pipeline);
-
- auto pipelineLayoutImpl = pipeline->m_pipelineLayout.Ptr();
- m_api.vkCmdBindDescriptorSets(commandBuffer, VK_PIPELINE_BIND_POINT_COMPUTE, pipelineLayoutImpl->m_pipelineLayout,
- 0, uint32_t(pipelineLayoutImpl->m_descriptorSetCount),
- &m_currentDescriptorSets[0],
- 0, nullptr);
+ _flushBindingState(commandBuffer, VK_PIPELINE_BIND_POINT_COMPUTE);
m_api.vkCmdDispatch(commandBuffer, x, y, z);
}
@@ -2284,10 +2353,61 @@ Result VKRenderer::createDescriptorSetLayout(const DescriptorSetLayout::Desc& de
{
auto& srcRange = desc.slotRanges[rr];
+ if(srcRange.type == DescriptorSlotType::RootConstant)
+ {
+ // 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 reprsent 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;
+ }
+
+ // 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(rr);
+ dstBinding.binding = uint32_t(bindingIndex);
dstBinding.descriptorType = dstDescriptorType;
dstBinding.descriptorCount = uint32_t(srcRange.count);
dstBinding.stageFlags = VK_SHADER_STAGE_ALL;
@@ -2297,8 +2417,20 @@ Result VKRenderer::createDescriptorSetLayout(const DescriptorSetLayout::Desc& de
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.descriptorType = dstDescriptorType;
+ rangeInfo.type = srcRange.type;
+ rangeInfo.vkDescriptorType = dstDescriptorType;
+ rangeInfo.vkBindingIndex = bindingIndex;
+ rangeInfo.arrayIndex = boundObjectArrayIndex;
descriptorSetLayoutImpl->m_ranges.add(rangeInfo);
}
@@ -2344,15 +2476,47 @@ Result VKRenderer::createPipelineLayout(const PipelineLayout::Desc& desc, Pipeli
UInt descriptorSetCount = desc.descriptorSetCount;
VkDescriptorSetLayout descriptorSetLayouts[kMaxDescriptorSets];
+ uint32_t descriptorSetRootConstantOffsets[kMaxDescriptorSets];
+ uint32_t totalRootConstantSize = 0;
for(UInt ii = 0; ii < descriptorSetCount; ++ii)
{
- descriptorSetLayouts[ii] = ((DescriptorSetLayoutImpl*) desc.descriptorSets[ii].layout)->m_descriptorSetLayout;
+ 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));
@@ -2360,6 +2524,12 @@ Result VKRenderer::createPipelineLayout(const PipelineLayout::Desc& desc, Pipeli
pipelineLayoutImpl->m_pipelineLayout = pipelineLayout;
pipelineLayoutImpl->m_descriptorSetCount = descriptorSetCount;
+ for(UInt ii = 0; ii < descriptorSetCount; ++ii)
+ {
+ pipelineLayoutImpl->m_descriptorSetRootConstantOffsets.add(
+ descriptorSetRootConstantOffsets[ii]);
+ }
+
*outLayout = pipelineLayoutImpl.detach();
return SLANG_OK;
}
@@ -2379,73 +2549,23 @@ Result VKRenderer::createDescriptorSet(DescriptorSetLayout* layout, DescriptorSe
RefPtr<DescriptorSetImpl> descriptorSetImpl = new DescriptorSetImpl(this);
descriptorSetImpl->m_layout = layoutImpl;
descriptorSetImpl->m_descriptorSet = descriptorSet;
- *outDescriptorSet = descriptorSetImpl.detach();
- return SLANG_OK;
-}
-/* static */VKRenderer::DescriptorSetImpl::Binding::Type VKRenderer::DescriptorSetImpl::_getBindingType(RefObject* ptr)
-{
- typedef Binding::Type Type;
+ descriptorSetImpl->m_rootConstantData.setCount(layoutImpl->m_rootConstantDataSize);
+ descriptorSetImpl->m_boundObjects.setCount(layoutImpl->m_totalBoundObjectCount);
- if (ptr)
- {
- if (dynamic_cast<ResourceView*>(ptr))
- {
- return Type::ResourceView;
- }
- else if (dynamic_cast<BufferResource*>(ptr))
- {
- return Type::BufferResource;
- }
- else if (dynamic_cast<SamplerState*>(ptr))
- {
- return Type::SamplerState;
- }
- }
- return Type::Unknown;
-}
-
-void VKRenderer::DescriptorSetImpl::_setBinding(Binding::Type type, UInt range, UInt index, RefObject* ptr)
-{
- SLANG_ASSERT(ptr == nullptr || _getBindingType(ptr) == type);
-
- const Index numBindings = m_bindings.getCount();
- for (Index i = 0; i < numBindings; ++i)
- {
- Binding& binding = m_bindings[i];
-
- if (binding.type == type && binding.range == uint32_t(range) && binding.index == uint32_t(index))
- {
- if (ptr)
- {
- binding.obj = ptr;
- }
- else
- {
- m_bindings.removeAt(i);
- }
-
- return;
- }
- }
-
- // If an entry is not found, and we have a pointer, create an entry
- if (ptr)
- {
- Binding binding;
- binding.type = type;
- binding.range = uint32_t(range);
- binding.index = uint32_t(index);
- binding.obj = ptr;
-
- m_bindings.add(binding);
- }
+ *outDescriptorSet = descriptorSetImpl.detach();
+ return SLANG_OK;
}
void VKRenderer::DescriptorSetImpl::setConstantBuffer(UInt range, UInt index, BufferResource* 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;
@@ -2453,19 +2573,24 @@ void VKRenderer::DescriptorSetImpl::setConstantBuffer(UInt range, UInt index, Bu
VkWriteDescriptorSet writeInfo = { VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET };
writeInfo.dstSet = m_descriptorSet;
- writeInfo.dstBinding = uint32_t(range);
+ writeInfo.dstBinding = uint32_t(bindingIndex);
writeInfo.dstArrayElement = uint32_t(index);
writeInfo.descriptorCount = 1;
- writeInfo.descriptorType = m_layout->m_ranges[range].descriptorType;
+ writeInfo.descriptorType = rangeInfo.vkDescriptorType;
writeInfo.pBufferInfo = &bufferInfo;
m_renderer->m_api.vkUpdateDescriptorSets(m_renderer->m_device, 1, &writeInfo, 0, nullptr);
-
- _setBinding(Binding::Type::BufferResource, range, index, buffer);
+ m_boundObjects[boundObjectIndex] = buffer;
}
void VKRenderer::DescriptorSetImpl::setResource(UInt range, UInt index, ResourceView* view)
{
+ 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;
switch (viewImpl->m_type)
{
@@ -2479,10 +2604,10 @@ void VKRenderer::DescriptorSetImpl::setResource(UInt range, UInt index, Resource
VkWriteDescriptorSet writeInfo = { VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET };
writeInfo.dstSet = m_descriptorSet;
- writeInfo.dstBinding = uint32_t(range);
+ writeInfo.dstBinding = uint32_t(bindingIndex);
writeInfo.dstArrayElement = uint32_t(index);
writeInfo.descriptorCount = 1;
- writeInfo.descriptorType = m_layout->m_ranges[range].descriptorType;
+ writeInfo.descriptorType = descriptorType;
writeInfo.pImageInfo = &imageInfo;
m_renderer->m_api.vkUpdateDescriptorSets(m_renderer->m_device, 1, &writeInfo, 0, nullptr);
@@ -2495,10 +2620,10 @@ void VKRenderer::DescriptorSetImpl::setResource(UInt range, UInt index, Resource
VkWriteDescriptorSet writeInfo = { VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET };
writeInfo.dstSet = m_descriptorSet;
- writeInfo.dstBinding = uint32_t(range);
+ writeInfo.dstBinding = uint32_t(bindingIndex);
writeInfo.dstArrayElement = uint32_t(index);
writeInfo.descriptorCount = 1;
- writeInfo.descriptorType = m_layout->m_ranges[range].descriptorType;
+ writeInfo.descriptorType = descriptorType;
writeInfo.pTexelBufferView = &bufferViewImpl->m_view;
m_renderer->m_api.vkUpdateDescriptorSets(m_renderer->m_device, 1, &writeInfo, 0, nullptr);
@@ -2516,25 +2641,32 @@ void VKRenderer::DescriptorSetImpl::setResource(UInt range, UInt index, Resource
VkWriteDescriptorSet writeInfo = { VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET };
writeInfo.dstSet = m_descriptorSet;
- writeInfo.dstBinding = uint32_t(range);
+ writeInfo.dstBinding = uint32_t(bindingIndex);
writeInfo.dstArrayElement = uint32_t(index);
writeInfo.descriptorCount = 1;
- writeInfo.descriptorType = m_layout->m_ranges[range].descriptorType;
+ writeInfo.descriptorType = descriptorType;
writeInfo.pBufferInfo = &bufferInfo;
m_renderer->m_api.vkUpdateDescriptorSets(m_renderer->m_device, 1, &writeInfo, 0, nullptr);
}
break;
-
}
- _setBinding(Binding::Type::ResourceView, range, index, view);
+ m_boundObjects[boundObjectIndex] = view;
}
void VKRenderer::DescriptorSetImpl::setSampler(UInt range, UInt index, SamplerState* 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;
+
+ // TODO: Actually bind it!
- _setBinding(Binding::Type::SamplerState, range, index, sampler);
+ m_boundObjects[boundObjectIndex] = sampler;
}
void VKRenderer::DescriptorSetImpl::setCombinedTextureSampler(
@@ -2543,9 +2675,46 @@ void VKRenderer::DescriptorSetImpl::setCombinedTextureSampler(
ResourceView* textureView,
SamplerState* 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] = textureView;
+ m_boundObjects[boundObjectIndex + 1] = sampler;
+}
+
+void VKRenderer::DescriptorSetImpl::setRootConstants(
+ UInt range,
+ UInt offset,
+ UInt size,
+ void const* data)
+{
+ // 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 <= rootConstantRangeInfo.size);
- _setBinding(Binding::Type::SamplerState, range, index, sampler);
- _setBinding(Binding::Type::ResourceView, range, index, textureView);
+ memcpy(m_rootConstantData.getBuffer() + rootConstantRangeInfo.offset + offset, data, size);
}
void VKRenderer::setDescriptorSet(PipelineType pipelineType, PipelineLayout* layout, UInt index, DescriptorSet* descriptorSet)
diff --git a/tools/gfx/vulkan/vk-api.h b/tools/gfx/vulkan/vk-api.h
index 6f12d5ddb..58bcc89fd 100644
--- a/tools/gfx/vulkan/vk-api.h
+++ b/tools/gfx/vulkan/vk-api.h
@@ -84,6 +84,7 @@ namespace gfx {
x(vkCmdEndRenderPass) \
x(vkCmdPipelineBarrier) \
x(vkCmdCopyBufferToImage)\
+ x(vkCmdPushConstants) \
\
x(vkCreateFence) \
x(vkDestroyFence) \
diff --git a/tools/render-test/shader-input-layout.cpp b/tools/render-test/shader-input-layout.cpp
index 4c68899ef..5e487f1ad 100644
--- a/tools/render-test/shader-input-layout.cpp
+++ b/tools/render-test/shader-input-layout.cpp
@@ -140,6 +140,11 @@ namespace renderer_test
entry.type = ShaderInputType::Buffer;
entry.bufferDesc.type = InputBufferType::ConstantBuffer;
}
+ else if (parser.LookAhead("root_constants"))
+ {
+ entry.type = ShaderInputType::Buffer;
+ entry.bufferDesc.type = InputBufferType::RootConstantBuffer;
+ }
else if (parser.LookAhead("ubuffer"))
{
entry.type = ShaderInputType::Buffer;
diff --git a/tools/render-test/shader-input-layout.h b/tools/render-test/shader-input-layout.h
index 0831f73bb..4c8d0dfa2 100644
--- a/tools/render-test/shader-input-layout.h
+++ b/tools/render-test/shader-input-layout.h
@@ -42,7 +42,8 @@ struct InputTextureDesc
enum class InputBufferType
{
- ConstantBuffer, StorageBuffer
+ ConstantBuffer, StorageBuffer,
+ RootConstantBuffer,
};
struct InputBufferDesc
diff --git a/tools/render-test/shader-renderer-util.cpp b/tools/render-test/shader-renderer-util.cpp
index 987b63b48..b2911ffd5 100644
--- a/tools/render-test/shader-renderer-util.cpp
+++ b/tools/render-test/shader-renderer-util.cpp
@@ -195,6 +195,18 @@ static RefPtr<SamplerState> _createSamplerState(
case InputBufferType::StorageBuffer:
slotRangeDesc.type = DescriptorSlotType::StorageBuffer;
break;
+
+ case InputBufferType::RootConstantBuffer:
+ {
+ // A root constant buffer maps to a root constant range
+ // where the `count` of slots is equal to the number
+ // of bytes of data.
+ //
+ Slang::UInt size = srcEntry.bufferData.getCount() * sizeof(srcEntry.bufferData[0]);
+ slotRangeDesc.type = DescriptorSlotType::RootConstant;
+ slotRangeDesc.count = size;
+ }
+ break;
}
}
break;
@@ -269,6 +281,20 @@ static RefPtr<SamplerState> _createSamplerState(
const InputBufferDesc& srcBuffer = srcEntry.bufferDesc;
const size_t bufferSize = srcEntry.bufferData.getCount() * sizeof(uint32_t);
+ if( srcBuffer.type == InputBufferType::RootConstantBuffer )
+ {
+ // A root constant buffer at the HLSL/Slang level actually
+ // maps to root constant data stored directly in the descriptor
+ // set, and thus does not need/want us to allocate a buffer
+ // to hold the data.
+ //
+ // Instead, we set the data directly here and then bypass
+ // the logic that handles the buffer-backed cases below.
+ //
+ descriptorSet->setRootConstants(rangeIndex, 0, bufferSize, srcEntry.bufferData.getBuffer());
+ break;
+ }
+
RefPtr<BufferResource> bufferResource;
SLANG_RETURN_ON_FAIL(createBufferResource(srcEntry.bufferDesc, srcEntry.isOutput, bufferSize, srcEntry.bufferData.getBuffer(), renderer, bufferResource));