From 43afcd6bfb1000b4cee976b1bf456c651ab50f74 Mon Sep 17 00:00:00 2001 From: Emil <65846814+emil28092005@users.noreply.github.com> Date: Wed, 23 Sep 2026 22:40:08 +0300 Subject: [PATCH] Declare R32f HZB image and support chunked GPU culling --- shaders/gpu_scene.slang | 6 +++--- tests/test_shader_reflection.py | 27 +++++++++++++++++++++++++++ 2 files changed, 30 insertions(+), 3 deletions(-) diff --git a/shaders/gpu_scene.slang b/shaders/gpu_scene.slang index 3b07c01..de77059 100644 --- a/shaders/gpu_scene.slang +++ b/shaders/gpu_scene.slang @@ -190,7 +190,7 @@ bool appendDeferred(uint candidateIndex) { [shader("compute")] [numthreads(64, 1, 1)] void gpuCullMain(uint3 dispatchId : SV_DispatchThreadID) { - uint index = dispatchId.x; + uint index = dispatchId.x + cullParameters.reserved0; if (index >= cullParameters.candidateCount) return; Candidate candidate = candidates[index]; InstanceRecord instance = cullInstances[candidate.instanceId]; @@ -207,7 +207,7 @@ void gpuCullMain(uint3 dispatchId : SV_DispatchThreadID) { [shader("compute")] [numthreads(64, 1, 1)] void gpuPostCullMain(uint3 dispatchId : SV_DispatchThreadID) { - uint index = dispatchId.x; + uint index = dispatchId.x + cullParameters.reserved0; if (index >= cullParameters.deferredCapacity || index >= deferredCount[0]) return; Candidate candidate = candidates[deferredIds[index]]; InstanceRecord instance = cullInstances[candidate.instanceId]; @@ -224,7 +224,7 @@ struct HzbParameters { }; [[vk::push_constant]] ConstantBuffer hzbParameters; [[vk::binding(0,0)]] Texture2D hzbSource; -[[vk::binding(1,0)]] RWTexture2D hzbOutput; +[[vk::binding(1,0)]] [vk::image_format("r32f")] RWTexture2D hzbOutput; [shader("compute")] [numthreads(8, 8, 1)] void gpuHzbMain(uint3 dispatchId : SV_DispatchThreadID) { diff --git a/tests/test_shader_reflection.py b/tests/test_shader_reflection.py index d2caa1c..12eff72 100644 --- a/tests/test_shader_reflection.py +++ b/tests/test_shader_reflection.py @@ -76,6 +76,33 @@ class ReflectionTests(unittest.TestCase): self.assertEqual([bindings[n]["type"] for n in (7, 8)], ["sampled_image_2d", "sampled_image_2d"]) + def test_hzb_storage_image_has_explicit_r32f_format(self): + compiler = os.environ["FASET_TEST_SLANGC"] + with tempfile.TemporaryDirectory(prefix="faset-hzb-format-") as directory: + process = subprocess.run( + [sys.executable, str(SCRIPT), "--compiler", compiler, "--source", + str(SCRIPT.parents[1] / "shaders" / "gpu_scene.slang"), "--entry", + "gpuHzbMain", "--define", "FASET_GPU_HZB=1", "--output", directory], + capture_output=True, text=True, + ) + self.assertEqual(process.returncode, 0, process.stderr) + bytecode = (Path(directory) / "gpuHzbMain.spv").read_bytes() + words = struct.unpack(f"<{len(bytecode) // 4}I", bytecode) + capabilities = set() + image_formats = [] + offset = 5 + while offset < len(words): + count, opcode = words[offset] >> 16, words[offset] & 0xffff + self.assertGreater(count, 0) + if opcode == 17: # OpCapability + capabilities.add(words[offset + 1]) + elif opcode == 25: # OpTypeImage; last operand is ImageFormat + image_formats.append((words[offset + 7], words[offset + 8])) + offset += count + self.assertNotIn(55, capabilities) # StorageImageReadWithoutFormat + self.assertNotIn(56, capabilities) # StorageImageWriteWithoutFormat + self.assertIn((2, 3), image_formats) # storage image, R32f + if __name__ == "__main__": unittest.main()