MayaFlux 1.0.0
Digital-First Multimedia Processing Framework
Loading...
Searching...
No Matches
SliceSet.cpp
Go to the documentation of this file.
1#include "SliceSet.hpp"
2
3#include "StagingUtils.hpp"
4
6
7namespace MayaFlux::Buffers {
8
10 : m_stride(stride_bytes)
11 , m_topology(topology)
12{
13 if (m_stride == 0) {
14 error<std::invalid_argument>(
17 std::source_location::current(),
18 "SliceSet: stride must be nonzero");
19 }
20}
21
23 std::span<const uint8_t> bytes,
24 std::optional<Portal::Graphics::PrimitiveTopology> topology)
25{
26 if (bytes.size() % m_stride != 0) {
28 "SliceSet::add: {} bytes is not a whole number of {} byte vertices",
29 bytes.size(), m_stride);
30 return 0;
31 }
32
33 const SliceId id = m_next_id++;
34 const size_t offset = m_mirror.size();
35
36 m_mirror.insert(m_mirror.end(), bytes.begin(), bytes.end());
37 m_slices.emplace(id, Slice { .byte_offset = offset, .byte_size = bytes.size(), .topology = topology.value_or(m_topology) });
38 m_memory_order.push_back(id);
39 m_draw_order.push_back(id);
40
41 mark_dirty(offset, m_mirror.size());
42 m_runs_changed = true;
43 return id;
44}
45
46void SliceSet::update(SliceId id, std::span<const uint8_t> bytes)
47{
48 auto it = m_slices.find(id);
49 if (it == m_slices.end() || bytes.size() % m_stride != 0) {
51 "SliceSet::update: unknown slice {} or {} bytes is not a whole number of {} byte vertices",
52 id, bytes.size(), m_stride);
53 return;
54 }
55
56 auto& slice = it->second;
57
58 if (bytes.size() == slice.byte_size) {
59 if (!bytes.empty()) {
60 std::memcpy(m_mirror.data() + slice.byte_offset, bytes.data(), bytes.size());
61 mark_dirty(slice.byte_offset, slice.byte_offset + bytes.size());
62 }
63 return;
64 }
65
66 const auto first = m_mirror.begin() + static_cast<std::ptrdiff_t>(slice.byte_offset);
67 m_mirror.erase(first, first + static_cast<std::ptrdiff_t>(slice.byte_size));
68 m_mirror.insert(m_mirror.begin() + static_cast<std::ptrdiff_t>(slice.byte_offset),
69 bytes.begin(), bytes.end());
70 slice.byte_size = bytes.size();
71
72 const auto position = std::ranges::find(m_memory_order, id);
73 repack_from(static_cast<size_t>(position - m_memory_order.begin()));
74 mark_dirty(slice.byte_offset, m_mirror.size());
75 m_runs_changed = true;
76}
77
79{
80 auto it = m_slices.find(id);
81 if (it == m_slices.end()) {
82 return;
83 }
84
85 const Slice slice = it->second;
86 const auto first = m_mirror.begin() + static_cast<std::ptrdiff_t>(slice.byte_offset);
87 m_mirror.erase(first, first + static_cast<std::ptrdiff_t>(slice.byte_size));
88
89 const auto position = std::ranges::find(m_memory_order, id);
90 const auto index = static_cast<size_t>(position - m_memory_order.begin());
92 std::erase(m_draw_order, id);
93 m_slices.erase(it);
94
96 mark_dirty(slice.byte_offset, m_mirror.size());
97 m_runs_changed = true;
98}
99
100void SliceSet::set_visible(SliceId id, bool visible)
101{
102 auto it = m_slices.find(id);
103 if (it == m_slices.end() || it->second.visible == visible) {
104 return;
105 }
106
107 it->second.visible = visible;
108 m_runs_changed = true;
109}
110
112{
113 if (!m_slices.contains(id) || m_draw_order.back() == id) {
114 return;
115 }
116
117 std::erase(m_draw_order, id);
118 m_draw_order.push_back(id);
119 m_runs_changed = true;
120}
121
123{
124 if (!m_slices.contains(id) || m_draw_order.front() == id) {
125 return;
126 }
127
128 std::erase(m_draw_order, id);
129 m_draw_order.insert(m_draw_order.begin(), id);
130 m_runs_changed = true;
131}
132
133uint32_t SliceSet::vertex_count() const noexcept
134{
135 return static_cast<uint32_t>(m_mirror.size() / m_stride);
136}
137
138std::optional<std::vector<Portal::Graphics::DrawRun>> SliceSet::take_runs()
139{
140 if (!m_runs_changed) {
141 return std::nullopt;
142 }
143 m_runs_changed = false;
144
145 std::vector<Portal::Graphics::DrawRun> runs;
146 for (const SliceId id : m_draw_order) {
147 const auto& slice = m_slices.at(id);
148 if (!slice.visible || slice.byte_size == 0) {
149 continue;
150 }
151
152 const auto offset = static_cast<uint32_t>(slice.byte_offset / m_stride);
153 const auto count = static_cast<uint32_t>(slice.byte_size / m_stride);
154
155 if (is_concatenable_topology(slice.topology) && !runs.empty()
156 && runs.back().topology == slice.topology
157 && runs.back().vertex_offset + runs.back().vertex_count == offset) {
158 runs.back().vertex_count += count;
159 continue;
160 }
161
162 runs.push_back({ .topology = slice.topology, .vertex_offset = offset, .vertex_count = count });
163 }
164
165 return runs;
166}
167
168void SliceSet::flush(const std::shared_ptr<VKBuffer>& target, std::shared_ptr<VKBuffer>& staging)
169{
170 if (!target) {
171 error<std::invalid_argument>(
174 std::source_location::current(),
175 "SliceSet::flush: target buffer is null");
176 }
177
178 const size_t capacity_before = target->get_size_bytes();
179 ensure_gpu_capacity(target, staging, m_mirror.size());
180 if (target->get_size_bytes() != capacity_before) {
181 m_all_dirty = true;
182 }
183
184 if (m_all_dirty) {
185 m_dirty.clear();
186 m_dirty.emplace_back(0, m_mirror.size());
187 m_all_dirty = false;
188 }
189
190 for (auto& [start, end] : m_dirty) {
191 end = std::min(end, m_mirror.size());
192 }
193
194 upload_intervals(m_mirror.data(), m_dirty, target, staging);
195 m_dirty.clear();
196}
197
198void SliceSet::repack_from(size_t memory_index)
199{
200 size_t offset = 0;
201 if (memory_index > 0) {
202 const auto& previous = m_slices.at(m_memory_order[memory_index - 1]);
203 offset = previous.byte_offset + previous.byte_size;
204 }
205
206 for (size_t i = memory_index; i < m_memory_order.size(); ++i) {
207 auto& slice = m_slices.at(m_memory_order[i]);
208 slice.byte_offset = offset;
209 offset += slice.byte_size;
210 }
211}
212
213void SliceSet::mark_dirty(size_t start, size_t end)
214{
215 if (start < end) {
216 m_dirty.emplace_back(start, end);
217 }
218}
219
220} // namespace MayaFlux::Buffers
#define MF_ERROR(comp, ctx,...)
uint64_t offset
Definition Composite.cpp:10
std::optional< glm::vec3 > position
Definition Decoder.cpp:93
PrimitiveTopology topology
uint32_t index
Definition VKDevice.cpp:122
size_t count
void remove(SliceId id)
Remove a block; later blocks shift down.
Definition SliceSet.cpp:78
void mark_dirty(size_t start, size_t end)
Definition SliceSet.cpp:213
std::unordered_map< SliceId, Slice > m_slices
Definition SliceSet.hpp:114
std::vector< SliceId > m_draw_order
Definition SliceSet.hpp:116
std::vector< SliceId > m_memory_order
Definition SliceSet.hpp:115
SliceId add(std::span< const uint8_t > bytes, std::optional< Portal::Graphics::PrimitiveTopology > topology=std::nullopt)
Append a block at the end of memory and draw order.
Definition SliceSet.cpp:22
std::optional< std::vector< Portal::Graphics::DrawRun > > take_runs()
Runs for the visible blocks in draw order, if they changed.
Definition SliceSet.cpp:138
SliceSet(uint32_t stride_bytes, Portal::Graphics::PrimitiveTopology topology)
Definition SliceSet.cpp:9
void repack_from(size_t memory_index)
Definition SliceSet.cpp:198
std::vector< uint8_t > m_mirror
Definition SliceSet.hpp:117
void bring_to_front(SliceId id)
Draw a block after every other.
Definition SliceSet.cpp:111
void send_to_back(SliceId id)
Draw a block before every other.
Definition SliceSet.cpp:122
Portal::Graphics::PrimitiveTopology m_topology
Definition SliceSet.hpp:111
void update(SliceId id, std::span< const uint8_t > bytes)
Replace a block's vertices.
Definition SliceSet.cpp:46
std::vector< std::pair< size_t, size_t > > m_dirty
Definition SliceSet.hpp:118
uint32_t vertex_count() const noexcept
Vertices in the mirror, hidden blocks included.
Definition SliceSet.cpp:133
void flush(const std::shared_ptr< VKBuffer > &target, std::shared_ptr< VKBuffer > &staging)
Upload what changed since the last flush.
Definition SliceSet.cpp:168
void set_visible(SliceId id, bool visible)
Hide or show a block without moving its data.
Definition SliceSet.cpp:100
void upload_intervals(const void *base, std::span< const std::pair< size_t, size_t > > intervals, const std::shared_ptr< VKBuffer > &target, std::shared_ptr< VKBuffer > &staging)
Upload several byte ranges of one source to the same offsets in target.
void ensure_gpu_capacity(const std::shared_ptr< VKBuffer > &target, const std::shared_ptr< VKBuffer > &staging, size_t required, float growth_factor)
Grow a GPU buffer (and its paired staging buffer) to fit required bytes.
@ BufferManagement
Buffer Management (Buffers::BufferManager, creating buffers)
@ BufferProcessing
Buffer processing (Buffers::BufferManager, processing chains)
@ Init
Engine/subsystem initialization.
@ Buffers
Buffers, Managers, processors and processing chains.
PrimitiveTopology
Vertex assembly primitive topology.