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
19.7 KiB609 linesraw
1#if 0
2// Duplicated: This is tested in slang-rhi\tests\test-cmd-draw.cpp
3
4#include "core/slang-basic.h"
5#include "gfx-test-util.h"
6#include "slang-gfx.h"
7#include "slang-rhi/shader-cursor.h"
8#include "unit-test/slang-unit-test.h"
9
10using namespace gfx;
11
12namespace gfx_test
13{
14struct Vertex
15{
16    float position[3];
17};
18
19struct Instance
20{
21    float position[3];
22    float color[3];
23};
24
25static const int kVertexCount = 6;
26static const Vertex kVertexData[kVertexCount] = {
27    // Triangle 1
28    {0, 0, 0.5},
29    {1, 0, 0.5},
30    {0, 1, 0.5},
31
32    // Triangle 2
33    {-1, 0, 0.5},
34    {0, 0, 0.5},
35    {-1, 1, 0.5},
36};
37
38static const int kInstanceCount = 2;
39static const Instance kInstanceData[kInstanceCount] = {
40    {{0, 0, 0}, {1, 0, 0}},
41    {{0, -1, 0}, {0, 0, 1}},
42};
43
44static const int kIndexCount = 6;
45static const uint32_t kIndexData[kIndexCount] = {
46    0,
47    2,
48    5,
49    0,
50    1,
51    2,
52};
53
54const int kWidth = 256;
55const int kHeight = 256;
56const Format format = Format::R32G32B32A32_FLOAT;
57
58ComPtr<IBufferResource> createVertexBuffer(IDevice* device)
59{
60    IBufferResource::Desc vertexBufferDesc;
61    vertexBufferDesc.type = IResource::Type::Buffer;
62    vertexBufferDesc.sizeInBytes = kVertexCount * sizeof(Vertex);
63    vertexBufferDesc.defaultState = ResourceState::VertexBuffer;
64    vertexBufferDesc.allowedStates = ResourceState::VertexBuffer;
65    ComPtr<IBufferResource> vertexBuffer =
66        device->createBufferResource(vertexBufferDesc, &kVertexData[0]);
67    SLANG_CHECK_ABORT(vertexBuffer != nullptr);
68    return vertexBuffer;
69}
70
71ComPtr<IBufferResource> createInstanceBuffer(IDevice* device)
72{
73    IBufferResource::Desc instanceBufferDesc;
74    instanceBufferDesc.type = IResource::Type::Buffer;
75    instanceBufferDesc.sizeInBytes = kInstanceCount * sizeof(Instance);
76    instanceBufferDesc.defaultState = ResourceState::VertexBuffer;
77    instanceBufferDesc.allowedStates = ResourceState::VertexBuffer;
78    ComPtr<IBufferResource> instanceBuffer =
79        device->createBufferResource(instanceBufferDesc, &kInstanceData[0]);
80    SLANG_CHECK_ABORT(instanceBuffer != nullptr);
81    return instanceBuffer;
82}
83
84ComPtr<IBufferResource> createIndexBuffer(IDevice* device)
85{
86    IBufferResource::Desc indexBufferDesc;
87    indexBufferDesc.type = IResource::Type::Buffer;
88    indexBufferDesc.sizeInBytes = kIndexCount * sizeof(uint32_t);
89    indexBufferDesc.defaultState = ResourceState::IndexBuffer;
90    indexBufferDesc.allowedStates = ResourceState::IndexBuffer;
91    ComPtr<IBufferResource> indexBuffer =
92        device->createBufferResource(indexBufferDesc, &kIndexData[0]);
93    SLANG_CHECK_ABORT(indexBuffer != nullptr);
94    return indexBuffer;
95}
96
97ComPtr<ITextureResource> createColorBuffer(IDevice* device)
98{
99    gfx::ITextureResource::Desc colorBufferDesc;
100    colorBufferDesc.type = IResource::Type::Texture2D;
101    colorBufferDesc.size.width = kWidth;
102    colorBufferDesc.size.height = kHeight;
103    colorBufferDesc.size.depth = 1;
104    colorBufferDesc.numMipLevels = 1;
105    colorBufferDesc.format = format;
106    colorBufferDesc.defaultState = ResourceState::RenderTarget;
107    colorBufferDesc.allowedStates = {ResourceState::RenderTarget, ResourceState::CopySource};
108    ComPtr<ITextureResource> colorBuffer = device->createTextureResource(colorBufferDesc, nullptr);
109    SLANG_CHECK_ABORT(colorBuffer != nullptr);
110    return colorBuffer;
111}
112
113class BaseDrawTest
114{
115public:
116    ComPtr<IDevice> device;
117    UnitTestContext* context;
118
119    ComPtr<ITransientResourceHeap> transientHeap;
120    ComPtr<IPipelineState> pipelineState;
121    ComPtr<IRenderPassLayout> renderPass;
122    ComPtr<IFramebuffer> framebuffer;
123
124    ComPtr<IBufferResource> vertexBuffer;
125    ComPtr<IBufferResource> instanceBuffer;
126    ComPtr<ITextureResource> colorBuffer;
127
128    void init(IDevice* device, UnitTestContext* context)
129    {
130        this->device = device;
131        this->context = context;
132    }
133
134    void createRequiredResources()
135    {
136        VertexStreamDesc vertexStreams[] = {
137            {sizeof(Vertex), InputSlotClass::PerVertex, 0},
138            {sizeof(Instance), InputSlotClass::PerInstance, 1},
139        };
140
141        InputElementDesc inputElements[] = {
142            // Vertex buffer data
143            {"POSITIONA", 0, Format::R32G32B32_FLOAT, offsetof(Vertex, position), 0},
144
145            // Instance buffer data
146            {"POSITIONB", 0, Format::R32G32B32_FLOAT, offsetof(Instance, position), 1},
147            {"COLOR", 0, Format::R32G32B32_FLOAT, offsetof(Instance, color), 1},
148        };
149        IInputLayout::Desc inputLayoutDesc = {};
150        inputLayoutDesc.inputElementCount = SLANG_COUNT_OF(inputElements);
151        inputLayoutDesc.inputElements = inputElements;
152        inputLayoutDesc.vertexStreamCount = SLANG_COUNT_OF(vertexStreams);
153        inputLayoutDesc.vertexStreams = vertexStreams;
154        auto inputLayout = device->createInputLayout(inputLayoutDesc);
155        SLANG_CHECK_ABORT(inputLayout != nullptr);
156
157        vertexBuffer = createVertexBuffer(device);
158        instanceBuffer = createInstanceBuffer(device);
159        colorBuffer = createColorBuffer(device);
160
161        ITransientResourceHeap::Desc transientHeapDesc = {};
162        transientHeapDesc.constantBufferSize = 4096;
163        GFX_CHECK_CALL_ABORT(
164            device->createTransientResourceHeap(transientHeapDesc, transientHeap.writeRef()));
165
166        ComPtr<IShaderProgram> shaderProgram;
167        slang::ProgramLayout* slangReflection;
168        GFX_CHECK_CALL_ABORT(loadGraphicsProgram(
169            device,
170            shaderProgram,
171            "graphics-smoke",
172            "vertexMain",
173            "fragmentMain",
174            slangReflection));
175
176        IFramebufferLayout::TargetLayout targetLayout;
177        targetLayout.format = format;
178        targetLayout.sampleCount = 1;
179
180        IFramebufferLayout::Desc framebufferLayoutDesc;
181        framebufferLayoutDesc.renderTargetCount = 1;
182        framebufferLayoutDesc.renderTargets = &targetLayout;
183        ComPtr<gfx::IFramebufferLayout> framebufferLayout =
184            device->createFramebufferLayout(framebufferLayoutDesc);
185        SLANG_CHECK_ABORT(framebufferLayout != nullptr);
186
187        GraphicsPipelineStateDesc pipelineDesc = {};
188        pipelineDesc.program = shaderProgram.get();
189        pipelineDesc.inputLayout = inputLayout;
190        pipelineDesc.framebufferLayout = framebufferLayout;
191        pipelineDesc.depthStencil.depthTestEnable = false;
192        pipelineDesc.depthStencil.depthWriteEnable = false;
193        GFX_CHECK_CALL_ABORT(
194            device->createGraphicsPipelineState(pipelineDesc, pipelineState.writeRef()));
195
196        IRenderPassLayout::Desc renderPassDesc = {};
197        renderPassDesc.framebufferLayout = framebufferLayout;
198        renderPassDesc.renderTargetCount = 1;
199        IRenderPassLayout::TargetAccessDesc renderTargetAccess = {};
200        renderTargetAccess.loadOp = IRenderPassLayout::TargetLoadOp::Clear;
201        renderTargetAccess.storeOp = IRenderPassLayout::TargetStoreOp::Store;
202        renderTargetAccess.initialState = ResourceState::RenderTarget;
203        renderTargetAccess.finalState = ResourceState::CopySource;
204        renderPassDesc.renderTargetAccess = &renderTargetAccess;
205        GFX_CHECK_CALL_ABORT(device->createRenderPassLayout(renderPassDesc, renderPass.writeRef()));
206
207        gfx::IResourceView::Desc colorBufferViewDesc;
208        memset(&colorBufferViewDesc, 0, sizeof(colorBufferViewDesc));
209        colorBufferViewDesc.format = format;
210        colorBufferViewDesc.renderTarget.shape = gfx::IResource::Type::Texture2D;
211        colorBufferViewDesc.type = gfx::IResourceView::Type::RenderTarget;
212        auto rtv = device->createTextureView(colorBuffer, colorBufferViewDesc);
213
214        gfx::IFramebuffer::Desc framebufferDesc;
215        framebufferDesc.renderTargetCount = 1;
216        framebufferDesc.depthStencilView = nullptr;
217        framebufferDesc.renderTargetViews = rtv.readRef();
218        framebufferDesc.layout = framebufferLayout;
219        GFX_CHECK_CALL_ABORT(device->createFramebuffer(framebufferDesc, framebuffer.writeRef()));
220    }
221
222    void checkTestResults(
223        int pixelCount,
224        int channelCount,
225        const int* testXCoords,
226        const int* testYCoords,
227        float* testResults)
228    {
229        // Read texture values back from four specific pixels located within the triangles
230        // and compare against expected values (because testing every single pixel will be too long
231        // and tedious and requires maintaining reference images).
232        ComPtr<ISlangBlob> resultBlob;
233        size_t rowPitch = 0;
234        size_t pixelSize = 0;
235        GFX_CHECK_CALL_ABORT(device->readTextureResource(
236            colorBuffer,
237            ResourceState::CopySource,
238            resultBlob.writeRef(),
239            &rowPitch,
240            &pixelSize));
241        auto result = (float*)resultBlob->getBufferPointer();
242
243        int cursor = 0;
244        for (int i = 0; i < pixelCount; ++i)
245        {
246            auto x = testXCoords[i];
247            auto y = testYCoords[i];
248            auto pixelPtr = result + x * channelCount + y * rowPitch / sizeof(float);
249            for (int j = 0; j < channelCount; ++j)
250            {
251                testResults[cursor] = pixelPtr[j];
252                cursor++;
253            }
254        }
255
256        float expectedResult[] = {
257            1.0f,
258            0.0f,
259            0.0f,
260            1.0f,
261            1.0f,
262            0.0f,
263            0.0f,
264            1.0f,
265            0.0f,
266            0.0f,
267            1.0f,
268            1.0f,
269            0.0f,
270            0.0f,
271            1.0f,
272            1.0f};
273        compareComputeResultFuzzy(testResults, expectedResult, sizeof(expectedResult));
274    }
275};
276
277struct DrawInstancedTest : BaseDrawTest
278{
279    void setUpAndDraw()
280    {
281        createRequiredResources();
282
283        ICommandQueue::Desc queueDesc = {ICommandQueue::QueueType::Graphics};
284        auto queue = device->createCommandQueue(queueDesc);
285        auto commandBuffer = transientHeap->createCommandBuffer();
286
287        auto encoder = commandBuffer->encodeRenderCommands(renderPass, framebuffer);
288        auto rootObject = encoder->bindPipeline(pipelineState);
289
290        gfx::Viewport viewport = {};
291        viewport.maxZ = 1.0f;
292        viewport.extentX = kWidth;
293        viewport.extentY = kHeight;
294        encoder->setViewportAndScissor(viewport);
295
296        uint32_t startVertex = 0;
297        uint32_t startInstanceLocation = 0;
298
299        encoder->setVertexBuffer(0, vertexBuffer);
300        encoder->setVertexBuffer(1, instanceBuffer);
301        encoder->setPrimitiveTopology(PrimitiveTopology::TriangleList);
302
303        encoder->drawInstanced(kVertexCount, kInstanceCount, startVertex, startInstanceLocation);
304        encoder->endEncoding();
305        commandBuffer->close();
306        queue->executeCommandBuffer(commandBuffer);
307        queue->waitOnHost();
308    }
309
310    void run()
311    {
312        setUpAndDraw();
313
314        const int kPixelCount = 4;
315        const int kChannelCount = 4;
316        int testXCoords[kPixelCount] = {64, 192, 64, 192};
317        int testYCoords[kPixelCount] = {100, 100, 250, 250};
318        float testResults[kPixelCount * kChannelCount];
319
320        checkTestResults(kPixelCount, kChannelCount, testXCoords, testYCoords, testResults);
321    }
322};
323
324struct DrawIndexedInstancedTest : BaseDrawTest
325{
326    ComPtr<IBufferResource> indexBuffer;
327
328    void setUpAndDraw()
329    {
330        createRequiredResources();
331
332        ICommandQueue::Desc queueDesc = {ICommandQueue::QueueType::Graphics};
333        auto queue = device->createCommandQueue(queueDesc);
334        auto commandBuffer = transientHeap->createCommandBuffer();
335
336        auto encoder = commandBuffer->encodeRenderCommands(renderPass, framebuffer);
337        auto rootObject = encoder->bindPipeline(pipelineState);
338
339        gfx::Viewport viewport = {};
340        viewport.maxZ = 1.0f;
341        viewport.extentX = kWidth;
342        viewport.extentY = kHeight;
343        encoder->setViewportAndScissor(viewport);
344
345        uint32_t startIndex = 0;
346        int32_t startVertex = 0;
347        uint32_t startInstanceLocation = 0;
348
349        encoder->setVertexBuffer(0, vertexBuffer);
350        encoder->setVertexBuffer(1, instanceBuffer);
351        encoder->setIndexBuffer(indexBuffer, Format::R32_UINT);
352        encoder->setPrimitiveTopology(PrimitiveTopology::TriangleList);
353
354        encoder->drawIndexedInstanced(
355            kIndexCount,
356            kInstanceCount,
357            startIndex,
358            startVertex,
359            startInstanceLocation);
360        encoder->endEncoding();
361        commandBuffer->close();
362        queue->executeCommandBuffer(commandBuffer);
363        queue->waitOnHost();
364    }
365
366    void run()
367    {
368        indexBuffer = createIndexBuffer(device);
369
370        setUpAndDraw();
371
372        const int kPixelCount = 4;
373        const int kChannelCount = 4;
374        int testXCoords[kPixelCount] = {64, 192, 64, 192};
375        int testYCoords[kPixelCount] = {32, 100, 150, 250};
376        float testResults[kPixelCount * kChannelCount];
377
378        checkTestResults(kPixelCount, kChannelCount, testXCoords, testYCoords, testResults);
379    }
380};
381
382struct DrawIndirectTest : BaseDrawTest
383{
384    ComPtr<IBufferResource> indirectBuffer;
385
386    struct IndirectArgData
387    {
388        float padding; // Ensure args and count don't start at 0 offset for testing purposes
389        IndirectDrawArguments args;
390    };
391
392    ComPtr<IBufferResource> createIndirectBuffer(IDevice* device)
393    {
394        static const IndirectArgData kIndirectData = {
395            42.0f,        // padding
396            {6, 2, 0, 0}, // args
397        };
398
399        IBufferResource::Desc indirectBufferDesc;
400        indirectBufferDesc.type = IResource::Type::Buffer;
401        indirectBufferDesc.sizeInBytes = sizeof(IndirectArgData);
402        indirectBufferDesc.defaultState = ResourceState::IndirectArgument;
403        indirectBufferDesc.allowedStates = ResourceState::IndirectArgument;
404        ComPtr<IBufferResource> indirectBuffer =
405            device->createBufferResource(indirectBufferDesc, &kIndirectData);
406        SLANG_CHECK_ABORT(indirectBuffer != nullptr);
407        return indirectBuffer;
408    }
409
410    void setUpAndDraw()
411    {
412        createRequiredResources();
413
414        ICommandQueue::Desc queueDesc = {ICommandQueue::QueueType::Graphics};
415        auto queue = device->createCommandQueue(queueDesc);
416        auto commandBuffer = transientHeap->createCommandBuffer();
417
418        auto encoder = commandBuffer->encodeRenderCommands(renderPass, framebuffer);
419        auto rootObject = encoder->bindPipeline(pipelineState);
420
421        gfx::Viewport viewport = {};
422        viewport.maxZ = 1.0f;
423        viewport.extentX = kWidth;
424        viewport.extentY = kHeight;
425        encoder->setViewportAndScissor(viewport);
426
427        encoder->setVertexBuffer(0, vertexBuffer);
428        encoder->setVertexBuffer(1, instanceBuffer);
429        encoder->setPrimitiveTopology(PrimitiveTopology::TriangleList);
430
431        uint32_t maxDrawCount = 1;
432        Offset argOffset = offsetof(IndirectArgData, args);
433
434        encoder->drawIndirect(maxDrawCount, indirectBuffer, argOffset);
435        encoder->endEncoding();
436        commandBuffer->close();
437        queue->executeCommandBuffer(commandBuffer);
438        queue->waitOnHost();
439    }
440
441    void run()
442    {
443        indirectBuffer = createIndirectBuffer(device);
444
445        setUpAndDraw();
446
447        const int kPixelCount = 4;
448        const int kChannelCount = 4;
449        int testXCoords[kPixelCount] = {64, 192, 64, 192};
450        int testYCoords[kPixelCount] = {100, 100, 250, 250};
451        float testResults[kPixelCount * kChannelCount];
452
453        checkTestResults(kPixelCount, kChannelCount, testXCoords, testYCoords, testResults);
454    }
455};
456
457struct DrawIndexedIndirectTest : BaseDrawTest
458{
459    ComPtr<IBufferResource> indexBuffer;
460    ComPtr<IBufferResource> indirectBuffer;
461
462    struct IndexedIndirectArgData
463    {
464        float padding; // Ensure args and count don't start at 0 offset for testing purposes
465        IndirectDrawIndexedArguments args;
466    };
467
468    ComPtr<IBufferResource> createIndirectBuffer(IDevice* device)
469    {
470        static const IndexedIndirectArgData kIndexedIndirectData = {
471            42.0f,           // padding
472            {6, 2, 0, 0, 0}, // args
473        };
474
475        IBufferResource::Desc indirectBufferDesc;
476        indirectBufferDesc.type = IResource::Type::Buffer;
477        indirectBufferDesc.sizeInBytes = sizeof(IndexedIndirectArgData);
478        indirectBufferDesc.defaultState = ResourceState::IndirectArgument;
479        indirectBufferDesc.allowedStates = ResourceState::IndirectArgument;
480        ComPtr<IBufferResource> indexBuffer =
481            device->createBufferResource(indirectBufferDesc, &kIndexedIndirectData);
482        SLANG_CHECK_ABORT(indexBuffer != nullptr);
483        return indexBuffer;
484    }
485
486    void setUpAndDraw()
487    {
488        createRequiredResources();
489
490        ICommandQueue::Desc queueDesc = {ICommandQueue::QueueType::Graphics};
491        auto queue = device->createCommandQueue(queueDesc);
492        auto commandBuffer = transientHeap->createCommandBuffer();
493
494        auto encoder = commandBuffer->encodeRenderCommands(renderPass, framebuffer);
495        auto rootObject = encoder->bindPipeline(pipelineState);
496
497        gfx::Viewport viewport = {};
498        viewport.maxZ = 1.0f;
499        viewport.extentX = kWidth;
500        viewport.extentY = kHeight;
501        encoder->setViewportAndScissor(viewport);
502
503        encoder->setVertexBuffer(0, vertexBuffer);
504        encoder->setVertexBuffer(1, instanceBuffer);
505        encoder->setIndexBuffer(indexBuffer, Format::R32_UINT);
506        encoder->setPrimitiveTopology(PrimitiveTopology::TriangleList);
507
508        uint32_t maxDrawCount = 1;
509        Offset argOffset = offsetof(IndexedIndirectArgData, args);
510
511        encoder->drawIndexedIndirect(maxDrawCount, indirectBuffer, argOffset);
512        encoder->endEncoding();
513        commandBuffer->close();
514        queue->executeCommandBuffer(commandBuffer);
515        queue->waitOnHost();
516    }
517
518    void run()
519    {
520        indexBuffer = createIndexBuffer(device);
521        indirectBuffer = createIndirectBuffer(device);
522
523        setUpAndDraw();
524
525        const int kPixelCount = 4;
526        const int kChannelCount = 4;
527        int testXCoords[kPixelCount] = {64, 192, 64, 192};
528        int testYCoords[kPixelCount] = {32, 100, 150, 250};
529        float testResults[kPixelCount * kChannelCount];
530
531        checkTestResults(kPixelCount, kChannelCount, testXCoords, testYCoords, testResults);
532    }
533};
534
535template<typename T>
536void drawTestImpl(IDevice* device, UnitTestContext* context)
537{
538    T test;
539    test.init(device, context);
540    test.run();
541}
542
543SLANG_UNIT_TEST(drawInstancedD3D11)
544{
545    runTestImpl(drawTestImpl<DrawInstancedTest>, unitTestContext, Slang::RenderApiFlag::D3D11);
546}
547
548SLANG_UNIT_TEST(drawIndexedInstancedD3D11)
549{
550    runTestImpl(
551        drawTestImpl<DrawIndexedInstancedTest>,
552        unitTestContext,
553        Slang::RenderApiFlag::D3D11);
554}
555
556SLANG_UNIT_TEST(drawInstancedD3D12)
557{
558    runTestImpl(drawTestImpl<DrawInstancedTest>, unitTestContext, Slang::RenderApiFlag::D3D12);
559}
560
561SLANG_UNIT_TEST(drawIndexedInstancedD3D12)
562{
563    runTestImpl(
564        drawTestImpl<DrawIndexedInstancedTest>,
565        unitTestContext,
566        Slang::RenderApiFlag::D3D12);
567}
568
569SLANG_UNIT_TEST(drawIndirectD3D12)
570{
571    runTestImpl(drawTestImpl<DrawIndirectTest>, unitTestContext, Slang::RenderApiFlag::D3D12);
572}
573
574SLANG_UNIT_TEST(drawIndexedIndirectD3D12)
575{
576    runTestImpl(
577        drawTestImpl<DrawIndexedIndirectTest>,
578        unitTestContext,
579        Slang::RenderApiFlag::D3D12);
580}
581
582SLANG_UNIT_TEST(drawInstancedVulkan)
583{
584    runTestImpl(drawTestImpl<DrawInstancedTest>, unitTestContext, Slang::RenderApiFlag::Vulkan);
585}
586
587SLANG_UNIT_TEST(drawIndexedInstancedVulkan)
588{
589    runTestImpl(
590        drawTestImpl<DrawIndexedInstancedTest>,
591        unitTestContext,
592        Slang::RenderApiFlag::Vulkan);
593}
594
595SLANG_UNIT_TEST(drawIndirectVulkan)
596{
597    runTestImpl(drawTestImpl<DrawIndirectTest>, unitTestContext, Slang::RenderApiFlag::Vulkan);
598}
599
600SLANG_UNIT_TEST(drawIndexedIndirectVulkan)
601{
602    runTestImpl(
603        drawTestImpl<DrawIndexedIndirectTest>,
604        unitTestContext,
605        Slang::RenderApiFlag::Vulkan);
606}
607} // namespace gfx_test
608
609#endif