MayaFlux 0.2.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, void* data, size_t size)
345{
346 if (!is_initialized() || !image || !data) {
348 "Invalid parameters for download_data");
349 return;
350 }
351
352 m_resource_manager->download_image_data(image, data, size);
353}
354
355//==============================================================================
356// Sampler Management
357//==============================================================================
358
360{
361 if (!is_initialized()) {
363 "TextureLoom not initialized");
364 return nullptr;
365 }
366
367 size_t hash = hash_sampler_config(config);
368 auto it = m_sampler_cache.find(hash);
369 if (it != m_sampler_cache.end()) {
370 return it->second;
371 }
372
373 vk::Sampler sampler = create_sampler(config);
374 m_sampler_cache[hash] = sampler;
375 return sampler;
376}
377
379{
380 SamplerConfig config;
381 return get_or_create_sampler(config);
382}
383
393
395{
397 static_cast<vk::Filter>(config.mag_filter),
398 static_cast<vk::SamplerAddressMode>(config.address_mode_u),
399 config.max_anisotropy);
400}
401
403{
404 size_t hash = 0;
405 hash ^= std::hash<int> {}(static_cast<int>(config.mag_filter)) << 0;
406 hash ^= std::hash<int> {}(static_cast<int>(config.min_filter)) << 4;
407 hash ^= std::hash<int> {}(static_cast<int>(config.address_mode_u)) << 8;
408 hash ^= std::hash<int> {}(static_cast<int>(config.address_mode_v)) << 12;
409 hash ^= std::hash<int> {}(static_cast<int>(config.address_mode_w)) << 16;
410 hash ^= std::hash<float> {}(config.max_anisotropy) << 20;
411 hash ^= std::hash<bool> {}(config.enable_mipmaps) << 24;
412 return hash;
413}
414
415//==============================================================================
416// Utilities
417//==============================================================================
418
420{
421 switch (format) {
422 case ImageFormat::R8:
423 return vk::Format::eR8Unorm;
424 case ImageFormat::RG8:
425 return vk::Format::eR8G8Unorm;
427 return vk::Format::eR8G8B8Unorm;
429 return vk::Format::eR8G8B8A8Unorm;
431 return vk::Format::eR8G8B8A8Srgb;
433 return vk::Format::eR16Sfloat;
435 return vk::Format::eR16G16Sfloat;
437 return vk::Format::eR16G16B16A16Sfloat;
439 return vk::Format::eR32Sfloat;
441 return vk::Format::eR32G32Sfloat;
443 return vk::Format::eR32G32B32A32Sfloat;
445 return vk::Format::eD16Unorm;
447 return vk::Format::eX8D24UnormPack32;
449 return vk::Format::eD32Sfloat;
451 return vk::Format::eD24UnormS8Uint;
452 default:
453 return vk::Format::eR8G8B8A8Unorm;
454 }
455}
456
458{
459 switch (format) {
460 case ImageFormat::R8:
461 return 1;
462 case ImageFormat::RG8:
463 return 2;
465 return 3;
468 return 4;
470 return 2;
472 return 4;
474 return 8;
476 return 4;
478 return 8;
480 return 16;
482 return 2;
486 default:
487 return 4;
488 }
489}
490
492 uint32_t width, uint32_t height, uint32_t depth, ImageFormat format)
493{
494 return static_cast<size_t>(width) * height * depth * get_bytes_per_pixel(format);
495}
496
497} // namespace MayaFlux::Portal::Graphics
#define MF_INFO(comp, ctx,...)
#define MF_ERROR(comp, ctx,...)
#define MF_WARN(comp, ctx,...)
void download_image_data(std::shared_ptr< VKImage > image, void *data, size_t size)
Download data from an image (creates staging buffer internally)
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 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(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.