@@ -9,6 +9,7 @@ namespace VkRenderer
99 int texWidth, texHeight, texChannels;
1010 stbi_uc* pixels = stbi_load (TEXTURE_PATH.c_str (), &texWidth, &texHeight, &texChannels, STBI_rgb_alpha);
1111 VkDeviceSize imageSize = texWidth * texHeight * 4 ;
12+ mipLevels = static_cast <uint32_t >(std::floor (std::log2 (std::max (texWidth, texHeight)))) + 1 ;
1213
1314 if (!pixels) {
1415 throw std::runtime_error (" failed to load texture image!" );
@@ -25,19 +26,21 @@ namespace VkRenderer
2526
2627 stbi_image_free (pixels);
2728
28- createImage (texWidth, texHeight, VK_FORMAT_R8G8B8A8_SRGB, VK_IMAGE_TILING_OPTIMAL, VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, textureImage, textureImageMemory);
29+ createImage (texWidth, texHeight, mipLevels, VK_FORMAT_R8G8B8A8_SRGB, VK_IMAGE_TILING_OPTIMAL, VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, textureImage, textureImageMemory);
2930
30- transitionImageLayout (textureImage, VK_FORMAT_R8G8B8A8_SRGB, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, commandPool);
31- copyBufferToImage (stagingBuffer, textureImage, static_cast <uint32_t >(texWidth), static_cast <uint32_t >(texHeight), commandPool);
32- transitionImageLayout (textureImage, VK_FORMAT_R8G8B8A8_SRGB, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, commandPool);
31+ transitionImageLayout (textureImage, VK_FORMAT_R8G8B8A8_SRGB, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, commandPool,mipLevels );
32+ copyBufferToImage (stagingBuffer, textureImage, static_cast <uint32_t >(texWidth), static_cast <uint32_t >(texHeight), commandPool);
33+ // transitioned to VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL while generating mipmaps
3334
3435 vkDestroyBuffer (setup_ref->device , stagingBuffer, nullptr );
3536 vkFreeMemory (setup_ref->device , stagingBufferMemory, nullptr );
37+
38+ generateMipmaps (textureImage, VK_FORMAT_R8G8B8A8_SRGB, texWidth, texHeight, mipLevels, commandPool);
3639 }
3740 void VkTextureManager::createTextureImageView () {
38- textureImageView = createImageView (textureImage, VK_FORMAT_R8G8B8A8_SRGB, VK_IMAGE_ASPECT_COLOR_BIT);
41+ textureImageView = createImageView (textureImage, VK_FORMAT_R8G8B8A8_SRGB, VK_IMAGE_ASPECT_COLOR_BIT, mipLevels );
3942 }
40- VkImageView VkTextureManager::createImageView (VkImage image, VkFormat format, VkImageAspectFlags aspectFlags)
43+ VkImageView VkTextureManager::createImageView (VkImage image, VkFormat format, VkImageAspectFlags aspectFlags, uint32_t mipLevels )
4144 {
4245 VkImageViewCreateInfo viewInfo{};
4346 viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
@@ -46,7 +49,7 @@ namespace VkRenderer
4649 viewInfo.format = format;
4750 viewInfo.subresourceRange .aspectMask = aspectFlags;
4851 viewInfo.subresourceRange .baseMipLevel = 0 ;
49- viewInfo.subresourceRange .levelCount = 1 ;
52+ viewInfo.subresourceRange .levelCount = mipLevels ;
5053 viewInfo.subresourceRange .baseArrayLayer = 0 ;
5154 viewInfo.subresourceRange .layerCount = 1 ;
5255
@@ -58,15 +61,15 @@ namespace VkRenderer
5861 return imageView;
5962 }
6063
61- void VkTextureManager::createImage (uint32_t width, uint32_t height, VkFormat format, VkImageTiling tiling, VkImageUsageFlags usage, VkMemoryPropertyFlags properties, VkImage& image, VkDeviceMemory& imageMemory)
64+ void VkTextureManager::createImage (uint32_t width, uint32_t height, uint32_t mipLevels, VkFormat format, VkImageTiling tiling, VkImageUsageFlags usage, VkMemoryPropertyFlags properties, VkImage& image, VkDeviceMemory& imageMemory)
6265 {
6366 VkImageCreateInfo imageInfo{};
6467 imageInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
6568 imageInfo.imageType = VK_IMAGE_TYPE_2D;
6669 imageInfo.extent .width = width;
6770 imageInfo.extent .height = height;
6871 imageInfo.extent .depth = 1 ;
69- imageInfo.mipLevels = 1 ;
72+ imageInfo.mipLevels = mipLevels ;
7073 imageInfo.arrayLayers = 1 ;
7174 imageInfo.format = format;
7275 imageInfo.tiling = tiling;
@@ -109,6 +112,9 @@ namespace VkRenderer
109112 samplerInfo.compareEnable = VK_FALSE;
110113 samplerInfo.compareOp = VK_COMPARE_OP_ALWAYS;
111114 samplerInfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR;
115+ samplerInfo.minLod = 0 ;
116+ samplerInfo.maxLod = static_cast <float >(mipLevels);
117+ samplerInfo.mipLodBias = 0 .0f ;
112118
113119 if (vkCreateSampler (setup_ref->device , &samplerInfo, nullptr , &textureSampler) != VK_SUCCESS) {
114120 throw std::runtime_error (" failed to create texture sampler!" );
@@ -137,8 +143,94 @@ namespace VkRenderer
137143
138144 buffer_ref->endSingleTimeCommands (commandBuffer,commandPool);
139145 }
146+ void VkTextureManager::generateMipmaps (VkImage image, VkFormat imageFormat, int32_t texWidth, int32_t texHeight, uint32_t mipLevels, VkCommandPool& commandPool) {
147+ // Check if image format supports linear blitting
148+ VkFormatProperties formatProperties;
149+ vkGetPhysicalDeviceFormatProperties (setup_ref->physicalDevice , imageFormat, &formatProperties);
150+
151+ if (!(formatProperties.optimalTilingFeatures & VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT)) {
152+ throw std::runtime_error (" texture image format does not support linear blitting!" );
153+ }
154+
155+ VkCommandBuffer commandBuffer = buffer_ref->beginSingleTimeCommands (commandPool);
140156
141- void VkTextureManager::transitionImageLayout (VkImage image, VkFormat format, VkImageLayout oldLayout, VkImageLayout newLayout, VkCommandPool& commandPool)
157+ VkImageMemoryBarrier barrier{};
158+ barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
159+ barrier.image = image;
160+ barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
161+ barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
162+ barrier.subresourceRange .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
163+ barrier.subresourceRange .baseArrayLayer = 0 ;
164+ barrier.subresourceRange .layerCount = 1 ;
165+ barrier.subresourceRange .levelCount = 1 ;
166+
167+ int32_t mipWidth = texWidth;
168+ int32_t mipHeight = texHeight;
169+
170+ for (uint32_t i = 1 ; i < mipLevels; i++) {
171+ barrier.subresourceRange .baseMipLevel = i - 1 ;
172+ barrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
173+ barrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL;
174+ barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
175+ barrier.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
176+
177+ vkCmdPipelineBarrier (commandBuffer,
178+ VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0 ,
179+ 0 , nullptr ,
180+ 0 , nullptr ,
181+ 1 , &barrier);
182+
183+ VkImageBlit blit{};
184+ blit.srcOffsets [0 ] = {0 , 0 , 0 };
185+ blit.srcOffsets [1 ] = {mipWidth, mipHeight, 1 };
186+ blit.srcSubresource .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
187+ blit.srcSubresource .mipLevel = i - 1 ;
188+ blit.srcSubresource .baseArrayLayer = 0 ;
189+ blit.srcSubresource .layerCount = 1 ;
190+ blit.dstOffsets [0 ] = {0 , 0 , 0 };
191+ blit.dstOffsets [1 ] = { mipWidth > 1 ? mipWidth / 2 : 1 , mipHeight > 1 ? mipHeight / 2 : 1 , 1 };
192+ blit.dstSubresource .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
193+ blit.dstSubresource .mipLevel = i;
194+ blit.dstSubresource .baseArrayLayer = 0 ;
195+ blit.dstSubresource .layerCount = 1 ;
196+
197+ vkCmdBlitImage (commandBuffer,
198+ image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
199+ image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
200+ 1 , &blit,
201+ VK_FILTER_LINEAR);
202+
203+ barrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL;
204+ barrier.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
205+ barrier.srcAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
206+ barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
207+
208+ vkCmdPipelineBarrier (commandBuffer,
209+ VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0 ,
210+ 0 , nullptr ,
211+ 0 , nullptr ,
212+ 1 , &barrier);
213+
214+ if (mipWidth > 1 ) mipWidth /= 2 ;
215+ if (mipHeight > 1 ) mipHeight /= 2 ;
216+ }
217+
218+ barrier.subresourceRange .baseMipLevel = mipLevels - 1 ;
219+ barrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
220+ barrier.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
221+ barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
222+ barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
223+
224+ vkCmdPipelineBarrier (commandBuffer,
225+ VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0 ,
226+ 0 , nullptr ,
227+ 0 , nullptr ,
228+ 1 , &barrier);
229+
230+ buffer_ref->endSingleTimeCommands (commandBuffer, commandPool);
231+ }
232+
233+ void VkTextureManager::transitionImageLayout (VkImage image, VkFormat format, VkImageLayout oldLayout, VkImageLayout newLayout, VkCommandPool& commandPool, uint32_t mipLevels)
142234 {
143235 VkCommandBuffer commandBuffer = buffer_ref->beginSingleTimeCommands (commandPool);
144236
@@ -151,7 +243,7 @@ namespace VkRenderer
151243 barrier.image = image;
152244 barrier.subresourceRange .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
153245 barrier.subresourceRange .baseMipLevel = 0 ;
154- barrier.subresourceRange .levelCount = 1 ;
246+ barrier.subresourceRange .levelCount = mipLevels ;
155247 barrier.subresourceRange .baseArrayLayer = 0 ;
156248 barrier.subresourceRange .layerCount = 1 ;
157249
0 commit comments