tess 0.4.0
Performance-first tile and path simulation substrate
Loading...
Searching...
No Matches
descriptors.h
1#pragma once
2
3#include <tess/core/shape.h>
4#include <tess/storage/chunk_page.h>
5
6#include <cstddef>
7#include <cstdint>
8#include <type_traits>
9
10// M13 GPU descriptors: the plain data a compile-time-polymorphic backend
11// consumes, derived from the field schema and chunk layout. Interface
12// only in the current pre-1.0 surface -- no backend implementation ships,
13// CPU stays authoritative
14// for gameplay state, and GPU products are derived/cached/versioned by a
15// future backend without redesigning core (the acceptance bar of the
16// gpu-backend-interface TDD). Nothing here touches a GPU API or adds a
17// dependency; descriptors are byte-level facts about storage tess
18// already owns.
19namespace tess::gpu {
20
26enum class GpuFieldFormat : std::uint8_t {
27 U8,
28 U16,
29 U32,
30 U64,
31 I8,
32 I16,
33 I32,
34 I64,
35 F32,
36};
37
38namespace detail {
39
40template <typename Value>
41[[nodiscard]] constexpr auto field_format() noexcept -> GpuFieldFormat {
42 // Integral and float32 only: `double` would otherwise fall through the
43 // is_signed branch into a lying I64 descriptor. Widen the enum before
44 // widening this gate.
45 static_assert(std::is_integral_v<Value> || std::is_same_v<Value, float>,
46 "GPU mirrors support integral and float32 field values only");
47 if constexpr (std::is_same_v<Value, float>) {
48 return GpuFieldFormat::F32;
49 } else if constexpr (std::is_signed_v<Value>) {
50 if constexpr (sizeof(Value) == 1) {
51 return GpuFieldFormat::I8;
52 } else if constexpr (sizeof(Value) == 2) {
53 return GpuFieldFormat::I16;
54 } else if constexpr (sizeof(Value) == 4) {
55 return GpuFieldFormat::I32;
56 } else {
57 return GpuFieldFormat::I64;
58 }
59 } else {
60 if constexpr (sizeof(Value) == 1) {
61 return GpuFieldFormat::U8;
62 } else if constexpr (sizeof(Value) == 2) {
63 return GpuFieldFormat::U16;
64 } else if constexpr (sizeof(Value) == 4) {
65 return GpuFieldFormat::U32;
66 } else {
67 return GpuFieldFormat::U64;
68 }
69 }
70}
71
72} // namespace detail
73
82 std::uint32_t field_index = 0;
83 GpuFieldFormat format = GpuFieldFormat::U8;
84 std::uint32_t value_bytes = 0;
85 std::uint64_t tiles_per_chunk = 0;
86 std::uint64_t bytes_per_chunk = 0;
87 std::uint64_t chunk_count = 0;
88
89 [[nodiscard]] constexpr auto total_bytes() const noexcept -> std::uint64_t {
90 return bytes_per_chunk * chunk_count;
91 }
92
93 friend constexpr auto operator==(const FieldMirrorDesc&,
94 const FieldMirrorDesc&) noexcept
95 -> bool = default;
96};
97
99template <typename World, typename Tag>
100[[nodiscard]] constexpr auto field_mirror_desc() noexcept -> FieldMirrorDesc {
101 using Schema = typename World::schema_type;
102 using Value = typename Schema::template value_type<Tag>;
104 // ShapeTraits deliberately does not bound total field bytes (sparse
105 // worlds may span trillions of chunks), so the dense-mirror byte
106 // counts this descriptor promises must be proven to fit u64 here --
107 // a wrapped total_bytes()/buffer_offset would be the exact lie this
108 // layer exists to prevent. Shapes whose dense mirror cannot be
109 // described fail to compile instead.
110 constexpr auto kMaxBytes = ~std::uint64_t{0};
111 static_assert(sizeof(Value) <= kMaxBytes / Traits::local_tile_count,
112 "per-chunk mirror bytes must fit std::uint64_t");
113 static_assert(
114 Traits::chunk_count <= 1 || Traits::local_tile_count * sizeof(Value) <=
115 kMaxBytes / Traits::chunk_count,
116 "chunk-key-major mirror byte size must fit std::uint64_t");
117 FieldMirrorDesc desc;
118 desc.field_index = static_cast<std::uint32_t>(Schema::template index<Tag>);
119 desc.format = detail::field_format<Value>();
120 desc.value_bytes = static_cast<std::uint32_t>(sizeof(Value));
121 desc.tiles_per_chunk = Traits::local_tile_count;
122 desc.bytes_per_chunk = Traits::local_tile_count * sizeof(Value);
123 desc.chunk_count = Traits::chunk_count;
124 return desc;
125}
126
134 ChunkKey chunk_key{};
135 std::uint32_t field_index = 0;
136 std::uint64_t buffer_offset = 0;
137 std::uint64_t byte_size = 0;
138 const void* data = nullptr;
139};
140
146template <typename Tag, typename World>
147[[nodiscard]] auto upload_desc(const World& world, ChunkKey chunk_key) noexcept
148 -> UploadDesc {
149 const auto span = world.template field_span<Tag>(chunk_key);
150 constexpr auto desc = field_mirror_desc<World, Tag>();
151 UploadDesc upload;
152 upload.chunk_key = chunk_key;
153 upload.field_index = desc.field_index;
154 upload.buffer_offset = chunk_key.value * desc.bytes_per_chunk;
155 upload.byte_size = span.size_bytes();
156 upload.data = span.data();
157 return upload;
158}
159
162 std::uint64_t product_key = 0;
163 std::uint32_t input_field_index = 0;
164 std::uint64_t chunk_count = 0;
165 std::uint32_t workgroups_per_chunk = 1;
166};
167
169enum class ReadbackPolicy : std::uint8_t {
170 None,
171 Summary,
172 SelectedTiles,
173 SelectedPath,
174 // Debug/explicit only.
175 FullField,
176};
177
180 std::uint64_t product_key = 0;
181 ReadbackPolicy policy = ReadbackPolicy::None;
182 std::uint64_t byte_size = 0;
183};
184
185} // namespace tess::gpu
Definition world.h:22
Definition shape.h:67
Definition shape.h:244
Definition descriptors.h:161
Definition descriptors.h:81
Definition descriptors.h:179
Definition descriptors.h:133