Files
IfcOpenShell/src/ifcviewer/ChunkPlanner.h
T
Dion Moult 1fe4570860 ifcviewer: extract ChunkPlanner + InstanceCompose; add Tier-1 test trio
The chunk planner (Morton sort + greedy pack) and instance composition
(federation × placement matrix chain + world-AABB derive) were inline
helpers in ViewportWindow.cpp. Pulled both out as free-function modules
so the math + lookup logic can be exercised without a Qt window or a
wgpu device. ViewportWindow now delegates; InstanceLookup is a using-
alias to InstanceCompose::InstanceLookup.

Also added an addSubBufferForTesting / clearSubPoolsForTesting seam to
BufferPool so the sub-allocator invariants can be pinned with fake
WGPUBuffer handles. The fakes are never dereferenced; the guard drops
the sub-pools before destructor would call wgpuBufferRelease.

Three new test binaries under src/ifcviewer/tests/, 33 cases / 173
assertions: BufferPool first-fit + alignment + coalescing + multi-
sub-pool isolation; ChunkPlanner Morton split / interleave / stable
sort / greedy-pack monotonicity and single-mesh-oversize; InstanceCompose
identity / translation / order-of-multiplication / large-placement
cancellation against federation false origin / column-major writeback /
findInstance lookup paths.
2026-06-04 17:19:24 +10:00

86 lines
4.3 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/********************************************************************************
* *
* This file is part of IfcOpenShell. *
* *
* IfcOpenShell is free software: you can redistribute it and/or modify *
* it under the terms of the Lesser GNU General Public License as published by *
* the Free Software Foundation, either version 3.0 of the License, or *
* (at your option) any later version. *
* *
* IfcOpenShell is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* Lesser GNU General Public License for more details. *
* *
* You should have received a copy of the Lesser GNU General Public License *
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
* *
********************************************************************************/
#ifndef CHUNKPLANNER_H
#define CHUNKPLANNER_H
// Chunk-planning helpers for the wgpu viewport. Replaces a previous
// lexicographic (z, y, x) sort with a 3D Morton (Z-order) sort over mesh
// centroids, then greedy-packs the resulting order into chunks bounded by
// a vertex-bytes ceiling. The two passes are split so each is unit-
// testable in isolation (no Qt / no wgpu).
#include <cstddef>
#include <cstdint>
#include <vector>
namespace ChunkPlanner {
// Interleave the low 21 bits of v with two zero bits between each,
// returning bits at positions 0, 3, 6, ..., 60 — one axis of a
// standard 21-bit-per-axis 3D Morton code. ORing three of these
// shifted by 0, 1, 2 gives a 63-bit (x, y, z)-interleaved code; the
// resulting integer ordering puts spatially-close points close in
// the sorted sequence (the classic Z-order curve).
uint64_t mortonSplit21(uint32_t v);
uint64_t mortonCode3D(uint32_t x, uint32_t y, uint32_t z);
// Return a mesh-id permutation sorted by 3D Morton (Z-order) code over
// the meshes' centroids. Replaces a lexicographic (z, y, x) sort,
// which was effectively a 1D Z-slab traversal — chunks ended up
// spanning the whole XY extent of the model, ~50m × 50m × 0.5m for a
// typical building. Morton clusters spatially in all 3 axes, so each
// chunk's AABB becomes a tight 3D voxel — small enough that
// per-chunk frustum / contribution / HiZ rejection becomes meaningful
// (a 1km-wide AABB never gets occluded; a 10m voxel often does).
//
// Meshes with no instances get a Morton code of 0 and sink to the
// front; they contribute no geometry / AABBs so where they land in
// the chunk plan doesn't matter.
std::vector<uint32_t> sortMeshIdsByMorton(
std::size_t n_meshes,
const std::vector<float>& mesh_cx,
const std::vector<float>& mesh_cy,
const std::vector<float>& mesh_cz,
const std::vector<uint32_t>& mesh_inst_count);
// Greedy-pack a pre-sorted mesh-id sequence into chunks bounded by
// `chunk_vertex_bytes_limit`. Each mesh is placed in the current
// chunk; if adding it would push the running byte count over the
// limit (and the chunk is non-empty), a new chunk is started.
//
// A mesh whose own vertex bytes already exceed the limit lands alone
// in its own (over-sized) chunk — the planner never splits a mesh
// across chunks, because the mega-draw bookkeeping is per-mesh
// chunk-local-offset.
//
// `sorted_mesh_ids` is the order produced by sortMeshIdsByMorton.
// `mesh_vertex_count[mesh_id]` gives the vertex count per mesh.
// `vertex_stride_bytes` is the per-vertex byte size on the GPU.
std::vector<std::vector<uint32_t>> greedyPackChunks(
const std::vector<uint32_t>& sorted_mesh_ids,
const std::vector<uint32_t>& mesh_vertex_count,
uint64_t vertex_stride_bytes,
uint64_t chunk_vertex_bytes_limit);
} // namespace ChunkPlanner
#endif // CHUNKPLANNER_H