summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--include/slang.h4
-rwxr-xr-xsource/slang/slang-compiler.h22
-rw-r--r--source/slang/slang-parameter-binding.cpp2
-rw-r--r--source/slang/slang-reflection-api.cpp5
-rw-r--r--source/slang/slang-type-layout.cpp14
-rw-r--r--source/slang/slang-type-layout.h3
-rw-r--r--source/slang/slang.cpp11
-rw-r--r--tests/autodiff/global-param-hoisting.slang2
-rw-r--r--tests/bindings/nested-parameter-block-2.slang1
-rw-r--r--tests/bugs/buffer-swizzle-store.slang2
-rw-r--r--tests/compute/entry-point-uniform-params.slang3
-rw-r--r--tests/compute/parameter-block.slang2
-rw-r--r--tests/compute/texture-subscript.slang30
-rw-r--r--tests/language-feature/shader-params/entry-point-uniform-params.slang2
-rw-r--r--tests/language-feature/types/opaque/inout-param-opaque-type-in-struct.slang3
-rw-r--r--tests/language-feature/types/opaque/out-param-opaque-type-in-struct.slang3
-rw-r--r--tests/language-feature/types/opaque/return-opaque-type-in-struct.slang3
-rw-r--r--tools/gfx/metal/metal-command-encoder.cpp20
-rw-r--r--tools/gfx/metal/metal-device.cpp19
-rw-r--r--tools/gfx/metal/metal-device.h2
-rw-r--r--tools/gfx/metal/metal-shader-object-layout.cpp10
-rw-r--r--tools/gfx/metal/metal-shader-object-layout.h3
-rw-r--r--tools/gfx/metal/metal-shader-object.cpp182
-rw-r--r--tools/gfx/metal/metal-shader-object.h21
24 files changed, 317 insertions, 52 deletions
diff --git a/include/slang.h b/include/slang.h
index 1671e7f38..6fd6da7b2 100644
--- a/include/slang.h
+++ b/include/slang.h
@@ -2316,7 +2316,7 @@ extern "C"
// The input_attachment_index subpass occupancy tracker
SLANG_PARAMETER_CATEGORY_SUBPASS,
- // Metal resource binding points.
+ // Metal tier-1 argument buffer element [[id]].
SLANG_PARAMETER_CATEGORY_METAL_ARGUMENT_BUFFER_ELEMENT,
// Metal [[attribute]] inputs.
@@ -2398,6 +2398,7 @@ extern "C"
enum SlangLayoutRules : SlangLayoutRulesIntegral
{
SLANG_LAYOUT_RULES_DEFAULT,
+ SLANG_LAYOUT_RULES_METAL_ARGUMENT_BUFFER_TIER_2,
};
typedef SlangUInt32 SlangModifierIDIntegral;
@@ -3585,6 +3586,7 @@ namespace slang
enum class LayoutRules : SlangLayoutRulesIntegral
{
Default = SLANG_LAYOUT_RULES_DEFAULT,
+ MetalArgumentBufferTier2 = SLANG_LAYOUT_RULES_METAL_ARGUMENT_BUFFER_TIER_2,
};
typedef struct ShaderReflection ProgramLayout;
diff --git a/source/slang/slang-compiler.h b/source/slang/slang-compiler.h
index 7fc43d778..4bae6c10d 100755
--- a/source/slang/slang-compiler.h
+++ b/source/slang/slang-compiler.h
@@ -1787,11 +1787,27 @@ namespace Slang
CodeGenTarget getTarget() { return optionSet.getEnumOption<CodeGenTarget>(CompilerOptionName::Target); }
// TypeLayouts created on the fly by reflection API
- Dictionary<Type*, RefPtr<TypeLayout>> typeLayouts;
+ struct TypeLayoutKey
+ {
+ Type* type;
+ slang::LayoutRules rules;
+ HashCode getHashCode() const
+ {
+ Hasher hasher;
+ hasher.hashValue(type);
+ hasher.hashValue(rules);
+ return hasher.getResult();
+ }
+ bool operator==(TypeLayoutKey other) const
+ {
+ return type == other.type && rules == other.rules;
+ }
+ };
+ Dictionary<TypeLayoutKey, RefPtr<TypeLayout>> typeLayouts;
- Dictionary<Type*, RefPtr<TypeLayout>>& getTypeLayouts() { return typeLayouts; }
+ Dictionary<TypeLayoutKey, RefPtr<TypeLayout>>& getTypeLayouts() { return typeLayouts; }
- TypeLayout* getTypeLayout(Type* type);
+ TypeLayout* getTypeLayout(Type* type, slang::LayoutRules rules);
CompilerOptionSet& getOptionSet() { return optionSet; }
diff --git a/source/slang/slang-parameter-binding.cpp b/source/slang/slang-parameter-binding.cpp
index 8b15126db..fd7fae430 100644
--- a/source/slang/slang-parameter-binding.cpp
+++ b/source/slang/slang-parameter-binding.cpp
@@ -3892,7 +3892,7 @@ RefPtr<ProgramLayout> generateParameterBindings(
}
// Try to find rules based on the selected code-generation target
- auto layoutContext = getInitialLayoutContextForTarget(targetReq, programLayout);
+ auto layoutContext = getInitialLayoutContextForTarget(targetReq, programLayout, slang::LayoutRules::Default);
// If there was no target, or there are no rules for the target,
// then bail out here.
diff --git a/source/slang/slang-reflection-api.cpp b/source/slang/slang-reflection-api.cpp
index 9e08330e4..8dc889c97 100644
--- a/source/slang/slang-reflection-api.cpp
+++ b/source/slang/slang-reflection-api.cpp
@@ -810,13 +810,13 @@ SLANG_API SlangReflectionType * spReflection_FindTypeByName(SlangReflection * re
SLANG_API SlangReflectionTypeLayout* spReflection_GetTypeLayout(
SlangReflection* reflection,
SlangReflectionType* inType,
- SlangLayoutRules /*rules*/)
+ SlangLayoutRules rules)
{
auto context = convert(reflection);
auto type = convert(inType);
auto targetReq = context->getTargetReq();
- auto typeLayout = targetReq->getTypeLayout(type);
+ auto typeLayout = targetReq->getTypeLayout(type, (slang::LayoutRules)rules);
return convert(typeLayout);
}
@@ -1875,6 +1875,7 @@ namespace Slang
case LayoutResourceKind::DescriptorTableSlot:
case LayoutResourceKind::Uniform:
case LayoutResourceKind::ConstantBuffer: // for metal
+ case LayoutResourceKind::MetalArgumentBufferElement:
resInfo = info;
break;
}
diff --git a/source/slang/slang-type-layout.cpp b/source/slang/slang-type-layout.cpp
index 293dce7c6..eca0d0ec3 100644
--- a/source/slang/slang-type-layout.cpp
+++ b/source/slang/slang-type-layout.cpp
@@ -1811,11 +1811,21 @@ LayoutRulesFamilyImpl* getDefaultLayoutRulesFamilyForTarget(TargetRequest* targe
}
}
-TypeLayoutContext getInitialLayoutContextForTarget(TargetRequest* targetReq, ProgramLayout* programLayout)
+TypeLayoutContext getInitialLayoutContextForTarget(TargetRequest* targetReq, ProgramLayout* programLayout, slang::LayoutRules rules)
{
auto astBuilder = targetReq->getLinkage()->getASTBuilder();
- LayoutRulesFamilyImpl* rulesFamily = getDefaultLayoutRulesFamilyForTarget(targetReq);
+ LayoutRulesFamilyImpl* rulesFamily;
+ switch (rules)
+ {
+ case slang::LayoutRules::Default:
+ default:
+ rulesFamily = getDefaultLayoutRulesFamilyForTarget(targetReq);
+ break;
+ case slang::LayoutRules::MetalArgumentBufferTier2:
+ rulesFamily = &kCPULayoutRulesFamilyImpl;
+ break;
+ }
TypeLayoutContext context;
context.astBuilder = astBuilder;
diff --git a/source/slang/slang-type-layout.h b/source/slang/slang-type-layout.h
index 37c0cd1e7..701708363 100644
--- a/source/slang/slang-type-layout.h
+++ b/source/slang/slang-type-layout.h
@@ -1288,7 +1288,8 @@ private:
//
TypeLayoutContext getInitialLayoutContextForTarget(
TargetRequest* targetRequest,
- ProgramLayout* programLayout);
+ ProgramLayout* programLayout,
+ slang::LayoutRules rules);
/// Direction(s) of a varying shader parameter
typedef unsigned int EntryPointParameterDirectionMask;
diff --git a/source/slang/slang.cpp b/source/slang/slang.cpp
index f440ce3cb..c6e460d02 100644
--- a/source/slang/slang.cpp
+++ b/source/slang/slang.cpp
@@ -1368,7 +1368,7 @@ SLANG_NO_THROW slang::TypeLayoutReflection* SLANG_MCALL Linkage::getTypeLayout(
//
SLANG_UNUSED(rules);
- auto typeLayout = target->getTypeLayout(type);
+ auto typeLayout = target->getTypeLayout(type, rules);
// TODO: We currently don't have a path for capturing
// errors that occur during layout (e.g., types that
@@ -1827,7 +1827,7 @@ CapabilitySet TargetRequest::getTargetCaps()
}
-TypeLayout* TargetRequest::getTypeLayout(Type* type)
+TypeLayout* TargetRequest::getTypeLayout(Type* type, slang::LayoutRules rules)
{
SLANG_AST_BUILDER_RAII(getLinkage()->getASTBuilder());
@@ -1841,13 +1841,14 @@ TypeLayout* TargetRequest::getTypeLayout(Type* type)
// parameter instead (leaving the user to figure out how that
// maps to the ordering via some API on the program layout).
//
- auto layoutContext = getInitialLayoutContextForTarget(this, nullptr);
+ auto layoutContext = getInitialLayoutContextForTarget(this, nullptr, rules);
RefPtr<TypeLayout> result;
- if (getTypeLayouts().tryGetValue(type, result))
+ auto key = TypeLayoutKey{ type, rules };
+ if (getTypeLayouts().tryGetValue(key, result))
return result.Ptr();
result = createTypeLayout(layoutContext, type);
- getTypeLayouts()[type] = result;
+ getTypeLayouts()[key] = result;
return result.Ptr();
}
diff --git a/tests/autodiff/global-param-hoisting.slang b/tests/autodiff/global-param-hoisting.slang
index 7e9c9e971..54d275974 100644
--- a/tests/autodiff/global-param-hoisting.slang
+++ b/tests/autodiff/global-param-hoisting.slang
@@ -7,7 +7,7 @@
//TEST(compute):COMPARE_COMPUTE_EX:-slang -compute -shaderobj -output-using-type
//TEST(compute, vulkan):COMPARE_COMPUTE_EX:-vk -compute -shaderobj -output-using-type
//TEST(compute):COMPARE_COMPUTE_EX:-cpu -compute -output-using-type -shaderobj
-//DISABLE_TEST(compute):COMPARE_COMPUTE:-slang -shaderobj -mtl
+//TEST(compute):COMPARE_COMPUTE:-slang -compute -mtl -output-using-type -render-features argument-buffer-tier-2
//TEST_INPUT:ubuffer(data=[0 0 0 0 0], stride=4):out,name=outputBuffer
RWStructuredBuffer<float> outputBuffer;
diff --git a/tests/bindings/nested-parameter-block-2.slang b/tests/bindings/nested-parameter-block-2.slang
index 59580f17d..790e3d060 100644
--- a/tests/bindings/nested-parameter-block-2.slang
+++ b/tests/bindings/nested-parameter-block-2.slang
@@ -1,6 +1,7 @@
//TEST(compute):COMPARE_COMPUTE(filecheck-buffer=CHECK): -shaderobj -output-using-type
//TEST(compute):COMPARE_COMPUTE(filecheck-buffer=CHECK): -d3d12 -use-dxil -shaderobj -output-using-type
//TEST(compute):COMPARE_COMPUTE(filecheck-buffer=CHECK): -vk -shaderobj -output-using-type
+//TEST(compute):COMPARE_COMPUTE(filecheck-buffer=CHECK): -metal -shaderobj -output-using-type -render-features argument-buffer-tier-2
// nested-parameter-block-2.slang
struct CB
diff --git a/tests/bugs/buffer-swizzle-store.slang b/tests/bugs/buffer-swizzle-store.slang
index 5850cd3c0..3cfcf6e59 100644
--- a/tests/bugs/buffer-swizzle-store.slang
+++ b/tests/bugs/buffer-swizzle-store.slang
@@ -1,6 +1,6 @@
//TEST(compute):COMPARE_COMPUTE_EX:-slang -compute -shaderobj -output-using-type
//TEST(compute, vulkan):COMPARE_COMPUTE_EX:-vk -compute -shaderobj -output-using-type
-//DISABLE_TEST(compute):COMPARE_COMPUTE:-slang -shaderobj -mtl
+//TEST(compute):COMPARE_COMPUTE:-slang -shaderobj -mtl -output-using-type
//TEST_INPUT: RWTexture2D(format=R16G16_FLOAT, size=4, content = one, mipMaps = 1):name g_test
[format("rg16f")]
diff --git a/tests/compute/entry-point-uniform-params.slang b/tests/compute/entry-point-uniform-params.slang
index 65da103b6..87a21a2e6 100644
--- a/tests/compute/entry-point-uniform-params.slang
+++ b/tests/compute/entry-point-uniform-params.slang
@@ -9,8 +9,7 @@
//TEST(compute):COMPARE_COMPUTE: -dx11 -shaderobj
//TEST(compute):COMPARE_COMPUTE: -cuda -shaderobj
//TEST(compute):COMPARE_COMPUTE: -cpu -shaderobj
-
-
+//TEST(compute):COMPARE_COMPUTE: -metal -shaderobj
struct Signs
{
diff --git a/tests/compute/parameter-block.slang b/tests/compute/parameter-block.slang
index 27cfed716..2922f0813 100644
--- a/tests/compute/parameter-block.slang
+++ b/tests/compute/parameter-block.slang
@@ -2,7 +2,7 @@
//TEST(compute):COMPARE_COMPUTE:-cuda -shaderobj
//TEST(compute):COMPARE_COMPUTE:-vk -shaderobj
//TEST(compute):COMPARE_COMPUTE:-shaderobj
-//DISABLE_TEST(compute):COMPARE_COMPUTE:-slang -shaderobj -mtl
+//TEST(compute):COMPARE_COMPUTE:-slang -shaderobj -mtl -render-features argument-buffer-tier-2
// Ensure that Slang `ParameterBlock` type is lowered
// to HLSL in the fashion that we expect.
diff --git a/tests/compute/texture-subscript.slang b/tests/compute/texture-subscript.slang
index 9251f49f1..c15a0824b 100644
--- a/tests/compute/texture-subscript.slang
+++ b/tests/compute/texture-subscript.slang
@@ -2,7 +2,7 @@
//TEST:SIMPLE(filecheck=METALLIB): -target metallib -entry computeMain -stage compute
// Metal lacks RWTexture GFX backend support.
// Due to this, Metal compute test is disabled
-//DISABLE_TEST(compute):COMPARE_COMPUTE(filecheck-buffer=BUF): -slang -output-using-type -shaderobj -mtl
+//TEST(compute):COMPARE_COMPUTE(filecheck-buffer=BUF): -slang -output-using-type -shaderobj -mtl
//TEST(compute):COMPARE_COMPUTE(filecheck-buffer=BUF): -slang -output-using-type -shaderobj -vk
//TEST(compute):COMPARE_COMPUTE(filecheck-buffer=BUF): -slang -output-using-type -shaderobj -vk -glsl
@@ -28,18 +28,22 @@ RWStructuredBuffer<uint> outputBuffer;
[numthreads(1,1,1)]
void computeMain()
{
- outputTexture1D[0].xz = int2(1,2).xx;
- outputTexture1D[1].x = int2(3,4).y;
-
- outputTexture2D[0].xz = int2(1,2).xx;
- outputTexture2D[int2(0, 1)].x = int2(3,4).y;
-
- outputTexture3D[0].xz = int2(1,2).xx;
- outputTexture3D[int3(0, 0, 1)].x = int2(3,4).y;
-
- outputTexture2DArray[0].xz = int2(1,2);
- outputTexture2DArray[int3(0, 0, 1)].xz = int2(3,4);
-
+ outputTexture1D[0].xz = int2(1, 2).xx;
+ AllMemoryBarrier();
+ outputTexture1D[1].x = int2(3, 4).y;
+ AllMemoryBarrier();
+ outputTexture2D[0].xz = int2(1, 2).xx;
+ AllMemoryBarrier();
+ outputTexture2D[int2(0, 1)].x = int2(3, 4).y;
+ AllMemoryBarrier();
+ outputTexture3D[0].xz = int2(1, 2).xx;
+ AllMemoryBarrier();
+ outputTexture3D[int3(0, 0, 1)].x = int2(3, 4).y;
+ AllMemoryBarrier();
+ outputTexture2DArray[0].xz = int2(1, 2);
+ AllMemoryBarrier();
+ outputTexture2DArray[int3(0, 0, 1)].xz = int2(3, 4);
+ AllMemoryBarrier();
outputBuffer[0] = uint(true
&& all(outputTexture1D[0] == int4(1, 0, 1, 0)) == true
&& all(outputTexture1D[1] == int4(4, 0, 0, 0)) == true
diff --git a/tests/language-feature/shader-params/entry-point-uniform-params.slang b/tests/language-feature/shader-params/entry-point-uniform-params.slang
index e150d3982..4b0b0e6a4 100644
--- a/tests/language-feature/shader-params/entry-point-uniform-params.slang
+++ b/tests/language-feature/shader-params/entry-point-uniform-params.slang
@@ -3,7 +3,7 @@
//TEST(compute):COMPARE_COMPUTE: -shaderobj
//TEST(compute):COMPARE_COMPUTE:-cuda -shaderobj
//TEST(compute):COMPARE_COMPUTE:-cpu -shaderobj
-//DISABLE_TEST(compute):COMPARE_COMPUTE:-slang -shaderobj -mtl
+//TEST(compute):COMPARE_COMPUTE:-slang -shaderobj -mtl
// Test that a shader can be written that
// only uses entry point `uniform` parameters,
diff --git a/tests/language-feature/types/opaque/inout-param-opaque-type-in-struct.slang b/tests/language-feature/types/opaque/inout-param-opaque-type-in-struct.slang
index 3fc28812f..37e2585cd 100644
--- a/tests/language-feature/types/opaque/inout-param-opaque-type-in-struct.slang
+++ b/tests/language-feature/types/opaque/inout-param-opaque-type-in-struct.slang
@@ -4,8 +4,7 @@
// aggregate type that includes an opaque type
//TEST(compute):COMPARE_COMPUTE:
-// GFX backend fails
-//DISABLE_TEST(compute):COMPARE_COMPUTE:-slang -shaderobj -mtl
+//TEST(compute):COMPARE_COMPUTE:-slang -shaderobj -mtl
struct Things
{
diff --git a/tests/language-feature/types/opaque/out-param-opaque-type-in-struct.slang b/tests/language-feature/types/opaque/out-param-opaque-type-in-struct.slang
index c778e56fa..846dfb76f 100644
--- a/tests/language-feature/types/opaque/out-param-opaque-type-in-struct.slang
+++ b/tests/language-feature/types/opaque/out-param-opaque-type-in-struct.slang
@@ -4,8 +4,7 @@
// aggregate type that includes an opaque type
//TEST(compute):COMPARE_COMPUTE:
-// GFX backend fails
-//DISABLE_TEST(compute):COMPARE_COMPUTE:-slang -shaderobj -mtl
+//TEST(compute):COMPARE_COMPUTE:-slang -shaderobj -mtl
struct Things
{
diff --git a/tests/language-feature/types/opaque/return-opaque-type-in-struct.slang b/tests/language-feature/types/opaque/return-opaque-type-in-struct.slang
index a53551d4a..3af17a0b4 100644
--- a/tests/language-feature/types/opaque/return-opaque-type-in-struct.slang
+++ b/tests/language-feature/types/opaque/return-opaque-type-in-struct.slang
@@ -4,8 +4,7 @@
// aggregate type that includes an opaque type
//TEST(compute):COMPARE_COMPUTE:
-// GFX backend fails
-//DISABLE_TEST(compute):COMPARE_COMPUTE:-slang -shaderobj -mtl
+//TEST(compute):COMPARE_COMPUTE:-slang -shaderobj -mtl
struct Things
{
diff --git a/tools/gfx/metal/metal-command-encoder.cpp b/tools/gfx/metal/metal-command-encoder.cpp
index 2447295c4..442c216aa 100644
--- a/tools/gfx/metal/metal-command-encoder.cpp
+++ b/tools/gfx/metal/metal-command-encoder.cpp
@@ -478,18 +478,28 @@ Result ComputeCommandEncoder::bindPipelineWithRootObject(
Result ComputeCommandEncoder::dispatchCompute(int x, int y, int z)
{
- auto pipeline = static_cast<PipelineStateImpl*>(m_currentPipeline.Ptr());
- pipeline->ensureAPIPipelineStateCreated();
-
MTL::ComputeCommandEncoder* encoder = m_commandBuffer->getMetalComputeCommandEncoder();
- encoder->setComputePipelineState(pipeline->m_computePipelineState.get());
ComputeBindingContext bindingContext;
bindingContext.init(m_commandBuffer->m_device, encoder);
auto program = static_cast<ShaderProgramImpl*>(m_currentPipeline->m_program.get());
m_commandBuffer->m_rootObject.bindAsRoot(&bindingContext, program->m_rootObjectLayout);
- encoder->dispatchThreadgroups(MTL::Size(x, y, z), pipeline->m_threadGroupSize);
+ auto pipeline = static_cast<PipelineStateImpl*>(m_currentPipeline.Ptr());
+ RootShaderObjectImpl* rootObjectImpl = &m_commandBuffer->m_rootObject;
+ RefPtr<PipelineStateBase> newPipeline;
+ SLANG_RETURN_ON_FAIL(m_commandBuffer->m_device->maybeSpecializePipeline(
+ m_currentPipeline, rootObjectImpl, newPipeline));
+ PipelineStateImpl* newPipelineImpl = static_cast<PipelineStateImpl*>(newPipeline.Ptr());
+
+ SLANG_RETURN_ON_FAIL(newPipelineImpl->ensureAPIPipelineStateCreated());
+ m_currentPipeline = newPipelineImpl;
+
+ m_currentPipeline->ensureAPIPipelineStateCreated();
+ encoder->setComputePipelineState(m_currentPipeline->m_computePipelineState.get());
+
+
+ encoder->dispatchThreadgroups(MTL::Size(x, y, z), m_currentPipeline->m_threadGroupSize);
return SLANG_OK;
}
diff --git a/tools/gfx/metal/metal-device.cpp b/tools/gfx/metal/metal-device.cpp
index 4a1c02480..609c1bf27 100644
--- a/tools/gfx/metal/metal-device.cpp
+++ b/tools/gfx/metal/metal-device.cpp
@@ -70,6 +70,12 @@ SlangResult DeviceImpl::initialize(const Desc& desc)
m_device = NS::TransferPtr(MTL::CreateSystemDefaultDevice());
m_commandQueue = NS::TransferPtr(m_device->newCommandQueue(64));
+ m_hasArgumentBufferTier2 = m_device->argumentBuffersSupport() >= MTL::ArgumentBuffersTier2;
+
+ if (m_hasArgumentBufferTier2)
+ {
+ m_features.add("argument-buffer-tier-2");
+ }
SLANG_RETURN_ON_FAIL(slangContext.initialize(
desc.slang,
@@ -415,8 +421,19 @@ Result DeviceImpl::createTextureResource(
}
if (desc.allowedStates.contains(ResourceState::UnorderedAccess))
{
+ textureUsage |= MTL::TextureUsageShaderRead;
textureUsage |= MTL::TextureUsageShaderWrite;
- textureUsage |= MTL::TextureUsageShaderAtomic;
+
+ // Request atomic access if the format allows it.
+ switch (desc.format)
+ {
+ case Format::R32_UINT:
+ case Format::R32_SINT:
+ case Format::R32G32_UINT:
+ case Format::R32G32_SINT:
+ textureUsage |= MTL::TextureUsageShaderAtomic;
+ break;
+ }
}
textureDesc->setMipmapLevelCount(desc.numMipLevels);
diff --git a/tools/gfx/metal/metal-device.h b/tools/gfx/metal/metal-device.h
index 4f08b346e..50eb0e88d 100644
--- a/tools/gfx/metal/metal-device.h
+++ b/tools/gfx/metal/metal-device.h
@@ -137,6 +137,8 @@ public:
uint32_t m_queueAllocCount;
+ bool m_hasArgumentBufferTier2 = false;
+
// A list to hold objects that may have a strong back reference to the device
// instance. Because of the pipeline cache in `RendererBase`, there could be a reference
// cycle among `DeviceImpl`->`PipelineStateImpl`->`ShaderProgramImpl`->`DeviceImpl`.
diff --git a/tools/gfx/metal/metal-shader-object-layout.cpp b/tools/gfx/metal/metal-shader-object-layout.cpp
index abde03b54..94bcb6db6 100644
--- a/tools/gfx/metal/metal-shader-object-layout.cpp
+++ b/tools/gfx/metal/metal-shader-object-layout.cpp
@@ -219,6 +219,16 @@ SlangResult ShaderObjectLayoutImpl::Builder::build(ShaderObjectLayoutImpl** outL
return SLANG_OK;
}
+slang::TypeLayoutReflection* ShaderObjectLayoutImpl::getParameterBlockTypeLayout()
+{
+ if (!m_parameterBlockTypeLayout)
+ {
+ m_parameterBlockTypeLayout = m_slangSession->getTypeLayout(
+ m_elementTypeLayout->getType(), 0, slang::LayoutRules::MetalArgumentBufferTier2);
+ }
+ return m_parameterBlockTypeLayout;
+}
+
Result ShaderObjectLayoutImpl::createForElementType(
RendererBase* renderer,
slang::ISession* session,
diff --git a/tools/gfx/metal/metal-shader-object-layout.h b/tools/gfx/metal/metal-shader-object-layout.h
index 969c78c3a..24a969c89 100644
--- a/tools/gfx/metal/metal-shader-object-layout.h
+++ b/tools/gfx/metal/metal-shader-object-layout.h
@@ -177,6 +177,7 @@ public:
uint32_t getTotalOrdinaryDataSize() const { return m_totalOrdinaryDataSize; }
+ slang::TypeLayoutReflection* getParameterBlockTypeLayout();
protected:
Result _init(Builder const* builder);
@@ -190,6 +191,8 @@ protected:
Index m_subObjectCount = 0;
uint32_t m_totalOrdinaryDataSize = 0;
List<SubObjectRangeInfo> m_subObjectRanges;
+ // The type layout to use when the shader object is bind as a parameter block.
+ slang::TypeLayoutReflection* m_parameterBlockTypeLayout = nullptr;
};
class RootShaderObjectLayoutImpl : public ShaderObjectLayoutImpl
diff --git a/tools/gfx/metal/metal-shader-object.cpp b/tools/gfx/metal/metal-shader-object.cpp
index 865196c5c..6d421e065 100644
--- a/tools/gfx/metal/metal-shader-object.cpp
+++ b/tools/gfx/metal/metal-shader-object.cpp
@@ -54,7 +54,7 @@ SLANG_NO_THROW Result SLANG_MCALL
memcpy(dest + offset, data, size);
m_isConstantBufferDirty = true;
-
+ m_isArgumentBufferDirty = true;
return SLANG_OK;
}
@@ -89,6 +89,7 @@ SLANG_NO_THROW Result SLANG_MCALL
// m_textures[bindingRange.baseIndex + offset.bindingArrayIndex] = static_cast<TextureResourceViewImpl*>(resourceView);
break;
}
+ m_isArgumentBufferDirty = true;
return SLANG_OK;
}
@@ -102,6 +103,7 @@ SLANG_NO_THROW Result SLANG_MCALL ShaderObjectImpl::setSampler(ShaderOffset cons
auto& bindingRange = layout->getBindingRange(offset.bindingRangeIndex);
m_samplers[bindingRange.baseIndex + offset.bindingArrayIndex] = static_cast<SamplerStateImpl*>(sampler);
+ m_isArgumentBufferDirty = true;
return SLANG_OK;
}
@@ -161,7 +163,7 @@ Result ShaderObjectImpl::init(IDevice* device, ShaderObjectLayoutImpl* layout)
m_objects[bindingRangeInfo.subObjectIndex + i] = subObject;
}
}
-
+ m_isArgumentBufferDirty = true;
return SLANG_OK;
}
@@ -255,7 +257,6 @@ Result ShaderObjectImpl::_writeOrdinaryData(
subObject->_writeOrdinaryData(subObjectDest, destSize - subObjectOffset, subObjectLayout);
}
}
-
return SLANG_OK;
}
@@ -325,6 +326,165 @@ Result ShaderObjectImpl::_bindOrdinaryDataBufferIfNeeded(
return SLANG_OK;
}
+void ShaderObjectImpl::writeOrdinaryDataIntoArgumentBuffer(
+ slang::TypeLayoutReflection* argumentBufferTypeLayout,
+ slang::TypeLayoutReflection* defaultTypeLayout,
+ uint8_t* argumentBuffer,
+ uint8_t* srcData)
+{
+ // If we are pure data, just copy it over from srcData.
+ if (defaultTypeLayout->getCategoryCount() == 1)
+ {
+ switch (defaultTypeLayout->getCategoryByIndex(0))
+ {
+ case slang::ParameterCategory::Uniform:
+ // Just copy the uniform data
+ memcpy(argumentBuffer, srcData, defaultTypeLayout->getSize());
+ break;
+ }
+ return;
+ }
+
+ for (unsigned int i = 0; i < argumentBufferTypeLayout->getFieldCount(); i++)
+ {
+ auto argumentBufferField = argumentBufferTypeLayout->getFieldByIndex(i);
+ auto defaultLayoutField = defaultTypeLayout->getFieldByIndex(i);
+ // If the field is mixed type, recurse.
+ writeOrdinaryDataIntoArgumentBuffer(
+ argumentBufferField->getTypeLayout(),
+ defaultLayoutField->getTypeLayout(),
+ argumentBuffer + argumentBufferField->getOffset(),
+ srcData + defaultLayoutField->getOffset());
+ }
+}
+
+BufferResourceImpl* ShaderObjectImpl::_ensureArgumentBufferUpToDate(
+ DeviceImpl* device,
+ ShaderObjectLayoutImpl* layout)
+{
+ auto typeLayout = layout->getParameterBlockTypeLayout();
+ auto defaultTypeLayout = m_layout->getElementTypeLayout();
+
+ // If we have already created a buffer to hold the parmaeter block, then we should
+ // simply re-use that buffer rather than re-create it.
+ if (!m_argumentBuffer)
+ {
+ ComPtr<IBufferResource> bufferResourcePtr;
+ IBufferResource::Desc bufferDesc = {};
+ bufferDesc.type = IResource::Type::Buffer;
+ bufferDesc.sizeInBytes = typeLayout->getSize();
+ bufferDesc.defaultState = ResourceState::ConstantBuffer;
+ bufferDesc.allowedStates =
+ ResourceStateSet(ResourceState::ConstantBuffer, ResourceState::CopyDestination);
+ bufferDesc.memoryType = MemoryType::Upload;
+ SLANG_RETURN_NULL_ON_FAIL(
+ device->createBufferResource(bufferDesc, nullptr, bufferResourcePtr.writeRef()));
+ m_argumentBuffer = static_cast<BufferResourceImpl*>(bufferResourcePtr.get());
+ }
+
+ if (m_isArgumentBufferDirty)
+ {
+ // Once the buffer is allocated, we can fill it in with the uniform data
+ // and resource bindings we have tracked, using `typeLayout` to obtain
+ // the offsets for each field.
+ //
+ auto dataSize = typeLayout->getSize();
+ MemoryRange range = { 0, dataSize };
+ void* argumentData;
+ SLANG_RETURN_NULL_ON_FAIL(m_argumentBuffer->map(&range, &argumentData));
+
+ // Now fill in argument values to `argumentData`.
+ int bindingRangeIndex = 0;
+ SLANG_ASSERT(defaultTypeLayout->getBindingRangeCount() == typeLayout->getBindingRangeCount());
+
+ int bufferBindingIndexOffset = layout->getTotalOrdinaryDataSize() != 0 ? 1 : 0;
+
+ for (unsigned int bindingRangeIndex = 0; bindingRangeIndex < defaultTypeLayout->getBindingRangeCount(); bindingRangeIndex++)
+ {
+ int bindingCount = defaultTypeLayout->getBindingRangeBindingCount(bindingRangeIndex);
+ int setIndex = defaultTypeLayout->getBindingRangeDescriptorSetIndex(bindingRangeIndex);
+ int rangeIndex = defaultTypeLayout->getBindingRangeFirstDescriptorRangeIndex(bindingRangeIndex);
+ int bindingOffset = defaultTypeLayout->getDescriptorSetDescriptorRangeIndexOffset(setIndex, rangeIndex);
+ auto bindingType = defaultTypeLayout->getBindingRangeType(bindingRangeIndex);
+ for (int i = 0; i < bindingCount; i++)
+ {
+ auto argumentDataOffset = typeLayout->getDescriptorSetDescriptorRangeIndexOffset(setIndex, rangeIndex) + i * sizeof(uint64_t);
+ auto argumentPtr = (uint8_t*)argumentData + argumentDataOffset;
+ auto resourceIndex = bindingOffset + i;
+ switch (bindingType)
+ {
+ case slang::BindingType::ConstantBuffer:
+ case slang::BindingType::ParameterBlock:
+ {
+ if (m_objects[resourceIndex])
+ {
+ auto subArgumentBuffer = m_objects[resourceIndex]->_ensureArgumentBufferUpToDate(device, m_objects[resourceIndex]->getLayout());
+ if (subArgumentBuffer)
+ {
+ gfx::DeviceAddress bufferPtr = subArgumentBuffer->m_buffer->gpuAddress();
+ memcpy(argumentPtr, &bufferPtr, sizeof(bufferPtr));
+ }
+ }
+ break;
+ }
+ case slang::BindingType::RawBuffer:
+ case slang::BindingType::MutableRawBuffer:
+ {
+ auto bufferViewImpl = static_cast<BufferResourceViewImpl*>(m_buffers[resourceIndex + bufferBindingIndexOffset].get());
+
+ if (bufferViewImpl)
+ {
+ gfx::DeviceAddress bufferPtr = bufferViewImpl->m_buffer->getDeviceAddress() + bufferViewImpl->m_offset;
+ memcpy(argumentPtr, &bufferPtr, sizeof(bufferPtr));
+ }
+ break;
+ }
+ case slang::BindingType::Texture:
+ case slang::BindingType::MutableTexture:
+ {
+ auto textureViewImpl = static_cast<TextureResourceViewImpl*>(m_textures[resourceIndex].get());
+ if (textureViewImpl)
+ {
+ auto resourceId = textureViewImpl->m_textureView->gpuResourceID();
+ memcpy(argumentPtr, &resourceId, sizeof(resourceId));
+ }
+ break;
+ }
+ case slang::BindingType::Sampler:
+ {
+ auto samplerStateImpl = static_cast<SamplerStateImpl*>(m_samplers[resourceIndex].get());
+ auto resourceId = samplerStateImpl->m_samplerState->gpuResourceID();
+ memcpy(argumentPtr, &resourceId, sizeof(resourceId));
+ break;
+ }
+ }
+ }
+ }
+ writeOrdinaryDataIntoArgumentBuffer(typeLayout, defaultTypeLayout, (uint8_t*)argumentData, (uint8_t*)m_data.getBuffer());
+ m_argumentBuffer->unmap(&range);
+ m_isArgumentBufferDirty = false;
+ }
+
+ return m_argumentBuffer.get();
+}
+
+Result ShaderObjectImpl::bindAsParameterBlock(
+ BindingContext* context,
+ BindingOffset const& inOffset,
+ ShaderObjectLayoutImpl* layout)
+{
+ if (!context->device->m_hasArgumentBufferTier2)
+ return SLANG_FAIL;
+
+ auto argumentBuffer = _ensureArgumentBufferUpToDate(context->device, layout);
+
+ if (m_argumentBuffer)
+ {
+ context->setBuffer(m_argumentBuffer->m_buffer.get(), inOffset.buffer);
+ }
+ return SLANG_OK;
+}
+
Result ShaderObjectImpl::bindAsConstantBuffer(
BindingContext* context,
BindingOffset const& inOffset,
@@ -425,7 +585,6 @@ Result ShaderObjectImpl::bindAsValue(
switch (bindingRange.bindingType)
{
case slang::BindingType::ConstantBuffer:
- case slang::BindingType::ParameterBlock:
{
BindingOffset objOffset = rangeOffset;
for (Index i = 0; i < count; ++i)
@@ -435,12 +594,23 @@ Result ShaderObjectImpl::bindAsValue(
// Unsurprisingly, we bind each object in the range as
// a constant buffer.
//
- subObject->bindAsConstantBuffer(context, objOffset, subObjectLayout);
+ SLANG_RETURN_ON_FAIL(subObject->bindAsConstantBuffer(context, objOffset, subObjectLayout));
objOffset += rangeStride;
}
+ break;
}
- break;
+ case slang::BindingType::ParameterBlock:
+ {
+ BindingOffset objOffset = rangeOffset;
+ for (Index i = 0; i < count; ++i)
+ {
+ auto subObject = m_objects[subObjectIndex + i];
+ SLANG_RETURN_ON_FAIL(subObject->bindAsParameterBlock(context, objOffset, subObjectLayout));
+ objOffset += rangeStride;
+ }
+ }
+ break;
#if 0
case slang::BindingType::ExistentialValue:
diff --git a/tools/gfx/metal/metal-shader-object.h b/tools/gfx/metal/metal-shader-object.h
index b9695febe..8a3d5d392 100644
--- a/tools/gfx/metal/metal-shader-object.h
+++ b/tools/gfx/metal/metal-shader-object.h
@@ -88,6 +88,16 @@ protected:
DeviceImpl* device,
ShaderObjectLayoutImpl* layout);
+ BufferResourceImpl* _ensureArgumentBufferUpToDate(
+ DeviceImpl* device,
+ ShaderObjectLayoutImpl* layout);
+
+ void writeOrdinaryDataIntoArgumentBuffer(
+ slang::TypeLayoutReflection* argumentBufferTypeLayout,
+ slang::TypeLayoutReflection* defaultTypeLayout,
+ uint8_t* argumentBuffer,
+ uint8_t* srcData);
+
/// Bind the buffer for ordinary/uniform data, if needed
///
/// The `ioOffset` parameter will be updated to reflect the constant buffer
@@ -105,6 +115,12 @@ public:
BindingOffset const& inOffset,
ShaderObjectLayoutImpl* layout);
+ /// Bind this object as if it was declared as a `ParameterBlock<T>` in Slang
+ Result bindAsParameterBlock(
+ BindingContext* context,
+ BindingOffset const& inOffset,
+ ShaderObjectLayoutImpl* layout);
+
/// Bind this object as a value that appears in the body of another object.
///
/// This case is directly used when binding an object for an interface-type
@@ -137,7 +153,12 @@ public:
/// Created on demand with `_createOrdinaryDataBufferIfNeeded()`
RefPtr<BufferResourceImpl> m_ordinaryDataBuffer;
+ /// Argument buffer created on demand to bind as a parameter block.
+ RefPtr<BufferResourceImpl> m_argumentBuffer;
+
+
bool m_isConstantBufferDirty = true;
+ bool m_isArgumentBufferDirty = true;
};
class MutableShaderObjectImpl