3#include <tess/core/capacity.h>
4#include <tess/core/config.h>
5#include <tess/core/fail_fast.h>
6#include <tess/core/shape.h>
7#include <tess/storage/world.h>
27enum class WritePolicy : std::uint8_t {
33static_assert(
sizeof(WritePolicy) ==
sizeof(std::uint8_t));
36[[nodiscard]]
constexpr bool is_valid_write_policy(
37 WritePolicy policy)
noexcept {
39 case WritePolicy::ReadOnly:
40 case WritePolicy::UniquePerTile:
41 case WritePolicy::UniquePerChunk:
42 case WritePolicy::Unsafe:
51 BlockScratch() =
default;
53 BlockScratch(BlockScratch&& other) noexcept
54 : storage_(std::move(other.storage_)),
55 capacity_bytes_(std::exchange(other.capacity_bytes_, 0)),
56 used_bytes_(std::exchange(other.used_bytes_, 0)) {}
58 auto operator=(BlockScratch&& other)
noexcept -> BlockScratch& {
59 storage_ = std::move(other.storage_);
60 capacity_bytes_ = std::exchange(other.capacity_bytes_, 0);
61 used_bytes_ = std::exchange(other.used_bytes_, 0);
65 BlockScratch(
const BlockScratch&) =
delete;
66 auto operator=(
const BlockScratch&) -> BlockScratch& =
delete;
68 ~BlockScratch() =
default;
73 [[nodiscard]]
auto reserve_bytes_checked(std::size_t bytes) -> ReserveStatus {
74 const auto word_count =
75 bytes / word_size + (bytes % word_size == 0 ? 0 : 1);
76 if (word_count > std::numeric_limits<std::size_t>::max() / word_size) {
77 return ReserveStatus::CapacityExceeded;
79 const auto byte_capacity = word_count * word_size;
80 if (byte_capacity > capacity_bytes_) {
84 storage_ = std::make_unique_for_overwrite<std::byte[]>(byte_capacity);
85 capacity_bytes_ = byte_capacity;
87 return ReserveStatus::Reserved;
93 void reserve_bytes(std::size_t bytes) {
94 if (reserve_bytes_checked(bytes) == ReserveStatus::Reserved) {
97#if TESS_HAS_EXCEPTIONS
98 throw std::bad_alloc{};
100 detail::fail_fast(
"BlockScratch capacity exceeded");
104 constexpr void reset()
noexcept { used_bytes_ = 0; }
106 [[nodiscard]]
constexpr auto capacity_bytes()
const noexcept -> std::size_t {
107 return capacity_bytes_;
110 [[nodiscard]]
constexpr auto used_bytes()
const noexcept -> std::size_t {
114 [[nodiscard]]
constexpr auto remaining_bytes()
const noexcept -> std::size_t {
115 return capacity_bytes() - used_bytes_;
118 template <
typename T>
119 [[nodiscard]]
auto allocate(std::size_t count)
noexcept -> std::span<T> {
120 static_assert(!std::is_void_v<T>,
121 "BlockScratch::allocate<T> requires an object type");
123 alignof(T) <=
alignof(std::max_align_t),
124 "BlockScratch::allocate<T> does not support over-aligned types");
126 std::is_trivially_default_constructible_v<T>,
127 "BlockScratch::allocate<T> requires T to be trivially default "
129 static_assert(std::is_trivially_destructible_v<T>,
130 "BlockScratch::allocate<T> requires T to be trivially "
136 if (count > std::numeric_limits<std::size_t>::max() /
sizeof(T)) {
140 const auto byte_count = count *
sizeof(T);
141 const auto aligned_offset = align_offset(used_bytes_,
alignof(T));
142 if (aligned_offset > capacity_bytes() ||
143 byte_count > capacity_bytes() - aligned_offset) {
151 std::launder(
reinterpret_cast<T*
>(storage_.get() + aligned_offset));
152 used_bytes_ = aligned_offset + byte_count;
153 return std::span<T>{ptr, count};
157 static constexpr auto word_size =
sizeof(std::max_align_t);
161 static_assert(__STDCPP_DEFAULT_NEW_ALIGNMENT__ >=
alignof(std::max_align_t));
163 [[nodiscard]]
static constexpr auto align_offset(
164 std::size_t offset, std::size_t alignment)
noexcept -> std::size_t {
165 const auto remainder = offset % alignment;
166 if (remainder == 0) {
169 return offset + (alignment - remainder);
172 std::unique_ptr<std::byte[]> storage_;
173 std::size_t capacity_bytes_ = 0;
174 std::size_t used_bytes_ = 0;
180 constexpr void record_scratch_allocation_failure()
noexcept {
181 ++scratch_allocation_failures_;
184 constexpr void reset()
noexcept { scratch_allocation_failures_ = 0; }
186 [[nodiscard]]
constexpr auto scratch_allocation_failures()
const noexcept
188 return scratch_allocation_failures_;
192 std::size_t scratch_allocation_failures_ = 0;
198 constexpr ChunkDomain()
noexcept =
default;
200 constexpr explicit ChunkDomain(std::span<const ChunkKey> keys) noexcept
203 [[nodiscard]]
constexpr auto keys()
const noexcept
204 -> std::span<const ChunkKey> {
208 [[nodiscard]]
constexpr auto begin()
const noexcept {
return keys_.begin(); }
210 [[nodiscard]]
constexpr auto end()
const noexcept {
return keys_.end(); }
212 [[nodiscard]]
constexpr auto size()
const noexcept -> std::size_t {
216 [[nodiscard]]
constexpr bool empty()
const noexcept {
return keys_.empty(); }
219 std::span<const ChunkKey> keys_;
223class OwnedChunkDomain {
225 OwnedChunkDomain() =
default;
227 explicit OwnedChunkDomain(std::vector<ChunkKey> keys)
228 : keys_(std::move(keys)) {}
238 [[nodiscard]]
constexpr auto view()
const&
noexcept ->
ChunkDomain {
243 [[nodiscard]]
constexpr auto keys()
const&
noexcept
244 -> std::span<const ChunkKey> {
247 auto keys()
const&& -> std::span<const ChunkKey> =
delete;
249 [[nodiscard]]
constexpr auto begin()
const&
noexcept {
return keys_.begin(); }
250 auto begin()
const&& =
delete;
252 [[nodiscard]]
constexpr auto end()
const&
noexcept {
return keys_.end(); }
253 auto end()
const&& =
delete;
255 [[nodiscard]]
constexpr auto size()
const noexcept -> std::size_t {
259 [[nodiscard]]
constexpr bool empty()
const noexcept {
return keys_.empty(); }
262 std::vector<ChunkKey> keys_;
266[[nodiscard]]
constexpr auto chunk_domain(
267 std::span<const ChunkKey> keys)
noexcept ->
ChunkDomain {
272[[nodiscard]]
constexpr auto chunk_domain(
const OwnedChunkDomain& keys)
noexcept
289[[nodiscard]]
inline auto explicit_chunk_domain(std::span<const ChunkKey> keys)
291 std::vector<ChunkKey> domain{keys.begin(), keys.end()};
292 std::sort(domain.begin(), domain.end(),
306inline auto sorted_domain(std::vector<ChunkKey> keys) -> OwnedChunkDomain {
307 std::sort(keys.begin(), keys.end(),
308 [](ChunkKey lhs, ChunkKey rhs) { return lhs.value < rhs.value; });
309 return OwnedChunkDomain{std::move(keys)};
314template <
typename World>
316[[nodiscard]]
auto dirty_chunk_domain(
const World& world,
DirtyMask mask)
318 return detail::sorted_domain(world.dirty_chunks(mask));
321template <
typename World>
323[[nodiscard]]
auto active_chunk_domain(
const World& world,
ActiveMask mask)
325 return detail::sorted_domain(world.active_chunks(mask));
328template <
typename World>
332 using world_type = std::remove_reference_t<World>;
333 using mutable_world_type = std::remove_cv_t<world_type>;
334 using shape_type = mutable_world_type::shape_type;
336 std::conditional_t<std::is_const_v<world_type>,
337 const typename mutable_world_type::page_type,
338 typename mutable_world_type::page_type>;
339 using meta_type = std::conditional_t<std::is_const_v<world_type>,
342 constexpr ChunkView(world_type& world,
ChunkKey key) noexcept
343 : page_(&world.chunk(key)),
344 meta_(&world.meta(key)),
346 coord_(chunk_coord<shape_type>(key)),
347 bounds_(chunk_bounds(coord_)) {}
349 [[nodiscard]]
constexpr auto page()
const noexcept -> page_type& {
353 [[nodiscard]]
constexpr auto meta()
const noexcept -> meta_type& {
357 [[nodiscard]]
constexpr auto key()
const noexcept ->
ChunkKey {
return key_; }
359 [[nodiscard]]
constexpr auto coord()
const noexcept ->
ChunkCoord3 {
363 [[nodiscard]]
constexpr auto bounds()
const noexcept ->
Box3 {
367 [[nodiscard]]
static constexpr auto local_bounds()
noexcept ->
Box3 {
368 return Box3{
Coord3{0, 0, 0}, ShapeTraits<shape_type>::chunk};
371 [[nodiscard]]
static constexpr bool contains_local(
Coord3 coord)
noexcept {
372 return tess::contains(local_bounds(), coord);
375 [[nodiscard]]
static constexpr auto try_local_coord(
Coord3 coord)
noexcept
376 -> std::optional<LocalCoord3> {
377 if (!contains_local(coord)) {
382 static_cast<std::uint64_t
>(coord.x),
383 static_cast<std::uint64_t
>(coord.y),
384 static_cast<std::uint64_t
>(coord.z),
388 [[nodiscard]]
static constexpr auto local_coord(
LocalTileId id)
noexcept
390 const auto chunk = ShapeTraits<shape_type>::chunk;
391 const auto local_xy = chunk.x * chunk.y;
392 const auto local_z =
id.value / local_xy;
393 const auto remainder =
id.value % local_xy;
394 const auto local_y = remainder / chunk.x;
395 const auto local_x = remainder % chunk.x;
400 [[nodiscard]]
static constexpr auto local_tile_id(
LocalCoord3 coord)
noexcept
402 return tess::local_tile_id<shape_type>(coord);
413 [[nodiscard]]
static constexpr bool is_boundary(
LocalCoord3 coord)
noexcept {
414 const auto chunk = ShapeTraits<shape_type>::chunk;
415 return (chunk.x > 1 && (coord.x == 0 || coord.x + 1 == chunk.x)) ||
416 (chunk.y > 1 && (coord.y == 0 || coord.y + 1 == chunk.y)) ||
417 (chunk.z > 1 && (coord.z == 0 || coord.z + 1 == chunk.z));
420 [[nodiscard]]
static constexpr bool is_interior(
LocalCoord3 coord)
noexcept {
421 return !is_boundary(coord);
424 [[nodiscard]]
constexpr auto world_coord(
426 const auto chunk = ShapeTraits<shape_type>::chunk;
428 static_cast<std::int64_t
>(coord_.x * chunk.x) + local_candidate.x,
429 static_cast<std::int64_t
>(coord_.y * chunk.y) + local_candidate.y,
430 static_cast<std::int64_t
>(coord_.z * chunk.z) + local_candidate.z,
434 [[nodiscard]]
constexpr auto world_coord(
LocalCoord3 coord)
const noexcept
436 return world_coord(
Coord3{
437 static_cast<std::int64_t
>(coord.x),
438 static_cast<std::int64_t
>(coord.y),
439 static_cast<std::int64_t
>(coord.z),
443 [[nodiscard]]
constexpr auto world_coord(
LocalTileId id)
const noexcept
445 return world_coord(local_coord(
id));
448 template <
typename Fn>
449 constexpr void for_each_tile(Fn&& fn)
const {
450 for (std::uint64_t i = 0; i < ShapeTraits<shape_type>::local_tile_count;
453 std::invoke(fn,
id, local_coord(
id));
457 template <
typename Tag>
458 [[nodiscard]]
constexpr auto field_span()
const noexcept {
459 return page_->template field_span<Tag>();
463 [[nodiscard]]
static constexpr auto chunk_bounds(
ChunkCoord3 coord)
noexcept
465 const auto chunk = ShapeTraits<shape_type>::chunk;
468 static_cast<std::int64_t
>(coord.x * chunk.x),
469 static_cast<std::int64_t
>(coord.y * chunk.y),
470 static_cast<std::int64_t
>(coord.z * chunk.z),
483template <
typename World, WritePolicy Policy>
487 static_assert(is_valid_write_policy(Policy));
489 using world_type = std::remove_reference_t<World>;
490 using view_world_type =
491 std::conditional_t<Policy == WritePolicy::ReadOnly,
492 const std::remove_const_t<world_type>, world_type>;
494 constexpr BlockCtx(world_type& world,
ChunkDomain domain,
500 diagnostics_(diagnostics) {}
502 [[nodiscard]]
constexpr auto world()
const noexcept -> view_world_type& {
506 [[nodiscard]]
constexpr auto domain()
const noexcept ->
ChunkDomain {
510 [[nodiscard]]
constexpr auto policy()
const noexcept -> WritePolicy {
514 [[nodiscard]]
constexpr auto size()
const noexcept -> std::size_t {
515 return domain_.size();
518 [[nodiscard]]
constexpr bool empty()
const noexcept {
519 return domain_.empty();
522 [[nodiscard]]
constexpr auto scratch()
noexcept ->
BlockScratch* {
526 [[nodiscard]]
constexpr auto scratch()
const noexcept ->
const BlockScratch* {
530 constexpr void reset_scratch()
const noexcept {
531 if (scratch_ !=
nullptr) {
540 [[nodiscard]]
constexpr auto diagnostics()
const noexcept
545 constexpr void reset_diagnostics()
const noexcept {
546 if (diagnostics_ !=
nullptr) {
547 diagnostics_->reset();
551 [[nodiscard]]
constexpr auto chunk_view(
ChunkKey key)
const noexcept
556 template <
typename Fn>
557 constexpr void for_each_chunk(Fn&& fn)
const {
558 for (
const auto key : domain_) {
559 std::invoke(fn, chunk_view(key));
570template <WritePolicy Policy,
typename World>
572[[nodiscard]]
constexpr auto block_ctx(
World& world,
578template <WritePolicy Policy,
typename World>
586template <WritePolicy Policy,
typename World>
594template <WritePolicy Policy,
typename World>
603template <WritePolicy Policy,
typename World,
typename Fn>
606 block_ctx<Policy>(world, domain).for_each_chunk(std::forward<Fn>(fn));
611template <WritePolicy Policy,
typename World,
typename Fn>
612constexpr void for_each_chunk_policy_view(World& world, ChunkDomain domain,
614 using world_type = std::remove_reference_t<World>;
615 using view_world_type =
616 std::conditional_t<Policy == WritePolicy::ReadOnly,
617 const std::remove_const_t<world_type>, world_type>;
619 if constexpr (std::is_invocable_v<Fn&, ChunkView<view_world_type>>) {
620 for (
const auto key : domain) {
621 std::invoke(fn, ChunkView<view_world_type>{world, key});
636 fail_fast(
"callback cannot accept the selected block policy view");
642template <
typename World,
typename Fn>
645 WritePolicy policy, Fn&& fn) {
646 assert(is_valid_write_policy(policy));
648 case WritePolicy::ReadOnly:
649 detail::for_each_chunk_policy_view<WritePolicy::ReadOnly>(
650 world, domain, std::forward<Fn>(fn));
652 case WritePolicy::UniquePerTile:
653 detail::for_each_chunk_policy_view<WritePolicy::UniquePerTile>(
654 world, domain, std::forward<Fn>(fn));
656 case WritePolicy::UniquePerChunk:
657 detail::for_each_chunk_policy_view<WritePolicy::UniquePerChunk>(
658 world, domain, std::forward<Fn>(fn));
660 case WritePolicy::Unsafe:
661 detail::for_each_chunk_policy_view<WritePolicy::Unsafe>(
662 world, domain, std::forward<Fn>(fn));
Definition metadata_types.h:49
Definition metadata_types.h:12