From 45bd309e8d58c19c81545a07e0d1faac68a5b771 Mon Sep 17 00:00:00 2001 From: Nanako <469449812@qq.com> Date: Tue, 20 Feb 2024 08:44:56 +0800 Subject: [PATCH] =?UTF-8?q?vulkan=E5=9B=BE=E7=89=87=E5=8A=A0=E8=BD=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- core/application/application.cpp | 22 +- core/application/application.h | 13 +- core/extern.h | 1 - core/rhi/buffer_vk.cpp | 61 ++++ core/rhi/buffer_vk.h | 52 ++++ core/rhi/compute_pipeline.cpp | 98 ++++++ core/rhi/compute_pipeline.h | 15 + core/rhi/pipeline.cpp | 53 ++++ core/rhi/{vulkan => }/pipeline.h | 30 +- core/rhi/render_target.cpp | 95 ++++++ core/rhi/render_target.h | 28 +- core/rhi/renderer.cpp | 420 +++++++++++++++++++++++++- core/rhi/renderer.h | 74 +++-- core/rhi/rhi_defintion.h | 34 --- core/rhi/shader.h | 58 ---- core/rhi/texture.cpp | 157 ++++++++++ core/rhi/texture.h | 30 +- core/rhi/{vulkan => }/utils/utils.cpp | 0 core/rhi/{vulkan => }/utils/utils.hpp | 0 core/rhi/vulkan/compute_pipeline.cpp | 48 --- core/rhi/vulkan/compute_pipeline.h | 14 - core/rhi/vulkan/pipeline.cpp | 51 ---- core/rhi/vulkan/render_target_vk.cpp | 1 - core/rhi/vulkan/render_target_vk.h | 8 - core/rhi/vulkan/renderer_vk.cpp | 404 ------------------------- core/rhi/vulkan/renderer_vk.h | 84 ------ core/rhi/vulkan/texture_vk.cpp | 51 ---- core/rhi/vulkan/texture_vk.h | 17 -- 28 files changed, 1076 insertions(+), 843 deletions(-) create mode 100644 core/rhi/buffer_vk.cpp create mode 100644 core/rhi/buffer_vk.h create mode 100644 core/rhi/compute_pipeline.cpp create mode 100644 core/rhi/compute_pipeline.h create mode 100644 core/rhi/pipeline.cpp rename core/rhi/{vulkan => }/pipeline.h (64%) create mode 100644 core/rhi/render_target.cpp delete mode 100644 core/rhi/rhi_defintion.h delete mode 100644 core/rhi/shader.h create mode 100644 core/rhi/texture.cpp rename core/rhi/{vulkan => }/utils/utils.cpp (100%) rename core/rhi/{vulkan => }/utils/utils.hpp (100%) delete mode 100644 core/rhi/vulkan/compute_pipeline.cpp delete mode 100644 core/rhi/vulkan/compute_pipeline.h delete mode 100644 core/rhi/vulkan/pipeline.cpp delete mode 100644 core/rhi/vulkan/render_target_vk.cpp delete mode 100644 core/rhi/vulkan/render_target_vk.h delete mode 100644 core/rhi/vulkan/renderer_vk.cpp delete mode 100644 core/rhi/vulkan/renderer_vk.h delete mode 100644 core/rhi/vulkan/texture_vk.cpp delete mode 100644 core/rhi/vulkan/texture_vk.h diff --git a/core/application/application.cpp b/core/application/application.cpp index c0ad385..4617e69 100644 --- a/core/application/application.cpp +++ b/core/application/application.cpp @@ -7,7 +7,7 @@ #include "imgui_internal.h" #include "filesystem/stb_image.h" #include "rhi/texture.h" -#include "rhi/vulkan/renderer_vk.h" +#include "rhi/renderer.h" #include "spdlog/async.h" #include "spdlog/spdlog.h" #include "spdlog/sinks/basic_file_sink.h" @@ -31,7 +31,7 @@ void application::init(const window_params& in_window_params, int argc, char** a init_glfw(); init_imgui(); - renderer_ = new renderer_vk(); + renderer_ = new renderer(); renderer_->pre_init(); @@ -44,7 +44,6 @@ void application::init(const window_params& in_window_params, int argc, char** a } renderer_->init(window_); - renderer_->resize(in_window_params.width, in_window_params.height); g_is_running = true; } @@ -67,7 +66,6 @@ int application::run() { void application::shutdown() { renderer_->shutdown(); destroy_imgui(); - renderer_->post_shutdown(); delete renderer_; renderer_ = nullptr; @@ -77,31 +75,31 @@ void application::shutdown() { g_is_running = false; } -std::shared_ptr application::load_texture(const std::string& path) const { +std::shared_ptr application::load_texture(const std::string& path, vk::Format format) const { int width = 0; int height = 0; - unsigned char* image_data = stbi_load(path.c_str(), &width, &height, nullptr, 4); + uint8_t* image_data = stbi_load(path.c_str(), &width, &height, nullptr, 4); if (!image_data) { spdlog::error("Failed to load texture: {}", path.c_str()); return nullptr; } - const auto texture = renderer_->create_texture(image_data, width, height); + const auto texture = renderer::create_texture(image_data, width, height, format); stbi_image_free(image_data); return texture; } std::shared_ptr -application::create_texture(const unsigned char* data, const int width, const int height) const { - return renderer_->create_texture(data, width, height); +application::create_texture(const unsigned char* data, const int width, const int height, vk::Format format) const { + return renderer::create_texture(data, width, height, format); } std::shared_ptr application::create_render_target(const int width, const int height, - texture_format format) const { - return renderer_->create_render_target(width, height, format); + vk::Format format) const { + return renderer::create_render_target(width, height, format); } std::shared_ptr application::create_pixel_shader_drawer() const { - return renderer_->create_pixel_shader_drawer(); + return nullptr; } void application::init_glfw() { diff --git a/core/application/application.h b/core/application/application.h index 1c193c7..e788008 100644 --- a/core/application/application.h +++ b/core/application/application.h @@ -2,8 +2,9 @@ #include #include "imgui.h" #include "GLFW/glfw3.h" +#include -class renderer_vk; +class renderer; class pixel_shader_drawer; class render_target; class texture; @@ -54,12 +55,12 @@ public: virtual void init_imgui(ImGuiContext* in_context) = 0; - [[nodiscard]] std::shared_ptr load_texture(const std::string& path) const; + [[nodiscard]] std::shared_ptr load_texture(const std::string& path, vk::Format format = vk::Format::eR8G8B8A8Unorm) const; - std::shared_ptr create_texture(const unsigned char* data, int width, int height) const; + std::shared_ptr create_texture(const unsigned char* data, int width, int height, vk::Format format) const; [[nodiscard]] std::shared_ptr create_render_target(int width, int height, - texture_format format) const; + vk::Format format) const; [[nodiscard]] std::shared_ptr create_pixel_shader_drawer() const; @@ -67,11 +68,11 @@ public: [[nodiscard]] virtual const char* get_draw_ps_vertex_shader_entry() const = 0; // Vertex Shader used for drawing ps - [[nodiscard]] renderer_vk* get_renderer() const { return renderer_; } + [[nodiscard]] renderer* get_renderer() const { return renderer_; } [[nodiscard]] GLFWwindow* get_window() const { return window_; } protected: - renderer_vk* renderer_ = nullptr; + renderer* renderer_ = nullptr; GLFWwindow* window_ = nullptr; std::shared_ptr async_spdlog_; diff --git a/core/extern.h b/core/extern.h index 2ac3d76..102d418 100644 --- a/core/extern.h +++ b/core/extern.h @@ -1,6 +1,5 @@ #pragma once #include "spdlog/spdlog.h" -#include "rhi/rhi_defintion.h" #ifdef core_EXPORTS #define CORE_API __declspec(dllexport) diff --git a/core/rhi/buffer_vk.cpp b/core/rhi/buffer_vk.cpp new file mode 100644 index 0000000..be6608d --- /dev/null +++ b/core/rhi/buffer_vk.cpp @@ -0,0 +1,61 @@ +#include "buffer_vk.h" + +#include "utils/utils.hpp" + +buffer_vk::buffer_vk() { + buffer = nullptr; + memory = nullptr; + descriptor_type = vk::DescriptorType::eUniformBuffer; +} + +void buffer_vk::create(uint32_t size, vk::BufferUsageFlagBits in_usage, vk::DescriptorType in_descriptor_type) { + destroy(); + descriptor_type = in_descriptor_type; + const renderer* render_vk = application::get()->get_renderer(); + const vk::Device& device = render_vk->device; + + vk::BufferCreateInfo buffer_create_info; + buffer_create_info.setSize(size); + buffer_create_info.setUsage(in_usage); + buffer_create_info.setSharingMode(vk::SharingMode::eExclusive); + buffer = device.createBuffer(buffer_create_info); + + const vk::PhysicalDeviceMemoryProperties memory_properties = render_vk->physical_device.getMemoryProperties(); + const vk::MemoryRequirements memory_requirements = device.getBufferMemoryRequirements(buffer); + const auto memory_type = vk::su::findMemoryType(memory_properties, memory_requirements.memoryTypeBits, vk::MemoryPropertyFlagBits::eHostVisible | vk::MemoryPropertyFlagBits::eHostCoherent); + + vk::MemoryAllocateInfo memory_allocate_info; + memory_allocate_info.setAllocationSize(memory_requirements.size); + memory_allocate_info.setMemoryTypeIndex(memory_type); + memory = device.allocateMemory(memory_allocate_info); + device.bindBufferMemory(buffer, memory, 0); +} + +void buffer_vk::create_storage(uint32_t size) { + create(size, vk::BufferUsageFlagBits::eStorageBuffer, vk::DescriptorType::eStorageBuffer); +} + +void buffer_vk::create_uniform(uint32_t size) { + create(size, vk::BufferUsageFlagBits::eUniformBuffer, vk::DescriptorType::eUniformBuffer); +} + +void buffer_vk::create_staging(uint32_t size) { + create(size, vk::BufferUsageFlagBits::eTransferSrc, vk::DescriptorType::eUniformBuffer); +} + +void buffer_vk::destroy() const { + if (!buffer) { + return; + } + const renderer* render_vk = application::get()->get_renderer(); + const vk::Device& device = render_vk->device; + + device.destroyBuffer(buffer); + device.freeMemory(memory); +} + +void buffer_vk::update(const void* data, uint32_t size) { + const auto p = map(); + memcpy(p, data, size); + unmap(); +} diff --git a/core/rhi/buffer_vk.h b/core/rhi/buffer_vk.h new file mode 100644 index 0000000..657a242 --- /dev/null +++ b/core/rhi/buffer_vk.h @@ -0,0 +1,52 @@ +#pragma once +#include +#include + +#include "renderer.h" + +class CORE_API buffer_vk { +public: + buffer_vk(); + ~buffer_vk() { destroy(); } + + void create(uint32_t size, vk::BufferUsageFlagBits in_usage, vk::DescriptorType in_descriptor_type); + void create_storage(uint32_t size); + void create_uniform(uint32_t size); + void create_staging(uint32_t size); + void destroy() const; + + template + T* map() { + const auto& device = application::get()->get_renderer()->device; + void* mapped; + const auto err = device.mapMemory(memory, 0, VK_WHOLE_SIZE, vk::MemoryMapFlags(), &mapped); + check_vk_result(err); + return static_cast(mapped); + } + void unmap() const { + const auto& device = application::get()->get_renderer()->device; + device.unmapMemory(memory); + } + + void update(const void* data, uint32_t size); + template + void update(const T& data) { + update(&data, sizeof(T)); + } + template + void update(const std::vector& data) { + update(data.data(), data.size() * sizeof(T)); + } + + operator vk::DescriptorBufferInfo() const { + vk::DescriptorBufferInfo descriptor_buffer_info; + descriptor_buffer_info.setBuffer(buffer); + descriptor_buffer_info.setOffset(0); + descriptor_buffer_info.setRange(VK_WHOLE_SIZE); + return descriptor_buffer_info; + } + + vk::Buffer buffer; + vk::DeviceMemory memory; + vk::DescriptorType descriptor_type; +}; diff --git a/core/rhi/compute_pipeline.cpp b/core/rhi/compute_pipeline.cpp new file mode 100644 index 0000000..7cd5475 --- /dev/null +++ b/core/rhi/compute_pipeline.cpp @@ -0,0 +1,98 @@ +#include "compute_pipeline.h" + +#include "renderer.h" + +#include + +void compute_pipeline::add_binding(uint32_t binding, vk::DescriptorType descriptor_type, uint32_t descriptor_count, + const vk::Sampler* immutable_samplers) { + const vk::ShaderStageFlags flag = vk::ShaderStageFlagBits::eCompute; + + vk::DescriptorSetLayoutBinding descriptor_set_layout_binding; + descriptor_set_layout_binding.setBinding(binding); + descriptor_set_layout_binding.setDescriptorType(descriptor_type); + descriptor_set_layout_binding.setDescriptorCount(descriptor_count); + descriptor_set_layout_binding.setStageFlags(flag); + descriptor_set_layout_binding.setPImmutableSamplers(immutable_samplers); + descriptor_set_layout_bindings_.push_back(descriptor_set_layout_binding); +} + +void compute_pipeline::create() { + const renderer* render_vk = application::get()->get_renderer(); + const vk::Device& device = render_vk->device; + + vk::PipelineCacheCreateInfo pipeline_cache_create_info; + pipeline_cache_create_info.setInitialDataSize(0); + pipeline_cache_ = device.createPipelineCache(pipeline_cache_create_info); + + create_pipeline_layout(); + + + vk::PipelineShaderStageCreateInfo pipeline_shader_stage_create_info; + pipeline_shader_stage_create_info.setStage(vk::ShaderStageFlagBits::eCompute); + pipeline_shader_stage_create_info.setModule(shader_module_); + pipeline_shader_stage_create_info.setPName("main"); + + vk::ComputePipelineCreateInfo pipeline_create_info; + pipeline_create_info.setLayout(pipeline_layout_); + pipeline_create_info.setStage(pipeline_shader_stage_create_info); + const auto pipeline_result = device.createComputePipeline(pipeline_cache_, pipeline_create_info); + + check_vk_result(pipeline_result.result); + pipeline_ = pipeline_result.value; +} + +void compute_pipeline::destroy() { + const renderer* render_vk = application::get()->get_renderer(); + const vk::Device& device = render_vk->device; + + device.destroyPipeline(pipeline_); + device.destroyPipelineCache(pipeline_cache_); + device.destroyPipelineLayout(pipeline_layout_); +} + +void compute_pipeline::dispatch(uint32_t group_count_x, uint32_t group_count_y, uint32_t group_count_z) const { +#ifdef _DEBUG + if (pipeline_ == vk::Pipeline()) { + throw std::runtime_error("Pipeline not created"); + } +#endif + const renderer* render_vk = application::get()->get_renderer(); + const vk::Device& device = render_vk->device; + const vk::CommandPool& command_pool = render_vk->get_command_pool(); + + vk::CommandBufferAllocateInfo command_buffer_allocate_info; + command_buffer_allocate_info.setCommandPool(command_pool); + command_buffer_allocate_info.setLevel(vk::CommandBufferLevel::ePrimary); + command_buffer_allocate_info.setCommandBufferCount(1); + const vk::CommandBuffer command_buffer = device.allocateCommandBuffers(command_buffer_allocate_info)[0]; + + vk::CommandBufferBeginInfo command_buffer_begin_info; + command_buffer_begin_info.setFlags(vk::CommandBufferUsageFlagBits::eOneTimeSubmit); + command_buffer.begin(command_buffer_begin_info); + + command_buffer.bindPipeline(vk::PipelineBindPoint::eCompute, pipeline_); + command_buffer.bindDescriptorSets(vk::PipelineBindPoint::eCompute, pipeline_layout_, 0, descriptor_set_, nullptr); + command_buffer.dispatch(group_count_x, group_count_y, group_count_z); + + command_buffer.end(); + + vk::SubmitInfo submit_info; + submit_info.setCommandBufferCount(1); + submit_info.setPCommandBuffers(&command_buffer); + render_vk->queue.submit(submit_info, nullptr); + render_vk->queue.waitIdle(); + + device.freeCommandBuffers(command_pool, command_buffer); + device.destroyCommandPool(command_pool); +} + +void compute_pipeline::set_shader(const uint8_t* shader_code, size_t shader_code_size) { + const renderer* render_vk = application::get()->get_renderer(); + const vk::Device& device = render_vk->device; + + vk::ShaderModuleCreateInfo shader_module_create_info; + shader_module_create_info.setCodeSize(shader_code_size); + shader_module_create_info.setPCode(reinterpret_cast(shader_code)); + shader_module_ = device.createShaderModule(shader_module_create_info); +} diff --git a/core/rhi/compute_pipeline.h b/core/rhi/compute_pipeline.h new file mode 100644 index 0000000..afd8657 --- /dev/null +++ b/core/rhi/compute_pipeline.h @@ -0,0 +1,15 @@ +#pragma once +#include "pipeline.h" + +class CORE_API compute_pipeline : public pipeline { +public: + void add_binding(uint32_t binding, vk::DescriptorType descriptor_type, uint32_t descriptor_count, const vk::Sampler* immutable_samplers) override; + void create() override; + void destroy() override; + + void dispatch(uint32_t group_count_x, uint32_t group_count_y, uint32_t group_count_z) const; + + void set_shader(const uint8_t *shader_code, size_t shader_code_size); +private: + vk::ShaderModule shader_module_; +}; diff --git a/core/rhi/pipeline.cpp b/core/rhi/pipeline.cpp new file mode 100644 index 0000000..c98a555 --- /dev/null +++ b/core/rhi/pipeline.cpp @@ -0,0 +1,53 @@ +#include "pipeline.h" + +#include "renderer.h" +#include "texture.h" + +void pipeline::create_pipeline_layout() { + const renderer* render_vk = application::get()->get_renderer(); + const vk::Device& device = render_vk->device; + + vk::DescriptorSetLayoutCreateInfo descriptor_set_layout_create_info; + descriptor_set_layout_create_info.setBindings(descriptor_set_layout_bindings_); + descriptor_set_layout_ = device.createDescriptorSetLayout(descriptor_set_layout_create_info); + + vk::PipelineLayoutCreateInfo pipeline_layout_create_info; + pipeline_layout_create_info.setSetLayouts(descriptor_set_layout_); + pipeline_layout_ = device.createPipelineLayout(pipeline_layout_create_info); + + vk::DescriptorSetAllocateInfo descriptor_set_allocate_info; + descriptor_set_allocate_info.setDescriptorPool(render_vk->descriptor_pool); + descriptor_set_allocate_info.setSetLayouts(descriptor_set_layout_); + descriptor_set_ = device.allocateDescriptorSets(descriptor_set_allocate_info).front(); + + std::vector write_descriptor_sets; + + for (const auto& binding_info: descriptor_set_layout_bindings_) { + vk::WriteDescriptorSet write_descriptor_set; + write_descriptor_set.setDstSet(descriptor_set_); + write_descriptor_set.setDstBinding(binding_info.binding); + write_descriptor_set.setDescriptorType(binding_info.descriptorType); + + switch (binding_info.descriptorType) { + case vk::DescriptorType::eCombinedImageSampler: + case vk::DescriptorType::eSampledImage: + case vk::DescriptorType::eStorageImage: { + const auto& t = textures_[binding_info.binding]; + vk::DescriptorImageInfo image_info = *t.get(); + write_descriptor_set.setImageInfo(image_info); + } + break; + case vk::DescriptorType::eUniformBuffer: + case vk::DescriptorType::eStorageBuffer: { + vk::DescriptorBufferInfo buffer_info = buffers_[binding_info.binding]; + write_descriptor_set.setBufferInfo(buffer_info); + } + break; + default: + continue; + } + write_descriptor_sets.push_back(write_descriptor_set); + } + + device.updateDescriptorSets(write_descriptor_sets, nullptr); +} diff --git a/core/rhi/vulkan/pipeline.h b/core/rhi/pipeline.h similarity index 64% rename from core/rhi/vulkan/pipeline.h rename to core/rhi/pipeline.h index 88cbd4f..b9744fb 100644 --- a/core/rhi/vulkan/pipeline.h +++ b/core/rhi/pipeline.h @@ -1,26 +1,34 @@ #pragma once #include +#include + +#include "buffer_vk.h" + +class texture; /// 1. add_binding /// 2. create(); -/// 3. execute_pipeline(); +/// 3. dispatch(); class pipeline { public: virtual ~pipeline() = default; virtual void add_binding(uint32_t binding, vk::DescriptorType descriptor_type, uint32_t descriptor_count, const vk::Sampler* immutable_samplers) = 0; - void add_uniform_buffer(uint32_t binding, uint32_t descriptor_count = 1) { + void add_uniform_buffer(uint32_t binding, const buffer_vk& buf, uint32_t descriptor_count = 1) { add_binding(binding, vk::DescriptorType::eUniformBuffer, descriptor_count, nullptr); + buffers_[binding] = buf; } - void add_storage_buffer(uint32_t binding, uint32_t descriptor_count = 1) { + void add_storage_buffer(uint32_t binding, const buffer_vk& buf, uint32_t descriptor_count = 1) { add_binding(binding, vk::DescriptorType::eStorageBuffer, descriptor_count, nullptr); + buffers_[binding] = buf; } void add_sampled_image(uint32_t binding, uint32_t descriptor_count = 1, const vk::Sampler* immutable_samplers = nullptr) { add_binding(binding, vk::DescriptorType::eSampledImage, descriptor_count, immutable_samplers); } - void add_storage_image(uint32_t binding, uint32_t descriptor_count = 1) { + void add_storage_image(uint32_t binding, std::shared_ptr in_texture, uint32_t descriptor_count = 1) { add_binding(binding, vk::DescriptorType::eStorageImage, descriptor_count, nullptr); + textures_[binding] = in_texture; } void add_input_attachment(uint32_t binding, uint32_t descriptor_count = 1) { add_binding(binding, vk::DescriptorType::eInputAttachment, descriptor_count, nullptr); @@ -28,19 +36,23 @@ public: void add_sampler(uint32_t binding, uint32_t descriptor_count = 1, const vk::Sampler* immutable_samplers = nullptr) { add_binding(binding, vk::DescriptorType::eSampler, descriptor_count, immutable_samplers); } - void add_combined_image_sampler(uint32_t binding, uint32_t descriptor_count = 1, const vk::Sampler* immutable_samplers = nullptr) { + void add_uniform_image(uint32_t binding, std::shared_ptr in_texture, uint32_t descriptor_count = 1, const vk::Sampler* immutable_samplers = nullptr) { add_binding(binding, vk::DescriptorType::eCombinedImageSampler, descriptor_count, immutable_samplers); + textures_[binding] = in_texture; } - void create_pipeline_layout(); - virtual void create() = 0; virtual void destroy() = 0; - virtual void bind(vk::CommandBuffer command_buffer) const = 0; - void execute_pipeline() const; vk::Pipeline pipeline_; vk::PipelineCache pipeline_cache_; vk::PipelineLayout pipeline_layout_; + vk::DescriptorSetLayout descriptor_set_layout_; + vk::DescriptorSet descriptor_set_; std::vector descriptor_set_layout_bindings_; + + std::map buffers_; + std::map> textures_; +protected: + void create_pipeline_layout(); }; diff --git a/core/rhi/render_target.cpp b/core/rhi/render_target.cpp new file mode 100644 index 0000000..de90976 --- /dev/null +++ b/core/rhi/render_target.cpp @@ -0,0 +1,95 @@ +#include "render_target.h" + +#include "renderer.h" +#include "application/application.h" +#include "utils/utils.hpp" + +void render_target::init(const int width, const int height, const vk::Format in_format) { + width_ = width; + height_ = height; + format = in_format; + + create(width, height); +} + +void* render_target::lock(lock_state state) const { + const auto r = application::get()->get_renderer(); + const auto& device = r->device; + + vk::ImageSubresource subresource; + subresource.setAspectMask(vk::ImageAspectFlagBits::eColor); + subresource.setMipLevel(0); + subresource.setArrayLayer(0); + + const vk::SubresourceLayout subresource_layout = device.getImageSubresourceLayout(image, subresource); + + void* data; + const vk::Result result = device.mapMemory(memory, 0, VK_WHOLE_SIZE, vk::MemoryMapFlags(), &data); + check_vk_result(result); + + return static_cast(data) + subresource_layout.offset; +} + +void render_target::unlock() { + const auto r = application::get()->get_renderer(); + const auto& device = r->device; + + device.unmapMemory(memory); +} + +void render_target::create(int width, int height) { + auto r = application::get()->get_renderer(); + const auto& device = r->device; + const auto& physical_device = r->physical_device; + + vk::ImageCreateInfo image_create_info; + image_create_info.setImageType(vk::ImageType::e2D); + image_create_info.setFormat(format); + image_create_info.setExtent({static_cast(width), static_cast(height), 1}); + image_create_info.setMipLevels(1); + image_create_info.setArrayLayers(1); + image_create_info.setSamples(vk::SampleCountFlagBits::e1); + image_create_info.setTiling(vk::ImageTiling::eOptimal); + image_create_info.setUsage(vk::ImageUsageFlagBits::eSampled | vk::ImageUsageFlagBits::eTransferDst | vk::ImageUsageFlagBits::eColorAttachment); + image_create_info.setSharingMode(vk::SharingMode::eExclusive); + image_create_info.setInitialLayout(vk::ImageLayout::eUndefined); + image = device.createImage(image_create_info); + + vk::PhysicalDeviceMemoryProperties memory_properties = physical_device.getMemoryProperties(); + vk::MemoryRequirements memory_requirements = device.getImageMemoryRequirements(image); + const uint32_t memory_type = vk::su::findMemoryType(memory_properties, memory_requirements.memoryTypeBits, vk::MemoryPropertyFlagBits::eDeviceLocal); + + vk::MemoryAllocateInfo memory_allocate_info; + memory_allocate_info.setAllocationSize(memory_requirements.size); + memory_allocate_info.setMemoryTypeIndex(memory_type); + memory = device.allocateMemory(memory_allocate_info); + device.bindImageMemory(image, memory, 0); + + vk::ImageViewCreateInfo image_view_create_info; + image_view_create_info.setImage(image); + image_view_create_info.setViewType(vk::ImageViewType::e2D); + image_view_create_info.setFormat(format); + image_view_create_info.setSubresourceRange({vk::ImageAspectFlagBits::eColor, 0, 1, 0, 1}); + image_view = device.createImageView(image_view_create_info); + + vk::FramebufferCreateInfo framebuffer_create_info; + framebuffer_create_info.setRenderPass(r->main_window_data.RenderPass); + framebuffer_create_info.setAttachmentCount(1); + framebuffer_create_info.setAttachments(image_view); + framebuffer_create_info.setWidth(width); + framebuffer_create_info.setHeight(height); + framebuffer_create_info.setLayers(1); + framebuffer = device.createFramebuffer(framebuffer_create_info); +} + +void render_target::on_resize(int width, int height) { + const auto r = application::get()->get_renderer(); + const auto& device = r->device; + + device.destroyFramebuffer(framebuffer); + device.destroyImageView(image_view); + device.destroyImage(image); + device.freeMemory(memory); + + create(width, height); +} diff --git a/core/rhi/render_target.h b/core/rhi/render_target.h index 3a4b737..73f28eb 100644 --- a/core/rhi/render_target.h +++ b/core/rhi/render_target.h @@ -1,6 +1,7 @@ #pragma once +#include + #include "render_resource.h" -#include "rhi_defintion.h" enum class lock_state { NONE, @@ -13,14 +14,11 @@ class render_target : public render_resource { public: [[nodiscard]] int get_height() const override { return height_; } [[nodiscard]] int get_width() const override { return width_; } + [[nodiscard]] ImTextureID get_texture_id() override { return descriptor_set; } - void init(const int width, const int height, const texture_format format) { - width_ = width; - height_ = height; - on_init(width, height, format); - } + void init(const int width, const int height, const vk::Format in_format); - virtual void resize(const int width, const int height) { + void resize(const int width, const int height) { if (width_ == width && height_ == height) return; @@ -31,15 +29,19 @@ public: on_resize_callback(shared_from_this()); } - virtual void* lock(lock_state state) = 0; - - virtual void unlock() = 0; + void* lock(lock_state state) const; + void unlock(); std::function)> on_resize_callback; - + vk::Image image; + vk::ImageView image_view; + vk::DeviceMemory memory; + vk::Framebuffer framebuffer; + vk::Format format; + vk::DescriptorSet descriptor_set; protected: - virtual void on_init(int width, int height, texture_format format) = 0; - virtual void on_resize(int width, int height) = 0; + void create(int width, int height); + void on_resize(int width, int height); int width_ = 0; int height_ = 0; diff --git a/core/rhi/renderer.cpp b/core/rhi/renderer.cpp index f7ec2b2..638fbed 100644 --- a/core/rhi/renderer.cpp +++ b/core/rhi/renderer.cpp @@ -1,13 +1,423 @@ #include "renderer.h" +#include "imgui_impl_glfw.h" +#include "render_target.h" + #include "application/application.h" +#include "texture.h" + +static bool is_extension_available(const std::vector& properties, const char* extension) { + return std::ranges::any_of(properties, [extension](const vk::ExtensionProperties& p) { + return strcmp(p.extensionName, extension) == 0; + }); +} + +vk::CommandPool renderer::get_command_pool() const { + return main_window_data.Frames[main_window_data.FrameIndex].CommandPool; +} + +vk::CommandBuffer renderer::create_command_buffer(vk::CommandBufferLevel level, bool begin) const { + vk::CommandBufferAllocateInfo alloc_info; + alloc_info.setCommandPool(get_command_pool()); + alloc_info.setLevel(level); + alloc_info.setCommandBufferCount(1); + + vk::CommandBuffer command_buffer; + auto err = device.allocateCommandBuffers(&alloc_info, &command_buffer); + check_vk_result(err); + + // If requested, also start the new command buffer + if (begin) { + vk::CommandBufferBeginInfo begin_info; + begin_info.setFlags(vk::CommandBufferUsageFlagBits::eOneTimeSubmit); + command_buffer.begin(begin_info); + } + + return command_buffer; +} + +void renderer::end_command_buffer(vk::CommandBuffer command_buffer) { + command_buffer.end(); + + vk::SubmitInfo submit_info; + submit_info.setCommandBuffers(command_buffer); + queue.submit(submit_info, nullptr); +} + +void renderer::init_vulkan(GLFWwindow* window_handle) { + std::vector extensions; + uint32_t extensions_count = 0; + const char** glfw_extensions = glfwGetRequiredInstanceExtensions(&extensions_count); + for (uint32_t i = 0; i < extensions_count; i++) + extensions.push_back(glfw_extensions[i]); + setup_vulkan(extensions); + + // Create Window Surface + VkSurfaceKHR surface; + VkResult err = glfwCreateWindowSurface(instance, window_handle, reinterpret_cast(allocator), &surface); + check_vk_result(err); + + // Create Framebuffers + int w, h; + glfwGetFramebufferSize(window_handle, &w, &h); + setup_vulkan_window(surface, w, h); + + ImGui_ImplGlfw_InitForVulkan(window_handle, true); + + ImGui_ImplVulkan_InitInfo init_info = {}; + init_info.Instance = instance; + init_info.PhysicalDevice = physical_device; + init_info.Device = device; + init_info.QueueFamily = queue_family; + init_info.Queue = queue; + init_info.PipelineCache = pipeline_cache; + init_info.DescriptorPool = descriptor_pool; + init_info.RenderPass = main_window_data.RenderPass; + init_info.Subpass = 0; + init_info.MinImageCount = min_image_count; + init_info.ImageCount = main_window_data.ImageCount; + init_info.MSAASamples = VK_SAMPLE_COUNT_1_BIT; + init_info.Allocator = reinterpret_cast(allocator); + init_info.CheckVkResultFn = check_vk_result; + ImGui_ImplVulkan_Init(&init_info); +} + +vk::PhysicalDevice renderer::setup_vulkan_select_physical_device() const { + const std::vector gpus = instance.enumeratePhysicalDevices(); + IM_ASSERT(!gpus.empty()); + + // If a number >1 of GPUs got reported, find discrete GPU if present, or use first one available. This covers + // most common cases (multi-gpu/integrated+dedicated graphics). Handling more complicated setups (multiple + // dedicated GPUs) is out of scope of this sample. + for (auto& device: gpus) { + if (const auto properties = device.getProperties(); properties.deviceType == vk::PhysicalDeviceType::eDiscreteGpu) + return device; + } + + // Use first GPU (Integrated) is a Discrete one is not available. + if (!gpus.empty()) + return gpus[0]; + return VK_NULL_HANDLE; +} + +void renderer::setup_vulkan(std::vector instance_extensions) { + // Create Vulkan Instance + { + vk::InstanceCreateInfo create_info; + + // VkInstanceCreateInfo create_info = {}; + // create_info.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO; + + // Enumerate available extensions + auto properties = vk::enumerateInstanceExtensionProperties(); + + // Enable required extensions + if (is_extension_available(properties, VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME)) + instance_extensions.push_back(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME); +#ifdef VK_KHR_PORTABILITY_ENUMERATION_EXTENSION_NAME + if (is_extension_available(properties, VK_KHR_PORTABILITY_ENUMERATION_EXTENSION_NAME)) { + instance_extensions.push_back(VK_KHR_PORTABILITY_ENUMERATION_EXTENSION_NAME); + create_info.flags |= vk::InstanceCreateFlagBits::eEnumeratePortabilityKHR; + } +#endif + + // Create Vulkan Instance + create_info.setPEnabledExtensionNames(instance_extensions); + instance = vk::createInstance(create_info, allocator); + } + + // Select Physical Device (GPU) + physical_device = setup_vulkan_select_physical_device(); + + // Select graphics queue family + { + const auto queues = physical_device.getQueueFamilyProperties(); + for (uint32_t i = 0; i < queues.size(); i++) { + if (queues[i].queueFlags & vk::QueueFlagBits::eGraphics) { + queue_family = i; + break; + } + } + IM_ASSERT(queue_family != static_cast(-1)); + } + + // Create Logical Device (with 1 queue) + { + std::vector device_extensions; + device_extensions.emplace_back("VK_KHR_swapchain"); + + // Enumerate physical device extension + auto properties = physical_device.enumerateDeviceExtensionProperties(); + +#ifdef VK_KHR_PORTABILITY_SUBSET_EXTENSION_NAME + if (is_extension_available(properties, VK_KHR_PORTABILITY_SUBSET_EXTENSION_NAME)) + device_extensions.push_back(VK_KHR_PORTABILITY_SUBSET_EXTENSION_NAME); +#endif + + std::vector queue_priority = {1.0f}; + vk::DeviceQueueCreateInfo queue_info; + queue_info.setQueueFamilyIndex(queue_family); + queue_info.setQueuePriorities(queue_priority); + + vk::DeviceCreateInfo create_info; + create_info.setQueueCreateInfos(queue_info); + create_info.setPEnabledExtensionNames(device_extensions); + device = physical_device.createDevice(create_info, allocator); + queue = device.getQueue(queue_family, 0); + } + + // Create Descriptor Pool + // The example only requires a single combined image sampler descriptor for the font image and only uses one descriptor set (for that) + // If you wish to load e.g. additional textures you may need to alter pools sizes. + { + std::vector pool_sizes; + pool_sizes.emplace_back(vk::DescriptorType::eCombinedImageSampler, 1); + + vk::DescriptorPoolCreateInfo descriptor_pool_create_info; + descriptor_pool_create_info.setMaxSets(2); + descriptor_pool_create_info.setPoolSizes(pool_sizes); + + descriptor_pool = device.createDescriptorPool(descriptor_pool_create_info); + } +} + +// All the ImGui_ImplVulkanH_XXX structures/functions are optional helpers used by the demo. +// Your real engine/app may not use them. +void renderer::setup_vulkan_window(VkSurfaceKHR surface, int width, + int height) { + main_window_data.Surface = surface; + + // Check for WSI support + vk::Bool32 res; + const auto err = physical_device.getSurfaceSupportKHR(queue_family, main_window_data.Surface, &res); + check_vk_result(err); + if (res != VK_TRUE) { + fprintf(stderr, "Error no WSI support on physical device 0\n"); + exit(-1); + } + + // Select Surface Format + constexpr VkFormat requestSurfaceImageFormat[] = { + VK_FORMAT_B8G8R8A8_UNORM, VK_FORMAT_R8G8B8A8_UNORM, VK_FORMAT_B8G8R8_UNORM, VK_FORMAT_R8G8B8_UNORM + }; + constexpr VkColorSpaceKHR requestSurfaceColorSpace = VK_COLORSPACE_SRGB_NONLINEAR_KHR; + main_window_data.SurfaceFormat = ImGui_ImplVulkanH_SelectSurfaceFormat(physical_device, main_window_data.Surface, requestSurfaceImageFormat, + (size_t) IM_ARRAYSIZE(requestSurfaceImageFormat), + requestSurfaceColorSpace); + + // Select Present Mode +#ifdef APP_USE_UNLIMITED_FRAME_RATE + VkPresentModeKHR present_modes[] = { VK_PRESENT_MODE_MAILBOX_KHR, VK_PRESENT_MODE_IMMEDIATE_KHR, VK_PRESENT_MODE_FIFO_KHR }; +#else + VkPresentModeKHR present_modes[] = {VK_PRESENT_MODE_FIFO_KHR}; +#endif + main_window_data.PresentMode = ImGui_ImplVulkanH_SelectPresentMode(physical_device, main_window_data.Surface, &present_modes[0], + IM_ARRAYSIZE(present_modes)); + //printf("[vulkan] Selected PresentMode = %d\n", wd->PresentMode); + + // Create SwapChain, RenderPass, Framebuffer, etc. + IM_ASSERT(min_image_count >= 2); + + ImGui_ImplVulkanH_CreateOrResizeWindow(instance, physical_device, device, &main_window_data, queue_family, + reinterpret_cast(allocator), width, + height, min_image_count); +} + +void renderer::cleanup_vulkan() const { + device.destroyDescriptorPool(descriptor_pool); +#ifdef APP_USE_VULKAN_DEBUG_REPORT + // Remove the debug report callback + auto vkDestroyDebugReportCallbackEXT = (PFN_vkDestroyDebugReportCallbackEXT)vkGetInstanceProcAddr(g_Instance, "vkDestroyDebugReportCallbackEXT"); + vkDestroyDebugReportCallbackEXT(g_Instance, g_DebugReport, g_Allocator); +#endif // APP_USE_VULKAN_DEBUG_REPORT + + device.destroy(); + instance.destroy(); +} + +void renderer::cleanup_vulkan_window() { + ImGui_ImplVulkanH_DestroyWindow(instance, device, &main_window_data, + reinterpret_cast(allocator)); +} + +void renderer::frame_render(ImDrawData* draw_data) { + vk::Semaphore image_acquired_semaphore = main_window_data.FrameSemaphores[main_window_data.SemaphoreIndex].ImageAcquiredSemaphore; + vk::Semaphore render_complete_semaphore = main_window_data.FrameSemaphores[main_window_data.SemaphoreIndex].RenderCompleteSemaphore; + + vk::Result err = device.acquireNextImageKHR(main_window_data.Swapchain, UINT64_MAX, image_acquired_semaphore, VK_NULL_HANDLE, + &main_window_data.FrameIndex); + if (err == vk::Result::eErrorOutOfDateKHR || err == vk::Result::eSuboptimalKHR) { + swap_chain_rebuild_ = true; + return; + } + + check_vk_result(err); + + ImGui_ImplVulkanH_Frame* fd = &main_window_data.Frames[main_window_data.FrameIndex]; + const vk::CommandBuffer cmd_buf = fd->CommandBuffer; + const vk::Fence fence = fd->Fence; { + err = device.waitForFences(1, &fence, VK_TRUE, UINT64_MAX); + + // wait indefinitely instead of periodically checking + check_vk_result(err); + + err = device.resetFences(1, &fence); + check_vk_result(err); + } { + const vk::CommandPool command_pool = fd->CommandPool; + device.resetCommandPool(command_pool); + + vk::CommandBufferBeginInfo info = {}; + info.setFlags(vk::CommandBufferUsageFlagBits::eOneTimeSubmit); + + cmd_buf.begin(info); + } { + const vk::Framebuffer framebuffer = fd->Framebuffer; + const vk::RenderPass render_pass = main_window_data.RenderPass; + + const auto clear_color = main_window_data.ClearValue.color.float32; + const auto clear_depth = main_window_data.ClearValue.depthStencil.depth; + const auto clear_stencil = main_window_data.ClearValue.depthStencil.stencil; + + vk::ClearValue clear_value; + clear_value.color = vk::ClearColorValue(std::array{ + clear_color[0], clear_color[1], clear_color[2], clear_color[3] + }); + clear_value.depthStencil = vk::ClearDepthStencilValue(clear_depth, clear_stencil); + + std::vector clear_values; + clear_values.emplace_back(clear_value); + + vk::RenderPassBeginInfo info; + info.setRenderPass(render_pass); + info.setFramebuffer(framebuffer); + info.renderArea.extent.width = main_window_data.Width; + info.renderArea.extent.height = main_window_data.Height; + info.setClearValues(clear_values); + + cmd_buf.beginRenderPass(info, vk::SubpassContents::eInline); + } + + // Record dear imgui primitives into command buffer + ImGui_ImplVulkan_RenderDrawData(draw_data, fd->CommandBuffer); + + // Submit command buffer + vkCmdEndRenderPass(fd->CommandBuffer); { + vk::PipelineStageFlags wait_stage = vk::PipelineStageFlagBits::eColorAttachmentOutput; + + vk::SubmitInfo info; + info.setWaitSemaphores(image_acquired_semaphore); + info.setWaitDstStageMask(wait_stage); + info.setCommandBuffers(cmd_buf); + info.setSignalSemaphores(render_complete_semaphore); + + cmd_buf.end(); + err = queue.submit(1, &info, fence); + check_vk_result(err); + } +} + +void renderer::frame_present() { + if (swap_chain_rebuild_) + return; + vk::Semaphore render_complete_semaphore = main_window_data.FrameSemaphores[main_window_data.SemaphoreIndex].RenderCompleteSemaphore; + vk::SwapchainKHR swapchain = main_window_data.Swapchain; + uint32_t frame_index = main_window_data.FrameIndex; + + vk::PresentInfoKHR info; + info.setWaitSemaphores(render_complete_semaphore); + info.setSwapchains(swapchain); + info.setImageIndices(frame_index); + + try { + (void)queue.presentKHR(info); + } catch (const vk::OutOfDateKHRError& e) { + swap_chain_rebuild_ = true; + return; + } + main_window_data.SemaphoreIndex = (main_window_data.SemaphoreIndex + 1) % main_window_data.SemaphoreCount; // Now we can use the next set of semaphores +} + +void renderer::pre_init() { + glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); +} + +bool renderer::init(GLFWwindow* window_handle) { + if (has_initialized_) + return true; + + if (!glfwVulkanSupported()) { + throw std::runtime_error("Vulkan not supported"); + } + init_vulkan(window_handle); + + has_initialized_ = true; + return true; +} void renderer::shutdown() { - default_vs_ = nullptr; + ImGui_ImplGlfw_Shutdown(); + ImGui_ImplVulkan_Shutdown(); + + cleanup_vulkan_window(); + cleanup_vulkan(); } -std::shared_ptr renderer::get_pixel_shader_render_default_vs() { - if (!default_vs_) - default_vs_ = load_shader(application::get()->get_draw_ps_vertex_shader_entry()); - return default_vs_; +void renderer::new_frame(GLFWwindow* window_handle) { + // Resize swap chain? + if (swap_chain_rebuild_) + { + int width, height; + glfwGetFramebufferSize(window_handle, &width, &height); + if (width > 0 && height > 0) + { + ImGui_ImplVulkan_SetMinImageCount(min_image_count); + ImGui_ImplVulkanH_CreateOrResizeWindow(instance, physical_device, device, &main_window_data, queue_family, reinterpret_cast(allocator), width, height, min_image_count); + main_window_data.FrameIndex = 0; + swap_chain_rebuild_ = false; + } + } + + // Start the Dear ImGui frame + ImGui_ImplVulkan_NewFrame(); + ImGui_ImplGlfw_NewFrame(); + ImGui::NewFrame(); +} + +void renderer::end_frame(GLFWwindow* window_handle) { + ImGuiIO& io = ImGui::GetIO(); + + // Rendering + ImGui::Render(); + ImDrawData* main_draw_data = ImGui::GetDrawData(); + const bool main_is_minimized = (main_draw_data->DisplaySize.x <= 0.0f || main_draw_data->DisplaySize.y <= 0.0f); + main_window_data.ClearValue.color.float32[0] = clear_color.x * clear_color.w; + main_window_data.ClearValue.color.float32[1] = clear_color.y * clear_color.w; + main_window_data.ClearValue.color.float32[2] = clear_color.z * clear_color.w; + main_window_data.ClearValue.color.float32[3] = clear_color.w; + if (!main_is_minimized) + frame_render(main_draw_data); + + // Update and Render additional Platform Windows + if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) + { + ImGui::UpdatePlatformWindows(); + ImGui::RenderPlatformWindowsDefault(); + } + + // Present Main Platform Window + if (!main_is_minimized) + frame_present(); +} + +std::shared_ptr renderer::create_texture(const uint8_t* data, int width, int height, vk::Format format) { + auto out = std::make_shared(); + out->init_data(data, width, height, format); + return out; +} + +std::shared_ptr renderer::create_render_target(int width, int height, vk::Format format) { + auto out = std::make_shared(); + out->init(width, height, format); + return out; } diff --git a/core/rhi/renderer.h b/core/rhi/renderer.h index 208a81f..01c4e67 100644 --- a/core/rhi/renderer.h +++ b/core/rhi/renderer.h @@ -1,10 +1,13 @@ #pragma once +#include "imgui_impl_vulkan.h" + #include "imgui.h" #include "GLFW/glfw3.h" +#include + class pixel_shader_drawer; -class shader; class render_target; class texture; constexpr ImVec4 clear_color = ImVec4(0.45f, 0.55f, 0.60f, 1.00f); @@ -12,39 +15,68 @@ constexpr float clear_color_with_alpha[4] = { clear_color.x * clear_color.w, clear_color.y * clear_color.w, clear_color.z * clear_color.w, clear_color.w }; +static void check_vk_result(vk::Result err) { + if (err == vk::Result::eSuccess) + return; + + spdlog::error("[vulkan] Error: VkResult = {}", vk::to_string(err)); + abort(); +} + +static void check_vk_result(VkResult err) { + if (err == VK_SUCCESS) + return; + if (err < 0) { + spdlog::error("[vulkan] Error: VkResult = {}", err); + abort(); + } +} + class renderer { public: virtual ~renderer() = default; - virtual void pre_init() { - } + void pre_init(); - virtual bool init(GLFWwindow* window_handle) = 0; + bool init(GLFWwindow* window_handle); virtual void shutdown(); - virtual void post_shutdown() { - } + void new_frame(GLFWwindow* window_handle); - virtual std::shared_ptr load_shader(const std::string& entry_name) = 0; + void end_frame(GLFWwindow* window_handle); - virtual std::shared_ptr get_pixel_shader_render_default_vs(); - - virtual std::shared_ptr create_pixel_shader_drawer() = 0; - - virtual void new_frame(GLFWwindow* window_handle) = 0; - - virtual void end_frame(GLFWwindow* window_handle) = 0; - - virtual void resize(int width, int height) = 0; - - virtual std::shared_ptr create_texture(const unsigned char* data, int width, int height) = 0; - - virtual std::shared_ptr create_render_target(int width, int height, texture_format format) = 0; + static CORE_API std::shared_ptr create_texture(const uint8_t* data, int width, int height, vk::Format format); + static CORE_API std::shared_ptr create_render_target(int width, int height, vk::Format format); void set_vsync(const bool vsync) { vsync_ = vsync; } + vk::AllocationCallbacks* allocator = nullptr; + vk::Instance instance = VK_NULL_HANDLE; + vk::PhysicalDevice physical_device = VK_NULL_HANDLE; + vk::Device device = VK_NULL_HANDLE; + uint32_t queue_family = (uint32_t) -1; + vk::Queue queue = VK_NULL_HANDLE; + vk::DescriptorPool descriptor_pool = VK_NULL_HANDLE; + vk::PipelineCache pipeline_cache = VK_NULL_HANDLE; + [[nodiscard]] vk::CommandPool get_command_pool() const; + [[nodiscard]] vk::CommandBuffer create_command_buffer(vk::CommandBufferLevel level, bool begin) const; + void end_command_buffer(vk::CommandBuffer command_buffer); + + ImGui_ImplVulkanH_Window main_window_data; + int min_image_count = 2; protected: - std::shared_ptr default_vs_; + void init_vulkan(GLFWwindow* window_handle); + [[nodiscard]] vk::PhysicalDevice setup_vulkan_select_physical_device() const; + void setup_vulkan(std::vector instance_extensions); + void setup_vulkan_window(VkSurfaceKHR surface, int width, int height); + void cleanup_vulkan() const; + void cleanup_vulkan_window(); + void frame_render(ImDrawData* draw_data); + void frame_present(); + + bool has_initialized_ = false; + bool swap_chain_rebuild_ = false; + bool vsync_ = true; }; diff --git a/core/rhi/rhi_defintion.h b/core/rhi/rhi_defintion.h deleted file mode 100644 index 1c9cea3..0000000 --- a/core/rhi/rhi_defintion.h +++ /dev/null @@ -1,34 +0,0 @@ -#pragma once - -enum class texture_format { - RGBA8, - RGBA16_FLOAT, - RGBA32_FLOAT, - R32_FLOAT, - RG32_FLOAT, - R11F_G11F_B10F, - R16_FLOAT, - RGBA16, - RGB10_A2, - RG16, - RG8, - R16, - R8, - RG16_SNORM, - RG8_SNORM, - R16_SNORM, - R8_SNORM, - R32I, - R32UI, - R8I, - R16I, - R32F, - R8UI, - R16UI, - RG32I, - RG32UI, - RG8I, - RG16I, - RGB10_A2UI, - RG16UI, -}; diff --git a/core/rhi/shader.h b/core/rhi/shader.h deleted file mode 100644 index 5cd5606..0000000 --- a/core/rhi/shader.h +++ /dev/null @@ -1,58 +0,0 @@ -#pragma once - -class render_target; -class texture; - -struct shader_draw_data { - int width; - int height; -}; - -class shader : public std::enable_shared_from_this { -public: - virtual ~shader() = default; - - virtual bool init() { return false; } - - [[nodiscard]] virtual bool is_initialized() const = 0; - - virtual void bind() = 0; - - virtual void unbind() = 0; - - virtual void compute(int x, int y, int z) { - } - - // param setters - virtual void set_cbuffer(const char* name, void* buffer, int size) = 0; - - virtual void set_uav_buffer(const char* name, void* buffer, int count, int element_size) { - } - - virtual void set_texture(const char* name, std::shared_ptr in_texture) = 0; - - virtual void set_render_target(const char* name, std::shared_ptr in_render_target) = 0; - - template - void set_cbuffer(const char* name, const T& buffer) { - set_cbuffer(name, (void*) &buffer, sizeof(T)); - } - - template - void set_cbuffer(const char* name, const std::vector& buffer) { - set_cbuffer(name, (void*) buffer.data(), sizeof(T) * buffer.size()); - } - - template - void set_uav_buffer(const char* name, const T& buffer) { - set_uav_buffer(name, (void*) &buffer, sizeof(buffer), sizeof(T)); - } - - template - void set_uav_buffer(const char* name, const std::vector& buffer) { - set_uav_buffer(name, (void*) buffer.data(), buffer.size(), sizeof(T)); - } - - void begin_draw(const shader_draw_data& in_data) { if (on_begin_draw) on_begin_draw(in_data, *this); } - std::function on_begin_draw; -}; diff --git a/core/rhi/texture.cpp b/core/rhi/texture.cpp new file mode 100644 index 0000000..162f677 --- /dev/null +++ b/core/rhi/texture.cpp @@ -0,0 +1,157 @@ +#include "texture.h" + + +#include "buffer_vk.h" +#include "application/application.h" +#include "renderer.h" +#include "utils/utils.hpp" + + +texture::texture() { + format = vk::Format::eUndefined; + width_ = 0; + height_ = 0; + image = nullptr; + image_view = nullptr; + sampler = nullptr; + memory = nullptr; +} + +texture::~texture() { + if (image) { + const renderer* render_vk = application::get()->get_renderer(); + const vk::Device& device = render_vk->device; + device.destroyImage(image); + device.destroyImageView(image_view); + device.destroySampler(sampler); + device.freeMemory(memory); + } +} + +bool texture::init_data(const uint8_t* data, int width, int height, vk::Format in_format) { + width_ = width; + height_ = height; + format = in_format; + + const renderer* r = application::get()->get_renderer(); + const vk::Device& device = r->device; + const vk::PhysicalDevice& physical_device = r->physical_device; + + vk::ImageCreateInfo image_info; + image_info.setImageType(vk::ImageType::e2D); + image_info.setFormat(format); + image_info.setExtent({static_cast(width), static_cast(height), 1}); + image_info.setMipLevels(1); + image_info.setArrayLayers(1); + image_info.setSamples(vk::SampleCountFlagBits::e1); + image_info.setTiling(vk::ImageTiling::eOptimal); + image_info.setUsage(vk::ImageUsageFlagBits::eSampled | vk::ImageUsageFlagBits::eTransferDst); + image_info.setSharingMode(vk::SharingMode::eExclusive); + image_info.setInitialLayout(vk::ImageLayout::eUndefined); + image = device.createImage(image_info); + + const vk::PhysicalDeviceMemoryProperties memory_properties = physical_device.getMemoryProperties(); + const vk::MemoryRequirements memory_requirements = device.getImageMemoryRequirements(image); + const uint32_t memory_type = vk::su::findMemoryType(memory_properties, memory_requirements.memoryTypeBits, + vk::MemoryPropertyFlagBits::eDeviceLocal); + + vk::MemoryAllocateInfo memory_allocate_info; + memory_allocate_info.setAllocationSize(memory_requirements.size); + memory_allocate_info.setMemoryTypeIndex(memory_type); + memory = device.allocateMemory(memory_allocate_info); + device.bindImageMemory(image, memory, 0); + + vk::ImageViewCreateInfo view_info; + view_info.setImage(image); + view_info.setViewType(vk::ImageViewType::e2D); + view_info.setFormat(format); + view_info.setSubresourceRange({vk::ImageAspectFlagBits::eColor, 0, 1, 0, 1}); + image_view = device.createImageView(view_info); + + vk::SamplerCreateInfo sampler_info; + sampler_info.setMagFilter(vk::Filter::eLinear); + sampler_info.setMinFilter(vk::Filter::eLinear); + sampler_info.setMipmapMode(vk::SamplerMipmapMode::eLinear); + sampler_info.setAddressModeU(vk::SamplerAddressMode::eRepeat); + sampler_info.setAddressModeV(vk::SamplerAddressMode::eRepeat); + sampler_info.setAddressModeW(vk::SamplerAddressMode::eRepeat); + sampler_info.setMinLod(-1000); + sampler_info.setMaxLod(1000); + sampler_info.setMaxAnisotropy(1); + sampler = device.createSampler(sampler_info); + + descriptor_set = ImGui_ImplVulkan_AddTexture(sampler, image_view, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); + uint32_t image_size = width * height * 4; + + if (data) { + upload(data, image_size); + } + return true; +} + +void texture::upload(const void* data, size_t size) { + renderer* r = application::get()->get_renderer(); + const vk::Device& device = r->device; + + // Create Upload Buffer + vk::BufferCreateInfo buffer_info; + buffer_info.setSize(size); + buffer_info.setUsage(vk::BufferUsageFlagBits::eTransferSrc); + buffer_info.setSharingMode(vk::SharingMode::eExclusive); + vk::Buffer upload_buffer = device.createBuffer(buffer_info); + + vk::MemoryRequirements req = device.getBufferMemoryRequirements(upload_buffer); + uint32_t upload_memory_type = vk::su::findMemoryType(r->physical_device.getMemoryProperties(), req.memoryTypeBits, + vk::MemoryPropertyFlagBits::eHostVisible); + vk::MemoryAllocateInfo alloc_info; + alloc_info.setAllocationSize(req.size); + alloc_info.setMemoryTypeIndex(upload_memory_type); + vk::DeviceMemory upload_memory = device.allocateMemory(alloc_info); + device.bindBufferMemory(upload_buffer, upload_memory, 0); + + void* p = device.mapMemory(upload_memory, 0, size); + memcpy(p, data, size); + vk::MappedMemoryRange memory_range; + memory_range.setMemory(upload_memory); + memory_range.setSize(size); + device.flushMappedMemoryRanges(memory_range); + device.unmapMemory(upload_memory); + + const vk::CommandBuffer command_buffer = r->create_command_buffer(vk::CommandBufferLevel::ePrimary, true); + + vk::ImageMemoryBarrier copy_barrier; + copy_barrier.setDstAccessMask(vk::AccessFlagBits::eTransferWrite); + copy_barrier.setOldLayout(vk::ImageLayout::eUndefined); + copy_barrier.setNewLayout(vk::ImageLayout::eTransferDstOptimal); + copy_barrier.setSrcQueueFamilyIndex(VK_QUEUE_FAMILY_IGNORED); + copy_barrier.setDstQueueFamilyIndex(VK_QUEUE_FAMILY_IGNORED); + copy_barrier.setImage(image); + copy_barrier.setSubresourceRange({vk::ImageAspectFlagBits::eColor, 0, 1, 0, 1}); + command_buffer.pipelineBarrier(vk::PipelineStageFlagBits::eHost, vk::PipelineStageFlagBits::eTransfer, {}, {}, {}, + copy_barrier); + + vk::BufferImageCopy region; + region.setImageSubresource({vk::ImageAspectFlagBits::eColor, 0, 0, 1}); + region.setImageExtent({static_cast(width_), static_cast(height_), 1}); + command_buffer.copyBufferToImage(upload_buffer, image, vk::ImageLayout::eTransferDstOptimal, region); + + vk::ImageMemoryBarrier use_barrier; + use_barrier.setSrcAccessMask(vk::AccessFlagBits::eTransferWrite); + use_barrier.setDstAccessMask(vk::AccessFlagBits::eShaderRead); + use_barrier.setOldLayout(vk::ImageLayout::eTransferDstOptimal); + use_barrier.setNewLayout(vk::ImageLayout::eShaderReadOnlyOptimal); + use_barrier.setSrcQueueFamilyIndex(VK_QUEUE_FAMILY_IGNORED); + use_barrier.setDstQueueFamilyIndex(VK_QUEUE_FAMILY_IGNORED); + use_barrier.setImage(image); + use_barrier.setSubresourceRange({vk::ImageAspectFlagBits::eColor, 0, 1, 0, 1}); + command_buffer.pipelineBarrier(vk::PipelineStageFlagBits::eTransfer, vk::PipelineStageFlagBits::eFragmentShader, {}, + {}, {}, use_barrier); + + r->end_command_buffer(command_buffer); + device.waitIdle(); + + device.destroyBuffer(upload_buffer); + device.freeMemory(upload_memory); + + // device.freeCommandBuffers(command_pool, command_buffer); +} diff --git a/core/rhi/texture.h b/core/rhi/texture.h index 1e6d3c8..f5a4ff4 100644 --- a/core/rhi/texture.h +++ b/core/rhi/texture.h @@ -1,21 +1,39 @@ #pragma once #include "imgui.h" -#include +#include +#include "buffer_vk.h" #include "render_resource.h" class texture : public render_resource { public: - texture(): width_(0), height_(0) { - } + texture(); - virtual bool init_data(const unsigned char* data, int width, int height) = 0; - - [[nodiscard]] virtual bool is_valid() const = 0; + ~texture() override; [[nodiscard]] int get_width() const override { return width_; } [[nodiscard]] int get_height() const override { return height_; } + [[nodiscard]] ImTextureID get_texture_id() override { return descriptor_set; } + bool init_data(const uint8_t* data, int width, int height, vk::Format in_format); + bool is_valid() const { return image_view; } + + operator vk::DescriptorImageInfo() const { + vk::DescriptorImageInfo info; + info.setImageLayout(vk::ImageLayout::eShaderReadOnlyOptimal); + info.setImageView(image_view); + info.setSampler(sampler); + return info; + } + void upload(const void* data, size_t size); + + vk::Image image; + vk::ImageView image_view; + vk::Sampler sampler; + vk::DeviceMemory memory; + vk::DescriptorSet descriptor_set; + + vk::Format format; protected: int width_; int height_; diff --git a/core/rhi/vulkan/utils/utils.cpp b/core/rhi/utils/utils.cpp similarity index 100% rename from core/rhi/vulkan/utils/utils.cpp rename to core/rhi/utils/utils.cpp diff --git a/core/rhi/vulkan/utils/utils.hpp b/core/rhi/utils/utils.hpp similarity index 100% rename from core/rhi/vulkan/utils/utils.hpp rename to core/rhi/utils/utils.hpp diff --git a/core/rhi/vulkan/compute_pipeline.cpp b/core/rhi/vulkan/compute_pipeline.cpp deleted file mode 100644 index 90cb902..0000000 --- a/core/rhi/vulkan/compute_pipeline.cpp +++ /dev/null @@ -1,48 +0,0 @@ -#include "compute_pipeline.h" - -#include "application/application.h" -#include - -#include "renderer_vk.h" - -void compute_pipeline::add_binding(uint32_t binding, vk::DescriptorType descriptor_type, uint32_t descriptor_count, const vk::Sampler* immutable_samplers) { - const vk::ShaderStageFlags flag = vk::ShaderStageFlagBits::eCompute; - - vk::DescriptorSetLayoutBinding descriptor_set_layout_binding; - descriptor_set_layout_binding.setBinding(binding); - descriptor_set_layout_binding.setDescriptorType(descriptor_type); - descriptor_set_layout_binding.setDescriptorCount(descriptor_count); - descriptor_set_layout_binding.setStageFlags(flag); - descriptor_set_layout_binding.setPImmutableSamplers(immutable_samplers); - descriptor_set_layout_bindings_.push_back(descriptor_set_layout_binding); -} - -void compute_pipeline::create() { - const auto renderer_vk = application::get()->get_renderer(); - auto device = renderer_vk->device; - - vk::PipelineCacheCreateInfo pipeline_cache_create_info; - pipeline_cache_create_info.setInitialDataSize(0); - pipeline_cache_ = device.createPipelineCache(pipeline_cache_create_info); - - create_pipeline_layout(); - - vk::ComputePipelineCreateInfo compute_pipeline_create_info; - compute_pipeline_create_info.setLayout(pipeline_layout_); - const auto pipeline_result = device.createComputePipeline(pipeline_cache_, compute_pipeline_create_info); - check_vk_result(pipeline_result.result); - pipeline_ = pipeline_result.value; -} - -void compute_pipeline::destroy() { - const auto renderer_vk = application::get()->get_renderer(); - auto device = renderer_vk->device; - - device.destroyPipeline(pipeline_); - device.destroyPipelineCache(pipeline_cache_); - device.destroyPipelineLayout(pipeline_layout_); -} - -void compute_pipeline::bind(vk::CommandBuffer command_buffer) const { - command_buffer.bindPipeline(vk::PipelineBindPoint::eCompute, pipeline_); -} diff --git a/core/rhi/vulkan/compute_pipeline.h b/core/rhi/vulkan/compute_pipeline.h deleted file mode 100644 index d66dd08..0000000 --- a/core/rhi/vulkan/compute_pipeline.h +++ /dev/null @@ -1,14 +0,0 @@ -#pragma once -#include "pipeline.h" - -class compute_pipeline : pipeline { -public: - void add_binding(uint32_t binding, vk::DescriptorType descriptor_type, uint32_t descriptor_count, const vk::Sampler* immutable_samplers) override; - void create() override; - void destroy() override; - void bind(vk::CommandBuffer command_buffer) const override; - - void set_shader(const std::string& file_path) { - - } -}; diff --git a/core/rhi/vulkan/pipeline.cpp b/core/rhi/vulkan/pipeline.cpp deleted file mode 100644 index c445cca..0000000 --- a/core/rhi/vulkan/pipeline.cpp +++ /dev/null @@ -1,51 +0,0 @@ -#include "pipeline.h" - -#include "renderer_vk.h" -#include "application/application.h" - -void pipeline::create_pipeline_layout() { - const auto renderer = application::get()->get_renderer(); - const auto device = renderer->device; - - vk::DescriptorSetLayoutCreateInfo descriptor_set_layout_create_info; - descriptor_set_layout_create_info.setBindings(descriptor_set_layout_bindings_); - vk::DescriptorSetLayout descriptor_set_layout = device.createDescriptorSetLayout(descriptor_set_layout_create_info); - - vk::PipelineLayoutCreateInfo pipeline_layout_create_info; - pipeline_layout_create_info.setSetLayouts(descriptor_set_layout); - pipeline_layout_ = device.createPipelineLayout(pipeline_layout_create_info); -} - -void pipeline::execute_pipeline() const { -#ifdef _DEBUG - if (pipeline_ == vk::Pipeline()) { - throw std::runtime_error("Pipeline not created"); - } -#endif - const auto renderer = application::get()->get_renderer(); - const auto device = renderer->device; - const auto command_pool = renderer->command_pool; - - vk::CommandBufferAllocateInfo command_buffer_allocate_info; - command_buffer_allocate_info.setCommandPool(command_pool); - command_buffer_allocate_info.setLevel(vk::CommandBufferLevel::ePrimary); - command_buffer_allocate_info.setCommandBufferCount(1); - const vk::CommandBuffer command_buffer = device.allocateCommandBuffers(command_buffer_allocate_info)[0]; - - vk::CommandBufferBeginInfo command_buffer_begin_info; - command_buffer_begin_info.setFlags(vk::CommandBufferUsageFlagBits::eOneTimeSubmit); - command_buffer.begin(command_buffer_begin_info); - - bind(command_buffer); - - command_buffer.end(); - - vk::SubmitInfo submit_info; - submit_info.setCommandBufferCount(1); - submit_info.setPCommandBuffers(&command_buffer); - renderer->queue.submit(submit_info, nullptr); - renderer->queue.waitIdle(); - - device.freeCommandBuffers(command_pool, command_buffer); - device.destroyCommandPool(command_pool); -} diff --git a/core/rhi/vulkan/render_target_vk.cpp b/core/rhi/vulkan/render_target_vk.cpp deleted file mode 100644 index 8715998..0000000 --- a/core/rhi/vulkan/render_target_vk.cpp +++ /dev/null @@ -1 +0,0 @@ -#include "render_target_vk.h" diff --git a/core/rhi/vulkan/render_target_vk.h b/core/rhi/vulkan/render_target_vk.h deleted file mode 100644 index 75930ba..0000000 --- a/core/rhi/vulkan/render_target_vk.h +++ /dev/null @@ -1,8 +0,0 @@ -#pragma once - -#include "rhi/render_target.h" - - -class render_target_vk : public render_target { - -}; diff --git a/core/rhi/vulkan/renderer_vk.cpp b/core/rhi/vulkan/renderer_vk.cpp deleted file mode 100644 index 243aa2c..0000000 --- a/core/rhi/vulkan/renderer_vk.cpp +++ /dev/null @@ -1,404 +0,0 @@ -#include "renderer_vk.h" - -#include "imgui_impl_glfw.h" -#include "utils/utils.hpp" -#include "GLFW/glfw3.h" - -static bool is_extension_available(const std::vector& properties, const char* extension) { - return std::ranges::any_of(properties, [extension](const vk::ExtensionProperties& p) { - return strcmp(p.extensionName, extension) == 0; - }); -} - -vk::PhysicalDevice renderer_vk::setup_vulkan_select_physical_device() const { - const std::vector gpus = instance.enumeratePhysicalDevices(); - IM_ASSERT(!gpus.empty()); - - // If a number >1 of GPUs got reported, find discrete GPU if present, or use first one available. This covers - // most common cases (multi-gpu/integrated+dedicated graphics). Handling more complicated setups (multiple - // dedicated GPUs) is out of scope of this sample. - for (auto& device: gpus) { - if (const auto properties = device.getProperties(); properties.deviceType == vk::PhysicalDeviceType::eDiscreteGpu) - return device; - } - - // Use first GPU (Integrated) is a Discrete one is not available. - if (!gpus.empty()) - return gpus[0]; - return VK_NULL_HANDLE; -} - -void renderer_vk::setup_vulkan(std::vector instance_extensions) { - // Create Vulkan Instance - { - vk::InstanceCreateInfo create_info; - - // VkInstanceCreateInfo create_info = {}; - // create_info.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO; - - // Enumerate available extensions - auto properties = vk::enumerateInstanceExtensionProperties(); - - // Enable required extensions - if (is_extension_available(properties, VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME)) - instance_extensions.push_back(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME); -#ifdef VK_KHR_PORTABILITY_ENUMERATION_EXTENSION_NAME - if (is_extension_available(properties, VK_KHR_PORTABILITY_ENUMERATION_EXTENSION_NAME)) { - instance_extensions.push_back(VK_KHR_PORTABILITY_ENUMERATION_EXTENSION_NAME); - create_info.flags |= vk::InstanceCreateFlagBits::eEnumeratePortabilityKHR; - } -#endif - - // Create Vulkan Instance - create_info.setPEnabledExtensionNames(instance_extensions); - instance = vk::createInstance(create_info, allocator); - } - - // Select Physical Device (GPU) - physical_device = setup_vulkan_select_physical_device(); - - // Select graphics queue family - { - const auto queues = physical_device.getQueueFamilyProperties(); - for (uint32_t i = 0; i < queues.size(); i++) { - if (queues[i].queueFlags & vk::QueueFlagBits::eGraphics) { - queue_family = i; - break; - } - } - IM_ASSERT(queue_family != static_cast(-1)); - } - - // Create Logical Device (with 1 queue) - { - std::vector device_extensions; - device_extensions.emplace_back("VK_KHR_swapchain"); - - // Enumerate physical device extension - auto properties = physical_device.enumerateDeviceExtensionProperties(); - -#ifdef VK_KHR_PORTABILITY_SUBSET_EXTENSION_NAME - if (is_extension_available(properties, VK_KHR_PORTABILITY_SUBSET_EXTENSION_NAME)) - device_extensions.push_back(VK_KHR_PORTABILITY_SUBSET_EXTENSION_NAME); -#endif - - std::vector queue_priority = {1.0f}; - vk::DeviceQueueCreateInfo queue_info; - queue_info.setQueueFamilyIndex(queue_family); - queue_info.setQueuePriorities(queue_priority); - - vk::DeviceCreateInfo create_info; - create_info.setQueueCreateInfos(queue_info); - create_info.setPEnabledExtensionNames(device_extensions); - device = physical_device.createDevice(create_info, allocator); - queue = device.getQueue(queue_family, 0); - } - - // Create Descriptor Pool - // The example only requires a single combined image sampler descriptor for the font image and only uses one descriptor set (for that) - // If you wish to load e.g. additional textures you may need to alter pools sizes. - { - std::vector pool_sizes; - pool_sizes.emplace_back(vk::DescriptorType::eCombinedImageSampler, 1); - - vk::DescriptorPoolCreateInfo descriptor_pool_create_info; - descriptor_pool_create_info.setMaxSets(1); - descriptor_pool_create_info.setPoolSizes(pool_sizes); - - descriptor_pool = device.createDescriptorPool(descriptor_pool_create_info); - } - - // Create Command Pool - { - vk::CommandPoolCreateInfo cmd_pool_info; - cmd_pool_info.setQueueFamilyIndex(queue_family); - cmd_pool_info.setFlags(vk::CommandPoolCreateFlagBits::eResetCommandBuffer); - command_pool = device.createCommandPool(cmd_pool_info); - } -} - -// All the ImGui_ImplVulkanH_XXX structures/functions are optional helpers used by the demo. -// Your real engine/app may not use them. -void renderer_vk::setup_vulkan_window(VkSurfaceKHR surface, int width, - int height) { - main_window_data.Surface = surface; - - // Check for WSI support - vk::Bool32 res; - const auto err = physical_device.getSurfaceSupportKHR(queue_family, main_window_data.Surface, &res); - check_vk_result(err); - if (res != VK_TRUE) { - fprintf(stderr, "Error no WSI support on physical device 0\n"); - exit(-1); - } - - // Select Surface Format - constexpr VkFormat requestSurfaceImageFormat[] = { - VK_FORMAT_B8G8R8A8_UNORM, VK_FORMAT_R8G8B8A8_UNORM, VK_FORMAT_B8G8R8_UNORM, VK_FORMAT_R8G8B8_UNORM - }; - constexpr VkColorSpaceKHR requestSurfaceColorSpace = VK_COLORSPACE_SRGB_NONLINEAR_KHR; - main_window_data.SurfaceFormat = ImGui_ImplVulkanH_SelectSurfaceFormat(physical_device, main_window_data.Surface, requestSurfaceImageFormat, - (size_t) IM_ARRAYSIZE(requestSurfaceImageFormat), - requestSurfaceColorSpace); - - // Select Present Mode -#ifdef APP_USE_UNLIMITED_FRAME_RATE - VkPresentModeKHR present_modes[] = { VK_PRESENT_MODE_MAILBOX_KHR, VK_PRESENT_MODE_IMMEDIATE_KHR, VK_PRESENT_MODE_FIFO_KHR }; -#else - VkPresentModeKHR present_modes[] = {VK_PRESENT_MODE_FIFO_KHR}; -#endif - main_window_data.PresentMode = ImGui_ImplVulkanH_SelectPresentMode(physical_device, main_window_data.Surface, &present_modes[0], - IM_ARRAYSIZE(present_modes)); - //printf("[vulkan] Selected PresentMode = %d\n", wd->PresentMode); - - // Create SwapChain, RenderPass, Framebuffer, etc. - IM_ASSERT(min_image_count >= 2); - - ImGui_ImplVulkanH_CreateOrResizeWindow(instance, physical_device, device, &main_window_data, queue_family, - reinterpret_cast(allocator), width, - height, min_image_count); -} - -void renderer_vk::cleanup_vulkan() const { - device.destroyDescriptorPool(descriptor_pool); -#ifdef APP_USE_VULKAN_DEBUG_REPORT - // Remove the debug report callback - auto vkDestroyDebugReportCallbackEXT = (PFN_vkDestroyDebugReportCallbackEXT)vkGetInstanceProcAddr(g_Instance, "vkDestroyDebugReportCallbackEXT"); - vkDestroyDebugReportCallbackEXT(g_Instance, g_DebugReport, g_Allocator); -#endif // APP_USE_VULKAN_DEBUG_REPORT - - device.destroy(); - instance.destroy(); -} - -void renderer_vk::cleanup_vulkan_window() { - ImGui_ImplVulkanH_DestroyWindow(instance, device, &main_window_data, - reinterpret_cast(allocator)); -} - -void renderer_vk::frame_render(ImDrawData* draw_data) { - vk::Semaphore image_acquired_semaphore = main_window_data.FrameSemaphores[main_window_data.SemaphoreIndex].ImageAcquiredSemaphore; - vk::Semaphore render_complete_semaphore = main_window_data.FrameSemaphores[main_window_data.SemaphoreIndex].RenderCompleteSemaphore; - - vk::Result err = device.acquireNextImageKHR(main_window_data.Swapchain, UINT64_MAX, image_acquired_semaphore, VK_NULL_HANDLE, - &main_window_data.FrameIndex); - if (err == vk::Result::eErrorOutOfDateKHR || err == vk::Result::eSuboptimalKHR) { - swap_chain_rebuild_ = true; - return; - } - - check_vk_result(err); - - ImGui_ImplVulkanH_Frame* fd = &main_window_data.Frames[main_window_data.FrameIndex]; - const vk::CommandBuffer cmd_buf = fd->CommandBuffer; - const vk::Fence fence = fd->Fence; { - err = device.waitForFences(1, &fence, VK_TRUE, UINT64_MAX); - - // wait indefinitely instead of periodically checking - check_vk_result(err); - - err = device.resetFences(1, &fence); - check_vk_result(err); - } { - const vk::CommandPool command_pool = fd->CommandPool; - device.resetCommandPool(command_pool); - - vk::CommandBufferBeginInfo info = {}; - info.setFlags(vk::CommandBufferUsageFlagBits::eOneTimeSubmit); - - cmd_buf.begin(info); - } { - const vk::Framebuffer framebuffer = fd->Framebuffer; - const vk::RenderPass render_pass = main_window_data.RenderPass; - - const auto clear_color = main_window_data.ClearValue.color.float32; - const auto clear_depth = main_window_data.ClearValue.depthStencil.depth; - const auto clear_stencil = main_window_data.ClearValue.depthStencil.stencil; - - vk::ClearValue clear_value; - clear_value.color = vk::ClearColorValue(std::array{ - clear_color[0], clear_color[1], clear_color[2], clear_color[3] - }); - clear_value.depthStencil = vk::ClearDepthStencilValue(clear_depth, clear_stencil); - - std::vector clear_values; - clear_values.emplace_back(clear_value); - - vk::RenderPassBeginInfo info; - info.setRenderPass(render_pass); - info.setFramebuffer(framebuffer); - info.renderArea.extent.width = main_window_data.Width; - info.renderArea.extent.height = main_window_data.Height; - info.setClearValues(clear_values); - - cmd_buf.beginRenderPass(info, vk::SubpassContents::eInline); - } - - // Record dear imgui primitives into command buffer - ImGui_ImplVulkan_RenderDrawData(draw_data, fd->CommandBuffer); - - // Submit command buffer - vkCmdEndRenderPass(fd->CommandBuffer); { - vk::PipelineStageFlags wait_stage = vk::PipelineStageFlagBits::eColorAttachmentOutput; - - vk::SubmitInfo info; - info.setWaitSemaphores(image_acquired_semaphore); - info.setWaitDstStageMask(wait_stage); - info.setCommandBuffers(cmd_buf); - info.setSignalSemaphores(render_complete_semaphore); - - cmd_buf.end(); - err = queue.submit(1, &info, fence); - check_vk_result(err); - } -} - -void renderer_vk::frame_present() { - if (swap_chain_rebuild_) - return; - vk::Semaphore render_complete_semaphore = main_window_data.FrameSemaphores[main_window_data.SemaphoreIndex].RenderCompleteSemaphore; - vk::SwapchainKHR swapchain = main_window_data.Swapchain; - uint32_t frame_index = main_window_data.FrameIndex; - - vk::PresentInfoKHR info; - info.setWaitSemaphores(render_complete_semaphore); - info.setSwapchains(swapchain); - info.setImageIndices(frame_index); - - try { - (void)queue.presentKHR(info); - } catch (const vk::OutOfDateKHRError& e) { - swap_chain_rebuild_ = true; - return; - } - main_window_data.SemaphoreIndex = (main_window_data.SemaphoreIndex + 1) % main_window_data.SemaphoreCount; // Now we can use the next set of semaphores -} - -void renderer_vk::pre_init() { - renderer::pre_init(); - glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); -} - -bool renderer_vk::init(GLFWwindow* window_handle) { - if (has_initialized_) - return true; - - if (!glfwVulkanSupported()) { - throw std::runtime_error("Vulkan not supported"); - } - init_vulkan(window_handle); - - has_initialized_ = true; - return true; -} - -void renderer_vk::shutdown() { - renderer::shutdown(); - - ImGui_ImplGlfw_Shutdown(); - ImGui_ImplVulkan_Shutdown(); -} - -std::shared_ptr renderer_vk::load_shader(const std::string& entry_name) { - return nullptr; -} - -std::shared_ptr renderer_vk::create_pixel_shader_drawer() { - return nullptr; -} - -void renderer_vk::new_frame(GLFWwindow* window_handle) { - // Resize swap chain? - if (swap_chain_rebuild_) - { - int width, height; - glfwGetFramebufferSize(window_handle, &width, &height); - if (width > 0 && height > 0) - { - ImGui_ImplVulkan_SetMinImageCount(min_image_count); - ImGui_ImplVulkanH_CreateOrResizeWindow(instance, physical_device, device, &main_window_data, queue_family, reinterpret_cast(allocator), width, height, min_image_count); - main_window_data.FrameIndex = 0; - swap_chain_rebuild_ = false; - } - } - - // Start the Dear ImGui frame - ImGui_ImplVulkan_NewFrame(); - ImGui_ImplGlfw_NewFrame(); - ImGui::NewFrame(); -} - -void renderer_vk::end_frame(GLFWwindow* window_handle) { - ImGuiIO& io = ImGui::GetIO(); - - // Rendering - ImGui::Render(); - ImDrawData* main_draw_data = ImGui::GetDrawData(); - const bool main_is_minimized = (main_draw_data->DisplaySize.x <= 0.0f || main_draw_data->DisplaySize.y <= 0.0f); - main_window_data.ClearValue.color.float32[0] = clear_color.x * clear_color.w; - main_window_data.ClearValue.color.float32[1] = clear_color.y * clear_color.w; - main_window_data.ClearValue.color.float32[2] = clear_color.z * clear_color.w; - main_window_data.ClearValue.color.float32[3] = clear_color.w; - if (!main_is_minimized) - frame_render(main_draw_data); - - // Update and Render additional Platform Windows - if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) - { - ImGui::UpdatePlatformWindows(); - ImGui::RenderPlatformWindowsDefault(); - } - - // Present Main Platform Window - if (!main_is_minimized) - frame_present(); -} - -void renderer_vk::resize(int width, int height) { -} - -std::shared_ptr renderer_vk::create_texture(const unsigned char* data, int width, int height) { - return nullptr; -} - -std::shared_ptr renderer_vk::create_render_target(int width, int height, texture_format format) { - return nullptr; -} - -void renderer_vk::init_vulkan(GLFWwindow* window_handle) { - std::vector extensions; - uint32_t extensions_count = 0; - const char** glfw_extensions = glfwGetRequiredInstanceExtensions(&extensions_count); - for (uint32_t i = 0; i < extensions_count; i++) - extensions.push_back(glfw_extensions[i]); - setup_vulkan(extensions); - - // Create Window Surface - VkSurfaceKHR surface; - VkResult err = glfwCreateWindowSurface(instance, window_handle, reinterpret_cast(allocator), &surface); - check_vk_result(err); - - // Create Framebuffers - int w, h; - glfwGetFramebufferSize(window_handle, &w, &h); - setup_vulkan_window(surface, w, h); - - ImGui_ImplGlfw_InitForVulkan(window_handle, true); - - ImGui_ImplVulkan_InitInfo init_info = {}; - init_info.Instance = instance; - init_info.PhysicalDevice = physical_device; - init_info.Device = device; - init_info.QueueFamily = queue_family; - init_info.Queue = queue; - init_info.PipelineCache = pipeline_cache; - init_info.DescriptorPool = descriptor_pool; - init_info.RenderPass = main_window_data.RenderPass; - init_info.Subpass = 0; - init_info.MinImageCount = min_image_count; - init_info.ImageCount = main_window_data.ImageCount; - init_info.MSAASamples = VK_SAMPLE_COUNT_1_BIT; - init_info.Allocator = reinterpret_cast(allocator); - init_info.CheckVkResultFn = check_vk_result; - ImGui_ImplVulkan_Init(&init_info); -} diff --git a/core/rhi/vulkan/renderer_vk.h b/core/rhi/vulkan/renderer_vk.h deleted file mode 100644 index 57b8161..0000000 --- a/core/rhi/vulkan/renderer_vk.h +++ /dev/null @@ -1,84 +0,0 @@ -// -// Created by 46944 on 2024/2/19. -// -#pragma once - -#include - -#include "imgui_impl_vulkan.h" -#include "rhi/renderer.h" - -static void check_vk_result(vk::Result err) { - if (err == vk::Result::eSuccess) - return; - - spdlog::error("[vulkan] Error: VkResult = {}", vk::to_string(err)); - abort(); -} - -static void check_vk_result(VkResult err) { - if (err == VK_SUCCESS) - return; - if (err < 0) { - spdlog::error("[vulkan] Error: VkResult = {}", err); - abort(); - } -} - -class renderer_vk : public renderer { -public: - void pre_init() override; - - bool init(GLFWwindow* window_handle) override; - - void shutdown() override; - - std::shared_ptr load_shader(const std::string& entry_name) override; - - std::shared_ptr create_pixel_shader_drawer() override; - - void new_frame(GLFWwindow* window_handle) override; - - void end_frame(GLFWwindow* window_handle) override; - - void resize(int width, int height) override; - - std::shared_ptr create_texture(const unsigned char* data, int width, int height) override; - - std::shared_ptr create_render_target(int width, int height, texture_format format) override; - - // Data - vk::AllocationCallbacks* allocator = nullptr; - vk::Instance instance = VK_NULL_HANDLE; - vk::PhysicalDevice physical_device = VK_NULL_HANDLE; - vk::Device device = VK_NULL_HANDLE; - uint32_t queue_family = (uint32_t) -1; - vk::Queue queue = VK_NULL_HANDLE; - vk::DescriptorPool descriptor_pool = VK_NULL_HANDLE; - vk::PipelineCache pipeline_cache = VK_NULL_HANDLE; - vk::CommandPool command_pool = VK_NULL_HANDLE; - - ImGui_ImplVulkanH_Window main_window_data; - int min_image_count = 2; - -protected: - [[nodiscard]] vk::PhysicalDevice setup_vulkan_select_physical_device() const; - - void setup_vulkan(std::vector instance_extensions); - - void setup_vulkan_window(VkSurfaceKHR surface, int width, int height); - - void cleanup_vulkan() const; - - void cleanup_vulkan_window(); - - void frame_render(ImDrawData* draw_data); - - void frame_present(); - -private: - void init_vulkan(GLFWwindow* window_handle); - - bool has_initialized_ = false; - bool swap_chain_rebuild_ = false; -}; diff --git a/core/rhi/vulkan/texture_vk.cpp b/core/rhi/vulkan/texture_vk.cpp deleted file mode 100644 index ab4bfb0..0000000 --- a/core/rhi/vulkan/texture_vk.cpp +++ /dev/null @@ -1,51 +0,0 @@ -#include "texture_vk.h" - -#include "renderer_vk.h" -#include "application/application.h" -#include "GLFW/glfw3.h" - -bool texture_vk::init_data(const unsigned char* data, int width, int height) { - auto vk_rder = static_cast(application::get()->get_renderer()); - auto& device = vk_rder->device; - - vk::ImageCreateInfo image_info; - image_info.setFormat(vk::Format::eR8G8B8A8Srgb); - image_info.setImageType(vk::ImageType::e2D); - image_info.setExtent({static_cast(width), static_cast(height), 1}); - image_info.setMipLevels(1); - image_info.setArrayLayers(1); - image_info.setSamples(vk::SampleCountFlagBits::e1); - image_ = device.createImage(image_info); - - vk::ImageViewCreateInfo view_info; - view_info.setImage(image_); - view_info.setViewType(vk::ImageViewType::e2D); - view_info.setFormat(vk::Format::eR8G8B8A8Srgb); - view_info.setSubresourceRange({vk::ImageAspectFlagBits::eColor, 0, 1, 0, 1}); - image_view_ = device.createImageView(view_info); - - vk::SamplerCreateInfo sampler_info; - sampler_info.setMagFilter(vk::Filter::eLinear); - sampler_info.setMinFilter(vk::Filter::eLinear); - sampler_info.setAddressModeU(vk::SamplerAddressMode::eClampToEdge); - sampler_info.setAddressModeV(vk::SamplerAddressMode::eClampToEdge); - sampler_info.setAddressModeW(vk::SamplerAddressMode::eClampToEdge); - sampler_info.setAnisotropyEnable(VK_FALSE); - sampler_info.setMaxAnisotropy(1); - sampler_info.setBorderColor(vk::BorderColor::eFloatOpaqueWhite); - sampler_info.setUnnormalizedCoordinates(VK_FALSE); - sampler_info.setCompareEnable(VK_FALSE); - sampler_info.setCompareOp(vk::CompareOp::eAlways); - sampler_info.setMipmapMode(vk::SamplerMipmapMode::eLinear); - sampler_ = device.createSampler(sampler_info); - - vk::BufferCreateInfo buffer_info; - buffer_info.setSize(width * height * 4); - buffer_info.setUsage(vk::BufferUsageFlagBits::eTransferSrc); - buffer_info.setSharingMode(vk::SharingMode::eExclusive); - buffer_ = device.createBuffer(buffer_info); - - - - return true; -} diff --git a/core/rhi/vulkan/texture_vk.h b/core/rhi/vulkan/texture_vk.h deleted file mode 100644 index 8b7b76a..0000000 --- a/core/rhi/vulkan/texture_vk.h +++ /dev/null @@ -1,17 +0,0 @@ -#pragma once -#include - -#include "rhi/texture.h" - - -class texture_vk : public texture { -public: - bool init_data(const unsigned char* data, int width, int height) override; - bool is_valid() const override { return image_view_; } - ImTextureID get_texture_id() override { return static_cast(image_view_); } -private: - vk::Image image_; - vk::ImageView image_view_; - vk::Sampler sampler_; - vk::Buffer buffer_; -};