yum-mirror/slang
Making it easier to work with shaders
git clone https://git.yummers.dev/yum-mirror/slang
43d0c2100
master
1#if 0 2// Duplcated: This is ported to slang-rhi\tests\test-ray-tracing.cpp 3 4#include "core/slang-basic.h" 5#include "gfx-test-texture-util.h" 6#include "gfx-test-util.h" 7#include "platform/vector-math.h" 8#include "unit-test/slang-unit-test.h" 9 10#include <chrono> 11#include <slang-rhi.h> 12#include <slang-rhi/shader-cursor.h> 13 14using namespace rhi ; 15using namespace Slang ; 16 17namespace gfx_test 18{ 19struct Vertex 20{ 21float position [3 ]; 22}; 23 24static const int kVertexCount = 9 ; 25static const Vertex kVertexData [kVertexCount ]= { 26// Triangle 1 27 {0 ,0 ,1 }, 28 {4 ,0 ,1 }, 29 {0 ,4 ,1 }, 30 31// Triangle 2 32 {-4 ,0 ,1 }, 33 {0 ,0 ,1 }, 34 {0 ,4 ,1 }, 35 36// Triangle 3 37 {0 ,0 ,1 }, 38 {4 ,0 ,1 }, 39 {0 ,-4 ,1 }, 40}; 41static const int kIndexCount = 9 ; 42static const uint32_t kIndexData [kIndexCount ]= { 430 , 441 , 452 , 463 , 474 , 485 , 496 , 507 , 518 , 52}; 53 54struct BaseRayTracingTest 55{ 56IDevice * device ; 57UnitTestContext * context ; 58 59ComPtr < ICommandQueue > queue ; 60 61ComPtr < IRayTracingPipeline > renderPipelineState ; 62ComPtr < IBuffer > vertexBuffer ; 63ComPtr < IBuffer > indexBuffer ; 64ComPtr < IBuffer > transformBuffer ; 65ComPtr < IBuffer > instanceBuffer ; 66ComPtr < IBuffer > BLASBuffer ; 67ComPtr < IAccelerationStructure > BLAS ; 68ComPtr < IBuffer > TLASBuffer ; 69ComPtr < IAccelerationStructure > TLAS ; 70ComPtr < ITexture > resultTexture ; 71ComPtr < ITextureView > resultTextureUAV ; 72ComPtr < IShaderTable > shaderTable ; 73 74uint32_t width = 2 ; 75uint32_t height = 2 ; 76 77void init (IDevice * device ,UnitTestContext * context ) 78 { 79if (!device -> hasFeature ("ray-tracing" )) 80 { 81SLANG_IGNORE_TEST ; 82 } 83 84this -> device = device ; 85this -> context = context ; 86 } 87 88// Load and compile shader code from source. 89Result loadShaderProgram (IDevice * device ,IShaderProgram ** outProgram ) 90 { 91ComPtr < slang::ISession > slangSession ; 92slangSession = device -> getSlangSession (); 93 94ComPtr < slang::IBlob > diagnosticsBlob ; 95 slang::IModule * module = 96slangSession -> loadModule ("ray-tracing-test-shaders" ,diagnosticsBlob .writeRef ()); 97if (!module ) 98return SLANG_FAIL ; 99 100Slang ::List < slang::IComponentType *> componentTypes ; 101componentTypes .add (module ); 102ComPtr < slang::IEntryPoint > entryPoint ; 103SLANG_RETURN_ON_FAIL (module -> findEntryPointByName ("rayGenShaderA" ,entryPoint .writeRef ())); 104componentTypes .add (entryPoint ); 105SLANG_RETURN_ON_FAIL (module -> findEntryPointByName ("rayGenShaderB" ,entryPoint .writeRef ())); 106componentTypes .add (entryPoint ); 107SLANG_RETURN_ON_FAIL (module -> findEntryPointByName ("missShaderA" ,entryPoint .writeRef ())); 108componentTypes .add (entryPoint ); 109SLANG_RETURN_ON_FAIL (module -> findEntryPointByName ("missShaderB" ,entryPoint .writeRef ())); 110componentTypes .add (entryPoint ); 111SLANG_RETURN_ON_FAIL ( 112module -> findEntryPointByName ("closestHitShaderA" ,entryPoint .writeRef ())); 113componentTypes .add (entryPoint ); 114SLANG_RETURN_ON_FAIL ( 115module -> findEntryPointByName ("closestHitShaderB" ,entryPoint .writeRef ())); 116componentTypes .add (entryPoint ); 117 118ComPtr < slang::IComponentType > linkedProgram ; 119SlangResult result = slangSession -> createCompositeComponentType ( 120componentTypes .getBuffer (), 121componentTypes .getCount (), 122linkedProgram .writeRef (), 123diagnosticsBlob .writeRef ()); 124SLANG_RETURN_ON_FAIL (result ); 125 126ShaderProgramDesc programDesc = {}; 127programDesc .slangGlobalScope = linkedProgram ; 128SLANG_RETURN_ON_FAIL (device -> createShaderProgram (programDesc ,outProgram )); 129 130return SLANG_OK ; 131 } 132 133void createResultTexture () 134 { 135TextureDesc resultTextureDesc = {}; 136resultTextureDesc .type = TextureType ::Texture2D ; 137resultTextureDesc .mipCount = 1 ; 138resultTextureDesc .size .width = width ; 139resultTextureDesc .size .height = height ; 140resultTextureDesc .size .depth = 1 ; 141resultTextureDesc .defaultState = ResourceState ::UnorderedAccess ; 142resultTextureDesc .format = Format ::RGBA32Float ; 143resultTextureDesc .usage = TextureUsage ::UnorderedAccess |TextureUsage ::CopySource ; 144resultTexture = device -> createTexture (resultTextureDesc ); 145 146TextureViewDesc resultUAVDesc = {}; 147resultUAVDesc .format = resultTextureDesc .format ; 148resultTextureUAV = resultTexture -> createView (resultUAVDesc ); 149 } 150 151void createRequiredResources () 152 { 153GFX_CHECK_CALL_ABORT (device -> getQueue (QueueType ::Graphics ,queue .writeRef ())); 154 155BufferDesc vertexBufferDesc ; 156vertexBufferDesc .size = kVertexCount * sizeof (Vertex ); 157vertexBufferDesc .defaultState = ResourceState ::ShaderResource ; 158vertexBufferDesc .usage = BufferUsage ::ShaderResource |BufferUsage ::AccelerationStructureBuildInput ; 159vertexBuffer = device -> createBuffer (vertexBufferDesc ,& kVertexData [0 ]); 160SLANG_CHECK_ABORT (vertexBuffer != nullptr ); 161 162BufferDesc indexBufferDesc ; 163indexBufferDesc .size = kIndexCount * sizeof (int32_t ); 164indexBufferDesc .defaultState = ResourceState ::ShaderResource ; 165indexBufferDesc .usage = BufferUsage ::ShaderResource |BufferUsage ::AccelerationStructureBuildInput ; 166indexBuffer = device -> createBuffer (indexBufferDesc ,& kIndexData [0 ]); 167SLANG_CHECK_ABORT (indexBuffer != nullptr ); 168 169BufferDesc transformBufferDesc ; 170transformBufferDesc .size = sizeof (float )* 12 ; 171transformBufferDesc .defaultState = ResourceState ::ShaderResource ; 172transformBufferDesc .usage = BufferUsage ::ShaderResource |BufferUsage ::AccelerationStructureBuildInput ; 173float transformData [12 ]= 174 {1.0f ,0.0f ,0.0f ,0.0f ,0.0f ,1.0f ,0.0f ,0.0f ,0.0f ,0.0f ,1.0f ,0.0f }; 175transformBuffer = device -> createBuffer (transformBufferDesc ,& transformData ); 176SLANG_CHECK_ABORT (transformBuffer != nullptr ); 177 178createResultTexture (); 179 180// Build bottom level acceleration structure. 181 { 182AccelerationStructureBuildInput geomInput = {}; 183geomInput .type = AccelerationStructureBuildInputType ::Triangles ; 184geomInput .triangles .flags = AccelerationStructureGeometryFlags ::Opaque ; 185geomInput .triangles .indexCount = kIndexCount ; 186geomInput .triangles .indexBuffer = BufferOffsetPair (indexBuffer ,0 ); 187geomInput .triangles .indexFormat = IndexFormat ::Uint32 ; 188geomInput .triangles .vertexCount = kVertexCount ; 189geomInput .triangles .vertexBuffers [0 ]= BufferOffsetPair (vertexBuffer ,0 ); 190geomInput .triangles .vertexBufferCount = 1 ; 191geomInput .triangles .vertexFormat = Format ::RGB32Float ; 192geomInput .triangles .vertexStride = sizeof (Vertex ); 193geomInput .triangles .preTransformBuffer = BufferOffsetPair (transformBuffer ,0 ); 194 195AccelerationStructureBuildDesc buildInputs = {}; 196buildInputs .inputs = & geomInput ; 197buildInputs .inputCount = 1 ; 198buildInputs .flags = AccelerationStructureBuildFlags ::AllowCompaction ; 199 200// Query buffer size for acceleration structure build. 201AccelerationStructureSizes sizes ; 202GFX_CHECK_CALL_ABORT (device -> getAccelerationStructureSizes (buildInputs ,& sizes )); 203 204// Allocate buffers for acceleration structure. 205BufferDesc asDraftBufferDesc ; 206asDraftBufferDesc .defaultState = ResourceState ::AccelerationStructure ; 207asDraftBufferDesc .size = sizes .accelerationStructureSize ; 208asDraftBufferDesc .usage = BufferUsage ::AccelerationStructure ; 209ComPtr < IBuffer > draftBuffer = device -> createBuffer (asDraftBufferDesc ); 210 211BufferDesc scratchBufferDesc ; 212scratchBufferDesc .defaultState = ResourceState ::UnorderedAccess ; 213scratchBufferDesc .size = sizes .scratchSize ; 214scratchBufferDesc .usage = BufferUsage ::UnorderedAccess ; 215ComPtr < IBuffer > scratchBuffer = device -> createBuffer (scratchBufferDesc ); 216 217// Build acceleration structure. 218ComPtr < IQueryPool > compactedSizeQuery ; 219QueryPoolDesc queryPoolDesc ; 220queryPoolDesc .count = 1 ; 221queryPoolDesc .type = QueryType ::AccelerationStructureCompactedSize ; 222GFX_CHECK_CALL_ABORT ( 223device -> createQueryPool (queryPoolDesc ,compactedSizeQuery .writeRef ())); 224 225ComPtr < IAccelerationStructure > draftAS ; 226AccelerationStructureDesc draftCreateDesc ; 227draftCreateDesc .size = sizes .accelerationStructureSize ; 228GFX_CHECK_CALL_ABORT ( 229device -> createAccelerationStructure (draftCreateDesc ,draftAS .writeRef ())); 230 231compactedSizeQuery -> reset (); 232 233auto commandEncoder = queue -> createCommandEncoder (); 234AccelerationStructureQueryDesc compactedSizeQueryDesc = {}; 235compactedSizeQueryDesc .queryPool = compactedSizeQuery ; 236compactedSizeQueryDesc .queryType = QueryType ::AccelerationStructureCompactedSize ; 237commandEncoder -> buildAccelerationStructure (buildInputs ,draftAS ,nullptr ,BufferOffsetPair (scratchBuffer ,0 ),1 ,& compactedSizeQueryDesc ); 238auto commandBuffer = commandEncoder -> finish (); 239queue -> submit (commandBuffer ); 240queue -> waitOnHost (); 241 242uint64_t compactedSize = 0 ; 243compactedSizeQuery -> getResult (0 ,1 ,& compactedSize ); 244 245BufferDesc asBufferDesc ; 246asBufferDesc .defaultState = ResourceState ::AccelerationStructure ; 247asBufferDesc .size = (size_t )compactedSize ; 248asBufferDesc .usage = BufferUsage ::AccelerationStructure ; 249BLASBuffer = device -> createBuffer (asBufferDesc ); 250 251AccelerationStructureDesc createDesc ; 252createDesc .size = (size_t )compactedSize ; 253device -> createAccelerationStructure (createDesc ,BLAS .writeRef ()); 254 255commandEncoder = queue -> createCommandEncoder (); 256commandEncoder -> copyAccelerationStructure ( 257BLAS , 258draftAS , 259AccelerationStructureCopyMode ::Compact ); 260commandBuffer = commandEncoder -> finish (); 261queue -> submit (commandBuffer ); 262queue -> waitOnHost (); 263 } 264 265// Build top level acceleration structure. 266 { 267List < AccelerationStructureInstanceDescGeneric > instanceDescs ; 268instanceDescs .setCount (1 ); 269instanceDescs [0 ].accelerationStructure .value = BLAS -> getDeviceAddress (); 270instanceDescs [0 ].flags = AccelerationStructureInstanceFlags ::TriangleFacingCullDisable ; 271instanceDescs [0 ].instanceContributionToHitGroupIndex = 0 ; 272instanceDescs [0 ].instanceID = 0 ; 273instanceDescs [0 ].instanceMask = 0xFF ; 274float transformMatrix []= 275 {1.0f ,0.0f ,0.0f ,0.0f ,0.0f ,1.0f ,0.0f ,0.0f ,0.0f ,0.0f ,1.0f ,0.0f }; 276memcpy (& instanceDescs [0 ].transform [0 ][0 ],transformMatrix ,sizeof (float )* 12 ); 277 278BufferDesc instanceBufferDesc ; 279instanceBufferDesc .size = instanceDescs .getCount ()* sizeof (AccelerationStructureInstanceDescGeneric ); 280instanceBufferDesc .defaultState = ResourceState ::ShaderResource ; 281instanceBufferDesc .usage = BufferUsage ::ShaderResource |BufferUsage ::AccelerationStructureBuildInput ; 282instanceBuffer = device -> createBuffer (instanceBufferDesc ,instanceDescs .getBuffer ()); 283SLANG_CHECK_ABORT (instanceBuffer != nullptr ); 284 285AccelerationStructureBuildInput instanceInput = {}; 286instanceInput .type = AccelerationStructureBuildInputType ::Instances ; 287instanceInput .instances .instanceBuffer = BufferOffsetPair (instanceBuffer ,0 ); 288instanceInput .instances .instanceStride = sizeof (AccelerationStructureInstanceDescGeneric ); 289instanceInput .instances .instanceCount = instanceDescs .getCount (); 290 291AccelerationStructureBuildDesc buildInputs = {}; 292buildInputs .inputs = & instanceInput ; 293buildInputs .inputCount = 1 ; 294 295// Query buffer size for acceleration structure build. 296AccelerationStructureSizes sizes ; 297GFX_CHECK_CALL_ABORT (device -> getAccelerationStructureSizes (buildInputs ,& sizes )); 298 299BufferDesc asBufferDesc ; 300asBufferDesc .defaultState = ResourceState ::AccelerationStructure ; 301asBufferDesc .size = sizes .accelerationStructureSize ; 302asBufferDesc .usage = BufferUsage ::AccelerationStructure ; 303TLASBuffer = device -> createBuffer (asBufferDesc ); 304 305BufferDesc scratchBufferDesc ; 306scratchBufferDesc .defaultState = ResourceState ::UnorderedAccess ; 307scratchBufferDesc .size = sizes .scratchSize ; 308scratchBufferDesc .usage = BufferUsage ::UnorderedAccess ; 309ComPtr < IBuffer > scratchBuffer = device -> createBuffer (scratchBufferDesc ); 310 311AccelerationStructureDesc createDesc ; 312createDesc .size = sizes .accelerationStructureSize ; 313GFX_CHECK_CALL_ABORT (device -> createAccelerationStructure (createDesc ,TLAS .writeRef ())); 314 315auto commandEncoder = queue -> createCommandEncoder (); 316commandEncoder -> buildAccelerationStructure (buildInputs ,TLAS ,nullptr ,BufferOffsetPair (scratchBuffer ,0 ),0 ,nullptr ); 317auto commandBuffer = commandEncoder -> finish (); 318queue -> submit (commandBuffer ); 319queue -> waitOnHost (); 320 } 321 322const char * hitgroupNames []= {"hitgroupA" ,"hitgroupB" }; 323 324ComPtr < IShaderProgram > rayTracingProgram ; 325SLANG_CHECK_ABORT (loadShaderProgram (device ,rayTracingProgram .writeRef ())); 326RayTracingPipelineDesc rtpDesc = {}; 327rtpDesc .program = rayTracingProgram ; 328rtpDesc .hitGroupCount = 2 ; 329HitGroupDesc hitGroups [2 ]; 330hitGroups [0 ].closestHitEntryPoint = "closestHitShaderA" ; 331hitGroups [0 ].hitGroupName = hitgroupNames [0 ]; 332hitGroups [1 ].closestHitEntryPoint = "closestHitShaderB" ; 333hitGroups [1 ].hitGroupName = hitgroupNames [1 ]; 334rtpDesc .hitGroups = hitGroups ; 335rtpDesc .maxRayPayloadSize = 64 ; 336rtpDesc .maxRecursion = 2 ; 337GFX_CHECK_CALL_ABORT ( 338device -> createRayTracingPipeline (rtpDesc ,renderPipelineState .writeRef ())); 339SLANG_CHECK_ABORT (renderPipelineState != nullptr ); 340 341const char * raygenNames []= {"rayGenShaderA" ,"rayGenShaderB" }; 342const char * missNames []= {"missShaderA" ,"missShaderB" }; 343 344ShaderTableDesc shaderTableDesc = {}; 345shaderTableDesc .program = rayTracingProgram ; 346shaderTableDesc .hitGroupCount = 2 ; 347shaderTableDesc .hitGroupNames = hitgroupNames ; 348shaderTableDesc .rayGenShaderCount = 2 ; 349shaderTableDesc .rayGenShaderEntryPointNames = raygenNames ; 350shaderTableDesc .missShaderCount = 2 ; 351shaderTableDesc .missShaderEntryPointNames = missNames ; 352GFX_CHECK_CALL_ABORT (device -> createShaderTable (shaderTableDesc ,shaderTable .writeRef ())); 353 } 354 355void checkTestResults (float * expectedResult ,uint32_t count ) 356 { 357ComPtr < ISlangBlob > resultBlob ; 358auto commandEncoder = queue -> createCommandEncoder (); 359commandEncoder -> setTextureState (resultTexture ,ResourceState ::CopySource ); 360queue -> submit (commandEncoder -> finish ()); 361queue -> waitOnHost (); 362 363SubresourceLayout layout ; 364GFX_CHECK_CALL_ABORT (device -> readTexture ( 365resultTexture , 3660 ,0 , 367resultBlob .writeRef (), 368& layout )); 369size_t rowPitch = layout .rowPitch ; 370size_t pixelSize = 4 ; 371 372#if 0 // for debugging only 373writeImage ("test.hdr" ,resultBlob ,width ,height , (uint32_t )rowPitch , (uint32_t )pixelSize ); 374#endif 375auto buffer = removePadding (resultBlob ,width ,height ,rowPitch ,pixelSize ); 376auto actualData = (float * )buffer .data (); 377SLANG_CHECK_ABORT (memcmp (actualData ,expectedResult ,count * sizeof (float ))== 0 ) 378 } 379}; 380 381struct RayTracingTestA :BaseRayTracingTest 382{ 383void renderFrame () 384 { 385auto commandEncoder = queue -> createCommandEncoder (); 386auto renderEncoder = commandEncoder -> beginRayTracingPass (); 387auto rootObject = renderEncoder -> bindPipeline (renderPipelineState ,shaderTable ); 388auto cursor = ShaderCursor (rootObject ); 389cursor ["resultTexture" ].setBinding (Binding (resultTextureUAV )); 390cursor ["sceneBVH" ].setBinding (Binding (TLAS )); 391renderEncoder -> dispatchRays (0 ,width ,height ,1 ); 392renderEncoder -> end (); 393auto commandBuffer = commandEncoder -> finish (); 394queue -> submit (commandBuffer ); 395queue -> waitOnHost (); 396 } 397 398void run () 399 { 400createRequiredResources (); 401renderFrame (); 402 403float expectedResult [16 ]= {1 ,1 ,1 ,1 ,0 ,0 ,1 ,1 ,0 ,1 ,0 ,1 ,1 ,0 ,0 ,1 }; 404checkTestResults (expectedResult ,16 ); 405 } 406}; 407 408struct RayTracingTestB :BaseRayTracingTest 409{ 410void renderFrame () 411 { 412auto commandEncoder = queue -> createCommandEncoder (); 413auto renderEncoder = commandEncoder -> beginRayTracingPass (); 414auto rootObject = renderEncoder -> bindPipeline (renderPipelineState ,shaderTable ); 415auto cursor = ShaderCursor (rootObject ); 416cursor ["resultTexture" ].setBinding (Binding (resultTextureUAV )); 417cursor ["sceneBVH" ].setBinding (Binding (TLAS )); 418renderEncoder -> dispatchRays (1 ,width ,height ,1 ); 419renderEncoder -> end (); 420auto commandBuffer = commandEncoder -> finish (); 421queue -> submit (commandBuffer ); 422queue -> waitOnHost (); 423 } 424 425void run () 426 { 427createRequiredResources (); 428renderFrame (); 429 430float expectedResult [16 ]= {0 ,0 ,0 ,1 ,1 ,1 ,0 ,1 ,1 ,0 ,1 ,1 ,0 ,1 ,1 ,1 }; 431checkTestResults (expectedResult ,16 ); 432 } 433}; 434 435template < typename T > 436void rayTracingTestImpl (IDevice * device ,UnitTestContext * context ) 437{ 438T test ; 439test .init (device ,context ); 440test .run (); 441} 442 443SLANG_UNIT_TEST (RayTracingTestAD3D12 ) 444{ 445runTestImpl (rayTracingTestImpl < RayTracingTestA > ,unitTestContext ,DeviceType ::D3D12 ); 446} 447 448SLANG_UNIT_TEST (RayTracingTestAVulkan ) 449{ 450runTestImpl (rayTracingTestImpl < RayTracingTestA > ,unitTestContext ,DeviceType ::Vulkan ); 451} 452 453SLANG_UNIT_TEST (RayTracingTestBD3D12 ) 454{ 455runTestImpl (rayTracingTestImpl < RayTracingTestB > ,unitTestContext ,DeviceType ::D3D12 ); 456} 457 458SLANG_UNIT_TEST (RayTracingTestBVulkan ) 459{ 460runTestImpl (rayTracingTestImpl < RayTracingTestB > ,unitTestContext ,DeviceType ::Vulkan ); 461} 462}// namespace gfx_test 463#endif