summaryrefslogtreecommitdiffstats
path: root/tests
diff options
context:
space:
mode:
authorTim Foley <tfoleyNV@users.noreply.github.com>2019-05-21 08:52:37 -0700
committerGitHub <noreply@github.com>2019-05-21 08:52:37 -0700
commit7ffe6f03976c98ba0233483d0182fc0ad600fd7a (patch)
tree2c79531e834879bb6cfc468a648a56bfb49fc496 /tests
parent71e35b6822b9e2846e129a888774d45a5e0827da (diff)
Allow interface types to be used inside of structs (#966)
Previously, interface types were allowed to be used directly as function parameters, local variables, and global shader parameters. Using an interface type as a field of a `struct` type or a `cbuffer` declaration was not implemented. This change adds that support, and fixes several unrelated issues that caused problems in doing so. * The most important work here was adding a case for `IRStructType` to `maybeSpecializeBindExistentialsType` that creates a specialized variant of a `struct` type on-demand based on specialization operands. This logic loops over the fields of the original struct, and creates new fields by binding the existentials/interfaces in the type of each field. Caching is used to ensure that the same `struct` type specialized to the same operands should yield the same result. * To allow subsequent specialization to occur when a `struct` with interface-type fields is used, it was also necessary to specialize field-address and field-extract instructions in cases where the value that the field is being extracted from is a `wrapExistential`. * Similarly, we neede to make sure that the logic for specializing called functions based on the concrete types for interfaces in the argument list would also take into account `struct` types with existential-type fields inside of them. * Doing the above changes revealed some serious flaws in how the `ir-specialize.cpp` logic was tracking which instructions still needed to be processed. It had previously been assuming that it could assume any relevant instructions were on its work list, and when the work list went empty it could exit. This runs into two problems: (1) sometimes we create new instructions when specializing, and it may be impossible to ensure that all the new instructions (e.g., those created by utility routines in other files) get added to the work list, and (2) sometimes the instruction(s) that need to be re-visited when we specialize something aren't its direct users, but instead somethign that transitively depends on the instruction. These issues were fixed by two changes to the pass: (1) we now maintain a list of known "clean" instructions instead of implicitly using the work-list as a list of "dirty" instructions (so that implicitly any new instruction is dirty), and periodically iterating over all instructions to add the non-clean ones to the work list for processing, and (2) when an instruction is specialized/replaced we mark everything that transitively depends on it "dirty" (by removing it from the "clean" list). * Added some logic to "fix up" the type of an IR function after changes that might modify its parameter list. Failing to have this logic meant that certain types were still live (because they were referenced by a function type) that couldn't actually be emitted as legal HLSL/GLSL. * Added some special cases to IR instruction creation for `wrapExistential` and `BindExistentialsType` so that they act as no-ops when there are no "slots" providing specialization information. This helps avoid some special cases when specializing structure fields (since some fields specialization and others don't, so in general there are zero or more operands specific to each field). * Added a test case that uses an interface type in a `cbuffer`, as well as an interface type in a `struct` passed as an entry-point `uniform` parameter. * Fixed up some parts of the `.natvis` files to reflect naming changes from a previous PR and thus restore some of the useful Visual Studio debugging experience for Slang.
Diffstat (limited to 'tests')
-rw-r--r--tests/compute/interface-shader-param-in-struct.slang127
-rw-r--r--tests/compute/interface-shader-param-in-struct.slang.expected.txt4
2 files changed, 131 insertions, 0 deletions
diff --git a/tests/compute/interface-shader-param-in-struct.slang b/tests/compute/interface-shader-param-in-struct.slang
new file mode 100644
index 000000000..2ffc70c36
--- /dev/null
+++ b/tests/compute/interface-shader-param-in-struct.slang
@@ -0,0 +1,127 @@
+// interface-shader-param-in-struct.slang
+
+// This test puts interface-type shader parameters
+// inside of structure types to make sure that works
+
+//TEST(compute):COMPARE_COMPUTE_EX:-slang -compute
+//TEST(compute):COMPARE_COMPUTE_EX:-slang -compute -dx12
+//TEST(compute, vulkan):COMPARE_COMPUTE_EX:-vk -compute
+
+// A lot of the setup is the same as for `interface-shader-param`,
+// so look there if you want the comments.
+
+interface IRandomNumberGenerator
+{
+ [mutating]
+ int randomInt();
+}
+
+interface IRandomNumberGenerationStrategy
+{
+ associatedtype Generator : IRandomNumberGenerator;
+ Generator makeGenerator(int seed);
+}
+
+interface IModifier
+{
+ int modify(int val);
+}
+
+int test(
+ int seed,
+ IRandomNumberGenerationStrategy inStrategy,
+ IModifier modifier)
+{
+ let strategy = inStrategy;
+ var generator = strategy.makeGenerator(seed);
+ let unused = generator.randomInt();
+ let val = generator.randomInt();
+ let modifiedVal = modifier.modify(val);
+ return modifiedVal;
+}
+
+
+//TEST_INPUT:ubuffer(data=[0 0 0 0], stride=4):dxbinding(0),glbinding(0),out
+RWStructuredBuffer<int> gOutputBuffer;
+
+cbuffer C
+{
+ IRandomNumberGenerationStrategy gStrategy;
+}
+
+struct Stuff
+{
+ IModifier modifier;
+ int extra;
+}
+
+[numthreads(4, 1, 1)]
+void computeMain(
+//TEST_INPUT:cbuffer(data=[256]):dxbinding(0),glbinding(2)
+ uniform Stuff stuff,
+
+ uint3 dispatchThreadID : SV_DispatchThreadID)
+{
+ let tid = dispatchThreadID.x;
+
+ let inputVal : int = tid;
+ let outputVal = test(inputVal, gStrategy, stuff.modifier)
+ + stuff.extra*stuff.extra;
+
+ gOutputBuffer[tid] = outputVal;
+}
+
+// Okay, now we get to the part that is unique starting
+// in this test: we add data to the concrete types
+// that we will use as parameters.
+
+struct MyStrategy : IRandomNumberGenerationStrategy
+{
+ RWStructuredBuffer<int> globalSeeds;
+
+ struct Generator : IRandomNumberGenerator
+ {
+ int state;
+
+ [mutating]
+ int randomInt()
+ {
+ return state++;
+ }
+ }
+
+ Generator makeGenerator(int seed)
+ {
+ Generator generator = { globalSeeds[seed] };
+ return generator;
+ }
+}
+
+struct MyModifier : IModifier
+{
+ RWStructuredBuffer<int> localModifiers;
+
+ int modify(int val)
+ {
+ return val ^ localModifiers[val & 3];
+ }
+}
+
+//TEST_INPUT: globalExistentialType MyStrategy
+//TEST_INPUT: entryPointExistentialType MyModifier
+
+// The concrete types we plug in for `gStrategy` and `modifier`
+// have buffer resources in them, so we need to assign them
+// data. The registers/bindings for these parameters will
+// always come after all other shader parameters, and their
+// relative order will match the relative order of their
+// declarations in the global order that Slang uses for
+// assigning bindings (all globals before all entry point parameters).
+//
+// Here's the data for `gStrategy`:
+//
+//TEST_INPUT:ubuffer(data=[1 2 4 8], stride=4):dxbinding(1),glbinding(1)
+//
+// Here's the data for `stuff.modifier`:
+//
+//TEST_INPUT:ubuffer(data=[16 32 64 128], stride=4):dxbinding(2),glbinding(3)
diff --git a/tests/compute/interface-shader-param-in-struct.slang.expected.txt b/tests/compute/interface-shader-param-in-struct.slang.expected.txt
new file mode 100644
index 000000000..e962124e1
--- /dev/null
+++ b/tests/compute/interface-shader-param-in-struct.slang.expected.txt
@@ -0,0 +1,4 @@
+10042
+10083
+10025
+10029