diff options
| author | Tim Foley <tfoleyNV@users.noreply.github.com> | 2021-04-01 16:15:09 -0700 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2021-04-01 16:15:09 -0700 |
| commit | 0ec8e5b016e56ad491a418ab72a5be28dd83f3b4 (patch) | |
| tree | e7934e78346cd63a4d944e68dc90fcafebc40d42 | |
| parent | 9475b11045089c9bc9773b16f7eb84f843db70c4 (diff) | |
Refactor D3D12 renderer root signature creation (#1779)
This change originated as an attempt to re-enable a test case, but it has ended up disabling more tests (for good reasons) than it re-enables.
The main change here is a significant overhaul of the way that the D3D12 render path extracts information from the Slang reflection API to produce a root signature.
There were also some supporting fixes in the reflection information to make sure it returns what the D3D12 back-end needed.
The big picture here is that the D3D12 path now uses the descriptor ranges stored in the reflection data more or less directly.
It still needs to use register/space offset information queried via the "old" reflection API, but it only does so at the top level now, for the program and entry points themselves.
All other layout information is derived directly from what Slang provides.
Smaller changes:
* The "flat" reflection API was expanded to include `getBindingRangeDescriptorRangeCount()` which was clearly missing.
* The "flat" reflection results for a constant buffer or parameter block that didn't contain any uniform data and was mapped to a plain constant buffer needed to be fixed up. That logic is still way to subtle to be trusted.
* Several additional tests were disabled that relied on static specialization, global/entry-point generi type parameters, structured buffers of interfaces or other features we don't officially support with shader objects right now. All of the affected tests were somehow passing by sheer luck and because they often passed in specialization arguments via explicit `TEST_INPUT` lines.
* The `inteface-shader-param` test is re-enabled now that we can properly describe its input with the new `set` mode on `TEST_INPUT`
* `ShaderCursor::getElement()` can now be used on structure types (in addition to arrays) to support by-index access to fields
* The `TEST_INPUT` system was expanded to support both by-name and by-index setting of structure fields for aggregates
* The `TEST_INPUT` system was expanded to allow an `out` prefix to mark parts of an expression as outputs on a `set` lines
* The `TEST_INPUT` system was expanded so that anything that would be allowed on a `TEST_INPUT` line by itself (like `ubuffer(...)`) can now be used as a sub-expression on a `set` line
Co-authored-by: Yong He <yonghe@outlook.com>
| -rw-r--r-- | slang.h | 7 | ||||
| -rw-r--r-- | source/slang/slang-reflection-api.cpp | 38 | ||||
| -rw-r--r-- | source/slang/slang.cpp | 4 | ||||
| -rw-r--r-- | tests/bugs/gh-357.slang | 2 | ||||
| -rw-r--r-- | tests/compute/assoctype-generic-arg.slang | 4 | ||||
| -rw-r--r-- | tests/compute/global-generic-value-param.slang | 2 | ||||
| -rw-r--r-- | tests/compute/global-type-param-in-entrypoint.slang | 2 | ||||
| -rw-r--r-- | tests/compute/global-type-param.slang | 2 | ||||
| -rw-r--r-- | tests/compute/int-generic.slang | 2 | ||||
| -rw-r--r-- | tests/compute/interface-assoc-type-param.slang | 4 | ||||
| -rw-r--r-- | tests/compute/interface-func-param-in-struct.slang | 6 | ||||
| -rw-r--r-- | tests/compute/interface-shader-param-in-struct.slang | 26 | ||||
| -rw-r--r-- | tests/compute/interface-shader-param.slang | 33 | ||||
| -rw-r--r-- | tests/compute/parameter-block.slang | 7 | ||||
| -rw-r--r-- | tests/disabled-tests.txt | 41 | ||||
| -rw-r--r-- | tools/gfx-util/shader-cursor.cpp | 50 | ||||
| -rw-r--r-- | tools/gfx/d3d12/render-d3d12.cpp | 466 | ||||
| -rw-r--r-- | tools/render-test/render-test-main.cpp | 28 | ||||
| -rw-r--r-- | tools/render-test/shader-input-layout.cpp | 11 | ||||
| -rw-r--r-- | tools/render-test/slang-support.cpp | 4 |
20 files changed, 471 insertions, 268 deletions
@@ -1993,6 +1993,7 @@ extern "C" SLANG_API SlangInt spReflectionTypeLayout_getBindingRangeDescriptorSetIndex(SlangReflectionTypeLayout* typeLayout, SlangInt index); SLANG_API SlangInt spReflectionTypeLayout_getBindingRangeFirstDescriptorRangeIndex(SlangReflectionTypeLayout* typeLayout, SlangInt index); + SLANG_API SlangInt spReflectionTypeLayout_getBindingRangeDescriptorRangeCount(SlangReflectionTypeLayout* typeLayout, SlangInt index); SLANG_API SlangInt spReflectionTypeLayout_getDescriptorSetCount(SlangReflectionTypeLayout* typeLayout); SLANG_API SlangInt spReflectionTypeLayout_getDescriptorSetSpaceOffset(SlangReflectionTypeLayout* typeLayout, SlangInt setIndex); @@ -2608,6 +2609,12 @@ namespace slang index); } + SlangInt getBindingRangeDescriptorRangeCount(SlangInt index) + { + return spReflectionTypeLayout_getBindingRangeDescriptorRangeCount( + (SlangReflectionTypeLayout*) this, + index); + } SlangInt getDescriptorSetCount() { diff --git a/source/slang/slang-reflection-api.cpp b/source/slang/slang-reflection-api.cpp index 2031b4a0f..6f022cc90 100644 --- a/source/slang/slang-reflection-api.cpp +++ b/source/slang/slang-reflection-api.cpp @@ -1326,6 +1326,7 @@ namespace Slang Index bindingRangeIndex = m_extendedInfo->m_bindingRanges.getCount(); SlangBindingType bindingType = SLANG_BINDING_TYPE_CONSTANT_BUFFER; Index spaceOffset = -1; + bool usesIndirectAllocation = false; LayoutResourceKind kind = LayoutResourceKind::None; // TODO: It is unclear if this should be looking at the resource @@ -1333,12 +1334,25 @@ namespace Slang // for(auto& resInfo : parameterGroupTypeLayout->resourceInfos) { + if( spaceOffset == -1 ) + { + spaceOffset = _calcSpaceOffset(path, kind); + } + kind = resInfo.kind; switch(kind) { default: continue; + case LayoutResourceKind::ConstantBuffer: + case LayoutResourceKind::PushConstantBuffer: + case LayoutResourceKind::DescriptorTableSlot: + break; + + // Certain cases indicate a parameter block that + // actually involves indirection. + // // Note: the only case where a parameter group should // reflect as consuming `Uniform` storage is on CPU/CUDA, // where that will be the only resource it contains. @@ -1346,18 +1360,19 @@ namespace Slang // TODO: If we ever support targets that don't have // constant buffers at all, this logic would be questionable. // - case LayoutResourceKind::ConstantBuffer: - case LayoutResourceKind::PushConstantBuffer: case LayoutResourceKind::RegisterSpace: - case LayoutResourceKind::DescriptorTableSlot: case LayoutResourceKind::Uniform: + usesIndirectAllocation = true; break; } bindingType = _calcBindingType(typeLayout, kind); - spaceOffset = _calcSpaceOffset(path, kind); break; } + if(spaceOffset == -1) + { + spaceOffset = 0; + } TypeLayout::ExtendedInfo::BindingRangeInfo bindingRange; bindingRange.leafTypeLayout = typeLayout; @@ -1419,7 +1434,7 @@ namespace Slang // because the physical storage for `C.a` is provided by the // memory allocation for `C` itself. - if( spaceOffset != -1 ) + if( !usesIndirectAllocation ) { // The logic here assumes that when a parameter group consumes // resources that must "leak" into the outer scope (including @@ -1848,6 +1863,19 @@ SLANG_API SlangInt spReflectionTypeLayout_getBindingRangeFirstDescriptorRangeInd return bindingRange.firstDescriptorRangeIndex; } +SLANG_API SlangInt spReflectionTypeLayout_getBindingRangeDescriptorRangeCount(SlangReflectionTypeLayout* inTypeLayout, SlangInt index) +{ + auto typeLayout = convert(inTypeLayout); + if(!typeLayout) return 0; + + auto extTypeLayout = Slang::getExtendedTypeLayout(typeLayout); + if(index < 0) return 0; + if(index >= extTypeLayout->m_bindingRanges.getCount()) return 0; + auto& bindingRange = extTypeLayout->m_bindingRanges[index]; + + return bindingRange.descriptorRangeCount; +} + SLANG_API SlangInt spReflectionTypeLayout_getDescriptorSetCount(SlangReflectionTypeLayout* inTypeLayout) { auto typeLayout = convert(inTypeLayout); diff --git a/source/slang/slang.cpp b/source/slang/slang.cpp index 8c6bf403e..c48701575 100644 --- a/source/slang/slang.cpp +++ b/source/slang/slang.cpp @@ -2832,7 +2832,9 @@ SLANG_NO_THROW SlangResult SLANG_MCALL ComponentType::specialize( auto specializationParamCount = getSpecializationParamCount(); if( specializationArgCount != specializationParamCount ) { - // TODO: diagnose + sink.diagnose(SourceLoc(), Diagnostics::mismatchSpecializationArguments, + specializationParamCount, + specializationArgCount); sink.getBlobIfNeeded(outDiagnostics); return SLANG_FAIL; } diff --git a/tests/bugs/gh-357.slang b/tests/bugs/gh-357.slang index 522cabd7c..4bd76b800 100644 --- a/tests/bugs/gh-357.slang +++ b/tests/bugs/gh-357.slang @@ -1,4 +1,4 @@ -//TEST(compute):COMPARE_COMPUTE: -shaderobj +//DISABLED_TEST(compute):COMPARE_COMPUTE: -shaderobj //TEST_INPUT:ubuffer(data=[0 0 0 0], stride=4):out,name outputBuffer diff --git a/tests/compute/assoctype-generic-arg.slang b/tests/compute/assoctype-generic-arg.slang index 1015ef775..cf71d7cde 100644 --- a/tests/compute/assoctype-generic-arg.slang +++ b/tests/compute/assoctype-generic-arg.slang @@ -1,5 +1,5 @@ -//TEST(compute):COMPARE_COMPUTE:-cpu -shaderobj -//TEST(compute):COMPARE_COMPUTE: -shaderobj +//DISABLED_TEST(compute):COMPARE_COMPUTE:-cpu -shaderobj +//DISABLED_TEST(compute):COMPARE_COMPUTE: -shaderobj //TEST_INPUT:type AssocImpl diff --git a/tests/compute/global-generic-value-param.slang b/tests/compute/global-generic-value-param.slang index dd1b091c1..3cd97da59 100644 --- a/tests/compute/global-generic-value-param.slang +++ b/tests/compute/global-generic-value-param.slang @@ -1,6 +1,6 @@ // global-generic-value-param.slang -//TEST(compute):COMPARE_COMPUTE: -shaderobj +//DISABLED_TEST(compute):COMPARE_COMPUTE: -shaderobj // This is a basic test of support for global generic // value parameters: explicit named parameters at global diff --git a/tests/compute/global-type-param-in-entrypoint.slang b/tests/compute/global-type-param-in-entrypoint.slang index 24ee113f5..326fc73d1 100644 --- a/tests/compute/global-type-param-in-entrypoint.slang +++ b/tests/compute/global-type-param-in-entrypoint.slang @@ -1,4 +1,4 @@ -//TEST(compute):COMPARE_RENDER_COMPUTE: -shaderobj +//DISABLED_TEST(compute):COMPARE_RENDER_COMPUTE: -shaderobj //TEST_INPUT: cbuffer(data=[1.0 0.0 0.0 0.0 0.0 1.0 0.0 0.0 0.0 0.0 1.0 0.0 0.0 0.0 0.0 1.0]):name Uniforms //TEST_INPUT: ubuffer(data=[0 0 0 0], stride=4):out,name outputBuffer diff --git a/tests/compute/global-type-param.slang b/tests/compute/global-type-param.slang index 7a3e31187..6ec957d9d 100644 --- a/tests/compute/global-type-param.slang +++ b/tests/compute/global-type-param.slang @@ -1,4 +1,4 @@ -//TEST(smoke,compute):COMPARE_COMPUTE: -shaderobj +//DISABLED_TEST(smoke,compute):COMPARE_COMPUTE: -shaderobj //TEST_INPUT:type Wrapper<Impl> diff --git a/tests/compute/int-generic.slang b/tests/compute/int-generic.slang index 7d693e42b..1c67591b7 100644 --- a/tests/compute/int-generic.slang +++ b/tests/compute/int-generic.slang @@ -1,4 +1,4 @@ -//TEST(compute):COMPARE_COMPUTE: -shaderobj +//DISABLED_TEST(compute):COMPARE_COMPUTE: -shaderobj //TEST_INPUT:type Material<1,2> diff --git a/tests/compute/interface-assoc-type-param.slang b/tests/compute/interface-assoc-type-param.slang index 4e6df4eb5..b315dd5f9 100644 --- a/tests/compute/interface-assoc-type-param.slang +++ b/tests/compute/interface-assoc-type-param.slang @@ -1,7 +1,7 @@ // Tests using associated types through an existential-struct-typed param. -//TEST(compute):COMPARE_COMPUTE_EX:-slang -compute -cuda -shaderobj -//TEST(compute):COMPARE_COMPUTE_EX:-slang -compute -cpu -shaderobj +//DISABLED_TEST(compute):COMPARE_COMPUTE_EX:-slang -compute -cuda -shaderobj +//DISABLED_TEST(compute):COMPARE_COMPUTE_EX:-slang -compute -cpu -shaderobj [anyValueSize(8)] interface IInterface diff --git a/tests/compute/interface-func-param-in-struct.slang b/tests/compute/interface-func-param-in-struct.slang index c32254cb5..9e3e6c201 100644 --- a/tests/compute/interface-func-param-in-struct.slang +++ b/tests/compute/interface-func-param-in-struct.slang @@ -1,7 +1,7 @@ // Tests specializing a function with existential-struct-typed param. -//TEST(compute):COMPARE_COMPUTE_EX:-slang -compute -cuda -shaderobj -//TEST(compute):COMPARE_COMPUTE_EX:-slang -compute -cpu -shaderobj +//DISABLED_TEST(compute):COMPARE_COMPUTE_EX:-slang -compute -cuda -shaderobj +//DISABLED_TEST(compute):COMPARE_COMPUTE_EX:-slang -compute -cpu -shaderobj [anyValueSize(8)] interface IInterface @@ -31,8 +31,6 @@ void compute(uint tid, Params p) gOutputBuffer[tid] = p.obj[0].eval(); } -//TEST_INPUT: entryPointExistentialType Impl - [numthreads(4, 1, 1)] void computeMain(uint3 dispatchThreadID : SV_DispatchThreadID, //TEST_INPUT:ubuffer(data=[0 0 0 0 1 0], stride=4):name=params.obj diff --git a/tests/compute/interface-shader-param-in-struct.slang b/tests/compute/interface-shader-param-in-struct.slang index 1098b4077..62aa093ed 100644 --- a/tests/compute/interface-shader-param-in-struct.slang +++ b/tests/compute/interface-shader-param-in-struct.slang @@ -4,7 +4,6 @@ // inside of structure types to make sure that works //DISABLED_TEST(compute):COMPARE_COMPUTE_EX:-slang -compute - //DISABLED_TEST(compute):COMPARE_COMPUTE_EX:-slang -compute -dx12 -profile sm_6_0 -use-dxil //DISABLED_TEST(compute, vulkan):COMPARE_COMPUTE_EX:-vk -compute @@ -42,7 +41,7 @@ int test( } -//TEST_INPUT:ubuffer(data=[0 0 0 0], stride=4):out +//TEST_INPUT:set gOutputBuffer = out ubuffer(data=[0 0 0 0], stride=4) RWStructuredBuffer<int> gOutputBuffer; // Note: even though `C` doesn't include any @@ -52,7 +51,8 @@ RWStructuredBuffer<int> gOutputBuffer; // that it *will* contain uniform/ordinary data // after specialization. // -//TEST_INPUT:cbuffer(data=[0 0 0 0 0 0 0 0]): +//TEST_INPUT:set C = new{ new MyStrategy{ ubuffer(data=[1 2 4 8], stride=4) } } +//TEST_INPUT: globalExistentialType MyStrategy cbuffer C { IRandomNumberGenerationStrategy gStrategy; @@ -64,23 +64,10 @@ struct Stuff int extra; } -// Note: the data for global-scope existential parameters -// is being introduced *before* the entry point declaration, -// because the default policy used by `slangc` (which the -// render test also uses) is to specialize the parameters at -// the global scope (producing a new layout) and then compose -// that specialized global scope with the entry point. -// -// (The net result is that data related to global-scope -// specialization always precedes the data for entry point -// parameters in these tests today) -// -//TEST_INPUT: globalExistentialType MyStrategy -//TEST_INPUT:ubuffer(data=[1 2 4 8], stride=4): - [numthreads(4, 1, 1)] void computeMain( -//TEST_INPUT:root_constants(data=[0 0 0 0 0 0 0 0 256]): +//TEST_INPUT:set stuff = { new MyModifier{ ubuffer(data=[16 32 64 128], stride=4) }, 256 } +//TEST_INPUT: entryPointExistentialType MyModifier uniform Stuff stuff, uint3 dispatchThreadID : SV_DispatchThreadID) @@ -129,6 +116,3 @@ struct MyModifier : IModifier return val ^ localModifiers[val & 3]; } } - -//TEST_INPUT: entryPointExistentialType MyModifier -//TEST_INPUT:ubuffer(data=[16 32 64 128], stride=4): diff --git a/tests/compute/interface-shader-param.slang b/tests/compute/interface-shader-param.slang index e57ff1bc6..e7c6ceb6a 100644 --- a/tests/compute/interface-shader-param.slang +++ b/tests/compute/interface-shader-param.slang @@ -3,11 +3,11 @@ // Test using interface tops as top-level shader parameters // (whether global, or on an entry point). -//DISABLED_TEST(compute):COMPARE_COMPUTE_EX:-slang -compute +//TEST(compute):COMPARE_COMPUTE_EX:-slang -compute -//DISABLED_TEST(compute):COMPARE_COMPUTE_EX:-slang -compute -dx12 -profile sm_6_0 -use-dxil -//DISABLED_TEST(compute, vulkan):COMPARE_COMPUTE_EX:-vk -compute -//DISABLE_TEST(compute):COMPARE_COMPUTE_EX:-cpu -compute +//TEST(compute):COMPARE_COMPUTE_EX:-slang -compute -dx12 -profile sm_6_0 -use-dxil +//TEST(compute, vulkan):COMPARE_COMPUTE_EX:-vk -compute +//TEST(compute):COMPARE_COMPUTE_EX:-cpu -compute // First we will define some fake interfaces for testing. // Let's pretend we are doing some kind of random number @@ -70,43 +70,27 @@ int test( return modifiedVal; } -// The global-scope parameters for this example include -// some `uniform` parameters, and that will trigger -// the allocation of a global-scope constant buffer to -// hold them. Slang's layout rules mean that the buffer -// will be allocated before any other global-scope parameters. -// -// In this example, the buffer will not be needed after specialization, -// but we need to declare/allocate it here so that the application -// creates a descriptor table/set that matches what the shader -// signature expects. -// -//TEST_INPUT:cbuffer(data=[0], stride=4):name=gStrategy - - - // Now we'll define a shader entry point that will use // these interfaces to define its behavior. // // We'll start with the buffer for writing the test output. -//TEST_INPUT:ubuffer(data=[0 0 0 0], stride=4):out,name=gOutputBuffer +//TEST_INPUT:set gOutputBuffer = out ubuffer(data=[0 0 0 0], stride=4) RWStructuredBuffer<int> gOutputBuffer; // Now we'll define a global shader parameter for the // random number generation strategy. // -//__disabled__TEST_INPUT:object(type=MyStrategy):name=gStrategy +//TEST_INPUT:set gStrategy = new MyStrategy{} uniform IRandomNumberGenerationStrategy gStrategy; // The other parameter (for the modifier) will be attached // the entry point instead, so that we are testing both // cases. // -//__disabled__TEST_INPUT:object(type=MyModifier):name=modifier [numthreads(4, 1, 1)] void computeMain( -//TEST_INPUT:root_constants(data=[0], stride=4): +//TEST_INPUT:set modifier = new MyModifier{} uniform IModifier modifier, uint3 dispatchThreadID : SV_DispatchThreadID) { @@ -150,6 +134,3 @@ struct MyModifier : IModifier return val * 16; } } - -//TEST_INPUT: globalExistentialType MyStrategy -//TEST_INPUT: entryPointExistentialType MyModifier diff --git a/tests/compute/parameter-block.slang b/tests/compute/parameter-block.slang index b1f861236..f7fa718de 100644 --- a/tests/compute/parameter-block.slang +++ b/tests/compute/parameter-block.slang @@ -3,10 +3,6 @@ //TEST(compute):COMPARE_COMPUTE:-vk -shaderobj //TEST(compute):COMPARE_COMPUTE:-shaderobj - -//TEST_INPUT:ubuffer(data=[0 0 0 0], stride=4):out,name=block0.buffer -//TEST_INPUT:ubuffer(data=[0 1 2 3], stride=4):name=block1.buffer - // Ensure that Slang `ParameterBlock` type is lowered // to HLSL in the fashion that we expect. @@ -15,7 +11,10 @@ struct P RWStructuredBuffer<int> buffer; }; +//TEST_INPUT: set block0 = new{ out ubuffer(data=[0 0 0 0], stride=4) } ParameterBlock<P> block0; + +//TEST_INPUT: set block1 = new{ ubuffer(data=[0 1 2 3], stride=4) } ParameterBlock<P> block1; [numthreads(4, 1, 1)] diff --git a/tests/disabled-tests.txt b/tests/disabled-tests.txt index ffc9736db..ea1a2330e 100644 --- a/tests/disabled-tests.txt +++ b/tests/disabled-tests.txt @@ -9,15 +9,53 @@ Test that don't work with shader objects in render-test The following tests were disabled because they had been running on non `-shaderobj` code paths that have since been removed. These tests will need to be re-enabled together with changes to the shader object implementation, or removed entirely if they no longer test useful functionality. +### `ConstantBuffer<ISomething>` + +These tests rely on details of how a `ConstantBuffer<ISomething>` or `ParameterBlock<ISomething>` gets laid out. +We need to fix the compiler and shader object implementation to agree that a `ConstantBuffer<ISomething>` +should compile as `exists T : ISomething . ConstantBuffer<T>` and *not* `ConstantBuffer<exists T : ISomething . T>` +like it currently does. +(The reason for this choice is that we want a shader object created from `SomeConcreteType`, where `SomeConcreteType : ISomething`, to be directly usable for a `ConstantBuffer<ISomething>` parameter with its existing buffer allocation.) + * compute/dynamic-dispatch-12.slang + +### `StructuredBuffer<ISomething>` + +These tests require support for structured buffers where the element type either is an interface type or transitively contains one. + * compute/dynamic-dispatch-13.slang * compute/dynamic-dispatch-14.slang * compute/dynamic-dispatch-bindless-texture.slang -* compute/global-type-param2.slang +* compute/interface-func-param-in-struct.slang +* compute/interface-assoc-type-param.slang + +### Generic Specialization Parameters + +These tests make use of generic specialization parameters in ways that don't easily align with the implementation approach that is more focused on existential parameters. +They should either be ported to use existentials directly (at which point we potentially get rid of support for generic specialization at global or entry-point scope?) or we should refine the implementation of generic specialization to be consistent with existential specialization. + * compute/global-type-param-array.slang * compute/global-type-param1.slang +* compute/global-type-param2.slang +* bugs/gh-357.slang +* compute/assoctype-generic-arg.slang +* compute/global-generic-value-param.slang +* compute/global-type-param-in-entrypoint.slang +* compute/global-type-param.slang +* compute/int-generic.slang + +### Static Specialization + +These tests rely on the ability of the static specialization path to provide locations for data that doesn't "fit" into the fixed-size payload of an existential-type field/value. +They will need to wait until the shader object implementation(s) are updated to support that case. + * compute/interface-shader-param-in-struct.slang * compute/interface-shader-param-legalization.slang + +### Uncategorized + +These tests need to be binned according to what features they need. + * compute/interface-shader-param.slang * compute/performance-profile.slang * compute/rewriter-parameter-block-complex.hlsl @@ -40,4 +78,3 @@ These tests will need to be re-enabled together with changes to the shader objec * render/tess.hlsl * render/unused-discard.hlsl * compute/interface-param-partial-specialize.slang -* compute/array-existential-parameter.slang diff --git a/tools/gfx-util/shader-cursor.cpp b/tools/gfx-util/shader-cursor.cpp index c2c34f5a5..b188901ec 100644 --- a/tools/gfx-util/shader-cursor.cpp +++ b/tools/gfx-util/shader-cursor.cpp @@ -145,19 +145,45 @@ Result ShaderCursor::getField(const char* name, const char* nameEnd, ShaderCurso ShaderCursor ShaderCursor::getElement(SlangInt index) const { // TODO: need to auto-dereference various buffer types... - - if (m_typeLayout->getKind() == slang::TypeReflection::Kind::Array) + switch( m_typeLayout->getKind() ) { - ShaderCursor elementCursor; - elementCursor.m_baseObject = m_baseObject; - elementCursor.m_typeLayout = m_typeLayout->getElementTypeLayout(); - elementCursor.m_offset.uniformOffset = - m_offset.uniformOffset + - index * m_typeLayout->getElementStride(SLANG_PARAMETER_CATEGORY_UNIFORM); - elementCursor.m_offset.bindingRangeIndex = m_offset.bindingRangeIndex; - elementCursor.m_offset.bindingArrayIndex = - m_offset.bindingArrayIndex * m_typeLayout->getElementCount() + index; - return elementCursor; + case slang::TypeReflection::Kind::Array: + { + ShaderCursor elementCursor; + elementCursor.m_baseObject = m_baseObject; + elementCursor.m_typeLayout = m_typeLayout->getElementTypeLayout(); + elementCursor.m_offset.uniformOffset = + m_offset.uniformOffset + + index * m_typeLayout->getElementStride(SLANG_PARAMETER_CATEGORY_UNIFORM); + elementCursor.m_offset.bindingRangeIndex = m_offset.bindingRangeIndex; + elementCursor.m_offset.bindingArrayIndex = + m_offset.bindingArrayIndex * m_typeLayout->getElementCount() + index; + return elementCursor; + } + break; + + case slang::TypeReflection::Kind::Struct: + { + // The logic here is similar to `getField()` except that we don't + // need to look up the field index based on a name first. + // + auto fieldIndex = index; + slang::VariableLayoutReflection* fieldLayout = + m_typeLayout->getFieldByIndex((unsigned int)fieldIndex); + if(!fieldLayout) + return ShaderCursor(); + + ShaderCursor fieldCursor; + fieldCursor.m_baseObject = m_baseObject; + fieldCursor.m_typeLayout = fieldLayout->getTypeLayout(); + fieldCursor.m_offset.uniformOffset = m_offset.uniformOffset + fieldLayout->getOffset(); + fieldCursor.m_offset.bindingRangeIndex = + m_offset.bindingRangeIndex + m_typeLayout->getFieldBindingRangeOffset(fieldIndex); + fieldCursor.m_offset.bindingArrayIndex = m_offset.bindingArrayIndex; + + return fieldCursor; + } + break; } return ShaderCursor(); diff --git a/tools/gfx/d3d12/render-d3d12.cpp b/tools/gfx/d3d12/render-d3d12.cpp index 6b818f100..891077a8b 100644 --- a/tools/gfx/d3d12/render-d3d12.cpp +++ b/tools/gfx/d3d12/render-d3d12.cpp @@ -1059,83 +1059,88 @@ public: } } + /// Stores offset information to apply to the reflected register/space for a descriptor range. + /// struct BindingRegisterOffset { - // The index to the physical descriptor set that stores the binding. - uint32_t descriptorSetIndex; - - uint32_t spaceOffset; // The `space` index as specified in shader. - uint32_t textureOffset; // `t` registers - uint32_t samplerOffset; // `s` registers - uint32_t constantBufferOffset; // `b` registers - uint32_t uavOffset; // `u` registers - void set(D3D12_DESCRIPTOR_RANGE_TYPE type, uint32_t value) + uint32_t spaceOffset = 0; // The `space` index as specified in shader. + + /// An offset to apply for each D3D12 register class, as given + /// by a `D3D12_DESCRIPTOR_RANGE_TYPE`. + /// + /// Note that the `D3D12_DESCRIPTOR_RANGE_TYPE` enumeration has + /// values between 0 and 3, inclusive. + /// + uint32_t offsetForRangeType[4] = {0, 0, 0, 0}; + + uint32_t& operator[](D3D12_DESCRIPTOR_RANGE_TYPE type) { - switch (type) - { - case D3D12_DESCRIPTOR_RANGE_TYPE_CBV: - constantBufferOffset = value; - return; - case D3D12_DESCRIPTOR_RANGE_TYPE_UAV: - uavOffset = value; - return; - case D3D12_DESCRIPTOR_RANGE_TYPE_SRV: - textureOffset = value; - return; - case D3D12_DESCRIPTOR_RANGE_TYPE_SAMPLER: - samplerOffset = value; - return; - default: - break; - } + return offsetForRangeType[int(type)]; } - uint32_t get(D3D12_DESCRIPTOR_RANGE_TYPE type) + + uint32_t operator[](D3D12_DESCRIPTOR_RANGE_TYPE type) const { - switch (type) - { - case D3D12_DESCRIPTOR_RANGE_TYPE_CBV: - return constantBufferOffset; - case D3D12_DESCRIPTOR_RANGE_TYPE_UAV: - return uavOffset; - case D3D12_DESCRIPTOR_RANGE_TYPE_SRV: - return textureOffset; - case D3D12_DESCRIPTOR_RANGE_TYPE_SAMPLER: - return samplerOffset; - default: - return 0; - } + return offsetForRangeType[int(type)]; } }; - void addDescriptorRange( - slang::TypeLayoutReflection* typeLayout, - D3D12_DESCRIPTOR_RANGE_TYPE rangeType, - Index bindingRangeIndex, - BindingRegisterOffset* offset, - BindingRegisterOffset* newOffset) + /// Add a new descriptor set to the layout being computed. + /// + /// Note that a "descriptor set" in the layout may amount to + /// zero, one, or two different descriptor *tables* in the + /// final D3D12 root signature. Each descriptor set may + /// contain zero or more view ranges (CBV/SRV/UAV) and zero + /// or more sampler ranges. It maps to a view descriptor table + /// if the number of view ranges is non-zero and to a sampler + /// descriptor table if the number of sampler ranges is non-zero. + /// + uint32_t addDescriptorSet() + { + auto result = (uint32_t) m_descriptorSets.getCount(); + m_descriptorSets.add(DescriptorSetLayout{}); + return result; + } + + /// Add one descriptor range as specified in Slang reflection information to the layout. + /// + /// The layout information is taken from `typeLayout` for the descriptor + /// range with the given `descriptorRangeIndex` within the logical + /// descriptor set (reflected by Slang) with the given `logicalDescriptorSetIndex`. + /// + /// The `physicalDescriptorSetIndex` is the index in the `m_descriptorSets` array of + /// the descriptor set that the range should be added to. + /// + /// The `offset` encodes information about space and/or register offsets that + /// should be applied to descrptor ranges. + /// + /// This operation can fail if the given descriptor range encodes a range that + /// doesn't map to anything directly supported by D3D12. Higher-level routines + /// will often want to ignore such failures. + /// + Result addDescriptorRange( + slang::TypeLayoutReflection* typeLayout, + Index physicalDescriptorSetIndex, + BindingRegisterOffset const& offset, + Index logicalDescriptorSetIndex, + Index descriptorRangeIndex) { + auto& descriptorSet = m_descriptorSets[physicalDescriptorSetIndex]; + + auto bindingType = typeLayout->getDescriptorSetDescriptorRangeType(logicalDescriptorSetIndex, descriptorRangeIndex); + auto count = typeLayout->getDescriptorSetDescriptorRangeDescriptorCount(logicalDescriptorSetIndex, descriptorRangeIndex); + auto index = typeLayout->getDescriptorSetDescriptorRangeIndexOffset(logicalDescriptorSetIndex, descriptorRangeIndex); + auto space = typeLayout->getDescriptorSetSpaceOffset(logicalDescriptorSetIndex); + + D3D12_DESCRIPTOR_RANGE_TYPE rangeType; + SLANG_RETURN_ON_FAIL(translateDescriptorRangeType(bindingType, &rangeType)); + D3D12_DESCRIPTOR_RANGE range = {}; range.RangeType = rangeType; - auto descriptorRangeIndex = - typeLayout->getBindingRangeFirstDescriptorRangeIndex(bindingRangeIndex); - auto relativeSpaceIndex = - (uint32_t)typeLayout->getBindingRangeDescriptorSetIndex(bindingRangeIndex); - auto space = offset->spaceOffset + relativeSpaceIndex; - // Update descriptor range descs in current descriptor set. - auto& descriptorSet = m_descriptorSets[offset->descriptorSetIndex]; - range.NumDescriptors = - (UINT)typeLayout->getDescriptorSetDescriptorRangeDescriptorCount( - relativeSpaceIndex, descriptorRangeIndex); - range.BaseShaderRegister = - (UINT)typeLayout->getDescriptorSetDescriptorRangeIndexOffset( - relativeSpaceIndex, descriptorRangeIndex) + - offset->get(range.RangeType); - newOffset->set( - range.RangeType, - Math::Max(range.BaseShaderRegister + 1, newOffset->get(range.RangeType))); + range.NumDescriptors = (UINT) count; + range.BaseShaderRegister = (UINT) index + offset[rangeType]; + range.RegisterSpace = (UINT) space; range.OffsetInDescriptorsFromTableStart = D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND; - range.RegisterSpace = space; if (range.RangeType == D3D12_DESCRIPTOR_RANGE_TYPE_SAMPLER) { descriptorSet.m_samplerRanges.add(range); @@ -1146,110 +1151,204 @@ public: descriptorSet.m_resourceRanges.add(range); descriptorSet.m_resourceCount += range.NumDescriptors; } + + return SLANG_OK; } - void addObject(slang::TypeLayoutReflection* typeLayout, BindingRegisterOffset* offset) + /// Add one binding range to the computed layout. + /// + /// The layout information is taken from `typeLayout` for the binding + /// range with the given `bindingRangeIndex`. + /// + /// The `physicalDescriptorSetIndex` is the index in the `m_descriptorSets` array of + /// the descriptor set that the range should be added to. + /// + /// The `offset` encodes information about space and/or register offsets that + /// should be applied to descrptor ranges. + /// + /// Note that a single binding range may encompass zero or more descriptor ranges. + /// + void addBindingRange( + slang::TypeLayoutReflection* typeLayout, + Index physicalDescriptorSetIndex, + BindingRegisterOffset const& offset, + Index bindingRangeIndex) { - typeLayout = _unwrapParameterGroups(typeLayout); - SlangInt bindingRangeCount = typeLayout->getBindingRangeCount(); - // `register` and `space` index offset of future sub-objects. - BindingRegisterOffset subObjectOffset = *offset; - for (SlangInt i = 0; i < bindingRangeCount; i++) + auto logicalDescriptorSetIndex = typeLayout->getBindingRangeDescriptorSetIndex(bindingRangeIndex); + auto firstDescriptorRangeIndex = typeLayout->getBindingRangeFirstDescriptorRangeIndex(bindingRangeIndex); + Index descriptorRangeCount = typeLayout->getBindingRangeDescriptorRangeCount(bindingRangeIndex); + for( Index i = 0; i < descriptorRangeCount; ++i ) + { + auto descriptorRangeIndex = firstDescriptorRangeIndex + i; + + // Note: we ignore the `Result` returned by `addDescriptorRange()` because we + // want to silently skip any ranges that represent kinds of bindings that + // don't actually exist in D3D12. + // + addDescriptorRange(typeLayout, physicalDescriptorSetIndex, offset, logicalDescriptorSetIndex, descriptorRangeIndex); + } + } + + /// Add binding ranges and parameter blocks to the root signature. + /// + /// The layout information is taken from `varLayout` which should + /// be a layout for either a program or an entry point. + /// + /// The `physicalDescriptorSetIndex` is the index in the `m_descriptorSets` array of + /// the descriptor set that binding ranges not belonging to nested + /// parameter blocks should be added to. + /// + /// This routine will use absolute offset information computed from `varLayout` + /// to apply appropriate space/register offsets to the bindings and parameter + /// blocks inside the layout. + /// + void addBindingRangesAndParameterBlocks( + slang::VariableLayoutReflection* varLayout, + Index physicalDescriptorSetIndex) + { + BindingRegisterOffset offset; + offset.spaceOffset = (UINT) varLayout->getOffset(SLANG_PARAMETER_CATEGORY_REGISTER_SPACE); + offset[D3D12_DESCRIPTOR_RANGE_TYPE_CBV] = (UINT) varLayout->getOffset(SLANG_PARAMETER_CATEGORY_CONSTANT_BUFFER); + offset[D3D12_DESCRIPTOR_RANGE_TYPE_SRV] = (UINT) varLayout->getOffset(SLANG_PARAMETER_CATEGORY_SHADER_RESOURCE); + offset[D3D12_DESCRIPTOR_RANGE_TYPE_UAV] = (UINT) varLayout->getOffset(SLANG_PARAMETER_CATEGORY_UNORDERED_ACCESS); + offset[D3D12_DESCRIPTOR_RANGE_TYPE_SAMPLER] = (UINT) varLayout->getOffset(SLANG_PARAMETER_CATEGORY_SAMPLER_STATE); + + addBindingRangesAndParameterBlocks(varLayout->getTypeLayout(), physicalDescriptorSetIndex, offset); + } + + /// Add binding ranges and parameter blocks to the root signature. + /// + /// The layout information is taken from `typeLayout` which should + /// be a layout for either a program or an entry point. + /// + /// The `physicalDescriptorSetIndex` is the index in the `m_descriptorSets` array of + /// the descriptor set that binding ranges not belonging to nested + /// parameter blocks should be added to. + /// + /// The `offset` encodes information about space and/or register offsets that + /// should be applied to descrptor ranges. + /// + void addBindingRangesAndParameterBlocks( + slang::TypeLayoutReflection* typeLayout, + Index physicalDescriptorSetIndex, + BindingRegisterOffset const& offset) + { + // Our first task is to add the binding ranges for stuff that is + // directly contained in `typeLayout` rather than via sub-objects. + // + // Our goal is to have the descriptors for directly-contained views/samplers + // always be contiguous in CPU and GPU memory, so that we can write + // to them easily with a single operaiton. + // + Index bindingRangeCount = typeLayout->getBindingRangeCount(); + for (Index bindingRangeIndex = 0; bindingRangeIndex < bindingRangeCount; bindingRangeIndex++) { - auto bindingType = typeLayout->getBindingRangeType(i); - D3D12_DESCRIPTOR_RANGE_TYPE rangeType; - if (translateDescriptorRangeType(bindingType, &rangeType) != SLANG_OK) + // We will look at the type of each binding range and intentionally + // skip those that represent sub-objects. + // + auto bindingType = typeLayout->getBindingRangeType(bindingRangeIndex); + switch(bindingType) { - // Ignore all descriptor ranges that does not map directly into a - // d3d descriptor. + case slang::BindingType::ConstantBuffer: + case slang::BindingType::ParameterBlock: + case slang::BindingType::ExistentialValue: continue; + + default: + break; } - // The CBV descriptor range, along with any additional descriptor ranges associated - // with the constant buffer binding range, will be appended to the end of this object's - // descriptor table, so we skip them now. - if (bindingType == slang::BindingType::ConstantBuffer) - continue; - addDescriptorRange(typeLayout, rangeType, i, offset, &subObjectOffset); + + // For binding ranges that don't represent sub-objects, we will add + // all of the descriptor ranges they encompass to the root signature. + // + addBindingRange(typeLayout, physicalDescriptorSetIndex, offset, bindingRangeIndex); } - auto subObjectCount = typeLayout->getSubObjectRangeCount(); - for (SlangInt i = 0; i < subObjectCount; i++) + + // Next we need to recurse on the various sub-objects that the type might contain. + // + Index subObjectCount = typeLayout->getSubObjectRangeCount(); + for (Index subObjectRangeIndex = 0; subObjectRangeIndex < subObjectCount; subObjectRangeIndex++) { - auto rangeIndex = typeLayout->getSubObjectRangeBindingRangeIndex(i); - switch (typeLayout->getBindingRangeType(rangeIndex)) + // There are a few different concerns being tackled at once here, which depend on + // the type of each sub-object range. + // + auto bindingRangeIndex = typeLayout->getSubObjectRangeBindingRangeIndex(subObjectRangeIndex); + auto bindingType = typeLayout->getBindingRangeType(bindingRangeIndex); + switch (bindingType) { case slang::BindingType::ConstantBuffer: { - auto subObjectType = typeLayout->getBindingRangeLeafTypeLayout(rangeIndex); - auto subObjectElementType = _unwrapParameterGroups(subObjectType); - if (subObjectElementType->getSize(SLANG_PARAMETER_CATEGORY_UNIFORM) != 0) - { - addDescriptorRange( - typeLayout, - D3D12_DESCRIPTOR_RANGE_TYPE_CBV, - rangeIndex, - offset, - &subObjectOffset); - } - addObject(subObjectType, &subObjectOffset); + // Constant buffer ranges (for `ConstantBuffer<ConcreteType>`) will "leak" their + // binding ranges into the surrounding type, so we can add them here like any other + // binding range. + // + // Note: It would be valid to allow `slang::BindingType::ConstantBuffer` to be handled + // in the earlier loop, but that would mean that descriptor ranges coming directly + // from the fields of `typeLayout` could be broken up with ranges coming from constant-buffer + // sub-objects. By moving the handling of constant buffers to this later loop, we + // guarantee that the descritpors used by non-sub-object binding ranges are all + // contiguous. + // + addBindingRange(typeLayout, physicalDescriptorSetIndex, offset, bindingRangeIndex); } break; + case slang::BindingType::ParameterBlock: { - BindingRegisterOffset newOffset = {}; - newOffset.descriptorSetIndex = (uint32_t)m_descriptorSets.getCount(); - m_descriptorSets.add(DescriptorSetLayout{}); - newOffset.spaceOffset = - offset->spaceOffset + - (uint32_t)typeLayout->getBindingRangeDescriptorSetIndex(rangeIndex); - auto subObjectType = - typeLayout->getBindingRangeLeafTypeLayout(rangeIndex); - addObject(subObjectType, &newOffset); + // A parameter block (`ParameterBlock<ConcreteType>`) will always map to a distinct + // descriptor set, and its contained view/sampler binding ranges will be bound + // through that set. + // + auto blockPhysicalDescriptorSetIndex = addDescriptorSet(); + + // We will need to recursively add the binding ranges implied by the type of + // the parameter block. + // + auto blockTypeLayout = typeLayout->getBindingRangeLeafTypeLayout(bindingRangeIndex); + + // One important detail is that the `blockTypeLayout` does not know the base register + // space that the contents of the block should use. All descriptor ranges stored in + // that layout will by default use a space offset of zero. + // + // We need to compute the space offset to apply when recursing into that block type + // based on the binding range we are processing here. + // + auto blockLogicalDescriptorSetIndex = typeLayout->getBindingRangeDescriptorSetIndex(bindingRangeIndex); + auto blockSpaceOffset = typeLayout->getDescriptorSetSpaceOffset(blockLogicalDescriptorSetIndex); + + // The space offset for this binding range should be added to any additional space + // offset that was being passed down from above this layer. + // + // Any other offset information (register offsets) should be ignored at this point, + // because `register` offsets from outside of the block don't affect layout within + // the block. + // + BindingRegisterOffset blockOffset; + blockOffset.spaceOffset = offset.spaceOffset + (uint32_t) blockSpaceOffset; + + // Once we have all the details worked out, we can write the binding ranges for the + // block's type into the newly-allocated descriptor set. + // + // Note: there is an important subtlety going on here. We are passing in the type + // `blockTypeLayout` which corresponds to `ParameterBlock<ConcreteType>` and *not* to + // `ConcreteType` alone. Because of that detail, the binding/descriptor ranges will + // include any "default constant buffer" range that needed to be allocated based + // on `ConcreteType`. + // + // TODO: validate that this logic is right. + // + addBindingRangesAndParameterBlocks(blockTypeLayout, blockPhysicalDescriptorSetIndex, blockOffset); } break; - } - } - *offset = subObjectOffset; - } - - static BindingRegisterOffset getOffsetFromVarLayout( - slang::VariableLayoutReflection* varLayout) - { - BindingRegisterOffset offset; - offset.descriptorSetIndex = 0; - offset.spaceOffset = - (uint32_t)varLayout->getOffset(SLANG_PARAMETER_CATEGORY_REGISTER_SPACE); - offset.samplerOffset = - (uint32_t)varLayout->getOffset(SLANG_PARAMETER_CATEGORY_SAMPLER_STATE); - offset.textureOffset = - (uint32_t)varLayout->getOffset(SLANG_PARAMETER_CATEGORY_SHADER_RESOURCE); - offset.constantBufferOffset = - (uint32_t)varLayout->getOffset(SLANG_PARAMETER_CATEGORY_CONSTANT_BUFFER); - offset.uavOffset = - (uint32_t)varLayout->getOffset(SLANG_PARAMETER_CATEGORY_UNORDERED_ACCESS); - return offset; - } - void addObject( - slang::TypeLayoutReflection* typeLayout, - slang::VariableLayoutReflection* varLayout) - { - auto offset = getOffsetFromVarLayout(varLayout); - addObject(typeLayout, &offset); - } + case slang::BindingType::ExistentialValue: + // TODO: Need to handle this case here. + break; - void addEntryPoint(slang::EntryPointReflection* entryPoint) - { - BindingRegisterOffset offset = getOffsetFromVarLayout(entryPoint->getVarLayout()); - if (entryPoint->hasDefaultConstantBuffer()) - { - addDescriptorRange( - entryPoint->getTypeLayout(), - D3D12_DESCRIPTOR_RANGE_TYPE_CBV, - 0, - &offset, - &offset); + default: + break; + } } - addObject(entryPoint->getTypeLayout(), &offset); } D3D12_ROOT_SIGNATURE_DESC& build( @@ -1307,19 +1406,46 @@ public: ID3D12RootSignature** outRootSignature, List<DescriptorSetInfo>& outRootDescriptorSetInfos) { + // We are going to build up the root signature by adding + // binding/descritpor ranges and nested parameter blocks + // based on the computed layout information for `program`. + // RootSignatureDescBuilder builder; - builder.m_descriptorSets.add(DescriptorSetLayout{}); - auto layout = program->getLayout(); - auto globalParamLayout = layout->getGlobalParamsTypeLayout(); - auto globalVarLayout = layout->getGlobalParamsVarLayout(); - builder.addObject(globalParamLayout, globalVarLayout); + // The layout information computed by Slang breaks up shader + // parameters into what we can think of as "logical" descriptor + // sets based on whether or not parameters have the same `space`. + // + // We want to basically ignore that decomposition and generate a + // single descriptor set to hold all top-level parameters, and only + // generate distinct descriptor sets when the shader has opted in + // via explicit parameter blocks. + // + // To achieve this goal, we will manually allocate a default descriptor + // set for root parameters in our signature, and then recursively + // add all the binding/descriptor ranges implied by the global-scope + // parameters. + // + auto rootDescriptorSetIndex = builder.addDescriptorSet(); + builder.addBindingRangesAndParameterBlocks(layout->getGlobalParamsVarLayout(), rootDescriptorSetIndex); for (SlangUInt i = 0; i < layout->getEntryPointCount(); i++) { + // Entry-point parameters should also be added to the default root + // descriptor set. + // + // We add the parameters using the "variable layout" for the entry point + // and not just its type layout, to ensure that any offset information is + // applied correctly to the `register` and `space` information for entry-point + // parameters. + // + // Note: When we start to support DXR we will need to handle entry-point parameters + // differently because they will need to map to local root signatures rather than + // being included in the global root signature as is being done here. + // auto entryPoint = layout->getEntryPointByIndex(i); - builder.addEntryPoint(entryPoint); + builder.addBindingRangesAndParameterBlocks(entryPoint->getVarLayout(), rootDescriptorSetIndex); } auto& rootSignatureDesc = builder.build(outRootDescriptorSetInfos); @@ -2034,21 +2160,13 @@ public: SLANG_RETURN_ON_FAIL(_writeOrdinaryData( encoder, m_ordinaryDataBuffer, 0, specializedOrdinaryDataSize, specializedLayout)); - return SLANG_OK; - } - - /// Bind the buffer for ordinary/uniform data, if needed - Result _bindOrdinaryDataBufferIfNeeded(PipelineCommandEncoder* encoder) - { - // We start by ensuring that the buffer is created, if it is needed. - // - SLANG_RETURN_ON_FAIL(_ensureOrdinaryDataBufferCreatedIfNeeded(encoder)); - - // If we did indeed need/create a buffer, then we must bind it - // into root binding state. - // - if (m_ordinaryDataBuffer) { + // We also create and store a descriptor for our root constant buffer + // into the descriptor table allocation that was reserved for them. + // + // We always know that the ordinary data buffer will be the first descriptor + // in the table of resource views. + // auto descriptorTable = m_descriptorSet.m_resourceTable; D3D12_CONSTANT_BUFFER_VIEW_DESC viewDesc = {}; viewDesc.BufferLocation = @@ -2067,7 +2185,7 @@ public: virtual Result bindObject(PipelineCommandEncoder* encoder, RootBindingState* bindingState) { ShaderObjectLayoutImpl* layout = getLayout(); - SLANG_RETURN_ON_FAIL(_bindOrdinaryDataBufferIfNeeded(encoder)); + SLANG_RETURN_ON_FAIL(_ensureOrdinaryDataBufferCreatedIfNeeded(encoder)); uint32_t descTableIndex = bindingState->rootParamIndex; auto& descSet = m_descriptorSet; if (descSet.m_resourceCount) @@ -2677,7 +2795,10 @@ public: // Submit - setting for graphics { GraphicsSubmitter submitter(m_d3dCmdList); - _bindRenderState(&submitter); + if(SLANG_FAILED(_bindRenderState(&submitter))) + { + assert(!"Failed to bind render state"); + } } m_d3dCmdList->IASetPrimitiveTopology(m_primitiveTopology); @@ -2844,7 +2965,10 @@ public: // Submit binding for compute { ComputeSubmitter submitter(m_d3dCmdList); - _bindRenderState(&submitter); + if(SLANG_FAILED(_bindRenderState(&submitter))) + { + assert(!"Failed to bind render state"); + } } m_d3dCmdList->Dispatch(x, y, z); } @@ -3238,7 +3362,7 @@ Result D3D12Device::PipelineCommandEncoder::_bindRenderState(Submitter* submitte submitter->setRootSignature(programImpl->m_rootObjectLayout->m_rootSignature); ShortList<DescriptorTable, kMaxDescriptorSetCount> descriptorTables; RefPtr<ShaderObjectLayoutImpl> specializedRootLayout; - rootObjectImpl->getSpecializedLayout(specializedRootLayout.writeRef()); + SLANG_RETURN_ON_FAIL(rootObjectImpl->getSpecializedLayout(specializedRootLayout.writeRef())); RootShaderObjectLayoutImpl* rootLayoutImpl = static_cast<RootShaderObjectLayoutImpl*>(specializedRootLayout.Ptr()); for (auto& descSet : rootLayoutImpl->m_gpuDescriptorSetInfos) diff --git a/tools/render-test/render-test-main.cpp b/tools/render-test/render-test-main.cpp index 27311d67a..db51339f1 100644 --- a/tools/render-test/render-test-main.cpp +++ b/tools/render-test/render-test-main.cpp @@ -274,19 +274,25 @@ struct AssignValsFromLayoutContext if(field.name.getLength() == 0) { - StdWriters::getError().print("error: entries in `ShaderInputLayout` must include a name\n"); - return SLANG_E_INVALID_ARG; + // If no name was given, assume by-indexing matching is requested + auto fieldCursor = dstCursor.getElement(fieldIndex); + if(!fieldCursor.isValid()) + { + StdWriters::getError().print("error: could not find shader parameter at index %d\n", (int)fieldIndex); + return SLANG_E_INVALID_ARG; + } + SLANG_RETURN_ON_FAIL(assign(fieldCursor, field.val)); } - - auto fieldCursor = dstCursor.getPath(field.name.getBuffer()); - - if(!fieldCursor.isValid()) + else { - StdWriters::getError().print("error: could not find shader parameter matching '%s'\n", field.name.begin()); - return SLANG_E_INVALID_ARG; + auto fieldCursor = dstCursor.getPath(field.name.getBuffer()); + if(!fieldCursor.isValid()) + { + StdWriters::getError().print("error: could not find shader parameter matching '%s'\n", field.name.begin()); + return SLANG_E_INVALID_ARG; + } + SLANG_RETURN_ON_FAIL(assign(fieldCursor, field.val)); } - - assign(fieldCursor, field.val); } return SLANG_OK; } @@ -329,7 +335,7 @@ struct AssignValsFromLayoutContext ComPtr<IShaderObject> shaderObject = device->createShaderObject(slangType); - assign(ShaderCursor(shaderObject), srcVal->contentVal); + SLANG_RETURN_ON_FAIL(assign(ShaderCursor(shaderObject), srcVal->contentVal)); dstCursor.setObject(shaderObject); return SLANG_OK; diff --git a/tools/render-test/shader-input-layout.cpp b/tools/render-test/shader-input-layout.cpp index c5f1cb6dd..4febc4bd5 100644 --- a/tools/render-test/shader-input-layout.cpp +++ b/tools/render-test/shader-input-layout.cpp @@ -504,10 +504,17 @@ namespace renderer_test val->contentVal = parseValExpr(parser); return val; } + else if( parser.AdvanceIf("out") ) + { + auto val = parseValExpr(parser); + val->isOutput = true; + return val; + } else { - // TODO: other named cases - throw ShaderInputLayoutFormatException(String("Unexpected '") + parser.NextToken().Content + String("' at line") + String(parser.NextToken().Position.Line)); + // We assume that any other word is introducing one of the other + // cases for a parse-able value. + return parseVal(parser); } } break; diff --git a/tools/render-test/slang-support.cpp b/tools/render-test/slang-support.cpp index aca354763..840b0d3e2 100644 --- a/tools/render-test/slang-support.cpp +++ b/tools/render-test/slang-support.cpp @@ -135,6 +135,10 @@ void ShaderCompilerUtil::Output::reset() { spSetPassThrough(slangRequest, input.passThrough); } + else + { + spSetCompileFlags(slangRequest, SLANG_COMPILE_FLAG_NO_CODEGEN); + } // Process any additional command-line options specified for Slang using // the `-xslang <arg>` option to `render-test`. |
