SleakEngine 1.0.0
C++23 multi-backend game engine
Loading...
Searching...
No Matches
VulkanTexture.cpp
Go to the documentation of this file.
2#include <backends/imgui_impl_vulkan.h>
3#include <Core/Logger.hpp>
4#include <stb_image.h>
5#include <cstring>
6#include <algorithm>
7#include <cmath>
8
9namespace Sleak {
10namespace RenderEngine {
11
13 VkPhysicalDevice physicalDevice,
14 VkCommandPool commandPool,
15 VkQueue graphicsQueue)
16 : m_device(device),
17 m_physicalDevice(physicalDevice),
18 m_commandPool(commandPool),
19 m_graphicsQueue(graphicsQueue) {}
20
22 Cleanup();
23}
24
25bool VulkanTexture::LoadFromMemory(const void* data, uint32_t width,
26 uint32_t height, TextureFormat format) {
27 if (!data || width == 0 || height == 0) return false;
28
29 Cleanup();
30
31 m_width = width;
32 m_height = height;
33 m_format = format;
34
35 VkDeviceSize imageSize = static_cast<VkDeviceSize>(width) * height * 4;
36
37 // 1. Create staging buffer
38 VkBuffer stagingBuffer;
39 VkDeviceMemory stagingMemory;
40
41 VkBufferCreateInfo bufferInfo{};
42 bufferInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
43 bufferInfo.size = imageSize;
44 bufferInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
45 bufferInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
46
47 if (vkCreateBuffer(m_device, &bufferInfo, nullptr, &stagingBuffer) !=
48 VK_SUCCESS) {
49 SLEAK_ERROR("VulkanTexture: Failed to create staging buffer");
50 return false;
51 }
52
53 VkMemoryRequirements memReqs;
54 vkGetBufferMemoryRequirements(m_device, stagingBuffer, &memReqs);
55
56 VkMemoryAllocateInfo allocInfo{};
57 allocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
58 allocInfo.allocationSize = memReqs.size;
59 allocInfo.memoryTypeIndex = FindMemoryType(
60 memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT |
61 VK_MEMORY_PROPERTY_HOST_COHERENT_BIT);
62
63 if (vkAllocateMemory(m_device, &allocInfo, nullptr, &stagingMemory) !=
64 VK_SUCCESS) {
65 vkDestroyBuffer(m_device, stagingBuffer, nullptr);
66 SLEAK_ERROR("VulkanTexture: Failed to allocate staging memory");
67 return false;
68 }
69
70 vkBindBufferMemory(m_device, stagingBuffer, stagingMemory, 0);
71
72 // 2. Copy pixel data to staging buffer
73 void* mapped;
74 vkMapMemory(m_device, stagingMemory, 0, imageSize, 0, &mapped);
75 memcpy(mapped, data, static_cast<size_t>(imageSize));
76 vkUnmapMemory(m_device, stagingMemory);
77
78 // 3. Create VkImage with a full mip chain
79 VkFormat vkFormat = VK_FORMAT_R8G8B8A8_UNORM;
80 if (format == TextureFormat::BGRA8)
81 vkFormat = VK_FORMAT_B8G8R8A8_UNORM;
82
83 uint32_t maxDim = std::max(width, height);
84 uint32_t mipLevels =
85 static_cast<uint32_t>(std::floor(std::log2(maxDim))) + 1u;
86 if (m_maxMipLevels > 0 && m_maxMipLevels < mipLevels)
87 mipLevels = m_maxMipLevels;
88
89 // Linear-blit support is required to downsample; else fall back to 1 level.
90 VkFormatProperties fmtProps{};
91 vkGetPhysicalDeviceFormatProperties(m_physicalDevice, vkFormat, &fmtProps);
92 if (!(fmtProps.optimalTilingFeatures &
93 VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT))
94 mipLevels = 1;
95 m_mipLevels = mipLevels;
96
97 if (!CreateImage(width, height, vkFormat,
98 VK_IMAGE_USAGE_TRANSFER_DST_BIT |
99 VK_IMAGE_USAGE_TRANSFER_SRC_BIT |
100 VK_IMAGE_USAGE_SAMPLED_BIT,
101 mipLevels)) {
102 vkDestroyBuffer(m_device, stagingBuffer, nullptr);
103 vkFreeMemory(m_device, stagingMemory, nullptr);
104 return false;
105 }
106
107 // 4. Single command buffer: barrier all mips -> DST, copy mip0, gen mips.
108 VkCommandBufferAllocateInfo cmdAllocInfo{};
109 cmdAllocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
110 cmdAllocInfo.commandPool = m_commandPool;
111 cmdAllocInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
112 cmdAllocInfo.commandBufferCount = 1;
113 VkCommandBuffer cmdBuffer;
114 vkAllocateCommandBuffers(m_device, &cmdAllocInfo, &cmdBuffer);
115
116 VkCommandBufferBeginInfo beginInfo{};
117 beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
118 beginInfo.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
119 vkBeginCommandBuffer(cmdBuffer, &beginInfo);
120
121 VkImageMemoryBarrier toDst{};
122 toDst.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
123 toDst.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED;
124 toDst.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
125 toDst.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
126 toDst.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
127 toDst.image = m_image;
128 toDst.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
129 toDst.subresourceRange.baseMipLevel = 0;
130 toDst.subresourceRange.levelCount = mipLevels;
131 toDst.subresourceRange.baseArrayLayer = 0;
132 toDst.subresourceRange.layerCount = 1;
133 toDst.srcAccessMask = 0;
134 toDst.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
135 vkCmdPipelineBarrier(cmdBuffer, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
136 VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, nullptr, 0,
137 nullptr, 1, &toDst);
138
139 VkBufferImageCopy region{};
140 region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
141 region.imageSubresource.mipLevel = 0;
142 region.imageSubresource.baseArrayLayer = 0;
143 region.imageSubresource.layerCount = 1;
144 region.imageOffset = {0, 0, 0};
145 region.imageExtent = {width, height, 1};
146 vkCmdCopyBufferToImage(cmdBuffer, stagingBuffer, m_image,
147 VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &region);
148
149 if (mipLevels > 1) {
150 GenerateMipmaps(cmdBuffer, static_cast<int32_t>(width),
151 static_cast<int32_t>(height));
152 } else {
153 // Single level: mip0 DST -> SHADER_READ
154 VkImageMemoryBarrier toRead = toDst;
155 toRead.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
156 toRead.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
157 toRead.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
158 toRead.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
159 toRead.subresourceRange.levelCount = 1;
160 vkCmdPipelineBarrier(cmdBuffer, VK_PIPELINE_STAGE_TRANSFER_BIT,
161 VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0, 0,
162 nullptr, 0, nullptr, 1, &toRead);
163 }
164
165 vkEndCommandBuffer(cmdBuffer);
166
167 VkSubmitInfo submitInfo{};
168 submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
169 submitInfo.commandBufferCount = 1;
170 submitInfo.pCommandBuffers = &cmdBuffer;
171 VkFenceCreateInfo fenceInfo{};
172 fenceInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
173 VkFence uploadFence;
174 vkCreateFence(m_device, &fenceInfo, nullptr, &uploadFence);
175 vkQueueSubmit(m_graphicsQueue, 1, &submitInfo, uploadFence);
176 vkWaitForFences(m_device, 1, &uploadFence, VK_TRUE, UINT64_MAX);
177 vkDestroyFence(m_device, uploadFence, nullptr);
178 vkFreeCommandBuffers(m_device, m_commandPool, 1, &cmdBuffer);
179
180 // 5. Cleanup staging resources
181 vkDestroyBuffer(m_device, stagingBuffer, nullptr);
182 vkFreeMemory(m_device, stagingMemory, nullptr);
183
184 // 6. Create image view and sampler
185 if (!CreateImageView(vkFormat)) return false;
186 if (!CreateSampler()) return false;
187
188 return true;
189}
190
191bool VulkanTexture::LoadFromFile(const std::string& filePath) {
192 int w, h, channels;
193 unsigned char* pixels =
194 stbi_load(filePath.c_str(), &w, &h, &channels, 4);
195 if (!pixels) {
196 SLEAK_ERROR("VulkanTexture: Failed to load image: {}", filePath);
197 return false;
198 }
199
200 bool result = LoadFromMemory(pixels, static_cast<uint32_t>(w),
201 static_cast<uint32_t>(h),
203 stbi_image_free(pixels);
204 return result;
205}
206
207void VulkanTexture::Bind(uint32_t slot) const {
208 // Binding in Vulkan is done through descriptor sets,
209 // handled by the renderer
210}
211
213 // No-op in Vulkan
214}
215
217 m_filter = filter;
218 if (m_sampler != VK_NULL_HANDLE) {
219 vkDestroySampler(m_device, m_sampler, nullptr);
220 m_sampler = VK_NULL_HANDLE;
221 CreateSampler();
222 UpdateDescriptorSets();
223 }
224}
225
227 m_wrapMode = wrapMode;
228 if (m_sampler != VK_NULL_HANDLE) {
229 vkDestroySampler(m_device, m_sampler, nullptr);
230 m_sampler = VK_NULL_HANDLE;
231 CreateSampler();
232 UpdateDescriptorSets();
233 }
234}
235
237 m_lodBias = bias;
238 if (m_sampler != VK_NULL_HANDLE) {
239 vkDestroySampler(m_device, m_sampler, nullptr);
240 m_sampler = VK_NULL_HANDLE;
241 CreateSampler();
242 UpdateDescriptorSets();
243 }
244}
245
246void VulkanTexture::UpdateDescriptorSets() {
247 if (m_descriptorSets.empty() || m_imageView == VK_NULL_HANDLE || m_sampler == VK_NULL_HANDLE)
248 return;
249
250 for (auto& set : m_descriptorSets) {
251 VkDescriptorImageInfo imageInfo{};
252 imageInfo.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
253 imageInfo.imageView = m_imageView;
254 imageInfo.sampler = m_sampler;
255
256 VkWriteDescriptorSet descriptorWrite{};
257 descriptorWrite.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
258 descriptorWrite.dstSet = set;
259 descriptorWrite.dstBinding = 0;
260 descriptorWrite.dstArrayElement = 0;
261 descriptorWrite.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
262 descriptorWrite.descriptorCount = 1;
263 descriptorWrite.pImageInfo = &imageInfo;
264
265 vkUpdateDescriptorSets(m_device, 1, &descriptorWrite, 0, nullptr);
266 }
267}
268
270 if (m_imguiDescriptorSet == VK_NULL_HANDLE && m_imageView != VK_NULL_HANDLE && m_sampler != VK_NULL_HANDLE) {
271 m_imguiDescriptorSet = ImGui_ImplVulkan_AddTexture(
272 m_sampler, m_imageView, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
273 }
274 return reinterpret_cast<uint64_t>(m_imguiDescriptorSet);
275}
276
277void VulkanTexture::Cleanup() {
278 if (m_device == VK_NULL_HANDLE) return;
279
280 if (m_imguiDescriptorSet != VK_NULL_HANDLE) {
281 ImGui_ImplVulkan_RemoveTexture(m_imguiDescriptorSet);
282 m_imguiDescriptorSet = VK_NULL_HANDLE;
283 }
284 if (m_sampler != VK_NULL_HANDLE) {
285 vkDestroySampler(m_device, m_sampler, nullptr);
286 m_sampler = VK_NULL_HANDLE;
287 }
288 if (m_imageView != VK_NULL_HANDLE) {
289 vkDestroyImageView(m_device, m_imageView, nullptr);
290 m_imageView = VK_NULL_HANDLE;
291 }
292 if (m_image != VK_NULL_HANDLE) {
293 vkDestroyImage(m_device, m_image, nullptr);
294 m_image = VK_NULL_HANDLE;
295 }
296 if (m_imageMemory != VK_NULL_HANDLE) {
297 vkFreeMemory(m_device, m_imageMemory, nullptr);
298 m_imageMemory = VK_NULL_HANDLE;
299 }
300}
301
302uint32_t VulkanTexture::FindMemoryType(
303 uint32_t typeFilter, VkMemoryPropertyFlags properties) {
304 VkPhysicalDeviceMemoryProperties memProperties;
305 vkGetPhysicalDeviceMemoryProperties(m_physicalDevice, &memProperties);
306
307 for (uint32_t i = 0; i < memProperties.memoryTypeCount; i++) {
308 if ((typeFilter & (1 << i)) &&
309 (memProperties.memoryTypes[i].propertyFlags & properties) ==
310 properties) {
311 return i;
312 }
313 }
314
315 SLEAK_ERROR("VulkanTexture: Failed to find suitable memory type");
316 return 0;
317}
318
319bool VulkanTexture::CreateImage(uint32_t width, uint32_t height,
320 VkFormat format,
321 VkImageUsageFlags usage,
322 uint32_t mipLevels) {
323 VkImageCreateInfo imageInfo{};
324 imageInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
325 imageInfo.imageType = VK_IMAGE_TYPE_2D;
326 imageInfo.extent.width = width;
327 imageInfo.extent.height = height;
328 imageInfo.extent.depth = 1;
329 imageInfo.mipLevels = mipLevels;
330 imageInfo.arrayLayers = 1;
331 imageInfo.format = format;
332 imageInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
333 imageInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
334 imageInfo.usage = usage;
335 imageInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
336 imageInfo.samples = VK_SAMPLE_COUNT_1_BIT;
337
338 if (vkCreateImage(m_device, &imageInfo, nullptr, &m_image) !=
339 VK_SUCCESS) {
340 SLEAK_ERROR("VulkanTexture: Failed to create image");
341 return false;
342 }
343
344 VkMemoryRequirements memReqs;
345 vkGetImageMemoryRequirements(m_device, m_image, &memReqs);
346
347 VkMemoryAllocateInfo allocInfo{};
348 allocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
349 allocInfo.allocationSize = memReqs.size;
350 allocInfo.memoryTypeIndex = FindMemoryType(
351 memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
352
353 if (vkAllocateMemory(m_device, &allocInfo, nullptr, &m_imageMemory) !=
354 VK_SUCCESS) {
355 SLEAK_ERROR("VulkanTexture: Failed to allocate image memory");
356 return false;
357 }
358
359 vkBindImageMemory(m_device, m_image, m_imageMemory, 0);
360 return true;
361}
362
363bool VulkanTexture::CreateImageView(VkFormat format) {
364 VkImageViewCreateInfo viewInfo{};
365 viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
366 viewInfo.image = m_image;
367 viewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;
368 viewInfo.format = format;
369 viewInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
370 viewInfo.subresourceRange.baseMipLevel = 0;
371 viewInfo.subresourceRange.levelCount = m_mipLevels;
372 viewInfo.subresourceRange.baseArrayLayer = 0;
373 viewInfo.subresourceRange.layerCount = 1;
374
375 if (vkCreateImageView(m_device, &viewInfo, nullptr, &m_imageView) !=
376 VK_SUCCESS) {
377 SLEAK_ERROR("VulkanTexture: Failed to create image view");
378 return false;
379 }
380 return true;
381}
382
383// Blit-downsample each mip from the previous, leaving all levels in
384// SHADER_READ_ONLY. Assumes mip0 is filled and all mips are TRANSFER_DST.
385void VulkanTexture::GenerateMipmaps(VkCommandBuffer cmd, int32_t width,
386 int32_t height) {
387 VkImageMemoryBarrier barrier{};
388 barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
389 barrier.image = m_image;
390 barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
391 barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
392 barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
393 barrier.subresourceRange.baseArrayLayer = 0;
394 barrier.subresourceRange.layerCount = 1;
395 barrier.subresourceRange.levelCount = 1;
396
397 int32_t mipW = width, mipH = height;
398 for (uint32_t i = 1; i < m_mipLevels; ++i) {
399 // Source mip (i-1): DST -> SRC
400 barrier.subresourceRange.baseMipLevel = i - 1;
401 barrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
402 barrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL;
403 barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
404 barrier.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
405 vkCmdPipelineBarrier(cmd, VK_PIPELINE_STAGE_TRANSFER_BIT,
406 VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, nullptr, 0,
407 nullptr, 1, &barrier);
408
409 VkImageBlit blit{};
410 blit.srcOffsets[0] = {0, 0, 0};
411 blit.srcOffsets[1] = {mipW, mipH, 1};
412 blit.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
413 blit.srcSubresource.mipLevel = i - 1;
414 blit.srcSubresource.baseArrayLayer = 0;
415 blit.srcSubresource.layerCount = 1;
416 blit.dstOffsets[0] = {0, 0, 0};
417 blit.dstOffsets[1] = {mipW > 1 ? mipW / 2 : 1,
418 mipH > 1 ? mipH / 2 : 1, 1};
419 blit.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
420 blit.dstSubresource.mipLevel = i;
421 blit.dstSubresource.baseArrayLayer = 0;
422 blit.dstSubresource.layerCount = 1;
423 vkCmdBlitImage(cmd, m_image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
424 m_image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &blit,
425 VK_FILTER_LINEAR);
426
427 // Source mip (i-1): SRC -> SHADER_READ
428 barrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL;
429 barrier.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
430 barrier.srcAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
431 barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
432 vkCmdPipelineBarrier(cmd, VK_PIPELINE_STAGE_TRANSFER_BIT,
433 VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0, 0,
434 nullptr, 0, nullptr, 1, &barrier);
435
436 if (mipW > 1) mipW /= 2;
437 if (mipH > 1) mipH /= 2;
438 }
439
440 // Last mip: DST -> SHADER_READ
441 barrier.subresourceRange.baseMipLevel = m_mipLevels - 1;
442 barrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
443 barrier.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
444 barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
445 barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
446 vkCmdPipelineBarrier(cmd, VK_PIPELINE_STAGE_TRANSFER_BIT,
447 VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0, 0, nullptr,
448 0, nullptr, 1, &barrier);
449}
450
451bool VulkanTexture::CreateSampler() {
452 VkSamplerCreateInfo samplerInfo{};
453 samplerInfo.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO;
454
455 VkPhysicalDeviceProperties props{};
456 vkGetPhysicalDeviceProperties(m_physicalDevice, &props);
457 float deviceMaxAniso = props.limits.maxSamplerAnisotropy;
458
459 // Filter and mip mode
460 VkFilter magFilter = VK_FILTER_LINEAR;
461 VkFilter minFilter = VK_FILTER_LINEAR;
462 VkSamplerMipmapMode mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR;
463 VkBool32 anisotropyEnable = VK_FALSE;
464 float maxAnisotropy = 1.0f;
465
466 switch (m_filter) {
468 // GL parity: NEAREST_MIPMAP_NEAREST, no aniso (aniso + nearest
469 // min filter speckles steep, thin-geometry silhouettes)
470 magFilter = VK_FILTER_NEAREST;
471 minFilter = VK_FILTER_NEAREST;
472 mipmapMode = VK_SAMPLER_MIPMAP_MODE_NEAREST;
473 break;
475 mipmapMode = VK_SAMPLER_MIPMAP_MODE_NEAREST;
476 break;
478 mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR;
479 break;
481 anisotropyEnable = VK_TRUE; maxAnisotropy = 2.0f;
482 break;
484 anisotropyEnable = VK_TRUE; maxAnisotropy = 4.0f;
485 break;
487 anisotropyEnable = VK_TRUE; maxAnisotropy = 8.0f;
488 break;
490 anisotropyEnable = VK_TRUE; maxAnisotropy = 16.0f;
491 break;
492 default:
493 break;
494 }
495
496 // Never exceed the device's anisotropy limit.
497 maxAnisotropy = std::min(maxAnisotropy, deviceMaxAniso);
498
499 samplerInfo.magFilter = magFilter;
500 samplerInfo.minFilter = minFilter;
501
502 // Wrap mode
503 VkSamplerAddressMode addressMode = VK_SAMPLER_ADDRESS_MODE_REPEAT;
504 switch (m_wrapMode) {
506 addressMode = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
507 break;
509 addressMode = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER;
510 break;
512 addressMode = VK_SAMPLER_ADDRESS_MODE_MIRRORED_REPEAT;
513 break;
515 addressMode = VK_SAMPLER_ADDRESS_MODE_MIRROR_CLAMP_TO_EDGE;
516 break;
517 default:
518 break;
519 }
520 samplerInfo.addressModeU = addressMode;
521 samplerInfo.addressModeV = addressMode;
522 samplerInfo.addressModeW = addressMode;
523
524 samplerInfo.anisotropyEnable = anisotropyEnable;
525 samplerInfo.maxAnisotropy = maxAnisotropy;
526 samplerInfo.borderColor = VK_BORDER_COLOR_INT_OPAQUE_BLACK;
527 samplerInfo.unnormalizedCoordinates = VK_FALSE;
528 samplerInfo.compareEnable = VK_FALSE;
529 samplerInfo.mipmapMode = mipmapMode;
530 samplerInfo.mipLodBias = m_lodBias;
531 samplerInfo.minLod = 0.0f;
532 samplerInfo.maxLod = static_cast<float>(m_mipLevels);
533
534 if (vkCreateSampler(m_device, &samplerInfo, nullptr, &m_sampler) !=
535 VK_SUCCESS) {
536 SLEAK_ERROR("VulkanTexture: Failed to create sampler");
537 return false;
538 }
539 return true;
540}
541
542void VulkanTexture::TransitionImageLayout(VkImage image,
543 VkImageLayout oldLayout,
544 VkImageLayout newLayout) {
545 VkCommandBufferAllocateInfo allocInfo{};
546 allocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
547 allocInfo.commandPool = m_commandPool;
548 allocInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
549 allocInfo.commandBufferCount = 1;
550
551 VkCommandBuffer cmdBuffer;
552 vkAllocateCommandBuffers(m_device, &allocInfo, &cmdBuffer);
553
554 VkCommandBufferBeginInfo beginInfo{};
555 beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
556 beginInfo.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
557 vkBeginCommandBuffer(cmdBuffer, &beginInfo);
558
559 VkImageMemoryBarrier barrier{};
560 barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
561 barrier.oldLayout = oldLayout;
562 barrier.newLayout = newLayout;
563 barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
564 barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
565 barrier.image = image;
566 barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
567 barrier.subresourceRange.baseMipLevel = 0;
568 barrier.subresourceRange.levelCount = 1;
569 barrier.subresourceRange.baseArrayLayer = 0;
570 barrier.subresourceRange.layerCount = 1;
571
572 VkPipelineStageFlags srcStage;
573 VkPipelineStageFlags dstStage;
574
575 if (oldLayout == VK_IMAGE_LAYOUT_UNDEFINED &&
576 newLayout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL) {
577 barrier.srcAccessMask = 0;
578 barrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
579 srcStage = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
580 dstStage = VK_PIPELINE_STAGE_TRANSFER_BIT;
581 } else if (oldLayout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL &&
582 newLayout == VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL) {
583 barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
584 barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
585 srcStage = VK_PIPELINE_STAGE_TRANSFER_BIT;
586 dstStage = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
587 } else {
588 barrier.srcAccessMask = 0;
589 barrier.dstAccessMask = 0;
590 srcStage = VK_PIPELINE_STAGE_ALL_COMMANDS_BIT;
591 dstStage = VK_PIPELINE_STAGE_ALL_COMMANDS_BIT;
592 }
593
594 vkCmdPipelineBarrier(cmdBuffer, srcStage, dstStage, 0, 0, nullptr, 0,
595 nullptr, 1, &barrier);
596
597 vkEndCommandBuffer(cmdBuffer);
598
599 VkSubmitInfo submitInfo{};
600 submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
601 submitInfo.commandBufferCount = 1;
602 submitInfo.pCommandBuffers = &cmdBuffer;
603
604 VkFenceCreateInfo fenceInfo{};
605 fenceInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
606 VkFence copyFence;
607 vkCreateFence(m_device, &fenceInfo, nullptr, &copyFence);
608 vkQueueSubmit(m_graphicsQueue, 1, &submitInfo, copyFence);
609 vkWaitForFences(m_device, 1, &copyFence, VK_TRUE, UINT64_MAX);
610 vkDestroyFence(m_device, copyFence, nullptr);
611 vkFreeCommandBuffers(m_device, m_commandPool, 1, &cmdBuffer);
612}
613
614} // namespace RenderEngine
615} // namespace Sleak
int width
int height
#define SLEAK_ERROR(...)
Definition Logger.hpp:22
void Bind(uint32_t slot=0) const override
VulkanTexture(VkDevice device, VkPhysicalDevice physicalDevice, VkCommandPool commandPool, VkQueue graphicsQueue)
void SetWrapMode(TextureWrapMode wrapMode) override
uint64_t GetImGuiTextureID() const override
void SetLodBias(float bias) override
void SetFilter(TextureFilter filter) override
bool LoadFromFile(const std::string &filePath) override
Decodes an image file and uploads it as a new Vulkan image.
bool LoadFromMemory(const void *data, uint32_t width, uint32_t height, TextureFormat format) override
Uploads raw pixel data through a staging buffer and generates mips.
TextureFormat
Definition Texture.hpp:10
TextureFilter
Definition Texture.hpp:28
TextureWrapMode
Definition Texture.hpp:43
Backend-facing rendering layer shared by the four graphics backends.
Root namespace for everything the engine exposes.
Definition Camera.hpp:10