38 using shape_type =
Shape;
39 using schema_type = Schema;
43 static constexpr std::uint64_t chunk_count = ShapeTraits<Shape>::chunk_count;
44 static constexpr std::uint64_t local_tile_count =
45 ShapeTraits<Shape>::local_tile_count;
46 static constexpr std::size_t field_count = Schema::field_count;
49 static constexpr std::size_t page_byte_size = page_type::byte_size;
50 static constexpr std::size_t storage_byte_size =
51 static_cast<std::size_t
>(chunk_count) * page_byte_size;
53 static_assert(chunk_count <= static_cast<std::uint64_t>(
54 std::numeric_limits<std::size_t>::max()),
55 "AlwaysResident World chunk count must fit std::size_t.");
56 static_assert(page_byte_size <= std::numeric_limits<std::size_t>::max() /
57 static_cast<std::size_t
>(chunk_count),
58 "AlwaysResident World storage bytes must fit std::size_t.");
62 pages_.reserve(
static_cast<std::size_t
>(chunk_count));
63 metadata_.reserve(
static_cast<std::size_t
>(chunk_count));
64 for (std::uint64_t key = 0; key < chunk_count; ++key) {
66 pages_.emplace_back(
chunk, chunk_coord<Shape>(
chunk));
67 metadata_.emplace_back();
69 dirty_masks_.assign(
static_cast<std::size_t
>(chunk_count),
DirtyMask{});
70 active_masks_.assign(
static_cast<std::size_t
>(chunk_count),
ActiveMask{});
71 dirty_bounds_.assign(
static_cast<std::size_t
>(chunk_count),
Box3{});
75 [[nodiscard]]
auto chunks() noexcept -> std::span<page_type> {
76 return {pages_.data(), pages_.size()};
80 [[nodiscard]]
auto chunks() const noexcept -> std::span<const page_type> {
81 return {pages_.data(), pages_.size()};
91 template <
typename Tag>
92 void fill_field(
const Schema::template value_type<Tag>& value)
noexcept {
93 for (
auto& page : pages_) {
105 TESS_ASSERT(key.value < chunk_count);
106 return pages_[
static_cast<std::size_t
>(key.value)];
111 TESS_ASSERT(key.value < chunk_count);
112 return pages_[
static_cast<std::size_t
>(key.value)];
120 return chunk(chunk_key<Shape>(coord));
125 ->
const page_type& {
126 return chunk(chunk_key<Shape>(coord));
131 if (key.value >= chunk_count) {
139 ->
const page_type* {
140 if (key.value >= chunk_count) {
148 if (!contains_chunk(coord)) {
151 return &
chunk(coord);
156 ->
const page_type* {
157 if (!contains_chunk(coord)) {
160 return &
chunk(coord);
168 TESS_ASSERT(key.value < chunk_count);
169 return metadata_[
static_cast<std::size_t
>(key.value)];
173 TESS_ASSERT(key.value < chunk_count);
174 return metadata_[
static_cast<std::size_t
>(key.value)];
177 [[nodiscard]]
auto meta(ChunkCoord3 coord)
noexcept -> ChunkMeta& {
178 return meta(chunk_key<Shape>(coord));
181 [[nodiscard]]
auto meta(ChunkCoord3 coord)
const noexcept
182 ->
const ChunkMeta& {
183 return meta(chunk_key<Shape>(coord));
188 if (key.value >= chunk_count) {
195 if (key.value >= chunk_count) {
201 [[nodiscard]]
auto try_meta(ChunkCoord3 coord)
noexcept -> ChunkMeta* {
202 if (!contains_chunk(coord)) {
208 [[nodiscard]]
auto try_meta(ChunkCoord3 coord)
const noexcept
209 ->
const ChunkMeta* {
210 if (!contains_chunk(coord)) {
216 [[nodiscard]]
auto chunk_activity(ChunkKey key)
const noexcept
218 return active_mask(key).empty() ? ChunkActivity::Sleeping
219 : ChunkActivity::Active;
222 [[nodiscard]]
auto chunk_activity(ChunkCoord3 coord)
const noexcept
224 return chunk_activity(chunk_key<Shape>(coord));
227 [[nodiscard]]
auto active_category_count(ChunkKey key)
const noexcept
229 return detail::popcount(active_mask(key));
234 [[nodiscard]]
auto dirty_mask(ChunkKey key)
const noexcept -> DirtyMask {
235 TESS_ASSERT(key.value < chunk_count);
236 return dirty_masks_[
static_cast<std::size_t
>(key.value)];
239 [[nodiscard]]
auto active_mask(ChunkKey key)
const noexcept -> ActiveMask {
240 TESS_ASSERT(key.value < chunk_count);
241 return active_masks_[
static_cast<std::size_t
>(key.value)];
244 [[nodiscard]]
auto dirty_bounds(ChunkKey key)
const noexcept -> Box3 {
245 TESS_ASSERT(key.value < chunk_count);
246 return dirty_bounds_[
static_cast<std::size_t
>(key.value)];
249 void mark_dirty(ChunkKey key, DirtyMask mask, Box3 bounds)
noexcept {
250 TESS_ASSERT(key.value < chunk_count);
251 const auto slot =
static_cast<std::size_t
>(key.value);
252 detail::meta_mark_dirty(dirty_masks_[slot], dirty_bounds_[slot], meta(key),
269 TESS_ASSERT(key.value < chunk_count);
270 detail::meta_mark_content_changed(
meta(key));
277 mark_dirty(key, mask, bounds);
278 ++meta(key).topology_version;
281 void mark_topology_rebuilt(ChunkKey key)
noexcept {
282 ++meta(key).topology_version;
285 void clear_dirty(ChunkKey key, DirtyMask mask)
noexcept {
286 TESS_ASSERT(key.value < chunk_count);
287 const auto slot =
static_cast<std::size_t
>(key.value);
288 detail::meta_clear_dirty(dirty_masks_[slot], dirty_bounds_[slot], mask);
291 [[nodiscard]]
auto observe_dirty(ChunkKey key, DirtyMask mask)
const noexcept
292 -> DirtyObservation {
293 TESS_ASSERT(key.value < chunk_count);
294 const auto slot =
static_cast<std::size_t
>(key.value);
295 return detail::meta_observe_dirty(dirty_masks_[slot], dirty_bounds_[slot],
303 bool clear_dirty_observed(ChunkKey key, DirtyObservation observed)
noexcept {
304 TESS_ASSERT(key.value < chunk_count);
305 const auto slot =
static_cast<std::size_t
>(key.value);
306 return detail::meta_clear_dirty_observed(
307 dirty_masks_[slot], dirty_bounds_[slot], meta(key), observed);
310 void mark_active(ChunkKey key, ActiveMask mask)
noexcept {
311 TESS_ASSERT(key.value < chunk_count);
312 const auto slot =
static_cast<std::size_t
>(key.value);
313 detail::meta_mark_active(active_masks_[slot], mask);
316 void clear_active(ChunkKey key, ActiveMask mask)
noexcept {
317 TESS_ASSERT(key.value < chunk_count);
318 const auto slot =
static_cast<std::size_t
>(key.value);
319 detail::meta_clear_active(active_masks_[slot], mask);
329 collect_matching_chunks(mask, dirty_masks_, out);
338 std::vector<ChunkKey>& out)
const {
339 collect_matching_chunks(mask, active_masks_, out);
344 -> std::vector<ChunkKey> {
345 std::vector<ChunkKey>
chunks;
352 -> std::vector<ChunkKey> {
353 std::vector<ChunkKey>
chunks;
364 TESS_ASSERT(contains<Shape>(coord));
365 const auto chunk_coord_value = chunk_coord<Shape>(coord);
367 chunk_key<Shape>(chunk_coord_value),
368 local_tile_id<Shape>(local_coord<Shape>(coord)),
374 -> std::optional<ResolvedTile<Shape>> {
375 if (!contains<Shape>(coord)) {
385 template <
typename Tag>
387 -> Schema::template value_type<Tag>& {
388 const auto resolved =
resolve(coord);
389 return chunk(resolved.chunk_key)
394 template <
typename Tag>
396 ->
const Schema::template value_type<Tag>& {
397 const auto resolved =
resolve(coord);
398 return chunk(resolved.chunk_key)
406 template <
typename Tag>
408 -> Schema::template value_type<Tag>* {
410 if (!resolved.has_value()) {
413 return &
chunk(resolved->chunk_key)
414 .template
field<Tag>(resolved->local_tile_id);
418 template <
typename Tag>
420 ->
const Schema::template value_type<Tag>* {
422 if (!resolved.has_value()) {
425 return &
chunk(resolved->chunk_key)
426 .template
field<Tag>(resolved->local_tile_id);
433 template <
typename Tag>
439 template <
typename Tag>
445 static constexpr bool contains_chunk(
ChunkCoord3 coord)
noexcept {
447 return coord.x < Traits::chunk_count_x && coord.y < Traits::chunk_count_y &&
448 coord.z < Traits::chunk_count_z;
453 template <
typename Mask>
454 void collect_matching_chunks(Mask mask,
const std::vector<Mask>& column,
455 std::vector<ChunkKey>& out)
const {
456 for (std::uint64_t key = 0; key < chunk_count; ++key) {
457 if (
static_cast<bool>(column[
static_cast<std::size_t
>(key)] & mask)) {
458 out.push_back(ChunkKey{key});
463 std::vector<page_type> pages_;
464 std::vector<ChunkMeta> metadata_;
465 std::vector<DirtyMask> dirty_masks_;
466 std::vector<ActiveMask> active_masks_;
467 std::vector<Box3> dirty_bounds_;
Definition chunk_page.h:123