Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
#include <limits>

#include "engine/TraceSection.h"
#include "rendering/vulkan/sort/VulkanRadixSort.h"

#include "splatkit/Log.h"

Expand Down Expand Up @@ -118,6 +117,16 @@ std::optional<GpuWorldInfo> 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<VulkanWorld> world,
std::unique_ptr<VulkanFrameCompute> 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<uint32_t>::max()) return false;
Expand All @@ -131,17 +140,12 @@ 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;
}

bool VulkanSplatRenderer::sortsOnGpu() const {
return VulkanVisibilityPass::queryCapabilities(ctx_).supported &&
VulkanRadixSort::queryCapabilities(ctx_).supported;
return VulkanFrameCompute::supported(ctx_);
}

bool VulkanSplatRenderer::selectsLodOnGpu() const {
Expand All @@ -161,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;
}

Expand All @@ -181,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;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ class VulkanSplatRenderer final : public SplatRenderer {
void rebuildPipelinesIfNeeded();
void rebuildSurface(VulkanFrameLoop::Status status);
void applyPolicyToCompute();
void adoptWorld(std::unique_ptr<VulkanWorld> world, std::unique_ptr<VulkanFrameCompute> compute);

VulkanContext& ctx_;
VulkanFrameLoop& frameLoop_;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<VkDescriptorSetLayout, kSlots> visibilityLayouts{};
std::array<VkDescriptorSetLayout, kSlots> prepareLayouts{};
std::array<VkDescriptorSetLayout, kFramesInFlight> visibilityLayouts{};
std::array<VkDescriptorSetLayout, kFramesInFlight> 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;
}
Expand All @@ -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};
Expand Down Expand Up @@ -344,12 +344,12 @@ bool VulkanVisibilityPass::reserve(uint32_t capacity) {
if (!dummy || !dummy->upload(zeros, sizeof(zeros))) return false;
}

std::array<std::unique_ptr<VulkanBuffer>, kSlots> indices;
std::array<std::unique_ptr<VulkanBuffer>, kSlots> depthKeys;
std::array<std::unique_ptr<VulkanBuffer>, kSlots> counts;
std::array<std::unique_ptr<VulkanBuffer>, kSlots> indirect;
std::array<std::unique_ptr<VulkanBuffer>, kSlots> status;
for (uint32_t slot = 0; slot < kSlots; ++slot) {
std::array<std::unique_ptr<VulkanBuffer>, kFramesInFlight> indices;
std::array<std::unique_ptr<VulkanBuffer>, kFramesInFlight> depthKeys;
std::array<std::unique_ptr<VulkanBuffer>, kFramesInFlight> counts;
std::array<std::unique_ptr<VulkanBuffer>, kFramesInFlight> indirect;
std::array<std::unique_ptr<VulkanBuffer>, 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(
Expand Down Expand Up @@ -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()};
}
Expand Down Expand Up @@ -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<uint32_t>(input.mode) > static_cast<uint32_t>(CandidateMode::ranges) ||
static_cast<uint32_t>(input.keyBits) > static_cast<uint32_t>(KeyBits::low16) ||
static_cast<uint32_t>(input.keyOrder) > static_cast<uint32_t>(KeyOrder::descending) ||
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -130,19 +130,19 @@ class VulkanVisibilityPass {
VkDescriptorSetLayout visibilitySetLayout_ = VK_NULL_HANDLE;
VkDescriptorSetLayout prepareSetLayout_ = VK_NULL_HANDLE;
VkDescriptorPool descriptorPool_ = VK_NULL_HANDLE;
std::array<VkDescriptorSet, kSlots> visibilitySets_{};
std::array<VkDescriptorSet, kSlots> prepareSets_{};
std::array<VkDescriptorSet, kFramesInFlight> visibilitySets_{};
std::array<VkDescriptorSet, kFramesInFlight> prepareSets_{};
VkPipelineLayout visibilityLayout_ = VK_NULL_HANDLE;
VkPipelineLayout prepareLayout_ = VK_NULL_HANDLE;
VkPipeline visibilityPipeline_ = VK_NULL_HANDLE;
VkPipeline preparePipeline_ = VK_NULL_HANDLE;

std::unique_ptr<VulkanBuffer> dummy_; // immutable zero words for inactive input descriptors
std::array<std::unique_ptr<VulkanBuffer>, kSlots> indices_{};
std::array<std::unique_ptr<VulkanBuffer>, kSlots> depthKeys_{};
std::array<std::unique_ptr<VulkanBuffer>, kSlots> counts_{};
std::array<std::unique_ptr<VulkanBuffer>, kSlots> indirect_{};
std::array<std::unique_ptr<VulkanBuffer>, kSlots> status_{};
std::array<std::unique_ptr<VulkanBuffer>, kFramesInFlight> indices_{};
std::array<std::unique_ptr<VulkanBuffer>, kFramesInFlight> depthKeys_{};
std::array<std::unique_ptr<VulkanBuffer>, kFramesInFlight> counts_{};
std::array<std::unique_ptr<VulkanBuffer>, kFramesInFlight> indirect_{};
std::array<std::unique_ptr<VulkanBuffer>, kFramesInFlight> status_{};
};

} // namespace splatkit
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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_; }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -34,7 +34,7 @@ class VulkanCameraUniforms {
private:
VulkanCameraUniforms() = default;

std::array<std::unique_ptr<VulkanBuffer>, VulkanFrameLoop::kFramesInFlight> uniforms_{};
std::array<std::unique_ptr<VulkanBuffer>, kFramesInFlight> uniforms_{};
};

} // namespace splatkit
Original file line number Diff line number Diff line change
Expand Up @@ -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<std::unique_ptr<VulkanFrameCompute>> VulkanFrameCompute::create(
const VulkanContext& ctx, uint32_t sourceCount, const splat::LodTree* tree, uint32_t budget) {
std::unique_ptr<VulkanFrameCompute> pass(new VulkanFrameCompute(ctx));
Expand Down Expand Up @@ -79,7 +84,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] =
Expand Down Expand Up @@ -151,11 +156,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);
}
Expand Down Expand Up @@ -196,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, &region);
Expand All @@ -214,31 +219,31 @@ 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;
}

std::optional<VulkanFrameCompute::Draw> 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;
input.camera = camera;
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,
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])
Expand All @@ -265,19 +270,18 @@ std::optional<VulkanFrameCompute::Draw> 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.
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_};
}
Expand Down
Loading
Loading