diff options
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 |
