Summary
SlangSession::create_session requests the hlsl_nvapi Slang capability unconditionally, while the NVAPI module that capability exists to support is requested only for SGL_HAS_NVAPI && DeviceType::d3d12. The capability request is therefore strictly broader than the link it backs.
Slang has tolerated this mismatch silently until now. shader-slang/slang#11225 ("Capabilities: error on capabilities incompatible with compilation target", fixes #4422) makes an incompatible capability request a hard error, so every non-HLSL target now fails to load any module:
error[E36121]: requested capability 'hlsl_nvapi' is incompatible with compilation target 'spirv'
error[E39999]: import failed due to compilation error
fatal error[E40003]: compilation ceased
This is a downstream bug in SlangPy, not a defect in slang#11225 — the Slang change is working as designed and is correctly surfacing a pre-existing latent looseness on our side. slang#11225 is currently blocked solely by our SlangPy Tests status.
The asymmetry (verified at main @ 086ca32)
Capability requested with no target/platform condition — src/sgl/device/shader.cpp:404-408:
// Add hlsl_nvapi capability.
session_options.add(
slang::CompilerOptionName::Capability,
int(m_device->global_session()->findCapability("hlsl_nvapi"))
);
The NVAPI module it backs is guarded in both places it is used:
shader.cpp:250 — module create: if (SGL_HAS_NVAPI && m_device->type() == DeviceType::d3d12)
shader.cpp:656 — module link: same predicate
shader.cpp:503-506 — the SGL_ENABLE_NVAPI macro define already branches on exactly this predicate, in the same function as the unguarded request
There is also an adjacent TODO at shader.cpp:409-413 recording that passing all detected capabilities "leads to slang compilation errors and needs more investigation" — the same underlying looseness.
Evidence
CI run: https://github.com/shader-slang/slangpy/actions/runs/30779697035 (slangpy main @ 086ca32 × slang#11225 head db61cec)
sgl_tests: 172 passed / 28 failed / 3 skipped, but 0 assertions failed (18535/18535 windows, 15593/15593 linux). Every one of the 28 is a thrown Failed to load slang module ... followed immediately by E36121 — no test logic failed, module loading did.
All 28 failures are the vulkan subcase; zero d3d12 failures. Extraction method, so this is checkable: doctest prints a multi-line DEEPEST SUBCASE STACK REACHED header with the subcase name on the following indented line, and windows.log is CRLF, so the naive one-line grep is vacuous — tr -d '\r' < log | grep -A2 'DEEPEST SUBCASE STACK REACHED' yields 28 vulkan, 0 d3d12 per job, and grep -c on the header returns 28, confirming the enumeration is complete rather than partially matched.
Correction (2026-08-03): an earlier version of this issue stated the d3d12 subcases "did execute and all passed" and called that decisive. That was an inference presented as an observation, and it is withdrawn. The logs contain no device-creation line, and every d3d12 string in windows.log is a build-artifact path. The defensible version: tests/sgl/testing.cpp:73 iterates device_types{d3d12, vulkan} on Windows vs {vulkan} on Linux, and Windows reports 18535 assertions against Linux's 15593 — consistent with d3d12 executing and not failing, but not direct evidence. Note the confound: both platforms report identical case counts (200 / 172 passed / 28 failed / 3 skipped), so the assertion delta could also come from Windows-only or D3D12-only assertions inside otherwise-shared cases rather than from a second device iteration. What would actually settle it is D3D12 CI on the PR. The SGL_HAS_NVAPI ON/OFF contrast below is unaffected and remains the load-bearing evidence.
The two platforms fail for two different reasons, which confirms both halves of the predicate are load-bearing:
| Platform |
Configure |
nvapi module |
Result |
| linux-gcc |
SGL_HAS_NVAPI: OFF |
never created or linked |
vulkan fails — capability requested for a module absent from the build entirely |
| windows-msvc |
SGL_HAS_NVAPI: ON |
created, linked for d3d12 only |
d3d12 passes; vulkan fails — capability requested for a non-HLSL target |
Deterministic: identical 28 cases on linux-gcc and windows-msvc, across both run attempts. Not a flake.
Affected test areas: hot_reload::* (13), refl::* (6), func::* (5), cursors::* (2), device::shader, device::texture_loader_batched_uploads.
Suggested fix
Gate the capability request with the same predicate already used at :250 / :656 / :503-506:
if (SGL_HAS_NVAPI && device_type == DeviceType::d3d12) {
session_options.add(
slang::CompilerOptionName::Capability,
int(m_device->global_session()->findCapability("hlsl_nvapi"))
);
}
device_type is already a local in this function (shader.cpp:307, = m_device->type()), and :381 establishes the in-function precedent for a d3d12-conditional block.
Scope notes:
- The fix is a green no-op against released Slang.
external/CMakeLists.txt:85 pins SGL_SLANG_VERSION "2026.12", which predates #11225, so normal CI cannot regress on it — it only changes behaviour once a Slang containing #11225 is picked up.
vulkan is the only non-HLSL target proven affected by this run (no metal/CUDA/CPU job exists in the dispatch matrix). By the same reasoning metal/cuda/wgsl should be affected too, but that is inference, not observed here.
- Sequencing:
.github/workflows/ci-latest-slang.yml checks out slangpy at its default branch for repository_dispatch runs — client_payload controls only the slang ref. So SlangPy Tests on slang#11225 will keep failing until this fix is merged to slangpy main; an open or draft PR does not change that status.
🤖 Generated by an automated SlangPy coworker — may be inaccurate. A human maintainer should verify.
Summary
SlangSession::create_sessionrequests thehlsl_nvapiSlang capability unconditionally, while the NVAPI module that capability exists to support is requested only forSGL_HAS_NVAPI && DeviceType::d3d12. The capability request is therefore strictly broader than the link it backs.Slang has tolerated this mismatch silently until now. shader-slang/slang#11225 ("Capabilities: error on capabilities incompatible with compilation target", fixes #4422) makes an incompatible capability request a hard error, so every non-HLSL target now fails to load any module:
This is a downstream bug in SlangPy, not a defect in slang#11225 — the Slang change is working as designed and is correctly surfacing a pre-existing latent looseness on our side. slang#11225 is currently blocked solely by our
SlangPy Testsstatus.The asymmetry (verified at
main@086ca32)Capability requested with no target/platform condition —
src/sgl/device/shader.cpp:404-408:The NVAPI module it backs is guarded in both places it is used:
shader.cpp:250— module create:if (SGL_HAS_NVAPI && m_device->type() == DeviceType::d3d12)shader.cpp:656— module link: same predicateshader.cpp:503-506— theSGL_ENABLE_NVAPImacro define already branches on exactly this predicate, in the same function as the unguarded requestThere is also an adjacent TODO at
shader.cpp:409-413recording that passing all detected capabilities "leads to slang compilation errors and needs more investigation" — the same underlying looseness.Evidence
CI run: https://github.com/shader-slang/slangpy/actions/runs/30779697035 (slangpy
main@086ca32× slang#11225 headdb61cec)sgl_tests: 172 passed / 28 failed / 3 skipped, but 0 assertions failed (18535/18535 windows, 15593/15593 linux). Every one of the 28 is a thrownFailed to load slang module ...followed immediately byE36121— no test logic failed, module loading did.All 28 failures are the
vulkansubcase; zero d3d12 failures. Extraction method, so this is checkable: doctest prints a multi-lineDEEPEST SUBCASE STACK REACHEDheader with the subcase name on the following indented line, andwindows.logis CRLF, so the naive one-line grep is vacuous —tr -d '\r' < log | grep -A2 'DEEPEST SUBCASE STACK REACHED'yields 28vulkan, 0d3d12per job, andgrep -con the header returns 28, confirming the enumeration is complete rather than partially matched.Correction (2026-08-03): an earlier version of this issue stated the d3d12 subcases "did execute and all passed" and called that decisive. That was an inference presented as an observation, and it is withdrawn. The logs contain no device-creation line, and every
d3d12string inwindows.logis a build-artifact path. The defensible version:tests/sgl/testing.cpp:73iteratesdevice_types{d3d12, vulkan}on Windows vs{vulkan}on Linux, and Windows reports 18535 assertions against Linux's 15593 — consistent with d3d12 executing and not failing, but not direct evidence. Note the confound: both platforms report identical case counts (200 / 172 passed / 28 failed / 3 skipped), so the assertion delta could also come from Windows-only or D3D12-only assertions inside otherwise-shared cases rather than from a second device iteration. What would actually settle it is D3D12 CI on the PR. TheSGL_HAS_NVAPION/OFF contrast below is unaffected and remains the load-bearing evidence.The two platforms fail for two different reasons, which confirms both halves of the predicate are load-bearing:
SGL_HAS_NVAPI: OFFSGL_HAS_NVAPI: ONDeterministic: identical 28 cases on linux-gcc and windows-msvc, across both run attempts. Not a flake.
Affected test areas:
hot_reload::*(13),refl::*(6),func::*(5),cursors::*(2),device::shader,device::texture_loader_batched_uploads.Suggested fix
Gate the capability request with the same predicate already used at
:250/:656/:503-506:device_typeis already a local in this function (shader.cpp:307,= m_device->type()), and:381establishes the in-function precedent for a d3d12-conditional block.Scope notes:
external/CMakeLists.txt:85pinsSGL_SLANG_VERSION "2026.12", which predates #11225, so normal CI cannot regress on it — it only changes behaviour once a Slang containing #11225 is picked up.vulkanis the only non-HLSL target proven affected by this run (no metal/CUDA/CPU job exists in the dispatch matrix). By the same reasoning metal/cuda/wgsl should be affected too, but that is inference, not observed here..github/workflows/ci-latest-slang.ymlchecks out slangpy at its default branch forrepository_dispatchruns —client_payloadcontrols only the slang ref. SoSlangPy Testson slang#11225 will keep failing until this fix is merged to slangpymain; an open or draft PR does not change that status.🤖 Generated by an automated SlangPy coworker — may be inaccurate. A human maintainer should verify.