summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore8
-rw-r--r--.gitmodules3
-rw-r--r--CMakeLists.txt15
m---------ext/glm0
-rw-r--r--src/app.cpp10
-rw-r--r--src/gltf_loader.cpp96
-rw-r--r--src/gltf_loader.h11
-rw-r--r--src/render_assets.h38
-rw-r--r--src/vulkan_helper.cpp165
-rw-r--r--src/vulkan_helper.h39
-rw-r--r--src/vulkan_swapchain.cpp46
-rw-r--r--src/vulkan_swapchain.h5
-rw-r--r--xmake.lua78
13 files changed, 356 insertions, 158 deletions
diff --git a/.gitignore b/.gitignore
index f9323eb..125e2ae 100644
--- a/.gitignore
+++ b/.gitignore
@@ -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