diff options
author | Leon Kang <2090093273@qq.com> | 2024-10-03 21:04:37 -0700 |
---|---|---|
committer | Leon Kang <2090093273@qq.com> | 2024-10-03 21:04:37 -0700 |
commit | 1f47c662a07ec83d4033b1a5f4b48dd8dfa58b7b (patch) | |
tree | cfb8941b80d1bc672bf25dba3ca716a1e0bc0823 | |
parent | c1ceb18d1c6d8af1266fc9cb9233e2328dd4d723 (diff) | |
parent | e59529d3f55b9128f798a7f02a7288f96bdaf9a4 (diff) | |
download | iris-1f47c662a07ec83d4033b1a5f4b48dd8dfa58b7b.tar.gz iris-1f47c662a07ec83d4033b1a5f4b48dd8dfa58b7b.zip |
Merge branch 'main' into windows-build
-rw-r--r-- | .gitignore | 8 | ||||
-rw-r--r-- | .gitmodules | 3 | ||||
-rw-r--r-- | CMakeLists.txt | 15 | ||||
m--------- | ext/glm | 0 | ||||
-rw-r--r-- | src/app.cpp | 10 | ||||
-rw-r--r-- | src/gltf_loader.cpp | 96 | ||||
-rw-r--r-- | src/gltf_loader.h | 11 | ||||
-rw-r--r-- | src/render_assets.h | 38 | ||||
-rw-r--r-- | src/vulkan_helper.cpp | 165 | ||||
-rw-r--r-- | src/vulkan_helper.h | 39 | ||||
-rw-r--r-- | src/vulkan_swapchain.cpp | 46 | ||||
-rw-r--r-- | src/vulkan_swapchain.h | 5 | ||||
-rw-r--r-- | xmake.lua | 78 |
13 files changed, 356 insertions, 158 deletions
@@ -46,4 +46,10 @@ cmake-build-debug/ cmake-build-release/ # clangd config -.clangd
\ No newline at end of file +.clangd +.xmake + +# imgui config file +imgui.ini + +e.log diff --git a/.gitmodules b/.gitmodules index 0d0d572..ffb9b3a 100644 --- a/.gitmodules +++ b/.gitmodules @@ -16,3 +16,6 @@ [submodule "ext/stb"] path = ext/stb url = https://github.com/nothings/stb +[submodule "ext/glm"] + path = ext/glm + url = https://github.com/g-truc/glm.git diff --git a/CMakeLists.txt b/CMakeLists.txt index b2dc213..ad7fd44 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -8,9 +8,6 @@ set(CMAKE_CXX_STANDARD 20) set(PROJECT_ROOT ${CMAKE_SOURCE_DIR}) set(SRC_DIR ${PROJECT_ROOT}/src) set(EXT_DIR ${PROJECT_ROOT}/ext) -if (CMAKE_BUILD_TYPE STREQUAL "Debug") - add_compile_definitions(USE_VULKAN_VALIDATION_LAYERS) -endif() # Add external libraries include(${EXT_DIR}/imgui.cmake) @@ -19,7 +16,7 @@ add_library(argparse INTERFACE) target_include_directories(argparse INTERFACE ${EXT_DIR}/argparse/include) add_library(tinygltf INTERFACE) -target_include_directories(tinygltf INTERFACE ${EXT_DIR}/tinygltf) +target_include_directories(tinygltf INTERFACE ${EXT_DIR}/tinygltf.git) add_library(tinyobjloader INTERFACE) target_include_directories(tinyobjloader INTERFACE ${EXT_DIR}/tinyobjloader) @@ -32,6 +29,13 @@ file(GLOB_RECURSE SOURCES "${SRC_DIR}/*.cpp") # Add executable from your source files add_executable(iris_renderer ${SOURCES}) +if (CMAKE_BUILD_TYPE STREQUAL "Debug") + add_compile_definitions(USE_VULKAN_VALIDATION_LAYERS) +endif() + +add_subdirectory(${EXT_DIR}/glm) +target_link_libraries(iris_renderer PRIVATE glm::glm) + find_package(glfw3 REQUIRED) target_link_libraries(iris_renderer PRIVATE glfw) @@ -41,6 +45,9 @@ target_link_libraries(iris_renderer PRIVATE Vulkan::Vulkan) find_package(VulkanMemoryAllocator CONFIG REQUIRED) target_link_libraries(iris_renderer PRIVATE GPUOpen::VulkanMemoryAllocator) +find_package(spdlog REQUIRED) +target_link_libraries(iris_renderer PRIVATE spdlog::spdlog) + # Link external libraries to your project target_link_libraries(iris_renderer PRIVATE argparse imgui tinygltf tinyobjloader stb diff --git a/ext/glm b/ext/glm new file mode 160000 +Subproject 33b4a621a697a305bc3a7610d290677b96beb18 diff --git a/src/app.cpp b/src/app.cpp index f48edd9..11a219d 100644 --- a/src/app.cpp +++ b/src/app.cpp @@ -4,6 +4,8 @@ #include "imgui_impl_vulkan.h" #include "argparse/argparse.hpp" #include <cstdint> +#include <spdlog/spdlog.h> +#include <spdlog/cfg/env.h> #include <sys/types.h> #include <vk_mem_alloc.h> #include <vulkan/vulkan_core.h> @@ -16,6 +18,7 @@ #include <vector> int main(int argc, char** argv) { + spdlog::cfg::load_env_levels(); argparse::ArgumentParser program("Iris Renderer"); program.add_argument("width") .help("display width of the window") @@ -39,9 +42,9 @@ int main(int argc, char** argv) { const char* description = nullptr; glfwGetError(&description); if (description != nullptr) { - std::cerr << "Error: " << description << std::endl; + spdlog::critical("Failed to initialize GLFW: {}", description); } else { - std::cerr << "Failed to initialize GLFW" << std::endl; + spdlog::critical("Failed to initialize GLFW"); } glfwTerminate(); @@ -54,7 +57,7 @@ int main(int argc, char** argv) { glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); auto window = glfwCreateWindow(window_width, window_height, "Iris Renderer", nullptr, nullptr); if (window == nullptr) { - std::cerr << "Failed to create GLFW window" << std::endl; + spdlog::critical("Failed to create GLFW window"); abort(); } @@ -103,6 +106,7 @@ int main(int argc, char** argv) { glfwDestroyWindow(swapchain.window); glfwTerminate(); + swapchain.release(); device.destroy(); return 0; diff --git a/src/gltf_loader.cpp b/src/gltf_loader.cpp new file mode 100644 index 0000000..2b3818f --- /dev/null +++ b/src/gltf_loader.cpp @@ -0,0 +1,96 @@ +#include "gltf_loader.h" +#include "spdlog/spdlog.h" + +enum class SceneFileType { + GLTF, + GLB, + UNKNOWN, +}; + +bool load_gltf(const std::string_view path, iris::Scene &scene) { + tinygltf::Model model; + tinygltf::TinyGLTF loader; + + std::string error; + std::string warning; + + SceneFileType file_type = [&path] { + if (path.find_last_of(".") != std::string::npos) { + std::string_view extension = path.substr(path.find_last_of(".") + 1); + if (extension == "glb") { + return SceneFileType::GLB; + } else if (extension == "gltf") { + return SceneFileType::GLTF; + } + } + return SceneFileType::UNKNOWN; + }(); + switch (file_type) { + case SceneFileType::GLTF: + if (!loader.LoadASCIIFromFile(&model, &error, &warning, path.data())) { + spdlog::error("Failed to load glTF file: {}", error); + return false; + } + break; + case SceneFileType::GLB: + if (!loader.LoadBinaryFromFile(&model, &error, &warning, path.data())) { + spdlog::error("Failed to load glTF file: {}", error); + return false; + } + break; + case SceneFileType::UNKNOWN: + spdlog::error("Unknown file type: {}", path); + return false; + } + + spdlog::info("loaded glTF file {} has:\n" + "{} accessors\n" + "{} animations\n" + "{} buffers\n" + "{} bufferViews\n" + "{} materials\n" + "{} meshes\n" + "{} nodes\n" + "{} textures\n" + "{} images\n" + "{} skins\n" + "{} samplers\n" + "{} cameras\n" + "{} scenes\n" + "{} lights", + path, + model.accessors.size(), + model.animations.size(), + model.buffers.size(), + model.bufferViews.size(), + model.materials.size(), + model.meshes.size(), + model.nodes.size(), + model.textures.size(), + model.images.size(), + model.skins.size(), + model.samplers.size(), + model.cameras.size(), + model.scenes.size(), + model.lights.size()); + + for (const auto &mesh : model.meshes) { + iris::Mesh<float> iris_mesh; + for (const auto &primitive : mesh.primitives) { + const auto &position_accessor = model.accessors[primitive.attributes.at("POSITION")]; + const auto &position_buffer_view = model.bufferViews[position_accessor.bufferView]; + const auto &position_buffer = model.buffers[position_buffer_view.buffer]; + const auto &position_data = reinterpret_cast<const float *>(position_buffer.data.data() + position_buffer_view.byteOffset + position_accessor.byteOffset); + + const auto &index_accessor = model.accessors[primitive.indices]; + const auto &index_buffer_view = model.bufferViews[index_accessor.bufferView]; + const auto &index_buffer = model.buffers[index_buffer_view.buffer]; + const auto &index_data = reinterpret_cast<const uint32_t *>(index_buffer.data.data() + index_buffer_view.byteOffset + index_accessor.byteOffset); + + iris_mesh.vertices.insert(iris_mesh.vertices.end(), position_data, position_data + position_accessor.count * 3); + iris_mesh.indices.insert(iris_mesh.indices.end(), index_data, index_data + index_accessor.count); + } + scene.meshes.push_back(iris_mesh); + } + return true; +}
\ No newline at end of file diff --git a/src/gltf_loader.h b/src/gltf_loader.h new file mode 100644 index 0000000..c239bb2 --- /dev/null +++ b/src/gltf_loader.h @@ -0,0 +1,11 @@ +#pragma once + +#include <string_view> +#include "render_assets.h" + +#define TINYGLTF_IMPLEMENTATION +#define STB_IMAGE_IMPLEMENTATION +#define STB_IMAGE_WRITE_IMPLEMENTATION +#include "tiny_gltf.h" + +bool load_gltf(const std::string_view path, iris::Scene &scene);
\ No newline at end of file diff --git a/src/render_assets.h b/src/render_assets.h new file mode 100644 index 0000000..763bf76 --- /dev/null +++ b/src/render_assets.h @@ -0,0 +1,38 @@ +#pragma once + +#include <cstdint> +#include <string> +#include <vector> +#include <glm/glm.hpp> + +namespace iris { + +template<typename T> +struct Mesh { + std::string name; + std::vector<T> vertices; + std::vector<uint32_t> indices; +}; + +struct Material { + +}; + +struct Texture { +}; + +struct Camera { + glm::vec3 position; + glm::vec3 direction; + glm::vec3 up; +}; + +struct Scene { + std::vector<Mesh<float>> meshes; + std::vector<Material> materials; + std::vector<Texture> textures; + + Camera camera_position; +}; + +} // namespace iris
\ No newline at end of file diff --git a/src/vulkan_helper.cpp b/src/vulkan_helper.cpp index 69617c4..c281b2c 100644 --- a/src/vulkan_helper.cpp +++ b/src/vulkan_helper.cpp @@ -1,12 +1,11 @@ #include "vulkan_helper.h" #include "vulkan/vulkan_core.h" #include <limits> -#define STB_IMAGE_IMPLEMENTATION +#include <spdlog/spdlog.h> #include "stb_image.h" #include <cstdint> #include <cstring> #include <cstdlib> -#include <iostream> #include <memory> #define VMA_IMPLEMENTATION #include <vk_mem_alloc.h> @@ -23,7 +22,7 @@ static VKAPI_ATTR VkBool32 VKAPI_CALL debug_report( void* pUserData) { (void)flags; (void)object; (void)location; (void)messageCode; (void)pUserData; (void)pLayerPrefix; // Unused arguments - fprintf(stderr, "[vulkan] Debug report from ObjectType: %i\nMessage: %s\n\n", objectType, pMessage); + spdlog::debug("[vulkan] Debug report from ObjectType: {}\nMessage: {}\n\n", (uint32_t)objectType, pMessage); return VK_FALSE; } @@ -36,7 +35,7 @@ VKAPI_ATTR VkBool32 VKAPI_CALL debug_callback( (void) messageSeverity; (void) messageType; (void) pUserData; - std::cerr << "Validation layer: " << pCallbackData->pMessage << std::endl; + spdlog::error("{}", pCallbackData->pMessage); return VK_FALSE; } #endif @@ -151,8 +150,7 @@ Device::Device(std::vector<std::string> layers, std::vector<std::string> instanc } if (!has_raytracing || !has_acceleration_structure) { - // TODO throw an exception - std::cerr << "Physical device does not support ray tracing extensions" << std::endl; + spdlog::critical("Physical device does not support ray tracing extensions"); abort(); } } @@ -225,6 +223,19 @@ Device::Device(std::vector<std::string> layers, std::vector<std::string> instanc // not handled by RAII, manually call at the end. void Device::destroy() { +#ifdef USE_VULKAN_VALIDATION_LAYERS + auto vkDestroyDebugReportCallback = (PFN_vkDestroyDebugReportCallbackEXT) + vkGetInstanceProcAddr(instance, "vkDestroyDebugReportCallbackEXT"); + if (vkDestroyDebugReportCallback) { + vkDestroyDebugReportCallback(instance, debugReportCallback, nullptr); + } + auto vkDestroyDebugUtilsMessenger = (PFN_vkDestroyDebugUtilsMessengerEXT) + vkGetInstanceProcAddr(instance, "vkDestroyDebugUtilsMessengerEXT"); + if (vkDestroyDebugUtilsMessenger) { + vkDestroyDebugUtilsMessenger(instance, debugUtilsMessenger, nullptr); + } +#endif + vmaDestroyAllocator(allocator); vkDestroyDevice(device, VK_NULL_HANDLE); vkDestroyInstance(instance, VK_NULL_HANDLE); @@ -265,56 +276,28 @@ CommandBuffer::CommandBuffer(VkDevice device, &fence_info, VK_NULL_HANDLE, &fence)); + spdlog::debug("Created command buffer: 0x{:x}", (uint64_t)buffer); + spdlog::debug("Created fence: 0x{:x}", (uint64_t)fence); } -AsyncCommandBuffer::AsyncCommandBuffer(VkDevice device, - uint32_t queue_family_index, - VkQueue queue) - : device(device), queue(queue) -{ - VkCommandPoolCreateInfo pool_info = { - .sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO, - .flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT, - .queueFamilyIndex = queue_family_index, // TODO: query capabilities to find a proper queue index - }; - CHECK_VULKAN(vkCreateCommandPool( - device, - &pool_info, - VK_NULL_HANDLE, - &this->pool)); - - VkCommandBufferAllocateInfo buffer_info = { - .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO, - .commandPool = this->pool, - .level = VK_COMMAND_BUFFER_LEVEL_PRIMARY, - .commandBufferCount = 1, - }; - CHECK_VULKAN(vkAllocateCommandBuffers( - device, - &buffer_info, - &buffer)); -} - -void AsyncCommandBuffer::destroy() { - vkFreeCommandBuffers(device, pool, 1, &buffer); - vkDestroyCommandPool(device, pool, VK_NULL_HANDLE); -} - - -void CommandBuffer::destroy() { - vkDestroyFence(device, fence, VK_NULL_HANDLE); - vkFreeCommandBuffers(device, pool, 1, &buffer); - vkDestroyCommandPool(device, pool, VK_NULL_HANDLE); +void CommandBuffer::release() { + if (fence != VK_NULL_HANDLE) { + vkDestroyFence(device, fence, VK_NULL_HANDLE); + vkFreeCommandBuffers(device, pool, 1, &buffer); + vkDestroyCommandPool(device, pool, VK_NULL_HANDLE); + + spdlog::debug("Destroyed fence: 0x{:x}", (uint64_t)fence); + spdlog::debug("Destroyed command buffer: 0x{:x}", (uint64_t)buffer); + fence = VK_NULL_HANDLE; + buffer = VK_NULL_HANDLE; + pool = VK_NULL_HANDLE; + } } CommandBuffer Device::create_command_buffer() { return CommandBuffer(device, main_queue_family_index, graphics_queue); } -AsyncCommandBuffer Device::create_async_command_buffer() { - return AsyncCommandBuffer(device, main_queue_family_index, graphics_queue); -} - void CommandBuffer::begin(VkCommandBufferUsageFlags flags) { CHECK_VULKAN(vkResetFences(device, 1, &fence)); CHECK_VULKAN(vkResetCommandPool(device, pool, 0u)); @@ -366,55 +349,51 @@ void Buffer_t::unmap() { void Buffer_t::release() { if (buffer != VK_NULL_HANDLE) { - std::cerr << "Destroying buffer: 0x" << std::hex << (uint64_t)buffer << std::endl; + spdlog::debug("Destroying buffer: 0x{:x}", (uint64_t)buffer); + vkDeviceWaitIdle(device); vmaDestroyBuffer(allocator, buffer, allocation); buffer = VK_NULL_HANDLE; } } -Buffer_t Device::create_buffer_raw(VkDeviceSize size, - VkBufferUsageFlags usage, - VmaAllocationCreateInfo create_info) +Buffer Device::create_buffer(VkDeviceSize size, + VkBufferUsageFlags usage, + VmaAllocationCreateInfo create_info) { VkBufferCreateInfo buffer_info = { .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, .size = size, .usage = usage, }; - Buffer_t buffer = { - .allocator = this->allocator, - .flags = usage, - .size = size, - }; + Buffer buffer = std::make_shared<Buffer_t>(); + buffer->device = this->device; + buffer->allocator = this->allocator; + buffer->flags = usage; + buffer->size = size; CHECK_VULKAN(vmaCreateBuffer( allocator, &buffer_info, &create_info, - &buffer.buffer, - &buffer.allocation, + &buffer->buffer, + &buffer->allocation, VK_NULL_HANDLE)); + spdlog::debug("Created buffer: 0x{:x}", (uint64_t)buffer->buffer); return buffer; } -Buffer Device::create_buffer(VkDeviceSize size, - VkBufferUsageFlags usage, - VmaAllocationCreateInfo create_info) -{ - return std::make_shared<Buffer_t>(create_buffer_raw(size, usage, create_info)); -} - void Texture2D_t::release() { if (image != VK_NULL_HANDLE) { - std::cerr << "Destroying image: 0x" << std::hex << (uint64_t)image << std::endl; + spdlog::debug("Destroying image: 0x{:x}", (uint64_t)image); + vkDeviceWaitIdle(device); vmaDestroyImage(allocator, image, allocation); image = VK_NULL_HANDLE; } } -Texture2D_t Device::create_texture_raw(VkExtent2D extent, - VkFormat format, - VkImageUsageFlags usage, - VmaAllocationCreateInfo create_info) +Texture2D Device::create_texture(VkExtent2D extent, + VkFormat format, + VkImageUsageFlags usage, + VmaAllocationCreateInfo create_info) { VkImageCreateInfo image_info = { .sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, @@ -427,33 +406,25 @@ Texture2D_t Device::create_texture_raw(VkExtent2D extent, .tiling = VK_IMAGE_TILING_OPTIMAL, .usage = usage, }; - Texture2D_t texture = { - .allocator = this->allocator, - .layout = VK_IMAGE_LAYOUT_UNDEFINED, - .flags = usage, - .extent = extent, - }; + + Texture2D texture = std::make_shared<Texture2D_t>(); + texture->device = this->device; + texture->allocator = this->allocator; + texture->layout = VK_IMAGE_LAYOUT_UNDEFINED; + texture->flags = usage; + texture->extent = extent; + CHECK_VULKAN(vmaCreateImage( allocator, &image_info, &create_info, - &texture.image, - &texture.allocation, + &texture->image, + &texture->allocation, VK_NULL_HANDLE)); - std::cerr << "Created image: 0x" << std::hex << (uint64_t)texture.image << std::endl; + spdlog::debug("Created image: 0x{:x}", (uint64_t)texture->image); return texture; } -Texture2D Device::create_texture(VkExtent2D extent, - VkFormat format, - VkImageUsageFlags usage, - VmaAllocationCreateInfo create_info) -{ - auto t = std::make_shared<Texture2D_t>(create_texture_raw(extent, format, usage, create_info)); - std::cerr << "Before create texture terminates" << std::endl; - return t; -} - Texture2D Device::create_texture_from_image(const char* filename, VkFormat format, VkImageUsageFlags usage, @@ -461,13 +432,12 @@ Texture2D Device::create_texture_from_image(const char* filename, int width, height, channels; stbi_uc* pixels = stbi_load(filename, &width, &height, &channels, STBI_rgb_alpha); if (pixels == nullptr) { - // TODO throw an exception - std::cerr << "Failed to load image: " << filename << std::endl; + spdlog::critical("Failed to load image: {}", filename); abort(); } // destroy after use, don't need to wrap with shared_ptr - auto staging_buf = create_buffer_raw( + auto staging_buf = create_buffer( width * height * 4, VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VmaAllocationCreateInfo { @@ -476,10 +446,10 @@ Texture2D Device::create_texture_from_image(const char* filename, .usage = VMA_MEMORY_USAGE_AUTO, }); std::memcpy( - staging_buf.map(), + staging_buf->map(), pixels, width * height * 4); - staging_buf.unmap(); + staging_buf->unmap(); stbi_image_free(pixels); VkExtent2D extent = {static_cast<uint32_t>(width), static_cast<uint32_t>(height)}; @@ -493,7 +463,6 @@ Texture2D Device::create_texture_from_image(const char* filename, .flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT, }; CHECK_VULKAN(vkBeginCommandBuffer(cmd_buf.buffer, &begin_info)); - std::cerr << "Image layout transition for 0x" << std::hex << (uint64_t)texture->image << std::endl; VkImageMemoryBarrier barrier = { .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, .srcAccessMask = VK_ACCESS_NONE, @@ -550,7 +519,7 @@ Texture2D Device::create_texture_from_image(const char* filename, }; vkCmdCopyBufferToImage( cmd_buf.buffer, - staging_buf.buffer, + staging_buf->buffer, texture->image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, @@ -606,8 +575,6 @@ Texture2D Device::create_texture_from_image(const char* filename, CHECK_VULKAN(vkWaitForFences(cmd_buf.device, 1, &cmd_buf.fence, VK_TRUE, std::numeric_limits<uint64_t>::max())); } texture->layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; - std::cerr << "Image layout transition done" << std::endl; - staging_buf.release(); return texture; } diff --git a/src/vulkan_helper.h b/src/vulkan_helper.h index c2b1c8c..04fcead 100644 --- a/src/vulkan_helper.h +++ b/src/vulkan_helper.h @@ -1,7 +1,10 @@ +#pragma once + #include <memory> #include <vulkan/vulkan_core.h> #include <vulkan/vk_enum_string_helper.h> #include <vk_mem_alloc.h> +#include <spdlog/spdlog.h> #include <cstdint> #include <vector> @@ -11,8 +14,7 @@ do { \ VkResult res = result; \ if (res != VK_SUCCESS) { \ - /* TODO: throw error instead of returning */ \ - std::cerr << "Vulkan error: " << string_VkResult(res) << std::endl; \ + spdlog::error("Vulkan error: {}", string_VkResult(res)); \ abort(); \ } \ } while (0) @@ -21,6 +23,7 @@ namespace iris { struct Buffer_t { VkBuffer buffer; + VkDevice device; VmaAllocator allocator; VmaAllocation allocation; VkBufferUsageFlags flags; @@ -30,12 +33,14 @@ struct Buffer_t { void* map(); void unmap(); void release(); + ~Buffer_t() { release(); } }; typedef std::shared_ptr<Buffer_t> Buffer; struct Texture2D_t { VkImage image; + VkDevice device; VmaAllocator allocator; VmaAllocation allocation; VkImageView image_view; @@ -45,6 +50,7 @@ struct Texture2D_t { VkExtent2D extent; void release(); + ~Texture2D_t() { release(); } }; typedef std::shared_ptr<Texture2D_t> Texture2D; @@ -59,19 +65,10 @@ struct CommandBuffer { VkQueue queue; CommandBuffer(VkDevice device, uint32_t queue_family_index, VkQueue queue); - void destroy(); + void release(); void begin(VkCommandBufferUsageFlags flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT); void submit_sync(); -}; - -struct AsyncCommandBuffer { - VkDevice device; - VkCommandPool pool; - VkCommandBuffer buffer; - VkQueue queue; - - AsyncCommandBuffer(VkDevice device, uint32_t queue_family_index, VkQueue queue); - void destroy(); + ~CommandBuffer() { release(); } }; struct Device { @@ -92,21 +89,6 @@ struct Device { std::vector<std::string> instance_extensions); void destroy(); - Buffer_t create_buffer_raw( - VkDeviceSize size, - VkBufferUsageFlags usage, - VmaAllocationCreateInfo create_info = { - .usage = VMA_MEMORY_USAGE_AUTO, - }); - - Texture2D_t create_texture_raw( - VkExtent2D extent, - VkFormat format, - VkImageUsageFlags usage, - VmaAllocationCreateInfo create_info = { - .usage = VMA_MEMORY_USAGE_AUTO, - }); - Buffer create_buffer( VkDeviceSize size, VkBufferUsageFlags usage, @@ -131,7 +113,6 @@ struct Device { }); CommandBuffer create_command_buffer(); - AsyncCommandBuffer create_async_command_buffer(); }; } // namespace iris
\ No newline at end of file diff --git a/src/vulkan_swapchain.cpp b/src/vulkan_swapchain.cpp index 7916a3b..4e08049 100644 --- a/src/vulkan_swapchain.cpp +++ b/src/vulkan_swapchain.cpp @@ -8,8 +8,8 @@ #include <cstdint> #include <cstdlib> +#include <spdlog/spdlog.h> #include <vector> -#include <iostream> #include <string> #include <vk_mem_alloc.h> @@ -77,8 +77,7 @@ void Swapchain::resize(uint32_t new_width, uint32_t new_height) { &image_count, nullptr)); if (image_count > SWAPCHAIN_IMAGE_COUNT) { - // TODO throw an exception - std::cerr << "Swapchain image count is greater than expected" << std::endl; + spdlog::critical("Swapchain image count is greater than expected"); abort(); } CHECK_VULKAN(vkGetSwapchainImagesKHR( @@ -134,6 +133,9 @@ void Swapchain::resize(uint32_t new_width, uint32_t new_height) { &framebuffers[i])); } + if (upload_texture) { + upload_texture->release(); + } upload_texture = device.create_texture( {width, height}, VK_FORMAT_B8G8R8A8_UNORM, @@ -152,11 +154,10 @@ Swapchain::Swapchain(GLFWwindow *window, , window(window) , width(width) , height(height) - , cmd_buf(device.create_async_command_buffer()) + , cmd_buf(device.create_command_buffer()) { if (!glfwVulkanSupported()) { - std::cerr << "GLFW failed to find Vulkan support" << std::endl; - // TODO throw an exception + spdlog::critical("GLFW failed to find Vulkan support"); abort(); } @@ -176,8 +177,7 @@ Swapchain::Swapchain(GLFWwindow *window, surface, &supported)); if (supported != VK_TRUE) { - // TODO throw an exception - std::cerr << "Surface does not support presentation" << std::endl; + spdlog::critical("Surface does not support presentation"); abort(); } @@ -188,8 +188,7 @@ Swapchain::Swapchain(GLFWwindow *window, &surface_capabilities)); if (surface_capabilities.maxImageCount < SWAPCHAIN_IMAGE_COUNT) { - // TODO throw an exception - std::cerr << "Surface does not support enough images" << std::endl; + spdlog::critical("Surface does not support enough images"); abort(); } } @@ -280,6 +279,7 @@ Swapchain::Swapchain(GLFWwindow *window, }; VkDescriptorPoolCreateInfo pool_info = { .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO, + .flags = VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT, .maxSets = 1, .poolSizeCount = 1, .pPoolSizes = &pool_size, @@ -317,11 +317,17 @@ Swapchain::Swapchain(GLFWwindow *window, } void Swapchain::start_frame() { + spdlog::trace("Starting frame {}", frame_index); VkCommandBufferBeginInfo begin_info = { .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, .flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT, }; + + if (vkGetFenceStatus(device.device, cmd_buf.fence) == VK_NOT_READY) { + vkWaitForFences(device.device, 1, &cmd_buf.fence, VK_TRUE, 2e6); + } CHECK_VULKAN(vkBeginCommandBuffer(cmd_buf.buffer, &begin_info)); + vkResetFences(device.device, 1, &cmd_buf.fence); } void Swapchain::display(Texture2D& texture) { @@ -331,7 +337,7 @@ void Swapchain::display(Texture2D& texture) { UINT64_MAX, image_available_semaphores[semaphore_index], VK_NULL_HANDLE, - &frame_index); + &semaphore_index); switch (present_result) { case VK_ERROR_OUT_OF_DATE_KHR: case VK_SUBOPTIMAL_KHR: @@ -355,7 +361,7 @@ void Swapchain::display(Texture2D& texture) { .newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, - .image = swapchain_images[frame_index], + .image = swapchain_images[semaphore_index], .subresourceRange = { .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, .levelCount = 1, @@ -417,7 +423,7 @@ void Swapchain::display(Texture2D& texture) { cmd_buf.buffer, texture->image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, - swapchain_images[frame_index], + swapchain_images[semaphore_index], VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &blit, @@ -456,7 +462,7 @@ void Swapchain::display(Texture2D& texture) { VkRenderPassBeginInfo render_pass_begin_info = { .sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO, .renderPass = render_pass, - .framebuffer = framebuffers[frame_index], + .framebuffer = framebuffers[semaphore_index], .renderArea = { .offset = {0, 0}, .extent = {width, height}, @@ -488,7 +494,7 @@ void Swapchain::display(Texture2D& texture) { cmd_buf.queue, 1, &submit_info, - VK_NULL_HANDLE)); + cmd_buf.fence)); } // Present the image to the swapchain @@ -498,7 +504,7 @@ void Swapchain::display(Texture2D& texture) { .pWaitSemaphores = &render_finished_semaphores[semaphore_index], .swapchainCount = 1, .pSwapchains = &swapchain, - .pImageIndices = &frame_index, + .pImageIndices = &semaphore_index, }; present_result = vkQueuePresentKHR(device.graphics_queue, &present_info); @@ -514,10 +520,12 @@ void Swapchain::display(Texture2D& texture) { // Rotate the semaphore index semaphore_index = (semaphore_index + 1) % SWAPCHAIN_IMAGE_COUNT; + frame_index += 1; } -Swapchain::~Swapchain() { - ImGui_ImplVulkan_Shutdown(); +void Swapchain::release() { + upload_texture->release(); + cmd_buf.release(); for (uint32_t i = 0; i < SWAPCHAIN_IMAGE_COUNT; i++) { vkDestroyFramebuffer(device.device, framebuffers[i], nullptr); @@ -530,8 +538,6 @@ Swapchain::~Swapchain() { vkDestroySwapchainKHR(device.device, swapchain, nullptr); vkDestroyRenderPass(device.device, render_pass, nullptr); vkDestroySurfaceKHR(device.instance, surface, nullptr); - - upload_texture.reset(); } } // namespace iris
\ No newline at end of file diff --git a/src/vulkan_swapchain.h b/src/vulkan_swapchain.h index 257840a..8d4604e 100644 --- a/src/vulkan_swapchain.h +++ b/src/vulkan_swapchain.h @@ -1,3 +1,4 @@ +#pragma once #include "imgui_impl_glfw.h" #include "vulkan_helper.h" #include "vulkan/vulkan_core.h" @@ -28,7 +29,7 @@ struct Swapchain { VkFramebuffer framebuffers[SWAPCHAIN_IMAGE_COUNT]; VkSemaphore image_available_semaphores[SWAPCHAIN_IMAGE_COUNT]; VkSemaphore render_finished_semaphores[SWAPCHAIN_IMAGE_COUNT]; - AsyncCommandBuffer cmd_buf; + CommandBuffer cmd_buf; Texture2D upload_texture; @@ -39,8 +40,8 @@ struct Swapchain { void resize(uint32_t new_width, uint32_t new_height); void start_frame(); void display(Texture2D &texture); + void release(); Swapchain(GLFWwindow *window, iris::Device device, uint32_t width, uint32_t height); - ~Swapchain(); }; } // namespace iris
\ No newline at end of file diff --git a/xmake.lua b/xmake.lua new file mode 100644 index 0000000..1b9730f --- /dev/null +++ b/xmake.lua @@ -0,0 +1,78 @@ +add_rules("mode.debug", "mode.release") + +-- Project settings +set_project("iris_renderer") +set_version("1.0") +set_languages("c++20") + + +-- Project directories +local project_root = os.projectdir() +local src_dir = path.join(project_root, "src") +local ext_dir = path.join(project_root, "ext") + +-- ImGui target +target("imgui") + set_kind("static") + local imgui_dir = path.join(ext_dir, "imgui") + + -- Add source files + add_files( + path.join(imgui_dir, "imgui.cpp"), + path.join(imgui_dir, "imgui_demo.cpp"), + path.join(imgui_dir, "imgui_draw.cpp"), + path.join(imgui_dir, "imgui_tables.cpp"), + path.join(imgui_dir, "imgui_widgets.cpp"), + path.join(imgui_dir, "backends/imgui_impl_glfw.cpp"), + path.join(imgui_dir, "backends/imgui_impl_vulkan.cpp") + ) + + -- Add include directories + add_includedirs( + imgui_dir, + path.join(imgui_dir, "backends"), + {public = true} + ) + +-- Arguments for compilation +target("argparse") + set_kind("headeronly") + add_includedirs(path.join(ext_dir, "argparse/include"), {public = true}) + +target("tinygltf") + set_kind("headeronly") + add_includedirs(path.join(ext_dir, "tinygltf.git"), {public = true}) + add_cxxflags("-w") + +target("tinyobjloader") + set_kind("headeronly") + add_includedirs(path.join(ext_dir, "tinyobjloader"), {public = true}) + +target("stb") + set_kind("headeronly") + add_includedirs(path.join(ext_dir, "stb"), {public = true}) + +-- Main executable target +target("iris_renderer") + set_kind("binary") + add_files(path.join(src_dir, "**.cpp")) + + -- Link external libraries + add_deps("argparse", "imgui", "tinygltf", "tinyobjloader", "stb") + + -- Add libraries + add_packages("vulkansdk", "glfw", "fmt", "vulkan-memory-allocator", "spdlog", "glm") + add_syslinks("vulkan", "glfw", "fmt") + + -- OS-specific libraries (dl, pthread, X11, etc.) + if is_plat("linux") then + add_syslinks("dl", "pthread", "X11", "Xxf86vm", "Xrandr", "Xi") + end + + -- Compiler options + add_cxxflags("-Wall", "-Wextra", "-Wno-missing-field-initializers") + + -- Vulkan validation layers for debug builds + if is_mode("debug") then + add_defines("USE_VULKAN_VALIDATION_LAYERS") + end |