coral_texture.cpp 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193
  1. #include "coral_texture.h"
  2. // STD
  3. #include <iostream>
  4. // LIBS
  5. #include <stb_image.h>
  6. #include "vk_initializers.h"
  7. #include "coral_device.h"
  8. #include "coral_buffer.h"
  9. using namespace coral_3d;
  10. bool coral_texture::Builder::load_image_from_file(coral_device& device, const std::string& file_name, VkFormat format)
  11. {
  12. int tex_width, tex_height, tex_channels;
  13. stbi_uc* img{ stbi_load(file_name.c_str(), &tex_width, &tex_height, &tex_channels, STBI_rgb_alpha)};
  14. if (!img)
  15. {
  16. std::cout << "Failed to load texture file " << file_name << std::endl;
  17. return false;
  18. }
  19. mip_levels = static_cast<uint32_t>(std::floor(std::log2(std::max(tex_width, tex_height)))) + 1;
  20. void* pPixels{ img };
  21. VkDeviceSize img_size{ static_cast<uint64_t>(tex_width * tex_height * 4) };
  22. // This format must match with the format loaded from stb_image
  23. VkFormat img_format{ format };
  24. // Holds texture data to upload to GPU
  25. coral_buffer staging_buffer
  26. {
  27. device,
  28. img_size,
  29. 1,
  30. VK_BUFFER_USAGE_TRANSFER_SRC_BIT,
  31. VMA_MEMORY_USAGE_AUTO,
  32. VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT
  33. };
  34. // Copy data to buffer
  35. staging_buffer.map();
  36. staging_buffer.write_to_buffer(pPixels);
  37. // Free the pixels
  38. stbi_image_free(img);
  39. VkExtent3D img_extent;
  40. img_extent.width = static_cast<uint32_t>(tex_width);
  41. img_extent.height = static_cast<uint32_t>(tex_height);
  42. img_extent.depth = 1;
  43. VkImageCreateInfo image_ci{ vkinit::image_ci(img_format, VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT, img_extent) };
  44. image_ci.mipLevels = mip_levels;
  45. VmaAllocationCreateInfo image_ai{};
  46. image_ai.usage = VMA_MEMORY_USAGE_GPU_ONLY;
  47. vmaCreateImage(device.allocator(), &image_ci, &image_ai,
  48. &image.image, &image.allocation, nullptr);
  49. // Transition layout using barries and copy buffer to image
  50. device.transition_image_layout(image.image, img_format, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, mip_levels);
  51. device.copy_buffer_to_image(staging_buffer.get_buffer(), image, img_extent.width, img_extent.height, 1);
  52. generate_mipmaps(device, tex_width, tex_height);
  53. return true;
  54. }
  55. void coral_texture::Builder::generate_mipmaps(coral_device& device, uint32_t width, uint32_t height)
  56. {
  57. device.immediate_submit([&](VkCommandBuffer cmd) {
  58. VkImageMemoryBarrier barrier{};
  59. barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
  60. barrier.image = image.image;
  61. barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
  62. barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
  63. barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
  64. barrier.subresourceRange.baseArrayLayer = 0;
  65. barrier.subresourceRange.layerCount = 1;
  66. barrier.subresourceRange.levelCount = 1;
  67. int32_t mipWidth = width;
  68. int32_t mipHeight = height;
  69. for (uint32_t i = 1; i < mip_levels; i++) {
  70. barrier.subresourceRange.baseMipLevel = i - 1;
  71. barrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
  72. barrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL;
  73. barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
  74. barrier.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
  75. vkCmdPipelineBarrier(cmd,
  76. VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0,
  77. 0, nullptr,
  78. 0, nullptr,
  79. 1, &barrier);
  80. VkImageBlit blit{};
  81. blit.srcOffsets[0] = { 0, 0, 0 };
  82. blit.srcOffsets[1] = { mipWidth, mipHeight, 1 };
  83. blit.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
  84. blit.srcSubresource.mipLevel = i - 1;
  85. blit.srcSubresource.baseArrayLayer = 0;
  86. blit.srcSubresource.layerCount = 1;
  87. blit.dstOffsets[0] = { 0, 0, 0 };
  88. blit.dstOffsets[1] = { mipWidth > 1 ? mipWidth / 2 : 1, mipHeight > 1 ? mipHeight / 2 : 1, 1 };
  89. blit.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
  90. blit.dstSubresource.mipLevel = i;
  91. blit.dstSubresource.baseArrayLayer = 0;
  92. blit.dstSubresource.layerCount = 1;
  93. vkCmdBlitImage(cmd,
  94. image.image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
  95. image.image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
  96. 1, &blit,
  97. VK_FILTER_LINEAR);
  98. barrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL;
  99. barrier.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
  100. barrier.srcAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
  101. barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
  102. vkCmdPipelineBarrier(cmd,
  103. VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0,
  104. 0, nullptr,
  105. 0, nullptr,
  106. 1, &barrier);
  107. if (mipWidth > 1) mipWidth /= 2;
  108. if (mipHeight > 1) mipHeight /= 2;
  109. }
  110. barrier.subresourceRange.baseMipLevel = mip_levels - 1;
  111. barrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
  112. barrier.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
  113. barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
  114. barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
  115. vkCmdPipelineBarrier(cmd,
  116. VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0,
  117. 0, nullptr,
  118. 0, nullptr,
  119. 1, &barrier);
  120. });
  121. }
  122. coral_texture::coral_texture(coral_device& device, AllocatedImage image, uint32_t mip_levels, VkFormat format)
  123. : device_{ device }
  124. , image_{ image }
  125. , mip_levels_{ mip_levels }
  126. {
  127. create_image_view(format);
  128. create_texture_sampler();
  129. }
  130. coral_texture::~coral_texture()
  131. {
  132. vkDestroySampler(device_.device(), sampler_, nullptr);
  133. vkDestroyImageView(device_.device(), image_view_, nullptr);
  134. vmaDestroyImage(device_.allocator(), image_.image, image_.allocation);
  135. }
  136. std::unique_ptr<coral_texture> coral_texture::create_texture_from_file(coral_device& device, const std::string& file_path, VkFormat format)
  137. {
  138. Builder builder{};
  139. builder.load_image_from_file(device, file_path, format);
  140. return std::make_unique<coral_texture>(device, builder.image, builder.mip_levels, format);
  141. }
  142. void coral_texture::create_image_view(VkFormat format)
  143. {
  144. VkImageViewCreateInfo image_info{ vkinit::image_view_ci(format, image_.image, VK_IMAGE_ASPECT_COLOR_BIT) };
  145. image_info.subresourceRange.levelCount = mip_levels_;
  146. if (vkCreateImageView(device_.device(), &image_info, nullptr, &image_view_) != VK_SUCCESS)
  147. throw std::runtime_error("ERROR! coral_texture::create_image_view() >> Failed to create image view!");
  148. }
  149. void coral_texture::create_texture_sampler()
  150. {
  151. VkSamplerCreateInfo sampler_info{ vkinit::sampler_ci(VK_FILTER_LINEAR, device_.properties.limits.maxSamplerAnisotropy) };
  152. sampler_info.maxLod = static_cast<float>(mip_levels_);
  153. if (vkCreateSampler(device_.device(), &sampler_info, nullptr, &sampler_) != VK_SUCCESS)
  154. throw std::runtime_error("ERROR! coral_texture::create_texture_sampler() >> Failed to create texture sampler!");
  155. }