diff options
| author | Jay Kwak <82421531+jkwak-work@users.noreply.github.com> | 2025-05-01 00:14:24 -0700 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2025-05-01 00:14:24 -0700 |
| commit | 15fce7c8f11985be5ac1fdb1f180a30150e9db89 (patch) | |
| tree | dc801f723860b852a7fb8966de8e8d22bb825723 | |
| parent | a8d7bb9f781fd77080f07bfaab54fbbcf3a3686d (diff) | |
Smoke test WASM as a part of CI (#6969)
* Simplify build of slang-wasm
* Add smoke-test for slang-wasm in ci
* Avoid git-clone playground
| -rw-r--r-- | .github/workflows/ci.yml | 8 | ||||
| -rw-r--r-- | CMakePresets.json | 8 | ||||
| -rw-r--r-- | source/compiler-core/slang-json-value.cpp | 9 | ||||
| -rw-r--r-- | tests/wasm/smoke/rand_float.slang | 63 | ||||
| -rw-r--r-- | tests/wasm/smoke/smoke-test.js | 116 |
5 files changed, 198 insertions, 6 deletions
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 46db88275..e2598cd73 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -135,8 +135,12 @@ jobs: cmake --install build --config Release --component generators --prefix generators emcmake cmake -DSLANG_GENERATORS_PATH=generators/bin --preset emscripten -DSLANG_SLANG_LLVM_FLAVOR=DISABLE cmake --build --preset emscripten --config "$cmake_config" --target slang-wasm - [ -f "build.em/$cmake_config/bin/slang-wasm.wasm" ] - [ -f "build.em/$cmake_config/bin/slang-wasm.js" ] + mkdir "build.em/$cmake_config/bin/smoke" + cp tests/wasm/smoke/* "build.em/$cmake_config/bin/smoke/" + cd "build.em/$cmake_config/bin" + [ -f "slang-wasm.wasm" ] + [ -f "slang-wasm.js" ] + node smoke/smoke-test.js smoke/rand_float.slang computeMain else if [[ "${{ matrix.os }}" =~ "windows" && "${{ matrix.config }}" != "release" && "${{ matrix.config }}" != "releaseWithDebugInfo" ]]; then # Doing a debug build will try to link against a release built llvm, this diff --git a/CMakePresets.json b/CMakePresets.json index 2632f522c..91f37de08 100644 --- a/CMakePresets.json +++ b/CMakePresets.json @@ -24,8 +24,14 @@ "binaryDir": "${sourceDir}/build.em", "cacheVariables": { "SLANG_SLANG_LLVM_FLAVOR": "DISABLE", - "SLANG_ENABLE_SPLIT_DEBUG_INFO": "OFF", + "SLANG_ENABLE_AFTERMATH": "OFF", + "SLANG_ENABLE_CUDA": "OFF", + "SLANG_ENABLE_GFX": "OFF", + "SLANG_ENABLE_OPTIX": "OFF", + "SLANG_ENABLE_REPLAYER": "OFF", "SLANG_ENABLE_SLANG_RHI": "OFF", + "SLANG_ENABLE_SPLIT_DEBUG_INFO": "OFF", + "SLANG_ENABLE_TESTS": "OFF", "CMAKE_C_FLAGS_INIT": "-fwasm-exceptions -Os", "CMAKE_CXX_FLAGS_INIT": "-fwasm-exceptions -Os", "CMAKE_EXE_LINKER_FLAGS": "-sASSERTIONS -sALLOW_MEMORY_GROWTH -fwasm-exceptions --export=__cpp_exception" diff --git a/source/compiler-core/slang-json-value.cpp b/source/compiler-core/slang-json-value.cpp index ae38bd086..56d003737 100644 --- a/source/compiler-core/slang-json-value.cpp +++ b/source/compiler-core/slang-json-value.cpp @@ -869,8 +869,8 @@ void JSONContainer::_removeKey(JSONValue& obj, Index globalIndex) { auto localBuf = m_objectValues.getBuffer() + range.startIndex; ::memmove( - localBuf + localIndex, - localBuf + localIndex + 1, + (void*)(localBuf + localIndex), + (void*)(localBuf + localIndex + 1), sizeof(*localBuf) * (range.count - (localIndex + 1))); } @@ -925,7 +925,10 @@ template<typename T> ioList.growToCount(newStartIndex + ioRange.count + 1); auto buffer = ioList.getBuffer(); - ::memmove(buffer + newStartIndex, buffer + ioRange.startIndex, sizeof(*buffer) * ioRange.count); + ::memmove( + (void*)(buffer + newStartIndex), + (void*)(buffer + ioRange.startIndex), + sizeof(*buffer) * ioRange.count); buffer[newStartIndex + ioRange.count] = value; diff --git a/tests/wasm/smoke/rand_float.slang b/tests/wasm/smoke/rand_float.slang new file mode 100644 index 000000000..732cff093 --- /dev/null +++ b/tests/wasm/smoke/rand_float.slang @@ -0,0 +1,63 @@ +// Hybrid Tausworthe PRNG +// +// Code adapted from: https://indico.cern.ch/event/93877/papers/2118070/files/4416-acat3.pdf (See document for license) +// + +uniform float seed; +RWStructuredBuffer<float> outputBuffer; + +uint seedPerThread(uint idx) +{ + return ((uint)idx + (uint)(seed * 1000000)) * 1099087573UL; +} + +uint tauStep(uint z, uint s1, uint s2, uint s3, uint M) +{ + uint b = (((z << s1) ^ z) >> s2); + return (((z & M) << s3) ^ b); +} + +[shader("compute")] +[numthreads(64, 1, 1)] +void computeMain(uint2 dispatchThreadId : SV_DispatchThreadID) +{ + uint idx = dispatchThreadId.x; + uint val = ((uint)idx) * 1099087573UL + ((uint)seed) * 12003927; + + uint z = tauStep(val, 13, 19, 12, 4294967294); + z = tauStep(z, 2, 25, 4, 4294967288); + z = tauStep(z, 3, 11, 17, 4294967280); + + uint z1, z2, z3, z4; + uint r0, r1, r2, r3; + + // STEP 1 + uint _seed = seedPerThread(idx); + z1 = tauStep(_seed, 13, 19, 12, 429496729UL); + z2 = tauStep(_seed, 2, 25, 4, 4294967288UL); + z3 = tauStep(_seed, 3, 11, 17, 429496280UL); + z4 = (1664525 * _seed + 1013904223UL); + r0 = (z1 ^ z2 ^ z3 ^ z4); + // STEP 2 + z1 = tauStep(r0, 13, 19, 12, 429496729UL); + z2 = tauStep(r0, 2, 25, 4, 4294967288UL); + z3 = tauStep(r0, 3, 11, 17, 429496280UL); + z4 = (1664525 * r0 + 1013904223UL); + r1 = (z1 ^ z2 ^ z3 ^ z4); + // STEP 3 + z1 = tauStep(r1, 13, 19, 12, 429496729UL); + z2 = tauStep(r1, 2, 25, 4, 4294967288UL); + z3 = tauStep(r1, 3, 11, 17, 429496280UL); + z4 = (1664525 * r1 + 1013904223UL); + r2 = (z1 ^ z2 ^ z3 ^ z4); + // STEP 4 + z1 = tauStep(r2, 13, 19, 12, 429496729UL); + z2 = tauStep(r2, 2, 25, 4, 4294967288UL); + z3 = tauStep(r2, 3, 11, 17, 429496280UL); + z4 = (1664525 * r2 + 1013904223UL); + r3 = (z1 ^ z2 ^ z3 ^ z4); + + float u4 = r3 * 2.3283064365387e-10; + + outputBuffer[idx] = u4; +}
\ No newline at end of file diff --git a/tests/wasm/smoke/smoke-test.js b/tests/wasm/smoke/smoke-test.js new file mode 100644 index 000000000..32611c923 --- /dev/null +++ b/tests/wasm/smoke/smoke-test.js @@ -0,0 +1,116 @@ +import * as wasmModule from '../slang-wasm.js'; +import { readFileSync } from 'fs'; +import { resolve, basename } from 'path'; + +async function runSmokeTest() { + try { + // Get the file path from command line arguments + const filePath = process.argv[2]; + const entryPointName = process.argv[3]; + if (!filePath || !entryPointName) { + console.error('Please provide a path to a .slang file and an entry point name'); + console.error('Usage: node test/smoke-test.js <path-to-slang-file> <entry-point-name>'); + process.exit(1); + } + + console.log(`Starting Slang WASM smoke test with file: ${filePath} and entry point: ${entryPointName}`); + + // Read the source file + const absolutePath = resolve(filePath); + const source = readFileSync(absolutePath, 'utf8'); + const fileName = basename(filePath); + + // Load the WASM module + const module = await wasmModule.default(); + console.log('WASM module loaded successfully'); + + // Print available compile targets + const targets = module.getCompileTargets(); + if (!targets) { + throw new Error('Failed to get compile targets'); + } + console.log('Available compile targets:', JSON.stringify(targets, null, 2)); + + // Find SPIRV target value + const spirvTarget = targets.findIndex(target => target.name.toLowerCase() === 'spirv'); + if (spirvTarget === -1) { + throw new Error('SPIRV target not found in available targets'); + } + console.log('Found SPIRV target at index:', spirvTarget); + + // Get the actual SPIRV target value + const spirvTargetValue = targets[spirvTarget].value; + console.log('SPIRV target value:', spirvTargetValue); + + // Create a global session + const globalSession = module.createGlobalSession(); + if (!globalSession) { + throw new Error('Failed to create global session'); + } + console.log('Global session created'); + + // Create a session with SPIRV as the target + const session = globalSession.createSession(spirvTargetValue); + if (!session) { + throw new Error('Failed to create session'); + } + console.log('Session created with SPIRV target'); + + // Load the shader source + const module1 = session.loadModuleFromSource(source, fileName, ''); + if (!module1) { + const error = module.getLastError(); + throw new Error(`Failed to load module: ${error ? error.message : 'Unknown error'}`); + } + console.log('Shader module loaded'); + + // Check for compilation errors + const error = module.getLastError(); + if (error && error.result !== module.SLANG_OK) { + throw new Error(`Compilation failed: ${error.message}`); + } + console.log('No compilation errors found'); + + // Try to find the entry point + const entryPoint = module1.findEntryPointByName(entryPointName); + if (!entryPoint) { + throw new Error(`Could not find entry point "${entryPointName}"`); + } + console.log(`Entry point "${entryPointName}" found`); + + // Create and link the program + const program = session.createCompositeComponentType([module1]); + if (!program) { + throw new Error('Failed to create composite component type'); + } + const linkedProgram = program.link(); + if (!linkedProgram) { + throw new Error('Failed to link program'); + } + console.log('Program created and linked successfully'); + + // Try to get the SPIRV code + console.log('\nTrying to generate SPIRV code:'); + const spirvBinary = linkedProgram.getTargetCodeBlob(0); // 0 is the target index + if (!spirvBinary) { + throw new Error('Could not generate SPIRV binary'); + } + console.log('SPIRV binary generated successfully'); + console.log('Generated binary length:', spirvBinary.length); + + // Clean up + linkedProgram.delete(); + program.delete(); + entryPoint.delete(); + module1.delete(); + session.delete(); + globalSession.delete(); + console.log('Smoke test completed successfully'); + process.exit(0); // Explicit success exit code + } catch (error) { + console.error('Smoke test failed:', error); + process.exit(1); // Error exit code + } +} + +runSmokeTest();
\ No newline at end of file |
