yum-mirror/slang

Making it easier to work with shaders

git clone https://git.yummers.dev/yum-mirror/slang

Gangzheng TongConvert gfx unit tests and examples to use slang-rhi (#7577)43d0c2100

master
7.8 KiB208 linesraw
1#include "core/slang-basic.h"
2#include "core/slang-blob.h"
3#include "gfx-test-util.h"
4#include "slang-rhi.h"
5#include "slang-rhi/shader-cursor.h"
6#include "unit-test/slang-unit-test.h"
7
8using namespace rhi;
9
10namespace gfx_test
11{
12static Slang::Result loadProgram(
13    rhi::IDevice* device,
14    Slang::ComPtr<rhi::IShaderProgram>& outShaderProgram,
15    slang::ProgramLayout*& slangReflection,
16    bool linkSpecialization = false)
17{
18    const char* moduleInterfaceSrc = R"(
19            interface IFoo
20            {
21                static const int offset;
22                [mutating] void setValue(float v);
23                float getValue();
24                property float val2{get;set;}
25            }
26            struct FooImpl : IFoo
27            {
28                float val;
29                static const int offset = -1;
30                [mutating] void setValue(float v) { val = v; }
31                float getValue() { return val + 1.0; }
32                property float val2 {
33                    get { return val + 2.0; }
34                    set { val = newValue; }
35                }
36            };
37            struct BarImpl : IFoo
38            {
39                float val;
40                static const int offset = 2;
41                [mutating] void setValue(float v) { val = v; }
42                float getValue() { return val + 1.0; }
43                property float val2 {
44                    get { return val; }
45                    set { val = newValue; }
46                }
47            };
48        )";
49    const char* module0Src = R"(
50            import ifoo;
51            extern struct Foo : IFoo = FooImpl;
52            extern static const float c = 0.0;
53            [numthreads(1,1,1)]
54            void computeMain(uniform RWStructuredBuffer<float> buffer)
55            {
56                Foo foo;
57                foo.setValue(3.0);
58                buffer[0] = foo.getValue() + foo.val2 + Foo.offset + c;
59            }
60        )";
61    const char* module1Src = R"(
62            import ifoo;
63            export struct Foo : IFoo = BarImpl;
64            export static const float c = 1.0;
65        )";
66    Slang::ComPtr<slang::ISession> slangSession;
67    SLANG_RETURN_ON_FAIL(device->getSlangSession(slangSession.writeRef()));
68    Slang::ComPtr<slang::IBlob> diagnosticsBlob;
69    auto moduleInterfaceBlob =
70        Slang::UnownedRawBlob::create(moduleInterfaceSrc, strlen(moduleInterfaceSrc));
71    auto module0Blob = Slang::UnownedRawBlob::create(module0Src, strlen(module0Src));
72    auto module1Blob = Slang::UnownedRawBlob::create(module1Src, strlen(module1Src));
73    slang::IModule* moduleInterface =
74        slangSession->loadModuleFromSource("ifoo", "ifoo.slang", moduleInterfaceBlob);
75    slang::IModule* module0 = slangSession->loadModuleFromSource("module0", "path0", module0Blob);
76    slang::IModule* module1 = slangSession->loadModuleFromSource("module1", "path1", module1Blob);
77    ComPtr<slang::IEntryPoint> computeEntryPoint;
78    SLANG_RETURN_ON_FAIL(
79        module0->findEntryPointByName("computeMain", computeEntryPoint.writeRef()));
80
81    Slang::List<slang::IComponentType*> componentTypes;
82    componentTypes.add(moduleInterface);
83    componentTypes.add(module0);
84    if (linkSpecialization)
85        componentTypes.add(module1);
86    componentTypes.add(computeEntryPoint);
87
88    Slang::ComPtr<slang::IComponentType> composedProgram;
89    SlangResult result = slangSession->createCompositeComponentType(
90        componentTypes.getBuffer(),
91        componentTypes.getCount(),
92        composedProgram.writeRef(),
93        diagnosticsBlob.writeRef());
94    diagnoseIfNeeded(diagnosticsBlob);
95    SLANG_RETURN_ON_FAIL(result);
96
97    ComPtr<slang::IComponentType> linkedProgram;
98    result = composedProgram->link(linkedProgram.writeRef(), diagnosticsBlob.writeRef());
99    diagnoseIfNeeded(diagnosticsBlob);
100    SLANG_RETURN_ON_FAIL(result);
101
102    composedProgram = linkedProgram;
103    slangReflection = composedProgram->getLayout();
104
105    ShaderProgramDesc programDesc = {};
106    programDesc.slangGlobalScope = composedProgram.get();
107
108    auto shaderProgram = device->createShaderProgram(programDesc);
109
110    outShaderProgram = shaderProgram;
111    return SLANG_OK;
112}
113
114void linkTimeDefaultTestImpl(IDevice* device, UnitTestContext* context)
115{
116    // Create pipeline without linking a specialization override module, so we should
117    // see the default value of `extern Foo`.
118    ComPtr<IShaderProgram> shaderProgram;
119    slang::ProgramLayout* slangReflection;
120    GFX_CHECK_CALL_ABORT(loadProgram(device, shaderProgram, slangReflection, false));
121
122    ComputePipelineDesc pipelineDesc = {};
123    pipelineDesc.program = shaderProgram.get();
124    ComPtr<IComputePipeline> pipelineState;
125    GFX_CHECK_CALL_ABORT(device->createComputePipeline(pipelineDesc, pipelineState.writeRef()));
126
127    // Create pipeline with a specialization override module linked in, so we should
128    // see the result of using `Bar` for `extern Foo`.
129    ComPtr<IShaderProgram> shaderProgram1;
130    GFX_CHECK_CALL_ABORT(loadProgram(device, shaderProgram1, slangReflection, true));
131
132    ComputePipelineDesc pipelineDesc1 = {};
133    pipelineDesc1.program = shaderProgram1.get();
134    ComPtr<IComputePipeline> pipelineState1;
135    GFX_CHECK_CALL_ABORT(device->createComputePipeline(pipelineDesc1, pipelineState1.writeRef()));
136
137    const int numberCount = 4;
138    float initialData[] = {0.0f, 0.0f, 0.0f, 0.0f};
139    BufferDesc bufferDesc = {};
140    bufferDesc.size = numberCount * sizeof(float);
141    bufferDesc.format = rhi::Format::Undefined;
142    bufferDesc.elementSize = sizeof(float);
143    bufferDesc.usage = BufferUsage::ShaderResource | BufferUsage::UnorderedAccess |
144                       BufferUsage::CopyDestination | BufferUsage::CopySource;
145    bufferDesc.defaultState = ResourceState::UnorderedAccess;
146    bufferDesc.memoryType = MemoryType::DeviceLocal;
147
148    ComPtr<IBuffer> numbersBuffer;
149    GFX_CHECK_CALL_ABORT(
150        device->createBuffer(bufferDesc, (void*)initialData, numbersBuffer.writeRef()));
151
152    auto queue = device->getQueue(QueueType::Graphics);
153
154    // We have done all the set up work, now it is time to start recording a command buffer for
155    // GPU execution.
156    {
157        auto commandEncoder = queue->createCommandEncoder();
158        auto computePassEncoder = commandEncoder->beginComputePass();
159
160        auto rootObject = computePassEncoder->bindPipeline(pipelineState);
161
162        ShaderCursor entryPointCursor(
163            rootObject->getEntryPoint(0)); // get a cursor the the first entry-point.
164        // Bind buffer to the entry point.
165        entryPointCursor.getPath("buffer").setBinding(Binding(numbersBuffer));
166
167        computePassEncoder->dispatchCompute(1, 1, 1);
168        computePassEncoder->end();
169        auto commandBuffer = commandEncoder->finish();
170        queue->submit(commandBuffer);
171        queue->waitOnHost();
172    }
173
174    compareComputeResult(device, numbersBuffer, std::array{8.0f});
175
176    // Now run again with the overrided program.
177    {
178        auto commandEncoder = queue->createCommandEncoder();
179        auto computePassEncoder = commandEncoder->beginComputePass();
180
181        auto rootObject = computePassEncoder->bindPipeline(pipelineState1);
182
183        ShaderCursor entryPointCursor(
184            rootObject->getEntryPoint(0)); // get a cursor the the first entry-point.
185        // Bind buffer to the entry point.
186        entryPointCursor.getPath("buffer").setBinding(Binding(numbersBuffer));
187
188        computePassEncoder->dispatchCompute(1, 1, 1);
189        computePassEncoder->end();
190        auto commandBuffer = commandEncoder->finish();
191        queue->submit(commandBuffer);
192        queue->waitOnHost();
193    }
194
195    compareComputeResult(device, numbersBuffer, std::array{10.0f});
196}
197
198SLANG_UNIT_TEST(linkTimeDefaultD3D12)
199{
200    runTestImpl(linkTimeDefaultTestImpl, unitTestContext, DeviceType::D3D12);
201}
202
203SLANG_UNIT_TEST(linkTimeDefaultVulkan)
204{
205    runTestImpl(linkTimeDefaultTestImpl, unitTestContext, DeviceType::Vulkan);
206}
207
208} // namespace gfx_test