From 92accc71ef722a02936f2ba28bd7022daa487ee6 Mon Sep 17 00:00:00 2001 From: SoHiEarth <126925211+SoHiEarth@users.noreply.github.com> Date: Thu, 11 Dec 2025 21:55:16 +0900 Subject: [PATCH 1/6] feat: Add some Vulkan boilerplate --- .github/workflows/linux-build.yml | 12 +- .github/workflows/windows-build.yml | 12 +- CMakeLists.txt | 13 +- include/ion/render.h | 8 +- include/ion/render/api.h | 64 +++++++++ src/base/assets.cc | 10 +- src/base/base_pipeline.cc | 10 +- src/base/render.cc | 200 ++++------------------------ src/base/render/api.cc | 25 ++++ src/base/render/debug.cc | 53 ++++++++ src/base/render/devices.cc | 172 ++++++++++++++++++++++++ src/base/render/image_views.cc | 26 ++++ src/base/render/instance.cc | 77 +++++++++++ src/base/render/surface.cc | 11 ++ src/base/render/swapchain.cc | 99 ++++++++++++++ src/base/shader.cc | 33 +---- src/base/texture.cc | 4 +- src/development/gui.cc | 23 ++-- src/development/inspector.cc | 79 ++++++----- src/development/main.cc | 3 +- src/development/package.cc | 30 +++-- src/run/runner.cc | 40 +++--- vcpkg.json | 4 +- 23 files changed, 692 insertions(+), 316 deletions(-) create mode 100644 include/ion/render/api.h create mode 100644 src/base/render/api.cc create mode 100644 src/base/render/debug.cc create mode 100644 src/base/render/devices.cc create mode 100644 src/base/render/image_views.cc create mode 100644 src/base/render/instance.cc create mode 100644 src/base/render/surface.cc create mode 100644 src/base/render/swapchain.cc diff --git a/.github/workflows/linux-build.yml b/.github/workflows/linux-build.yml index e05cf66..0e2342e 100644 --- a/.github/workflows/linux-build.yml +++ b/.github/workflows/linux-build.yml @@ -1,10 +1,10 @@ -name: Linux Build +name: Linux Build (Vulkan) on: push: - branches: [ "main" ] + branches: [ "vulkan" ] pull_request: - branches: [ "main" ] + branches: [ "vulkan" ] jobs: build: @@ -88,8 +88,8 @@ jobs: uses: "marvinpinto/action-automatic-releases@latest" with: repo_token: "${{ secrets.GH_TOKEN }}" - automatic_release_tag: "latest-linux" + automatic_release_tag: "latest-linux-vulkan" prerelease: true - title: "Pre-release Build (Linux)" + title: "Pre-release Build (Linux, Vulkan)" files: | - builds/ninja-multi-vcpkg/Release/ion-latest-linux.zip + builds/ninja-multi-vcpkg/Release/ion-latest-linux-vulkan.zip diff --git a/.github/workflows/windows-build.yml b/.github/workflows/windows-build.yml index aab400b..eb1607f 100644 --- a/.github/workflows/windows-build.yml +++ b/.github/workflows/windows-build.yml @@ -1,10 +1,10 @@ -name: Windows Build +name: Windows Build (Vulkan) on: push: - branches: [ "main" ] + branches: [ "vulkan" ] pull_request: - branches: [ "main" ] + branches: [ "vulkan" ] jobs: build: @@ -84,8 +84,8 @@ jobs: uses: "marvinpinto/action-automatic-releases@latest" with: repo_token: "${{ secrets.GH_TOKEN }}" - automatic_release_tag: "latest-windows" + automatic_release_tag: "latest-windows-vulkan" prerelease: true - title: "Pre-release Build (Windows)" + title: "Pre-release Build (Windows, Vulkan Branch)" files: | - builds/ninja-multi-vcpkg/Release/ion-latest-windows.zip + builds/ninja-multi-vcpkg/Release/ion-latest-windows-vulkan.zip diff --git a/CMakeLists.txt b/CMakeLists.txt index 5484cea..3760732 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -6,7 +6,7 @@ find_package(glad CONFIG REQUIRED) find_package(glfw3 CONFIG REQUIRED) find_package(glm CONFIG REQUIRED) find_package(ImGui CONFIG REQUIRED) -find_package(OpenGL REQUIRED) +find_package(Vulkan REQUIRED) find_package(Stb REQUIRED) find_package(tinyfiledialogs CONFIG REQUIRED) find_package(Python3 COMPONENTS Development REQUIRED) @@ -27,6 +27,13 @@ target_sources(ion-base PRIVATE src/base/texture.cc src/base/physics.cc src/base/world.cc + src/base/render/api.cc + src/base/render/debug.cc + src/base/render/devices.cc + src/base/render/instance.cc + src/base/render/surface.cc + src/base/render/swapchain.cc + src/base/render/image_views.cc ) set_target_properties(ion-base PROPERTIES PRIVATE ${PROJECT_VERSION}) set_target_properties(ion-base PROPERTIES PRIVATE ${PROJECT_VERSION_MAJOR}) @@ -37,7 +44,7 @@ target_link_libraries(ion-base PUBLIC glfw glm::glm imgui::imgui - OpenGL::GL + Vulkan::Vulkan tinyfiledialogs::tinyfiledialogs Python3::Python pugixml::pugixml @@ -90,4 +97,4 @@ target_include_directories(ion-run PRIVATE ${CMAKE_SOURCE_DIR}/include) target_link_libraries(ion-run PRIVATE ion-base ion-game -) \ No newline at end of file +) diff --git a/include/ion/render.h b/include/ion/render.h index 337ea25..1334c8c 100644 --- a/include/ion/render.h +++ b/include/ion/render.h @@ -1,13 +1,12 @@ #pragma once #include "component.h" -#include "gpu_data.h" -// #include #include "exports.h" +#include "gpu_data.h" +#include #include #include #include #include -#include struct Transform; struct Texture; @@ -63,7 +62,8 @@ void UpdateFramebuffers(); void BindFramebuffer(std::shared_ptr); void UnbindFramebuffer(); void DrawFramebuffer(std::shared_ptr, std::shared_ptr, - std::shared_ptr quad, std::shared_ptr = nullptr); + std::shared_ptr quad, + std::shared_ptr = nullptr); const std::map, std::string> &GetFramebuffers(); void BindTexture(std::shared_ptr texture, int slot); diff --git a/include/ion/render/api.h b/include/ion/render/api.h new file mode 100644 index 0000000..b02d6bf --- /dev/null +++ b/include/ion/render/api.h @@ -0,0 +1,64 @@ +#pragma once +#include +#include +#include +#include +#include + +namespace ion::render::api { + +struct QueueFamilyIndices { + std::optional graphics_family, present_family; +}; + +struct SwapchainSupportInfo { + VkSurfaceCapabilitiesKHR capabilities; + std::vector formats; + std::vector present_modes; +}; + +namespace internal { +#ifdef NDEBUG +static const bool enable_validation_layers = false; +#else +static const bool enable_validation_layers = true; +#endif + +static const std::vector + validation_layers = {"VK_LAYER_KHRONOS_validation"}, + device_extensions = {VK_KHR_SWAPCHAIN_EXTENSION_NAME}; + +static VKAPI_ATTR VkBool32 VKAPI_CALL DebugCallback( + VkDebugUtilsMessageSeverityFlagBitsEXT severity, + VkDebugUtilsMessageTypeFlagsEXT type, + const VkDebugUtilsMessengerCallbackDataEXT *data, void *user_data) { + printf("Validation Layer: %s\n", data->pMessage); + return false; +} + +QueueFamilyIndices FindQueueFamilies(VkPhysicalDevice device); +SwapchainSupportInfo QuerySwapchainSupport(VkPhysicalDevice device); + +extern VkInstance instance; +extern VkDebugUtilsMessengerEXT messenger; +extern VkPhysicalDevice physical_device; +extern VkDevice device; +extern VkQueue graphics_queue, present_queue; +extern VkSurfaceKHR surface; +extern VkSwapchainKHR swapchain; +extern std::vector swapchain_image_views; +extern std::vector swapchain_images; +extern VkFormat swapchain_image_format; +extern VkExtent2D swapchain_extent; +} // namespace internal + +void CreateInstance(); +void CreateMessenger(); +void CreateSurface(); +void CreateDevice(); +void CreateSwapchain(); +void CreateImageViews(); + +void DestroyMessenger(); +void Quit(); +} // namespace ion::render::api diff --git a/src/base/assets.cc b/src/base/assets.cc index 8c72ce2..c36c7cf 100644 --- a/src/base/assets.cc +++ b/src/base/assets.cc @@ -149,15 +149,15 @@ ION_API bool ion::res::CheckApplicationStructure() { auto dir = tinyfd_selectFolderDialog("Select Assets Directory", nullptr); if (std::filesystem::exists({dir})) { std::filesystem::create_directory("assets"); - for (const auto& entry : std::filesystem::directory_iterator(dir)) { + for (const auto &entry : std::filesystem::directory_iterator(dir)) { if (entry.is_directory()) { std::filesystem::create_directory("assets" / - entry.path().filename()); + entry.path().filename()); } std::filesystem::copy( - entry.path(), "assets" / entry.path().filename(), - std::filesystem::copy_options::recursive | - std::filesystem::copy_options::update_existing); + entry.path(), "assets" / entry.path().filename(), + std::filesystem::copy_options::recursive | + std::filesystem::copy_options::update_existing); } } else { return false; diff --git a/src/base/base_pipeline.cc b/src/base/base_pipeline.cc index 9f873ad..28d7d0d 100644 --- a/src/base/base_pipeline.cc +++ b/src/base/base_pipeline.cc @@ -15,7 +15,7 @@ BasePipeline::BasePipeline() { bloom_buffer_2 = ion::render::CreateFramebuffer( FramebufferInfo{.recreate_on_resize = true, .name = "Bloom 2"}); output_buffer = ion::render::CreateFramebuffer( - FramebufferInfo{ .recreate_on_resize = true, .name = "Output" }); + FramebufferInfo{.recreate_on_resize = true, .name = "Output"}); deferred_shader = ion::res::LoadAsset("assets/deferred_shader", false); screen_shader = ion::res::LoadAsset("assets/screen_shader", false); @@ -70,11 +70,11 @@ void BasePipeline::Render(std::shared_ptr world, } if (settings.render_to_output_buffer) { - ion::render::BindFramebuffer(output_buffer); + ion::render::BindFramebuffer(output_buffer); ion::render::Clear(); - ion::render::DrawFramebuffer(shaded, screen_shader, screen_data, output_buffer); - } - else { + ion::render::DrawFramebuffer(shaded, screen_shader, screen_data, + output_buffer); + } else { ion::render::DrawFramebuffer(shaded, screen_shader, screen_data); } } diff --git a/src/base/render.cc b/src/base/render.cc index 5d6a152..87c23a1 100644 --- a/src/base/render.cc +++ b/src/base/render.cc @@ -1,9 +1,7 @@ -// dependency -#include -// end +#include "ion/render.h" #include "ion/component.h" #include "ion/error_code.h" -#include "ion/render.h" +#include "ion/render/api.h" #include "ion/shader.h" #include "ion/texture.h" #include "ion/world.h" @@ -12,9 +10,10 @@ #include #include #include -#include +#include #include #include +#include namespace ion::render::internal { ION_API GLFWwindow *window = nullptr; @@ -46,27 +45,11 @@ static glm::mat4 GetModelFromTransform(std::shared_ptr transform) { glm::scale(model, glm::vec3(transform->scale.x, transform->scale.y, 1)); return model; } -static GLenum GetTypeEnum(DataType type) { - switch (type) { - case DataType::INT: - return GL_INT; - case DataType::UNSIGNED_INT: - return GL_UNSIGNED_INT; - case DataType::FLOAT: - return GL_FLOAT; - default: - return GL_FLOAT; - } -} int ion::render::Init() { glfwInit(); - glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); - glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); - glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); -#ifdef __APPLE__ - glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); -#endif + glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); + glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE); internal::window = glfwCreateWindow( r_config.window_size.x, r_config.window_size.y, "ion", NULL, NULL); if (!internal::window) { @@ -74,13 +57,13 @@ int ion::render::Init() { glfwTerminate(); return -1; } - glfwMakeContextCurrent(internal::window); - if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)) { - printf("%d\n", OPENGL_LOADER_FAIL); - return -1; - } - glEnable(GL_DEPTH_TEST); - glDisable(GL_BLEND); + api::CreateInstance(); + api::CreateMessenger(); + api::CreateSurface(); + api::CreateDevice(); + api::CreateSwapchain(); + api::CreateImageViews(); + glfwSetFramebufferSizeCallback(internal::window, SizeCallback); return 0; } @@ -99,82 +82,28 @@ void ion::render::SetClearColor(glm::vec3 color) { void ion::render::ConfigureData(std::shared_ptr gpu_data) { auto desc = gpu_data->GetDescriptor(); gpu_data->element_enabled = desc.element_enabled; - glGenVertexArrays(1, &gpu_data->vertex_attrib); - glGenBuffers(1, &gpu_data->vertex_buffer); if (gpu_data->element_enabled) { - glGenBuffers(1, &gpu_data->element_buffer); } - glBindVertexArray(gpu_data->vertex_attrib); - glBindBuffer(GL_ARRAY_BUFFER, gpu_data->vertex_buffer); - glBufferData(GL_ARRAY_BUFFER, sizeof(float) * desc.vertices.size(), - desc.vertices.data(), GL_STATIC_DRAW); if (gpu_data->element_enabled) { - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, gpu_data->element_buffer); - glBufferData(GL_ELEMENT_ARRAY_BUFFER, - sizeof(unsigned int) * desc.indices.size(), - desc.indices.data(), GL_STATIC_DRAW); } for (int i = 0; i < desc.pointers.size(); i++) { auto &pointer_data = desc.pointers[i]; - glVertexAttribPointer(i, pointer_data.size, GetTypeEnum(pointer_data.type), - pointer_data.normalized, pointer_data.stride, - pointer_data.pointer); - glEnableVertexAttribArray(i); } UnbindData(); } void ion::render::DestroyData(std::shared_ptr data) { - glDeleteVertexArrays(1, &data->vertex_attrib); - glDeleteBuffers(1, &data->vertex_buffer); if (data->element_enabled) { - glDeleteBuffers(1, &data->element_buffer); } } void ion::render::BindData(std::shared_ptr data) { - glBindVertexArray(data->vertex_attrib); - glBindBuffer(GL_ARRAY_BUFFER, data->vertex_buffer); if (data->element_enabled) { - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, data->element_buffer); } } -void ion::render::UnbindData() { - glBindVertexArray(0); - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); - glBindBuffer(GL_ARRAY_BUFFER, 0); -} +void ion::render::UnbindData() {} unsigned int ion::render::ConfigureTexture(const TextureInfo &texture_info) { - unsigned int texture; - glGenTextures(1, &texture); - glBindTexture(GL_TEXTURE_2D, texture); + unsigned int texture = 0; if (texture_info.data) { - GLenum format; - switch (texture_info.nr_channels) { - case 1: - format = GL_RED; - break; - case 2: - format = GL_RG; - break; - case 3: - format = GL_RGB; - break; - case 4: - format = GL_RGBA; - break; - default: - format = GL_RGB; - break; - } - glTexImage2D(GL_TEXTURE_2D, 0, format, texture_info.width, - texture_info.height, 0, format, GL_UNSIGNED_BYTE, - texture_info.data); - glGenerateMipmap(GL_TEXTURE_2D); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, - GL_NEAREST_MIPMAP_NEAREST); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); } else { printf("%d\n", TEXTURE_LOAD_FAIL); } @@ -185,77 +114,31 @@ std::shared_ptr ion::render::CreateFramebuffer(const FramebufferInfo &info) { auto framebuffer = std::make_shared(); framebuffer->recreate_on_resize = info.recreate_on_resize; - glGenFramebuffers(1, &framebuffer->framebuffer); - glBindFramebuffer(GL_FRAMEBUFFER, framebuffer->framebuffer); - glGenTextures(1, &framebuffer->colorbuffer); - glBindTexture(GL_TEXTURE_2D, framebuffer->colorbuffer); - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA16F, - static_cast(r_config.window_size.x) / r_config.render_scale, - static_cast(r_config.window_size.y) / r_config.render_scale, - 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - glBindTexture(GL_TEXTURE_2D, 0); - glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, - framebuffer->colorbuffer, 0); - if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) { - printf("Failed to create framebuffer\n"); - } - glBindFramebuffer(GL_FRAMEBUFFER, 0); internal::framebuffers.insert({framebuffer, info.name}); return framebuffer; } void ion::render::UpdateFramebuffers() { for (auto &[framebuffer, name] : internal::framebuffers) { if (framebuffer->recreate_on_resize) { - glBindTexture(GL_TEXTURE_2D, framebuffer->colorbuffer); - glTexImage2D( - GL_TEXTURE_2D, 0, GL_RGBA16F, - static_cast(r_config.window_size.x) / r_config.render_scale, - static_cast(r_config.window_size.y) / r_config.render_scale, 0, - GL_RGBA, GL_UNSIGNED_BYTE, nullptr); - glBindTexture(GL_TEXTURE_2D, 0); } } } -void ion::render::BindFramebuffer(std::shared_ptr framebuffer) { - glBindFramebuffer(GL_FRAMEBUFFER, framebuffer->framebuffer); - glViewport(0, 0, - static_cast(r_config.window_size.x) / r_config.render_scale, - static_cast(r_config.window_size.y) / r_config.render_scale); -} -void ion::render::UnbindFramebuffer() { glBindFramebuffer(GL_FRAMEBUFFER, 0); } +void ion::render::BindFramebuffer(std::shared_ptr framebuffer) {} +void ion::render::UnbindFramebuffer() {} + void ion::render::DrawFramebuffer(std::shared_ptr framebuffer, std::shared_ptr shader, std::shared_ptr quad, std::shared_ptr final_buffer) { - if (final_buffer) { - glBindFramebuffer(GL_FRAMEBUFFER, final_buffer->framebuffer); - glViewport(0, 0, - static_cast(r_config.window_size.x) / r_config.render_scale, - static_cast(r_config.window_size.y) / - r_config.render_scale); + if (final_buffer) { + } else { } - else { - glBindFramebuffer(GL_FRAMEBUFFER, 0); - glViewport(0, 0, static_cast(r_config.window_size.x), - static_cast(r_config.window_size.y)); - } - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - glDisable(GL_DEPTH_TEST); shader->Use(); BindData(quad); - glActiveTexture(GL_TEXTURE0); - glBindTexture(GL_TEXTURE_2D, framebuffer->colorbuffer); shader->SetUniform("screen_texture", 0); if (quad->element_enabled) { - glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0); } else { - glDrawArrays(GL_TRIANGLES, 0, 6); } - glEnable(GL_DEPTH_TEST); UnbindData(); } const std::map, std::string> & @@ -263,21 +146,12 @@ ion::render::GetFramebuffers() { return internal::framebuffers; } -void ion::render::BindTexture(std::shared_ptr texture, int slot) { - glActiveTexture(GL_TEXTURE0 + slot); - glBindTexture(GL_TEXTURE_2D, texture->texture); -} +void ion::render::BindTexture(std::shared_ptr texture, int slot) {} void ion::render::BindTexture(std::shared_ptr framebuffer, - int slot) { - glActiveTexture(GL_TEXTURE0 + slot); - glBindTexture(GL_TEXTURE_2D, framebuffer->colorbuffer); -} + int slot) {} -void ion::render::UseShader(std::shared_ptr shader) { - glUseProgram(shader->GetProgram()); -} +void ion::render::UseShader(std::shared_ptr shader) {} void ion::render::DestroyShader(std::shared_ptr shader) { - glDeleteProgram(shader->GetProgram()); shader.reset(); } @@ -307,14 +181,10 @@ void ion::render::DrawWorld(std::shared_ptr world, RenderPass pass) { renderable->shader->SetUniform("projection", projection); renderable->shader->SetUniform("model", GetModelFromTransform(transform)); - glActiveTexture(GL_TEXTURE0); if (pass == RENDER_PASS_COLOR) { - glBindTexture(GL_TEXTURE_2D, renderable->color->texture); } else if (pass == RENDER_PASS_NORMAL) { - glBindTexture(GL_TEXTURE_2D, renderable->normal->texture); } renderable->shader->SetUniform("sample", 0); - glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0); UnbindData(); } } @@ -329,34 +199,20 @@ void ion::render::RunPass(std::shared_ptr in, BindTexture(in, 0); BindData(quad); if (quad->element_enabled) { - glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0); } else { - glDrawArrays(GL_TRIANGLES, 0, 6); } UnbindData(); } -void ion::render::Clear() { - glClearColor(r_config.clear_color.r, r_config.clear_color.g, - r_config.clear_color.b, 1.0f); - glClear(GL_COLOR_BUFFER_BIT); -} -void ion::render::Clear(glm::vec4 color) { - glClearColor(color.r, color.g, color.b, color.a); - glClear(GL_COLOR_BUFFER_BIT); -} +void ion::render::Clear() {} +void ion::render::Clear(glm::vec4 color) {} int ion::render::Render(std::shared_ptr color_fb, std::shared_ptr normal_fb, std::shared_ptr quad, std::shared_ptr shader, std::shared_ptr world) { shader->Use(); - glClear(GL_COLOR_BUFFER_BIT); - glActiveTexture(GL_TEXTURE0); - glBindTexture(GL_TEXTURE_2D, color_fb->colorbuffer); shader->SetUniform("color_texture", 0); - glActiveTexture(GL_TEXTURE1); - glBindTexture(GL_TEXTURE_2D, normal_fb->colorbuffer); shader->SetUniform("normal_texture", 1); shader->SetUniform("light_count", static_cast(world->GetComponentSet().size())); @@ -396,18 +252,16 @@ int ion::render::Render(std::shared_ptr color_fb, light->volumetric_intensity); } BindData(quad); - glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0); UnbindData(); return 0; } int ion::render::Quit() { for (auto &[framebuffer, name] : internal::framebuffers) { - glDeleteFramebuffers(1, &framebuffer->framebuffer); - glDeleteTextures(1, &framebuffer->colorbuffer); } internal::framebuffers.clear(); + api::Quit(); glfwDestroyWindow(internal::window); glfwTerminate(); return 0; } -void ion::render::Present() { glfwSwapBuffers(internal::window); } \ No newline at end of file +void ion::render::Present() {} diff --git a/src/base/render/api.cc b/src/base/render/api.cc new file mode 100644 index 0000000..5227462 --- /dev/null +++ b/src/base/render/api.cc @@ -0,0 +1,25 @@ +#include "ion/render/api.h" + +namespace ion::render::api::internal { +VkInstance instance{}; +VkDebugUtilsMessengerEXT messenger{}; +VkPhysicalDevice physical_device = VK_NULL_HANDLE; +VkDevice device{}; +VkQueue graphics_queue{}, present_queue{}; +VkSurfaceKHR surface{}; +VkSwapchainKHR swapchain{}; +std::vector swapchain_image_views; +std::vector swapchain_images; +VkFormat swapchain_image_format; +VkExtent2D swapchain_extent; +} // namespace ion::render::api::internal + +void ion::render::api::Quit() { + for (auto view : internal::swapchain_image_views) { + vkDestroyImageView(internal::device, view, nullptr); + } + vkDestroySwapchainKHR(internal::device, internal::swapchain, nullptr); + vkDestroyDevice(internal::device, nullptr); + vkDestroySurfaceKHR(internal::instance, internal::surface, nullptr); + vkDestroyInstance(internal::instance, nullptr); +} diff --git a/src/base/render/debug.cc b/src/base/render/debug.cc new file mode 100644 index 0000000..978d7f8 --- /dev/null +++ b/src/base/render/debug.cc @@ -0,0 +1,53 @@ +#include "ion/render/api.h" +#include +#include + +VkResult CreateDebugUtilsMessengerEXT( + VkInstance instance, const VkDebugUtilsMessengerCreateInfoEXT *pCreateInfo, + const VkAllocationCallbacks *pAllocator, + VkDebugUtilsMessengerEXT *pDebugMessenger) { + auto func = (PFN_vkCreateDebugUtilsMessengerEXT)vkGetInstanceProcAddr( + instance, "vkCreateDebugUtilsMessengerEXT"); + if (func != nullptr) { + return func(instance, pCreateInfo, pAllocator, pDebugMessenger); + } else { + return VK_ERROR_EXTENSION_NOT_PRESENT; + } +} + +void DestroyDebugUtilsMessengerEXT(VkInstance instance, + VkDebugUtilsMessengerEXT debugMessenger, + const VkAllocationCallbacks *pAllocator) { + auto func = (PFN_vkDestroyDebugUtilsMessengerEXT)vkGetInstanceProcAddr( + instance, "vkDestroyDebugUtilsMessengerEXT"); + if (func != nullptr) { + func(instance, debugMessenger, pAllocator); + } +} + +void ion::render::api::CreateMessenger() { + if (!internal::enable_validation_layers) { + return; + } + + auto info = VkDebugUtilsMessengerCreateInfoEXT{}; + info.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT; + info.messageSeverity = VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT | + VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT | + VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT; + info.messageType = VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT | + VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT | + VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT; + info.pfnUserCallback = internal::DebugCallback; + info.pUserData = nullptr; + + if (CreateDebugUtilsMessengerEXT(internal::instance, &info, nullptr, + &internal::messenger) != VK_SUCCESS) { + throw std::runtime_error("Failed to create messenger"); + } +} + +void ion::render::api::DestroyMessenger() { + DestroyDebugUtilsMessengerEXT(internal::instance, internal::messenger, + nullptr); +} diff --git a/src/base/render/devices.cc b/src/base/render/devices.cc new file mode 100644 index 0000000..a38d86f --- /dev/null +++ b/src/base/render/devices.cc @@ -0,0 +1,172 @@ +#include "ion/render/api.h" +#include +#include +#include +#include +#include +#include +#include + +ion::render::api::QueueFamilyIndices +ion::render::api::internal::FindQueueFamilies(VkPhysicalDevice device) { + auto indices = QueueFamilyIndices{}; + uint32_t count = 0; + vkGetPhysicalDeviceQueueFamilyProperties(device, &count, nullptr); + auto families = std::vector{count}; + vkGetPhysicalDeviceQueueFamilyProperties(device, &count, families.data()); + + for (int i = 0; i < families.size(); i++) { + if (families[i].queueFlags & VK_QUEUE_GRAPHICS_BIT) { + indices.graphics_family = i; + } + VkBool32 present_support = false; + vkGetPhysicalDeviceSurfaceSupportKHR(device, i, surface, &present_support); + if (present_support) { + indices.present_family = i; + } + } + + return indices; +} + +bool DeviceSupportsExtensions(VkPhysicalDevice device) { + uint32_t extension_count; + vkEnumerateDeviceExtensionProperties(device, nullptr, &extension_count, + nullptr); + auto available_extensions = + std::vector{extension_count}; + vkEnumerateDeviceExtensionProperties(device, nullptr, &extension_count, + available_extensions.data()); + std::set required_extensions( + ion::render::api::internal::device_extensions.begin(), + ion::render::api::internal::device_extensions.end()); + for (const auto &extension : available_extensions) { + required_extensions.erase(extension.extensionName); + } + + return required_extensions.empty(); +} + +ion::render::api::SwapchainSupportInfo +ion::render::api::internal::QuerySwapchainSupport(VkPhysicalDevice device) { + auto support_info = SwapchainSupportInfo{}; + vkGetPhysicalDeviceSurfaceCapabilitiesKHR(device, surface, + &support_info.capabilities); + + uint32_t format_count; + vkGetPhysicalDeviceSurfaceFormatsKHR(device, surface, &format_count, nullptr); + if (format_count != 0) { + support_info.formats.resize(format_count); + vkGetPhysicalDeviceSurfaceFormatsKHR(device, surface, &format_count, + support_info.formats.data()); + } + + uint32_t present_mode_count; + vkGetPhysicalDeviceSurfacePresentModesKHR(device, surface, + &present_mode_count, nullptr); + if (present_mode_count != 0) { + support_info.present_modes.resize(present_mode_count); + vkGetPhysicalDeviceSurfacePresentModesKHR( + device, surface, &present_mode_count, + support_info.present_modes.data()); + } + + return support_info; +} + +int RateDevice(VkPhysicalDevice device) { + auto properties = VkPhysicalDeviceProperties{}; + vkGetPhysicalDeviceProperties(device, &properties); + auto features = VkPhysicalDeviceFeatures{}; + vkGetPhysicalDeviceFeatures(device, &features); + auto indices = ion::render::api::internal::FindQueueFamilies(device); + + int score = 0; + if (properties.deviceType == VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU) { + score += 1000; + } + score += properties.limits.maxImageDimension2D; + if (!features.geometryShader) { + return 0; + } + + if (!indices.graphics_family.has_value() || + !indices.present_family.has_value()) { + return 0; + } + + bool swapchain_supported = false; + if (DeviceSupportsExtensions(device)) { + auto support_info = + ion::render::api::internal::QuerySwapchainSupport(device); + swapchain_supported = + !support_info.formats.empty() && !support_info.present_modes.empty(); + } + if (!swapchain_supported) { + return 0; + } + + return score; +} + +void ion::render::api::CreateDevice() { + uint32_t device_count = 0; + vkEnumeratePhysicalDevices(internal::instance, &device_count, nullptr); + if (device_count == 0) { + throw std::runtime_error("Failed to find GPUs"); + } + auto devices = std::vector{device_count}; + vkEnumeratePhysicalDevices(internal::instance, &device_count, devices.data()); + + auto rated_devices = std::multimap{}; + for (const auto &device : devices) { + rated_devices.insert({RateDevice(device), device}); + } + if (rated_devices.rbegin()->first > 0) { + internal::physical_device = rated_devices.rbegin()->second; + } else { + throw std::runtime_error("Failed to find a suitable GPU"); + } + + auto indices = internal::FindQueueFamilies(internal::physical_device); + auto queue_create_infos = std::vector{}; + auto unique_queue_families = std::set{ + indices.graphics_family.value(), indices.present_family.value()}; + float priority = 1.0f; + for (uint32_t family : unique_queue_families) { + auto queue_info = VkDeviceQueueCreateInfo{}; + queue_info.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; + queue_info.queueFamilyIndex = family; + queue_info.queueCount = 1; + queue_info.pQueuePriorities = &priority; + queue_create_infos.push_back(queue_info); + } + + auto features = VkPhysicalDeviceFeatures{}; + auto info = VkDeviceCreateInfo{}; + info.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO; + info.queueCreateInfoCount = static_cast(queue_create_infos.size()); + info.pQueueCreateInfos = queue_create_infos.data(); + info.pEnabledFeatures = &features; + + info.enabledExtensionCount = + static_cast(internal::device_extensions.size()); + info.ppEnabledExtensionNames = internal::device_extensions.data(); + + if (internal::enable_validation_layers) { + info.enabledLayerCount = + static_cast(internal::validation_layers.size()); + info.ppEnabledLayerNames = internal::validation_layers.data(); + } else { + info.enabledLayerCount = 0; + } + + if (vkCreateDevice(internal::physical_device, &info, nullptr, + &internal::device) != VK_SUCCESS) { + throw std::runtime_error("Failed to create device"); + } + vkGetDeviceQueue(internal::device, indices.graphics_family.value(), 0, + &internal::graphics_queue); + vkGetDeviceQueue(internal::device, indices.present_family.value(), 0, + &internal::present_queue); +} diff --git a/src/base/render/image_views.cc b/src/base/render/image_views.cc new file mode 100644 index 0000000..309cedf --- /dev/null +++ b/src/base/render/image_views.cc @@ -0,0 +1,26 @@ +#include "ion/render/api.h" +#include +void ion::render::api::CreateImageViews() { + internal::swapchain_image_views.resize(internal::swapchain_images.size()); + for (size_t i = 0; i < internal::swapchain_images.size(); i++) { + auto info = VkImageViewCreateInfo{}; + info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; + info.image = internal::swapchain_images.at(i); + info.viewType = VK_IMAGE_VIEW_TYPE_2D; + info.format = internal::swapchain_image_format; + info.components.r = VK_COMPONENT_SWIZZLE_IDENTITY; + info.components.g = VK_COMPONENT_SWIZZLE_IDENTITY; + info.components.b = VK_COMPONENT_SWIZZLE_IDENTITY; + info.components.a = VK_COMPONENT_SWIZZLE_IDENTITY; + info.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + info.subresourceRange.baseMipLevel = 0; + info.subresourceRange.levelCount = 1; + info.subresourceRange.baseArrayLayer = 0; + info.subresourceRange.layerCount = 1; + if (vkCreateImageView(internal::device, &info, nullptr, + &internal::swapchain_image_views.at(i)) != + VK_SUCCESS) { + throw std::runtime_error("Failed to create image view"); + } + } +} diff --git a/src/base/render/instance.cc b/src/base/render/instance.cc new file mode 100644 index 0000000..8bbf0df --- /dev/null +++ b/src/base/render/instance.cc @@ -0,0 +1,77 @@ +#include "ion/render/api.h" +#include +#include +#include +#include +#include +#include + +bool CheckValidationLayerSupport() { + uint32_t layer_count; + vkEnumerateInstanceLayerProperties(&layer_count, nullptr); + auto available_layers = std::vector{layer_count}; + vkEnumerateInstanceLayerProperties(&layer_count, available_layers.data()); + + for (auto name : ion::render::api::internal::validation_layers) { + bool layer_found = false; + for (const auto &properties : available_layers) { + if (strcmp(name, properties.layerName) == 0) { + layer_found = true; + break; + } + } + if (!layer_found) { + return false; + } + return false; + } + return true; +} + +std::vector GetRequiredExtensions() { + uint32_t glfw_extension_count = 0; + auto glfw_extensions = + glfwGetRequiredInstanceExtensions(&glfw_extension_count); + auto extensions = std::vector{ + glfw_extensions, glfw_extensions + glfw_extension_count}; + + if (ion::render::api::internal::enable_validation_layers) { + extensions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME); + } + return extensions; +} + +void ion::render::api::CreateInstance() { + if (internal::enable_validation_layers && !CheckValidationLayerSupport()) { + throw std::runtime_error("Requested validation layers were not available."); + } + auto app_info = VkApplicationInfo{}; + app_info.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO; + app_info.pApplicationName = "ion"; + app_info.applicationVersion = VK_MAKE_VERSION(1, 0, 0); + app_info.pEngineName = "ion"; + app_info.engineVersion = VK_MAKE_VERSION(1, 0, 0); + app_info.apiVersion = VK_API_VERSION_1_0; + + auto create_info = VkInstanceCreateInfo{}; + create_info.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO; + create_info.pApplicationInfo = &app_info; + + auto extensions = GetRequiredExtensions(); + create_info.enabledExtensionCount = static_cast(extensions.size()); + create_info.ppEnabledExtensionNames = extensions.data(); + + if (internal::enable_validation_layers) { + create_info.enabledLayerCount = static_cast( + ion::render::api::internal::validation_layers.size()); + create_info.ppEnabledLayerNames = + ion::render::api::internal::validation_layers.data(); + } else { + create_info.enabledLayerCount = 0; + } + + if (vkCreateInstance(&create_info, nullptr, &internal::instance) != + VK_SUCCESS) { + throw std::runtime_error("Failed to create instance"); + } +} diff --git a/src/base/render/surface.cc b/src/base/render/surface.cc new file mode 100644 index 0000000..33c8080 --- /dev/null +++ b/src/base/render/surface.cc @@ -0,0 +1,11 @@ +#define GLFW_INCLUDE_VULKAN +#include "ion/render.h" +#include "ion/render/api.h" +#include + +void ion::render::api::CreateSurface() { + if (glfwCreateWindowSurface(internal::instance, ion::render::GetWindow(), + nullptr, &internal::surface) != VK_SUCCESS) { + throw std::runtime_error("Failed to create surface"); + } +} diff --git a/src/base/render/swapchain.cc b/src/base/render/swapchain.cc new file mode 100644 index 0000000..9f1fc78 --- /dev/null +++ b/src/base/render/swapchain.cc @@ -0,0 +1,99 @@ +#include "ion/render.h" +#include "ion/render/api.h" +#include +#include +#include +#include + +VkSurfaceFormatKHR +ChooseSurfaceFormat(const std::vector &available_formats) { + for (const auto &available_format : available_formats) { + if (available_format.format == VK_FORMAT_B8G8R8A8_SRGB && + available_format.colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR) { + return available_format; + } + } + + return available_formats.front(); +} + +VkPresentModeKHR +ChoosePresentMode(const std::vector &available_modes) { + for (const auto &available_mode : available_modes) { + if (available_mode == VK_PRESENT_MODE_MAILBOX_KHR) { + return available_mode; + } + } + return VK_PRESENT_MODE_FIFO_KHR; +} + +VkExtent2D ChooseExtent(const VkSurfaceCapabilitiesKHR &capabilities) { + if (capabilities.currentExtent.width != + std::numeric_limits::max()) { + return capabilities.currentExtent; + } else { + int width, height; + glfwGetFramebufferSize(ion::render::GetWindow(), &width, &height); + VkExtent2D actual_extent = {static_cast(width), + static_cast(height)}; + actual_extent.width = + std::clamp(actual_extent.width, capabilities.minImageExtent.width, + capabilities.maxImageExtent.width); + actual_extent.height = + std::clamp(actual_extent.height, capabilities.minImageExtent.height, + capabilities.maxImageExtent.height); + return actual_extent; + } +} + +void ion::render::api::CreateSwapchain() { + auto support = internal::QuerySwapchainSupport(internal::physical_device); + auto surface_format = ChooseSurfaceFormat(support.formats); + auto present_mode = ChoosePresentMode(support.present_modes); + auto extent = ChooseExtent(support.capabilities); + uint32_t image_count = support.capabilities.minImageCount + 1; + if (support.capabilities.maxImageCount > 0 && + image_count > support.capabilities.maxImageCount) { + image_count = support.capabilities.maxImageCount; + } + + auto info = VkSwapchainCreateInfoKHR{}; + info.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR; + info.surface = internal::surface; + info.minImageCount = image_count; + info.imageFormat = surface_format.format; + info.imageColorSpace = surface_format.colorSpace; + info.imageExtent = extent; + info.imageArrayLayers = 1; + info.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; + auto indices = internal::FindQueueFamilies(internal::physical_device); + uint32_t queue_family_indices[] = {indices.graphics_family.value(), + indices.present_family.value()}; + if (indices.graphics_family != indices.present_family) { + info.imageSharingMode = VK_SHARING_MODE_CONCURRENT; + info.queueFamilyIndexCount = 2; + info.pQueueFamilyIndices = queue_family_indices; + } else { + info.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE; + info.queueFamilyIndexCount = 0; + info.pQueueFamilyIndices = nullptr; + } + info.preTransform = support.capabilities.currentTransform; + info.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR; + info.presentMode = present_mode; + info.clipped = VK_TRUE; + info.oldSwapchain = VK_NULL_HANDLE; + + if (vkCreateSwapchainKHR(internal::device, &info, nullptr, + &internal::swapchain) != VK_SUCCESS) { + throw std::runtime_error("Failed to create swapchain"); + } + + vkGetSwapchainImagesKHR(internal::device, internal::swapchain, &image_count, + nullptr); + internal::swapchain_images.resize(image_count); + vkGetSwapchainImagesKHR(internal::device, internal::swapchain, &image_count, + internal::swapchain_images.data()); + internal::swapchain_image_format = surface_format.format; + internal::swapchain_extent = extent; +} diff --git a/src/base/shader.cc b/src/base/shader.cc index be45179..c54242d 100644 --- a/src/base/shader.cc +++ b/src/base/shader.cc @@ -11,39 +11,30 @@ #include #include -void Shader::Use() { glUseProgram(program); } +void Shader::Use() {} + unsigned int Shader::GetProgram() { return program; } template <> int Shader::SetUniform(std::string_view name, int value) { - auto loc = glGetUniformLocation(program, name.data()); - glUniform1i(loc, value); return 0; } template <> int Shader::SetUniform(std::string_view name, float value) { - auto loc = glGetUniformLocation(program, name.data()); - glUniform1f(loc, value); return 0; } template <> int Shader::SetUniform(std::string_view name, glm::vec2 value) { - auto loc = glGetUniformLocation(program, name.data()); - glUniform2fv(loc, 1, glm::value_ptr(value)); return 0; } template <> int Shader::SetUniform(std::string_view name, glm::vec3 value) { - auto loc = glGetUniformLocation(program, name.data()); - glUniform3fv(loc, 1, glm::value_ptr(value)); return 0; } template <> int Shader::SetUniform(std::string_view name, glm::mat4 value) { - auto loc = glGetUniformLocation(program, name.data()); - glUniformMatrix4fv(loc, 1, GL_FALSE, glm::value_ptr(value)); return 0; } @@ -67,43 +58,25 @@ Shader::Shader(std::filesystem::path new_path, std::string_view new_id) : path(new_path), id(new_id) { unsigned int vertex, fragment; int success; - std::array info_log; - vertex = glCreateShader(GL_VERTEX_SHADER); - fragment = glCreateShader(GL_FRAGMENT_SHADER); + std::array info_log = {}; auto vertex_code = _ShaderInternalReadFile(path / "vs.glsl"), fragment_code = _ShaderInternalReadFile(path / "fs.glsl"); auto vertex_code_char = vertex_code.c_str(), fragment_code_char = fragment_code.c_str(); - glShaderSource(vertex, 1, &vertex_code_char, nullptr); - glCompileShader(vertex); - glGetShaderiv(vertex, GL_COMPILE_STATUS, &success); if (!success) { - glGetShaderInfoLog(vertex, info_log.size(), nullptr, info_log.data()); printf("Error while compiling vertex shader %s\n", (path / "vs.glsl").c_str()); printf("%s\n", info_log.data()); printf("%d\n", VERTEX_COMPILATION_FAIL); } - glShaderSource(fragment, 1, &fragment_code_char, nullptr); - glCompileShader(fragment); - glGetShaderiv(fragment, GL_COMPILE_STATUS, &success); if (!success) { - glGetShaderInfoLog(fragment, 512, nullptr, info_log.data()); printf("Error while compiling fragment shader %s\n", (path / "fs.glsl").c_str()); printf("%s\n", info_log.data()); printf("%d\n", FRAGMENT_COMPILATION_FAIL); } - program = glCreateProgram(); - glAttachShader(program, vertex); - glAttachShader(program, fragment); - glLinkProgram(program); - glGetProgramiv(program, GL_LINK_STATUS, &success); if (!success) { - glGetProgramInfoLog(program, 512, nullptr, info_log.data()); printf("%s\n", info_log.data()); printf("%d\n", SHADER_PROGRAM_LINK_FAIL); } - glDeleteShader(vertex); - glDeleteShader(fragment); } diff --git a/src/base/texture.cc b/src/base/texture.cc index 1048f7d..be365bc 100644 --- a/src/base/texture.cc +++ b/src/base/texture.cc @@ -1,4 +1,6 @@ #include "ion/texture.h" #include -void Texture::Use() { glBindTexture(GL_TEXTURE_2D, texture); } \ No newline at end of file +void Texture::Use() { + // glBindTexture(GL_TEXTURE_2D, texture); +} diff --git a/src/development/gui.cc b/src/development/gui.cc index 9c1b601..7d3b9f9 100644 --- a/src/development/gui.cc +++ b/src/development/gui.cc @@ -1,9 +1,8 @@ #include "ion/development/gui.h" -#include +#include "ion/render/api.h" #include #include -#include -#include +#include static ImGuiContext *g_SharedImGuiContext = nullptr; void *ion_gui_alloc(size_t size, void *user_data) { return malloc(size); } @@ -19,11 +18,11 @@ void ion::gui::Init(GLFWwindow *window) { ImGui::SetAllocatorFunctions(ion_gui_alloc, ion_gui_free, user_data); g_SharedImGuiContext = ImGui::CreateContext(); ImGui::SetCurrentContext(g_SharedImGuiContext); - ImGuiIO& imgui_io = ImGui::GetIO(); + ImGuiIO &imgui_io = ImGui::GetIO(); imgui_io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; imgui_io.ConfigFlags |= ImGuiConfigFlags_DockingEnable; imgui_io.ConfigFlags |= ImGuiConfigFlags_IsSRGB; - ImGuiStyle& style = ImGui::GetStyle(); + ImGuiStyle &style = ImGui::GetStyle(); style.WindowRounding = GUI_ROUNDING_MORE; style.ChildRounding = GUI_ROUNDING_MORE; style.FrameRounding = GUI_ROUNDING_LESS; @@ -31,29 +30,31 @@ void ion::gui::Init(GLFWwindow *window) { style.ScrollbarRounding = GUI_ROUNDING_LESS; style.GrabRounding = GUI_ROUNDING_LESS; style.TabRounding = GUI_ROUNDING_LESS; - ImGui_ImplGlfw_InitForOpenGL(window, true); - ImGui_ImplOpenGL3_Init("#version 150"); + ImGui_ImplGlfw_InitForVulkan(window, true); + auto info = ImGui_ImplVulkan_InitInfo{}; + info.Instance = ion::render::api::internal::instance; + ImGui_ImplVulkan_Init(&info); } void ion::gui::NewFrame() { ImGui::SetCurrentContext(g_SharedImGuiContext); - ImGui_ImplOpenGL3_NewFrame(); ImGui_ImplGlfw_NewFrame(); + ImGui_ImplVulkan_NewFrame(); ImGui::NewFrame(); } void ion::gui::Render() { ImGui::SetCurrentContext(g_SharedImGuiContext); ImGui::Render(); - ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData()); + ImGui_ImplVulkan_RenderDrawData(ImGui::GetDrawData(), nullptr); } void ion::gui::Quit() { ImGui::SetCurrentContext(g_SharedImGuiContext); - ImGui_ImplOpenGL3_Shutdown(); + ImGui_ImplVulkan_Shutdown(); ImGui_ImplGlfw_Shutdown(); ImGui::DestroyContext(g_SharedImGuiContext); g_SharedImGuiContext = nullptr; } -ImGuiContext *ion::gui::GetContext() { return g_SharedImGuiContext; } \ No newline at end of file +ImGuiContext *ion::gui::GetContext() { return g_SharedImGuiContext; } diff --git a/src/development/inspector.cc b/src/development/inspector.cc index 5190825..e57ac79 100644 --- a/src/development/inspector.cc +++ b/src/development/inspector.cc @@ -1,5 +1,6 @@ #include "ion/development/inspector.h" #include "ion/assets.h" +#include "ion/base_pipeline.h" #include "ion/development/gui.h" #include "ion/development/id.h" #include "ion/development/package.h" @@ -11,11 +12,10 @@ #include #include #include -#include #include +#include #include #include -#include "ion/base_pipeline.h" constexpr int BLOOM_STRENGTH_MIN = 1; constexpr int BLOOM_STRENGTH_MAX = 20; @@ -29,9 +29,9 @@ constexpr const char *VIEWPORT_INSPECTOR_KEY = "Viewport"; constexpr ImVec2 FRAMEBUFFER_PREVIEW_SIZE = ImVec2(200, 200); constexpr ImVec2 FRAMEBUFFER_UV_0 = ImVec2(0, 1); constexpr ImVec2 FRAMEBUFFER_UV_1 = ImVec2(1, 0); -constexpr float DOCK_LEFT_WIDTH= 0.3F; -constexpr float DOCK_RIGHT_WIDTH= 0.3F; -constexpr float DOCK_CENTER_WIDTH= 0.4F; +constexpr float DOCK_LEFT_WIDTH = 0.3F; +constexpr float DOCK_RIGHT_WIDTH = 0.3F; +constexpr float DOCK_CENTER_WIDTH = 0.4F; namespace ion::dev::ui::internal { std::map inspector_state; @@ -504,29 +504,31 @@ static void SystemInspector() { } static void MinimalStateInspector() { - ImGui::Begin("State", nullptr, ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoCollapse); - auto playing = ion::systems::GetState(); - if (playing) { + ImGui::Begin("State", nullptr, + ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoTitleBar | + ImGuiWindowFlags_NoCollapse); + auto playing = ion::systems::GetState(); + if (playing) { if (ImGui::Button("Pause")) { ion::systems::SetState(false); - } - } else { + } + } else { if (ImGui::Button("Play")) { ion::systems::SetState(true); - } - } + } + } ImGui::End(); } -void FramebufferInspector(PipelineSettings& settings) { +void FramebufferInspector(PipelineSettings &settings) { ImGui::Begin("Framebuffers"); ImGui::Checkbox("Enable Bloom", &settings.bloom_enable); ImGui::SliderInt("Bloom Strength", &settings.bloom_strength, - BLOOM_STRENGTH_MIN, BLOOM_STRENGTH_MAX); - for (auto& [buffer, name] : ion::render::GetFramebuffers()) { + BLOOM_STRENGTH_MIN, BLOOM_STRENGTH_MAX); + for (auto &[buffer, name] : ion::render::GetFramebuffers()) { ImGui::PushID(buffer->framebuffer); ImGui::Image(buffer->colorbuffer, FRAMEBUFFER_PREVIEW_SIZE, - FRAMEBUFFER_UV_0, FRAMEBUFFER_UV_1); + FRAMEBUFFER_UV_0, FRAMEBUFFER_UV_1); ImGui::SameLine(); ImGui::TextUnformatted(name.c_str()); ImGui::PopID(); @@ -534,27 +536,30 @@ void FramebufferInspector(PipelineSettings& settings) { ImGui::End(); } -void ViewportInspector(BasePipeline& pipeline) { +void ViewportInspector(BasePipeline &pipeline) { ImGui::Begin("Viewport", nullptr, ImGuiWindowFlags_NoDecoration); - auto window_size = ImGui::GetWindowSize(); + auto window_size = ImGui::GetWindowSize(); ImGui::Image(pipeline.output_buffer->colorbuffer, window_size, - FRAMEBUFFER_UV_0, FRAMEBUFFER_UV_1); + FRAMEBUFFER_UV_0, FRAMEBUFFER_UV_1); ImGui::End(); } void ion::dev::ui::RenderInspector(std::shared_ptr &world, - Defaults &defaults, BasePipeline& pipeline, PipelineSettings& settings) { + Defaults &defaults, BasePipeline &pipeline, + PipelineSettings &settings) { ION_GUI_PREP_CONTEXT(); - ImGuiViewport* viewport = ImGui::GetMainViewport(); + ImGuiViewport *viewport = ImGui::GetMainViewport(); ImGui::SetNextWindowPos(viewport->Pos); ImGui::SetNextWindowSize(viewport->Size); ImGui::SetNextWindowViewport(viewport->ID); ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0F); ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.0F); - ImGuiWindowFlags window_flags = ImGuiWindowFlags_NoDocking | ImGuiWindowFlags_NoTitleBar | - ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoResize | - ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoBringToFrontOnFocus | - ImGuiWindowFlags_NoNavFocus | ImGuiWindowFlags_MenuBar | ImGuiWindowFlags_NoBackground; + ImGuiWindowFlags window_flags = + ImGuiWindowFlags_NoDocking | ImGuiWindowFlags_NoTitleBar | + ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoResize | + ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoBringToFrontOnFocus | + ImGuiWindowFlags_NoNavFocus | ImGuiWindowFlags_MenuBar | + ImGuiWindowFlags_NoBackground; ImGui::Begin("DockSpace_Window", nullptr, window_flags); ImGui::PopStyleVar(2); ImGuiID dockspace_id = ImGui::GetID("DockSpace"); @@ -567,21 +572,21 @@ void ion::dev::ui::RenderInspector(std::shared_ptr &world, ImGui::DockBuilderAddNode(dockspace_id, ImGuiDockNodeFlags_DockSpace); ImGui::DockBuilderSetNodeSize(dockspace_id, viewport->Size); ImGuiID dock_main_id = dockspace_id; - ImGuiID dock_id_top = ImGui::DockBuilderSplitNode(dock_main_id, ImGuiDir_Up, - 0.1F, nullptr, &dock_main_id); - ImGuiID dock_id_bottom = ImGui::DockBuilderSplitNode(dock_main_id, ImGuiDir_Down, - DOCK_CENTER_WIDTH, nullptr, &dock_main_id); - ImGuiID dock_id_left = ImGui::DockBuilderSplitNode(dock_main_id, ImGuiDir_Left, DOCK_LEFT_WIDTH, - nullptr, &dock_main_id); - ImGuiID dock_id_right = ImGui::DockBuilderSplitNode(dock_main_id, ImGuiDir_Right, - DOCK_RIGHT_WIDTH, nullptr, &dock_main_id); + ImGuiID dock_id_top = ImGui::DockBuilderSplitNode( + dock_main_id, ImGuiDir_Up, 0.1F, nullptr, &dock_main_id); + ImGuiID dock_id_bottom = ImGui::DockBuilderSplitNode( + dock_main_id, ImGuiDir_Down, DOCK_CENTER_WIDTH, nullptr, &dock_main_id); + ImGuiID dock_id_left = ImGui::DockBuilderSplitNode( + dock_main_id, ImGuiDir_Left, DOCK_LEFT_WIDTH, nullptr, &dock_main_id); + ImGuiID dock_id_right = ImGui::DockBuilderSplitNode( + dock_main_id, ImGuiDir_Right, DOCK_RIGHT_WIDTH, nullptr, &dock_main_id); ImGui::DockBuilderDockWindow("Viewport", dock_main_id); ImGui::DockBuilderDockWindow("World", dock_id_left); ImGui::DockBuilderDockWindow("Render", dock_id_right); ImGui::DockBuilderDockWindow("IO", dock_id_bottom); ImGui::DockBuilderDockWindow("Assets", dock_id_bottom); ImGui::DockBuilderDockWindow("Framebuffers", dock_id_bottom); - ImGui::DockBuilderDockWindow("Systems", dock_id_bottom); + ImGui::DockBuilderDockWindow("Systems", dock_id_bottom); ImGui::DockBuilderDockWindow("State", dock_id_top); ImGui::DockBuilderFinish(dockspace_id); } @@ -604,9 +609,9 @@ void ion::dev::ui::RenderInspector(std::shared_ptr &world, } if (internal::inspector_state[FRAMEBUFFER_INSPECTOR_KEY]) { FramebufferInspector(settings); - } + } if (internal::inspector_state[VIEWPORT_INSPECTOR_KEY]) { ViewportInspector(pipeline); - } - MinimalStateInspector(); + } + MinimalStateInspector(); } \ No newline at end of file diff --git a/src/development/main.cc b/src/development/main.cc index 021dec9..d134e23 100644 --- a/src/development/main.cc +++ b/src/development/main.cc @@ -70,7 +70,8 @@ int main() { glfwPollEvents(); ion::systems::UpdateSystems(world, ion::systems::UpdatePhase::PRE_UPDATE); ion::gui::NewFrame(); - ion::dev::ui::RenderInspector(world, defaults, pipeline, pipeline_settings); + ion::dev::ui::RenderInspector(world, defaults, pipeline, + pipeline_settings); ion::systems::UpdateSystems(world, ion::systems::UpdatePhase::UPDATE); pipeline.Render(world, pipeline_settings); ion::render::UnbindFramebuffer(); diff --git a/src/development/package.cc b/src/development/package.cc index a4a8577..9c0c0d9 100644 --- a/src/development/package.cc +++ b/src/development/package.cc @@ -1,20 +1,21 @@ #include "ion/development/package.h" #include "ion/world.h" #include -#include #include +#include #define TOOLS_RUNNER_DIRECTORY "" #define TOOLS_RUNNER_LOCATION "ion-run.exe" void ion::dev::internal::PackInfo::Write(const std::filesystem::path &path) { - pugi::xml_document doc; - auto root = doc.append_child("worlds"); - for (const auto& [id, world_path] : worlds) { + pugi::xml_document doc; + auto root = doc.append_child("worlds"); + for (const auto &[id, world_path] : worlds) { auto world_node = root.append_child("world"); world_node.append_attribute("id") = id; - world_node.append_attribute("path") = ("assets" / world_path.filename()).string().c_str(); - } - doc.save_file(path.string().c_str()); + world_node.append_attribute("path") = + ("assets" / world_path.filename()).string().c_str(); + } + doc.save_file(path.string().c_str()); } void PrepareDirectory(std::filesystem::path path) { @@ -83,14 +84,19 @@ void CopyRunner(std::filesystem::path output) { } std::filesystem::copy_file(TOOLS_RUNNER_LOCATION, output / "ion-run.exe", std::filesystem::copy_options::overwrite_existing); - std::filesystem::directory_iterator dll_iter(std::filesystem::absolute(std::filesystem::path(TOOLS_RUNNER_LOCATION)).parent_path()); - for (const auto& dll_entry : dll_iter) { + std::filesystem::directory_iterator dll_iter( + std::filesystem::absolute(std::filesystem::path(TOOLS_RUNNER_LOCATION)) + .parent_path()); + for (const auto &dll_entry : dll_iter) { if (dll_entry.path().extension() == ".dll") { std::filesystem::copy_file( - dll_entry.path(), output / dll_entry.path().filename(), - std::filesystem::copy_options::overwrite_existing); + dll_entry.path(), output / dll_entry.path().filename(), + std::filesystem::copy_options::overwrite_existing); printf("Copied %s to %s\n", dll_entry.path().filename().string().c_str(), - std::filesystem::path(TOOLS_RUNNER_LOCATION).parent_path().string().c_str()); + std::filesystem::path(TOOLS_RUNNER_LOCATION) + .parent_path() + .string() + .c_str()); } } } diff --git a/src/run/runner.cc b/src/run/runner.cc index 9090ecd..10ced3c 100644 --- a/src/run/runner.cc +++ b/src/run/runner.cc @@ -1,4 +1,5 @@ #include "ion/assets.h" +#include "ion/base_pipeline.h" #include "ion/defaults.h" #include "ion/development/package.h" #include "ion/game/game.h" @@ -6,35 +7,34 @@ #include "ion/render.h" #include "ion/script.h" #include "ion/shader.h" -#include "ion/texture.h" -#include "ion/base_pipeline.h" #include "ion/systems.h" +#include "ion/texture.h" #include #include #include #include std::map ReadWorldList(std::filesystem::path path) { - if (!std::filesystem::exists(path)) { - return {}; + if (!std::filesystem::exists(path)) { + return {}; } - auto doc = pugi::xml_document{}; - auto result = doc.load_file(path.string().c_str()); + auto doc = pugi::xml_document{}; + auto result = doc.load_file(path.string().c_str()); if (!result) { - throw std::runtime_error( - std::string("Failed to load world list: ") + result.description()); - } + throw std::runtime_error(std::string("Failed to load world list: ") + + result.description()); + } std::map world_list{}; - auto root = doc.child("worlds"); + auto root = doc.child("worlds"); for (auto world_node : root.children("world")) { int id = world_node.attribute("id").as_int(); std::string world_path_str = world_node.attribute("path").as_string(); auto world_path = std::filesystem::path(world_path_str); world_list[id] = world_path; - } - return world_list; + } + return world_list; } static void Init() { @@ -70,26 +70,26 @@ int main() { try { Init(); - RegisterAllSystems(); + RegisterAllSystems(); } catch (std::exception &e) { printf("Init Error: %s\n", e.what()); return -1; - } + } auto world = ion::res::LoadAsset(world_list.begin()->second.string()); - ion::systems::SetState(true); + ion::systems::SetState(true); - auto pipeline_settings = PipelineSettings{}; + auto pipeline_settings = PipelineSettings{}; auto pipeline = BasePipeline{}; auto defaults = Defaults{}; while (!glfwWindowShouldClose(ion::render::GetWindow())) { glfwPollEvents(); - ion::systems::UpdateSystems(world, ion::systems::UpdatePhase::PRE_UPDATE); - ion::systems::UpdateSystems(world, ion::systems::UpdatePhase::UPDATE); - pipeline.Render(world, pipeline_settings); + ion::systems::UpdateSystems(world, ion::systems::UpdatePhase::PRE_UPDATE); + ion::systems::UpdateSystems(world, ion::systems::UpdatePhase::UPDATE); + pipeline.Render(world, pipeline_settings); ion::render::Present(); - ion::systems::UpdateSystems(world, ion::systems::UpdatePhase::LATE_UPDATE); + ion::systems::UpdateSystems(world, ion::systems::UpdatePhase::LATE_UPDATE); } ion::physics::Quit(); diff --git a/vcpkg.json b/vcpkg.json index 8b96321..c42ec38 100644 --- a/vcpkg.json +++ b/vcpkg.json @@ -12,10 +12,10 @@ "docking-experimental", "freetype", "glfw-binding", - "opengl3-binding" + "vulkan-binding" ] }, - "opengl", + "vulkan", "python3", "pugixml", "stb", From c120297c3e472a188a45428fb456dabef10be543 Mon Sep 17 00:00:00 2001 From: SoHiEarth <126925211+SoHiEarth@users.noreply.github.com> Date: Fri, 12 Dec 2025 07:08:37 +0900 Subject: [PATCH 2/6] Include stdexcept for Vulkan surface creation Added stdexcept include for exception handling. --- src/base/render/surface.cc | 1 + 1 file changed, 1 insertion(+) diff --git a/src/base/render/surface.cc b/src/base/render/surface.cc index 33c8080..1a2b293 100644 --- a/src/base/render/surface.cc +++ b/src/base/render/surface.cc @@ -1,6 +1,7 @@ #define GLFW_INCLUDE_VULKAN #include "ion/render.h" #include "ion/render/api.h" +#include #include void ion::render::api::CreateSurface() { From fe0b831385d31431239c088dfbbbe0fff4afdacf Mon Sep 17 00:00:00 2001 From: SoHiEarth <126925211+SoHiEarth@users.noreply.github.com> Date: Fri, 12 Dec 2025 07:23:03 +0900 Subject: [PATCH 3/6] fix: Add missing include --- src/base/render/swapchain.cc | 1 + 1 file changed, 1 insertion(+) diff --git a/src/base/render/swapchain.cc b/src/base/render/swapchain.cc index 9f1fc78..80cf230 100644 --- a/src/base/render/swapchain.cc +++ b/src/base/render/swapchain.cc @@ -3,6 +3,7 @@ #include #include #include +#include #include VkSurfaceFormatKHR From 75c2032c3db8bec871c82df37025f0a06c5cb4f7 Mon Sep 17 00:00:00 2001 From: SoHiEarth <126925211+SoHiEarth@users.noreply.github.com> Date: Fri, 12 Dec 2025 19:04:17 +0900 Subject: [PATCH 4/6] feat: Finish vulkan backend --- CMakeLists.txt | 6 +- assets/test/fs.spv | Bin 0 -> 572 bytes assets/test/shader.frag | 9 ++ assets/test/shader.vert | 20 ++++ assets/test/vs.spv | Bin 0 -> 1504 bytes include/ion/render/api.h | 17 ++++ src/base/render.cc | 11 +- src/base/render/api.cc | 19 ++++ src/base/render/command.cc | 133 ++++++++++++++++++++++++ src/base/render/framebuffers.cc | 22 ++++ src/base/render/pipeline.cc | 174 ++++++++++++++++++++++++++++++++ src/base/render/render_pass.cc | 43 ++++++++ src/base/shader.cc | 42 +------- src/development/main.cc | 24 ++--- 14 files changed, 460 insertions(+), 60 deletions(-) create mode 100644 assets/test/fs.spv create mode 100644 assets/test/shader.frag create mode 100644 assets/test/shader.vert create mode 100644 assets/test/vs.spv create mode 100644 src/base/render/command.cc create mode 100644 src/base/render/framebuffers.cc create mode 100644 src/base/render/pipeline.cc create mode 100644 src/base/render/render_pass.cc diff --git a/CMakeLists.txt b/CMakeLists.txt index 3760732..270ca38 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -28,12 +28,16 @@ target_sources(ion-base PRIVATE src/base/physics.cc src/base/world.cc src/base/render/api.cc + src/base/render/command.cc src/base/render/debug.cc src/base/render/devices.cc + src/base/render/framebuffers.cc + src/base/render/image_views.cc src/base/render/instance.cc + src/base/render/pipeline.cc + src/base/render/render_pass.cc src/base/render/surface.cc src/base/render/swapchain.cc - src/base/render/image_views.cc ) set_target_properties(ion-base PROPERTIES PRIVATE ${PROJECT_VERSION}) set_target_properties(ion-base PROPERTIES PRIVATE ${PROJECT_VERSION_MAJOR}) diff --git a/assets/test/fs.spv b/assets/test/fs.spv new file mode 100644 index 0000000000000000000000000000000000000000..da37f7ede672fe162b5322c32f5938a7836409a7 GIT binary patch literal 572 zcmYk2PfNo<6vUrx)7IAhv!FMrcod2U6+zU4NG^dYet=MtD1q3NHWj@2+5A*q1n0LV z(uK*}H#=`OEEHA71Yeo8+BI6MPEqd}+{a8uKdhAl0+aGA( z6gLqLrRPob_)qk0tMXUiuge|}IP@J=^z`Vvs`?d60nUc?u0MFnDF)EG3?WD_tXd~G(}N+zk@O66FYiu^P-@u*JZ3F?aDVc^abjOR}^*Rec7t&^?*uDZdsTY;mcUNHNK*l`ZeJhd1~N% zVfN;G@ULpjeZowB?r%b zYcjq?9UA(*E6?8CvAYBAl()eh+><8{x30*ScQxcN?q)-tnSB{^fj4Dv-1U~cFPo6D z4|rR~qW-=-Ie6;9J2glCpr4#8b{zD+OS{Tg%D)Ps4SXvn{ir=I+g-U4rsnOSK5 zit*&%2=S)4TVn2pb9*f{y*-f8*E^}{>5&XQJR17r{{#NBH0-%sb8=&DujZKfR8`FJ yM240e_srd~53@d%ah{wReSy)tzMd;~eKG5SjNakVF!!Mho_fv({7 swapchain_image_views; extern std::vector swapchain_images; extern VkFormat swapchain_image_format; extern VkExtent2D swapchain_extent; +extern VkRenderPass render_pass; +extern VkPipelineLayout pipeline_layout; +extern VkPipeline graphics_pipeline; +extern std::vector swapchain_framebuffers; +extern VkCommandPool command_pool; +extern VkCommandBuffer command_buffer; +extern VkSemaphore image_available_semaphore, render_finished_semaphore; +extern VkFence in_flight_fence; } // namespace internal void CreateInstance(); @@ -58,6 +66,15 @@ void CreateSurface(); void CreateDevice(); void CreateSwapchain(); void CreateImageViews(); +void CreatePipeline(); +void CreateRenderPass(); +void CreateFramebuffers(); +void CreateSyncObjects(); +void CreateCommandPool(); +void CreateCommandBuffer(); + +void RecordCommandBuffer(VkCommandBuffer, uint32_t); +void Render(); void DestroyMessenger(); void Quit(); diff --git a/src/base/render.cc b/src/base/render.cc index 87c23a1..575c0de 100644 --- a/src/base/render.cc +++ b/src/base/render.cc @@ -63,6 +63,12 @@ int ion::render::Init() { api::CreateDevice(); api::CreateSwapchain(); api::CreateImageViews(); + api::CreateRenderPass(); + api::CreatePipeline(); + api::CreateFramebuffers(); + api::CreateCommandPool(); + api::CreateCommandBuffer(); + api::CreateSyncObjects(); glfwSetFramebufferSizeCallback(internal::window, SizeCallback); return 0; @@ -256,12 +262,9 @@ int ion::render::Render(std::shared_ptr color_fb, return 0; } int ion::render::Quit() { - for (auto &[framebuffer, name] : internal::framebuffers) { - } - internal::framebuffers.clear(); api::Quit(); glfwDestroyWindow(internal::window); glfwTerminate(); return 0; } -void ion::render::Present() {} +void ion::render::Present() { api::Render(); } diff --git a/src/base/render/api.cc b/src/base/render/api.cc index 5227462..fdec92f 100644 --- a/src/base/render/api.cc +++ b/src/base/render/api.cc @@ -12,9 +12,28 @@ std::vector swapchain_image_views; std::vector swapchain_images; VkFormat swapchain_image_format; VkExtent2D swapchain_extent; +VkRenderPass render_pass; +VkPipelineLayout pipeline_layout; +VkPipeline graphics_pipeline; +std::vector swapchain_framebuffers; +VkCommandPool command_pool; +VkCommandBuffer command_buffer; +VkSemaphore image_available_semaphore, render_finished_semaphore; +VkFence in_flight_fence; } // namespace ion::render::api::internal void ion::render::api::Quit() { + vkDestroySemaphore(internal::device, internal::image_available_semaphore, + nullptr); + vkDestroySemaphore(internal::device, internal::render_finished_semaphore, + nullptr); + vkDestroyFence(internal::device, internal::in_flight_fence, nullptr); + for (auto framebuffer : internal::swapchain_framebuffers) { + vkDestroyFramebuffer(internal::device, framebuffer, nullptr); + } + vkDestroyPipeline(internal::device, internal::graphics_pipeline, nullptr); + vkDestroyPipelineLayout(internal::device, internal::pipeline_layout, nullptr); + vkDestroyRenderPass(internal::device, internal::render_pass, nullptr); for (auto view : internal::swapchain_image_views) { vkDestroyImageView(internal::device, view, nullptr); } diff --git a/src/base/render/command.cc b/src/base/render/command.cc new file mode 100644 index 0000000..4c08fa2 --- /dev/null +++ b/src/base/render/command.cc @@ -0,0 +1,133 @@ +#include "ion/render/api.h" +#include +#include + +void ion::render::api::CreateCommandPool() { + auto indices = internal::FindQueueFamilies(internal::physical_device); + auto pool_info = VkCommandPoolCreateInfo{}; + pool_info.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO; + pool_info.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT; + pool_info.queueFamilyIndex = indices.graphics_family.value(); + if (vkCreateCommandPool(internal::device, &pool_info, nullptr, + &internal::command_pool) != VK_SUCCESS) { + throw std::runtime_error("Failed to create command pool"); + } +} + +void ion::render::api::CreateCommandBuffer() { + auto alloc_info = VkCommandBufferAllocateInfo{}; + alloc_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; + alloc_info.commandPool = internal::command_pool; + alloc_info.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; + alloc_info.commandBufferCount = 1; + if (vkAllocateCommandBuffers(internal::device, &alloc_info, + &internal::command_buffer) != VK_SUCCESS) { + throw std::runtime_error("Failed to create command buffers"); + } +} + +void ion::render::api::CreateSyncObjects() { + auto semaphore_info = VkSemaphoreCreateInfo{}; + semaphore_info.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO; + auto fence_info = VkFenceCreateInfo{}; + fence_info.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO; + fence_info.flags = VK_FENCE_CREATE_SIGNALED_BIT; + if (vkCreateSemaphore(internal::device, &semaphore_info, nullptr, + &internal::image_available_semaphore) != VK_SUCCESS || + vkCreateSemaphore(internal::device, &semaphore_info, nullptr, + &internal::render_finished_semaphore) != VK_SUCCESS || + vkCreateFence(internal::device, &fence_info, nullptr, + &internal::in_flight_fence) != VK_SUCCESS) { + throw std::runtime_error("Failed to create sync objects"); + } +} + +void ion::render::api::RecordCommandBuffer(VkCommandBuffer buffer, + uint32_t index) { + auto begin_info = VkCommandBufferBeginInfo{}; + begin_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; + begin_info.flags = 0; + begin_info.pInheritanceInfo = nullptr; + if (vkBeginCommandBuffer(buffer, &begin_info) != VK_SUCCESS) { + throw std::runtime_error("Failed to begin recording command buffer"); + } + + auto render_pass_info = VkRenderPassBeginInfo{}; + render_pass_info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO; + render_pass_info.renderPass = internal::render_pass; + render_pass_info.framebuffer = internal::swapchain_framebuffers.at(index); + render_pass_info.renderArea.offset = {0, 0}; + render_pass_info.renderArea.extent = internal::swapchain_extent; + auto clear_color = VkClearValue{0.0, 0.0, 0.0, 0.0}; + render_pass_info.clearValueCount = 1; + render_pass_info.pClearValues = &clear_color; + vkCmdBeginRenderPass(buffer, &render_pass_info, VK_SUBPASS_CONTENTS_INLINE); + + vkCmdBindPipeline(buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, + internal::graphics_pipeline); + auto viewport = VkViewport{}; + viewport.x = 0.0f; + viewport.y = 0.0f; + viewport.width = static_cast(internal::swapchain_extent.width); + viewport.height = static_cast(internal::swapchain_extent.height); + viewport.minDepth = 0.0f; + viewport.maxDepth = 1.0f; + vkCmdSetViewport(buffer, 0, 1, &viewport); + + auto scissor = VkRect2D{}; + scissor.offset = {0, 0}; + scissor.extent = internal::swapchain_extent; + vkCmdSetScissor(buffer, 0, 1, &scissor); + + vkCmdDraw(buffer, 3, 1, 0, 0); + + vkCmdEndRenderPass(internal::command_buffer); + if (vkEndCommandBuffer(internal::command_buffer) != VK_SUCCESS) { + throw std::runtime_error("Failed to end command buffer"); + } +} + +void ion::render::api::Render() { + vkWaitForFences(internal::device, 1, &internal::in_flight_fence, VK_TRUE, + UINT64_MAX); + vkResetFences(internal::device, 1, &internal::in_flight_fence); + + uint32_t image_index; + vkAcquireNextImageKHR(internal::device, internal::swapchain, UINT64_MAX, + internal::image_available_semaphore, VK_NULL_HANDLE, + &image_index); + vkResetCommandBuffer(internal::command_buffer, 0); + RecordCommandBuffer(internal::command_buffer, 0); + + auto submit_info = VkSubmitInfo{}; + submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; + + VkSemaphore wait_semaphores[] = {internal::image_available_semaphore}; + VkPipelineStageFlags wait_stages[] = { + VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT}; + submit_info.waitSemaphoreCount = 1; + submit_info.pWaitSemaphores = wait_semaphores; + submit_info.pWaitDstStageMask = wait_stages; + submit_info.commandBufferCount = 1; + submit_info.pCommandBuffers = &internal::command_buffer; + + VkSemaphore signal_semaphores[] = {internal::render_finished_semaphore}; + submit_info.signalSemaphoreCount = 1; + submit_info.pSignalSemaphores = signal_semaphores; + + if (vkQueueSubmit(internal::graphics_queue, 1, &submit_info, + internal::in_flight_fence) != VK_SUCCESS) { + throw std::runtime_error("Failed to submit draw command buffer"); + } + + auto present_info = VkPresentInfoKHR{}; + present_info.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR; + present_info.waitSemaphoreCount = 1; + present_info.pWaitSemaphores = signal_semaphores; + VkSwapchainKHR swapchains[] = {internal::swapchain}; + present_info.swapchainCount = 1; + present_info.pSwapchains = swapchains; + present_info.pImageIndices = &image_index; + present_info.pResults = nullptr; + vkQueuePresentKHR(internal::present_queue, &present_info); +} diff --git a/src/base/render/framebuffers.cc b/src/base/render/framebuffers.cc new file mode 100644 index 0000000..11bbf18 --- /dev/null +++ b/src/base/render/framebuffers.cc @@ -0,0 +1,22 @@ +#include "ion/render/api.h" +#include +void ion::render::api::CreateFramebuffers() { + internal::swapchain_framebuffers.resize( + internal::swapchain_image_views.size()); + for (size_t i = 0; i < internal::swapchain_image_views.size(); i++) { + VkImageView attachments[] = {internal::swapchain_image_views.at(i)}; + auto info = VkFramebufferCreateInfo{}; + info.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO; + info.renderPass = internal::render_pass; + info.attachmentCount = 1; + info.pAttachments = attachments; + info.width = internal::swapchain_extent.width; + info.height = internal::swapchain_extent.height; + info.layers = 1; + if (vkCreateFramebuffer(internal::device, &info, nullptr, + &internal::swapchain_framebuffers.at(i)) != + VK_SUCCESS) { + throw std::runtime_error("Failed to create framebuffer"); + } + } +} diff --git a/src/base/render/pipeline.cc b/src/base/render/pipeline.cc new file mode 100644 index 0000000..f131d8d --- /dev/null +++ b/src/base/render/pipeline.cc @@ -0,0 +1,174 @@ +#include "ion/render/api.h" +#include +#include +#include + +static std::vector ReadFile(std::string_view path) { + auto file = std::ifstream(path.data(), std::ios::ate | std::ios::binary); + if (!file.is_open()) { + throw std::runtime_error("Failed to open file"); + } + size_t file_size = (size_t)file.tellg(); + auto buffer = std::vector(file_size); + file.seekg(0); + file.read(buffer.data(), file_size); + file.close(); + return buffer; +} + +VkShaderModule CreateShaderModule(const std::vector &code) { + auto info = VkShaderModuleCreateInfo{}; + info.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO; + info.codeSize = code.size(); + info.pCode = reinterpret_cast(code.data()); + auto module = VkShaderModule{}; + if (vkCreateShaderModule(ion::render::api::internal::device, &info, nullptr, + &module) != VK_SUCCESS) { + throw std::runtime_error("Failed to create shader module"); + } + return module; +} + +void ion::render::api::CreatePipeline() { + auto vertex_module = CreateShaderModule(ReadFile("assets/test/vs.spv")); + auto fragment_module = CreateShaderModule(ReadFile("assets/test/fs.spv")); + + auto vertex_info = VkPipelineShaderStageCreateInfo{}; + vertex_info.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; + vertex_info.stage = VK_SHADER_STAGE_VERTEX_BIT; + vertex_info.module = vertex_module; + vertex_info.pName = "main"; + + auto fragment_info = VkPipelineShaderStageCreateInfo{}; + fragment_info.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; + fragment_info.stage = VK_SHADER_STAGE_FRAGMENT_BIT; + fragment_info.module = fragment_module; + fragment_info.pName = "main"; + + VkPipelineShaderStageCreateInfo stages[] = {vertex_info, fragment_info}; + + auto input_info = VkPipelineVertexInputStateCreateInfo{}; + input_info.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO; + input_info.vertexBindingDescriptionCount = 0; + input_info.pVertexBindingDescriptions = nullptr; + input_info.vertexAttributeDescriptionCount = 0; + input_info.pVertexAttributeDescriptions = nullptr; + + auto input_assembly = VkPipelineInputAssemblyStateCreateInfo{}; + input_assembly.sType = + VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO; + input_assembly.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST; + input_assembly.primitiveRestartEnable = VK_FALSE; + + auto viewport = VkViewport{}; + viewport.x = 0.0f; + viewport.y = 0.0f; + viewport.width = static_cast(internal::swapchain_extent.width); + viewport.height = static_cast(internal::swapchain_extent.height); + viewport.minDepth = 0.0f; + viewport.maxDepth = 1.0f; + + auto scissor = VkRect2D{}; + scissor.offset = {0, 0}; + scissor.extent = internal::swapchain_extent; + + auto dynamic_states = std::vector{VK_DYNAMIC_STATE_VIEWPORT, + VK_DYNAMIC_STATE_SCISSOR}; + auto dynamic_state = VkPipelineDynamicStateCreateInfo{}; + dynamic_state.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO; + dynamic_state.dynamicStateCount = + static_cast(dynamic_states.size()); + dynamic_state.pDynamicStates = dynamic_states.data(); + + auto viewport_state = VkPipelineViewportStateCreateInfo{}; + viewport_state.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO; + viewport_state.viewportCount = 1; + viewport_state.pViewports = &viewport; + viewport_state.scissorCount = 1; + viewport_state.pScissors = &scissor; + + auto rasterizer = VkPipelineRasterizationStateCreateInfo{}; + rasterizer.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO; + rasterizer.depthClampEnable = VK_FALSE; + rasterizer.rasterizerDiscardEnable = VK_FALSE; + rasterizer.polygonMode = VK_POLYGON_MODE_FILL; + rasterizer.lineWidth = 1.0f; + rasterizer.cullMode = VK_CULL_MODE_BACK_BIT; + rasterizer.frontFace = VK_FRONT_FACE_CLOCKWISE; + rasterizer.depthBiasEnable = VK_FALSE; + rasterizer.depthBiasConstantFactor = 0.0f; + rasterizer.depthBiasClamp = 0.0f; + rasterizer.depthBiasSlopeFactor = 0.0f; + + auto multisample = VkPipelineMultisampleStateCreateInfo{}; + multisample.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO; + multisample.sampleShadingEnable = VK_FALSE; + multisample.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT; + multisample.minSampleShading = 1.0f; + multisample.pSampleMask = nullptr; + multisample.alphaToCoverageEnable = VK_FALSE; + multisample.alphaToOneEnable = VK_FALSE; + + auto color_blend_attachment = VkPipelineColorBlendAttachmentState{}; + color_blend_attachment.colorWriteMask = + VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | + VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT; + color_blend_attachment.blendEnable = VK_TRUE; + color_blend_attachment.srcColorBlendFactor = VK_BLEND_FACTOR_SRC_ALPHA; + color_blend_attachment.dstColorBlendFactor = + VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA; + color_blend_attachment.colorBlendOp = VK_BLEND_OP_ADD; + color_blend_attachment.srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE; + color_blend_attachment.dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO; + color_blend_attachment.alphaBlendOp = VK_BLEND_OP_ADD; + + auto color_blending = VkPipelineColorBlendStateCreateInfo{}; + color_blending.sType = + VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO; + color_blending.logicOpEnable = VK_FALSE; + color_blending.logicOp = VK_LOGIC_OP_COPY; + color_blending.attachmentCount = 1; + color_blending.pAttachments = &color_blend_attachment; + color_blending.blendConstants[0] = 0.0f; + color_blending.blendConstants[1] = 0.0f; + color_blending.blendConstants[2] = 0.0f; + color_blending.blendConstants[3] = 0.0f; + + auto pipeline_layout_info = VkPipelineLayoutCreateInfo{}; + pipeline_layout_info.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO; + pipeline_layout_info.setLayoutCount = 0; + pipeline_layout_info.pSetLayouts = nullptr; + pipeline_layout_info.pushConstantRangeCount = 0; + pipeline_layout_info.pPushConstantRanges = nullptr; + if (vkCreatePipelineLayout(internal::device, &pipeline_layout_info, nullptr, + &internal::pipeline_layout) != VK_SUCCESS) { + throw std::runtime_error("Failed to create pipeline layout"); + } + + auto pipeline_info = VkGraphicsPipelineCreateInfo{}; + pipeline_info.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO; + pipeline_info.stageCount = 2; + pipeline_info.pStages = stages; + pipeline_info.pVertexInputState = &input_info; + pipeline_info.pInputAssemblyState = &input_assembly; + pipeline_info.pViewportState = &viewport_state; + pipeline_info.pRasterizationState = &rasterizer; + pipeline_info.pMultisampleState = &multisample; + pipeline_info.pDepthStencilState = nullptr; + pipeline_info.pColorBlendState = &color_blending; + pipeline_info.pDynamicState = &dynamic_state; + pipeline_info.layout = internal::pipeline_layout; + pipeline_info.renderPass = internal::render_pass; + pipeline_info.subpass = 0; + pipeline_info.basePipelineHandle = VK_NULL_HANDLE; + pipeline_info.basePipelineIndex = -1; + + if (vkCreateGraphicsPipelines(internal::device, VK_NULL_HANDLE, 1, + &pipeline_info, nullptr, + &internal::graphics_pipeline) != VK_SUCCESS) { + throw std::runtime_error("Failed to create graphics pipeline"); + } + + vkDestroyShaderModule(internal::device, vertex_module, nullptr); + vkDestroyShaderModule(internal::device, fragment_module, nullptr); +} diff --git a/src/base/render/render_pass.cc b/src/base/render/render_pass.cc new file mode 100644 index 0000000..7e47ed8 --- /dev/null +++ b/src/base/render/render_pass.cc @@ -0,0 +1,43 @@ +#include "ion/render/api.h" +#include +void ion::render::api::CreateRenderPass() { + auto color_attachment = VkAttachmentDescription{}; + color_attachment.format = internal::swapchain_image_format; + color_attachment.samples = VK_SAMPLE_COUNT_1_BIT; + color_attachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; + color_attachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE; + color_attachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; + color_attachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; + color_attachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; + color_attachment.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; + + auto attachment_reference = VkAttachmentReference{}; + attachment_reference.attachment = 0; + attachment_reference.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + + auto subpass = VkSubpassDescription{}; + subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; + subpass.colorAttachmentCount = 1; + subpass.pColorAttachments = &attachment_reference; + + VkSubpassDependency dependency{}; + dependency.srcSubpass = VK_SUBPASS_EXTERNAL; + dependency.dstSubpass = 0; + dependency.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; + dependency.srcAccessMask = 0; + dependency.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; + dependency.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; + + auto render_pass_info = VkRenderPassCreateInfo{}; + render_pass_info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO; + render_pass_info.attachmentCount = 1; + render_pass_info.pAttachments = &color_attachment; + render_pass_info.subpassCount = 1; + render_pass_info.pSubpasses = &subpass; + render_pass_info.dependencyCount = 1; + render_pass_info.pDependencies = &dependency; + if (vkCreateRenderPass(internal::device, &render_pass_info, nullptr, + &internal::render_pass) != VK_SUCCESS) { + throw std::runtime_error("Failed to create render pass"); + } +} diff --git a/src/base/shader.cc b/src/base/shader.cc index c54242d..c97a7e2 100644 --- a/src/base/shader.cc +++ b/src/base/shader.cc @@ -38,45 +38,5 @@ int Shader::SetUniform(std::string_view name, glm::mat4 value) { return 0; } -std::string _ShaderInternalReadFile(std::filesystem::path path) { - std::string data{}; - std::ifstream file{}; - file.exceptions(std::ifstream::failbit | std::ifstream::badbit); - try { - file.open(path); - std::stringstream stream; - stream << file.rdbuf(); - file.close(); - data = stream.str(); - } catch (std::ifstream::failure e) { - printf("File read fail: %s, %d\n", path.string().c_str(), FILE_READ_FAIL); - } - return data; -} - Shader::Shader(std::filesystem::path new_path, std::string_view new_id) - : path(new_path), id(new_id) { - unsigned int vertex, fragment; - int success; - std::array info_log = {}; - auto vertex_code = _ShaderInternalReadFile(path / "vs.glsl"), - fragment_code = _ShaderInternalReadFile(path / "fs.glsl"); - auto vertex_code_char = vertex_code.c_str(), - fragment_code_char = fragment_code.c_str(); - if (!success) { - printf("Error while compiling vertex shader %s\n", - (path / "vs.glsl").c_str()); - printf("%s\n", info_log.data()); - printf("%d\n", VERTEX_COMPILATION_FAIL); - } - if (!success) { - printf("Error while compiling fragment shader %s\n", - (path / "fs.glsl").c_str()); - printf("%s\n", info_log.data()); - printf("%d\n", FRAGMENT_COMPILATION_FAIL); - } - if (!success) { - printf("%s\n", info_log.data()); - printf("%d\n", SHADER_PROGRAM_LINK_FAIL); - } -} + : path(new_path), id(new_id) {} diff --git a/src/development/main.cc b/src/development/main.cc index d134e23..796a380 100644 --- a/src/development/main.cc +++ b/src/development/main.cc @@ -7,21 +7,17 @@ #include "ion/physics.h" #include "ion/render.h" #include "ion/script.h" -#include "ion/shader.h" #include "ion/systems.h" -#include "ion/texture.h" #include "ion/world.h" -#include #include #include #include #include -#include static void Init() { ion::render::Init(); - ion::gui::Init(ion::render::GetWindow()); - ION_GUI_PREP_CONTEXT(); + // ion::gui::Init(ion::render::GetWindow()); + // ION_GUI_PREP_CONTEXT(); ion::physics::Init(); ion::script::Init(); } @@ -51,10 +47,10 @@ int main() { return -1; } - std::shared_ptr world = nullptr; + std::shared_ptr world = std::make_shared("a"); try { - world = ion::gui::StartWindow(); + // world = ion::gui::StartWindow(); } catch (std::exception &e) { printf("Start Window Error: %s\n", e.what()); return -1; @@ -69,14 +65,14 @@ int main() { while (!glfwWindowShouldClose(ion::render::GetWindow())) { glfwPollEvents(); ion::systems::UpdateSystems(world, ion::systems::UpdatePhase::PRE_UPDATE); - ion::gui::NewFrame(); - ion::dev::ui::RenderInspector(world, defaults, pipeline, - pipeline_settings); + // ion::gui::NewFrame(); + // ion::dev::ui::RenderInspector(world, defaults, pipeline, + // pipeline_settings); ion::systems::UpdateSystems(world, ion::systems::UpdatePhase::UPDATE); pipeline.Render(world, pipeline_settings); ion::render::UnbindFramebuffer(); ion::render::Clear(); - ion::gui::Render(); + // ion::gui::Render(); ion::render::Present(); ion::systems::UpdateSystems(world, ion::systems::UpdatePhase::LATE_UPDATE); @@ -88,6 +84,6 @@ int main() { ion::physics::Quit(); ion::script::Quit(); ion::render::Quit(); - ion::gui::Quit(); + // ion::gui::Quit(); return 0; -} \ No newline at end of file +} From c561409f6098009834f273a618d4b99fc0e02eb9 Mon Sep 17 00:00:00 2001 From: SoHiEarth <126925211+SoHiEarth@users.noreply.github.com> Date: Fri, 26 Dec 2025 21:23:34 +0900 Subject: [PATCH 5/6] fix: Windows build --- CMakeLists.txt | 11 ++++---- include/ion/development/id.h | 36 ++----------------------- include/ion/render/api.h | 10 ++++--- src/base/render/api.cc | 39 +++++++++++++++------------ src/base/render/command.cc | 52 ++++++++++++++++++++++-------------- src/base/render/devices.cc | 2 +- src/base/render/instance.cc | 5 ++-- src/development/id.cc | 36 +++++++++++++++++++++++++ 8 files changed, 108 insertions(+), 83 deletions(-) create mode 100644 src/development/id.cc diff --git a/CMakeLists.txt b/CMakeLists.txt index 270ca38..f16e88d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -12,9 +12,9 @@ find_package(tinyfiledialogs CONFIG REQUIRED) find_package(Python3 COMPONENTS Development REQUIRED) find_package(pugixml CONFIG REQUIRED) -add_library(ion-base SHARED) +add_library(ion-base STATIC) target_compile_features(ion-base PRIVATE cxx_std_20) -target_compile_definitions(ion-base PRIVATE ION_EXPORTS) +#target_compile_definitions(ion-base PRIVATE ION_EXPORTS) target_compile_definitions(ion-base PRIVATE ION_BUILD_VERSION="${PROJECT_VERSION}") target_sources(ion-base PRIVATE src/base/assets.cc @@ -27,6 +27,7 @@ target_sources(ion-base PRIVATE src/base/texture.cc src/base/physics.cc src/base/world.cc + src/development/id.cc src/base/render/api.cc src/base/render/command.cc src/base/render/debug.cc @@ -54,9 +55,9 @@ target_link_libraries(ion-base PUBLIC pugixml::pugixml ) -add_library(ion-game SHARED) +add_library(ion-game STATIC) target_compile_features(ion-game PRIVATE cxx_std_20) -target_compile_definitions(ion-game PRIVATE ION_EXPORTS) +#target_compile_definitions(ion-game PRIVATE ION_EXPORTS) target_compile_definitions(ion-game PRIVATE ION_BUILD_VERSION="${PROJECT_VERSION}") target_sources(ion-game PRIVATE src/game/game.cc @@ -80,7 +81,7 @@ target_sources(ion-development PRIVATE target_compile_definitions(ion-development PRIVATE ION_BUILD_VERSION="${PROJECT_VERSION}" ION_DEVELOPMENT - ION_IMPORTS + #ION_IMPORTS ) set_target_properties(ion-development PROPERTIES VERSION ${PROJECT_VERSION}) target_include_directories(ion-development PRIVATE ${CMAKE_SOURCE_DIR}/include) diff --git a/include/ion/development/id.h b/include/ion/development/id.h index 443e341..261c76a 100644 --- a/include/ion/development/id.h +++ b/include/ion/development/id.h @@ -9,39 +9,7 @@ namespace ion { static std::uniform_int_distribution<> distribution(0, 15); static std::uniform_int_distribution<> distribution_2(8, 11); - std::string GenerateUUID() { - std::stringstream ss; - int i; - ss << std::hex; - for (i = 0; i < 8; i++) { - ss << distribution(gen); - } - ss << "-"; - for (i = 0; i < 4; i++) { - ss << distribution(gen); - } - ss << "-4"; - for (i = 0; i < 3; i++) { - ss << distribution(gen); - } - ss << "-"; - ss << distribution_2(gen); - for (i = 0; i < 3; i++) { - ss << distribution(gen); - } - ss << "-"; - for (i = 0; i < 12; i++) { - ss << distribution(gen); - }; - return ss.str(); - } - - std::string GenerateHashFromString(const std::string& input) { - std::hash hasher; - size_t hash = hasher(input); - std::stringstream ss; - ss << std::hex << hash; - return ss.str(); - } + std::string GenerateUUID(); + std::string GenerateHashFromString(const std::string& input); } // namespace uuid } // namespace ion \ No newline at end of file diff --git a/include/ion/render/api.h b/include/ion/render/api.h index cdd9dc6..57d9acc 100644 --- a/include/ion/render/api.h +++ b/include/ion/render/api.h @@ -3,7 +3,6 @@ #include #include #include -#include namespace ion::render::api { @@ -18,11 +17,14 @@ struct SwapchainSupportInfo { }; namespace internal { +const int max_frames_in_flight = 2; + #ifdef NDEBUG static const bool enable_validation_layers = false; #else static const bool enable_validation_layers = true; #endif +static bool using_fallback_layer = false; static const std::vector validation_layers = {"VK_LAYER_KHRONOS_validation"}, @@ -55,9 +57,9 @@ extern VkPipelineLayout pipeline_layout; extern VkPipeline graphics_pipeline; extern std::vector swapchain_framebuffers; extern VkCommandPool command_pool; -extern VkCommandBuffer command_buffer; -extern VkSemaphore image_available_semaphore, render_finished_semaphore; -extern VkFence in_flight_fence; +extern std::vector command_buffer; +extern std::vector image_available_semaphore, render_finished_semaphore; +extern std::vector in_flight_fence; } // namespace internal void CreateInstance(); diff --git a/src/base/render/api.cc b/src/base/render/api.cc index fdec92f..e4a5a6c 100644 --- a/src/base/render/api.cc +++ b/src/base/render/api.cc @@ -8,26 +8,31 @@ VkDevice device{}; VkQueue graphics_queue{}, present_queue{}; VkSurfaceKHR surface{}; VkSwapchainKHR swapchain{}; -std::vector swapchain_image_views; -std::vector swapchain_images; -VkFormat swapchain_image_format; -VkExtent2D swapchain_extent; -VkRenderPass render_pass; -VkPipelineLayout pipeline_layout; -VkPipeline graphics_pipeline; -std::vector swapchain_framebuffers; -VkCommandPool command_pool; -VkCommandBuffer command_buffer; -VkSemaphore image_available_semaphore, render_finished_semaphore; -VkFence in_flight_fence; +std::vector swapchain_image_views{}; +std::vector swapchain_images{}; +VkFormat swapchain_image_format{}; +VkExtent2D swapchain_extent{}; +VkRenderPass render_pass{}; +VkPipelineLayout pipeline_layout{}; +VkPipeline graphics_pipeline{}; +std::vector swapchain_framebuffers{}; +VkCommandPool command_pool{}; +std::vector command_buffer{}; +std::vector image_available_semaphore{}, render_finished_semaphore{}; +std::vector in_flight_fence{}; } // namespace ion::render::api::internal void ion::render::api::Quit() { - vkDestroySemaphore(internal::device, internal::image_available_semaphore, - nullptr); - vkDestroySemaphore(internal::device, internal::render_finished_semaphore, - nullptr); - vkDestroyFence(internal::device, internal::in_flight_fence, nullptr); + vkDeviceWaitIdle(internal::device); + for (auto semaphore : internal::render_finished_semaphore) { + vkDestroySemaphore(internal::device, semaphore, nullptr); + } + for (auto semaphore : internal::image_available_semaphore) { + vkDestroySemaphore(internal::device, semaphore, nullptr); + } + for (auto fence : internal::in_flight_fence) { + vkDestroyFence(internal::device, fence, nullptr); + } for (auto framebuffer : internal::swapchain_framebuffers) { vkDestroyFramebuffer(internal::device, framebuffer, nullptr); } diff --git a/src/base/render/command.cc b/src/base/render/command.cc index 4c08fa2..a4c73b1 100644 --- a/src/base/render/command.cc +++ b/src/base/render/command.cc @@ -2,6 +2,8 @@ #include #include +uint32_t current_rendering_frame = 0; + void ion::render::api::CreateCommandPool() { auto indices = internal::FindQueueFamilies(internal::physical_device); auto pool_info = VkCommandPoolCreateInfo{}; @@ -15,30 +17,39 @@ void ion::render::api::CreateCommandPool() { } void ion::render::api::CreateCommandBuffer() { + internal::command_buffer.resize(internal::max_frames_in_flight); auto alloc_info = VkCommandBufferAllocateInfo{}; alloc_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; alloc_info.commandPool = internal::command_pool; alloc_info.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; - alloc_info.commandBufferCount = 1; + alloc_info.commandBufferCount = (uint32_t)internal::command_buffer.size(); if (vkAllocateCommandBuffers(internal::device, &alloc_info, - &internal::command_buffer) != VK_SUCCESS) { + internal::command_buffer.data()) != VK_SUCCESS) { throw std::runtime_error("Failed to create command buffers"); } } void ion::render::api::CreateSyncObjects() { + internal::image_available_semaphore.resize(internal::max_frames_in_flight); + internal::render_finished_semaphore.resize(internal::max_frames_in_flight); + internal::in_flight_fence.resize(internal::max_frames_in_flight); + auto semaphore_info = VkSemaphoreCreateInfo{}; semaphore_info.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO; auto fence_info = VkFenceCreateInfo{}; fence_info.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO; fence_info.flags = VK_FENCE_CREATE_SIGNALED_BIT; - if (vkCreateSemaphore(internal::device, &semaphore_info, nullptr, - &internal::image_available_semaphore) != VK_SUCCESS || - vkCreateSemaphore(internal::device, &semaphore_info, nullptr, - &internal::render_finished_semaphore) != VK_SUCCESS || - vkCreateFence(internal::device, &fence_info, nullptr, - &internal::in_flight_fence) != VK_SUCCESS) { - throw std::runtime_error("Failed to create sync objects"); + for (size_t i = 0; i < internal::max_frames_in_flight; i++) { + if (vkCreateSemaphore(internal::device, &semaphore_info, nullptr, + &internal::image_available_semaphore[i]) != + VK_SUCCESS || + vkCreateSemaphore(internal::device, &semaphore_info, nullptr, + &internal::render_finished_semaphore[i]) != + VK_SUCCESS || + vkCreateFence(internal::device, &fence_info, nullptr, + &internal::in_flight_fence[i]) != VK_SUCCESS) { + throw std::runtime_error("Failed to create sync objects"); + } } } @@ -81,42 +92,42 @@ void ion::render::api::RecordCommandBuffer(VkCommandBuffer buffer, vkCmdDraw(buffer, 3, 1, 0, 0); - vkCmdEndRenderPass(internal::command_buffer); - if (vkEndCommandBuffer(internal::command_buffer) != VK_SUCCESS) { + vkCmdEndRenderPass(buffer); + if (vkEndCommandBuffer(buffer) != VK_SUCCESS) { throw std::runtime_error("Failed to end command buffer"); } } void ion::render::api::Render() { - vkWaitForFences(internal::device, 1, &internal::in_flight_fence, VK_TRUE, + vkWaitForFences(internal::device, 1, &internal::in_flight_fence[current_rendering_frame], VK_TRUE, UINT64_MAX); - vkResetFences(internal::device, 1, &internal::in_flight_fence); + vkResetFences(internal::device, 1, &internal::in_flight_fence[current_rendering_frame]); uint32_t image_index; vkAcquireNextImageKHR(internal::device, internal::swapchain, UINT64_MAX, - internal::image_available_semaphore, VK_NULL_HANDLE, + internal::image_available_semaphore[current_rendering_frame], VK_NULL_HANDLE, &image_index); - vkResetCommandBuffer(internal::command_buffer, 0); - RecordCommandBuffer(internal::command_buffer, 0); + vkResetCommandBuffer(internal::command_buffer[current_rendering_frame], 0); + RecordCommandBuffer(internal::command_buffer[current_rendering_frame], 0); auto submit_info = VkSubmitInfo{}; submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; - VkSemaphore wait_semaphores[] = {internal::image_available_semaphore}; + VkSemaphore wait_semaphores[] = {internal::image_available_semaphore[current_rendering_frame] }; VkPipelineStageFlags wait_stages[] = { VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT}; submit_info.waitSemaphoreCount = 1; submit_info.pWaitSemaphores = wait_semaphores; submit_info.pWaitDstStageMask = wait_stages; submit_info.commandBufferCount = 1; - submit_info.pCommandBuffers = &internal::command_buffer; + submit_info.pCommandBuffers = &internal::command_buffer[current_rendering_frame]; - VkSemaphore signal_semaphores[] = {internal::render_finished_semaphore}; + VkSemaphore signal_semaphores[] = {internal::render_finished_semaphore[current_rendering_frame] }; submit_info.signalSemaphoreCount = 1; submit_info.pSignalSemaphores = signal_semaphores; if (vkQueueSubmit(internal::graphics_queue, 1, &submit_info, - internal::in_flight_fence) != VK_SUCCESS) { + internal::in_flight_fence[current_rendering_frame]) != VK_SUCCESS) { throw std::runtime_error("Failed to submit draw command buffer"); } @@ -130,4 +141,5 @@ void ion::render::api::Render() { present_info.pImageIndices = &image_index; present_info.pResults = nullptr; vkQueuePresentKHR(internal::present_queue, &present_info); + current_rendering_frame = (current_rendering_frame + 1) % internal::max_frames_in_flight; } diff --git a/src/base/render/devices.cc b/src/base/render/devices.cc index a38d86f..509f155 100644 --- a/src/base/render/devices.cc +++ b/src/base/render/devices.cc @@ -153,7 +153,7 @@ void ion::render::api::CreateDevice() { static_cast(internal::device_extensions.size()); info.ppEnabledExtensionNames = internal::device_extensions.data(); - if (internal::enable_validation_layers) { + if (internal::enable_validation_layers && !internal::using_fallback_layer) { info.enabledLayerCount = static_cast(internal::validation_layers.size()); info.ppEnabledLayerNames = internal::validation_layers.data(); diff --git a/src/base/render/instance.cc b/src/base/render/instance.cc index 8bbf0df..091721a 100644 --- a/src/base/render/instance.cc +++ b/src/base/render/instance.cc @@ -43,7 +43,8 @@ std::vector GetRequiredExtensions() { void ion::render::api::CreateInstance() { if (internal::enable_validation_layers && !CheckValidationLayerSupport()) { - throw std::runtime_error("Requested validation layers were not available."); + printf("Requested validation layers were not available.\n"); + internal::using_fallback_layer = true; } auto app_info = VkApplicationInfo{}; app_info.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO; @@ -61,7 +62,7 @@ void ion::render::api::CreateInstance() { create_info.enabledExtensionCount = static_cast(extensions.size()); create_info.ppEnabledExtensionNames = extensions.data(); - if (internal::enable_validation_layers) { + if (internal::enable_validation_layers && !internal::using_fallback_layer) { create_info.enabledLayerCount = static_cast( ion::render::api::internal::validation_layers.size()); create_info.ppEnabledLayerNames = diff --git a/src/development/id.cc b/src/development/id.cc new file mode 100644 index 0000000..ad66fe9 --- /dev/null +++ b/src/development/id.cc @@ -0,0 +1,36 @@ +#include "ion/development/id.h" + +std::string ion::id::GenerateUUID() { + std::stringstream ss; + int i; + ss << std::hex; + for (i = 0; i < 8; i++) { + ss << distribution(gen); + } + ss << "-"; + for (i = 0; i < 4; i++) { + ss << distribution(gen); + } + ss << "-4"; + for (i = 0; i < 3; i++) { + ss << distribution(gen); + } + ss << "-"; + ss << distribution_2(gen); + for (i = 0; i < 3; i++) { + ss << distribution(gen); + } + ss << "-"; + for (i = 0; i < 12; i++) { + ss << distribution(gen); + }; + return ss.str(); +} + +std::string ion::id::GenerateHashFromString(const std::string& input) { + std::hash hasher; + size_t hash = hasher(input); + std::stringstream ss; + ss << std::hex << hash; + return ss.str(); +} \ No newline at end of file From d6a22f01b6f07881ffc512c502259beca10b8423 Mon Sep 17 00:00:00 2001 From: SoHiEarth <126925211+SoHiEarth@users.noreply.github.com> Date: Fri, 26 Dec 2025 22:11:06 +0900 Subject: [PATCH 6/6] feat: Add ImGui support --- include/ion/render/api.h | 2 ++ src/base/render/api.cc | 2 ++ src/base/render/command.cc | 5 +++- src/development/gui.cc | 57 +++++++++++++++++++++++++++++++------- src/development/main.cc | 14 +++++----- 5 files changed, 62 insertions(+), 18 deletions(-) diff --git a/include/ion/render/api.h b/include/ion/render/api.h index 57d9acc..85c96c6 100644 --- a/include/ion/render/api.h +++ b/include/ion/render/api.h @@ -60,6 +60,8 @@ extern VkCommandPool command_pool; extern std::vector command_buffer; extern std::vector image_available_semaphore, render_finished_semaphore; extern std::vector in_flight_fence; + +extern VkDescriptorPool descriptor_pool; } // namespace internal void CreateInstance(); diff --git a/src/base/render/api.cc b/src/base/render/api.cc index e4a5a6c..79dac8e 100644 --- a/src/base/render/api.cc +++ b/src/base/render/api.cc @@ -20,6 +20,8 @@ VkCommandPool command_pool{}; std::vector command_buffer{}; std::vector image_available_semaphore{}, render_finished_semaphore{}; std::vector in_flight_fence{}; + +VkDescriptorPool descriptor_pool{}; } // namespace ion::render::api::internal void ion::render::api::Quit() { diff --git a/src/base/render/command.cc b/src/base/render/command.cc index a4c73b1..6b33302 100644 --- a/src/base/render/command.cc +++ b/src/base/render/command.cc @@ -1,6 +1,7 @@ #include "ion/render/api.h" #include #include +#include uint32_t current_rendering_frame = 0; @@ -92,6 +93,8 @@ void ion::render::api::RecordCommandBuffer(VkCommandBuffer buffer, vkCmdDraw(buffer, 3, 1, 0, 0); + ImGui_ImplVulkan_RenderDrawData(ImGui::GetDrawData(), buffer); + vkCmdEndRenderPass(buffer); if (vkEndCommandBuffer(buffer) != VK_SUCCESS) { throw std::runtime_error("Failed to end command buffer"); @@ -108,7 +111,7 @@ void ion::render::api::Render() { internal::image_available_semaphore[current_rendering_frame], VK_NULL_HANDLE, &image_index); vkResetCommandBuffer(internal::command_buffer[current_rendering_frame], 0); - RecordCommandBuffer(internal::command_buffer[current_rendering_frame], 0); + RecordCommandBuffer(internal::command_buffer[current_rendering_frame], image_index); auto submit_info = VkSubmitInfo{}; submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; diff --git a/src/development/gui.cc b/src/development/gui.cc index 7d3b9f9..cbea4ab 100644 --- a/src/development/gui.cc +++ b/src/development/gui.cc @@ -3,26 +3,26 @@ #include #include #include +#include -static ImGuiContext *g_SharedImGuiContext = nullptr; -void *ion_gui_alloc(size_t size, void *user_data) { return malloc(size); } - -void ion_gui_free(void *ptr, void *user_data) { free(ptr); } +static ImGuiContext* g_SharedImGuiContext = nullptr; +static void* ion_gui_alloc(size_t size, void* user_data) { return malloc(size); } +static void ion_gui_free(void* ptr, void* user_data) { free(ptr); } constexpr float GUI_ROUNDING_MORE = 8.0F; constexpr float GUI_ROUNDING_LESS = 6.0F; -void ion::gui::Init(GLFWwindow *window) { +void ion::gui::Init(GLFWwindow* window) { IMGUI_CHECKVERSION(); - void *user_data = nullptr; + void* user_data = nullptr; ImGui::SetAllocatorFunctions(ion_gui_alloc, ion_gui_free, user_data); g_SharedImGuiContext = ImGui::CreateContext(); ImGui::SetCurrentContext(g_SharedImGuiContext); - ImGuiIO &imgui_io = ImGui::GetIO(); + ImGuiIO& imgui_io = ImGui::GetIO(); imgui_io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; imgui_io.ConfigFlags |= ImGuiConfigFlags_DockingEnable; imgui_io.ConfigFlags |= ImGuiConfigFlags_IsSRGB; - ImGuiStyle &style = ImGui::GetStyle(); + ImGuiStyle& style = ImGui::GetStyle(); style.WindowRounding = GUI_ROUNDING_MORE; style.ChildRounding = GUI_ROUNDING_MORE; style.FrameRounding = GUI_ROUNDING_LESS; @@ -30,23 +30,60 @@ void ion::gui::Init(GLFWwindow *window) { style.ScrollbarRounding = GUI_ROUNDING_LESS; style.GrabRounding = GUI_ROUNDING_LESS; style.TabRounding = GUI_ROUNDING_LESS; + + std::vector pool_sizes = { + { VK_DESCRIPTOR_TYPE_SAMPLER, 1000 }, + { VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1000 }, + { VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, 1000 }, + { VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, 1000 }, + { VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER, 1000 }, + { VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER, 1000 }, + { VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1000 }, + { VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1000 }, + { VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, 1000 }, + { VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC, 1000 }, + { VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT, 1000 } + }; + VkDescriptorPoolCreateInfo pool_info = {}; + pool_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO; + pool_info.flags = VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT; + pool_info.maxSets = 1000; + pool_info.poolSizeCount = static_cast(pool_sizes.size()); + pool_info.pPoolSizes = pool_sizes.data(); + if (vkCreateDescriptorPool(ion::render::api::internal::device, &pool_info, nullptr, &ion::render::api::internal::descriptor_pool) != VK_SUCCESS) { + throw std::runtime_error("Failed to create ImGui descriptor pool!"); + } + ImGui_ImplGlfw_InitForVulkan(window, true); auto info = ImGui_ImplVulkan_InitInfo{}; info.Instance = ion::render::api::internal::instance; + info.PhysicalDevice = ion::render::api::internal::physical_device; + info.Device = ion::render::api::internal::device; + auto queue_family = ion::render::api::internal::FindQueueFamilies(ion::render::api::internal::physical_device); + info.QueueFamily = queue_family.graphics_family.value(); + info.Queue = ion::render::api::internal::graphics_queue; + info.DescriptorPool = ion::render::api::internal::descriptor_pool; + info.MinImageCount = 2; + info.ImageCount = ion::render::api::internal::swapchain_images.size(); + info.Allocator = nullptr; + info.RenderPass = ion::render::api::internal::render_pass; + info.PipelineCache = VK_NULL_HANDLE; + info.MSAASamples = VK_SAMPLE_COUNT_1_BIT; + info.CheckVkResultFn = nullptr; ImGui_ImplVulkan_Init(&info); + ImGui_ImplVulkan_CreateFontsTexture(); } void ion::gui::NewFrame() { ImGui::SetCurrentContext(g_SharedImGuiContext); - ImGui_ImplGlfw_NewFrame(); ImGui_ImplVulkan_NewFrame(); + ImGui_ImplGlfw_NewFrame(); ImGui::NewFrame(); } void ion::gui::Render() { ImGui::SetCurrentContext(g_SharedImGuiContext); ImGui::Render(); - ImGui_ImplVulkan_RenderDrawData(ImGui::GetDrawData(), nullptr); } void ion::gui::Quit() { diff --git a/src/development/main.cc b/src/development/main.cc index 796a380..393b07c 100644 --- a/src/development/main.cc +++ b/src/development/main.cc @@ -16,8 +16,8 @@ static void Init() { ion::render::Init(); - // ion::gui::Init(ion::render::GetWindow()); - // ION_GUI_PREP_CONTEXT(); + ion::gui::Init(ion::render::GetWindow()); + ION_GUI_PREP_CONTEXT(); ion::physics::Init(); ion::script::Init(); } @@ -65,14 +65,14 @@ int main() { while (!glfwWindowShouldClose(ion::render::GetWindow())) { glfwPollEvents(); ion::systems::UpdateSystems(world, ion::systems::UpdatePhase::PRE_UPDATE); - // ion::gui::NewFrame(); - // ion::dev::ui::RenderInspector(world, defaults, pipeline, - // pipeline_settings); + ion::gui::NewFrame(); + ion::dev::ui::RenderInspector(world, defaults, pipeline, + pipeline_settings); ion::systems::UpdateSystems(world, ion::systems::UpdatePhase::UPDATE); pipeline.Render(world, pipeline_settings); ion::render::UnbindFramebuffer(); ion::render::Clear(); - // ion::gui::Render(); + ion::gui::Render(); ion::render::Present(); ion::systems::UpdateSystems(world, ion::systems::UpdatePhase::LATE_UPDATE); @@ -83,7 +83,7 @@ int main() { ion::physics::Quit(); ion::script::Quit(); + ion::gui::Quit(); ion::render::Quit(); - // ion::gui::Quit(); return 0; }