MayaFlux 0.3.0
Digital-First Multimedia Processing Framework
Loading...
Searching...
No Matches
TextureLoom.cpp
Go to the documentation of this file.
1#include "TextureLoom.hpp"
2
7
9
11
12bool TextureLoom::initialize(const std::shared_ptr<Core::VulkanBackend>& backend)
13{
14 if (s_initialized) {
16 "TextureLoom already initialized (static flag)");
17 return true;
18 }
19
20 if (!backend) {
22 "Cannot initialize TextureLoom with null backend");
23 return false;
24 }
25
26 if (m_backend) {
28 "TextureLoom already initialized");
29 return true;
30 }
31
32 m_backend = backend;
33 m_resource_manager = &m_backend->get_resource_manager();
34 s_initialized = true;
35
37 "TextureLoom initialized");
38 return true;
39}
40
42{
43 if (!s_initialized) {
44 return;
45 }
46
47 if (!m_backend) {
48 return;
49 }
50
52 "Shutting down TextureLoom...");
53
54 for (auto& texture : m_textures) {
55 if (texture && texture->is_initialized()) {
57 }
58 }
59 m_textures.clear();
60 m_sampler_cache.clear();
61 m_resource_manager = nullptr;
62 m_backend = nullptr;
63
64 s_initialized = false;
65
67 "TextureLoom shutdown complete");
68}
69
70//==============================================================================
71// Texture Creation
72//==============================================================================
73
74std::shared_ptr<Core::VKImage> TextureLoom::create_2d(
75 uint32_t width, uint32_t height,
76 ImageFormat format, const void* data, uint32_t mip_levels)
77{
78 if (!is_initialized()) {
80 "TextureLoom not initialized");
81 return nullptr;
82 }
83
84 auto vk_format = to_vulkan_format(format);
85 auto image = std::make_shared<Core::VKImage>(
86 width, height, 1, vk_format,
89 mip_levels, 1,
91
93
94 if (!image->is_initialized()) {
96 "Failed to initialize VKImage");
97 return nullptr;
98 }
99
100 if (data) {
101 size_t data_size = calculate_image_size(width, height, 1, format);
102 upload_data(image, data, data_size);
103 } else {
105 image->get_image(),
106 vk::ImageLayout::eUndefined,
107 vk::ImageLayout::eShaderReadOnlyOptimal,
108 mip_levels, 1, vk::ImageAspectFlagBits::eColor);
109 image->set_current_layout(vk::ImageLayout::eShaderReadOnlyOptimal);
110 }
111
112 m_textures.push_back(image);
114 "Created 2D texture: {}x{}, format: {}, mips: {}",
115 width, height, vk::to_string(vk_format), mip_levels);
116 return image;
117}
118
119std::shared_ptr<Core::VKImage> TextureLoom::create_3d(
120 uint32_t width, uint32_t height, uint32_t depth,
121 ImageFormat format, const void* data)
122{
123 if (!is_initialized()) {
125 "TextureLoom not initialized");
126 return nullptr;
127 }
128
129 auto vk_format = to_vulkan_format(format);
130 auto image = std::make_shared<Core::VKImage>(
131 width, height, depth, vk_format,
135
137
138 if (!image->is_initialized()) {
140 "Failed to initialize 3D VKImage");
141 return nullptr;
142 }
143
144 if (data) {
145 size_t data_size = calculate_image_size(width, height, depth, format);
146 upload_data(image, data, data_size);
147 } else {
149 image->get_image(),
150 vk::ImageLayout::eUndefined,
151 vk::ImageLayout::eShaderReadOnlyOptimal,
152 1, 1, vk::ImageAspectFlagBits::eColor);
153 image->set_current_layout(vk::ImageLayout::eShaderReadOnlyOptimal);
154 }
155
156 m_textures.push_back(image);
158 "Created 3D texture: {}x{}x{}, format: {}",
159 width, height, depth, vk::to_string(vk_format));
160 return image;
161}
162
163std::shared_ptr<Core::VKImage> TextureLoom::create_cubemap(
164 uint32_t size, ImageFormat format, const void* data)
165{
166 if (!is_initialized()) {
168 "TextureLoom not initialized");
169 return nullptr;
170 }
171
172 auto vk_format = to_vulkan_format(format);
173 auto image = std::make_shared<Core::VKImage>(
174 size, size, 1, vk_format,
178
180
181 if (!image->is_initialized()) {
183 "Failed to initialize cubemap VKImage");
184 return nullptr;
185 }
186
187 if (data) {
188 size_t face_size = calculate_image_size(size, size, 1, format);
189 size_t total_size = face_size * 6;
190 upload_data(image, data, total_size);
191 } else {
193 image->get_image(),
194 vk::ImageLayout::eUndefined,
195 vk::ImageLayout::eShaderReadOnlyOptimal,
196 1, 6, vk::ImageAspectFlagBits::eColor);
197 image->set_current_layout(vk::ImageLayout::eShaderReadOnlyOptimal);
198 }
199
200 m_textures.push_back(image);
202 "Created cubemap: {}x{}, format: {}", size, size, vk::to_string(vk_format));
203 return image;
204}
205
206std::shared_ptr<Core::VKImage> TextureLoom::create_render_target(
207 uint32_t width, uint32_t height, ImageFormat format)
208{
209 if (!is_initialized()) {
211 "TextureLoom not initialized");
212 return nullptr;
213 }
214
215 auto vk_format = to_vulkan_format(format);
216 auto image = std::make_shared<Core::VKImage>(
217 width, height, 1, vk_format,
221
223
224 if (!image->is_initialized()) {
226 "Failed to initialize render target VKImage");
227 return nullptr;
228 }
229
231 image->get_image(),
232 vk::ImageLayout::eUndefined,
233 vk::ImageLayout::eColorAttachmentOptimal,
234 1, 1, vk::ImageAspectFlagBits::eColor);
235 image->set_current_layout(vk::ImageLayout::eColorAttachmentOptimal);
236
237 m_textures.push_back(image);
239 "Created render target: {}x{}, format: {}",
240 width, height, vk::to_string(vk_format));
241 return image;
242}
243
244std::shared_ptr<Core::VKImage> TextureLoom::create_depth_buffer(
245 uint32_t width, uint32_t height, bool with_stencil)
246{
247 if (!is_initialized()) {
249 "TextureLoom not initialized");
250 return nullptr;
251 }
252
253 vk::Format vk_format = with_stencil
254 ? vk::Format::eD24UnormS8Uint
255 : vk::Format::eD32Sfloat;
256
257 auto image = std::make_shared<Core::VKImage>(
258 width, height, 1, vk_format,
262
264
265 if (!image->is_initialized()) {
267 "Failed to initialize depth buffer VKImage");
268 return nullptr;
269 }
270
271 vk::ImageAspectFlags aspect = with_stencil
272 ? (vk::ImageAspectFlagBits::eDepth | vk::ImageAspectFlagBits::eStencil)
273 : vk::ImageAspectFlagBits::eDepth;
274
276 image->get_image(),
277 vk::ImageLayout::eUndefined,
278 vk::ImageLayout::eDepthStencilAttachmentOptimal,
279 1, 1, aspect);
280 image->set_current_layout(vk::ImageLayout::eDepthStencilAttachmentOptimal);
281
282 m_textures.push_back(image);
284 "Created depth buffer: {}x{}, format: {}, stencil: {}",
285 width, height, vk::to_string(vk_format), with_stencil);
286 return image;
287}
288
289std::shared_ptr<Core::VKImage> TextureLoom::create_storage_image(
290 uint32_t width, uint32_t height, ImageFormat format)
291{
292 if (!is_initialized()) {
294 "TextureLoom not initialized");
295 return nullptr;
296 }
297
298 auto vk_format = to_vulkan_format(format);
299 auto image = std::make_shared<Core::VKImage>(
300 width, height, 1, vk_format,
304
306
307 if (!image->is_initialized()) {
309 "Failed to initialize storage image VKImage");
310 return nullptr;
311 }
312
314 image->get_image(),
315 vk::ImageLayout::eUndefined,
316 vk::ImageLayout::eGeneral,
317 1, 1, vk::ImageAspectFlagBits::eColor);
318 image->set_current_layout(vk::ImageLayout::eGeneral);
319
320 m_textures.push_back(image);
322 "Created storage image: {}x{}, format: {}",
323 width, height, vk::to_string(vk_format));
324 return image;
325}
326
327//==============================================================================
328// Data Upload/Download
329//==============================================================================
330
332 const std::shared_ptr<Core::VKImage>& image, const void* data, size_t size)
333{
334 if (!is_initialized() || !image || !data) {
336 "Invalid parameters for upload_data");
337 return;
338 }
339
340 m_resource_manager->upload_image_data(image, data, size);
341}
342
344 const std::shared_ptr<Core::VKImage>& image,
345 const void* data,
346 size_t size,
347 const std::shared_ptr<Buffers::VKBuffer>& staging)
348{
349 if (!is_initialized() || !image || !data || !staging) {
351 "Invalid parameters for upload_data_streaming");
352 return;
353 }
354
355 m_resource_manager->upload_image_data_with_staging(image, data, size, staging);
356}
357
359 const std::shared_ptr<Core::VKImage>& image, void* data, size_t size)
360{
361 if (!is_initialized() || !image || !data) {
363 "Invalid parameters for download_data");
364 return;
365 }
366
367 m_resource_manager->download_image_data(image, data, size);
368}
369
370//==============================================================================
371// Sampler Management
372//==============================================================================
373
375{
376 if (!is_initialized()) {
378 "TextureLoom not initialized");
379 return nullptr;
380 }
381
382 size_t hash = hash_sampler_config(config);
383 auto it = m_sampler_cache.find(hash);
384 if (it != m_sampler_cache.end()) {
385 return it->second;
386 }
387
388 vk::Sampler sampler = create_sampler(config);
389 m_sampler_cache[hash] = sampler;
390 return sampler;
391}
392
394{
395 SamplerConfig config;
396 return get_or_create_sampler(config);
397}
398
408
410{
412 static_cast<vk::Filter>(config.mag_filter),
413 static_cast<vk::SamplerAddressMode>(config.address_mode_u),
414 config.max_anisotropy);
415}
416
418{
419 size_t hash = 0;
420 hash ^= std::hash<int> {}(static_cast<int>(config.mag_filter)) << 0;
421 hash ^= std::hash<int> {}(static_cast<int>(config.min_filter)) << 4;
422 hash ^= std::hash<int> {}(static_cast<int>(config.address_mode_u)) << 8;
423 hash ^= std::hash<int> {}(static_cast<int>(config.address_mode_v)) << 12;
424 hash ^= std::hash<int> {}(static_cast<int>(config.address_mode_w)) << 16;
425 hash ^= std::hash<float> {}(config.max_anisotropy) << 20;
426 hash ^= std::hash<bool> {}(config.enable_mipmaps) << 24;
427 return hash;
428}
429
430//==============================================================================
431// Utilities
432//==============================================================================
433
435{
436 switch (format) {
437 case ImageFormat::R8:
438 return vk::Format::eR8Unorm;
439 case ImageFormat::RG8:
440 return vk::Format::eR8G8Unorm;
442 return vk::Format::eR8G8B8Unorm;
444 return vk::Format::eR8G8B8A8Unorm;
446 return vk::Format::eR8G8B8A8Srgb;
448 return vk::Format::eR16Sfloat;
450 return vk::Format::eR16G16Sfloat;
452 return vk::Format::eR16G16B16A16Sfloat;
454 return vk::Format::eR32Sfloat;
456 return vk::Format::eR32G32Sfloat;
458 return vk::Format::eR32G32B32A32Sfloat;
460 return vk::Format::eD16Unorm;
462 return vk::Format::eX8D24UnormPack32;
464 return vk::Format::eD32Sfloat;
466 return vk::Format::eD24UnormS8Uint;
467 default:
468 return vk::Format::eR8G8B8A8Unorm;
469 }
470}
471
473{
474 switch (format) {
475 case ImageFormat::R8:
476 return 1;
477 case ImageFormat::RG8:
478 return 2;
480 return 3;
483 return 4;
485 return 2;
487 return 4;
489 return 8;
491 return 4;
493 return 8;
495 return 16;
497 return 2;
501 default:
502 return 4;
503 }
504}
505
507 uint32_t width, uint32_t height, uint32_t depth, ImageFormat format)
508{
509 return static_cast<size_t>(width) * height * depth * get_bytes_per_pixel(format);
510}
511
512} // namespace MayaFlux::Portal::Graphics
#define MF_INFO(comp, ctx,...)
#define MF_ERROR(comp, ctx,...)
#define MF_WARN(comp, ctx,...)
void cleanup_image(const std::shared_ptr< VKImage > &image)
Cleanup a VKImage (destroy view, image, and free memory)
void initialize_image(const std::shared_ptr< VKImage > &image)
Initialize a VKImage (allocate VkImage, memory, and create image view)
vk::Sampler create_sampler(vk::Filter filter=vk::Filter::eLinear, vk::SamplerAddressMode address_mode=vk::SamplerAddressMode::eRepeat, float max_anisotropy=0.0F)
Create sampler.
void download_image_data(std::shared_ptr< VKImage > image, void *data, size_t size, vk::ImageLayout restore_layout=vk::ImageLayout::eShaderReadOnlyOptimal, vk::PipelineStageFlags restore_stage=vk::PipelineStageFlagBits::eFragmentShader)
Download data from an image into a caller-supplied buffer.
void transition_image_layout(vk::Image image, vk::ImageLayout old_layout, vk::ImageLayout new_layout, uint32_t mip_levels=1, uint32_t array_layers=1, vk::ImageAspectFlags aspect_flags=vk::ImageAspectFlagBits::eColor)
Transition image layout using a pipeline barrier.
void upload_image_data_with_staging(std::shared_ptr< VKImage > image, const void *data, size_t size, const std::shared_ptr< Buffers::VKBuffer > &staging)
Upload image data using a caller-supplied persistent staging buffer.
void upload_image_data(std::shared_ptr< VKImage > image, const void *data, size_t size)
Upload data to an image (creates staging buffer internally)
@ RENDER_TARGET
Color attachment for rendering.
@ STORAGE
Storage image (compute shader read/write)
@ TEXTURE_2D
Sampled texture (shader read)
@ DEPTH_STENCIL
Depth/stencil attachment.
std::shared_ptr< Core::VKImage > create_render_target(uint32_t width, uint32_t height, ImageFormat format=ImageFormat::RGBA8)
Create a render target (color attachment)
std::shared_ptr< Core::VKImage > create_depth_buffer(uint32_t width, uint32_t height, bool with_stencil=false)
Create a depth buffer.
static size_t get_bytes_per_pixel(ImageFormat format)
Get bytes per pixel for a format.
vk::Sampler get_nearest_sampler()
Get a default nearest sampler (for pixel-perfect sampling)
static size_t calculate_image_size(uint32_t width, uint32_t height, uint32_t depth, ImageFormat format)
Calculate image data size.
static vk::Format to_vulkan_format(ImageFormat format)
Convert Portal ImageFormat to Vulkan format.
std::shared_ptr< Core::VKImage > create_storage_image(uint32_t width, uint32_t height, ImageFormat format=ImageFormat::RGBA8)
Create a storage image (compute shader read/write)
void upload_data(const std::shared_ptr< Core::VKImage > &image, const void *data, size_t size)
Upload pixel data to an existing texture.
bool initialize(const std::shared_ptr< Core::VulkanBackend > &backend)
Initialize texture manager.
vk::Sampler get_or_create_sampler(const SamplerConfig &config)
Get or create a sampler with the given configuration.
void download_data(const std::shared_ptr< Core::VKImage > &image, void *data, size_t size)
Download pixel data from a texture.
std::shared_ptr< Core::VulkanBackend > m_backend
std::shared_ptr< Core::VKImage > create_3d(uint32_t width, uint32_t height, uint32_t depth, ImageFormat format=ImageFormat::RGBA8, const void *data=nullptr)
Create a 3D texture (volumetric)
vk::Sampler create_sampler(const SamplerConfig &config)
void shutdown()
Shutdown and cleanup all textures.
Core::BackendResourceManager * m_resource_manager
std::shared_ptr< Core::VKImage > create_2d(uint32_t width, uint32_t height, ImageFormat format=ImageFormat::RGBA8, const void *data=nullptr, uint32_t mip_levels=1)
Create a 2D texture.
static size_t hash_sampler_config(const SamplerConfig &config)
std::vector< std::shared_ptr< Core::VKImage > > m_textures
std::shared_ptr< Core::VKImage > create_cubemap(uint32_t size, ImageFormat format=ImageFormat::RGBA8, const void *data=nullptr)
Create a cubemap texture.
vk::Sampler get_default_sampler()
Get a default linear sampler (for convenience)
std::unordered_map< size_t, vk::Sampler > m_sampler_cache
bool is_initialized() const
Check if manager is initialized.
@ ImageProcessing
Image processing tasks (filters, transformations)
@ Portal
High-level user-facing API layer.
@ VOLUMETRIC_3D
3D volumetric data
@ IMAGE_COLOR
2D RGB/RGBA image
@ IMAGE_2D
2D image (grayscale or single channel)
@ NEAREST
Nearest neighbor (pixelated)
ImageFormat
User-friendly image format enum.
@ DEPTH24_STENCIL8
24-bit depth + 8-bit stencil
@ RGBA32F
Four channel 32-bit float.
@ R16F
Single channel 16-bit float.
@ RGBA16F
Four channel 16-bit float.
@ RG32F
Two channel 32-bit float.
@ R32F
Single channel 32-bit float.
@ RG16F
Two channel 16-bit float.
@ RGBA8_SRGB
Four channel 8-bit sRGB.