aboutsummaryrefslogtreecommitdiff
path: root/src/engine.cpp
diff options
context:
space:
mode:
authorvimene <vincent.menegaux@gmail.com>2026-02-03 20:26:01 +0100
committervimene <vincent.menegaux@gmail.com>2026-02-03 20:26:01 +0100
commitcbf7d23623b5bb2d2092cb6c86bc965138b4ea75 (patch)
tree7234b255b871cecca24aadc03c8d0857202f48e4 /src/engine.cpp
parent0d10b77f77459333c5549711334f417623ab1f3e (diff)
downloadengine-cbf7d23623b5bb2d2092cb6c86bc965138b4ea75.tar.gz
added mipmaps for the hw renderer, improvementsHEADmain
This commit add mipmaps, but for now, mipmaps are generated on the gpu at runtime. We should generate them in advance. - added mipmaps for the hardware renderer - renamed stb_image.c to stb_image.cpp - add compiler flag to stb_image.cpp to prevent warning - added pipe notation for various objects to have all clipping functions be left to right. We need this for the time being because dot notation would considerably complicate the current implementation - small improvements
Diffstat (limited to 'src/engine.cpp')
-rw-r--r--src/engine.cpp205
1 files changed, 177 insertions, 28 deletions
diff --git a/src/engine.cpp b/src/engine.cpp
index 305d254..91f7447 100644
--- a/src/engine.cpp
+++ b/src/engine.cpp
@@ -46,6 +46,7 @@
#include "math/mat4.hpp"
#include "math/quat.hpp"
#include "math/tform.hpp"
+#include "math/utils.hpp"
#include "ctrl/keyboard.hpp"
#include "ctrl/mouse.hpp"
#include "shaders/shaders.hpp"
@@ -313,15 +314,15 @@ static int main_term(Scene& scene) {
#define SCREEN_WIDTH 800
#define SCREEN_HEIGHT 600
-static const std::vector<const char*> validation_layers = {
- "VK_LAYER_KHRONOS_validation",
+constexpr std::array validation_layers {
+ static_cast<const char*>("VK_LAYER_KHRONOS_validation"),
};
-static const std::vector<const char*> physical_device_ext_names = {
- VK_KHR_SWAPCHAIN_EXTENSION_NAME,
- VK_KHR_SPIRV_1_4_EXTENSION_NAME,
- VK_KHR_SYNCHRONIZATION_2_EXTENSION_NAME,
- VK_KHR_CREATE_RENDERPASS_2_EXTENSION_NAME,
+constexpr std::array physical_device_ext_names {
+ static_cast<const char*>(VK_KHR_SWAPCHAIN_EXTENSION_NAME),
+ static_cast<const char*>(VK_KHR_SPIRV_1_4_EXTENSION_NAME),
+ static_cast<const char*>(VK_KHR_SYNCHRONIZATION_2_EXTENSION_NAME),
+ static_cast<const char*>(VK_KHR_CREATE_RENDERPASS_2_EXTENSION_NAME),
};
#ifdef NDEBUG
@@ -613,7 +614,7 @@ static void copy_buf_to_img(VkCommandBuffer cmd_buf, VkBuffer src, VkImage dst,
}
static std::tuple<VkImage, VkDeviceMemory>
-create_img(VkPhysicalDevice physical_device, VkDevice device, uint32_t width, uint32_t height, VkFormat format,
+create_img(VkPhysicalDevice physical_device, VkDevice device, uint32_t width, uint32_t height, uint32_t mip_lvls, VkFormat format,
VkImageTiling tiling, VkImageUsageFlags usage, VkMemoryPropertyFlags mem_flags) {
auto texture_img = [&] {
VkImageCreateInfo img_ci {
@@ -624,7 +625,7 @@ create_img(VkPhysicalDevice physical_device, VkDevice device, uint32_t width, ui
// TODO: check if this format is supported
.format = format,
.extent = { .width = width, .height = height, .depth = 1 },
- .mipLevels = 1,
+ .mipLevels = mip_lvls,
.arrayLayers = 1,
.samples = VK_SAMPLE_COUNT_1_BIT,
.tiling = tiling,
@@ -669,7 +670,7 @@ create_img(VkPhysicalDevice physical_device, VkDevice device, uint32_t width, ui
static std::tuple<VkImage, VkDeviceMemory, VkImageView>
create_depth_resources(VkPhysicalDevice physical_device, VkDevice device, VkExtent2D swapchain_extent, VkFormat depth_format) {
- auto [depth_img, depth_img_device_mem] = create_img(physical_device, device, swapchain_extent.width, swapchain_extent.height, depth_format,
+ auto [depth_img, depth_img_device_mem] = create_img(physical_device, device, swapchain_extent.width, swapchain_extent.height, 1, depth_format,
VK_IMAGE_TILING_OPTIMAL, VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
auto depth_img_view = [&] {
VkImageViewCreateInfo img_view_ci {
@@ -704,7 +705,7 @@ static void destroy_depth_resources(VkDevice device, VkImage depth_img, VkDevice
vkDestroyImage(device, depth_img, nullptr);
}
-static void transition_image_layout(VkCommandBuffer cmd_buf, VkImage img,
+static void transition_image_layout(VkCommandBuffer cmd_buf, VkImage img, uint32_t mip_lvls,
VkImageLayout old_layout, VkImageLayout new_layout,
VkAccessFlags2 src_access_mask, VkAccessFlags2 dst_access_mask,
VkPipelineStageFlags2 src_stage_mask, VkPipelineStageFlags2 dst_stage_mask) {
@@ -723,7 +724,7 @@ static void transition_image_layout(VkCommandBuffer cmd_buf, VkImage img,
.subresourceRange = {
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
.baseMipLevel = 0,
- .levelCount = 1,
+ .levelCount = mip_lvls,
.baseArrayLayer = 0,
.layerCount = 1,
},
@@ -742,6 +743,154 @@ static void transition_image_layout(VkCommandBuffer cmd_buf, VkImage img,
vkCmdPipelineBarrier2(cmd_buf, &dependency_info);
}
+static void generate_mipmaps(VkCommandBuffer cmd_buf, VkPhysicalDevice physical_device, VkImage img, VkFormat fmt, int w, int h, uint32_t mip_lvls) noexcept {
+ if (mip_lvls == 1) return;
+
+ VkFormatProperties2 format_props {};
+ format_props.sType = VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2;
+ format_props.pNext = nullptr;
+ vkGetPhysicalDeviceFormatProperties2(physical_device, fmt, &format_props);
+ // TODO: I'm not sure if we should check for these 3 flags, maybe we only need to check for the
+ // first and the other two are implied
+ if ((format_props.formatProperties.optimalTilingFeatures
+ & (VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT))
+ != (VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT)) {
+ std::println(stderr, "texture format {} doesn't support vkCmdBlitImage2", string_VkFormat(fmt));
+ exit(EXIT_FAILURE);
+ }
+
+ int32_t mip_w = static_cast<int32_t>(w);
+ int32_t mip_h = static_cast<int32_t>(h);
+ for (uint32_t i = 0; i < mip_lvls - 1; i++) {
+ {
+ VkImageMemoryBarrier2 img_mem_barrier {
+ .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2,
+ .pNext = nullptr,
+ .srcStageMask = VK_PIPELINE_STAGE_2_TRANSFER_BIT,
+ .srcAccessMask = VK_ACCESS_2_TRANSFER_WRITE_BIT,
+ .dstStageMask = VK_PIPELINE_STAGE_2_TRANSFER_BIT,
+ .dstAccessMask = VK_ACCESS_2_TRANSFER_READ_BIT,
+ .oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
+ .newLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
+ .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
+ .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
+ .image = img,
+ .subresourceRange = {
+ .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
+ .baseMipLevel = i,
+ .levelCount = 1,
+ .baseArrayLayer = 0,
+ .layerCount = 1,
+ },
+ };
+ VkDependencyInfo dependency_info {
+ .sType = VK_STRUCTURE_TYPE_DEPENDENCY_INFO,
+ .pNext = nullptr,
+ .dependencyFlags = {},
+ .memoryBarrierCount = 0,
+ .pMemoryBarriers = {},
+ .bufferMemoryBarrierCount = 0,
+ .pBufferMemoryBarriers = {},
+ .imageMemoryBarrierCount = 1,
+ .pImageMemoryBarriers = &img_mem_barrier,
+ };
+ vkCmdPipelineBarrier2(cmd_buf, &dependency_info);
+ }
+ int32_t next_mip_w = std::max(mip_w / 2, 1);
+ int32_t next_mip_h = std::max(mip_h / 2, 1);
+ {
+ VkImageBlit2 region {
+ .sType = VK_STRUCTURE_TYPE_IMAGE_BLIT_2,
+ .pNext = nullptr,
+ .srcSubresource = {
+ .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
+ .mipLevel = i,
+ .baseArrayLayer = 0,
+ .layerCount = 1,
+ },
+ .srcOffsets = { { 0, 0, 0 }, { mip_w, mip_h, 1 } },
+ .dstSubresource = {
+ .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
+ .mipLevel = i + 1,
+ .baseArrayLayer = 0,
+ .layerCount = 1,
+ },
+ .dstOffsets = { { 0, 0, 0 }, { next_mip_w, next_mip_h, 1 } },
+ };
+ VkBlitImageInfo2 blit_img_info {
+ .sType = VK_STRUCTURE_TYPE_BLIT_IMAGE_INFO_2,
+ .pNext = nullptr,
+ .srcImage = img,
+ .srcImageLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
+ .dstImage = img,
+ .dstImageLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
+ .regionCount = 1,
+ .pRegions = &region,
+ .filter = VK_FILTER_LINEAR,
+ };
+ vkCmdBlitImage2(cmd_buf, &blit_img_info);
+ }
+ mip_w = next_mip_w;
+ mip_h = next_mip_h;
+ }
+ {
+ std::array img_mem_barriers {
+ VkImageMemoryBarrier2 {
+ .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2,
+ .pNext = nullptr,
+ .srcStageMask = VK_PIPELINE_STAGE_2_TRANSFER_BIT,
+ .srcAccessMask = VK_ACCESS_2_TRANSFER_READ_BIT,
+ .dstStageMask = VK_PIPELINE_STAGE_2_FRAGMENT_SHADER_BIT,
+ .dstAccessMask = VK_ACCESS_2_SHADER_READ_BIT,
+ .oldLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
+ .newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
+ .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
+ .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
+ .image = img,
+ .subresourceRange = {
+ .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
+ .baseMipLevel = 0,
+ .levelCount = mip_lvls - 1,
+ .baseArrayLayer = 0,
+ .layerCount = 1,
+ },
+ },
+ VkImageMemoryBarrier2 {
+ .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2,
+ .pNext = nullptr,
+ .srcStageMask = VK_PIPELINE_STAGE_2_TRANSFER_BIT,
+ .srcAccessMask = VK_ACCESS_2_TRANSFER_WRITE_BIT,
+ .dstStageMask = VK_PIPELINE_STAGE_2_FRAGMENT_SHADER_BIT,
+ .dstAccessMask = VK_ACCESS_2_SHADER_READ_BIT,
+ .oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
+ .newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
+ .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
+ .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
+ .image = img,
+ .subresourceRange = {
+ .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
+ .baseMipLevel = mip_lvls - 1,
+ .levelCount = 1,
+ .baseArrayLayer = 0,
+ .layerCount = 1,
+ },
+ },
+ };
+ VkDependencyInfo dependency_info {
+ .sType = VK_STRUCTURE_TYPE_DEPENDENCY_INFO,
+ .pNext = nullptr,
+ .dependencyFlags = {},
+ .memoryBarrierCount = 0,
+ .pMemoryBarriers = {},
+ .bufferMemoryBarrierCount = 0,
+ .pBufferMemoryBarriers = {},
+ .imageMemoryBarrierCount = img_mem_barriers.size(),
+ .pImageMemoryBarriers = img_mem_barriers.data(),
+ };
+ vkCmdPipelineBarrier2(cmd_buf, &dependency_info);
+ }
+}
+
static int main_graphical(Scene& scene) {
// init window
glfwInit();
@@ -1664,10 +1813,12 @@ static int main_graphical(Scene& scene) {
std::tie(depth_img, depth_img_device_mem, depth_img_view) = create_depth_resources(physical_device, device, swapchain_extent, depth_format);
// create texture image
- auto [texture_img, texture_img_device_mem, texture_w, texture_h, texture_pixels] = [&] {
+ auto [texture_img, texture_mip_lvls, texture_img_device_mem, texture_w, texture_h, texture_pixels] = [&] {
int w, h, channels;
stbi_uc* pixels = stbi_load(DATADIR "/assets/textures/viking_room.png", &w, &h, &channels, STBI_rgb_alpha);
+ uint32_t texture_mip_lvls = engine::math::utils::log2_floored(static_cast<uint32_t>(std::max(w, h))) + 1;
+
// TODO: we're also using this size as the host pixels buffer size, I don't know if mixing
// the two will cause problems later. Right now they are the same, so it doesn't
VkDeviceSize img_size = w * h * 4;
@@ -1694,27 +1845,25 @@ static int main_graphical(Scene& scene) {
vkUnmapMemory(device, staging_buf_device_mem);
}
- auto [texture_img, texture_img_device_mem] = create_img(physical_device, device, static_cast<uint32_t>(w), static_cast<uint32_t>(h), 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);
+ auto [texture_img, texture_img_device_mem] = create_img(physical_device, device, static_cast<uint32_t>(w), static_cast<uint32_t>(h),
+ texture_mip_lvls, 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);
{
auto cmd_buf = begin_single_time_cmds(device, cmd_pool);
- transition_image_layout(cmd_buf, texture_img,
+ transition_image_layout(cmd_buf, texture_img, texture_mip_lvls,
VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
{}, VK_ACCESS_2_TRANSFER_WRITE_BIT,
VK_PIPELINE_STAGE_2_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_2_TRANSFER_BIT);
copy_buf_to_img(cmd_buf, staging_buf, texture_img, static_cast<uint32_t>(w), static_cast<uint32_t>(h));
- transition_image_layout(cmd_buf, texture_img,
- VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
- VK_ACCESS_2_TRANSFER_WRITE_BIT, VK_ACCESS_2_SHADER_READ_BIT,
- VK_PIPELINE_STAGE_2_TRANSFER_BIT, VK_PIPELINE_STAGE_2_FRAGMENT_SHADER_BIT);
+ generate_mipmaps(cmd_buf, physical_device, texture_img, VK_FORMAT_R8G8B8A8_SRGB, w, h, texture_mip_lvls);
end_single_time_cmds(graphics_queue, device, cmd_pool, cmd_buf);
}
vkFreeMemory(device, staging_buf_device_mem, nullptr);
vkDestroyBuffer(device, staging_buf, nullptr);
- return std::tuple { texture_img, texture_img_device_mem, w, h, pixels };
+ return std::tuple { texture_img, texture_mip_lvls, texture_img_device_mem, w, h, pixels };
}();
// create texture image view
@@ -1733,7 +1882,7 @@ static int main_graphical(Scene& scene) {
.subresourceRange = { // VkImageSubresourceRange
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
.baseMipLevel = 0,
- .levelCount = 1,
+ .levelCount = texture_mip_lvls,
.baseArrayLayer = 0,
.layerCount = 1,
},
@@ -1763,9 +1912,9 @@ static int main_graphical(Scene& scene) {
.maxAnisotropy = physical_device_props.properties.limits.maxSamplerAnisotropy,
.compareEnable = VK_FALSE,
// TODO: check if it has to be VK_COMPARE_OP_ALWAYS instead of VK_COMPARE_OP_NEVER
- .compareOp = VK_COMPARE_OP_NEVER,
+ .compareOp = VK_COMPARE_OP_ALWAYS,
.minLod = 0.f,
- .maxLod = 0.f,
+ .maxLod = VK_LOD_CLAMP_NONE,
.borderColor = VK_BORDER_COLOR_INT_OPAQUE_BLACK,
.unnormalizedCoordinates = VK_FALSE,
};
@@ -2232,7 +2381,7 @@ static int main_graphical(Scene& scene) {
// render_and_present_frame
[&](const Matrix4& view_mat, const Matrix4& proj_mat, const Matrix4& inv_view_mat, float /* time */, float ellapsed_time) {
- if (show_fps)
+ if constexpr (show_fps)
std::cout << "\rfps: " << (1.f / ellapsed_time) << " ";
if (VkResult res = vkWaitForFences(device, 1, &fences_in_flight[frame_idx], VK_TRUE, std::numeric_limits<uint64_t>::max()); res != VK_SUCCESS) {
@@ -2381,7 +2530,7 @@ static int main_graphical(Scene& scene) {
}
{
- VkClearValue clear_color_val { .color { .float32 = { 0.f, 0.f, 0.f, (transparent_window ? 0.f : 1.f) } } };
+ VkClearValue clear_color_val { .color { .float32 { 0.f, 0.f, 0.f, (transparent_window ? 0.f : 1.f) } } };
VkClearValue clear_depth_val { .depthStencil { .depth = 1.f, .stencil = 0 } };
VkRenderingAttachmentInfo rendering_color_info {
.sType = VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO,
@@ -2461,7 +2610,7 @@ static int main_graphical(Scene& scene) {
vkCmdEndRendering(cmd_bufs[frame_idx]);
- transition_image_layout(cmd_bufs[frame_idx], swapchain_imgs[img_idx],
+ transition_image_layout(cmd_bufs[frame_idx], swapchain_imgs[img_idx], 1,
VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR,
VK_ACCESS_2_COLOR_ATTACHMENT_WRITE_BIT, {},
VK_PIPELINE_STAGE_2_COLOR_ATTACHMENT_OUTPUT_BIT, VK_PIPELINE_STAGE_2_BOTTOM_OF_PIPE_BIT);
@@ -2597,7 +2746,7 @@ static int main_graphical(Scene& scene) {
}
);
- if (show_fps)
+ if constexpr (show_fps)
std::cout << std::endl;
if (VkResult res = vkDeviceWaitIdle(device); res != VK_SUCCESS) {