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 "core/slang-io.h" 4#include "core/slang-memory-file-system.h" 5#include "gfx-test-util.h" 6#include "slang-rhi.h" 7#include "slang-rhi/shader-cursor.h" 8#include "unit-test/slang-unit-test.h" 9 10#include <mutex> 11using namespace rhi ; 12 13namespace gfx_test 14{ 15// Test that precompiled module cache is working. 16 17Slang ::ComPtr < slang::ISession > createSession (rhi::IDevice * device ,ISlangFileSystemExt * fileSys ) 18{ 19static std::mutex m ; 20 std::lock_guard < std ::mutex > lock (m ); 21 22Slang ::ComPtr < slang::ISession > slangSession ; 23device -> getSlangSession (slangSession .writeRef ()); 24 slang::SessionDesc sessionDesc = {}; 25sessionDesc .searchPathCount = 1 ; 26const char * searchPath = "cache/" ; 27sessionDesc .searchPaths = & searchPath ; 28sessionDesc .targetCount = 1 ; 29sessionDesc .compilerOptionEntryCount = 1 ; 30 slang::CompilerOptionEntry entry ; 31entry .name = slang::CompilerOptionName ::UseUpToDateBinaryModule ; 32entry .value .kind = slang::CompilerOptionValueKind ::Int ; 33entry .value .intValue0 = 1 ; 34sessionDesc .compilerOptionEntries = & entry ; 35 slang::TargetDesc targetDesc = {}; 36switch (device -> getInfo ().deviceType ) 37 { 38case rhi::DeviceType ::D3D12 : 39targetDesc .format = SLANG_DXIL ; 40targetDesc .profile = device -> getSlangSession ()-> getGlobalSession ()-> findProfile ("sm_6_1" ); 41break ; 42case rhi::DeviceType ::Vulkan : 43targetDesc .format = SLANG_SPIRV ; 44targetDesc .profile = device -> getSlangSession ()-> getGlobalSession ()-> findProfile ("GLSL_460" ); 45break ; 46 } 47sessionDesc .targets = & targetDesc ; 48sessionDesc .fileSystem = fileSys ; 49auto globalSession = slangSession -> getGlobalSession (); 50globalSession -> createSession (sessionDesc ,slangSession .writeRef ()); 51return slangSession ; 52} 53 54static Slang ::Result precompileProgram ( 55 rhi::IDevice * device , 56ISlangMutableFileSystem * fileSys , 57const char * shaderModuleName ) 58{ 59Slang ::ComPtr < slang::ISession > slangSession = createSession (device ,fileSys ); 60 61Slang ::ComPtr < slang::IBlob > diagnosticsBlob ; 62 slang::IModule * module = slangSession -> loadModule (shaderModuleName ,diagnosticsBlob .writeRef ()); 63diagnoseIfNeeded (diagnosticsBlob ); 64if (!module ) 65return SLANG_FAIL ; 66 67// Write loaded modules to memory file system. 68for (SlangInt i = 0 ;i < slangSession -> getLoadedModuleCount ();i ++ ) 69 { 70auto module = slangSession -> getLoadedModule (i ); 71auto path = module -> getFilePath (); 72if (path ) 73 { 74auto name = module -> getName (); 75ComPtr < ISlangBlob > outBlob ; 76module -> serialize (outBlob .writeRef ()); 77fileSys -> saveFileBlob ( 78 (Slang ::String ("cache/" )+ Slang ::String (name )+ ".slang-module" ).getBuffer (), 79outBlob ); 80 } 81 } 82return SLANG_OK ; 83} 84 85void precompiledModuleCacheTestImpl (IDevice * device ,UnitTestContext * context ) 86{ 87// First, Initialize our file system. 88ComPtr < ISlangMutableFileSystem > memoryFileSystem = 89ComPtr < ISlangMutableFileSystem > (new Slang ::MemoryFileSystem ()); 90memoryFileSystem -> createDirectory ("cache" ); 91 92const char * moduleSrc = R"( 93import "precompiled-module-imported"; 94 95// Main entry-point. 96 97using namespace ns; 98 99[shader("compute")] 100[numthreads(4, 1, 1)] 101void computeMain( 102uint3 sv_dispatchThreadID : SV_DispatchThreadID, 103uniform RWStructuredBuffer <float> buffer) 104{ 105buffer[sv_dispatchThreadID.x] = helperFunc() + helperFunc1(); 106} 107)" ; 108memoryFileSystem -> saveFile ("precompiled-module.slang" ,moduleSrc ,strlen (moduleSrc )); 109 110const char * moduleSrc2 = R"( 111module "precompiled-module-imported"; 112 113__include "precompiled-module-included.slang"; 114 115namespace ns 116{ 117public int helperFunc() 118{ 119return 1; 120} 121} 122)" ; 123memoryFileSystem -> saveFile ("precompiled-module-imported.slang" ,moduleSrc2 ,strlen (moduleSrc2 )); 124const char * moduleSrc3 = R"( 125implementing "precompiled-module-imported"; 126 127namespace ns 128{ 129public int helperFunc1() 130{ 131return 2; 132} 133} 134)" ; 135memoryFileSystem -> saveFile ("precompiled-module-included.slang" ,moduleSrc3 ,strlen (moduleSrc3 )); 136 137// Precompile a module. 138ComPtr < IShaderProgram > shaderProgram ; 139 slang::ProgramLayout * slangReflection ; 140GFX_CHECK_CALL_ABORT ( 141precompileProgram (device ,memoryFileSystem .get (),"precompiled-module-imported" )); 142 143// Next, load the precompiled slang program. 144Slang ::ComPtr < slang::ISession > slangSession = createSession (device ,memoryFileSystem ); 145ComPtr < ISlangBlob > binaryBlob ; 146memoryFileSystem -> loadFile ( 147"cache/precompiled-module-imported.slang-module" , 148binaryBlob .writeRef ()); 149auto upToDate = 150slangSession -> isBinaryModuleUpToDate ("precompiled-module-imported.slang" ,binaryBlob ); 151SLANG_CHECK (upToDate );// The module should be up-to-date. 152 153GFX_CHECK_CALL_ABORT (loadComputeProgram ( 154device , 155slangSession , 156shaderProgram , 157"precompiled-module" , 158"computeMain" , 159slangReflection )); 160 161ComputePipelineDesc pipelineDesc = {}; 162pipelineDesc .program = shaderProgram .get (); 163ComPtr < IComputePipeline > computePipeline ; 164GFX_CHECK_CALL_ABORT (device -> createComputePipeline (pipelineDesc ,computePipeline .writeRef ())); 165 166const int numberCount = 4 ; 167float initialData []= {0.0f ,0.0f ,0.0f ,0.0f }; 168BufferDesc bufferDesc = {}; 169bufferDesc .size = numberCount * sizeof (float ); 170bufferDesc .usage = BufferUsage ::UnorderedAccess |BufferUsage ::ShaderResource | 171BufferUsage ::CopySource |BufferUsage ::CopyDestination ; 172bufferDesc .memoryType = MemoryType ::DeviceLocal ; 173 174ComPtr < IBuffer > numbersBuffer ; 175GFX_CHECK_CALL_ABORT ( 176device -> createBuffer (bufferDesc , (void * )initialData ,numbersBuffer .writeRef ())); 177 178// We have done all the set up work, now it is time to start recording a command buffer for 179// GPU execution. 180 { 181auto queue = device -> getQueue (QueueType ::Graphics ); 182 183auto commandEncoder = queue -> createCommandEncoder (); 184auto encoder = commandEncoder -> beginComputePass (); 185 186ComPtr < IShaderObject > rootObject ; 187device -> createRootShaderObject (shaderProgram ,rootObject .writeRef ()); 188encoder -> bindPipeline (computePipeline ,rootObject ); 189 190ShaderCursor entryPointCursor ( 191rootObject -> getEntryPoint (0 ));// get a cursor the the first entry-point. 192// Bind buffer to the entry point. 193entryPointCursor .getPath ("buffer" ).setBinding (numbersBuffer ); 194 195encoder -> dispatchCompute (1 ,1 ,1 ); 196encoder -> end (); 197queue -> submit (commandEncoder -> finish ()); 198queue -> waitOnHost (); 199 } 200 201compareComputeResult (device ,numbersBuffer , std::array {3.0f ,3.0f ,3.0f ,3.0f }); 202 203// Now we change the source and check if the precompiled module is still up-to-date. 204const char * moduleSrc4 = R"( 205implementing "precompiled-module-imported"; 206namespace ns { 207public int helperFunc1() { 208return 2; 209} 210} 211)" ; 212memoryFileSystem -> saveFile ("precompiled-module-included.slang" ,moduleSrc4 ,strlen (moduleSrc4 )); 213 214slangSession = createSession (device ,memoryFileSystem ); 215upToDate = 216slangSession -> isBinaryModuleUpToDate ("precompiled-module-imported.slang" ,binaryBlob ); 217SLANG_CHECK (!upToDate );// The module should not be up-to-date because the source has changed. 218} 219 220SLANG_UNIT_TEST (precompiledModuleCacheD3D12 ) 221{ 222runTestImpl (precompiledModuleCacheTestImpl ,unitTestContext ,DeviceType ::D3D12 , {}); 223} 224 225SLANG_UNIT_TEST (precompiledModuleCacheVulkan ) 226{ 227runTestImpl (precompiledModuleCacheTestImpl ,unitTestContext ,DeviceType ::Vulkan , {}); 228} 229 230}// namespace gfx_test