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 <limits> // std::numeric_limits
#include <fstream> // to load shaders
#include <memory>
#include <string>
#include <sys/types.h>
#include <utility>
@ -183,6 +184,8 @@ class HelloTriangleApplication {
vk::raii::Image depthImage = nullptr;
vk::raii::DeviceMemory depthImageMemory = nullptr;
vk::raii::ImageView depthImageView = nullptr;
// Minmaps
uint32_t mipLevels;
// Indexes
uint32_t queueIndex = ~0;
uint32_t frameIndex = 0;
@ -490,7 +493,8 @@ class HelloTriangleApplication {
swapChainImageViews.emplace_back(createImageView(
swapChainImages[i],
swapChainImageFormat,
vk::ImageAspectFlagBits::eColor
vk::ImageAspectFlagBits::eColor,
1
));
}
#ifndef NDEBUG
@ -684,6 +688,7 @@ class HelloTriangleApplication {
createImage(
swapChainExtent.width,
swapChainExtent.height,
1,
depthFormat,
vk::ImageTiling::eOptimal,
vk::ImageUsageFlagBits::eDepthStencilAttachment,
@ -694,7 +699,8 @@ class HelloTriangleApplication {
depthImageView = createImageView(
depthImage,
depthFormat,
vk::ImageAspectFlagBits::eDepth
vk::ImageAspectFlagBits::eDepth,
1
);
#ifndef NDEBUG
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);
vk::DeviceSize imageSize = texWidth * texHeight * 4;
mipLevels = static_cast<uint32_t> (std::floor(std::log2(std::max(texWidth, texHeight)))) + 1;
if (!pixels) {
throw std::runtime_error("Failed to load texture image!\nAborting...");
}
@ -731,17 +739,41 @@ class HelloTriangleApplication {
createImage(
texWidth,
texHeight,
mipLevels,
vk::Format::eR8G8B8A8Srgb,
vk::ImageTiling::eOptimal,
vk::ImageUsageFlagBits::eTransferDst | vk::ImageUsageFlagBits::eSampled,
vk::ImageUsageFlagBits::eTransferSrc | vk::ImageUsageFlagBits::eTransferDst | vk::ImageUsageFlagBits::eSampled,
vk::MemoryPropertyFlagBits::eDeviceLocal,
textureImage,
textureImageMemory
);
transitionImageLayout(textureImage, vk::ImageLayout::eUndefined, vk::ImageLayout::eTransferDstOptimal);
copyBufferToImage(stagingBuffer, textureImage, static_cast<uint32_t>(texWidth), static_cast<uint32_t>(texHeight));
transitionImageLayout(textureImage, vk::ImageLayout::eTransferDstOptimal, vk::ImageLayout::eShaderReadOnlyOptimal);
transitionImageLayout(
textureImage,
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
std::cout << "Texture image created !\n";
@ -755,7 +787,8 @@ class HelloTriangleApplication {
textureImageView = createImageView(
textureImage,
vk::Format::eR8G8B8A8Srgb,
vk::ImageAspectFlagBits::eColor
vk::ImageAspectFlagBits::eColor,
mipLevels
);
#ifndef NDEBUG
std::cout << "Texture Image created !\n";
@ -765,6 +798,7 @@ class HelloTriangleApplication {
void createImage(
uint32_t width,
uint32_t height,
uint32_t mipLevels,
vk::Format format,
vk::ImageTiling tiling,
vk::ImageUsageFlags usage,
@ -779,7 +813,7 @@ class HelloTriangleApplication {
.imageType = vk::ImageType::e2D,
.format = format,
.extent = { width, height, 1 },
.mipLevels = 1,
.mipLevels = mipLevels,
.arrayLayers = 1,
.samples = vk::SampleCountFlagBits::e1,
.tiling = tiling,
@ -820,7 +854,7 @@ class HelloTriangleApplication {
.compareEnable = vk::True,
.compareOp = vk::CompareOp::eAlways,
.minLod = 0.0f,
.maxLod = 0.0f,
.maxLod = vk::LodClampNone,
.borderColor = vk::BorderColor::eIntOpaqueBlack,
.unnormalizedCoordinates = vk::False
};
@ -1222,7 +1256,8 @@ class HelloTriangleApplication {
void transitionImageLayout(
const vk::raii::Image& image,
vk::ImageLayout oldLayout,
vk::ImageLayout newLayout
vk::ImageLayout newLayout,
uint32_t mipLevels
) {
auto commandBuffer = beginSingleTimeCommands();
@ -1236,7 +1271,7 @@ class HelloTriangleApplication {
.subresourceRange = {
vk::ImageAspectFlagBits::eColor,
0,
1,
mipLevels,
0,
1
}
@ -1444,21 +1479,28 @@ class HelloTriangleApplication {
const vk::DebugUtilsMessengerCallbackDataEXT *pCallbackData,
void *
){
if (severity == vk::DebugUtilsMessageSeverityFlagBitsEXT::eError || severity == vk::DebugUtilsMessageSeverityFlagBitsEXT::eWarning) {
if (severity == vk::DebugUtilsMessageSeverityFlagBitsEXT::eError || severity == vk::DebugUtilsMessageSeverityFlagBitsEXT::eWarning){
#ifndef NDEBUG
std::cerr << "Validation layer: type " << to_string(type) << " msg: " << pCallbackData->pMessage << std::endl;
#endif
}
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));
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();
for (uint32_t i = 0; i < memProperties.memoryTypeCount; i++) {
if ((typeFilter & (1 << i)) && (memProperties.memoryTypes[i].propertyFlags & properties) == properties) {
@ -1593,11 +1635,12 @@ class HelloTriangleApplication {
queue.waitIdle();
}
vk::raii::ImageView createImageView(
vk::Image image,
[[nodiscard]] vk::raii::ImageView createImageView(
const vk::Image& image,
vk::Format format,
vk::ImageAspectFlags aspectFlags
) {
vk::ImageAspectFlags aspectFlags,
uint32_t mipLevels
) const {
vk::ImageViewCreateInfo viewInfo{
.image = image,
.viewType = vk::ImageViewType::e2D,
@ -1605,7 +1648,7 @@ class HelloTriangleApplication {
.subresourceRange = {
aspectFlags,
0,
1,
mipLevels,
0,
1
}
@ -1649,6 +1692,132 @@ class HelloTriangleApplication {
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() {