MayaFlux 1.0.0
Digital-First Multimedia Processing Framework
Loading...
Searching...
No Matches
TextureArrayBuffer.cpp
Go to the documentation of this file.
2
4
12
14
15namespace MayaFlux::Buffers {
16
18 uint32_t width,
19 uint32_t height,
21 uint32_t layers)
23 , m_layers(std::max(layers, 1U))
24 , m_layer_bytes(static_cast<size_t>(width) * height * Portal::Graphics::TextureLoom::get_bytes_per_pixel(format))
25{
26 RenderConfig config;
27 config.vertex_shader = "texture.vert.spv";
28 config.fragment_shader = "texture_array.frag.spv";
29 config.default_texture_binding = "textureArray";
32
34 m_push.weights.fill(1.0F);
35}
36
38 const Kakshya::VideoStreamSpec& spec,
39 std::optional<Portal::Graphics::FitMode> fit)
40 : TextureArrayBuffer(spec.width, spec.height, spec.format, static_cast<uint32_t>(spec.ring_frames))
41{
42 m_fit = fit;
43}
44
46{
49
50 if (const auto processor = get_texture_processor()) {
51 processor->set_streaming_mode(true);
52 }
53}
54
56{
59
60 if (const auto processor = get_render_processor()) {
61 processor->set_push_constant_size(m_custom_push.empty() ? sizeof(Push) : m_custom_push.size());
62 }
63 flush_push();
65}
66
67bool TextureArrayBuffer::submit_layer(uint32_t layer, std::span<const uint8_t> pixels)
68{
69 if (layer >= m_layers || pixels.size() != m_layer_bytes) {
71 "TextureArrayBuffer::submit_layer needs a layer below {} and {} bytes, got layer {} and {} bytes",
72 m_layers, m_layer_bytes, layer, pixels.size());
73 return false;
74 }
75
76 if (m_layer_mode) {
77 if (!m_layer_staging) {
79 }
81 get_texture(), layer, pixels.data(), pixels.size(), m_layer_staging);
82 }
83
85 if (storage.size() != m_layer_bytes * m_layers) {
86 storage.assign(m_layer_bytes * m_layers, 0);
87 }
88
89 std::memcpy(storage.data() + static_cast<size_t>(layer) * m_layer_bytes, pixels.data(), m_layer_bytes);
91 return true;
92}
93
95{
96 if (layer >= m_layers || image.byte_size() == 0 || !image.is_consistent()) {
98 "TextureArrayBuffer::submit_layer needs a layer below {} and a consistent image, got layer {}",
99 m_layers, layer);
100 return false;
101 }
102
103 const bool same_extent = image.width == get_width() && image.height == get_height();
104 if (same_extent && image.format == get_format()) {
105 return submit_layer(layer,
106 std::span<const uint8_t>(static_cast<const uint8_t*>(image.data()), image.byte_size()));
107 }
108
109 if (!same_extent && !fit_for(layer)) {
110 return false;
111 }
112
113 if (!m_source_staging || m_source_staging->get_size() < image.byte_size()) {
115 }
116
118 .width = image.width,
119 .height = image.height,
120 .format = image.format
121 };
122
124 m_source_cache, key,
125 std::span<const uint8_t>(static_cast<const uint8_t*>(image.data()), image.byte_size()),
127 if (!source) {
129 "TextureArrayBuffer::submit_layer failed to upload a {}x{} image", image.width, image.height);
130 return false;
131 }
132
133 return submit_layer(layer, source);
134}
135
136bool TextureArrayBuffer::submit_layer(uint32_t layer, const std::shared_ptr<Core::VKImage>& image)
137{
138 if (layer >= m_layers || !image) {
140 "TextureArrayBuffer::submit_layer needs a layer below {} and an image, got layer {}",
141 m_layers, layer);
142 return false;
143 }
144
145 const bool same_extent = image->get_width() == get_width() && image->get_height() == get_height();
146 const auto fit = fit_for(layer);
147 if (!same_extent && !fit) {
148 return false;
149 }
150
152 if (!m_layer_mode) {
153 return false;
154 }
155
157 image->get_width(), image->get_height(), get_width(), get_height(),
158 same_extent ? Portal::Graphics::FitMode::STRETCH : *fit, m_filter);
159
161 bool stored = !plan.blits.empty();
162
163 if (stored) {
164 const vk::Rect2D& first = plan.blits.front().dst;
165 const bool covers = plan.blits.size() == 1
166 && first.offset.x == 0 && first.offset.y == 0
167 && first.extent.width == get_width() && first.extent.height == get_height();
168 if (!covers) {
169 clear_layer(layer);
170 }
171 }
172
173 for (auto& blit : plan.blits) {
174 blit.dst_layer = layer;
175 stored = loom.blit_layer(image, get_texture(), blit) && stored;
176 }
177
178 return stored;
179}
180
182{
183 if (m_layer_data) {
184 return;
185 }
186
187 const auto service = Registry::BackendRegistry::instance()
189 if (!service) {
191 "TextureArrayBuffer: layer data needs a buffer service");
192 return;
193 }
194
195 m_layer_values.assign(static_cast<size_t>(m_layers) * 2, glm::vec4(0.0F));
196 m_layer_data = std::make_shared<VKBuffer>(
197 m_layer_values.size() * sizeof(glm::vec4),
198 VKBuffer::Usage::HOST_STORAGE,
200 service->initialize_buffer(m_layer_data);
201
202 std::memcpy(m_layer_data->get_mapped_ptr(), m_layer_values.data(),
203 m_layer_values.size() * sizeof(glm::vec4));
205}
206
207void TextureArrayBuffer::set_layer_params(uint32_t layer, const glm::vec4& values)
208{
209 if (layer < m_layers) {
210 write_layer_value(static_cast<size_t>(layer) * 2, values);
211 }
212}
213
214void TextureArrayBuffer::set_layer_timing(uint32_t layer, const glm::vec4& values)
215{
216 if (layer < m_layers) {
217 write_layer_value(static_cast<size_t>(layer) * 2 + 1, values);
218 }
219}
220
222{
224 if (!m_layer_data) {
225 return;
226 }
227
229 std::memcpy(static_cast<uint8_t*>(m_layer_data->get_mapped_ptr()) + index * sizeof(glm::vec4),
230 &values, sizeof(glm::vec4));
231}
232
234{
235 if (const auto processor = get_render_processor(); processor && m_layer_data) {
236 processor->bind_buffer("layerData", m_layer_data);
237 }
238}
239
241{
242 if (m_blank.size() != m_layer_bytes) {
243 m_blank.assign(m_layer_bytes, 0);
244 }
245 if (!m_layer_staging) {
247 }
248
250 get_texture(), layer, m_blank.data(), m_blank.size(), m_layer_staging);
251}
252
253std::optional<Portal::Graphics::FitMode> TextureArrayBuffer::fit_for(uint32_t layer) const
254{
255 const auto found = m_layer_fit.find(layer);
256 return found != m_layer_fit.end() ? found->second : m_fit;
257}
258
259void TextureArrayBuffer::set_weight(uint32_t layer, float weight)
260{
261 if (layer >= m_layers || layer >= m_push.weights.size()) {
262 return;
263 }
264
265 m_push.weights.at(layer) = weight;
266 flush_push();
267}
268
269void TextureArrayBuffer::set_weights(std::span<const float> weights)
270{
271 const auto count = std::min<size_t>({ weights.size(), m_layers, m_push.weights.size() });
272 std::copy_n(weights.begin(), count, m_push.weights.begin());
273 flush_push();
274}
275
277{
278 m_push.mode = mode;
279 flush_push();
280}
281
282void TextureArrayBuffer::set_push_constants(const void* data, size_t size)
283{
284 if (!data || size == 0) {
285 m_custom_push.clear();
286 } else {
287 const auto* bytes = static_cast<const uint8_t*>(data);
288 m_custom_push.assign(bytes, bytes + size);
289 }
290
291 if (const auto processor = get_render_processor()) {
292 processor->set_push_constant_size(m_custom_push.empty() ? sizeof(Push) : m_custom_push.size());
293 }
294 flush_push();
295}
296
298{
299 if (has_texture()) {
300 return;
301 }
302
304 auto image = loom.create_2d_array(get_width(), get_height(), m_layers, get_format());
305 if (!image) {
307 "TextureArrayBuffer: failed to create a {} layer array image", m_layers);
308 return;
309 }
310
311 set_gpu_texture(std::move(image));
312}
313
315{
316 if (m_layer_mode) {
317 return;
318 }
319
320 ensure_array();
321 if (!has_texture()) {
322 return;
323 }
324
326 const size_t total = m_layer_bytes * m_layers;
327 const auto staging = create_image_staging_buffer(total);
328 if (!staging) {
330 "TextureArrayBuffer: failed to allocate staging for {} bytes", total);
331 return;
332 }
333
335 if (storage.size() == total) {
336 loom.upload_data(get_texture(), storage.data(), total, staging);
337 } else {
338 const std::vector<uint8_t> blank(total, 0);
339 loom.upload_data(get_texture(), blank.data(), total, staging);
340 }
341
342 std::vector<uint8_t>().swap(storage);
343 m_layer_mode = true;
344}
345
347{
348 const auto processor = get_render_processor();
349 if (!processor) {
350 return;
351 }
352
353 if (m_custom_push.empty()) {
354 processor->set_push_constant_data(m_push);
355 } else {
356 processor->set_push_constant_data_raw(m_custom_push.data(), m_custom_push.size());
357 }
358}
359
360}
#define MF_ERROR(comp, ctx,...)
#define MF_RT_ERROR(comp, ctx,...)
IO::ImageData image
Definition Decoder.cpp:47
const std::vector< float > * pixels
Definition Decoder.cpp:48
bool format
bool size
uint32_t total
uint32_t mode
double weight
SampleStorage storage
glm::ivec3 max
std::vector< std::byte > values
Definition VDBWriter.cpp:26
uint32_t index
Definition VKDevice.cpp:122
size_t count
uint32_t width
uint32_t height
std::optional< Portal::Graphics::FitMode > fit_for(uint32_t layer) const
void enable_layer_data()
Create the per-layer buffer the shader reads as layerData.
void set_layer_params(uint32_t layer, const glm::vec4 &values)
Set the params of one layer, layerData[2 * layer].
std::shared_ptr< VKBuffer > m_source_staging
std::unordered_map< uint32_t, std::optional< Portal::Graphics::FitMode > > m_layer_fit
std::optional< Portal::Graphics::FitMode > m_fit
TextureArrayBuffer(uint32_t width, uint32_t height, Portal::Graphics::ImageFormat format, uint32_t layers)
void set_weight(uint32_t layer, float weight)
Set the weight of one layer.
void set_layer_timing(uint32_t layer, const glm::vec4 &values)
Set the timing of one layer, layerData[2 * layer + 1].
void setup_processors(ProcessingToken token) override
Setup processors with a processing token.
void set_mode(uint32_t mode)
Set the mode number passed to the shader.
void setup_rendering(const RenderConfig &config) override
Setup rendering with RenderProcessor.
std::shared_ptr< VKBuffer > m_layer_staging
void set_weights(std::span< const float > weights)
Set the weights of the first layers in order.
bool submit_layer(uint32_t layer, std::span< const uint8_t > pixels)
Replace the frame of one layer with raw pixels.
void set_push_constants(const void *data, size_t size)
Replace the default push constants with the shader's own.
void write_layer_value(size_t index, const glm::vec4 &values)
Portal::Graphics::ImageCacheEntry m_source_cache
A TextureBuffer whose image is a stack of layers, composed in one draw.
void setup_processors(ProcessingToken token) override
Setup processors with a processing token.
void set_gpu_texture(std::shared_ptr< Core::VKImage > image)
Inject a pre-existing GPU texture, bypassing TextureProcessor allocation.
std::shared_ptr< TextureProcessor > get_texture_processor() const
Portal::Graphics::ImageFormat get_format() const
virtual void setup_rendering(const RenderConfig &config)
Setup rendering with RenderProcessor.
void mark_pixels_dirty()
Mark pixel data as changed Use this if you modify pixel data in-place without calling set_pixel_data(...
std::shared_ptr< Core::VKImage > get_texture() const
Get GPU texture image Suitable for binding to shaders via RenderProcessor::bind_texture()
std::vector< uint8_t > & get_pixel_data_mutable()
A hybrid buffer managing both a textured quad geometry and its pixel data.
virtual std::shared_ptr< RenderProcessor > get_render_processor() const
Get a RenderProcessor suitable for rendering this buffer.
Definition VKBuffer.hpp:605
void set_default_render_config(const RenderConfig &config)
Called by derived classes to set their context-specific defaults.
Definition VKBuffer.hpp:631
static FitPlan plan_fit(uint32_t src_width, uint32_t src_height, uint32_t dst_width, uint32_t dst_height, FitMode mode=FitMode::STRETCH, FilterMode filter=FilterMode::LINEAR)
Work out how a source extent maps onto a destination extent.
std::shared_ptr< Core::VKImage > refresh_cached_image(ImageCacheEntry &entry, const ImageKey &key, std::span< const uint8_t > pixels, const std::shared_ptr< Buffers::VKBuffer > &staging={})
Refresh a sampled image in an owner-held cache entry.
bool upload_layer(const std::shared_ptr< Core::VKImage > &image, uint32_t layer, const void *data, size_t size, const std::shared_ptr< Buffers::VKBuffer > &staging=nullptr, bool deferred=false)
Upload pixels into one layer of an array image.
Interface * get_service()
Query for a backend service.
static BackendRegistry & instance()
Get the global registry instance.
std::shared_ptr< VKBuffer > create_image_staging_buffer(size_t size)
Allocate a persistent host-visible staging buffer sized for repeated streaming uploads to an image of...
ProcessingToken
Bitfield enum defining processing characteristics and backend requirements for buffer operations.
@ BufferProcessing
Buffer processing (Buffers::BufferManager, processing chains)
@ Buffers
Buffers, Managers, processors and processing chains.
ImageFormat
User-friendly image format enum.
Definition ImageData.hpp:12
@ UNKNOWN
Unknown or undefined modality.
MAYAFLUX_API TextureLoom & get_texture_manager()
Get the global texture manager instance.
@ STRETCH
Whole source onto whole destination, aspect ignored.
static constexpr DomainSpec Graphics
Domain constant for Graphics domain.
Definition Creator.hpp:444
std::array< float, WEIGHT_SLOTS > weights
Raw image data loaded from file or read back from a GPU image.
Definition ImageData.hpp:55
Describes a DynamicVideoStream so it can be built by designated initializer.
Immutable properties that determine whether an image can be reused.
Unified rendering configuration for graphics buffers.
Backend buffer management service interface.