Added LoDs

This commit is contained in:
Alexandre 2026-04-14 01:15:37 +02:00
parent 2fbc0e8f22
commit ddb6f97646

View File

@ -11,6 +11,7 @@
#include <ios> #include <ios>
#include <limits> // std::numeric_limits #include <limits> // std::numeric_limits
#include <fstream> // to load shaders #include <fstream> // to load shaders
#include <memory>
#include <string> #include <string>
#include <sys/types.h> #include <sys/types.h>
#include <utility> #include <utility>
@ -183,6 +184,8 @@ class HelloTriangleApplication {
vk::raii::Image depthImage = nullptr; vk::raii::Image depthImage = nullptr;
vk::raii::DeviceMemory depthImageMemory = nullptr; vk::raii::DeviceMemory depthImageMemory = nullptr;
vk::raii::ImageView depthImageView = nullptr; vk::raii::ImageView depthImageView = nullptr;
// Minmaps
uint32_t mipLevels;
// Indexes // Indexes
uint32_t queueIndex = ~0; uint32_t queueIndex = ~0;
uint32_t frameIndex = 0; uint32_t frameIndex = 0;
@ -490,7 +493,8 @@ class HelloTriangleApplication {
swapChainImageViews.emplace_back(createImageView( swapChainImageViews.emplace_back(createImageView(
swapChainImages[i], swapChainImages[i],
swapChainImageFormat, swapChainImageFormat,
vk::ImageAspectFlagBits::eColor vk::ImageAspectFlagBits::eColor,
1
)); ));
} }
#ifndef NDEBUG #ifndef NDEBUG
@ -684,6 +688,7 @@ class HelloTriangleApplication {
createImage( createImage(
swapChainExtent.width, swapChainExtent.width,
swapChainExtent.height, swapChainExtent.height,
1,
depthFormat, depthFormat,
vk::ImageTiling::eOptimal, vk::ImageTiling::eOptimal,
vk::ImageUsageFlagBits::eDepthStencilAttachment, vk::ImageUsageFlagBits::eDepthStencilAttachment,
@ -694,7 +699,8 @@ class HelloTriangleApplication {
depthImageView = createImageView( depthImageView = createImageView(
depthImage, depthImage,
depthFormat, depthFormat,
vk::ImageAspectFlagBits::eDepth vk::ImageAspectFlagBits::eDepth,
1
); );
#ifndef NDEBUG #ifndef NDEBUG
std::cout << "Depth ressouce created !\n"; std::cout << "Depth ressouce created !\n";
@ -709,6 +715,8 @@ class HelloTriangleApplication {
stbi_uc* pixels = stbi_load(TEXTURE_PATH.c_str(), &texWidth, &texHeight, &texChannels, STBI_rgb_alpha); stbi_uc* pixels = stbi_load(TEXTURE_PATH.c_str(), &texWidth, &texHeight, &texChannels, STBI_rgb_alpha);
vk::DeviceSize imageSize = texWidth * texHeight * 4; vk::DeviceSize imageSize = texWidth * texHeight * 4;
mipLevels = static_cast<uint32_t> (std::floor(std::log2(std::max(texWidth, texHeight)))) + 1;
if (!pixels) { if (!pixels) {
throw std::runtime_error("Failed to load texture image!\nAborting..."); throw std::runtime_error("Failed to load texture image!\nAborting...");
} }
@ -731,17 +739,41 @@ class HelloTriangleApplication {
createImage( createImage(
texWidth, texWidth,
texHeight, texHeight,
mipLevels,
vk::Format::eR8G8B8A8Srgb, vk::Format::eR8G8B8A8Srgb,
vk::ImageTiling::eOptimal, vk::ImageTiling::eOptimal,
vk::ImageUsageFlagBits::eTransferDst | vk::ImageUsageFlagBits::eSampled, vk::ImageUsageFlagBits::eTransferSrc | vk::ImageUsageFlagBits::eTransferDst | vk::ImageUsageFlagBits::eSampled,
vk::MemoryPropertyFlagBits::eDeviceLocal, vk::MemoryPropertyFlagBits::eDeviceLocal,
textureImage, textureImage,
textureImageMemory textureImageMemory
); );
transitionImageLayout(textureImage, vk::ImageLayout::eUndefined, vk::ImageLayout::eTransferDstOptimal); transitionImageLayout(
copyBufferToImage(stagingBuffer, textureImage, static_cast<uint32_t>(texWidth), static_cast<uint32_t>(texHeight)); textureImage,
transitionImageLayout(textureImage, vk::ImageLayout::eTransferDstOptimal, vk::ImageLayout::eShaderReadOnlyOptimal); vk::ImageLayout::eUndefined,
vk::ImageLayout::eTransferDstOptimal,
mipLevels
);
copyBufferToImage(
stagingBuffer,
textureImage,
static_cast<uint32_t>(texWidth),
static_cast<uint32_t>(texHeight)
);
transitionImageLayout(
textureImage,
vk::ImageLayout::eTransferDstOptimal,
vk::ImageLayout::eShaderReadOnlyOptimal,
mipLevels
);
generateMipmaps(
textureImage,
vk::Format::eR8G8B8A8Srgb,
texWidth,
texHeight,
mipLevels
);
#ifndef NDEBUG #ifndef NDEBUG
std::cout << "Texture image created !\n"; std::cout << "Texture image created !\n";
@ -755,7 +787,8 @@ class HelloTriangleApplication {
textureImageView = createImageView( textureImageView = createImageView(
textureImage, textureImage,
vk::Format::eR8G8B8A8Srgb, vk::Format::eR8G8B8A8Srgb,
vk::ImageAspectFlagBits::eColor vk::ImageAspectFlagBits::eColor,
mipLevels
); );
#ifndef NDEBUG #ifndef NDEBUG
std::cout << "Texture Image created !\n"; std::cout << "Texture Image created !\n";
@ -765,6 +798,7 @@ class HelloTriangleApplication {
void createImage( void createImage(
uint32_t width, uint32_t width,
uint32_t height, uint32_t height,
uint32_t mipLevels,
vk::Format format, vk::Format format,
vk::ImageTiling tiling, vk::ImageTiling tiling,
vk::ImageUsageFlags usage, vk::ImageUsageFlags usage,
@ -779,7 +813,7 @@ class HelloTriangleApplication {
.imageType = vk::ImageType::e2D, .imageType = vk::ImageType::e2D,
.format = format, .format = format,
.extent = { width, height, 1 }, .extent = { width, height, 1 },
.mipLevels = 1, .mipLevels = mipLevels,
.arrayLayers = 1, .arrayLayers = 1,
.samples = vk::SampleCountFlagBits::e1, .samples = vk::SampleCountFlagBits::e1,
.tiling = tiling, .tiling = tiling,
@ -820,7 +854,7 @@ class HelloTriangleApplication {
.compareEnable = vk::True, .compareEnable = vk::True,
.compareOp = vk::CompareOp::eAlways, .compareOp = vk::CompareOp::eAlways,
.minLod = 0.0f, .minLod = 0.0f,
.maxLod = 0.0f, .maxLod = vk::LodClampNone,
.borderColor = vk::BorderColor::eIntOpaqueBlack, .borderColor = vk::BorderColor::eIntOpaqueBlack,
.unnormalizedCoordinates = vk::False .unnormalizedCoordinates = vk::False
}; };
@ -1222,7 +1256,8 @@ class HelloTriangleApplication {
void transitionImageLayout( void transitionImageLayout(
const vk::raii::Image& image, const vk::raii::Image& image,
vk::ImageLayout oldLayout, vk::ImageLayout oldLayout,
vk::ImageLayout newLayout vk::ImageLayout newLayout,
uint32_t mipLevels
) { ) {
auto commandBuffer = beginSingleTimeCommands(); auto commandBuffer = beginSingleTimeCommands();
@ -1236,7 +1271,7 @@ class HelloTriangleApplication {
.subresourceRange = { .subresourceRange = {
vk::ImageAspectFlagBits::eColor, vk::ImageAspectFlagBits::eColor,
0, 0,
1, mipLevels,
0, 0,
1 1
} }
@ -1451,14 +1486,21 @@ class HelloTriangleApplication {
} }
return vk::False; return vk::False;
} };
static void framebufferResizeCallback(GLFWwindow* window, int width, int height) { static void framebufferResizeCallback(
GLFWwindow* window,
int width,
int height
){
auto app = reinterpret_cast<HelloTriangleApplication*>(glfwGetWindowUserPointer(window)); auto app = reinterpret_cast<HelloTriangleApplication*>(glfwGetWindowUserPointer(window));
app->framebufferResized = true; app->framebufferResized = true;
} };
uint32_t findMemoryType(uint32_t typeFilter, vk::MemoryPropertyFlags properties) { uint32_t findMemoryType(
uint32_t typeFilter,
vk::MemoryPropertyFlags properties
){
vk::PhysicalDeviceMemoryProperties memProperties = physicalDevice.getMemoryProperties(); vk::PhysicalDeviceMemoryProperties memProperties = physicalDevice.getMemoryProperties();
for (uint32_t i = 0; i < memProperties.memoryTypeCount; i++) { for (uint32_t i = 0; i < memProperties.memoryTypeCount; i++) {
if ((typeFilter & (1 << i)) && (memProperties.memoryTypes[i].propertyFlags & properties) == properties) { if ((typeFilter & (1 << i)) && (memProperties.memoryTypes[i].propertyFlags & properties) == properties) {
@ -1593,11 +1635,12 @@ class HelloTriangleApplication {
queue.waitIdle(); queue.waitIdle();
} }
vk::raii::ImageView createImageView( [[nodiscard]] vk::raii::ImageView createImageView(
vk::Image image, const vk::Image& image,
vk::Format format, vk::Format format,
vk::ImageAspectFlags aspectFlags vk::ImageAspectFlags aspectFlags,
) { uint32_t mipLevels
) const {
vk::ImageViewCreateInfo viewInfo{ vk::ImageViewCreateInfo viewInfo{
.image = image, .image = image,
.viewType = vk::ImageViewType::e2D, .viewType = vk::ImageViewType::e2D,
@ -1605,7 +1648,7 @@ class HelloTriangleApplication {
.subresourceRange = { .subresourceRange = {
aspectFlags, aspectFlags,
0, 0,
1, mipLevels,
0, 0,
1 1
} }
@ -1649,6 +1692,132 @@ class HelloTriangleApplication {
return format == vk::Format::eD32SfloatS8Uint || format == vk::Format::eD24UnormS8Uint; return format == vk::Format::eD32SfloatS8Uint || format == vk::Format::eD24UnormS8Uint;
} }
void generateMipmaps(
vk::raii::Image& image,
vk::Format imageFormat,
int32_t texWidth,
int32_t texHeight,
uint32_t mipLevels
){
#ifndef NDEBUG
std::cout << "Generating mipmaps...\n";
#endif
vk::FormatProperties formatProperties = physicalDevice . getFormatProperties(imageFormat);
if (!(formatProperties.optimalTilingFeatures & vk::FormatFeatureFlagBits::eSampledImageFilterLinear)) {
throw std::runtime_error("Texture image format does not support linear blitting !\nAborting...");
}
vk::raii::CommandBuffer commandBuffer = beginSingleTimeCommands();
vk::ImageMemoryBarrier barrier{};
barrier.srcAccessMask = vk::AccessFlagBits::eTransferWrite;
barrier.dstAccessMask = vk::AccessFlagBits::eTransferRead;
barrier.oldLayout = vk::ImageLayout::eTransferDstOptimal;
barrier.newLayout = vk::ImageLayout::eTransferSrcOptimal;
barrier.srcQueueFamilyIndex = vk::QueueFamilyIgnored;
barrier.dstQueueFamilyIndex = vk::QueueFamilyIgnored;
barrier.image = image;
barrier.subresourceRange.aspectMask = vk::ImageAspectFlagBits::eColor;
barrier.subresourceRange.baseArrayLayer = 0;
barrier.subresourceRange.layerCount = 1;
barrier.subresourceRange.levelCount = 1;
int32_t mipWidth = texWidth;
int32_t mipHeight = texHeight;
for (uint32_t i = 1; i < mipLevels; i++) {
barrier.subresourceRange.baseMipLevel = i - 1;
barrier.oldLayout = vk::ImageLayout::eTransferDstOptimal;
barrier.newLayout = vk::ImageLayout::eTransferSrcOptimal;
barrier.srcAccessMask = vk::AccessFlagBits::eTransferWrite;
barrier.dstAccessMask = vk::AccessFlagBits::eTransferRead;
commandBuffer.pipelineBarrier(
vk::PipelineStageFlagBits::eTransfer,
vk::PipelineStageFlagBits::eTransfer,
{},
{},
{},
barrier
);
std::array<vk::Offset3D, 2> srcOffsets = {
vk::Offset3D{0, 0, 0},
vk::Offset3D{mipWidth, mipHeight, 1}
};
std::array<vk::Offset3D, 2> dstOffsets = {
vk::Offset3D{0, 0, 0},
vk::Offset3D{mipWidth > 1 ? mipWidth / 2 : 1, mipHeight > 1 ? mipHeight / 2 : 1, 1}
};
vk::ImageBlit blit = {
.srcSubresource = {},
.srcOffsets = srcOffsets,
.dstSubresource = {},
.dstOffsets = dstOffsets,
};
blit.srcSubresource = vk::ImageSubresourceLayers(
vk::ImageAspectFlagBits::eColor,
i - 1,
0,
1
);
blit.dstSubresource = vk::ImageSubresourceLayers(
vk::ImageAspectFlagBits::eColor,
i,
0,
1
);
commandBuffer.blitImage(
image,
vk::ImageLayout::eTransferSrcOptimal,
image,
vk::ImageLayout::eTransferDstOptimal,
{ blit },
vk::Filter::eLinear
);
barrier.oldLayout = vk::ImageLayout::eTransferSrcOptimal;
barrier.newLayout = vk::ImageLayout::eShaderReadOnlyOptimal;
barrier.srcAccessMask = vk::AccessFlagBits::eTransferRead;
barrier.dstAccessMask = vk::AccessFlagBits::eShaderRead;
commandBuffer.pipelineBarrier(
vk::PipelineStageFlagBits::eTransfer,
vk::PipelineStageFlagBits::eFragmentShader,
{},
{},
{},
barrier
);
if (mipWidth > 1) mipWidth /= 2;
if (mipHeight > 1) mipHeight /= 2;
}
barrier.subresourceRange.baseMipLevel = mipLevels - 1;
barrier.oldLayout = vk::ImageLayout::eTransferDstOptimal;
barrier.newLayout = vk::ImageLayout::eShaderReadOnlyOptimal;
barrier.srcAccessMask = vk::AccessFlagBits::eTransferWrite;
barrier.dstAccessMask = vk::AccessFlagBits::eShaderRead;
commandBuffer.pipelineBarrier(
vk::PipelineStageFlagBits::eTransfer,
vk::PipelineStageFlagBits::eFragmentShader,
{},
{},
{},
barrier
);
endSingleTimeCommands(commandBuffer);
#ifndef NDEBUG
std::cout << "Mipmaps generated !\n";
#endif
}
}; };
int main() { int main() {