Version: SHaRC v1.8.0 (SHARC_VERSION_* in SharcCommon.h), GLSL path (SHARC_ENABLE_GLSL), compiled with SHARC_ENABLE_RESPONSIVE_LIGHTING=1 and SHARC_ENABLE_SH_ENCODING=1. Observed on Windows, RTX 4090, driver 610.74, Vulkan 1.3.
Summary
When a hash-grid bucket is full, HashGridInsertEntry returns HASH_GRID_INVALID_CACHE_INDEX (0xFFFFFFFF), and SharcUpdateHit stores that sentinel into sharcState.cacheIndices[0] verbatim (there is no guard at the store site).
On the next vertex/miss, the propagation loops in both SharcUpdateHit and SharcUpdateMiss run the stored index through an unconditional mask:
tempHashGridIndex &= SHARC_CACHE_INDEX_BIT_MASK; // SharcUpdateHit
hashGridIndex &= SHARC_CACHE_INDEX_BIT_MASK; // SharcUpdateMiss
This block is compiled in whenever SHARC_ENABLE_RESPONSIVE_LIGHTING is defined — the runtime isResponsiveLighting=false toggle does not bypass the mask. The mask rewrites 0xFFFFFFFF to SHARC_CACHE_INDEX_BIT_MASK (0x3FFFFFF with the default 26-bit layout), which is no longer equal to HASH_GRID_INVALID_CACHE_INDEX, so the guard inside SharcAddVoxelData passes and the interlocked adds land at:
accumulationBuffer + 0x3FFFFFF * sizeof(SharcAccumulationData) // ≈ base + 2 GiB with the 32 B SH layout
far past the end of any realistically sized allocation → page fault → VK_ERROR_DEVICE_LOST.
Why it looks intermittent
The bug requires a full bucket, so it only fires once the table gets crowded (large sceneScale, geometry-dense views, e.g. a 100k-instance dynamic scene filling the screen). In practice this presented as "device lost after 1–2 seconds whenever the camera looks at the dense content, never otherwise".
Evidence
Diagnosed via VK_EXT_device_fault plus logging each SSBO's device-address range. With sharcCapacityLog2 = 22 (SH layout, accumulation stride 32 B):
- accumulation SSBO:
0x1d33c00000 .. 0x1d3bc00000 (128 MiB)
- faulting page reported by the driver:
0x1db3bff000
0x1db3bff000 is exactly the page of accumulationBase + 0x3FFFFFF * 32 — i.e. the masked sentinel index, not a random scribble. The fault addresses were bit-identical across independent device losses.
Suggested fix
Skip sentinel entries at the top of both propagation loops, before the responsive-offset block (a post-mask compare is not sufficient: with responsive lighting enabled at runtime, SharcGetResponsiveIndexOffset can shift the sentinel first):
for (...)
{
HashGridIndex idx = sharcState.cacheIndices[i];
if (idx == HASH_GRID_INVALID_CACHE_INDEX)
continue;
...
}
With this change our reproducer (dense 25k–100k instance scene, sceneScale 100, capacity 2^22) went from crashing on every run to completing indefinitely; the fault signature has not reappeared.
An equivalent hardening alternative would be a hashGridIndex >= capacity guard inside SharcAddVoxelData, which would also catch any future index-forging path.
Version: SHaRC v1.8.0 (
SHARC_VERSION_*inSharcCommon.h), GLSL path (SHARC_ENABLE_GLSL), compiled withSHARC_ENABLE_RESPONSIVE_LIGHTING=1andSHARC_ENABLE_SH_ENCODING=1. Observed on Windows, RTX 4090, driver 610.74, Vulkan 1.3.Summary
When a hash-grid bucket is full,
HashGridInsertEntryreturnsHASH_GRID_INVALID_CACHE_INDEX(0xFFFFFFFF), andSharcUpdateHitstores that sentinel intosharcState.cacheIndices[0]verbatim (there is no guard at the store site).On the next vertex/miss, the propagation loops in both
SharcUpdateHitandSharcUpdateMissrun the stored index through an unconditional mask:This block is compiled in whenever
SHARC_ENABLE_RESPONSIVE_LIGHTINGis defined — the runtimeisResponsiveLighting=falsetoggle does not bypass the mask. The mask rewrites 0xFFFFFFFF toSHARC_CACHE_INDEX_BIT_MASK(0x3FFFFFF with the default 26-bit layout), which is no longer equal toHASH_GRID_INVALID_CACHE_INDEX, so the guard insideSharcAddVoxelDatapasses and the interlocked adds land at:far past the end of any realistically sized allocation → page fault →
VK_ERROR_DEVICE_LOST.Why it looks intermittent
The bug requires a full bucket, so it only fires once the table gets crowded (large
sceneScale, geometry-dense views, e.g. a 100k-instance dynamic scene filling the screen). In practice this presented as "device lost after 1–2 seconds whenever the camera looks at the dense content, never otherwise".Evidence
Diagnosed via
VK_EXT_device_faultplus logging each SSBO's device-address range. WithsharcCapacityLog2 = 22(SH layout, accumulation stride 32 B):0x1d33c00000 .. 0x1d3bc00000(128 MiB)0x1db3bff0000x1db3bff000is exactly the page ofaccumulationBase + 0x3FFFFFF * 32— i.e. the masked sentinel index, not a random scribble. The fault addresses were bit-identical across independent device losses.Suggested fix
Skip sentinel entries at the top of both propagation loops, before the responsive-offset block (a post-mask compare is not sufficient: with responsive lighting enabled at runtime,
SharcGetResponsiveIndexOffsetcan shift the sentinel first):With this change our reproducer (dense 25k–100k instance scene, sceneScale 100, capacity 2^22) went from crashing on every run to completing indefinitely; the fault signature has not reappeared.
An equivalent hardening alternative would be a
hashGridIndex >= capacityguard insideSharcAddVoxelData, which would also catch any future index-forging path.