device/acceleration.cpp
| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | // SPDX-License-Identifier: BSD-3-Clause | ||
| 2 | /// @file acceleration.cpp | ||
| 3 | /// @brief Sizing acceleration structures, keeping them in a pool, and recording their builds, | ||
| 4 | /// compacted-size queries and compaction. | ||
| 5 | #include "ernie/device/acceleration.h" | ||
| 6 | |||
| 7 | #include "ernie/device/context.h" | ||
| 8 | |||
| 9 | #include <stdexcept> | ||
| 10 | #include <utility> | ||
| 11 | |||
| 12 | namespace ernie { | ||
| 13 | namespace { | ||
| 14 | |||
| 15 | /// The alignment the specification requires of an acceleration structure's offset. | ||
| 16 | constexpr std::uint64_t kStructureAlignment = 256; | ||
| 17 | /// The alignment instance arrays need. | ||
| 18 | constexpr std::uint64_t kInstanceAlignment = 16; | ||
| 19 | |||
| 20 | 289 | VkDeviceAddress DeviceAddress(const BufferView& view) { | |
| 21 | 289 | return view.buffer == nullptr ? 0 : view.buffer->Address() + view.offset; | |
| 22 | } | ||
| 23 | |||
| 24 | 107 | void RequireRayQueries(const DeviceContext& context) { | |
| 25 | 107 | if (!context.Capabilities().rayQuery) { | |
| 26 | ✗ | throw DeviceError(DeviceErrorKind::Unsupported, | |
| 27 | ✗ | context.Info().name + " has no hardware acceleration structures"); | |
| 28 | } | ||
| 29 | 107 | } | |
| 30 | |||
| 31 | 148 | VkBuildAccelerationStructureFlagsKHR ToVk(const BuildFlags& flags) { | |
| 32 | 296 | VkBuildAccelerationStructureFlagsKHR vk = | |
| 33 | 148 | flags.fastTrace ? VK_BUILD_ACCELERATION_STRUCTURE_PREFER_FAST_TRACE_BIT_KHR | |
| 34 | : VK_BUILD_ACCELERATION_STRUCTURE_PREFER_FAST_BUILD_BIT_KHR; | ||
| 35 | 148 | if (flags.compactable) { | |
| 36 | 89 | vk |= VK_BUILD_ACCELERATION_STRUCTURE_ALLOW_COMPACTION_BIT_KHR; | |
| 37 | } | ||
| 38 | 148 | if (flags.updatable) { | |
| 39 | 3 | vk |= VK_BUILD_ACCELERATION_STRUCTURE_ALLOW_UPDATE_BIT_KHR; | |
| 40 | } | ||
| 41 | 148 | return vk; | |
| 42 | } | ||
| 43 | |||
| 44 | 224 | VkAccelerationStructureTypeKHR ToVk(AccelerationKind kind) { | |
| 45 | 224 | return kind == AccelerationKind::Bottom ? VK_ACCELERATION_STRUCTURE_TYPE_BOTTOM_LEVEL_KHR | |
| 46 | 224 | : VK_ACCELERATION_STRUCTURE_TYPE_TOP_LEVEL_KHR; | |
| 47 | } | ||
| 48 | |||
| 49 | 49 | VkAccelerationStructureGeometryKHR Geometry(const TriangleGeometry& mesh) { | |
| 50 | 49 | VkAccelerationStructureGeometryTrianglesDataKHR triangles{ | |
| 51 | .sType = VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_GEOMETRY_TRIANGLES_DATA_KHR}; | ||
| 52 | 49 | triangles.vertexFormat = VK_FORMAT_R32G32B32_SFLOAT; | |
| 53 | 49 | triangles.vertexData.deviceAddress = DeviceAddress(mesh.vertices); | |
| 54 | 49 | triangles.vertexStride = mesh.stride; | |
| 55 | 49 | triangles.maxVertex = mesh.vertexCount - 1; | |
| 56 | 49 | triangles.indexType = VK_INDEX_TYPE_UINT32; | |
| 57 | 49 | triangles.indexData.deviceAddress = DeviceAddress(mesh.indices); | |
| 58 | 49 | VkAccelerationStructureGeometryKHR geometry{ | |
| 59 | .sType = VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_GEOMETRY_KHR, | ||
| 60 | .geometryType = VK_GEOMETRY_TYPE_TRIANGLES_KHR, | ||
| 61 | .flags = VK_GEOMETRY_OPAQUE_BIT_KHR}; | ||
| 62 | 49 | geometry.geometry.triangles = triangles; | |
| 63 | 49 | return geometry; | |
| 64 | } | ||
| 65 | |||
| 66 | 44 | VkAccelerationStructureGeometryKHR Geometry(const BoxGeometry& boxes) { | |
| 67 | 44 | VkAccelerationStructureGeometryAabbsDataKHR aabbs{ | |
| 68 | .sType = VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_GEOMETRY_AABBS_DATA_KHR}; | ||
| 69 | 44 | aabbs.data.deviceAddress = DeviceAddress(boxes.boxes); | |
| 70 | 44 | aabbs.stride = boxes.stride; | |
| 71 | // Opaque: a candidate is committed by the shader that intersects it exactly. | ||
| 72 | 44 | VkAccelerationStructureGeometryKHR geometry{ | |
| 73 | .sType = VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_GEOMETRY_KHR, | ||
| 74 | .geometryType = VK_GEOMETRY_TYPE_AABBS_KHR, | ||
| 75 | .flags = VK_GEOMETRY_OPAQUE_BIT_KHR}; | ||
| 76 | 44 | geometry.geometry.aabbs = aabbs; | |
| 77 | 44 | return geometry; | |
| 78 | } | ||
| 79 | |||
| 80 | 56 | VkAccelerationStructureGeometryKHR Geometry(const BufferView& instances) { | |
| 81 | 56 | VkAccelerationStructureGeometryInstancesDataKHR data{ | |
| 82 | .sType = VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_GEOMETRY_INSTANCES_DATA_KHR}; | ||
| 83 | 56 | data.data.deviceAddress = DeviceAddress(instances); | |
| 84 | 56 | VkAccelerationStructureGeometryKHR geometry{ | |
| 85 | .sType = VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_GEOMETRY_KHR, | ||
| 86 | .geometryType = VK_GEOMETRY_TYPE_INSTANCES_KHR}; | ||
| 87 | 56 | geometry.geometry.instances = data; | |
| 88 | 56 | return geometry; | |
| 89 | } | ||
| 90 | |||
| 91 | /// A bottom level's geometries and their primitive counts. | ||
| 92 | 140 | struct BottomInput { | |
| 93 | std::vector<VkAccelerationStructureGeometryKHR> geometry; | ||
| 94 | std::vector<std::uint32_t> primitives; | ||
| 95 | }; | ||
| 96 | |||
| 97 | 94 | BottomInput Input(std::span<const TriangleGeometry> triangles, std::span<const BoxGeometry> boxes) { | |
| 98 | 94 | if (triangles.empty() == boxes.empty()) { | |
| 99 | 1 | throw std::invalid_argument("A bottom level holds triangles or boxes, one kind only"); | |
| 100 | } | ||
| 101 | 93 | BottomInput input; | |
| 102 | 142 | for (const TriangleGeometry& mesh : triangles) { | |
| 103 | 49 | if (mesh.vertexCount == 0 || mesh.triangleCount == 0) { | |
| 104 | ✗ | throw std::invalid_argument("A bottom level's triangles need vertices"); | |
| 105 | } | ||
| 106 | 49 | input.geometry.push_back(Geometry(mesh)); | |
| 107 | 49 | input.primitives.push_back(mesh.triangleCount); | |
| 108 | } | ||
| 109 | 137 | for (const BoxGeometry& box : boxes) { | |
| 110 | 44 | input.geometry.push_back(Geometry(box)); | |
| 111 | 44 | input.primitives.push_back(box.count); | |
| 112 | } | ||
| 113 | 93 | return input; | |
| 114 | ✗ | } | |
| 115 | |||
| 116 | VkAccelerationStructureBuildGeometryInfoKHR | ||
| 117 | 148 | BuildInfo(AccelerationKind kind, const BuildFlags& flags, | |
| 118 | std::span<const VkAccelerationStructureGeometryKHR> geometry) { | ||
| 119 | 148 | return {.sType = VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_BUILD_GEOMETRY_INFO_KHR, | |
| 120 | 148 | .type = ToVk(kind), | |
| 121 | 148 | .flags = ToVk(flags), | |
| 122 | .mode = VK_BUILD_ACCELERATION_STRUCTURE_MODE_BUILD_KHR, | ||
| 123 | 148 | .geometryCount = static_cast<std::uint32_t>(geometry.size()), | |
| 124 | 148 | .pGeometries = geometry.data()}; | |
| 125 | } | ||
| 126 | |||
| 127 | 68 | AccelerationSizes Query(const DeviceContext& context, AccelerationKind kind, | |
| 128 | const BuildFlags& flags, | ||
| 129 | std::span<const VkAccelerationStructureGeometryKHR> geometry, | ||
| 130 | std::span<const std::uint32_t> primitives) { | ||
| 131 | 68 | const VkAccelerationStructureBuildGeometryInfoKHR build = BuildInfo(kind, flags, geometry); | |
| 132 | 68 | VkAccelerationStructureBuildSizesInfoKHR sizes{ | |
| 133 | .sType = VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_BUILD_SIZES_INFO_KHR}; | ||
| 134 | 68 | vkGetAccelerationStructureBuildSizesKHR(context.Device(), | |
| 135 | VK_ACCELERATION_STRUCTURE_BUILD_TYPE_DEVICE_KHR, &build, | ||
| 136 | primitives.data(), &sizes); | ||
| 137 | 68 | return {.kind = kind, | |
| 138 | .flags = flags, | ||
| 139 | 68 | .storage = sizes.accelerationStructureSize, | |
| 140 | 68 | .buildScratch = sizes.buildScratchSize, | |
| 141 | 1 | .updateScratch = flags.updatable ? sizes.updateScratchSize : 0, | |
| 142 | 68 | .primitives = {primitives.begin(), primitives.end()}}; | |
| 143 | 69 | } | |
| 144 | |||
| 145 | /// Checks a build holds no more geometries or primitives than its target was sized for. | ||
| 146 | 81 | void CheckFits(const AccelerationStructure& target, std::span<const std::uint32_t> primitives) { | |
| 147 | 81 | const std::vector<std::uint32_t>& sized = target.Sizes().primitives; | |
| 148 | 81 | bool fits = primitives.size() <= sized.size(); | |
| 149 | 162 | for (std::size_t i = 0; fits && i < primitives.size(); ++i) { | |
| 150 | 81 | fits = primitives[i] <= sized[i]; | |
| 151 | } | ||
| 152 | 81 | if (!fits) { | |
| 153 | 2 | throw std::invalid_argument("A build of " + target.Name() + | |
| 154 | 3 | " holds more than it was sized for"); | |
| 155 | } | ||
| 156 | 80 | } | |
| 157 | |||
| 158 | 38 | std::uint64_t QueryScratchAlignment(const DeviceContext& context) { | |
| 159 | 38 | VkPhysicalDeviceAccelerationStructurePropertiesKHR properties{ | |
| 160 | .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ACCELERATION_STRUCTURE_PROPERTIES_KHR}; | ||
| 161 | 38 | VkPhysicalDeviceProperties2 all{.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2, | |
| 162 | 38 | .pNext = &properties}; | |
| 163 | 38 | vkGetPhysicalDeviceProperties2(context.Physical(), &all); | |
| 164 | 38 | return std::max<std::uint64_t>(properties.minAccelerationStructureScratchOffsetAlignment, 1); | |
| 165 | } | ||
| 166 | |||
| 167 | /// @return The pool the builds' targets share, which sets their scratch alignment. | ||
| 168 | 101 | const AccelerationPool& BuildPool(std::span<const BottomBuild> bottoms, | |
| 169 | std::span<const TopBuild> tops) { | ||
| 170 | 101 | const AccelerationStructure* first = bottoms.empty() ? tops[0].target : bottoms[0].target; | |
| 171 | 101 | if (first == nullptr) { | |
| 172 | ✗ | throw std::invalid_argument("A build has no target"); | |
| 173 | } | ||
| 174 | 101 | return first->Pool(); | |
| 175 | } | ||
| 176 | |||
| 177 | 206 | std::uint64_t AlignUp(std::uint64_t value, std::uint64_t alignment) { | |
| 178 | 206 | return (value + alignment - 1) / alignment * alignment; | |
| 179 | } | ||
| 180 | |||
| 181 | 149 | std::uint64_t Scratch(const AccelerationStructure& target, bool update) { | |
| 182 | 149 | return update ? target.Sizes().updateScratch : target.Sizes().buildScratch; | |
| 183 | } | ||
| 184 | |||
| 185 | 82 | void CheckTarget(const AccelerationStructure* target, AccelerationKind kind, bool update) { | |
| 186 | 82 | if (target == nullptr || target->Kind() != kind) { | |
| 187 | ✗ | throw std::invalid_argument("A build's target is missing or of the other kind"); | |
| 188 | } | ||
| 189 | 82 | if (update && !target->Sizes().flags.updatable) { | |
| 190 | 2 | throw std::invalid_argument(target->Name() + " was not built to be updated"); | |
| 191 | } | ||
| 192 | 81 | } | |
| 193 | |||
| 194 | } // namespace | ||
| 195 | |||
| 196 | 25 | AccelerationSizes Sizes(const DeviceContext& context, std::span<const TriangleGeometry> triangles, | |
| 197 | BuildFlags flags) { | ||
| 198 | 25 | RequireRayQueries(context); | |
| 199 | 25 | const BottomInput input = Input(triangles, {}); | |
| 200 | 24 | return Query(context, AccelerationKind::Bottom, flags, input.geometry, input.primitives); | |
| 201 | 24 | } | |
| 202 | |||
| 203 | 22 | AccelerationSizes Sizes(const DeviceContext& context, std::span<const BoxGeometry> boxes, | |
| 204 | BuildFlags flags) { | ||
| 205 | 22 | RequireRayQueries(context); | |
| 206 | 22 | const BottomInput input = Input({}, boxes); | |
| 207 | 22 | return Query(context, AccelerationKind::Bottom, flags, input.geometry, input.primitives); | |
| 208 | 22 | } | |
| 209 | |||
| 210 | 22 | AccelerationSizes Sizes(const DeviceContext& context, std::uint32_t capacity, BuildFlags flags) { | |
| 211 | 22 | RequireRayQueries(context); | |
| 212 | 22 | const VkAccelerationStructureGeometryKHR instances = Geometry(BufferView{}); | |
| 213 | 22 | return Query(context, AccelerationKind::Top, flags, std::span(&instances, 1), | |
| 214 | 22 | std::span(&capacity, 1)); | |
| 215 | } | ||
| 216 | |||
| 217 | 38 | AccelerationPool::AccelerationPool(DeviceContext& context, std::string_view name, | |
| 218 | 38 | std::uint64_t arenaSize) | |
| 219 | 38 | : context_(context), name_(name), arenas_(context, MemoryUse::Device, name, arenaSize), | |
| 220 | 38 | scratchAlignment_(QueryScratchAlignment(context)) { | |
| 221 | 38 | RequireRayQueries(context); | |
| 222 | 38 | } | |
| 223 | |||
| 224 | 76 | BufferRange AccelerationPool::Allocate(AccelerationKind kind, std::uint64_t bytes) { | |
| 225 | 76 | const BufferRange range = arenas_.Allocate(bytes, kStructureAlignment); | |
| 226 | 76 | if (kind == AccelerationKind::Bottom) { | |
| 227 | 54 | usage_.bottomBytes += bytes; | |
| 228 | 54 | ++usage_.bottoms; | |
| 229 | } else { | ||
| 230 | 22 | usage_.topBytes += bytes; | |
| 231 | 22 | ++usage_.tops; | |
| 232 | } | ||
| 233 | 76 | return range; | |
| 234 | } | ||
| 235 | |||
| 236 | 76 | void AccelerationPool::Free(AccelerationKind kind, const BufferRange& range) { | |
| 237 | 76 | arenas_.Free(range); | |
| 238 | 76 | if (kind == AccelerationKind::Bottom) { | |
| 239 | 54 | usage_.bottomBytes -= range.size; | |
| 240 | 54 | --usage_.bottoms; | |
| 241 | } else { | ||
| 242 | 22 | usage_.topBytes -= range.size; | |
| 243 | 22 | --usage_.tops; | |
| 244 | } | ||
| 245 | 76 | } | |
| 246 | |||
| 247 | 76 | AccelerationStructure::AccelerationStructure(AccelerationPool& pool, const AccelerationSizes& sizes, | |
| 248 | 76 | std::string_view name) | |
| 249 | 76 | : pool_(pool), sizes_(sizes), name_(name), range_(pool.Allocate(sizes.kind, sizes.storage)) { | |
| 250 | 76 | DeviceContext& context = pool.Context(); | |
| 251 | 76 | const VkAccelerationStructureCreateInfoKHR info{ | |
| 252 | .sType = VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_CREATE_INFO_KHR, | ||
| 253 | 76 | .buffer = Storage().Handle(), | |
| 254 | 76 | .offset = range_.offset, | |
| 255 | 76 | .size = sizes.storage, | |
| 256 | 76 | .type = ToVk(sizes.kind)}; | |
| 257 | 76 | const VkResult created = | |
| 258 | 76 | vkCreateAccelerationStructureKHR(context.Device(), &info, nullptr, &structure_); | |
| 259 | 76 | if (created < VK_SUCCESS) { | |
| 260 | ✗ | pool.Free(sizes.kind, range_); | |
| 261 | } | ||
| 262 | 76 | CheckVk(created, [&] { return "Creating acceleration structure " + name_; }); | |
| 263 | 76 | context.Name(VK_OBJECT_TYPE_ACCELERATION_STRUCTURE_KHR, | |
| 264 | 76 | reinterpret_cast<std::uint64_t>(structure_), name); | |
| 265 | 76 | const VkAccelerationStructureDeviceAddressInfoKHR address{ | |
| 266 | .sType = VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_DEVICE_ADDRESS_INFO_KHR, | ||
| 267 | 76 | .accelerationStructure = structure_}; | |
| 268 | 76 | address_ = vkGetAccelerationStructureDeviceAddressKHR(context.Device(), &address); | |
| 269 | 76 | } | |
| 270 | |||
| 271 | 76 | AccelerationStructure::~AccelerationStructure() { | |
| 272 | // Work submitted so far may still trace it, or build a top level that refers to it. | ||
| 273 | 76 | DeviceContext& context = pool_.Context(); | |
| 274 | 76 | context.Retire([device = context.Device(), structure = structure_] { | |
| 275 | 76 | vkDestroyAccelerationStructureKHR(device, structure, nullptr); | |
| 276 | }); | ||
| 277 | 76 | pool_.Free(sizes_.kind, range_); | |
| 278 | 76 | } | |
| 279 | |||
| 280 | 106 | const Buffer& AccelerationStructure::Storage() const { | |
| 281 | 106 | return pool_.arenas_.Storage(range_); | |
| 282 | } | ||
| 283 | |||
| 284 | 44 | std::uint64_t ScratchBytes(std::span<const BottomBuild> bottoms, std::span<const TopBuild> tops) { | |
| 285 | 44 | if (bottoms.empty() && tops.empty()) { | |
| 286 | return 0; | ||
| 287 | } | ||
| 288 | 44 | const std::uint64_t alignment = BuildPool(bottoms, tops).ScratchAlignment(); | |
| 289 | // One alignment's slack places the first build's scratch, wherever `scratch` starts. | ||
| 290 | 44 | std::uint64_t bytes = alignment; | |
| 291 | 85 | for (const BottomBuild& build : bottoms) { | |
| 292 | 41 | bytes += AlignUp(Scratch(*build.target, build.update), alignment); | |
| 293 | } | ||
| 294 | 72 | for (const TopBuild& build : tops) { | |
| 295 | 28 | bytes += AlignUp(Scratch(*build.target, build.update), alignment); | |
| 296 | } | ||
| 297 | 44 | return bytes; | |
| 298 | } | ||
| 299 | |||
| 300 | 57 | void RecordBuilds(VkCommandBuffer commands, std::span<const BottomBuild> bottoms, | |
| 301 | std::span<const TopBuild> tops, BufferView scratch) { | ||
| 302 | 57 | if (bottoms.empty() && tops.empty()) { | |
| 303 | ✗ | return; | |
| 304 | } | ||
| 305 | 57 | const AccelerationPool& pool = BuildPool(bottoms, tops); | |
| 306 | 57 | const std::uint64_t alignment = pool.ScratchAlignment(); | |
| 307 | 57 | VkDeviceAddress next = AlignUp(DeviceAddress(scratch), alignment); | |
| 308 | 57 | std::vector<BottomInput> inputs; | |
| 309 | 57 | std::vector<VkAccelerationStructureGeometryKHR> instances; | |
| 310 | 57 | inputs.reserve(bottoms.size()); | |
| 311 | 57 | instances.reserve(tops.size()); | |
| 312 | 57 | std::vector<VkAccelerationStructureBuildGeometryInfoKHR> infos; | |
| 313 | 57 | std::vector<std::vector<VkAccelerationStructureBuildRangeInfoKHR>> ranges; | |
| 314 | 137 | const auto add = [&](const AccelerationStructure& target, bool update, | |
| 315 | std::span<const VkAccelerationStructureGeometryKHR> geometry, | ||
| 316 | std::span<const std::uint32_t> primitives) { | ||
| 317 | 80 | VkAccelerationStructureBuildGeometryInfoKHR info = | |
| 318 | 80 | BuildInfo(target.Kind(), target.Sizes().flags, geometry); | |
| 319 | 80 | info.mode = update ? VK_BUILD_ACCELERATION_STRUCTURE_MODE_UPDATE_KHR | |
| 320 | : VK_BUILD_ACCELERATION_STRUCTURE_MODE_BUILD_KHR; | ||
| 321 | 80 | info.srcAccelerationStructure = update ? target.Handle() : VK_NULL_HANDLE; | |
| 322 | 80 | info.dstAccelerationStructure = target.Handle(); | |
| 323 | 80 | info.scratchData.deviceAddress = next; | |
| 324 | 80 | next += AlignUp(Scratch(target, update), alignment); | |
| 325 | 80 | infos.push_back(info); | |
| 326 | 80 | std::vector<VkAccelerationStructureBuildRangeInfoKHR>& range = ranges.emplace_back(); | |
| 327 | 160 | for (const std::uint32_t count : primitives) { | |
| 328 | 80 | range.push_back({.primitiveCount = count}); | |
| 329 | } | ||
| 330 | 137 | }; | |
| 331 | 103 | for (const BottomBuild& build : bottoms) { | |
| 332 | 48 | CheckTarget(build.target, AccelerationKind::Bottom, build.update); | |
| 333 | 47 | const BottomInput& input = inputs.emplace_back(Input(build.triangles, build.boxes)); | |
| 334 | 47 | CheckFits(*build.target, input.primitives); | |
| 335 | 46 | add(*build.target, build.update, input.geometry, input.primitives); | |
| 336 | } | ||
| 337 | 89 | for (const TopBuild& build : tops) { | |
| 338 | 34 | CheckTarget(build.target, AccelerationKind::Top, build.update); | |
| 339 | 34 | if (DeviceAddress(build.instances) % kInstanceAlignment != 0) { | |
| 340 | ✗ | throw std::invalid_argument("A top level's instances need 16-byte alignment"); | |
| 341 | } | ||
| 342 | 34 | CheckFits(*build.target, std::span(&build.count, 1)); | |
| 343 | 34 | const VkAccelerationStructureGeometryKHR& geometry = | |
| 344 | 34 | instances.emplace_back(Geometry(build.instances)); | |
| 345 | 34 | add(*build.target, build.update, std::span(&geometry, 1), std::span(&build.count, 1)); | |
| 346 | } | ||
| 347 | 55 | std::vector<const VkAccelerationStructureBuildRangeInfoKHR*> pointers; | |
| 348 | 135 | for (const auto& range : ranges) { | |
| 349 | 80 | pointers.push_back(range.data()); | |
| 350 | } | ||
| 351 | 55 | vkCmdBuildAccelerationStructuresKHR(commands, static_cast<std::uint32_t>(infos.size()), | |
| 352 | 55 | infos.data(), pointers.data()); | |
| 353 | 63 | } | |
| 354 | |||
| 355 | 15 | CompactionQuery::CompactionQuery(DeviceContext& context, std::uint32_t capacity) | |
| 356 | 15 | : device_(context.Device()), capacity_(capacity) { | |
| 357 | 15 | const VkQueryPoolCreateInfo info{.sType = VK_STRUCTURE_TYPE_QUERY_POOL_CREATE_INFO, | |
| 358 | .queryType = | ||
| 359 | VK_QUERY_TYPE_ACCELERATION_STRUCTURE_COMPACTED_SIZE_KHR, | ||
| 360 | 15 | .queryCount = capacity}; | |
| 361 | 15 | CheckVk(vkCreateQueryPool(device_, &info, nullptr, &pool_), "Creating a compaction query"); | |
| 362 | 15 | } | |
| 363 | |||
| 364 | 15 | CompactionQuery::~CompactionQuery() { | |
| 365 | 15 | vkDestroyQueryPool(device_, pool_, nullptr); | |
| 366 | 15 | } | |
| 367 | |||
| 368 | 15 | void CompactionQuery::Record(VkCommandBuffer commands, | |
| 369 | std::span<const AccelerationStructure* const> built) { | ||
| 370 | 15 | if (built.size() > capacity_) { | |
| 371 | ✗ | throw std::invalid_argument("A compaction query holds " + std::to_string(capacity_) + | |
| 372 | ✗ | " sizes, not " + std::to_string(built.size())); | |
| 373 | } | ||
| 374 | 15 | std::vector<VkAccelerationStructureKHR> handles; | |
| 375 | 40 | for (const AccelerationStructure* structure : built) { | |
| 376 | 25 | if (!structure->Sizes().flags.compactable) { | |
| 377 | ✗ | throw std::invalid_argument(structure->Name() + " was not built to be compacted"); | |
| 378 | } | ||
| 379 | 25 | handles.push_back(structure->Handle()); | |
| 380 | } | ||
| 381 | 15 | count_ = static_cast<std::uint32_t>(handles.size()); | |
| 382 | 15 | vkCmdResetQueryPool(commands, pool_, 0, capacity_); | |
| 383 | 15 | if (count_ > 0) { | |
| 384 | 15 | vkCmdWriteAccelerationStructuresPropertiesKHR( | |
| 385 | 15 | commands, count_, handles.data(), | |
| 386 | VK_QUERY_TYPE_ACCELERATION_STRUCTURE_COMPACTED_SIZE_KHR, pool_, 0); | ||
| 387 | } | ||
| 388 | 15 | } | |
| 389 | |||
| 390 | 6 | std::optional<std::vector<std::uint64_t>> CompactionQuery::Sizes() const { | |
| 391 | // Each size followed by its availability. | ||
| 392 | 6 | std::vector<std::uint64_t> results(2 * std::size_t{count_}); | |
| 393 | 6 | const VkResult result = | |
| 394 | 6 | count_ == 0 | |
| 395 | 6 | ? VK_SUCCESS | |
| 396 | 6 | : vkGetQueryPoolResults(device_, pool_, 0, count_, | |
| 397 | 6 | results.size() * sizeof(std::uint64_t), results.data(), | |
| 398 | 2 * sizeof(std::uint64_t), | ||
| 399 | 12 | VK_QUERY_RESULT_64_BIT | VK_QUERY_RESULT_WITH_AVAILABILITY_BIT); | |
| 400 | 6 | if (result < VK_SUCCESS) { | |
| 401 | ✗ | CheckVk(result, "Reading compacted sizes"); | |
| 402 | } | ||
| 403 | 6 | std::vector<std::uint64_t> sizes; | |
| 404 | 14 | for (std::uint32_t i = 0; i < count_; ++i) { | |
| 405 | 8 | if (results[2 * i + 1] == 0) { | |
| 406 | ✗ | return std::nullopt; | |
| 407 | } | ||
| 408 | 8 | sizes.push_back(results[2 * i]); | |
| 409 | } | ||
| 410 | 6 | return sizes; | |
| 411 | 6 | } | |
| 412 | |||
| 413 | 8 | void RecordCompaction(VkCommandBuffer commands, const AccelerationStructure& source, | |
| 414 | const AccelerationStructure& compacted) { | ||
| 415 | 8 | const VkCopyAccelerationStructureInfoKHR copy{ | |
| 416 | .sType = VK_STRUCTURE_TYPE_COPY_ACCELERATION_STRUCTURE_INFO_KHR, | ||
| 417 | 8 | .src = source.Handle(), | |
| 418 | 8 | .dst = compacted.Handle(), | |
| 419 | 8 | .mode = VK_COPY_ACCELERATION_STRUCTURE_MODE_COMPACT_KHR}; | |
| 420 | 8 | vkCmdCopyAccelerationStructureKHR(commands, ©); | |
| 421 | 8 | } | |
| 422 | |||
| 423 | } // namespace ernie | ||
| 424 |