summaryrefslogtreecommitdiffstats
path: root/tests/compute/interface-shader-param.slang
blob: e57ff1bc63b8277d66cc50a73cf2a3dab1126e6f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
// interface-shader-param.slang

// Test using interface tops as top-level shader parameters
// (whether global, or on an entry point).

//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
//DISABLE_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
// generation, so we need an interface for a generator.
//
interface IRandomNumberGenerator
{
    [mutating]
    int randomInt();
}

// We want each shader thread to have its own generator,
// so what we actually pass to the shader is a "strategy"
// for random number generation, which has the generator
// as its associated type.
//
interface IRandomNumberGenerationStrategy
{
    associatedtype Generator : IRandomNumberGenerator;

    Generator makeGenerator(int seed);
}

// Finally, just to give us another interface type to pass
// in, we'll define an interface to modify the generated
// random number (e.g., to make them fit an expected
// distribution).
//
interface IModifier
{
    int modify(int val);
}

// Let's define a subroutine that will use these interfaces
// to do something mildly interesting.
//
int test(
    int                             seed,
    IRandomNumberGenerationStrategy inStrategy,
    IModifier                       modifier)
{
    // HACK: The compiler currently has a problem with
    // looking up the conformance witness for `inStrategy`
    // to the `IRandomNumberGenreationStrategy` interface,
    // because it requires creating an `ExtractExistentialSubtypeWitness`
    // which refers to the value bound in a `LetExpr` created
    // by `maybeOpenExistential`, but we have no guarantee that
    // the code will actually emit the logic to initialize
    // that `LetExpr`...
    //
    let strategy = inStrategy;

    var generator = strategy.makeGenerator(seed);

    let unused = generator.randomInt();
    let val = generator.randomInt();

    let modifiedVal = modifier.modify(val);

    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
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
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):
    uniform IModifier   modifier,
            uint3       dispatchThreadID : SV_DispatchThreadID)
{
    let tid = dispatchThreadID.x;

    let inputVal : int = tid;
    let outputVal = test(inputVal, gStrategy, modifier);

    gOutputBuffer[tid] = outputVal;
}

// Now that we've define all the logic of the entry point,
// we will define some concrete types that we can plug
// in for the interface-type parameters.


struct MyStrategy : IRandomNumberGenerationStrategy
{
    struct Generator : IRandomNumberGenerator
    {
        int state;

        [mutating]
        int randomInt()
        {
            return state++;
        }
    }

    Generator makeGenerator(int seed)
    {
        Generator generator = { seed };
        return generator;
    }
}

struct MyModifier : IModifier
{
    int modify(int val)
    {
        return val * 16;
    }
}

//TEST_INPUT: globalExistentialType MyStrategy
//TEST_INPUT: entryPointExistentialType MyModifier