Skip to content

Commit 7ffe6f0

Browse files
author
Tim Foley
authored
Allow interface types to be used inside of structs (shader-slang#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.
1 parent 71e35b6 commit 7ffe6f0

8 files changed

+633
-58
lines changed

source/core/core.natvis

+15-15
Original file line numberDiff line numberDiff line change
@@ -3,41 +3,41 @@
33
<AutoVisualizer xmlns="http://schemas.microsoft.com/vstudio/debugger/natvis/2010">
44

55
<Type Name="Slang::String">
6-
<DisplayString>{((char*) (buffer.pointer+1)),s}</DisplayString>
7-
<StringView>((char*) (buffer.pointer+1)),s</StringView>
6+
<DisplayString>{((char*) (m_buffer.pointer+1)),s}</DisplayString>
7+
<StringView>((char*) (m_buffer.pointer+1)),s</StringView>
88
</Type>
99

1010
<Type Name="Slang::ArrayView&lt;*&gt;">
11-
<DisplayString>{{ size={_count} }}</DisplayString>
11+
<DisplayString>{{ size={m_count} }}</DisplayString>
1212
<Expand>
13-
<Item Name="[size]">_count</Item>
13+
<Item Name="[size]">m_count</Item>
1414
<ArrayItems>
15-
<Size>_count</Size>
16-
<ValuePointer>_buffer</ValuePointer>
15+
<Size>m_count</Size>
16+
<ValuePointer>m_buffer</ValuePointer>
1717
</ArrayItems>
1818
</Expand>
1919
</Type>
2020

2121
<Type Name="Slang::List&lt;*&gt;">
22-
<DisplayString>{{ size={_count} }}</DisplayString>
22+
<DisplayString>{{ size={m_count} }}</DisplayString>
2323
<Expand>
24-
<Item Name="[size]">_count</Item>
25-
<Item Name="[capacity]">bufferSize</Item>
24+
<Item Name="[size]">m_count</Item>
25+
<Item Name="[capacity]">m_capacity</Item>
2626
<ArrayItems>
27-
<Size>_count</Size>
28-
<ValuePointer>buffer</ValuePointer>
27+
<Size>m_count</Size>
28+
<ValuePointer>m_buffer</ValuePointer>
2929
</ArrayItems>
3030
</Expand>
3131
</Type>
3232

3333

3434
<Type Name="Slang::Array&lt;*,*&gt;">
35-
<DisplayString>{{ size={_count} }}</DisplayString>
35+
<DisplayString>{{ size={m_count} }}</DisplayString>
3636
<Expand>
37-
<Item Name="[size]">_count</Item>
37+
<Item Name="[size]">m_count</Item>
3838
<ArrayItems>
39-
<Size>_count</Size>
40-
<ValuePointer>_buffer</ValuePointer>
39+
<Size>m_count</Size>
40+
<ValuePointer>m_buffer</ValuePointer>
4141
</ArrayItems>
4242
</Expand>
4343
</Type>

source/slang/ir-entry-point-uniforms.cpp

+2
Original file line numberDiff line numberDiff line change
@@ -347,6 +347,8 @@ struct MoveEntryPointUniformParametersToGlobalScope
347347
//
348348
param->removeAndDeallocate();
349349
}
350+
351+
fixUpFuncType(func);
350352
}
351353

352354
// We need to be able to determine if a parameter is logically

source/slang/ir-insts.h

+13
Original file line numberDiff line numberDiff line change
@@ -919,6 +919,19 @@ struct IRBuilder
919919
UInt slotArgCount,
920920
IRInst* const* slotArgs);
921921

922+
IRInst* emitWrapExistential(
923+
IRType* type,
924+
IRInst* value,
925+
UInt slotArgCount,
926+
IRUse const* slotArgs)
927+
{
928+
List<IRInst*> slotArgVals;
929+
for(UInt ii = 0; ii < slotArgCount; ++ii)
930+
slotArgVals.add(slotArgs[ii].get());
931+
932+
return emitWrapExistential(type, value, slotArgCount, slotArgVals.getBuffer());
933+
}
934+
922935
IRUndefined* emitUndefined(IRType* type);
923936

924937

0 commit comments

Comments
 (0)