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