From 114e577e56c600cfbf478a87835babd98bb2c35c Mon Sep 17 00:00:00 2001 From: Xget7 Date: Tue, 20 Oct 2026 01:15:57 -0300 Subject: [PATCH 1/4] Share one frame-slot count and one sort key width across the Vulkan jobs Every slot array and descriptor pool sized itself from a local copy of 2 (VulkanFrameLoop::kFramesInFlight and a kSlots in each of the visibility, sort, LOD selection and frame compute classes). They now read kFramesInFlight from layout/VulkanFrameSlots.h, so world/, frame/, raster/, cull/, sort/ and selection/ no longer include presentation/ for a constant. The visibility pass and the radix sort each declared their own KeyBits. Both use layout/VulkanSortKeys.h now, which also names the 16 and 32 bit widths that VulkanFrameCompute and the radix pass count passes from. Behaviour, shader text and SPIR-V are unchanged: the generated .spv hashes equal the layout baseline. --- .../vulkan/cull/VulkanVisibilityPass.cpp | 30 +++++++++---------- .../vulkan/cull/VulkanVisibilityPass.h | 18 +++++------ .../vulkan/frame/VulkanCameraUniforms.h | 4 +-- .../vulkan/frame/VulkanFrameCompute.cpp | 15 +++++----- .../vulkan/frame/VulkanFrameCompute.h | 17 +++++++---- .../vulkan/layout/VulkanFrameSlots.h | 11 +++++++ .../rendering/vulkan/layout/VulkanSortKeys.h | 17 +++++++++++ .../vulkan/presentation/VulkanFrameLoop.h | 3 +- .../vulkan/raster/VulkanSplatPipeline.cpp | 15 +++++----- .../vulkan/raster/VulkanSplatPipeline.h | 4 +-- .../vulkan/selection/VulkanLodSelection.cpp | 13 ++++---- .../vulkan/selection/VulkanLodSelection.h | 4 +-- .../rendering/vulkan/sort/VulkanRadixSort.cpp | 13 ++++---- .../rendering/vulkan/sort/VulkanRadixSort.h | 9 +++--- .../cpp/rendering/vulkan/world/VulkanWorld.h | 4 +-- .../vulkan/cull/VulkanVisibilityPassTest.cpp | 20 ++++++------- .../selection/VulkanLodSelectionTest.cpp | 3 +- .../vulkan/sort/VulkanRadixSortTest.cpp | 12 ++++---- .../vulkan/world/VulkanWorldTest.cpp | 4 +-- 19 files changed, 126 insertions(+), 90 deletions(-) create mode 100644 packages/splatkit-android/src/main/cpp/rendering/vulkan/layout/VulkanFrameSlots.h create mode 100644 packages/splatkit-android/src/main/cpp/rendering/vulkan/layout/VulkanSortKeys.h diff --git a/packages/splatkit-android/src/main/cpp/rendering/vulkan/cull/VulkanVisibilityPass.cpp b/packages/splatkit-android/src/main/cpp/rendering/vulkan/cull/VulkanVisibilityPass.cpp index 9e4f220..d4282f9 100644 --- a/packages/splatkit-android/src/main/cpp/rendering/vulkan/cull/VulkanVisibilityPass.cpp +++ b/packages/splatkit-android/src/main/cpp/rendering/vulkan/cull/VulkanVisibilityPass.cpp @@ -255,29 +255,29 @@ bool VulkanVisibilityPass::createDescriptors() { return false; VkDescriptorPoolSize sizes[2]{}; - sizes[0] = {VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, kSlots}; + sizes[0] = {VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, kFramesInFlight}; sizes[1] = {VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, - (kVisibilityStorageBindings + kPrepareBindings) * kSlots}; + (kVisibilityStorageBindings + kPrepareBindings) * kFramesInFlight}; VkDescriptorPoolCreateInfo poolInfo{VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO}; - poolInfo.maxSets = 2 * kSlots; + poolInfo.maxSets = 2 * kFramesInFlight; poolInfo.poolSizeCount = 2; poolInfo.pPoolSizes = sizes; if (vkCreateDescriptorPool(device, &poolInfo, nullptr, &descriptorPool_) != VK_SUCCESS) return false; - std::array visibilityLayouts{}; - std::array prepareLayouts{}; + std::array visibilityLayouts{}; + std::array prepareLayouts{}; visibilityLayouts.fill(visibilitySetLayout_); prepareLayouts.fill(prepareSetLayout_); VkDescriptorSetAllocateInfo visibilityAlloc{VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO}; visibilityAlloc.descriptorPool = descriptorPool_; - visibilityAlloc.descriptorSetCount = kSlots; + visibilityAlloc.descriptorSetCount = kFramesInFlight; visibilityAlloc.pSetLayouts = visibilityLayouts.data(); if (vkAllocateDescriptorSets(device, &visibilityAlloc, visibilitySets_.data()) != VK_SUCCESS) return false; VkDescriptorSetAllocateInfo prepareAlloc{VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO}; prepareAlloc.descriptorPool = descriptorPool_; - prepareAlloc.descriptorSetCount = kSlots; + prepareAlloc.descriptorSetCount = kFramesInFlight; prepareAlloc.pSetLayouts = prepareLayouts.data(); return vkAllocateDescriptorSets(device, &prepareAlloc, prepareSets_.data()) == VK_SUCCESS; } @@ -344,12 +344,12 @@ bool VulkanVisibilityPass::reserve(uint32_t capacity) { if (!dummy || !dummy->upload(zeros, sizeof(zeros))) return false; } - std::array, kSlots> indices; - std::array, kSlots> depthKeys; - std::array, kSlots> counts; - std::array, kSlots> indirect; - std::array, kSlots> status; - for (uint32_t slot = 0; slot < kSlots; ++slot) { + std::array, kFramesInFlight> indices; + std::array, kFramesInFlight> depthKeys; + std::array, kFramesInFlight> counts; + std::array, kFramesInFlight> indirect; + std::array, kFramesInFlight> status; + for (uint32_t slot = 0; slot < kFramesInFlight; ++slot) { indices[slot] = VulkanBuffer::deviceLocal( ctx_, indicesBytes, VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_SRC_BIT); depthKeys[slot] = VulkanBuffer::deviceLocal( @@ -380,7 +380,7 @@ bool VulkanVisibilityPass::reserve(uint32_t capacity) { } VulkanVisibilityPass::Output VulkanVisibilityPass::output(uint32_t slot) const { - if (slot >= kSlots || capacity_ == 0) return {}; + if (slot >= kFramesInFlight || capacity_ == 0) return {}; return {indices_[slot]->handle(), depthKeys_[slot]->handle(), counts_[slot]->handle(), indirect_[slot]->handle(), status_[slot]->handle()}; } @@ -436,7 +436,7 @@ void VulkanVisibilityPass::barrier(VkCommandBuffer cmd, VkPipelineStageFlags src bool VulkanVisibilityPass::encode(VkCommandBuffer cmd, uint32_t slot, const Input& input) const { const uint32_t bound = candidateBound(input); - if (cmd == VK_NULL_HANDLE || slot >= kSlots || capacity_ == 0 || + if (cmd == VK_NULL_HANDLE || slot >= kFramesInFlight || capacity_ == 0 || static_cast(input.mode) > static_cast(CandidateMode::ranges) || static_cast(input.keyBits) > static_cast(KeyBits::low16) || static_cast(input.keyOrder) > static_cast(KeyOrder::descending) || diff --git a/packages/splatkit-android/src/main/cpp/rendering/vulkan/cull/VulkanVisibilityPass.h b/packages/splatkit-android/src/main/cpp/rendering/vulkan/cull/VulkanVisibilityPass.h index a650e4f..ba800b4 100644 --- a/packages/splatkit-android/src/main/cpp/rendering/vulkan/cull/VulkanVisibilityPass.h +++ b/packages/splatkit-android/src/main/cpp/rendering/vulkan/cull/VulkanVisibilityPass.h @@ -10,6 +10,8 @@ #include "rendering/vulkan/device/VulkanBuffer.h" #include "rendering/vulkan/device/VulkanContext.h" +#include "rendering/vulkan/layout/VulkanFrameSlots.h" +#include "rendering/vulkan/layout/VulkanSortKeys.h" #include "splat/core/Result.h" namespace splatkit { @@ -51,7 +53,6 @@ class VulkanVisibilityPass { const VulkanVisibilityCapabilities& capabilities() const { return capabilities_; } enum class CandidateMode : uint32_t { prefix, indices, ranges }; - enum class KeyBits : uint32_t { full32, low16 }; enum class KeyOrder : uint32_t { ascending, descending }; struct Range { uint32_t offset, count, prefixEnd, pad; @@ -101,7 +102,6 @@ class VulkanVisibilityPass { bool encode(VkCommandBuffer cmd, uint32_t slot, const Input& input) const; Output output(uint32_t slot) const; - static constexpr uint32_t kSlots = 2; static constexpr uint32_t kWorkgroupSize = 128; static constexpr uint32_t kMaxCapacity = 3000000; static constexpr uint32_t kOverflow = 1u; @@ -130,19 +130,19 @@ class VulkanVisibilityPass { VkDescriptorSetLayout visibilitySetLayout_ = VK_NULL_HANDLE; VkDescriptorSetLayout prepareSetLayout_ = VK_NULL_HANDLE; VkDescriptorPool descriptorPool_ = VK_NULL_HANDLE; - std::array visibilitySets_{}; - std::array prepareSets_{}; + std::array visibilitySets_{}; + std::array prepareSets_{}; VkPipelineLayout visibilityLayout_ = VK_NULL_HANDLE; VkPipelineLayout prepareLayout_ = VK_NULL_HANDLE; VkPipeline visibilityPipeline_ = VK_NULL_HANDLE; VkPipeline preparePipeline_ = VK_NULL_HANDLE; std::unique_ptr dummy_; // immutable zero words for inactive input descriptors - std::array, kSlots> indices_{}; - std::array, kSlots> depthKeys_{}; - std::array, kSlots> counts_{}; - std::array, kSlots> indirect_{}; - std::array, kSlots> status_{}; + std::array, kFramesInFlight> indices_{}; + std::array, kFramesInFlight> depthKeys_{}; + std::array, kFramesInFlight> counts_{}; + std::array, kFramesInFlight> indirect_{}; + std::array, kFramesInFlight> status_{}; }; } // namespace splatkit diff --git a/packages/splatkit-android/src/main/cpp/rendering/vulkan/frame/VulkanCameraUniforms.h b/packages/splatkit-android/src/main/cpp/rendering/vulkan/frame/VulkanCameraUniforms.h index 7566f42..5e2400c 100644 --- a/packages/splatkit-android/src/main/cpp/rendering/vulkan/frame/VulkanCameraUniforms.h +++ b/packages/splatkit-android/src/main/cpp/rendering/vulkan/frame/VulkanCameraUniforms.h @@ -8,7 +8,7 @@ #include "rendering/vulkan/device/VulkanBuffer.h" #include "rendering/vulkan/device/VulkanContext.h" -#include "rendering/vulkan/presentation/VulkanFrameLoop.h" +#include "rendering/vulkan/layout/VulkanFrameSlots.h" #include "splat/core/Result.h" #include "splat/math/Mat4.h" #include "splat/math/Vec3.h" @@ -34,7 +34,7 @@ class VulkanCameraUniforms { private: VulkanCameraUniforms() = default; - std::array, VulkanFrameLoop::kFramesInFlight> uniforms_{}; + std::array, kFramesInFlight> uniforms_{}; }; } // namespace splatkit diff --git a/packages/splatkit-android/src/main/cpp/rendering/vulkan/frame/VulkanFrameCompute.cpp b/packages/splatkit-android/src/main/cpp/rendering/vulkan/frame/VulkanFrameCompute.cpp index 563639e..d94318e 100644 --- a/packages/splatkit-android/src/main/cpp/rendering/vulkan/frame/VulkanFrameCompute.cpp +++ b/packages/splatkit-android/src/main/cpp/rendering/vulkan/frame/VulkanFrameCompute.cpp @@ -79,7 +79,7 @@ bool VulkanFrameCompute::initialize(uint32_t sourceCount, const splat::LodTree* timestampBits_ = families[ctx_.queueFamily()].timestampValidBits; timestampPeriod_ = properties.limits.timestampPeriod; if (!properties.limits.timestampComputeAndGraphics) timestampBits_ = 0; - for (uint32_t slot = 0; slot < kSlots; ++slot) { + for (uint32_t slot = 0; slot < kFramesInFlight; ++slot) { ranges_[slot] = VulkanBuffer::hostVisible(ctx_, kMaxRanges * sizeof(RangeRecord), VK_BUFFER_USAGE_STORAGE_BUFFER_BIT); readback_[slot] = @@ -151,11 +151,11 @@ bool VulkanFrameCompute::prepareRanges(uint32_t slot, const SplatRenderer::Frame } void VulkanFrameCompute::submitted(uint32_t slot, uint64_t submission) { - if (slot < kSlots && pending_[slot]) submission_[slot] = submission; + if (slot < kFramesInFlight && pending_[slot]) submission_[slot] = submission; } void VulkanFrameCompute::collectCompleted(uint64_t completedSubmission) { - for (uint32_t slot = 0; slot < kSlots; ++slot) { + for (uint32_t slot = 0; slot < kFramesInFlight; ++slot) { if (pending_[slot] && submission_[slot] != 0 && submission_[slot] <= completedSubmission) collect(slot); } @@ -223,7 +223,8 @@ void VulkanFrameCompute::copyDiagnostics(VkCommandBuffer cmd, uint32_t slot, std::optional VulkanFrameCompute::encode( VkCommandBuffer cmd, uint32_t slot, VkBuffer camera, const VulkanBuffer& splats, const SplatRenderer::Frame& frame) { - if (!cmd || slot >= kSlots || !camera || frame.orderSource != SplatRenderer::OrderSource::gpu) + if (!cmd || slot >= kFramesInFlight || !camera || + frame.orderSource != SplatRenderer::OrderSource::gpu) return std::nullopt; collect(slot); VulkanVisibilityPass::Input input; @@ -231,8 +232,7 @@ std::optional VulkanFrameCompute::encode( input.splats = splats.handle(); input.splatsBytes = splats.size(); input.sourceCount = sourceCount_; - input.keyBits = sortKeyBits_ == SortKeyBits::low16 ? VulkanVisibilityPass::KeyBits::low16 - : VulkanVisibilityPass::KeyBits::full32; + input.keyBits = keyBits(); input.keyOrder = VulkanVisibilityPass::KeyOrder::descending; // Hardware uses back-to-front over. if (!lod_ && !prepareRanges(slot, frame, input)) return std::nullopt; memoryBarrier(cmd, VK_PIPELINE_STAGE_HOST_BIT | VK_PIPELINE_STAGE_TRANSFER_BIT, @@ -265,8 +265,7 @@ std::optional VulkanFrameCompute::encode( sort.count = visible.count; sort.keysBytes = sort.valuesBytes = VkDeviceSize{capacity_} * sizeof(uint32_t); sort.countBytes = sizeof(uint32_t); - sort.keyBits = sortKeyBits_ == SortKeyBits::low16 ? VulkanRadixSort::KeyBits::low16 - : VulkanRadixSort::KeyBits::full32; + sort.keyBits = keyBits(); if (!radix_->encode(cmd, slot, sort)) return std::nullopt; timestamp(3); // The sorter's checked GPU count is authoritative even if visibility succeeded. diff --git a/packages/splatkit-android/src/main/cpp/rendering/vulkan/frame/VulkanFrameCompute.h b/packages/splatkit-android/src/main/cpp/rendering/vulkan/frame/VulkanFrameCompute.h index f9c5143..4b83129 100644 --- a/packages/splatkit-android/src/main/cpp/rendering/vulkan/frame/VulkanFrameCompute.h +++ b/packages/splatkit-android/src/main/cpp/rendering/vulkan/frame/VulkanFrameCompute.h @@ -6,6 +6,8 @@ #include "rendering/vulkan/cull/VulkanVisibilityPass.h" #include "rendering/vulkan/device/VulkanBuffer.h" +#include "rendering/vulkan/layout/VulkanFrameSlots.h" +#include "rendering/vulkan/layout/VulkanSortKeys.h" #include "rendering/vulkan/selection/VulkanLodSelection.h" #include "splatkit/rendering/SplatRenderer.h" @@ -52,7 +54,7 @@ class VulkanFrameCompute { // Per-instance renderer policy. Render thread, while idle. Only sortDepth and // subpixelThreshold are honoured; the caller resolves the rest. bool applyRenderPolicy(const RenderPolicy& policy, std::string* reason); - uint32_t sortKeyBits() const { return sortKeyBits_ == SortKeyBits::low16 ? 16u : 32u; } + uint32_t sortKeyBits() const { return keyBitCount(keyBits()); } float minPixelRadius() const { return visibility_ ? visibility_->minPixelRadius() : 0.0f; } const VulkanVisibilityCapabilities& visibilityCapabilities() const { return visibility_->capabilities(); @@ -60,6 +62,9 @@ class VulkanFrameCompute { private: explicit VulkanFrameCompute(const VulkanContext& ctx); + KeyBits keyBits() const { + return sortKeyBits_ == SortKeyBits::low16 ? KeyBits::low16 : KeyBits::full32; + } bool initialize(uint32_t sourceCount, const splat::LodTree* tree, uint32_t budget); bool prepareRanges(uint32_t slot, const SplatRenderer::Frame& frame, VulkanVisibilityPass::Input& input); @@ -70,7 +75,6 @@ class VulkanFrameCompute { struct RangeRecord { uint32_t offset, count, prefixEnd, pad; }; - static constexpr uint32_t kSlots = 2; static constexpr uint32_t kMaxRanges = 65536; const VulkanContext& ctx_; uint32_t sourceCount_ = 0, capacity_ = 0; @@ -80,10 +84,11 @@ class VulkanFrameCompute { std::unique_ptr lod_; std::unique_ptr visibility_; std::unique_ptr radix_; - std::array, kSlots> ranges_, readback_; - std::array queries_{}; - std::array pending_{}; - std::array submission_{}; // of the pending encode; 0 when not submitted + std::array, kFramesInFlight> ranges_, readback_; + std::array queries_{}; + std::array pending_{}; + std::array + submission_{}; // of the pending encode; 0 when not submitted uint64_t collectedSubmission_ = 0; std::vector rangeRecords_; Stats stats_; diff --git a/packages/splatkit-android/src/main/cpp/rendering/vulkan/layout/VulkanFrameSlots.h b/packages/splatkit-android/src/main/cpp/rendering/vulkan/layout/VulkanFrameSlots.h new file mode 100644 index 0000000..1e05fb4 --- /dev/null +++ b/packages/splatkit-android/src/main/cpp/rendering/vulkan/layout/VulkanFrameSlots.h @@ -0,0 +1,11 @@ +#pragma once + +#include + +namespace splatkit { + +// Frames the CPU records while the GPU still draws earlier ones. Every per-frame slot array +// and descriptor pool in the Vulkan renderer is sized from this one count. +inline constexpr uint32_t kFramesInFlight = 2; + +} // namespace splatkit diff --git a/packages/splatkit-android/src/main/cpp/rendering/vulkan/layout/VulkanSortKeys.h b/packages/splatkit-android/src/main/cpp/rendering/vulkan/layout/VulkanSortKeys.h new file mode 100644 index 0000000..62d7452 --- /dev/null +++ b/packages/splatkit-android/src/main/cpp/rendering/vulkan/layout/VulkanSortKeys.h @@ -0,0 +1,17 @@ +#pragma once + +#include + +namespace splatkit { + +// Width of the depth keys the visibility pass writes and the radix sort orders. +enum class KeyBits : uint32_t { full32, low16 }; + +inline constexpr uint32_t kFullKeyBits = 32; +inline constexpr uint32_t kLowKeyBits = 16; + +constexpr uint32_t keyBitCount(KeyBits bits) { + return bits == KeyBits::low16 ? kLowKeyBits : kFullKeyBits; +} + +} // namespace splatkit diff --git a/packages/splatkit-android/src/main/cpp/rendering/vulkan/presentation/VulkanFrameLoop.h b/packages/splatkit-android/src/main/cpp/rendering/vulkan/presentation/VulkanFrameLoop.h index ab6c9ea..2a4fc8d 100644 --- a/packages/splatkit-android/src/main/cpp/rendering/vulkan/presentation/VulkanFrameLoop.h +++ b/packages/splatkit-android/src/main/cpp/rendering/vulkan/presentation/VulkanFrameLoop.h @@ -6,6 +6,7 @@ #include #include "rendering/vulkan/device/VulkanContext.h" +#include "rendering/vulkan/layout/VulkanFrameSlots.h" #include "rendering/vulkan/presentation/VulkanSwapchain.h" namespace splatkit { @@ -19,8 +20,6 @@ namespace splatkit { // because presentation may still be reading it when the frame slot comes around again. class VulkanFrameLoop { public: - static constexpr uint32_t kFramesInFlight = 2; - // `swapchainSuboptimal`: the frame was presented, but the surface reports the swapchain // no longer matches it. The owner decides whether that needs a rebuild. enum class Status { ok, swapchainSuboptimal, swapchainOutOfDate, error }; diff --git a/packages/splatkit-android/src/main/cpp/rendering/vulkan/raster/VulkanSplatPipeline.cpp b/packages/splatkit-android/src/main/cpp/rendering/vulkan/raster/VulkanSplatPipeline.cpp index 16ae58f..5317c15 100644 --- a/packages/splatkit-android/src/main/cpp/rendering/vulkan/raster/VulkanSplatPipeline.cpp +++ b/packages/splatkit-android/src/main/cpp/rendering/vulkan/raster/VulkanSplatPipeline.cpp @@ -62,24 +62,23 @@ bool VulkanSplatPipeline::createDescriptors(const VulkanCameraUniforms& cameras) return false; VkDescriptorPoolSize sizes[2]{}; - sizes[0] = {VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, VulkanFrameLoop::kFramesInFlight}; - sizes[1] = {VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, - kStorageBindings * VulkanFrameLoop::kFramesInFlight}; + sizes[0] = {VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, kFramesInFlight}; + sizes[1] = {VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, kStorageBindings * kFramesInFlight}; VkDescriptorPoolCreateInfo poolInfo{VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO}; - poolInfo.maxSets = VulkanFrameLoop::kFramesInFlight; + poolInfo.maxSets = kFramesInFlight; poolInfo.poolSizeCount = 2; poolInfo.pPoolSizes = sizes; if (vkCreateDescriptorPool(device, &poolInfo, nullptr, &pool_) != VK_SUCCESS) return false; - std::array layouts; + std::array layouts; layouts.fill(setLayout_); VkDescriptorSetAllocateInfo allocInfo{VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO}; allocInfo.descriptorPool = pool_; - allocInfo.descriptorSetCount = VulkanFrameLoop::kFramesInFlight; + allocInfo.descriptorSetCount = kFramesInFlight; allocInfo.pSetLayouts = layouts.data(); if (vkAllocateDescriptorSets(device, &allocInfo, sets_.data()) != VK_SUCCESS) return false; - for (uint32_t i = 0; i < VulkanFrameLoop::kFramesInFlight; ++i) { + for (uint32_t i = 0; i < kFramesInFlight; ++i) { const VkDescriptorBufferInfo info{cameras.buffer(i), 0, sizeof(CameraUniform)}; VkWriteDescriptorSet write{VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET}; write.dstSet = sets_[i]; @@ -184,7 +183,7 @@ bool VulkanSplatPipeline::createPipelines(VkRenderPass renderPass) { } void VulkanSplatPipeline::bindWorld(const VulkanWorld& world) { - for (uint32_t i = 0; i < VulkanFrameLoop::kFramesInFlight; ++i) { + for (uint32_t i = 0; i < kFramesInFlight; ++i) { const VkDescriptorBufferInfo splats{world.splats->handle(), 0, VK_WHOLE_SIZE}; const VkDescriptorBufferInfo order{world.order->handle(), 0, VK_WHOLE_SIZE}; const VkDescriptorBufferInfo sh{world.sh->handle(), 0, VK_WHOLE_SIZE}; diff --git a/packages/splatkit-android/src/main/cpp/rendering/vulkan/raster/VulkanSplatPipeline.h b/packages/splatkit-android/src/main/cpp/rendering/vulkan/raster/VulkanSplatPipeline.h index a6e9214..c05c9ca 100644 --- a/packages/splatkit-android/src/main/cpp/rendering/vulkan/raster/VulkanSplatPipeline.h +++ b/packages/splatkit-android/src/main/cpp/rendering/vulkan/raster/VulkanSplatPipeline.h @@ -8,7 +8,7 @@ #include "rendering/vulkan/device/VulkanContext.h" #include "rendering/vulkan/frame/VulkanCameraUniforms.h" -#include "rendering/vulkan/presentation/VulkanFrameLoop.h" +#include "rendering/vulkan/layout/VulkanFrameSlots.h" #include "rendering/vulkan/world/VulkanWorld.h" #include "splat/core/Result.h" @@ -51,7 +51,7 @@ class VulkanSplatPipeline { const VulkanContext& ctx_; VkDescriptorSetLayout setLayout_ = VK_NULL_HANDLE; VkDescriptorPool pool_ = VK_NULL_HANDLE; - std::array sets_{}; + std::array sets_{}; VkPipelineLayout layout_ = VK_NULL_HANDLE; // One pipeline per SH degree, specialised so degree 0 worlds pay nothing for SH. std::array pipelines_{}; diff --git a/packages/splatkit-android/src/main/cpp/rendering/vulkan/selection/VulkanLodSelection.cpp b/packages/splatkit-android/src/main/cpp/rendering/vulkan/selection/VulkanLodSelection.cpp index 5fc0064..ae44fda 100644 --- a/packages/splatkit-android/src/main/cpp/rendering/vulkan/selection/VulkanLodSelection.cpp +++ b/packages/splatkit-android/src/main/cpp/rendering/vulkan/selection/VulkanLodSelection.cpp @@ -79,18 +79,19 @@ bool VulkanLodSelection::initialize() { set.pBindings = bindings; if (vkCreateDescriptorSetLayout(ctx_.device(), &set, nullptr, &setLayout_) != VK_SUCCESS) return false; - VkDescriptorPoolSize sizes[] = {{VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, kSlots}, - {VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, kStorageBindings * kSlots}}; + VkDescriptorPoolSize sizes[] = { + {VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, kFramesInFlight}, + {VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, kStorageBindings * kFramesInFlight}}; VkDescriptorPoolCreateInfo pool{VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO}; - pool.maxSets = kSlots; + pool.maxSets = kFramesInFlight; pool.poolSizeCount = 2; pool.pPoolSizes = sizes; if (vkCreateDescriptorPool(ctx_.device(), &pool, nullptr, &pool_) != VK_SUCCESS) return false; - std::array layouts{}; + std::array layouts{}; layouts.fill(setLayout_); VkDescriptorSetAllocateInfo allocate{VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO}; allocate.descriptorPool = pool_; - allocate.descriptorSetCount = kSlots; + allocate.descriptorSetCount = kFramesInFlight; allocate.pSetLayouts = layouts.data(); if (vkAllocateDescriptorSets(ctx_.device(), &allocate, sets_.data()) != VK_SUCCESS) return false; const VkPushConstantRange push{VK_SHADER_STAGE_COMPUTE_BIT, 0, sizeof(Config)}; @@ -193,7 +194,7 @@ VulkanLodSelection::Output VulkanLodSelection::output() const { } bool VulkanLodSelection::encode(VkCommandBuffer cmd, uint32_t slot, const Input& input) const { - if (!cmd || slot >= kSlots || !scratch_ || !input.camera || + if (!cmd || slot >= kFramesInFlight || !scratch_ || !input.camera || input.cameraOffset % limits_.minUniformBufferOffsetAlignment != 0 || input.cameraOffset > std::numeric_limits::max() - sizeof(CameraUniform)) return false; diff --git a/packages/splatkit-android/src/main/cpp/rendering/vulkan/selection/VulkanLodSelection.h b/packages/splatkit-android/src/main/cpp/rendering/vulkan/selection/VulkanLodSelection.h index 1e6660c..5ec5a6c 100644 --- a/packages/splatkit-android/src/main/cpp/rendering/vulkan/selection/VulkanLodSelection.h +++ b/packages/splatkit-android/src/main/cpp/rendering/vulkan/selection/VulkanLodSelection.h @@ -5,6 +5,7 @@ #include #include "rendering/vulkan/device/VulkanBuffer.h" +#include "rendering/vulkan/layout/VulkanFrameSlots.h" #include "splat/lod/LodTree.h" namespace splatkit { @@ -32,7 +33,6 @@ class VulkanLodSelection { VkBuffer state = VK_NULL_HANDLE; // count/diagnostics at constants below, TRANSFER_SRC capable uint32_t capacity = 0; }; - static constexpr uint32_t kSlots = 2; static constexpr uint32_t kMaxBudget = 2200000; static constexpr VkDeviceSize kCountOffset = 0; static constexpr VkDeviceSize kLimitedOffset = 16; @@ -73,7 +73,7 @@ class VulkanLodSelection { uint32_t rounds_ = 0; VkDescriptorSetLayout setLayout_ = VK_NULL_HANDLE; VkDescriptorPool pool_ = VK_NULL_HANDLE; - std::array sets_{}; + std::array sets_{}; VkPipelineLayout layout_ = VK_NULL_HANDLE; VkPipeline pipeline_ = VK_NULL_HANDLE; std::unique_ptr clusters_, leaves_, scratch_, indices_; diff --git a/packages/splatkit-android/src/main/cpp/rendering/vulkan/sort/VulkanRadixSort.cpp b/packages/splatkit-android/src/main/cpp/rendering/vulkan/sort/VulkanRadixSort.cpp index 2c32e99..8b0659a 100644 --- a/packages/splatkit-android/src/main/cpp/rendering/vulkan/sort/VulkanRadixSort.cpp +++ b/packages/splatkit-android/src/main/cpp/rendering/vulkan/sort/VulkanRadixSort.cpp @@ -128,9 +128,10 @@ bool VulkanRadixSort::initialize() { setInfo.pBindings = bindings.data(); if (vkCreateDescriptorSetLayout(device, &setInfo, nullptr, &setLayout_) != VK_SUCCESS) return false; - const VkDescriptorPoolSize size{VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, kBindings * kSlots * kSets}; + const VkDescriptorPoolSize size{VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, + kBindings * kFramesInFlight * kSets}; VkDescriptorPoolCreateInfo poolInfo{VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO}; - poolInfo.maxSets = kSlots * kSets; + poolInfo.maxSets = kFramesInFlight * kSets; poolInfo.poolSizeCount = 1; poolInfo.pPoolSizes = &size; if (vkCreateDescriptorPool(device, &poolInfo, nullptr, &pool_) != VK_SUCCESS) return false; @@ -183,7 +184,7 @@ bool VulkanRadixSort::reserve(uint32_t capacity) { uint64_t{blocks} > uint64_t{limits.maxComputeWorkGroupCount[0]} * limits.maxComputeWorkGroupCount[1]) return false; - std::array next; + std::array next; for (auto& slot : next) { for (auto& keys : slot.keys) keys = VulkanBuffer::deviceLocal(ctx_, pairBytes, kStorage); for (auto& values : slot.values) values = VulkanBuffer::deviceLocal(ctx_, pairBytes, kStorage); @@ -203,7 +204,7 @@ bool VulkanRadixSort::reserve(uint32_t capacity) { } VulkanRadixSort::Output VulkanRadixSort::output(uint32_t slot) const { - if (slot >= kSlots || !capacity_) return {}; + if (slot >= kFramesInFlight || !capacity_) return {}; const auto& s = slots_[slot]; return {s.keys[0]->handle(), s.values[0]->handle(), s.count->handle(), s.status->handle()}; } @@ -278,7 +279,7 @@ void VulkanRadixSort::recordSort(VkCommandBuffer cmd, uint32_t slot, KeyBits key cmd, kCompute, VK_ACCESS_SHADER_WRITE_BIT, kCompute | VK_PIPELINE_STAGE_DRAW_INDIRECT_BIT, VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT | VK_ACCESS_INDIRECT_COMMAND_READ_BIT); const VkBuffer state = slots_[slot].state->handle(); - const uint32_t passes = (keyBits == KeyBits::low16 ? 16 : 32) / kDigitBits; + const uint32_t passes = keyBitCount(keyBits) / kDigitBits; for (uint32_t pass = 0; pass < passes; ++pass) { push.shift = pass * kDigitBits; const VkDescriptorSet set = sets_[slot][passSet(pass)]; @@ -299,7 +300,7 @@ void VulkanRadixSort::recordSort(VkCommandBuffer cmd, uint32_t slot, KeyBits key } bool VulkanRadixSort::encode(VkCommandBuffer cmd, uint32_t slot, const Input& input) const { - if (!cmd || slot >= kSlots || !capacity_ || + if (!cmd || slot >= kFramesInFlight || !capacity_ || (input.keyBits != KeyBits::full32 && input.keyBits != KeyBits::low16)) return false; if (!acceptsInput(input)) return false; diff --git a/packages/splatkit-android/src/main/cpp/rendering/vulkan/sort/VulkanRadixSort.h b/packages/splatkit-android/src/main/cpp/rendering/vulkan/sort/VulkanRadixSort.h index ddb9507..2315bdb 100644 --- a/packages/splatkit-android/src/main/cpp/rendering/vulkan/sort/VulkanRadixSort.h +++ b/packages/splatkit-android/src/main/cpp/rendering/vulkan/sort/VulkanRadixSort.h @@ -5,6 +5,8 @@ #include #include "rendering/vulkan/device/VulkanBuffer.h" +#include "rendering/vulkan/layout/VulkanFrameSlots.h" +#include "rendering/vulkan/layout/VulkanSortKeys.h" #include "splat/core/Result.h" namespace splatkit { @@ -17,7 +19,6 @@ namespace splatkit { // VkBuffer handles must not alias the same memory. External offsets/sizes must be truthful. class VulkanRadixSort { public: - enum class KeyBits : uint32_t { full32, low16 }; struct Capabilities { bool supported = false; uint32_t subgroupSize = 0; @@ -35,7 +36,7 @@ class VulkanRadixSort { VkBuffer keys = VK_NULL_HANDLE, values = VK_NULL_HANDLE; VkBuffer count = VK_NULL_HANDLE, status = VK_NULL_HANDLE; }; - static constexpr uint32_t kSlots = 2, kBlock = 2048, kMaxCapacity = 3000000; + static constexpr uint32_t kBlock = 2048, kMaxCapacity = 3000000; static constexpr uint32_t kInvalidCount = 1; static Capabilities queryCapabilities(const VulkanContext& ctx); static splat::Result> create(const VulkanContext& ctx); @@ -71,7 +72,7 @@ class VulkanRadixSort { void recordSort(VkCommandBuffer cmd, uint32_t slot, KeyBits keyBits) const; const VulkanContext& ctx_; uint32_t capacity_ = 0; - std::array slots_; + std::array slots_; VkDescriptorSetLayout setLayout_ = VK_NULL_HANDLE; VkDescriptorPool pool_ = VK_NULL_HANDLE; VkPipelineLayout layout_ = VK_NULL_HANDLE; @@ -79,6 +80,6 @@ class VulkanRadixSort { std::array pipelines_{}; // First pass external->B; second B->A; subsequent A->B and B->A. static constexpr uint32_t kSets = 3; - std::array, kSlots> sets_{}; + std::array, kFramesInFlight> sets_{}; }; } // namespace splatkit diff --git a/packages/splatkit-android/src/main/cpp/rendering/vulkan/world/VulkanWorld.h b/packages/splatkit-android/src/main/cpp/rendering/vulkan/world/VulkanWorld.h index 68bb4ab..ee252e1 100644 --- a/packages/splatkit-android/src/main/cpp/rendering/vulkan/world/VulkanWorld.h +++ b/packages/splatkit-android/src/main/cpp/rendering/vulkan/world/VulkanWorld.h @@ -8,7 +8,7 @@ #include "rendering/vulkan/device/VulkanBuffer.h" #include "rendering/vulkan/device/VulkanContext.h" -#include "rendering/vulkan/presentation/VulkanFrameLoop.h" +#include "rendering/vulkan/layout/VulkanFrameSlots.h" #include "splat/formats/SplatCloud.h" namespace splatkit { @@ -47,7 +47,7 @@ class VulkanWorld { int shDegree = 0; // One host visible staging buffer per frame slot: the slot's fence guarantees the GPU // finished reading it before the CPU writes the next order into it. - std::array, VulkanFrameLoop::kFramesInFlight> orderStaging; + std::array, kFramesInFlight> orderStaging; uint32_t count = 0; private: diff --git a/packages/splatkit-android/tests/rendering/vulkan/cull/VulkanVisibilityPassTest.cpp b/packages/splatkit-android/tests/rendering/vulkan/cull/VulkanVisibilityPassTest.cpp index f94febb..41f45c9 100644 --- a/packages/splatkit-android/tests/rendering/vulkan/cull/VulkanVisibilityPassTest.cpp +++ b/packages/splatkit-android/tests/rendering/vulkan/cull/VulkanVisibilityPassTest.cpp @@ -14,6 +14,8 @@ namespace { using splatkit::CameraUniform; using splatkit::GpuSplat; +using splatkit::KeyBits; +using splatkit::kFramesInFlight; using splatkit::packSplats; using splatkit::VulkanBuffer; using splatkit::VulkanVisibilityPass; @@ -182,7 +184,7 @@ void testSparseIndexed(const Gpu& gpu, const VulkanVisibilityPass& pass, const F auto invalidProjection = camera(); invalidProjection.proj = splat::Mat4::identity(); f.setCamera(invalidProjection); - sparse.keyBits = VulkanVisibilityPass::KeyBits::low16; + sparse.keyBits = KeyBits::low16; failed(run(gpu, pass, sparse), VulkanVisibilityPass::kInvalidProjection); f.setCamera(camera()); std::puts("PASS sparse indexed workgroups, poisoned tails and empty diagnostics"); @@ -246,12 +248,11 @@ void testOverflow(const Gpu& gpu, const VulkanVisibilityPass& pass, const Fixtur std::puts("PASS output overflow fails closed and recovers"); } -uint32_t expectedDepthKey(float depth, VulkanVisibilityPass::KeyBits bits, - VulkanVisibilityPass::KeyOrder order) { +uint32_t expectedDepthKey(float depth, KeyBits bits, VulkanVisibilityPass::KeyOrder order) { const auto cam = camera(); const float nearPlane = cam.proj.at(2, 3) / cam.proj.at(2, 2); const float farPlane = cam.proj.at(2, 3) / (cam.proj.at(2, 2) + 1); - const bool low16 = bits == VulkanVisibilityPass::KeyBits::low16; + const bool low16 = bits == KeyBits::low16; uint32_t expected = 0; std::memcpy(&expected, &depth, 4); if (low16) @@ -262,12 +263,11 @@ uint32_t expectedDepthKey(float depth, VulkanVisibilityPass::KeyBits bits, return expected; } -void requireDepthKeys(const Result& result, VulkanVisibilityPass::KeyBits bits, - VulkanVisibilityPass::KeyOrder order) { +void requireDepthKeys(const Result& result, KeyBits bits, VulkanVisibilityPass::KeyOrder order) { for (size_t i = 0; i < result.count; ++i) { const auto depth = static_cast(1 + result.indices[i] % 10); const uint32_t expected = expectedDepthKey(depth, bits, order); - if (bits == VulkanVisibilityPass::KeyBits::full32) + if (bits == KeyBits::full32) require(result.keys[i] == expected, "32-bit depth key preserves float bits and BTF inversion"); else @@ -280,7 +280,7 @@ void requireDepthKeys(const Result& result, VulkanVisibilityPass::KeyBits bits, void testDepthKeys(const Gpu& gpu, const VulkanVisibilityPass& pass, const Fixture& f) { auto in = f.input(); in.candidateCapacity = 3; - for (auto bits : {VulkanVisibilityPass::KeyBits::full32, VulkanVisibilityPass::KeyBits::low16}) { + for (auto bits : {KeyBits::full32, KeyBits::low16}) { for (auto order : {VulkanVisibilityPass::KeyOrder::ascending, VulkanVisibilityPass::KeyOrder::descending}) { in.keyBits = bits; @@ -293,7 +293,7 @@ void testDepthKeys(const Gpu& gpu, const VulkanVisibilityPass& pass, const Fixtu auto badCamera = camera(); badCamera.proj = splat::Mat4::identity(); f.setCamera(badCamera); - in.keyBits = VulkanVisibilityPass::KeyBits::low16; + in.keyBits = KeyBits::low16; failed(run(gpu, pass, in), VulkanVisibilityPass::kInvalidProjection); f.setCamera(camera()); std::puts("PASS full32/low16 ascending/BTF keys and invalid projection"); @@ -330,7 +330,7 @@ void testDescriptorRejection(const Gpu& gpu, VulkanVisibilityPass& pass, const F bad.candidatesBytes = indexBuffer->size(); bad.candidateCountBytes = 3; require(!pass.encode(cmd, 0, bad), "reject undersized count descriptor"); - require(!pass.encode(cmd, VulkanVisibilityPass::kSlots, in), "reject invalid slot"); + require(!pass.encode(cmd, kFramesInFlight, in), "reject invalid slot"); require(!pass.encode(VK_NULL_HANDLE, 0, in), "reject null command"); }); membership(run(gpu, pass, in), {0, 1, 2}); diff --git a/packages/splatkit-android/tests/rendering/vulkan/selection/VulkanLodSelectionTest.cpp b/packages/splatkit-android/tests/rendering/vulkan/selection/VulkanLodSelectionTest.cpp index 34ff527..6cfa060 100644 --- a/packages/splatkit-android/tests/rendering/vulkan/selection/VulkanLodSelectionTest.cpp +++ b/packages/splatkit-android/tests/rendering/vulkan/selection/VulkanLodSelectionTest.cpp @@ -13,6 +13,7 @@ namespace { using splatkit::CameraUniform; +using splatkit::kFramesInFlight; using splatkit::VulkanBuffer; using splatkit::VulkanLodSelection; namespace test = splatkit::test; @@ -171,7 +172,7 @@ void tests(const test::VulkanTestContext& gpu) { require(select(gpu, *lod, camera()).cut == denied.cut, "old world survives failed upload"); require(!lod->encode(VK_NULL_HANDLE, 0, {}), "reject null command"); gpu.submit([&](VkCommandBuffer cmd) { - require(!lod->encode(cmd, VulkanLodSelection::kSlots, {}), "reject invalid slot"); + require(!lod->encode(cmd, kFramesInFlight, {}), "reject invalid slot"); require(!lod->encode(cmd, 0, {}), "reject null camera"); }); std::puts("PASS invalid inputs and transactional upload"); diff --git a/packages/splatkit-android/tests/rendering/vulkan/sort/VulkanRadixSortTest.cpp b/packages/splatkit-android/tests/rendering/vulkan/sort/VulkanRadixSortTest.cpp index 1536b65..a6d2ac9 100644 --- a/packages/splatkit-android/tests/rendering/vulkan/sort/VulkanRadixSortTest.cpp +++ b/packages/splatkit-android/tests/rendering/vulkan/sort/VulkanRadixSortTest.cpp @@ -9,6 +9,8 @@ #include "rendering/vulkan/sort/VulkanRadixSort.h" #include "support/VulkanTestContext.h" +using splatkit::KeyBits; +using splatkit::kFramesInFlight; using splatkit::VulkanBuffer; using splatkit::VulkanRadixSort; using splatkit::test::require; @@ -90,7 +92,7 @@ void requireInvalidInputsRejected(VulkanTestContext& gpu, const VulkanRadixSort& const VulkanRadixSort::Input& input) { require(!sort.encode(VK_NULL_HANDLE, 0, input), "reject null command buffer"); gpu.submit([&](VkCommandBuffer cmd) { - require(!sort.encode(cmd, VulkanRadixSort::kSlots, input), "reject invalid slot"); + require(!sort.encode(cmd, kFramesInFlight, input), "reject invalid slot"); auto bad = input; bad.keysBytes = input.keysBytes - 1; require(!sort.encode(cmd, 0, bad), "reject undersized input"); @@ -100,7 +102,7 @@ void requireInvalidInputsRejected(VulkanTestContext& gpu, const VulkanRadixSort& bad = input; // Fixed uint32_t underlying type permits this value; exercise enum validation. // NOLINTNEXTLINE(clang-analyzer-optin.core.EnumCastOutOfRange) - bad.keyBits = static_cast(9); + bad.keyBits = static_cast(9); require(!sort.encode(cmd, 0, bad), "reject key mode"); bad = input; bad.keys = sort.output(1).keys; @@ -156,7 +158,7 @@ void requireSortedOutput(VulkanTestContext& gpu, const VulkanRadixSort::Output& void runCase(VulkanTestContext& gpu, const VulkanRadixSort& sort, const InputBuffers& buffers, const VulkanRadixSort::Input& input, uint32_t n, uint32_t pattern, std::mt19937& random, uint32_t& cases) { - const bool low16 = input.keyBits == VulkanRadixSort::KeyBits::low16; + const bool low16 = input.keyBits == KeyBits::low16; std::printf("radix case=%u bits=%u n=%u pattern=%u stage=upload\n", cases, low16 ? 16u : 32u, n, pattern); const auto inKeys = makeKeys(n, pattern, low16, random); @@ -170,7 +172,7 @@ void runCase(VulkanTestContext& gpu, const VulkanRadixSort& sort, const InputBuf const uint32_t mask = low16 ? 65535u : UINT32_MAX; std::stable_sort(expected.begin(), expected.end(), [&](uint32_t a, uint32_t b) { return (inKeys[a] & mask) < (inKeys[b] & mask); }); - const uint32_t slot = cases++ % VulkanRadixSort::kSlots; + const uint32_t slot = cases++ % kFramesInFlight; std::puts("radix stage=encode-submit-wait"); gpu.submit([&](VkCommandBuffer cmd) { require(sort.encode(cmd, slot, input), "encode radix"); }); requireSortedOutput(gpu, sort.output(slot), n, inKeys, expected); @@ -181,7 +183,7 @@ uint32_t runAllCases(VulkanTestContext& gpu, const VulkanRadixSort& sort, const std::vector& sizes) { std::mt19937 random(0x51a7u); uint32_t cases = 0; - for (const auto bits : {VulkanRadixSort::KeyBits::full32, VulkanRadixSort::KeyBits::low16}) { + for (const auto bits : {KeyBits::full32, KeyBits::low16}) { input.keyBits = bits; for (const uint32_t n : sizes) for (uint32_t pattern = 0; pattern < 4; ++pattern) diff --git a/packages/splatkit-android/tests/rendering/vulkan/world/VulkanWorldTest.cpp b/packages/splatkit-android/tests/rendering/vulkan/world/VulkanWorldTest.cpp index d8e0507..20bfd1a 100644 --- a/packages/splatkit-android/tests/rendering/vulkan/world/VulkanWorldTest.cpp +++ b/packages/splatkit-android/tests/rendering/vulkan/world/VulkanWorldTest.cpp @@ -7,8 +7,8 @@ #include "support/VulkanTestContext.h" namespace { +using splatkit::kFramesInFlight; using splatkit::shStride; -using splatkit::VulkanFrameLoop; using splatkit::VulkanWorld; namespace test = splatkit::test; using test::require; @@ -119,7 +119,7 @@ int main() { test::VulkanTestContext gpu; std::printf("GPU: %s; validation: %s\n", gpu.context->deviceDescription().c_str(), gpu.context->validationEnabled() ? "on" : "off"); - static_assert(VulkanFrameLoop::kFramesInFlight >= 2, "the order test uses slots 0 and 1"); + static_assert(kFramesInFlight >= 2, "the order test uses slots 0 and 1"); uploadShape(gpu); std::puts("PASS world upload shape"); uploadShDegree(gpu); From 4f48b5de000d99c2818b617bd448fa7f73b30e99 Mon Sep 17 00:00:00 2001 From: Xget7 Date: Tue, 20 Oct 2026 01:16:27 -0300 Subject: [PATCH 2/4] Ask the GPU frame job whether the device can run it VulkanSplatRenderer decided selectsLodOnGpu() by querying the visibility pass and the radix sort itself, so the renderer root knew which passes make up the GPU frame. VulkanFrameCompute::supported(ctx) now answers that, and the renderer no longer includes the sort header. No behaviour change. --- .../src/main/cpp/rendering/vulkan/VulkanSplatRenderer.cpp | 4 +--- .../main/cpp/rendering/vulkan/frame/VulkanFrameCompute.cpp | 5 +++++ .../src/main/cpp/rendering/vulkan/frame/VulkanFrameCompute.h | 2 ++ 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/packages/splatkit-android/src/main/cpp/rendering/vulkan/VulkanSplatRenderer.cpp b/packages/splatkit-android/src/main/cpp/rendering/vulkan/VulkanSplatRenderer.cpp index ea2d948..acac837 100644 --- a/packages/splatkit-android/src/main/cpp/rendering/vulkan/VulkanSplatRenderer.cpp +++ b/packages/splatkit-android/src/main/cpp/rendering/vulkan/VulkanSplatRenderer.cpp @@ -4,7 +4,6 @@ #include #include "engine/TraceSection.h" -#include "rendering/vulkan/sort/VulkanRadixSort.h" #include "splatkit/Log.h" @@ -140,8 +139,7 @@ bool VulkanSplatRenderer::uploadWorld(const splat::SplatCloud& cloud, int maxShD } bool VulkanSplatRenderer::sortsOnGpu() const { - return VulkanVisibilityPass::queryCapabilities(ctx_).supported && - VulkanRadixSort::queryCapabilities(ctx_).supported; + return VulkanFrameCompute::supported(ctx_); } bool VulkanSplatRenderer::selectsLodOnGpu() const { diff --git a/packages/splatkit-android/src/main/cpp/rendering/vulkan/frame/VulkanFrameCompute.cpp b/packages/splatkit-android/src/main/cpp/rendering/vulkan/frame/VulkanFrameCompute.cpp index d94318e..7c85c14 100644 --- a/packages/splatkit-android/src/main/cpp/rendering/vulkan/frame/VulkanFrameCompute.cpp +++ b/packages/splatkit-android/src/main/cpp/rendering/vulkan/frame/VulkanFrameCompute.cpp @@ -40,6 +40,11 @@ static_assert(VulkanLodSelection::kEvaluatedOffset == VulkanFrameCompute::VulkanFrameCompute(const VulkanContext& ctx) : ctx_(ctx) {} +bool VulkanFrameCompute::supported(const VulkanContext& ctx) { + return VulkanVisibilityPass::queryCapabilities(ctx).supported && + VulkanRadixSort::queryCapabilities(ctx).supported; +} + splat::Result> VulkanFrameCompute::create( const VulkanContext& ctx, uint32_t sourceCount, const splat::LodTree* tree, uint32_t budget) { std::unique_ptr pass(new VulkanFrameCompute(ctx)); diff --git a/packages/splatkit-android/src/main/cpp/rendering/vulkan/frame/VulkanFrameCompute.h b/packages/splatkit-android/src/main/cpp/rendering/vulkan/frame/VulkanFrameCompute.h index 4b83129..ec4781f 100644 --- a/packages/splatkit-android/src/main/cpp/rendering/vulkan/frame/VulkanFrameCompute.h +++ b/packages/splatkit-android/src/main/cpp/rendering/vulkan/frame/VulkanFrameCompute.h @@ -24,6 +24,8 @@ class VulkanFrameCompute { public: // Most splats one frame can keep, which bounds the visibility output and the sort. static constexpr uint32_t kMaxVisible = VulkanVisibilityPass::kMaxCapacity; + // True when the device can run the GPU visibility and sort path. + static bool supported(const VulkanContext& ctx); static splat::Result> create( const VulkanContext& ctx, uint32_t sourceCount, const splat::LodTree* tree = nullptr, uint32_t budget = VulkanLodSelection::kMaxBudget); From 2f92e4cfd3ee5f9b216fb4c8a288e22ed32db805 Mon Sep 17 00:00:00 2001 From: Xget7 Date: Tue, 20 Oct 2026 01:16:33 -0300 Subject: [PATCH 3/4] Adopt a new world through one helper in VulkanSplatRenderer uploadWorld, uploadLodWorld and createSlab each repeated the same five steps once the new world and frame compute were ready: store both, apply the stored policy, bind the world to the pipeline and reset the world frame completion. adoptWorld does them in that order. The waitIdle calls stay in the callers exactly where they were, including uploadLodWorld having none after the upload. No behaviour change. --- .../rendering/vulkan/VulkanSplatRenderer.cpp | 28 +++++++++---------- .../rendering/vulkan/VulkanSplatRenderer.h | 1 + 2 files changed, 14 insertions(+), 15 deletions(-) diff --git a/packages/splatkit-android/src/main/cpp/rendering/vulkan/VulkanSplatRenderer.cpp b/packages/splatkit-android/src/main/cpp/rendering/vulkan/VulkanSplatRenderer.cpp index acac837..4c44216 100644 --- a/packages/splatkit-android/src/main/cpp/rendering/vulkan/VulkanSplatRenderer.cpp +++ b/packages/splatkit-android/src/main/cpp/rendering/vulkan/VulkanSplatRenderer.cpp @@ -117,6 +117,16 @@ std::optional VulkanSplatRenderer::world() const { return GpuWorldInfo{world_->count, world_->shDegree}; } +// Callers have waited for the GPU to finish with the world being replaced. +void VulkanSplatRenderer::adoptWorld(std::unique_ptr world, + std::unique_ptr compute) { + world_ = std::move(world); + compute_ = std::move(compute); + applyPolicyToCompute(); + splats_->bindWorld(*world_); + worldFrameCompletion_.reset(); +} + bool VulkanSplatRenderer::uploadWorld(const splat::SplatCloud& cloud, int maxShDegree) { const TraceSection section(trace::kUploadWorld); if (!splats_ || cloud.count() > std::numeric_limits::max()) return false; @@ -130,11 +140,7 @@ bool VulkanSplatRenderer::uploadWorld(const splat::SplatCloud& cloud, int maxShD auto world = VulkanWorld::upload(ctx_, cloud, maxShDegree, false); if (!world) return false; ctx_.waitIdle(); // the previous world may still be in flight - world_ = std::move(world); - compute_ = std::move(compute.value()); - applyPolicyToCompute(); - splats_->bindWorld(*world_); - worldFrameCompletion_.reset(); + adoptWorld(std::move(world), std::move(compute.value())); return true; } @@ -159,11 +165,7 @@ bool VulkanSplatRenderer::uploadLodWorld(const splat::LodTree& tree, int maxShDe } auto world = VulkanWorld::upload(ctx_, tree.nodes, maxShDegree, false); if (!world) return false; - world_ = std::move(world); - compute_ = std::move(compute.value()); - applyPolicyToCompute(); - splats_->bindWorld(*world_); - worldFrameCompletion_.reset(); + adoptWorld(std::move(world), std::move(compute.value())); return true; } @@ -179,11 +181,7 @@ bool VulkanSplatRenderer::createSlab(uint32_t capacity, int shDegree) { auto world = VulkanWorld::createSlab(ctx_, capacity, shDegree, false); if (!world) return false; ctx_.waitIdle(); // the previous world may still be in flight - world_ = std::move(world); - compute_ = std::move(compute.value()); - applyPolicyToCompute(); - splats_->bindWorld(*world_); - worldFrameCompletion_.reset(); + adoptWorld(std::move(world), std::move(compute.value())); return true; } diff --git a/packages/splatkit-android/src/main/cpp/rendering/vulkan/VulkanSplatRenderer.h b/packages/splatkit-android/src/main/cpp/rendering/vulkan/VulkanSplatRenderer.h index 2141866..14330c1 100644 --- a/packages/splatkit-android/src/main/cpp/rendering/vulkan/VulkanSplatRenderer.h +++ b/packages/splatkit-android/src/main/cpp/rendering/vulkan/VulkanSplatRenderer.h @@ -85,6 +85,7 @@ class VulkanSplatRenderer final : public SplatRenderer { void rebuildPipelinesIfNeeded(); void rebuildSurface(VulkanFrameLoop::Status status); void applyPolicyToCompute(); + void adoptWorld(std::unique_ptr world, std::unique_ptr compute); VulkanContext& ctx_; VulkanFrameLoop& frameLoop_; From 8e11c53d4d4bf001911188ef50914485d55f7a78 Mon Sep 17 00:00:00 2001 From: Xget7 Date: Tue, 20 Oct 2026 01:16:43 -0300 Subject: [PATCH 4/4] Give the shared Vulkan helpers the Vulkan prefix their files carry ShaderModule is now VulkanShaderModule, matching device/VulkanShaderModule.h and the other device/ classes. The free function memoryBarrier in device/VulkanBarriers.h is now vulkanMemoryBarrier, so it reads as the Vulkan helper it is at its call sites in the passes. No behaviour change. --- .../vulkan/cull/VulkanVisibilityPass.cpp | 8 ++--- .../rendering/vulkan/device/VulkanBarriers.h | 6 ++-- .../vulkan/device/VulkanShaderModule.h | 10 +++---- .../vulkan/frame/VulkanFrameCompute.cpp | 30 +++++++++---------- .../vulkan/raster/VulkanSplatPipeline.cpp | 4 +-- .../vulkan/selection/VulkanLodSelection.cpp | 24 +++++++-------- .../rendering/vulkan/sort/VulkanRadixSort.cpp | 22 +++++++------- 7 files changed, 53 insertions(+), 51 deletions(-) diff --git a/packages/splatkit-android/src/main/cpp/rendering/vulkan/cull/VulkanVisibilityPass.cpp b/packages/splatkit-android/src/main/cpp/rendering/vulkan/cull/VulkanVisibilityPass.cpp index d4282f9..5a946c9 100644 --- a/packages/splatkit-android/src/main/cpp/rendering/vulkan/cull/VulkanVisibilityPass.cpp +++ b/packages/splatkit-android/src/main/cpp/rendering/vulkan/cull/VulkanVisibilityPass.cpp @@ -299,10 +299,10 @@ bool VulkanVisibilityPass::createPipelines() { if (vkCreatePipelineLayout(device, &prepareLayoutInfo, nullptr, &prepareLayout_) != VK_SUCCESS) return false; - const ShaderModule visibilityModule(device, shaders::visibility_comp, - shaders::visibility_comp_size); - const ShaderModule prepareModule(device, shaders::prepare_indirect_comp, - shaders::prepare_indirect_comp_size); + const VulkanShaderModule visibilityModule(device, shaders::visibility_comp, + shaders::visibility_comp_size); + const VulkanShaderModule prepareModule(device, shaders::prepare_indirect_comp, + shaders::prepare_indirect_comp_size); if (!visibilityModule || !prepareModule) return false; VkPipelineShaderStageCreateInfo visibilityStage{ VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO}; diff --git a/packages/splatkit-android/src/main/cpp/rendering/vulkan/device/VulkanBarriers.h b/packages/splatkit-android/src/main/cpp/rendering/vulkan/device/VulkanBarriers.h index 442797f..70b88de 100644 --- a/packages/splatkit-android/src/main/cpp/rendering/vulkan/device/VulkanBarriers.h +++ b/packages/splatkit-android/src/main/cpp/rendering/vulkan/device/VulkanBarriers.h @@ -5,9 +5,9 @@ namespace splatkit { // One global memory barrier; the passes order whole buffers, never single ranges. -inline void memoryBarrier(VkCommandBuffer cmd, VkPipelineStageFlags srcStage, - VkAccessFlags srcAccess, VkPipelineStageFlags dstStage, - VkAccessFlags dstAccess) { +inline void vulkanMemoryBarrier(VkCommandBuffer cmd, VkPipelineStageFlags srcStage, + VkAccessFlags srcAccess, VkPipelineStageFlags dstStage, + VkAccessFlags dstAccess) { VkMemoryBarrier barrier{VK_STRUCTURE_TYPE_MEMORY_BARRIER}; barrier.srcAccessMask = srcAccess; barrier.dstAccessMask = dstAccess; diff --git a/packages/splatkit-android/src/main/cpp/rendering/vulkan/device/VulkanShaderModule.h b/packages/splatkit-android/src/main/cpp/rendering/vulkan/device/VulkanShaderModule.h index 79bb4a1..7d03b20 100644 --- a/packages/splatkit-android/src/main/cpp/rendering/vulkan/device/VulkanShaderModule.h +++ b/packages/splatkit-android/src/main/cpp/rendering/vulkan/device/VulkanShaderModule.h @@ -9,20 +9,20 @@ namespace splatkit { // A shader module that lives for one pipeline build and is destroyed on every exit from it. // `size` is in bytes, as the generated shaders::*_size constants are. False on failure. -class ShaderModule { +class VulkanShaderModule { public: - ShaderModule(VkDevice device, const uint32_t* code, size_t size) : device_(device) { + VulkanShaderModule(VkDevice device, const uint32_t* code, size_t size) : device_(device) { VkShaderModuleCreateInfo info{VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO}; info.codeSize = size; info.pCode = code; if (vkCreateShaderModule(device, &info, nullptr, &module_) != VK_SUCCESS) module_ = VK_NULL_HANDLE; } - ~ShaderModule() { + ~VulkanShaderModule() { if (module_ != VK_NULL_HANDLE) vkDestroyShaderModule(device_, module_, nullptr); } - ShaderModule(const ShaderModule&) = delete; - ShaderModule& operator=(const ShaderModule&) = delete; + VulkanShaderModule(const VulkanShaderModule&) = delete; + VulkanShaderModule& operator=(const VulkanShaderModule&) = delete; explicit operator bool() const { return module_ != VK_NULL_HANDLE; } VkShaderModule get() const { return module_; } diff --git a/packages/splatkit-android/src/main/cpp/rendering/vulkan/frame/VulkanFrameCompute.cpp b/packages/splatkit-android/src/main/cpp/rendering/vulkan/frame/VulkanFrameCompute.cpp index 7c85c14..a54d74f 100644 --- a/packages/splatkit-android/src/main/cpp/rendering/vulkan/frame/VulkanFrameCompute.cpp +++ b/packages/splatkit-android/src/main/cpp/rendering/vulkan/frame/VulkanFrameCompute.cpp @@ -201,10 +201,10 @@ void VulkanFrameCompute::copyDiagnostics(VkCommandBuffer cmd, uint32_t slot, uint32_t words[kReadbackWords]{}; words[kSelected] = candidates; vkCmdUpdateBuffer(cmd, target, 0, sizeof(words), words); - memoryBarrier(cmd, VK_PIPELINE_STAGE_TRANSFER_BIT | VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, - VK_ACCESS_TRANSFER_WRITE_BIT | VK_ACCESS_SHADER_WRITE_BIT, - VK_PIPELINE_STAGE_TRANSFER_BIT, - VK_ACCESS_TRANSFER_READ_BIT | VK_ACCESS_TRANSFER_WRITE_BIT); + vulkanMemoryBarrier(cmd, VK_PIPELINE_STAGE_TRANSFER_BIT | VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, + VK_ACCESS_TRANSFER_WRITE_BIT | VK_ACCESS_SHADER_WRITE_BIT, + VK_PIPELINE_STAGE_TRANSFER_BIT, + VK_ACCESS_TRANSFER_READ_BIT | VK_ACCESS_TRANSFER_WRITE_BIT); const auto copy = [&](VkBuffer source, VkDeviceSize from, VkDeviceSize to, VkDeviceSize bytes) { const VkBufferCopy region{from, to, bytes}; vkCmdCopyBuffer(cmd, source, target, 1, ®ion); @@ -219,8 +219,8 @@ void VulkanFrameCompute::copyDiagnostics(VkCommandBuffer cmd, uint32_t slot, copy(lod_->output().state, VulkanLodSelection::kLimitedOffset, wordOffset(kLimited), 2 * sizeof(uint32_t)); } - memoryBarrier(cmd, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_ACCESS_TRANSFER_WRITE_BIT, - VK_PIPELINE_STAGE_HOST_BIT, VK_ACCESS_HOST_READ_BIT); + vulkanMemoryBarrier(cmd, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_ACCESS_TRANSFER_WRITE_BIT, + VK_PIPELINE_STAGE_HOST_BIT, VK_ACCESS_HOST_READ_BIT); pending_[slot] = true; submission_[slot] = 0; } @@ -240,10 +240,10 @@ std::optional VulkanFrameCompute::encode( input.keyBits = keyBits(); input.keyOrder = VulkanVisibilityPass::KeyOrder::descending; // Hardware uses back-to-front over. if (!lod_ && !prepareRanges(slot, frame, input)) return std::nullopt; - memoryBarrier(cmd, VK_PIPELINE_STAGE_HOST_BIT | VK_PIPELINE_STAGE_TRANSFER_BIT, - VK_ACCESS_HOST_WRITE_BIT | VK_ACCESS_TRANSFER_WRITE_BIT, - VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, - VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_UNIFORM_READ_BIT); + vulkanMemoryBarrier(cmd, VK_PIPELINE_STAGE_HOST_BIT | VK_PIPELINE_STAGE_TRANSFER_BIT, + VK_ACCESS_HOST_WRITE_BIT | VK_ACCESS_TRANSFER_WRITE_BIT, + VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, + VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_UNIFORM_READ_BIT); if (queries_[slot]) vkCmdResetQueryPool(cmd, queries_[slot], 0, kTimestampCount); const auto timestamp = [&](uint32_t index) { if (queries_[slot]) @@ -274,14 +274,14 @@ std::optional VulkanFrameCompute::encode( if (!radix_->encode(cmd, slot, sort)) return std::nullopt; timestamp(3); // The sorter's checked GPU count is authoritative even if visibility succeeded. - memoryBarrier(cmd, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_ACCESS_SHADER_WRITE_BIT, - VK_PIPELINE_STAGE_TRANSFER_BIT, - VK_ACCESS_TRANSFER_READ_BIT | VK_ACCESS_TRANSFER_WRITE_BIT); + vulkanMemoryBarrier(cmd, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_ACCESS_SHADER_WRITE_BIT, + VK_PIPELINE_STAGE_TRANSFER_BIT, + VK_ACCESS_TRANSFER_READ_BIT | VK_ACCESS_TRANSFER_WRITE_BIT); const VkBufferCopy countToDraw{0, offsetof(VkDrawIndirectCommand, instanceCount), sizeof(uint32_t)}; vkCmdCopyBuffer(cmd, radix_->output(slot).count, visible.indirect, 1, &countToDraw); - memoryBarrier(cmd, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_ACCESS_TRANSFER_WRITE_BIT, - VK_PIPELINE_STAGE_DRAW_INDIRECT_BIT, VK_ACCESS_INDIRECT_COMMAND_READ_BIT); + vulkanMemoryBarrier(cmd, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_ACCESS_TRANSFER_WRITE_BIT, + VK_PIPELINE_STAGE_DRAW_INDIRECT_BIT, VK_ACCESS_INDIRECT_COMMAND_READ_BIT); copyDiagnostics(cmd, slot, visible, input.candidateCapacity); return Draw{radix_->output(slot).values, visible.indirect, capacity_}; } diff --git a/packages/splatkit-android/src/main/cpp/rendering/vulkan/raster/VulkanSplatPipeline.cpp b/packages/splatkit-android/src/main/cpp/rendering/vulkan/raster/VulkanSplatPipeline.cpp index 5317c15..94bbd33 100644 --- a/packages/splatkit-android/src/main/cpp/rendering/vulkan/raster/VulkanSplatPipeline.cpp +++ b/packages/splatkit-android/src/main/cpp/rendering/vulkan/raster/VulkanSplatPipeline.cpp @@ -93,8 +93,8 @@ bool VulkanSplatPipeline::createDescriptors(const VulkanCameraUniforms& cameras) bool VulkanSplatPipeline::createPipelines(VkRenderPass renderPass) { VkDevice device = ctx_.device(); - const ShaderModule vert(device, shaders::splat_vert, shaders::splat_vert_size); - const ShaderModule frag(device, shaders::splat_frag, shaders::splat_frag_size); + const VulkanShaderModule vert(device, shaders::splat_vert, shaders::splat_vert_size); + const VulkanShaderModule frag(device, shaders::splat_frag, shaders::splat_frag_size); if (!vert || !frag) return false; // constant_id 0 of the vertex shader is the SH degree; each pipeline gets its own. diff --git a/packages/splatkit-android/src/main/cpp/rendering/vulkan/selection/VulkanLodSelection.cpp b/packages/splatkit-android/src/main/cpp/rendering/vulkan/selection/VulkanLodSelection.cpp index ae44fda..00b569e 100644 --- a/packages/splatkit-android/src/main/cpp/rendering/vulkan/selection/VulkanLodSelection.cpp +++ b/packages/splatkit-android/src/main/cpp/rendering/vulkan/selection/VulkanLodSelection.cpp @@ -101,8 +101,8 @@ bool VulkanLodSelection::initialize() { layout.pushConstantRangeCount = 1; layout.pPushConstantRanges = &push; if (vkCreatePipelineLayout(ctx_.device(), &layout, nullptr, &layout_) != VK_SUCCESS) return false; - const ShaderModule module(ctx_.device(), shaders::lod_selection_comp, - shaders::lod_selection_comp_size); + const VulkanShaderModule module(ctx_.device(), shaders::lod_selection_comp, + shaders::lod_selection_comp_size); if (!module) return false; VkComputePipelineCreateInfo pipeline{VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO}; pipeline.layout = layout_; @@ -216,7 +216,7 @@ bool VulkanLodSelection::encode(VkCommandBuffer cmd, uint32_t slot, const Input& } vkUpdateDescriptorSets(ctx_.device(), kBindings, writes, 0, nullptr); // Inter-frame WAR/WAW plus upload/host writes, including consumers on previous submissions. - memoryBarrier( + vulkanMemoryBarrier( cmd, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT | VK_PIPELINE_STAGE_HOST_BIT, VK_ACCESS_MEMORY_WRITE_BIT | VK_ACCESS_MEMORY_READ_BIT | VK_ACCESS_HOST_WRITE_BIT, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, @@ -232,10 +232,10 @@ bool VulkanLodSelection::encode(VkCommandBuffer cmd, uint32_t slot, const Input& vkCmdDispatch(cmd, 1, 1, 1); else vkCmdDispatchIndirect(cmd, scratch_->handle(), indirectOffset); - memoryBarrier(cmd, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_ACCESS_SHADER_WRITE_BIT, - VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT | VK_PIPELINE_STAGE_DRAW_INDIRECT_BIT, - VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT | - VK_ACCESS_INDIRECT_COMMAND_READ_BIT); + vulkanMemoryBarrier(cmd, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_ACCESS_SHADER_WRITE_BIT, + VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT | VK_PIPELINE_STAGE_DRAW_INDIRECT_BIT, + VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT | + VK_ACCESS_INDIRECT_COMMAND_READ_BIT); }; dispatch(kReset); for (uint32_t round = 0; round < rounds_; ++round) { @@ -251,11 +251,11 @@ bool VulkanLodSelection::encode(VkCommandBuffer cmd, uint32_t slot, const Input& dispatch(kAdvance); } dispatch(kCopyPackets, kPacketGroupsOffset); - memoryBarrier(cmd, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_ACCESS_SHADER_WRITE_BIT, - VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT | VK_PIPELINE_STAGE_VERTEX_SHADER_BIT | - VK_PIPELINE_STAGE_DRAW_INDIRECT_BIT | VK_PIPELINE_STAGE_TRANSFER_BIT, - VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_INDIRECT_COMMAND_READ_BIT | - VK_ACCESS_TRANSFER_READ_BIT); + vulkanMemoryBarrier(cmd, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_ACCESS_SHADER_WRITE_BIT, + VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT | VK_PIPELINE_STAGE_VERTEX_SHADER_BIT | + VK_PIPELINE_STAGE_DRAW_INDIRECT_BIT | VK_PIPELINE_STAGE_TRANSFER_BIT, + VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_INDIRECT_COMMAND_READ_BIT | + VK_ACCESS_TRANSFER_READ_BIT); return true; } } // namespace splatkit diff --git a/packages/splatkit-android/src/main/cpp/rendering/vulkan/sort/VulkanRadixSort.cpp b/packages/splatkit-android/src/main/cpp/rendering/vulkan/sort/VulkanRadixSort.cpp index 8b0659a..c6cbd9e 100644 --- a/packages/splatkit-android/src/main/cpp/rendering/vulkan/sort/VulkanRadixSort.cpp +++ b/packages/splatkit-android/src/main/cpp/rendering/vulkan/sort/VulkanRadixSort.cpp @@ -42,8 +42,8 @@ uint32_t passSet(uint32_t pass) { } void computeBarrier(VkCommandBuffer cmd) { - memoryBarrier(cmd, kCompute, VK_ACCESS_SHADER_WRITE_BIT, kCompute, - VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT); + vulkanMemoryBarrier(cmd, kCompute, VK_ACCESS_SHADER_WRITE_BIT, kCompute, + VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT); } // Bind after each pipeline transition: MoltenVK/gfxstream otherwise retained stale @@ -157,7 +157,7 @@ bool VulkanRadixSort::initialize() { const size_t sizes[] = {shaders::radix_prepare_comp_size, shaders::radix_histogram_comp_size, shaders::radix_scan_comp_size, shaders::radix_scatter_comp_size}; for (uint32_t i = 0; i < kStageCount; ++i) { - const ShaderModule module(device, code[i], sizes[i]); + const VulkanShaderModule module(device, code[i], sizes[i]); if (!module) return false; VkComputePipelineCreateInfo pipeline{VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO}; pipeline.stage = {VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO}; @@ -275,7 +275,7 @@ void VulkanRadixSort::recordSort(VkCommandBuffer cmd, uint32_t slot, KeyBits key PushConstants push{capacity_, 0, limits.maxComputeWorkGroupCount[0]}; bindStage(cmd, layout_, pipelines_[kPrepare], sets_[slot][0], push); vkCmdDispatch(cmd, 1, 1, 1); - memoryBarrier( + vulkanMemoryBarrier( cmd, kCompute, VK_ACCESS_SHADER_WRITE_BIT, kCompute | VK_PIPELINE_STAGE_DRAW_INDIRECT_BIT, VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT | VK_ACCESS_INDIRECT_COMMAND_READ_BIT); const VkBuffer state = slots_[slot].state->handle(); @@ -294,9 +294,10 @@ void VulkanRadixSort::recordSort(VkCommandBuffer cmd, uint32_t slot, KeyBits key vkCmdDispatchIndirect(cmd, state, 0); computeBarrier(cmd); } - memoryBarrier(cmd, kCompute, VK_ACCESS_SHADER_WRITE_BIT, - kCompute | VK_PIPELINE_STAGE_VERTEX_SHADER_BIT | VK_PIPELINE_STAGE_TRANSFER_BIT, - VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_TRANSFER_READ_BIT); + vulkanMemoryBarrier( + cmd, kCompute, VK_ACCESS_SHADER_WRITE_BIT, + kCompute | VK_PIPELINE_STAGE_VERTEX_SHADER_BIT | VK_PIPELINE_STAGE_TRANSFER_BIT, + VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_TRANSFER_READ_BIT); } bool VulkanRadixSort::encode(VkCommandBuffer cmd, uint32_t slot, const Input& input) const { @@ -305,9 +306,10 @@ bool VulkanRadixSort::encode(VkCommandBuffer cmd, uint32_t slot, const Input& in return false; if (!acceptsInput(input)) return false; writeDescriptorSets(slot, input); - memoryBarrier(cmd, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT | VK_PIPELINE_STAGE_HOST_BIT, - VK_ACCESS_MEMORY_WRITE_BIT | VK_ACCESS_MEMORY_READ_BIT | VK_ACCESS_HOST_WRITE_BIT, - kCompute, VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT); + vulkanMemoryBarrier( + cmd, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT | VK_PIPELINE_STAGE_HOST_BIT, + VK_ACCESS_MEMORY_WRITE_BIT | VK_ACCESS_MEMORY_READ_BIT | VK_ACCESS_HOST_WRITE_BIT, kCompute, + VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT); recordSort(cmd, slot, input.keyBits); return true; }