diff --git a/modules/renderer/.clang-tidy b/modules/renderer/.clang-tidy index 56f5fef..cbd7bdf 100644 --- a/modules/renderer/.clang-tidy +++ b/modules/renderer/.clang-tidy @@ -128,7 +128,7 @@ cppcoreguidelines-pro-type-const-cast, cppcoreguidelines-pro-type-cstyle-cast, cppcoreguidelines-pro-type-member-init, cppcoreguidelines-pro-type-reinterpret-cast, -cppcoreguidelines-pro-type-static-cast-downcast, +-cppcoreguidelines-pro-type-static-cast-downcast, cppcoreguidelines-pro-type-vararg, cppcoreguidelines-rvalue-reference-param-not-moved, cppcoreguidelines-slicing, diff --git a/modules/renderer/.clangd b/modules/renderer/.clangd new file mode 100644 index 0000000..fa8caec --- /dev/null +++ b/modules/renderer/.clangd @@ -0,0 +1,3 @@ +Diagnostics: + UnusedIncludes: None + MissingIncludes: None diff --git a/modules/renderer/CMakeLists.txt b/modules/renderer/CMakeLists.txt index 6863120..7308b59 100644 --- a/modules/renderer/CMakeLists.txt +++ b/modules/renderer/CMakeLists.txt @@ -1,14 +1,21 @@ add_library_module(renderer system.cpp - vk/debug/messenger.cpp - vk/context/instance.cpp - vk/context/surface.cpp - vk/context/device.cpp - vk/context/swapchain.cpp - vk/context/context.cpp - vk/renderer/pass.cpp - vk/renderer/renderer.cpp - vk/pipeline.cpp + + # Vulkan - backend + backend/vk/messenger.cpp + backend/vk/context/context.cpp + backend/vk/context/device.cpp + backend/vk/context/gpu.cpp + backend/vk/context/instance.cpp + backend/vk/context/surface.cpp + backend/vk/context/swapchain.cpp + backend/vk/renderer/pass.cpp + backend/vk/renderer/renderer.cpp + + # Vulkan - frontend + frontend/messenger.cpp + frontend/context/context.cpp + frontend/renderer/renderer.cpp ) target_link_libraries(renderer @@ -23,21 +30,21 @@ PRIVATE pthread ) -add_test_module(renderer - system.test.cpp - vk/test_utils.cpp - vk/debug/messenger.test.cpp - vk/context/instance.test.cpp - vk/context/surface.test.cpp - vk/context/device.test.cpp - vk/context/swapchain.test.cpp - vk/context/context.test.cpp - vk/renderer/pass.test.cpp - vk/renderer/renderer.test.cpp - vk/pipeline.test.cpp -) -target_link_libraries(renderer_tests -PRIVATE - surface - pthread -) +# add_test_module(renderer +# system.test.cpp +# vk/test_utils.cpp +# vk/debug/messenger.test.cpp +# vk/context/instance.test.cpp +# vk/context/surface.test.cpp +# vk/context/device.test.cpp +# vk/context/swapchain.test.cpp +# vk/context/context.test.cpp +# vk/renderer/pass.test.cpp +# vk/renderer/renderer.test.cpp +# vk/pipeline.test.cpp +# ) +# target_link_libraries(renderer_tests +# PRIVATE +# surface +# pthread +# ) diff --git a/modules/renderer/private/backend/vk/context/context.cpp b/modules/renderer/private/backend/vk/context/context.cpp new file mode 100644 index 0000000..1a2a04d --- /dev/null +++ b/modules/renderer/private/backend/vk/context/context.cpp @@ -0,0 +1,40 @@ +#include +#include +#include +#include +#include +#include + +namespace lt::renderer::vk { + +Context::Context(const ecs::Entity &surface_entity) + : m_instance(Instance::get()) + , m_surface(create_scope(m_instance, surface_entity)) + , m_gpu(create_scope(m_instance)) + , m_device(create_scope(m_gpu.get(), m_surface.get())) + , m_swapchain(create_scope(m_surface.get(), m_gpu.get(), m_device.get())) +{ + ensure( + static_cast(m_instance)->vk(), + "Failed to create vulkan context: null instance" + ); + + ensure( + static_cast(m_surface.get())->vk(), + "Failed to create vulkan context: null surface" + ); + + ensure(static_cast(m_gpu.get())->vk(), "Failed to create vulkan context: null gpu"); + + ensure( + static_cast(m_device.get())->vk(), + "Failed to create vulkan context: null device" + ); + + ensure( + static_cast(m_swapchain.get())->vk(), + "Failed to create vulkan context: null swapchain" + ); +} + +} // namespace lt::renderer::vk diff --git a/modules/renderer/private/backend/vk/context/context.hpp b/modules/renderer/private/backend/vk/context/context.hpp new file mode 100644 index 0000000..b96ea5d --- /dev/null +++ b/modules/renderer/private/backend/vk/context/context.hpp @@ -0,0 +1,60 @@ +#pragma once + +#include +#include +#include + +namespace lt::renderer::vk { + +using memory::NullOnMove; + +/** A layer of glue between main graphics objects. */ +class Context: public IContext +{ +public: + Context(const ecs::Entity &surface_entity); + + [[nodiscard]] auto instance() const -> IInstance * override + { + return m_instance; + } + + [[nodiscard]] auto gpu() const -> IGpu * override + { + return m_gpu.get(); + } + + [[nodiscard]] auto device() const -> IDevice * override + { + return m_device.get(); + } + + [[nodiscard]] auto swapchain() const -> ISwapchain * override + { + return m_swapchain.get(); + } + + [[nodiscard]] auto surface() const -> ISurface * override + { + return m_surface.get(); + } + + void recreate_swapchain() override + { + m_swapchain.reset(); + // m_swapchain = create_scope(m_device, m_surface); + } + +private: + IInstance *m_instance; + + Scope m_surface; + + Scope m_gpu; + + Scope m_device; + + Scope m_swapchain; +}; + +} // namespace lt::renderer::vk diff --git a/modules/renderer/private/backend/vk/context/device.cpp b/modules/renderer/private/backend/vk/context/device.cpp new file mode 100644 index 0000000..3b069d8 --- /dev/null +++ b/modules/renderer/private/backend/vk/context/device.cpp @@ -0,0 +1,384 @@ +// +#include +#include +#include +#include + +// +#include + +namespace lt::renderer::vk { + +Device::Device(IGpu *gpu, ISurface *surface) + : m_gpu(static_cast(gpu)) + , m_surface(static_cast(surface)) +{ + ensure(m_surface->vk(), "Failed to initialize vk::Device: null vulkan surface"); + + initialize_queue_indices(); + initialize_logical_device(); + Instance::load_device_functions(m_device); + + vk_get_device_queue(m_device, m_graphics_queue_family_index, 0, &m_graphics_queue); + vk_get_device_queue(m_device, m_present_queue_family_index, 0, &m_present_queue); + + if (m_present_queue == m_graphics_queue) + { + name(m_present_queue, "graphics|present queue"); + } + else + { + name(m_graphics_queue, "graphics queue"); + name(m_present_queue, "present queue"); + } +} + +Device::~Device() +{ + if (!m_gpu) + { + return; + } + + vkc(vk_device_wait_idle(m_device)); + vk_destroy_device(m_device, nullptr); +} + +void Device::initialize_logical_device() +{ + const float priorities = .0f; + + auto queue_infos = std::vector {}; + auto queue_families = std::set { m_graphics_queue_family_index, m_present_queue_family_index }; + + for (auto queue_family : queue_families) + { + queue_infos.emplace_back( + VkDeviceQueueCreateInfo { + .sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO, + .queueFamilyIndex = queue_family, + .queueCount = 1u, + .pQueuePriorities = &priorities, + } + ); + } + + auto physical_device_features = VkPhysicalDeviceFeatures {}; + + auto extensions = std::vector { + VK_KHR_SWAPCHAIN_EXTENSION_NAME, + }; + + auto device_info = VkDeviceCreateInfo { + .sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO, + .queueCreateInfoCount = static_cast(queue_infos.size()), + .pQueueCreateInfos = queue_infos.data(), + .enabledExtensionCount = static_cast(extensions.size()), + .ppEnabledExtensionNames = extensions.data(), + .pEnabledFeatures = &physical_device_features, + }; + + ensure( + !vk_create_device(m_gpu->vk(), &device_info, nullptr, &m_device), + "Failed to create logical vulkan device" + ); +} + +void Device::initialize_queue_indices() +{ + auto properties = m_gpu->get_queue_family_properties(); + for (auto idx = uint32_t { 0u }; const auto &property : properties) + { + if (property.queueFlags & VK_QUEUE_GRAPHICS_BIT) + { + m_graphics_queue_family_index = idx; + } + + if (m_gpu->queue_family_supports_presentation(m_surface->vk(), idx)) + { + m_present_queue_family_index = idx; + } + + if (m_graphics_queue_family_index != VK_QUEUE_FAMILY_IGNORED + && m_present_queue_family_index != VK_QUEUE_FAMILY_IGNORED) + { + break; + } + + ++idx; + } + + ensure( + m_graphics_queue_family_index != VK_QUEUE_FAMILY_IGNORED, + "Failed to find graphics queue family" + ); + + ensure( + m_present_queue_family_index != VK_QUEUE_FAMILY_IGNORED, + "Failed to find presentation queue family" + ); +} + +void Device::submit(VkSubmitInfo info, VkFence fence) const +{ + vkc(vk_queue_submit(m_graphics_queue, 1u, &info, fence)); +} + +void Device::present(VkPresentInfoKHR info) const +{ + vk_queue_present_khr(m_present_queue, &info); +} + +void Device::wait_idle() const +{ + vkc(vk_device_wait_idle(m_device)); +} + +void Device::wait_for_fence(VkFence fence) const +{ + vkc(vk_wait_for_fences(m_device, 1u, &fence, true, std::numeric_limits::max())); +} + +void Device::reset_fence(VkFence fence) const +{ + vkc(vk_reset_fences(m_device, 1u, &fence)); +} + +void Device::reset_fences(std::span fences) const +{ + vkc(vk_reset_fences(m_device, fences.size(), fences.data())); +} + +auto Device::acquire_image(VkSwapchainKHR swapchain, VkSemaphore semaphore, uint64_t timeout) + -> std::optional +{ + auto image_idx = uint32_t {}; + const auto result = vk_acquire_next_image_khr( + m_device, + swapchain, + timeout, + semaphore, + VK_NULL_HANDLE, + &image_idx + ); + + if (result == VK_SUCCESS) + { + return image_idx; + } + + if (result == VK_SUBOPTIMAL_KHR || result == VK_ERROR_OUT_OF_DATE_KHR) + { + return {}; + } + + vkc(result); // throws + return {}; +} + +void Device::wait_for_fences(std::span fences) const +{ + vkc(vk_wait_for_fences( + m_device, + fences.size(), + fences.data(), + true, + std::numeric_limits::max() + )); +} + +[[nodiscard]] auto Device::get_swapchain_images(VkSwapchainKHR swapchain) const + -> std::vector +{ + auto count = uint32_t { 0u }; + vkc(vk_get_swapchain_images_khr(m_device, swapchain, &count, nullptr)); + ensure(count != 0u, "Failed to get swapchain images"); + + auto images = std::vector(count); + vkc(vk_get_swapchain_images_khr(m_device, swapchain, &count, images.data())); + return images; +} + +[[nodiscard]] auto Device::create_swapchain(VkSwapchainCreateInfoKHR info) const -> VkSwapchainKHR +{ + auto *swapchain = VkSwapchainKHR {}; + vkc(vk_create_swapchain_khr(m_device, &info, nullptr, &swapchain)); + return swapchain; +} + +[[nodiscard]] auto Device::create_framebuffer(VkFramebufferCreateInfo info) const -> VkFramebuffer +{ + auto *framebuffer = VkFramebuffer {}; + vkc(vk_create_frame_buffer(m_device, &info, m_allocator, &framebuffer)); + return framebuffer; +} + +[[nodiscard]] auto Device::create_image_view(VkImageViewCreateInfo info) const -> VkImageView +{ + auto *view = VkImageView {}; + vkc(vk_create_image_view(m_device, &info, m_allocator, &view)); + return view; +} + +[[nodiscard]] auto Device::create_graphics_pipeline(VkGraphicsPipelineCreateInfo info) const + -> VkPipeline +{ + auto *graphics_pipeline = VkPipeline {}; + vkc(vk_create_graphics_pipelines( + m_device, + VK_NULL_HANDLE, + 1u, + &info, + m_allocator, + &graphics_pipeline + + )); + return graphics_pipeline; +} + +[[nodiscard]] auto Device::create_pass(VkRenderPassCreateInfo info) const -> VkRenderPass +{ + auto *pass = VkRenderPass {}; + vkc(vk_create_render_pass(m_device, &info, nullptr, &pass)); + return pass; +} + +[[nodiscard]] auto Device::create_pipeline_layout(VkPipelineLayoutCreateInfo info) const + -> VkPipelineLayout +{ + auto *pipeline_layout = VkPipelineLayout {}; + vkc(vk_create_pipeline_layout(m_device, &info, nullptr, &pipeline_layout)); + return pipeline_layout; +} + +[[nodiscard]] auto Device::create_shader_module(VkShaderModuleCreateInfo info) const + -> VkShaderModule +{ + auto *module = VkShaderModule {}; + vkc(vk_create_shader_module(m_device, &info, m_allocator, &module)); + return module; +} + +[[nodiscard]] auto Device::create_command_pool(VkCommandPoolCreateInfo info) const -> VkCommandPool +{ + auto *command_pool = VkCommandPool {}; + vkc(vk_create_command_pool(m_device, &info, m_allocator, &command_pool)); + return command_pool; +} + +[[nodiscard]] auto Device::create_semaphores(uint32_t count) const -> std::vector +{ + auto info = VkSemaphoreCreateInfo { + .sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO, + }; + + auto semaphores = std::vector(count); + for (auto &semaphore : semaphores) + { + vk_create_semaphore(m_device, &info, m_allocator, &semaphore); + } + return semaphores; +} + +[[nodiscard]] auto Device::create_fences(VkFenceCreateInfo info, uint32_t count) const + -> std::vector +{ + auto fences = std::vector(count); + for (auto &fence : fences) + { + vk_create_fence(m_device, &info, m_allocator, &fence); + } + return fences; +} + +[[nodiscard]] auto Device::allocate_command_buffers(VkCommandBufferAllocateInfo info) const + -> std::vector +{ + auto command_buffers = std::vector(info.commandBufferCount); + vkc(vk_allocate_command_buffers(m_device, &info, command_buffers.data())); + return command_buffers; +} + +void Device::destroy_swapchain(VkSwapchainKHR swapchain) const +{ + vk_destroy_swapchain_khr(m_device, swapchain, m_allocator); +} + +void Device::destroy_framebuffer(VkFramebuffer framebuffer) const +{ + vk_destroy_frame_buffer(m_device, framebuffer, m_allocator); +} + +void Device::destroy_framebuffers(std::span framebuffers) const +{ + for (auto &framebuffer : framebuffers) + { + destroy_framebuffer(framebuffer); + } +} + +void Device::destroy_image_view(VkImageView image_view) const +{ + vk_destroy_image_view(m_device, image_view, m_allocator); +} + +void Device::destroy_image_views(std::span image_views) const +{ + for (auto &image_view : image_views) + { + destroy_image_view(image_view); + } +} + +void Device::destroy_pipeline(VkPipeline pipeline) const +{ + vk_destroy_pipeline(m_device, pipeline, m_allocator); +} + +void Device::destroy_pass(VkRenderPass pass) const +{ + vk_destroy_render_pass(m_device, pass, m_allocator); +} + +void Device::destroy_pipeline_layout(VkPipelineLayout pipeline_layout) const +{ + vk_destroy_pipeline_layout(m_device, pipeline_layout, m_allocator); +} + +void Device::destroy_shader_module(VkShaderModule shader_module) const +{ + vk_destroy_shader_module(m_device, shader_module, m_allocator); +} + +void Device::destroy_command_pool(VkCommandPool command_pool) const +{ + vk_destroy_command_pool(m_device, command_pool, m_allocator); +} + +void Device::destroy_semaphore(VkSemaphore semaphore) const +{ + vk_destroy_semaphore(m_device, semaphore, m_allocator); +} + +void Device::destroy_semaphores(std::span semaphores) const +{ + for (auto &semaphore : semaphores) + { + destroy_semaphore(semaphore); + } +} + +void Device::destroy_fence(VkFence fence) const +{ + vk_destroy_fence(m_device, fence, m_allocator); +} + +void Device::destroy_fences(std::span fences) const +{ + for (auto &fence : fences) + { + destroy_fence(fence); + } +} + +} // namespace lt::renderer::vk diff --git a/modules/renderer/private/backend/vk/context/device.hpp b/modules/renderer/private/backend/vk/context/device.hpp new file mode 100644 index 0000000..0d523b2 --- /dev/null +++ b/modules/renderer/private/backend/vk/context/device.hpp @@ -0,0 +1,221 @@ +#pragma once + +#include +#include +#include +#include +#include + +namespace lt::renderer::vk { + +class Device: public IDevice +{ +public: + Device(IGpu *gpu, ISurface *surface); + + ~Device() override; + + Device(Device &&) = default; + + Device(const Device &) = delete; + + auto operator=(Device &&) -> Device & = default; + + auto operator=(const Device &) const -> Device & = delete; + + [[nodiscard]] auto vk() const -> VkDevice + { + return m_device; + } + + [[nodiscard]] auto get_family_indices() const -> std::array + { + return { m_graphics_queue_family_index, m_present_queue_family_index }; + } + + [[nodiscard]] auto get_graphics_queue() const -> VkQueue + { + return m_graphics_queue; + } + + [[nodiscard]] auto get_present_queue() const -> VkQueue + { + return m_present_queue; + } + + /** utilities */ + template + void name(const T &object, std::format_string fmt, Args &&...args) + { + const auto name = std::format(fmt, std::forward(args)...); + auto info = VkDebugUtilsObjectNameInfoEXT { + .sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_OBJECT_NAME_INFO_EXT, + .objectType = get_object_type(object), + .objectHandle = (uint64_t)(object), + .pObjectName = name.c_str(), + }; + + vk_set_debug_object_name(m_device, &info); + } + + template + void name(const T &object, const char *name) + { + auto info = VkDebugUtilsObjectNameInfoEXT { + .sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_OBJECT_NAME_INFO_EXT, + .objectType = get_object_type(object), + .objectHandle = (uint64_t)(object), + .pObjectName = name, + }; + + vk_set_debug_object_name(m_device, &info); + } + + + /** work functions */ + void submit(VkSubmitInfo info, VkFence fence) const; + + void present(VkPresentInfoKHR info) const; + + void wait_idle() const; + + void wait_for_fence(VkFence fence) const; + + void wait_for_fences(std::span fences) const; + + void reset_fence(VkFence fence) const; + + void reset_fences(std::span fences) const; + + /** getter functions */ + [[nodiscard]] auto acquire_image( + VkSwapchainKHR swapchain, + VkSemaphore semaphore, + uint64_t timeout = 1'000'000 + ) -> std::optional; + + [[nodiscard]] auto get_swapchain_images(VkSwapchainKHR swapchain) const -> std::vector; + + /** create functions */ + [[nodiscard]] auto create_swapchain(VkSwapchainCreateInfoKHR info) const -> VkSwapchainKHR; + + [[nodiscard]] auto create_framebuffer(VkFramebufferCreateInfo info) const -> VkFramebuffer; + + [[nodiscard]] auto create_image_view(VkImageViewCreateInfo info) const -> VkImageView; + + [[nodiscard]] auto create_graphics_pipeline(VkGraphicsPipelineCreateInfo info) const + -> VkPipeline; + + [[nodiscard]] auto create_pass(VkRenderPassCreateInfo info) const -> VkRenderPass; + + [[nodiscard]] auto create_pipeline_layout(VkPipelineLayoutCreateInfo info) const + -> VkPipelineLayout; + + [[nodiscard]] auto create_shader_module(VkShaderModuleCreateInfo info) const -> VkShaderModule; + + [[nodiscard]] auto create_command_pool(VkCommandPoolCreateInfo info) const -> VkCommandPool; + + [[nodiscard]] auto create_semaphores(uint32_t count) const -> std::vector; + + [[nodiscard]] auto create_fences(VkFenceCreateInfo info, uint32_t count) const + -> std::vector; + + /** allocation functions */ + [[nodiscard]] auto allocate_command_buffers(VkCommandBufferAllocateInfo info) const + -> std::vector; + + /** destroy functions */ + void destroy_swapchain(VkSwapchainKHR swapchain) const; + + void destroy_framebuffer(VkFramebuffer framebuffer) const; + + void destroy_framebuffers(std::span framebuffers) const; + + void destroy_image_view(VkImageView image_view) const; + + void destroy_image_views(std::span image_views) const; + + void destroy_pipeline(VkPipeline pipeline) const; + + void destroy_pass(VkRenderPass pass) const; + + void destroy_pipeline_layout(VkPipelineLayout pipeline_layout) const; + + void destroy_shader_module(VkShaderModule shader_module) const; + + void destroy_command_pool(VkCommandPool command_pool) const; + + void destroy_semaphore(VkSemaphore semaphore) const; + + void destroy_semaphores(std::span semaphores) const; + + void destroy_fence(VkFence fence) const; + + void destroy_fences(std::span fences) const; + +private: + template + static auto get_object_type(const T &object) -> VkObjectType + { + std::ignore = object; + + if constexpr (std::is_same_v) + { + return VK_OBJECT_TYPE_QUEUE; + } + + if constexpr (std::is_same_v) + { + return VK_OBJECT_TYPE_FENCE; + } + + if constexpr (std::is_same_v) + { + return VK_OBJECT_TYPE_SEMAPHORE; + } + + if constexpr (std::is_same_v) + { + return VK_OBJECT_TYPE_SWAPCHAIN_KHR; + } + + if constexpr (std::is_same_v) + { + return VK_OBJECT_TYPE_IMAGE; + } + + if constexpr (std::is_same_v) + { + return VK_OBJECT_TYPE_IMAGE_VIEW; + } + + static_assert("invalid type"); + } + + + void initialize_physical_device(); + + void initialize_logical_device(); + + void initialize_queue_indices(); + + [[nodiscard]] auto find_suitable_queue_family() const -> uint32_t; + + memory::NullOnMove m_gpu {}; + + class Surface *m_surface {}; + + VkAllocationCallbacks *m_allocator = nullptr; + + VkDevice m_device = VK_NULL_HANDLE; + + VkQueue m_graphics_queue = VK_NULL_HANDLE; + + VkQueue m_present_queue = VK_NULL_HANDLE; + + uint32_t m_graphics_queue_family_index = VK_QUEUE_FAMILY_IGNORED; + + uint32_t m_present_queue_family_index = VK_QUEUE_FAMILY_IGNORED; +}; + +} // namespace lt::renderer::vk diff --git a/modules/renderer/private/backend/vk/context/gpu.cpp b/modules/renderer/private/backend/vk/context/gpu.cpp new file mode 100644 index 0000000..c6bdc81 --- /dev/null +++ b/modules/renderer/private/backend/vk/context/gpu.cpp @@ -0,0 +1,71 @@ +#include +#include +#include + +namespace lt::renderer::vk { + +Gpu::Gpu(IInstance *instance) +{ + auto gpus = static_cast(instance)->enumerate_gpus(); + + for (auto &gpu : gpus) + { + auto properties = VkPhysicalDeviceProperties {}; + auto features = VkPhysicalDeviceFeatures {}; + + vk_get_physical_device_properties(gpu, &properties); + vk_get_physical_device_features(gpu, &features); + + if (properties.deviceType == VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU + && features.geometryShader) + { + m_gpu = gpu; + } + } + ensure(m_gpu, "Failed to find any suitable Vulkan physical device"); +} + +[[nodiscard]] auto Gpu::get_queue_family_properties() const -> std::vector +{ + auto count = uint32_t { 0u }; + vk_get_physical_device_queue_family_properties(m_gpu, &count, nullptr); + + auto properties = std::vector(count); + vk_get_physical_device_queue_family_properties(m_gpu, &count, properties.data()); + + return properties; +} + +[[nodiscard]] auto Gpu::queue_family_supports_presentation( + VkSurfaceKHR surface, + uint32_t queue_family_idx +) -> bool +{ + auto supported = VkBool32 { false }; + vkc(vk_get_physical_device_surface_support(m_gpu, queue_family_idx, surface, &supported)); + + return supported; +} + +[[nodiscard]] auto Gpu::get_surface_capabilities(VkSurfaceKHR surface) const + -> VkSurfaceCapabilitiesKHR +{ + auto capabilities = VkSurfaceCapabilitiesKHR {}; + vkc(vk_get_physical_device_surface_capabilities(m_gpu, surface, &capabilities)); + return capabilities; +} + +[[nodiscard]] auto Gpu::get_surface_formats(VkSurfaceKHR surface) const + -> std::vector +{ + auto count = uint32_t { 0u }; + vkc(vk_get_physical_device_surface_formats(m_gpu, surface, &count, nullptr)); + + auto formats = std::vector(count); + vkc(vk_get_physical_device_surface_formats(m_gpu, surface, &count, formats.data())); + + return formats; +} + + +} // namespace lt::renderer::vk diff --git a/modules/renderer/private/backend/vk/context/gpu.hpp b/modules/renderer/private/backend/vk/context/gpu.hpp new file mode 100644 index 0000000..9fc3868 --- /dev/null +++ b/modules/renderer/private/backend/vk/context/gpu.hpp @@ -0,0 +1,37 @@ +#pragma once + +#include +#include +#include +#include + +namespace lt::renderer::vk { + +class Gpu: public IGpu +{ +public: + Gpu(IInstance *instance); + + [[nodiscard]] auto vk() const -> VkPhysicalDevice + { + return m_gpu; + } + + [[nodiscard]] auto get_queue_family_properties() const -> std::vector; + + [[nodiscard]] auto queue_family_supports_presentation( + VkSurfaceKHR surface, + uint32_t queue_family_idx + ) -> bool; + + [[nodiscard]] auto get_surface_capabilities(VkSurfaceKHR surface) const + -> VkSurfaceCapabilitiesKHR; + + [[nodiscard]] auto get_surface_formats(VkSurfaceKHR surface) const + -> std::vector; + +private: + memory::NullOnMove m_gpu = VK_NULL_HANDLE; +}; + +} // namespace lt::renderer::vk diff --git a/modules/renderer/private/vk/context/instance.cpp b/modules/renderer/private/backend/vk/context/instance.cpp similarity index 83% rename from modules/renderer/private/vk/context/instance.cpp rename to modules/renderer/private/backend/vk/context/instance.cpp index 88082c6..c91754e 100644 --- a/modules/renderer/private/vk/context/instance.cpp +++ b/modules/renderer/private/backend/vk/context/instance.cpp @@ -1,6 +1,6 @@ #include -#include -#include +#include +#include #if defined(_WIN32) #error "Unsupported platform (currently)" @@ -92,23 +92,10 @@ PFN_vkGetPhysicalDeviceSurfaceSupportKHR vk_get_physical_device_surface_support PFN_vkGetPhysicalDeviceSurfaceCapabilitiesKHR vk_get_physical_device_surface_capabilities {}; PFN_vkGetPhysicalDeviceSurfaceFormatsKHR vk_get_physical_device_surface_formats {}; -PFN_vkCreateXlibSurfaceKHR vk_create_xlib_surface_khr {}; -PFN_vkDestroySurfaceKHR vk_destroy_surface_khr {}; +auto vk_create_xlib_surface_khr = PFN_vkCreateXlibSurfaceKHR {}; +auto vk_destroy_surface_khr = PFN_vkDestroySurfaceKHR {}; // NOLINTEND(cppcoreguidelines-avoid-non-const-global-variables) -auto parse_message_type(VkDebugUtilsMessageTypeFlagsEXT message_types) -> const char *; - -auto parse_message_severity(VkDebugUtilsMessageSeverityFlagBitsEXT message_severity) - -> app::SystemDiagnosis::Severity; - -auto validation_layers_callback( - VkDebugUtilsMessageSeverityFlagBitsEXT message_severity, - VkDebugUtilsMessageTypeFlagsEXT message_types, - VkDebugUtilsMessengerCallbackDataEXT const *callback_data, - void *vulkan_user_data -) -> VkBool32; - - Instance::Instance() { load_library(); @@ -375,84 +362,41 @@ void Instance::load_device_functions_impl(VkDevice device) load_fn(vk_reset_command_buffer, "vkResetCommandBuffer"); } -auto parse_message_type(VkDebugUtilsMessageTypeFlagsEXT message_types) -> const char * +auto Instance::enumerate_gpus() const -> std::vector { - if (message_types == VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT) - { - return "GENERAL"; - } + auto count = 0u; + vkc(vk_enumerate_physical_devices(m_instance, &count, nullptr)); + ensure(count != 0u, "Failed to find any gpus with Vulkan support"); - if (message_types - == (VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT - | VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT)) - { - return "VALIDATION | PERFORMANCE"; - } - - if (message_types == VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT) - { - return "VALIDATION"; - } - - return "PERFORMANCE"; + auto gpus = std::vector(count); + vkc(vk_enumerate_physical_devices(m_instance, &count, gpus.data())); + return gpus; } -auto parse_message_severity(VkDebugUtilsMessageSeverityFlagBitsEXT message_severity) - -> app::SystemDiagnosis::Severity +auto Instance::create_xlib_surface(VkXlibSurfaceCreateInfoKHR info) const -> VkSurfaceKHR { - using enum app::SystemDiagnosis::Severity; + auto *value = VkSurfaceKHR {}; + vk_create_xlib_surface_khr(m_instance, &info, m_allocator, &value); - switch (message_severity) - { - case VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT: return verbose; - case VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT: return info; - case VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT: return warning; - case VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT: return error; - default: ensure(false, "Invalid message severity: {}", static_cast(message_severity)); - } - - return {}; + return value; } -auto validation_layers_callback( - VkDebugUtilsMessageSeverityFlagBitsEXT const message_severity, - VkDebugUtilsMessageTypeFlagsEXT const message_types, - VkDebugUtilsMessengerCallbackDataEXT const *const callback_data, - void *const vulkan_user_data -) -> VkBool32 +[[nodiscard]] auto Instance::create_messenger(VkDebugUtilsMessengerCreateInfoEXT info) const + -> VkDebugUtilsMessengerEXT { - std::cout << callback_data->pMessage << std::endl; - return VK_FALSE; + auto *messenger = VkDebugUtilsMessengerEXT {}; + vkc(vk_create_debug_messenger(m_instance, &info, m_allocator, &messenger)); + return messenger; +} - log_dbg("VALIDATION: {}", callback_data->pMessage); +void Instance::destroy_surface(VkSurfaceKHR surface) const +{ + vk_destroy_surface_khr(m_instance, surface, m_allocator); +} - ensure(vulkan_user_data, "Validation layers's user data is not set!"); - - auto stats = *(Ref *)vulkan_user_data; // NOLINT - - const auto &type = parse_message_type(message_types); - - auto message = std::format( - "Vulkan Validation Message:\ntype: {}\nseverity: {}\nmessage: {}", - type, - std::to_underlying(parse_message_severity(message_severity)), - callback_data->pMessage - ); - - auto severity = parse_message_severity(message_severity); - if (std::to_underlying(severity) < 2) - { - return static_cast(VK_FALSE); - } - - stats->push_diagnosis( - app::SystemDiagnosis { - .message = message, - .code = {}, // TODO(Light): extract vulkan validation-layers code from the message - .severity = severity, - } - ); - return static_cast(VK_FALSE); +void Instance::destroy_messenger(VkDebugUtilsMessengerEXT messenger) const +{ + vk_destroy_debug_messenger(m_instance, messenger, m_allocator); } } // namespace lt::renderer::vk diff --git a/modules/renderer/private/vk/context/instance.hpp b/modules/renderer/private/backend/vk/context/instance.hpp similarity index 53% rename from modules/renderer/private/vk/context/instance.hpp rename to modules/renderer/private/backend/vk/context/instance.hpp index 157d465..a4b337a 100644 --- a/modules/renderer/private/vk/context/instance.hpp +++ b/modules/renderer/private/backend/vk/context/instance.hpp @@ -1,31 +1,34 @@ #pragma once -#include +#include +#include namespace lt::renderer::vk { /** * Responsible for dynamically loading Vulkan library/functions. * - * @note: The delayed vkInstance destruction is due to a work-around for libx11: + * @note: The delayed vkInstance destruction is due to a work-around for a libx11 quirk: * @ref: * https://github.com/KhronosGroup/Vulkan-LoaderAndValidationLayers/commit/0017308648b6bf8eef10ef0ffb9470576c0c2e9e * https://www.xfree86.org/4.7.0/DRI11.html * https://github.com/KhronosGroup/Vulkan-LoaderAndValidationLayers/issues/1894 */ -class Instance +class Instance: public IInstance { public: - static auto get() -> VkInstance + static auto get() -> IInstance * { - return Instance::instance().m_instance; + return &Instance::instance(); } static auto load_device_functions(VkDevice device) { - instance().load_device_functions_impl(device); + static_cast(instance()).load_device_functions_impl(device); } + ~Instance() override; + Instance(Instance &&) = delete; Instance(const Instance &) = delete; @@ -34,8 +37,26 @@ public: auto operator=(Instance &&) -> Instance & = delete; + [[nodiscard]] auto vk() const -> VkInstance + { + return m_instance; + } + + /* create functions */ + [[nodiscard]] auto create_xlib_surface(VkXlibSurfaceCreateInfoKHR info) const -> VkSurfaceKHR; + + [[nodiscard]] auto create_messenger(VkDebugUtilsMessengerCreateInfoEXT info) const + -> VkDebugUtilsMessengerEXT; + + /* destroy functions */ + void destroy_surface(VkSurfaceKHR surface) const; + + void destroy_messenger(VkDebugUtilsMessengerEXT messenger) const; + + [[nodiscard]] auto enumerate_gpus() const -> std::vector; + private: - static auto instance() -> Instance & + static auto instance() -> IInstance & { static auto instance = Instance {}; return instance; @@ -43,8 +64,6 @@ private: Instance(); - ~Instance(); - void initialize_instance(); void load_library(); @@ -58,6 +77,8 @@ private: void load_device_functions_impl(VkDevice device); VkInstance m_instance = VK_NULL_HANDLE; + + VkAllocationCallbacks *m_allocator = nullptr; }; } // namespace lt::renderer::vk diff --git a/modules/renderer/private/vk/context/surface.cpp b/modules/renderer/private/backend/vk/context/surface.cpp similarity index 51% rename from modules/renderer/private/vk/context/surface.cpp rename to modules/renderer/private/backend/vk/context/surface.cpp index 05e378d..6e15a73 100644 --- a/modules/renderer/private/vk/context/surface.cpp +++ b/modules/renderer/private/backend/vk/context/surface.cpp @@ -1,31 +1,35 @@ -#include +#include +#include #include namespace lt::renderer::vk { -Surface::Surface(ecs::Entity surface_entity): m_surface_entity(surface_entity) +Surface::Surface(IInstance *instance, const ecs::Entity &surface_entity) + : m_surface_entity(surface_entity) + , m_instance(static_cast(instance)) { const auto &component = surface_entity.get(); ensure(component.get_native_data().display, "Failed to initialize vk::Surface: null x-display"); ensure(component.get_native_data().window, "Failed to initialize vk::Surface: null x-window"); - auto create_info = VkXlibSurfaceCreateInfoKHR { - .sType = VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR, - .dpy = component.get_native_data().display, - .window = component.get_native_data().window, - }; - - auto *instance = Instance::get(); - auto result = vk_create_xlib_surface_khr(instance, &create_info, nullptr, &m_surface); + m_surface = m_instance->create_xlib_surface( + VkXlibSurfaceCreateInfoKHR { + .sType = VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR, + .dpy = component.get_native_data().display, + .window = component.get_native_data().window, + } + ); } Surface::~Surface() { - if (Instance::get()) + if (!m_instance) { - vk_destroy_surface_khr(Instance::get(), m_surface, nullptr); + return; } + + m_instance->destroy_surface(m_surface); } [[nodiscard]] auto Surface::get_framebuffer_size() const -> VkExtent2D diff --git a/modules/renderer/private/vk/context/surface.hpp b/modules/renderer/private/backend/vk/context/surface.hpp similarity index 60% rename from modules/renderer/private/vk/context/surface.hpp rename to modules/renderer/private/backend/vk/context/surface.hpp index 82abc39..31aae37 100644 --- a/modules/renderer/private/vk/context/surface.hpp +++ b/modules/renderer/private/backend/vk/context/surface.hpp @@ -2,16 +2,20 @@ #include #include -#include +#include +#include +#include namespace lt::renderer::vk { -class Surface +class Instance; + +class Surface: public ISurface { public: - Surface(ecs::Entity surface_entity); + Surface(IInstance *instance, const ecs::Entity &surface_entity); - ~Surface(); + ~Surface() override; Surface(Surface &&) = default; @@ -29,7 +33,9 @@ public: [[nodiscard]] auto get_framebuffer_size() const -> VkExtent2D; private: - memory::NullOnMove m_surface = VK_NULL_HANDLE; + class Instance *m_instance {}; + + VkSurfaceKHR m_surface = VK_NULL_HANDLE; ecs::Entity m_surface_entity; }; diff --git a/modules/renderer/private/backend/vk/context/swapchain.cpp b/modules/renderer/private/backend/vk/context/swapchain.cpp new file mode 100644 index 0000000..54830c3 --- /dev/null +++ b/modules/renderer/private/backend/vk/context/swapchain.cpp @@ -0,0 +1,152 @@ +#include +#include +#include +#include +#include +#include +#include + +namespace lt::renderer::vk { + +Swapchain::Swapchain(ISurface *surface, IGpu *gpu, IDevice *device) + : m_surface(static_cast(surface)) + , m_gpu(static_cast(gpu)) + , m_device(static_cast(device)) +{ + static auto idx = 0u; + + const auto capabilities = m_gpu->get_surface_capabilities(m_surface->vk()); + const auto formats = m_gpu->get_surface_formats(m_surface->vk()); + + // TODO(Light): parameterize + constexpr auto desired_image_count = uint32_t { 3 }; + const auto surface_format = formats.front(); + const auto queue_indices = m_device->get_family_indices(); + m_format = surface_format.format; + + m_swapchain = m_device->create_swapchain( + VkSwapchainCreateInfoKHR { + .sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR, + .surface = m_surface->vk(), + .minImageCount = get_optimal_image_count(capabilities, desired_image_count), + .imageFormat = surface_format.format, + .imageColorSpace = surface_format.colorSpace, + .imageExtent = capabilities.currentExtent, + .imageArrayLayers = 1u, + .imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT, + .imageSharingMode = VK_SHARING_MODE_EXCLUSIVE, + .queueFamilyIndexCount = queue_indices.size(), + .pQueueFamilyIndices = queue_indices.data(), + .preTransform = capabilities.currentTransform, + .compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR, + .presentMode = VK_PRESENT_MODE_FIFO_KHR, // TODO(Light): parameterize + .clipped = VK_TRUE, + .oldSwapchain = nullptr, + } + ); + m_device->name(m_swapchain, "swapchain {}", idx++); + m_device->wait_idle(); + + + m_images = m_device->get_swapchain_images(m_swapchain); + m_image_views.resize(m_images.size()); + for (auto idx = 0u; auto [image, view] : std::views::zip(m_images, m_image_views)) + { + view = m_device->create_image_view(VkImageViewCreateInfo { + .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, + .image = image, + .viewType = VK_IMAGE_VIEW_TYPE_2D, + .format = surface_format.format, + .components = VkComponentMapping { + .r = VK_COMPONENT_SWIZZLE_IDENTITY, + .g = VK_COMPONENT_SWIZZLE_IDENTITY, + .b = VK_COMPONENT_SWIZZLE_IDENTITY, + .a = VK_COMPONENT_SWIZZLE_IDENTITY, + }, + .subresourceRange = VkImageSubresourceRange { + .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, + .baseMipLevel = 0u, + .levelCount = 1u, + .baseArrayLayer = 0u, + .layerCount = 1u, + } + }); + + m_device->name(image, "swapchain image {}", idx++); + m_device->name(view, "swapchain image view {}", idx++); + } +} + +Swapchain::~Swapchain() +{ + if (!m_surface) + { + return; + } + + try + { + m_device->wait_idle(); + m_device->destroy_image_views(m_image_views); + m_device->destroy_swapchain(m_swapchain); + } + catch (const std::exception &exp) + { + log_err("Failed to destroy swapchain:"); + log_err("\twhat: {}", exp.what()); + } +} + + +[[nodiscard]] auto Swapchain::create_framebuffers_for_pass(VkRenderPass pass) const + -> std::vector +{ + auto framebuffers = std::vector(m_image_views.size()); + + for (auto idx = 0u; auto &framebuffer : framebuffers) + { + framebuffer = m_device->create_framebuffer( + VkFramebufferCreateInfo { + .sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO, + .renderPass = pass, + .attachmentCount = 1u, + .pAttachments = &m_image_views[idx++], + .width = m_resolution.width, + .height = m_resolution.height, + .layers = 1u, + } + ); + } + + return framebuffers; +} + +[[nodiscard]] auto Swapchain::get_optimal_image_count( + VkSurfaceCapabilitiesKHR capabilities, + uint32_t desired_image_count +) const -> uint32_t +{ + const auto min_image_count = capabilities.minImageCount; + const auto max_image_count = capabilities.maxImageCount; + + const auto has_max_limit = max_image_count != 0; + + // Desired image count is in range + if ((!has_max_limit || max_image_count >= desired_image_count) + && min_image_count <= desired_image_count) + { + return desired_image_count; + } + + // Fall-back to 2 if in ange + if (min_image_count <= 2 && max_image_count >= 2) + { + return 2; + } + + // Fall-back to min_image_count + return min_image_count; +} + + +} // namespace lt::renderer::vk diff --git a/modules/renderer/private/backend/vk/context/swapchain.hpp b/modules/renderer/private/backend/vk/context/swapchain.hpp new file mode 100644 index 0000000..6a0bcd4 --- /dev/null +++ b/modules/renderer/private/backend/vk/context/swapchain.hpp @@ -0,0 +1,78 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +namespace lt::renderer::vk { + +class Swapchain: public ISwapchain +{ +public: + Swapchain(ISurface *surface, IGpu *gpu, IDevice *device); + ~Swapchain() override; + + Swapchain(Swapchain &&) = default; + + Swapchain(const Swapchain &) = delete; + + auto operator=(Swapchain &&) -> Swapchain & = default; + + auto operator=(const Swapchain &) const -> Swapchain & = delete; + + [[nodiscard]] auto vk() const -> VkSwapchainKHR + { + return m_swapchain; + } + + [[nodiscard]] auto vk_ptr() -> VkSwapchainKHR * + { + return &m_swapchain; + } + + [[nodiscard]] auto get_resolution() const -> VkExtent2D + { + return m_resolution; + } + + [[nodiscard]] auto get_format() const -> VkFormat + { + return m_format; + } + + [[nodiscard]] auto get_image_count() const -> size_t + { + return m_images.size(); + } + + [[nodiscard]] auto create_framebuffers_for_pass(VkRenderPass pass) const + -> std::vector; + +private: + [[nodiscard]] auto get_optimal_image_count( + VkSurfaceCapabilitiesKHR capabilities, + uint32_t desired_image_count + ) const -> uint32_t; + + memory::NullOnMove m_surface {}; + + class Gpu *m_gpu {}; + + class Device *m_device {}; + + VkSwapchainKHR m_swapchain = VK_NULL_HANDLE; + + std::vector m_images; + + std::vector m_image_views; + + VkExtent2D m_resolution {}; + + VkFormat m_format {}; +}; + +} // namespace lt::renderer::vk diff --git a/modules/renderer/private/backend/vk/messenger.cpp b/modules/renderer/private/backend/vk/messenger.cpp new file mode 100644 index 0000000..055cfba --- /dev/null +++ b/modules/renderer/private/backend/vk/messenger.cpp @@ -0,0 +1,177 @@ +#include + +namespace lt::renderer::vk { + +Messenger::Messenger(IInstance *instance, ecs::Entity entity) + : m_instance(static_cast(instance)) + , m_entity(std::move(entity)) + +{ + const auto &component = entity.get(); + + m_debug_messenger = m_instance->create_messenger( + VkDebugUtilsMessengerCreateInfoEXT { + .sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT, + .messageSeverity = to_native_severity(component.severities), + .messageType = to_native_type(component.types), + .pfnUserCallback = &native_callback, + .pUserData = this, + } + ); +} + +Messenger::~Messenger() +{ + if (!m_instance) + { + return; + } + + m_instance->destroy_messenger(m_debug_messenger); +} + +/*static*/ auto Messenger::native_callback( + VkDebugUtilsMessageSeverityFlagBitsEXT severity, + VkDebugUtilsMessageTypeFlagsEXT type, + const VkDebugUtilsMessengerCallbackDataEXT *callback_data, + void *vulkan_user_data +) -> VkBool32 +{ + try + { + ensure(vulkan_user_data, "Null vulkan_user_data received in messenger callback"); + + auto *messenger = (Messenger *)vulkan_user_data; // NOLINT + auto &component = messenger->m_entity.get(); + component.callback( + from_native_severity(severity), + from_native_type(type), + { + .message = callback_data->pMessage, + }, + component.user_data + ); + } + catch (const std::exception &exp) + { + log_err("Uncaught exception in messenger callback:"); + log_err("\twhat: {}", exp.what()); + } + + return VK_FALSE; +} + +[[nodiscard]] /*static*/ auto Messenger::to_native_severity(MessageSeverity severity) + -> VkDebugUtilsMessageSeverityFlagsEXT +{ + using enum MessageSeverity; + + const auto value = std::to_underlying(severity); + auto flags = VkDebugUtilsMessageSeverityFlagsEXT {}; + + if (value & std::to_underlying(error)) + { + flags |= VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT; + } + + if (value & std::to_underlying(warning)) + { + flags |= VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT; + } + + if (value & std::to_underlying(info)) + { + flags |= VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT; + } + + if (value & std::to_underlying(verbose)) + { + flags |= VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT; + } + + return flags; +} + +[[nodiscard]] /*static*/ auto Messenger::from_native_severity( + VkDebugUtilsMessageSeverityFlagsEXT severity +) -> MessageSeverity +{ + using enum MessageSeverity; + + auto flags = std::underlying_type_t {}; + + if (severity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT) + { + flags &= std::to_underlying(error); + } + + if (severity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT) + { + flags &= std::to_underlying(warning); + } + + if (severity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT) + { + flags &= std::to_underlying(info); + } + + if (severity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT) + { + flags &= std::to_underlying(verbose); + } + + return static_cast(flags); +} + +[[nodiscard]] /*static*/ auto Messenger::to_native_type(MessageType type) + -> VkDebugUtilsMessageTypeFlagsEXT +{ + using enum MessageType; + + const auto value = std::to_underlying(type); + auto flags = VkDebugUtilsMessageTypeFlagsEXT {}; + + if (value & std::to_underlying(general)) + { + flags |= VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT; + } + + if (value & std::to_underlying(validation)) + { + flags |= VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT; + } + + if (value & std::to_underlying(performance)) + { + flags |= VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT; + } + + return flags; +} + +[[nodiscard]] /* static */ auto Messenger::from_native_type(VkDebugUtilsMessageTypeFlagsEXT type) + -> MessageType +{ + using enum MessageType; + + auto flags = std::underlying_type_t {}; + + if (type & VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT) + { + flags |= std::to_underlying(general); + } + + if (type & VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT) + { + flags |= std::to_underlying(validation); + } + + if (type & VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT) + { + flags |= std::to_underlying(general); + } + + return static_cast(flags); +} + +} // namespace lt::renderer::vk diff --git a/modules/renderer/private/backend/vk/messenger.hpp b/modules/renderer/private/backend/vk/messenger.hpp new file mode 100644 index 0000000..38ff399 --- /dev/null +++ b/modules/renderer/private/backend/vk/messenger.hpp @@ -0,0 +1,57 @@ +#pragma once + +#include +#include +#include +#include +#include +#include + +namespace lt::renderer::vk { + +class Messenger: public IMessenger +{ +public: + Messenger(IInstance *instance, ecs::Entity entity); + + ~Messenger() override; + + Messenger(Messenger &&) = default; + + Messenger(const Messenger &) = delete; + + auto operator=(Messenger &&) -> Messenger & = default; + + auto operator=(const Messenger &) const -> Messenger & = delete; + + +private: + static auto native_callback( + VkDebugUtilsMessageSeverityFlagBitsEXT severity, + VkDebugUtilsMessageTypeFlagsEXT type, + const VkDebugUtilsMessengerCallbackDataEXT *callback_data, + void *vulkan_user_data + ) -> VkBool32; + + [[nodiscard]] static auto to_native_severity(MessageSeverity severity) + -> VkDebugUtilsMessageSeverityFlagsEXT; + + [[nodiscard]] static auto from_native_severity(VkDebugUtilsMessageSeverityFlagsEXT severity) + -> MessageSeverity; + + [[nodiscard]] static auto to_native_type(MessageType type) -> VkDebugUtilsMessageTypeFlagsEXT; + + [[nodiscard]] static auto from_native_type(VkDebugUtilsMessageTypeFlagsEXT type) -> MessageType; + + memory::NullOnMove m_instance {}; + + ecs::Entity m_entity; + + VkDebugUtilsMessengerEXT m_debug_messenger = VK_NULL_HANDLE; + + MessageSeverity m_severities {}; + + MessageType m_types {}; +}; + +} // namespace lt::renderer::vk diff --git a/modules/renderer/private/vk/debug/messenger.test.cpp b/modules/renderer/private/backend/vk/messenger.test.cpp similarity index 96% rename from modules/renderer/private/vk/debug/messenger.test.cpp rename to modules/renderer/private/backend/vk/messenger.test.cpp index 17c918c..7136b88 100644 --- a/modules/renderer/private/vk/debug/messenger.test.cpp +++ b/modules/renderer/private/backend/vk/messenger.test.cpp @@ -1,6 +1,6 @@ -#include -#include -#include +#include +#include +#include #include #include #include diff --git a/modules/renderer/private/backend/vk/renderer/pass.cpp b/modules/renderer/private/backend/vk/renderer/pass.cpp new file mode 100644 index 0000000..4f04e3e --- /dev/null +++ b/modules/renderer/private/backend/vk/renderer/pass.cpp @@ -0,0 +1,225 @@ +#include +#include +#include + +namespace lt::renderer::vk { + +Pass::Pass( + IContext &context, + lt::assets::ShaderAsset vertex_shader, + lt::assets::ShaderAsset fragment_shader +) + : m_device(static_cast(context.device())) +{ + auto *vertex_module = create_module( + vertex_shader.unpack(lt::assets::ShaderAsset::BlobTag::code) + ); + + auto *fragment_module = create_module( + fragment_shader.unpack(lt::assets::ShaderAsset::BlobTag::code) + ); + + auto shader_stages = std::array { + VkPipelineShaderStageCreateInfo { + .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, + .stage = VK_SHADER_STAGE_VERTEX_BIT, + .module = vertex_module, + .pName = "main", + }, + VkPipelineShaderStageCreateInfo { + .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, + .stage = VK_SHADER_STAGE_FRAGMENT_BIT, + .module = fragment_module, + .pName = "main", + }, + }; + + auto dynamic_states = std::array { + VK_DYNAMIC_STATE_VIEWPORT, + VK_DYNAMIC_STATE_SCISSOR, + }; + + auto dynamic_state = VkPipelineDynamicStateCreateInfo { + .sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO, + .dynamicStateCount = static_cast(dynamic_states.size()), + .pDynamicStates = dynamic_states.data(), + }; + + auto vertex_input = VkPipelineVertexInputStateCreateInfo { + .sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO, + }; + + auto input_assembly = VkPipelineInputAssemblyStateCreateInfo { + .sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO, + .topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, + .primitiveRestartEnable = VK_FALSE, + }; + + auto viewport_state = VkPipelineViewportStateCreateInfo { + .sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO, + .viewportCount = 1u, + .scissorCount = 1u, + }; + + auto rasterization = VkPipelineRasterizationStateCreateInfo { + .sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO, + .depthClampEnable = VK_FALSE, + .rasterizerDiscardEnable = VK_FALSE, + .polygonMode = VK_POLYGON_MODE_FILL, + .cullMode = VK_CULL_MODE_NONE, + .frontFace = VK_FRONT_FACE_CLOCKWISE, + .lineWidth = 1.0, + }; + + auto multisampling = VkPipelineMultisampleStateCreateInfo { + .sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO, + .rasterizationSamples = VK_SAMPLE_COUNT_1_BIT, + .sampleShadingEnable = VK_FALSE, + .minSampleShading = 1.0, + .pSampleMask = nullptr, + .alphaToCoverageEnable = VK_FALSE, + .alphaToOneEnable = VK_FALSE, + }; + + auto color_blend_attachment = VkPipelineColorBlendAttachmentState { + .blendEnable = VK_FALSE, + .srcColorBlendFactor = VK_BLEND_FACTOR_ONE, + .dstColorBlendFactor = VK_BLEND_FACTOR_ZERO, + .colorBlendOp = VK_BLEND_OP_ADD, + .srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE, + .dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO, + .alphaBlendOp = VK_BLEND_OP_ADD, + .colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT + | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT, + }; + + auto color_blend = VkPipelineColorBlendStateCreateInfo { + .sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO, + .logicOpEnable = VK_FALSE, + .logicOp = VK_LOGIC_OP_COPY, + .attachmentCount = 1, + .pAttachments = &color_blend_attachment, + .blendConstants = { 0.0f, 0.0, 0.0, 0.0 }, + }; + + m_layout = m_device->create_pipeline_layout( + VkPipelineLayoutCreateInfo { + .sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, + .setLayoutCount = 0u, + .pSetLayouts = nullptr, + .pushConstantRangeCount = 0u, + .pPushConstantRanges = nullptr, + } + ); + + auto attachment_description = VkAttachmentDescription { + .format = static_cast(context.swapchain())->get_format(), + .samples = VK_SAMPLE_COUNT_1_BIT, + .loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR, + .storeOp = VK_ATTACHMENT_STORE_OP_STORE, + .stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE, + .stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE, + .initialLayout = VK_IMAGE_LAYOUT_UNDEFINED, + .finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, + }; + + auto color_attachment_ref = VkAttachmentReference { + .attachment = 0, + .layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, + }; + + auto subpass_description = VkSubpassDescription { + .pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS, + .colorAttachmentCount = 1u, + .pColorAttachments = &color_attachment_ref, + }; + + auto pass_dependency = VkSubpassDependency { + .srcSubpass = VK_SUBPASS_EXTERNAL, + .dstSubpass = 0u, + .srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, + .dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, + .srcAccessMask = 0u, + .dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, + }; + + m_pass = m_device->create_pass( + VkRenderPassCreateInfo { + .sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO, + .attachmentCount = 1u, + .pAttachments = &attachment_description, + .subpassCount = 1u, + .pSubpasses = &subpass_description, + .dependencyCount = 1u, + .pDependencies = &pass_dependency, + } + ); + + m_pipeline = m_device->create_graphics_pipeline( + VkGraphicsPipelineCreateInfo { + .sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO, + .stageCount = static_cast(shader_stages.size()), + .pStages = shader_stages.data(), + .pVertexInputState = &vertex_input, + .pInputAssemblyState = &input_assembly, + .pViewportState = &viewport_state, + .pRasterizationState = &rasterization, + .pMultisampleState = &multisampling, + .pDepthStencilState = nullptr, + .pColorBlendState = &color_blend, + .pDynamicState = &dynamic_state, + .layout = m_layout, + .renderPass = m_pass, + .subpass = 0u, + .basePipelineHandle = VK_NULL_HANDLE, + .basePipelineIndex = -1, + } + ); + + m_framebuffers = static_cast(context.swapchain()) + ->create_framebuffers_for_pass(m_pass); + + + m_device->destroy_shader_module(vertex_module); + m_device->destroy_shader_module(fragment_module); +} + +Pass::~Pass() +{ + if (!m_device) + { + return; + } + + m_device->wait_idle(); + m_device->destroy_framebuffers(m_framebuffers); + m_device->destroy_pipeline(m_pipeline); + m_device->destroy_pass(m_pass); + m_device->destroy_pipeline_layout(m_layout); +} + +void Pass::replace_swapchain(const ISwapchain &swapchain) +{ + if (!m_device) + { + return; + } + + m_device->wait_idle(); + m_device->destroy_framebuffers(m_framebuffers); + m_framebuffers = static_cast(swapchain).create_framebuffers_for_pass(m_pass); +} + +auto Pass::create_module(lt::assets::Blob blob) -> VkShaderModule +{ + return m_device->create_shader_module( + VkShaderModuleCreateInfo { + .sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO, + .codeSize = blob.size(), + .pCode = reinterpret_cast(blob.data()) // NOLINT + } + ); +} + + +} // namespace lt::renderer::vk diff --git a/modules/renderer/private/backend/vk/renderer/pass.hpp b/modules/renderer/private/backend/vk/renderer/pass.hpp new file mode 100644 index 0000000..204044c --- /dev/null +++ b/modules/renderer/private/backend/vk/renderer/pass.hpp @@ -0,0 +1,61 @@ +#pragma once + +#include +#include +#include +#include +#include + +namespace lt::renderer::vk { + +class Pass: public IPass +{ +public: + Pass( + IContext &context, + lt::assets::ShaderAsset vertex_shader, + lt::assets::ShaderAsset fragment_shader + ); + + ~Pass() override; + + Pass(Pass &&) = default; + + Pass(const Pass &) = delete; + + auto operator=(Pass &&) -> Pass & = default; + + auto operator=(const Pass &) -> Pass & = delete; + + void replace_swapchain(const ISwapchain &swapchain); + + [[nodiscard]] auto get_pass() -> VkRenderPass + { + return m_pass; + } + + [[nodiscard]] auto get_pipeline() -> VkPipeline + { + return m_pipeline; + } + + [[nodiscard]] auto get_framebuffers() -> std::vector & + { + return m_framebuffers; + } + +private: + auto create_module(lt::assets::Blob blob) -> VkShaderModule; + + memory::NullOnMove m_device {}; + + VkRenderPass m_pass = VK_NULL_HANDLE; + + VkPipeline m_pipeline = VK_NULL_HANDLE; + + VkPipelineLayout m_layout = VK_NULL_HANDLE; + + std::vector m_framebuffers; +}; + +} // namespace lt::renderer::vk diff --git a/modules/renderer/private/backend/vk/renderer/renderer.cpp b/modules/renderer/private/backend/vk/renderer/renderer.cpp new file mode 100644 index 0000000..6621649 --- /dev/null +++ b/modules/renderer/private/backend/vk/renderer/renderer.cpp @@ -0,0 +1,191 @@ +#include +#include + +namespace lt::renderer::vk { + +Renderer::Renderer(IContext &context, uint32_t max_frames_in_flight) + : m_device(static_cast(context.device())) + , m_swapchain(static_cast(context.swapchain())) + , m_resolution(m_swapchain->get_resolution()) + , m_max_frames_in_flight(max_frames_in_flight) +{ + ensure(m_device, "Failed to initialize renderer: null device"); + ensure(m_swapchain, "Failed to initialize renderer: null swapchain"); + + // TODO(Light): HARDCODED PASS!!! + m_pass = create_ref( + context, + assets::ShaderAsset { "./data/test_assets/triangle.vert.asset" }, + assets::ShaderAsset { "./data/test_assets/triangle.frag.asset" } + ); + + m_pool = m_device->create_command_pool( + VkCommandPoolCreateInfo { + .sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO, + .flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT, + .queueFamilyIndex = m_device->get_family_indices()[0], + } + ); + + m_cmds.resize(m_max_frames_in_flight); + m_cmds = m_device->allocate_command_buffers( + VkCommandBufferAllocateInfo { + .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO, + .commandPool = m_pool, + .level = VK_COMMAND_BUFFER_LEVEL_PRIMARY, + .commandBufferCount = static_cast(m_cmds.size()), + } + ); + + m_aquire_image_semaphores = m_device->create_semaphores(m_max_frames_in_flight); + m_frame_fences = m_device->create_fences( + VkFenceCreateInfo { + .sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO, + .flags = VK_FENCE_CREATE_SIGNALED_BIT, + }, + m_max_frames_in_flight + ); + for (auto idx = 0u; + auto [semaphore, fence] : std::views::zip(m_aquire_image_semaphores, m_frame_fences)) + { + m_device->name(semaphore, "acquire image semaphore {}", idx); + m_device->name(fence, "frame fence {}", idx); + } + + m_submit_semaphores = m_device->create_semaphores(m_swapchain->get_image_count()); + for (auto idx = 0u; auto &semaphore : m_submit_semaphores) + { + m_device->name(semaphore, "submit semaphore {}", idx); + } +}; + +Renderer::~Renderer() +{ + if (!m_device) + { + return; + } + + m_device->wait_idle(); + m_device->destroy_semaphores(m_aquire_image_semaphores); + m_device->destroy_semaphores(m_submit_semaphores); + m_device->destroy_fences(m_frame_fences); + m_device->destroy_command_pool(m_pool); +} + +[[nodiscard]] auto Renderer::draw(uint32_t frame_idx) -> DrawResult +{ + ensure( + frame_idx < m_max_frames_in_flight, + "Failed to draw: frame_idx >= max_frames_in_flight ({} >= {})", + frame_idx, + m_max_frames_in_flight + ); + + auto &frame_fence = m_frame_fences[frame_idx]; + auto &aquire_semaphore = m_aquire_image_semaphores[frame_idx]; + auto &cmd = m_cmds[frame_idx]; + + m_device->wait_for_fence(frame_fence); + + auto image_idx = m_device->acquire_image(m_swapchain->vk(), aquire_semaphore); + if (!image_idx.has_value()) + { + return {}; + } + + m_device->reset_fence(frame_fence); + vk_reset_command_buffer(cmd, {}); + record_cmd(cmd, *image_idx); + + auto wait_stage = VkPipelineStageFlags { VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT }; + auto &submit_semaphore = m_submit_semaphores[*image_idx]; + m_device->submit( + VkSubmitInfo { + .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO, + .waitSemaphoreCount = 1u, + .pWaitSemaphores = &aquire_semaphore, + .pWaitDstStageMask = &wait_stage, + .commandBufferCount = 1u, + .pCommandBuffers = &cmd, + .signalSemaphoreCount = 1u, + .pSignalSemaphores = &submit_semaphore, + }, + frame_fence + ); + + // TODO(Light): handle result + auto result = VkResult {}; + m_device->present( + VkPresentInfoKHR { + .sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR, + .waitSemaphoreCount = 1u, + .pWaitSemaphores = &submit_semaphore, + .swapchainCount = 1u, + .pSwapchains = m_swapchain->vk_ptr(), + .pImageIndices = &image_idx.value(), + .pResults = &result, + } + ); + return DrawResult::success; +} + +void Renderer::replace_swapchain(ISwapchain *swapchain) +{ + m_device->wait_idle(); + m_swapchain = static_cast(swapchain); + m_resolution = m_swapchain->get_resolution(); +} + +void Renderer::record_cmd(VkCommandBuffer cmd, uint32_t image_idx) +{ + auto cmd_begin_info = VkCommandBufferBeginInfo { + .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, + .flags = {}, + .pInheritanceInfo = nullptr, + }; + + vkc(vk_begin_command_buffer(cmd, &cmd_begin_info)); + + auto clear_value = VkClearValue { + .color = { + 0.93, + 0.93, + 0.93, + 1.0, + }, + }; + + auto pass_begin_info = VkRenderPassBeginInfo { + .sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO, + .renderPass = m_pass->get_pass(), + .framebuffer = m_pass->get_framebuffers()[image_idx], + .renderArea = { .offset = {}, .extent = m_resolution }, + .clearValueCount = 1u, + .pClearValues = &clear_value + }; + vk_cmd_begin_render_pass(cmd, &pass_begin_info, VK_SUBPASS_CONTENTS_INLINE); + vk_cmd_bind_pipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, m_pass->get_pipeline()); + + auto viewport = VkViewport { + .x = 0.0f, + .y = 0.0f, + .width = static_cast(m_resolution.width), + .height = static_cast(m_resolution.height), + .minDepth = 0.0f, + .maxDepth = 1.0f, + }; + vk_cmd_set_viewport(cmd, 0, 1, &viewport); + + auto scissor = VkRect2D { + .offset = { 0u, 0u }, + .extent = m_resolution, + }; + vk_cmd_set_scissors(cmd, 0, 1, &scissor); + + vk_cmd_draw(cmd, 3, 1, 0, 0); + vk_cmd_end_render_pass(cmd); + vkc(vk_end_command_buffer(cmd)); +} + +} // namespace lt::renderer::vk diff --git a/modules/renderer/private/backend/vk/renderer/renderer.hpp b/modules/renderer/private/backend/vk/renderer/renderer.hpp new file mode 100644 index 0000000..8b3d103 --- /dev/null +++ b/modules/renderer/private/backend/vk/renderer/renderer.hpp @@ -0,0 +1,57 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace lt::renderer::vk { + +class Renderer: public IRenderer +{ +public: + Renderer(IContext &context, uint32_t max_frames_in_flight); + + ~Renderer() override; + + Renderer(Renderer &&) = default; + + Renderer(const Renderer &) = delete; + + auto operator=(Renderer &&) -> Renderer & = default; + + auto operator=(const Renderer &) -> Renderer & = delete; + + [[nodiscard]] DrawResult draw(uint32_t frame_idx) override; + + void replace_swapchain(ISwapchain *swapchain) override; + +private: + void record_cmd(VkCommandBuffer cmd, uint32_t image_idx); + + memory::NullOnMove m_device {}; + + class Swapchain *m_swapchain {}; + + Ref m_pass; + + VkCommandPool m_pool = VK_NULL_HANDLE; + + std::vector m_cmds; + + std::vector m_frame_fences; + + std::vector m_aquire_image_semaphores; + + std::vector m_submit_semaphores; + + VkExtent2D m_resolution; + + uint32_t m_max_frames_in_flight {}; +}; + +} // namespace lt::renderer::vk diff --git a/modules/renderer/private/vk/test_utils.cpp b/modules/renderer/private/backend/vk/test_utils.cpp similarity index 100% rename from modules/renderer/private/vk/test_utils.cpp rename to modules/renderer/private/backend/vk/test_utils.cpp diff --git a/modules/renderer/private/vk/test_utils.hpp b/modules/renderer/private/backend/vk/test_utils.hpp similarity index 94% rename from modules/renderer/private/vk/test_utils.hpp rename to modules/renderer/private/backend/vk/test_utils.hpp index 09f90d5..21a3308 100644 --- a/modules/renderer/private/vk/test_utils.hpp +++ b/modules/renderer/private/backend/vk/test_utils.hpp @@ -1,9 +1,9 @@ #pragma once #include -#include -#include -#include +#include +#include +#include #include #include #include diff --git a/modules/renderer/private/backend/vk/utils.hpp b/modules/renderer/private/backend/vk/utils.hpp new file mode 100644 index 0000000..3ce9618 --- /dev/null +++ b/modules/renderer/private/backend/vk/utils.hpp @@ -0,0 +1,20 @@ +#pragma once + +#include +#include + +namespace lt::renderer::vk { + +inline void vkc(VkResult result) +{ + if (result) + { + throw std::runtime_error { std::format( + "Vulkan call failed with result: {}({})", + string_VkResult(result), + std::to_underlying(result) + ) }; + } +} + +} // namespace lt::renderer::vk diff --git a/modules/renderer/private/vk/vulkan.hpp b/modules/renderer/private/backend/vk/vulkan.hpp similarity index 87% rename from modules/renderer/private/vk/vulkan.hpp rename to modules/renderer/private/backend/vk/vulkan.hpp index 8d3335a..cd4c980 100644 --- a/modules/renderer/private/vk/vulkan.hpp +++ b/modules/renderer/private/backend/vk/vulkan.hpp @@ -8,15 +8,6 @@ namespace lt::renderer::vk { // NOLINTBEGIN(cppcoreguidelines-avoid-non-const-global-variables) -// global functions -extern PFN_vkGetInstanceProcAddr vk_get_instance_proc_address; -extern PFN_vkCreateInstance vk_create_instance; -extern PFN_vkEnumerateInstanceExtensionProperties vk_enumerate_instance_extension_properties; -extern PFN_vkEnumerateInstanceLayerProperties vk_enumerate_instance_layer_properties; - -// instance functions -extern PFN_vkDestroyInstance vk_destroy_instance; -extern PFN_vkEnumeratePhysicalDevices vk_enumerate_physical_devices; extern PFN_vkGetPhysicalDeviceProperties vk_get_physical_device_properties; extern PFN_vkGetPhysicalDeviceQueueFamilyProperties vk_get_physical_device_queue_family_properties; extern PFN_vkCreateDevice vk_create_device; @@ -42,8 +33,6 @@ extern PFN_vkSubmitDebugUtilsMessageEXT vk_submit_debug_message; extern PFN_vkGetPhysicalDeviceSurfaceSupportKHR vk_get_physical_device_surface_support; extern PFN_vkGetPhysicalDeviceSurfaceCapabilitiesKHR vk_get_physical_device_surface_capabilities; extern PFN_vkGetPhysicalDeviceSurfaceFormatsKHR vk_get_physical_device_surface_formats; -extern PFN_vkCreateXlibSurfaceKHR vk_create_xlib_surface_khr; -extern PFN_vkDestroySurfaceKHR vk_destroy_surface_khr; // device functions extern PFN_vkGetDeviceQueue vk_get_device_queue; diff --git a/modules/renderer/private/frontend/context/context.cpp b/modules/renderer/private/frontend/context/context.cpp new file mode 100644 index 0000000..a0b3581 --- /dev/null +++ b/modules/renderer/private/frontend/context/context.cpp @@ -0,0 +1,16 @@ +#include +#include +#include + +namespace lt::renderer { + +auto IContext::create(API target_api, const ecs::Entity &surface_entity) -> Scope +{ + switch (target_api) + { + case API::Vulkan: return create_scope(surface_entity); + default: throw std::runtime_error { "Invalid API" }; + } +} + +} // namespace lt::renderer diff --git a/modules/renderer/private/frontend/context/context.hpp b/modules/renderer/private/frontend/context/context.hpp new file mode 100644 index 0000000..977d1ca --- /dev/null +++ b/modules/renderer/private/frontend/context/context.hpp @@ -0,0 +1,44 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include + + +namespace lt::renderer { + +class IContext +{ +public: + static auto create(API target_api, const ecs::Entity &surface_entity) -> Scope; + IContext() = default; + + virtual ~IContext() = default; + + IContext(IContext &&) = default; + + IContext(const IContext &) = delete; + + auto operator=(IContext &&) -> IContext & = default; + + auto operator=(const IContext &) -> IContext & = delete; + + virtual void recreate_swapchain() = 0; + + [[nodiscard]] virtual auto instance() const -> IInstance * = 0; + + [[nodiscard]] virtual auto surface() const -> ISurface * = 0; + + [[nodiscard]] virtual auto gpu() const -> IGpu * = 0; + + [[nodiscard]] virtual auto device() const -> IDevice * = 0; + + [[nodiscard]] virtual auto swapchain() const -> ISwapchain * = 0; +}; + + +} // namespace lt::renderer diff --git a/modules/renderer/private/frontend/context/context.test.cpp b/modules/renderer/private/frontend/context/context.test.cpp new file mode 100644 index 0000000..e535ecf --- /dev/null +++ b/modules/renderer/private/frontend/context/context.test.cpp @@ -0,0 +1,54 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +using lt::renderer::API; +using lt::renderer::IContext; +using ::lt::test::Case; +using ::lt::test::expect_eq; +using ::lt::test::expect_false; +using ::lt::test::expect_not_nullptr; +using ::lt::test::expect_throw; +using ::lt::test::expect_true; +using ::lt::test::Suite; +using ::std::ignore; + +namespace constants { + +constexpr auto resolution = lt::math::uvec2 { 800u, 600u }; + +} + + +class Fixture +{ +public: + Fixture() + : m_registry(lt::create_ref()) + , m_surface_system(lt::create_scope(m_registry)) + , m_surface_entity(m_registry, m_registry->create_entity()) + { + } + + auto get_surface_entity() -> lt::ecs::Entity + { + return m_surface_entity; + } + +private: + lt::Ref m_registry; + lt::Scope m_surface_system; + lt::ecs::Entity m_surface_entity; +}; + +Suite raii = "context_raii"_suite = [] { + Case { "Happy path won't throw" } = [] { + auto fixture = Fixture {}; + IContext::create(API::Vulkan, fixture.get_surface_entity()); + }; +}; diff --git a/modules/renderer/private/frontend/context/device.hpp b/modules/renderer/private/frontend/context/device.hpp new file mode 100644 index 0000000..add80f8 --- /dev/null +++ b/modules/renderer/private/frontend/context/device.hpp @@ -0,0 +1,21 @@ +#pragma once + +namespace lt::renderer { + +class IDevice +{ +public: + IDevice() = default; + + virtual ~IDevice() = default; + + IDevice(IDevice &&) = default; + + IDevice(const IDevice &) = delete; + + auto operator=(IDevice &&) -> IDevice & = default; + + auto operator=(const IDevice &) -> IDevice & = delete; +}; + +} // namespace lt::renderer diff --git a/modules/renderer/private/vk/context/device.test.cpp b/modules/renderer/private/frontend/context/device.test.cpp similarity index 100% rename from modules/renderer/private/vk/context/device.test.cpp rename to modules/renderer/private/frontend/context/device.test.cpp diff --git a/modules/renderer/private/frontend/context/gpu.hpp b/modules/renderer/private/frontend/context/gpu.hpp new file mode 100644 index 0000000..b18cd82 --- /dev/null +++ b/modules/renderer/private/frontend/context/gpu.hpp @@ -0,0 +1,21 @@ +#pragma once + +namespace lt::renderer { + +class IGpu +{ +public: + IGpu() = default; + + virtual ~IGpu() = default; + + IGpu(IGpu &&) = default; + + IGpu(const IGpu &) = delete; + + auto operator=(IGpu &&) -> IGpu & = default; + + auto operator=(const IGpu &) -> IGpu & = delete; +}; + +} // namespace lt::renderer diff --git a/modules/renderer/private/frontend/context/instance.hpp b/modules/renderer/private/frontend/context/instance.hpp new file mode 100644 index 0000000..2d384f4 --- /dev/null +++ b/modules/renderer/private/frontend/context/instance.hpp @@ -0,0 +1,21 @@ +#pragma once + +namespace lt::renderer { + +class IInstance +{ +public: + IInstance() = default; + + virtual ~IInstance() = default; + + IInstance(IInstance &&) = default; + + IInstance(const IInstance &) = delete; + + auto operator=(IInstance &&) -> IInstance & = default; + + auto operator=(const IInstance &) -> IInstance & = delete; +}; + +} // namespace lt::renderer diff --git a/modules/renderer/private/vk/context/instance.test.cpp b/modules/renderer/private/frontend/context/instance.test.cpp similarity index 100% rename from modules/renderer/private/vk/context/instance.test.cpp rename to modules/renderer/private/frontend/context/instance.test.cpp diff --git a/modules/renderer/private/frontend/context/surface.hpp b/modules/renderer/private/frontend/context/surface.hpp new file mode 100644 index 0000000..e6931ef --- /dev/null +++ b/modules/renderer/private/frontend/context/surface.hpp @@ -0,0 +1,21 @@ +#pragma once + +namespace lt::renderer { + +class ISurface +{ +public: + ISurface() = default; + + virtual ~ISurface() = default; + + ISurface(ISurface &&) = default; + + ISurface(const ISurface &) = delete; + + auto operator=(ISurface &&) -> ISurface & = default; + + auto operator=(const ISurface &) -> ISurface & = delete; +}; + +} // namespace lt::renderer diff --git a/modules/renderer/private/vk/context/surface.test.cpp b/modules/renderer/private/frontend/context/surface.test.cpp similarity index 100% rename from modules/renderer/private/vk/context/surface.test.cpp rename to modules/renderer/private/frontend/context/surface.test.cpp diff --git a/modules/renderer/private/frontend/context/swapchain.hpp b/modules/renderer/private/frontend/context/swapchain.hpp new file mode 100644 index 0000000..3eef813 --- /dev/null +++ b/modules/renderer/private/frontend/context/swapchain.hpp @@ -0,0 +1,21 @@ +#pragma once + +namespace lt::renderer { + +class ISwapchain +{ +public: + ISwapchain() = default; + + virtual ~ISwapchain() = default; + + ISwapchain(ISwapchain &&) = default; + + ISwapchain(const ISwapchain &) = delete; + + auto operator=(ISwapchain &&) -> ISwapchain & = default; + + auto operator=(const ISwapchain &) -> ISwapchain & = delete; +}; + +} // namespace lt::renderer diff --git a/modules/renderer/private/vk/context/swapchain.test.cpp b/modules/renderer/private/frontend/context/swapchain.test.cpp similarity index 100% rename from modules/renderer/private/vk/context/swapchain.test.cpp rename to modules/renderer/private/frontend/context/swapchain.test.cpp diff --git a/modules/renderer/private/frontend/messenger.cpp b/modules/renderer/private/frontend/messenger.cpp new file mode 100644 index 0000000..d935f58 --- /dev/null +++ b/modules/renderer/private/frontend/messenger.cpp @@ -0,0 +1,20 @@ +#include +#include + +namespace lt::renderer { + +[[nodiscard]] /* static */ auto IMessenger::create( + API target_api, + IInstance *instance, + ecs::Entity entity +) -> Scope +{ + switch (target_api) + { + case API::Vulkan: return create_scope(instance, std::move(entity)); + + case API::Metal: + case API::DirectX: throw std::runtime_error { "Invalid API" }; + } +} +} // namespace lt::renderer diff --git a/modules/renderer/private/frontend/messenger.hpp b/modules/renderer/private/frontend/messenger.hpp new file mode 100644 index 0000000..9337189 --- /dev/null +++ b/modules/renderer/private/frontend/messenger.hpp @@ -0,0 +1,27 @@ +#pragma once + +#include +#include + +namespace lt::renderer { + +class IMessenger +{ +public: + [[nodiscard]] static auto create(API target_api, class IInstance *instance, ecs::Entity entity) + -> Scope; + + IMessenger() = default; + + virtual ~IMessenger() = default; + + IMessenger(IMessenger &&) = default; + + IMessenger(const IMessenger &) = delete; + + auto operator=(IMessenger &&) -> IMessenger & = default; + + auto operator=(const IMessenger &) -> IMessenger & = delete; +}; + +} // namespace lt::renderer diff --git a/modules/renderer/private/frontend/renderer/pass.hpp b/modules/renderer/private/frontend/renderer/pass.hpp new file mode 100644 index 0000000..854aafd --- /dev/null +++ b/modules/renderer/private/frontend/renderer/pass.hpp @@ -0,0 +1,25 @@ +#pragma once + +#include + +namespace lt::renderer { + +class IPass +{ +public: + IPass() = default; + + virtual ~IPass() = default; + + IPass(IPass &&) = default; + + IPass(const IPass &) = delete; + + auto operator=(IPass &&) -> IPass & = default; + + auto operator=(const IPass &) -> IPass & = delete; + + void replace_swapchain(const ISwapchain &swapchain); +}; + +} // namespace lt::renderer diff --git a/modules/renderer/private/vk/renderer/pass.test.cpp b/modules/renderer/private/frontend/renderer/pass.test.cpp similarity index 100% rename from modules/renderer/private/vk/renderer/pass.test.cpp rename to modules/renderer/private/frontend/renderer/pass.test.cpp diff --git a/modules/renderer/private/frontend/renderer/renderer.cpp b/modules/renderer/private/frontend/renderer/renderer.cpp new file mode 100644 index 0000000..1480b49 --- /dev/null +++ b/modules/renderer/private/frontend/renderer/renderer.cpp @@ -0,0 +1,17 @@ +#include +#include +#include + +namespace lt::renderer { + +auto IRenderer::create(API target_api, IContext &context, uint32_t max_frames_in_flight) + -> Scope +{ + switch (target_api) + { + case API::Vulkan: return create_scope(context, max_frames_in_flight); + default: throw std::runtime_error { "Invalid API" }; + } +} + +} // namespace lt::renderer diff --git a/modules/renderer/private/frontend/renderer/renderer.hpp b/modules/renderer/private/frontend/renderer/renderer.hpp new file mode 100644 index 0000000..bea74e2 --- /dev/null +++ b/modules/renderer/private/frontend/renderer/renderer.hpp @@ -0,0 +1,37 @@ +#pragma once + +#include + +namespace lt::renderer { + +class IRenderer +{ +public: + enum class DrawResult : uint8_t + { + success = 0, + invalid_swapchain, + error, + }; + + static auto create(API target_api, class IContext &context, uint32_t max_frames_in_flight) + -> Scope; + + IRenderer() = default; + + virtual ~IRenderer() = default; + + IRenderer(IRenderer &&) = default; + + IRenderer(const IRenderer &) = delete; + + auto operator=(IRenderer &&) -> IRenderer & = default; + + auto operator=(const IRenderer &) -> IRenderer & = delete; + + [[nodiscard]] virtual auto draw(uint32_t frame_idx) -> DrawResult = 0; + + virtual void replace_swapchain(class ISwapchain *swapchain) = 0; +}; + +} // namespace lt::renderer diff --git a/modules/renderer/private/vk/renderer/renderer.test.cpp b/modules/renderer/private/frontend/renderer/renderer.test.cpp similarity index 100% rename from modules/renderer/private/vk/renderer/renderer.test.cpp rename to modules/renderer/private/frontend/renderer/renderer.test.cpp diff --git a/modules/renderer/private/system.cpp b/modules/renderer/private/system.cpp index a21afe8..cd4d394 100644 --- a/modules/renderer/private/system.cpp +++ b/modules/renderer/private/system.cpp @@ -1,92 +1,28 @@ +#include +#include +#include +#include +#include #include -#include -#include -#include -#include - -using ::lt::assets::ShaderAsset; - -namespace lt::renderer::vk { -class ValidationObserver -{ - using Messenger = lt::renderer::vk::Messenger; - using enum Messenger::Type; - using enum Messenger::Severity; - -public: - ValidationObserver() - : m_messenger( - Messenger::CreateInfo { - .severity = static_cast(warning | error), - .type = lt::renderer::vk::Messenger::all_type, - .callback = &callback, - .user_data = &m_had_any_messages, - } - ) - { - } - - ~ValidationObserver() - { - } - - [[nodiscard]] auto had_any_messages() const -> bool - { - return m_had_any_messages; - } - -private: - static void callback( - Messenger::Severity message_severity, - Messenger::Type message_type, - Messenger::CallbackData_T vulkan_data, - void *user_data - ) - { - std::ignore = message_severity; - std::ignore = message_type; - for (auto idx = 0; idx < vulkan_data->objectCount; ++idx) - { - auto object = vulkan_data->pObjects[idx]; - std::println( - "0x{:x}({}) = {}", - object.objectHandle, - string_VkObjectType(object.objectType), - object.pObjectName ? object.pObjectName : "unnamed" - ); - } - - std::println("Validation message: {}", vulkan_data->pMessage); - - // NOLINTNEXTLINE(cppcoreguidelines-pro-type-cstyle-cast) - *(bool *)user_data = true; - } - - Messenger m_messenger; - bool m_had_any_messages = false; -}; -} // namespace lt::renderer::vk - +#include namespace lt::renderer { System::System(CreateInfo info) - : m_registry(std::move(info.registry)) - , m_stats(info.system_stats) - , m_context(create_scope(info.surface_entity)) + : m_api(info.config.target_api) + , m_registry(std::move(info.registry)) + , m_context(IContext::create(m_api, info.surface_entity)) , m_surface_entity(info.surface_entity) + , m_max_frames_in_flight(info.config.max_frames_in_flight) { - m_validation_observer = new vk::ValidationObserver(); // ensure(m_stats, "Failed to initialize system: null stats"); ensure(m_registry, "Failed to initialize renderer system: null registry"); - m_pass = create_ref( - *m_context, - ShaderAsset { "./data/test_assets/triangle.vert.asset" }, - ShaderAsset { "./data/test_assets/triangle.frag.asset" } - ); + m_renderer = IRenderer::create(m_api, *m_context, info.config.max_frames_in_flight); - m_renderer = create_scope(*m_context, m_pass); + // WIP(Light): attach debug messenger on messenger component construction + m_registry->connect_on_construct([](ecs::Registry ®istry, + ecs::EntityId entity) {}); } System::~System() @@ -103,25 +39,27 @@ void System::on_unregister() void System::tick(app::TickInfo tick) { + std::ignore = tick; + for (const auto &event : m_surface_entity.get().peek_events()) { if (std::holds_alternative(event)) { m_context->recreate_swapchain(); m_renderer->replace_swapchain(m_context->swapchain()); - m_pass->replace_swapchain(m_context->swapchain()); + // m_pass->replace_swapchain(m_context->swapchain()); } } - if (m_renderer->draw(m_frame_idx)) + if (m_renderer->draw(m_frame_idx) != IRenderer::DrawResult::success) { m_context->recreate_swapchain(); m_renderer->replace_swapchain(m_context->swapchain()); - m_pass->replace_swapchain(m_context->swapchain()); - m_renderer->draw(m_frame_idx); // don't drop the frame + // m_pass->replace_swapchain(m_context->swapchain()); + std::ignore = m_renderer->draw(m_frame_idx); // drop the frame if failed twice } - m_frame_idx = (m_frame_idx + 1) % vk::Renderer::max_frames_in_flight; + m_frame_idx = (m_frame_idx + 1) % m_max_frames_in_flight; } } // namespace lt::renderer diff --git a/modules/renderer/private/vk/context/context.cpp b/modules/renderer/private/vk/context/context.cpp deleted file mode 100644 index 576cc02..0000000 --- a/modules/renderer/private/vk/context/context.cpp +++ /dev/null @@ -1,16 +0,0 @@ -#include -#include - -namespace lt::renderer::vk { - -Context::Context(const ecs::Entity &surface_entity) - : m_surface(surface_entity) - , m_device(m_surface) - , m_swapchain(m_device, m_surface) -{ - ensure(m_surface.vk(), "Failed to create vulkan context: null surface"); - ensure(m_device.vk(), "Failed to create vulkan context: null device"); - ensure(m_swapchain.vk(), "Failed to create vulkan context: null swapchain"); -} - -} // namespace lt::renderer::vk diff --git a/modules/renderer/private/vk/context/context.hpp b/modules/renderer/private/vk/context/context.hpp deleted file mode 100644 index 1240785..0000000 --- a/modules/renderer/private/vk/context/context.hpp +++ /dev/null @@ -1,52 +0,0 @@ -#pragma once - -#include -#include -#include -#include - -// -#include -#include -#include -#include - -namespace lt::renderer::vk { - -using memory::NullOnMove; - -class Context -{ -public: - Context(const ecs::Entity &surface_entity); - - [[nodiscard]] auto instance() const -> VkInstance - { - return Instance::get(); - } - - [[nodiscard]] auto device() const -> const Device & - { - return m_device; - } - - [[nodiscard]] auto swapchain() const -> const Swapchain & - { - return m_swapchain; - } - - void recreate_swapchain() - { - m_swapchain.destroy(); - m_swapchain = Swapchain { m_device, m_surface }; - } - -private: - Surface m_surface; - - Device m_device; - - Swapchain m_swapchain; -}; - -} // namespace lt::renderer::vk diff --git a/modules/renderer/private/vk/context/context.test.cpp b/modules/renderer/private/vk/context/context.test.cpp deleted file mode 100644 index e69de29..0000000 diff --git a/modules/renderer/private/vk/context/device.cpp b/modules/renderer/private/vk/context/device.cpp deleted file mode 100644 index 9eba491..0000000 --- a/modules/renderer/private/vk/context/device.cpp +++ /dev/null @@ -1,175 +0,0 @@ -#include -#include -#include -#include - -namespace lt::renderer::vk { - -Device::Device(const Surface &surface) -{ - ensure(surface.vk(), "Failed to initialize vk::Device: null vulkan surface"); - - initialize_physical_device(); - initialize_queue_indices(surface); - initialize_logical_device(); - Instance::load_device_functions(m_device); - - vk_get_device_queue(m_device, m_graphics_queue_family_index, 0, &m_graphics_queue); - vk_get_device_queue(m_device, m_present_queue_family_index, 0, &m_present_queue); - - if (m_present_queue.get() == m_graphics_queue.get()) - { - set_object_name(m_device, m_present_queue.get(), "unified queue"); - } - else - { - set_object_name(m_device, m_graphics_queue.get(), "graphics queue"); - set_object_name(m_device, m_present_queue.get(), "present queue"); - } -} - -Device::~Device() -{ - if (m_device) - { - vkc(vk_device_wait_idle(m_device)); - vk_destroy_device(m_device, nullptr); - } -} - -void Device::initialize_physical_device() -{ - auto count = 0u; - vkc(vk_enumerate_physical_devices(Instance::get(), &count, nullptr)); - ensure(count != 0u, "Failed to find any physical devices with Vulkan support"); - - auto devices = std::vector(count); - vkc(vk_enumerate_physical_devices(Instance::get(), &count, devices.data())); - - for (auto &device : devices) - { - auto properties = VkPhysicalDeviceProperties {}; - auto features = VkPhysicalDeviceFeatures {}; - - vk_get_physical_device_properties(device, &properties); - vk_get_physical_device_features(device, &features); - - if (properties.deviceType == VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU - && features.geometryShader) - { - m_physical_device = device; - } - } - ensure(m_physical_device, "Failed to find any suitable Vulkan physical device"); -} - -void Device::initialize_logical_device() -{ - const float priorities = .0f; - - auto queue_infos = std::vector {}; - auto queue_families = std::set { m_graphics_queue_family_index, m_present_queue_family_index }; - - for (auto queue_family : queue_families) - { - queue_infos.emplace_back( - VkDeviceQueueCreateInfo { - .sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO, - .queueFamilyIndex = queue_family, - .queueCount = 1u, - .pQueuePriorities = &priorities, - } - ); - } - - auto physical_device_features = VkPhysicalDeviceFeatures {}; - - auto extensions = std::vector { - VK_KHR_SWAPCHAIN_EXTENSION_NAME, - }; - - auto device_info = VkDeviceCreateInfo { - .sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO, - .queueCreateInfoCount = static_cast(queue_infos.size()), - .pQueueCreateInfos = queue_infos.data(), - .enabledExtensionCount = static_cast(extensions.size()), - .ppEnabledExtensionNames = extensions.data(), - .pEnabledFeatures = &physical_device_features, - }; - - ensure( - !vk_create_device(m_physical_device, &device_info, nullptr, &m_device), - "Failed to create logical vulkan device" - ); -} - -[[nodiscard]] auto Device::find_suitable_queue_family() const -> uint32_t -{ - // auto count = 0u; - // vk_get_physical_device_queue_family_properties(m_physical_device, &count, nullptr); - // ensure(count != 0u, "Failed to find any physical devices with Vulkan support"); - // - // auto families = std::vector(count); - // vk_get_physical_device_queue_family_properties(m_physical_device, &count, families.data()); - // - // const auto required_flags = VK_QUEUE_GRAPHICS_BIT | VK_QUEUE_COMPUTE_BIT; - // for (auto idx = 0u; auto &family : families) - // { - // if ((family.queueFlags & required_flags) == required_flags) - // { - // return idx; - // } - // } - // - // ensure(false, "Failed to find a suitable Vulkan queue family"); - // return 0; -} - -void Device::initialize_queue_indices(const Surface &surface) -{ - auto count = uint32_t { 0u }; - vk_get_physical_device_queue_family_properties(m_physical_device, &count, nullptr); - - auto properties = std::vector(count); - vk_get_physical_device_queue_family_properties(m_physical_device, &count, properties.data()); - - for (auto idx = uint32_t { 0u }; const auto &property : properties) - { - if (property.queueFlags & VK_QUEUE_GRAPHICS_BIT) - { - m_graphics_queue_family_index = idx; - } - - auto has_presentation_support = VkBool32 { false }; - vkc(vk_get_physical_device_surface_support( - m_physical_device, - idx, - surface.vk(), - &has_presentation_support - )); - if (has_presentation_support) - { - m_present_queue_family_index = idx; - } - - ++idx; - - if (m_graphics_queue_family_index != VK_QUEUE_FAMILY_IGNORED - && m_present_queue_family_index != VK_QUEUE_FAMILY_IGNORED) - { - break; - } - } - - ensure( - m_graphics_queue_family_index != VK_QUEUE_FAMILY_IGNORED, - "Failed to find graphics queue family" - ); - - ensure( - m_present_queue_family_index != VK_QUEUE_FAMILY_IGNORED, - "Failed to find presentation queue family" - ); -} - -} // namespace lt::renderer::vk diff --git a/modules/renderer/private/vk/context/device.hpp b/modules/renderer/private/vk/context/device.hpp deleted file mode 100644 index ed10a17..0000000 --- a/modules/renderer/private/vk/context/device.hpp +++ /dev/null @@ -1,71 +0,0 @@ -#pragma once - -#include -#include - -namespace lt::renderer::vk { - -class Device -{ -public: - Device(const class Surface &surface); - - ~Device(); - - Device(Device &&) = default; - - Device(const Device &) = delete; - - auto operator=(Device &&) -> Device & = default; - - auto operator=(const Device &) const -> Device & = delete; - - [[nodiscard]] auto vk() const -> VkDevice - { - return m_device; - } - - [[nodiscard]] auto physical() const -> VkPhysicalDevice - { - return m_physical_device; - } - - [[nodiscard]] auto get_family_indices() const -> std::array - { - return { m_graphics_queue_family_index, m_present_queue_family_index }; - } - - [[nodiscard]] auto get_graphics_queue() const -> VkQueue - { - return m_graphics_queue; - } - - [[nodiscard]] auto get_present_queue() const -> VkQueue - { - return m_present_queue; - } - -private: - void initialize_physical_device(); - - void initialize_logical_device(); - - void initialize_queue_indices(const class Surface &surface); - - [[nodiscard]] auto find_suitable_queue_family() const -> uint32_t; - - // logical device - memory::NullOnMove m_physical_device = VK_NULL_HANDLE; - - memory::NullOnMove m_device = VK_NULL_HANDLE; - - memory::NullOnMove m_graphics_queue = VK_NULL_HANDLE; - - memory::NullOnMove m_present_queue = VK_NULL_HANDLE; - - uint32_t m_graphics_queue_family_index = VK_QUEUE_FAMILY_IGNORED; - - uint32_t m_present_queue_family_index = VK_QUEUE_FAMILY_IGNORED; -}; - -} // namespace lt::renderer::vk diff --git a/modules/renderer/private/vk/context/swapchain.cpp b/modules/renderer/private/vk/context/swapchain.cpp deleted file mode 100644 index 549bf58..0000000 --- a/modules/renderer/private/vk/context/swapchain.cpp +++ /dev/null @@ -1,126 +0,0 @@ -#include -#include -#include -#include -#include -#include - -namespace lt::renderer::vk { - -Swapchain::Swapchain(const Device &device, const Surface &surface): m_device(device.vk()) -{ - static auto idx = 0u; - auto *physical_device = device.physical(); - - auto capabilities = VkSurfaceCapabilitiesKHR {}; - vkc(vk_get_physical_device_surface_capabilities(physical_device, surface.vk(), &capabilities)); - - auto count = uint32_t { 0 }; - vkc(vk_get_physical_device_surface_formats(physical_device, surface.vk(), &count, nullptr)); - - auto formats = std::vector(count); - vkc(vk_get_physical_device_surface_formats( - physical_device, - surface.vk(), - &count, - formats.data() - )); - ensure(!formats.empty(), "Surface has no formats!"); - - // TODO(Light): parameterize - constexpr auto desired_swapchain_image_count = uint32_t { 3 }; - const auto surface_format = formats.front(); - const auto queue_indices = device.get_family_indices(); - m_format = surface_format.format; - - auto create_info = VkSwapchainCreateInfoKHR { - .sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR, - .surface = surface.vk(), - .minImageCount = get_optimal_image_count(capabilities, desired_swapchain_image_count), - .imageFormat = surface_format.format, - .imageColorSpace = surface_format.colorSpace, - .imageExtent = capabilities.currentExtent, - .imageArrayLayers = 1u, - .imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT, - .imageSharingMode = VK_SHARING_MODE_EXCLUSIVE, - .queueFamilyIndexCount = queue_indices.size(), - .pQueueFamilyIndices = queue_indices.data(), - .preTransform = capabilities.currentTransform, - .compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR, - .presentMode = VK_PRESENT_MODE_FIFO_KHR, // TODO(Light): parameterize - .clipped = VK_TRUE, - .oldSwapchain = nullptr, - }; - m_resolution = capabilities.currentExtent; - - vkc(vk_create_swapchain_khr(device.vk(), &create_info, nullptr, &m_swapchain)); - vkc(vk_device_wait_idle(device.vk())); - set_object_name(device.vk(), m_swapchain.get(), "swapchain {}", idx++); - - auto image_count = uint32_t { 0u }; - vk_get_swapchain_images_khr(device.vk(), m_swapchain, &image_count, nullptr); - - m_images.resize(image_count); - m_image_views.resize(image_count); - vk_get_swapchain_images_khr(device.vk(), m_swapchain, &image_count, m_images.data()); - - for (auto [image, view] : std::views::zip(m_images, m_image_views)) - { - auto create_info = VkImageViewCreateInfo { - .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, - .image = image, - .viewType = VK_IMAGE_VIEW_TYPE_2D, - .format = surface_format.format, - .components = VkComponentMapping { - .r = VK_COMPONENT_SWIZZLE_IDENTITY, - .g = VK_COMPONENT_SWIZZLE_IDENTITY, - .b = VK_COMPONENT_SWIZZLE_IDENTITY, - .a = VK_COMPONENT_SWIZZLE_IDENTITY, - }, - .subresourceRange = VkImageSubresourceRange { - .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, - .baseMipLevel = 0u, - .levelCount = 1u, - .baseArrayLayer = 0u, - .layerCount = 1u, - } - }; - - vkc(vk_create_image_view(device.vk(), &create_info, nullptr, &view)); - } -} - -Swapchain::~Swapchain() -{ - destroy(); -} - -[[nodiscard]] auto Swapchain::get_optimal_image_count( - VkSurfaceCapabilitiesKHR capabilities, - uint32_t desired_image_count -) const -> uint32_t -{ - const auto min_image_count = capabilities.minImageCount; - const auto max_image_count = capabilities.maxImageCount; - - const auto has_max_limit = max_image_count != 0; - - // Desired image count is in range - if ((!has_max_limit || max_image_count >= desired_image_count) - && min_image_count <= desired_image_count) - { - return desired_image_count; - } - - // Fall-back to 2 if in ange - if (min_image_count <= 2 && max_image_count >= 2) - { - return 2; - } - - // Fall-back to min_image_count - return min_image_count; -} - - -} // namespace lt::renderer::vk diff --git a/modules/renderer/private/vk/context/swapchain.hpp b/modules/renderer/private/vk/context/swapchain.hpp deleted file mode 100644 index 70ef2b0..0000000 --- a/modules/renderer/private/vk/context/swapchain.hpp +++ /dev/null @@ -1,108 +0,0 @@ -#pragma once - -#include -#include -#include - -namespace lt::renderer::vk { - -class Swapchain -{ -public: - Swapchain(const class Device &device, const class Surface &surface); - - ~Swapchain(); - - Swapchain(Swapchain &&) = default; - - Swapchain(const Swapchain &) = delete; - - auto operator=(Swapchain &&) -> Swapchain & = default; - - auto operator=(const Swapchain &) const -> Swapchain & = delete; - - void destroy() - { - try - { - if (m_device) - { - vkc(vk_device_wait_idle(m_device)); - for (auto &view : m_image_views) - { - vk_destroy_image_view(m_device, view, nullptr); - } - - vk_destroy_swapchain_khr(m_device, m_swapchain, nullptr); - } - } - catch (const std::exception &exp) - { - log_err("Failed to destroy swapchain:"); - log_err("\twhat: {}", exp.what()); - } - } - - [[nodiscard]] auto vk() const -> VkSwapchainKHR - { - return m_swapchain; - } - - [[nodiscard]] auto get_resolution() const -> VkExtent2D - { - return m_resolution; - } - - [[nodiscard]] auto get_format() const -> VkFormat - { - return m_format; - } - - [[nodiscard]] auto get_image_count() const -> size_t - { - return m_images.size(); - } - - [[nodiscard]] auto create_framebuffers_for_pass(VkRenderPass pass) const - -> std::vector - { - auto framebuffers = std::vector(m_image_views.size()); - - for (auto idx = 0u; auto &framebuffer : framebuffers) - { - auto info = VkFramebufferCreateInfo { - .sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO, - .renderPass = pass, - .attachmentCount = 1u, - .pAttachments = &m_image_views[idx++], - .width = m_resolution.width, - .height = m_resolution.height, - .layers = 1u - }; - - vkc(vk_create_frame_buffer(m_device, &info, nullptr, &framebuffer)); - } - - return framebuffers; - } - -private: - [[nodiscard]] auto get_optimal_image_count( - VkSurfaceCapabilitiesKHR capabilities, - uint32_t desired_image_count - ) const -> uint32_t; - - memory::NullOnMove m_device; - - memory::NullOnMove m_swapchain = VK_NULL_HANDLE; - - std::vector m_images; - - std::vector m_image_views; - - VkExtent2D m_resolution; - - VkFormat m_format; -}; - -} // namespace lt::renderer::vk diff --git a/modules/renderer/private/vk/debug/messenger.cpp b/modules/renderer/private/vk/debug/messenger.cpp deleted file mode 100644 index e69de29..0000000 diff --git a/modules/renderer/private/vk/debug/messenger.hpp b/modules/renderer/private/vk/debug/messenger.hpp deleted file mode 100644 index 6386e97..0000000 --- a/modules/renderer/private/vk/debug/messenger.hpp +++ /dev/null @@ -1,143 +0,0 @@ -#pragma once - -#include -#include -#include - -namespace lt::renderer::vk { - -class Messenger -{ -public: - // NOLINTNEXTLINE(performance-enum-size) - enum Severity : decltype(std::to_underlying( - VK_DEBUG_UTILS_MESSAGE_SEVERITY_FLAG_BITS_MAX_ENUM_EXT - )) - { - verbose = VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT, - info = VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT, - warning = VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT, - error = VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT, - - all_severity = VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT - | VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT - | VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT - | VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT, - }; - - // NOLINTNEXTLINE(performance-enum-size) - enum Type : decltype(std::to_underlying(VK_DEBUG_UTILS_MESSAGE_TYPE_FLAG_BITS_MAX_ENUM_EXT)) - { - general = VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT, - validation = VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT, - performance = VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT, - - // address_binding = VK_DEBUG_UTILS_MESSAGE_TYPE_DEVICE_ADDRESS_BINDING_BIT_EXT, - - /** @note: does not include address binding yet. */ - all_type = VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT - | VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT - | VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT, - }; - - using CallbackData_T = const VkDebugUtilsMessengerCallbackDataEXT *; - - using Callback_T = std::function; - - struct CreateInfo - { - Severity severity; - - Type type; - - Callback_T callback; - - void *user_data; - }; - - explicit Messenger(CreateInfo info) - : m_callback(std::move(info.callback)) - , m_user_data(info.user_data) - { - ensure(m_callback, "Failed to initialize vk::Messenger: null callback"); - ensure(info.severity != Severity {}, "Failed to initialize vk::Messenger: null severity"); - ensure(info.type != Type {}, "Failed to initialize vk::Messenger: null type"); - - // Instance may not be initialized yet - // Making it get initialized inside a call to vk_create_debug_messenger - // Which would invoke a nullptr... - if (!vk_create_debug_messenger) - { - Instance::get(); - } - - const auto vulkan_info = VkDebugUtilsMessengerCreateInfoEXT { - .sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT, - .messageSeverity = info.severity, - .messageType = info.type, - .pfnUserCallback = &validation_layers_callback, - .pUserData = this, - }; - - ensure( - !vk_create_debug_messenger(Instance::get(), &vulkan_info, nullptr, &m_debug_messenger), - "Failed to create vulkan debug utils messenger" - ); - } - - ~Messenger() - { - vk_destroy_debug_messenger(Instance::get(), m_debug_messenger, nullptr); - } - - Messenger(Messenger &&) = default; - - Messenger(const Messenger &) = delete; - - auto operator=(Messenger &&) -> Messenger & = default; - - auto operator=(const Messenger &) const -> Messenger & = delete; - - -private: - static auto validation_layers_callback( - VkDebugUtilsMessageSeverityFlagBitsEXT const message_severity, - VkDebugUtilsMessageTypeFlagsEXT const message_type, - VkDebugUtilsMessengerCallbackDataEXT const *const callback_data, - void *const vulkan_user_data - ) -> VkBool32 - { - auto *messenger = (Messenger *)vulkan_user_data; // NOLINT - ensure(messenger, "Null vulkan_user_data received in messenger callback"); - - messenger->validation_layers_callback_impl(message_severity, message_type, callback_data); - return VK_FALSE; - } - - void validation_layers_callback_impl( - VkDebugUtilsMessageSeverityFlagBitsEXT message_severity, - VkDebugUtilsMessageTypeFlagsEXT message_type, - const VkDebugUtilsMessengerCallbackDataEXT *callback_data - ) - { - m_callback( - static_cast(message_severity), - static_cast(message_type), - callback_data, - m_user_data - ); - } - - memory::NullOnMove m_debug_messenger = VK_NULL_HANDLE; - - Callback_T m_callback; - - void *m_user_data; -}; - -} // namespace lt::renderer::vk diff --git a/modules/renderer/private/vk/debug/validation.hpp b/modules/renderer/private/vk/debug/validation.hpp deleted file mode 100644 index af54c25..0000000 --- a/modules/renderer/private/vk/debug/validation.hpp +++ /dev/null @@ -1,66 +0,0 @@ -#pragma once - -#include -#include - - -namespace lt::renderer::vk { - -inline void vkc(VkResult result) -{ - if (result) - { - throw std::runtime_error { std::format( - "Vulkan call failed with result: {}({})", - string_VkResult(result), - std::to_underlying(result) - ) }; - } -} - -template -inline auto get_object_type(T object) -> VkObjectType -{ - if constexpr (std::is_same_v) - { - return VK_OBJECT_TYPE_QUEUE; - } - - if constexpr (std::is_same_v) - { - return VK_OBJECT_TYPE_FENCE; - } - - if constexpr (std::is_same_v) - { - return VK_OBJECT_TYPE_SEMAPHORE; - } - - if constexpr (std::is_same_v) - { - return VK_OBJECT_TYPE_SWAPCHAIN_KHR; - } - - static_assert("invalid type"); -} - -template -inline void set_object_name( - VkDevice device, - T object, - std::format_string fmt, - Args &&...args -) -{ - const auto name = std::format(fmt, std::forward(args)...); - auto info = VkDebugUtilsObjectNameInfoEXT { - .sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_OBJECT_NAME_INFO_EXT, - .objectType = get_object_type(object), - .objectHandle = (uint64_t)(object), - .pObjectName = name.c_str(), - }; - - vk_set_debug_object_name(device, &info); -} - -} // namespace lt::renderer::vk diff --git a/modules/renderer/private/vk/pipeline.cpp b/modules/renderer/private/vk/pipeline.cpp deleted file mode 100644 index 8d35d8b..0000000 --- a/modules/renderer/private/vk/pipeline.cpp +++ /dev/null @@ -1,18 +0,0 @@ -#include - -namespace lt::renderer::vk { - -Pipeline::Pipeline(CreateInfo info): m_context(std::move(info.context)) -{ - ensure(m_context, "Failed to create vk pipeline: null context"); -} - -Pipeline::~Pipeline() -{ - if (m_context) - { - return; - } -} - -} // namespace lt::renderer::vk diff --git a/modules/renderer/private/vk/pipeline.hpp b/modules/renderer/private/vk/pipeline.hpp deleted file mode 100644 index 9f4b7b0..0000000 --- a/modules/renderer/private/vk/pipeline.hpp +++ /dev/null @@ -1,36 +0,0 @@ -#pragma once - -#include -#include - -namespace lt::renderer::vk { - -class Pipeline -{ -public: - struct CreateInfo - { - Ref context; - }; - - Pipeline(CreateInfo info); - - ~Pipeline(); - - Pipeline(Pipeline &&) = default; - - Pipeline(const Pipeline &) = delete; - - auto operator=(Pipeline &&) -> Pipeline & = default; - - auto operator=(const Pipeline &) -> Pipeline & = delete; - -private: - VkPipeline m_pipeline = {}; - - VkPipelineLayout m_pipeline_layout = {}; - - Ref m_context; -}; - -}; // namespace lt::renderer::vk diff --git a/modules/renderer/private/vk/pipeline.test.cpp b/modules/renderer/private/vk/pipeline.test.cpp deleted file mode 100644 index 3b2a565..0000000 --- a/modules/renderer/private/vk/pipeline.test.cpp +++ /dev/null @@ -1,59 +0,0 @@ -#include -#include -#include -#include -#include - -using namespace lt; - -using renderer::vk::Context; -using renderer::vk::Pipeline; - -class VkPipelineTest -{ -public: - VkPipelineTest() - { - m_registry = create_ref(); - - m_surface_system = create_ref(m_registry); - - m_surface_entity = create_scope(m_registry, m_registry->create_entity()); - m_surface_entity->add(surface::SurfaceComponent::CreateInfo { - .title = "", - .resolution = constants::resolution, - }); - - m_context = create_ref(*m_surface_entity); - } - - [[nodiscard]] auto context() -> Ref - { - return m_context; - } - - [[nodiscard]] auto device() -> VkDevice - { - return m_context->device().vk(); - } - -private: - Ref m_registry; - - Ref m_surface_system; - - Scope m_surface_entity; - - Ref m_context; -}; - -Suite raii = "raii"_suite = [] { - Case { "happy path won't throw" } = [] { - auto fixture = VkPipelineTest {}; - std::ignore = Pipeline { { .context = fixture.context() } }; - }; - - Case { "unhappy path throws" } = [] { - expect_throw([] { std::ignore = Pipeline { { .context = nullptr } }; }); - }; -}; diff --git a/modules/renderer/private/vk/renderer/pass.cpp b/modules/renderer/private/vk/renderer/pass.cpp deleted file mode 100644 index e69de29..0000000 diff --git a/modules/renderer/private/vk/renderer/pass.hpp b/modules/renderer/private/vk/renderer/pass.hpp deleted file mode 100644 index 7fea5c7..0000000 --- a/modules/renderer/private/vk/renderer/pass.hpp +++ /dev/null @@ -1,281 +0,0 @@ -#pragma once - -#include -#include -#include - -namespace lt::renderer::vk { - -class Pass -{ -public: - Pass( - Context &context, - lt::assets::ShaderAsset vertex_shader, - lt::assets::ShaderAsset fragment_shader - ) - : m_device(context.device().vk()) - { - // auto fragment_blob = vertex_shader.unpack(lt::assets::ShaderAsset::BlobTag::code); - - auto *vertex_module = create_module( - vertex_shader.unpack(lt::assets::ShaderAsset::BlobTag::code) - ); - - auto *fragment_module = create_module( - fragment_shader.unpack(lt::assets::ShaderAsset::BlobTag::code) - ); - - auto shader_stages = std::array { - VkPipelineShaderStageCreateInfo { - .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, - .stage = VK_SHADER_STAGE_VERTEX_BIT, - .module = vertex_module, - .pName = "main", - }, - VkPipelineShaderStageCreateInfo { - .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, - .stage = VK_SHADER_STAGE_FRAGMENT_BIT, - .module = fragment_module, - .pName = "main", - }, - }; - - auto dynamic_states = std::array { - VK_DYNAMIC_STATE_VIEWPORT, - VK_DYNAMIC_STATE_SCISSOR, - }; - - auto dynamic_state = VkPipelineDynamicStateCreateInfo { - .sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO, - .dynamicStateCount = static_cast(dynamic_states.size()), - .pDynamicStates = dynamic_states.data(), - }; - - auto vertex_input = VkPipelineVertexInputStateCreateInfo { - .sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO, - }; - - auto input_assembly = VkPipelineInputAssemblyStateCreateInfo { - .sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO, - .topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, - .primitiveRestartEnable = VK_FALSE, - }; - - auto viewport_state = VkPipelineViewportStateCreateInfo { - .sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO, - .viewportCount = 1u, - .scissorCount = 1u, - }; - - auto rasterization = VkPipelineRasterizationStateCreateInfo { - .sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO, - .depthClampEnable = VK_FALSE, - .rasterizerDiscardEnable = VK_FALSE, - .polygonMode = VK_POLYGON_MODE_FILL, - .cullMode = VK_CULL_MODE_NONE, - .frontFace = VK_FRONT_FACE_CLOCKWISE, - .lineWidth = 1.0, - }; - - auto multisampling = VkPipelineMultisampleStateCreateInfo { - .sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO, - .rasterizationSamples = VK_SAMPLE_COUNT_1_BIT, - .sampleShadingEnable = VK_FALSE, - .minSampleShading = 1.0, - .pSampleMask = nullptr, - .alphaToCoverageEnable = VK_FALSE, - .alphaToOneEnable = VK_FALSE, - }; - - auto color_blend_attachment = VkPipelineColorBlendAttachmentState { - .blendEnable = VK_FALSE, - .srcColorBlendFactor = VK_BLEND_FACTOR_ONE, - .dstColorBlendFactor = VK_BLEND_FACTOR_ZERO, - .colorBlendOp = VK_BLEND_OP_ADD, - .srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE, - .dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO, - .alphaBlendOp = VK_BLEND_OP_ADD, - .colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT - | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT, - }; - - auto color_blend = VkPipelineColorBlendStateCreateInfo { - .sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO, - .logicOpEnable = VK_FALSE, - .logicOp = VK_LOGIC_OP_COPY, - .attachmentCount = 1, - .pAttachments = &color_blend_attachment, - .blendConstants = { 0.0f, 0.0, 0.0, 0.0 }, - }; - - auto layout_info = VkPipelineLayoutCreateInfo { - .sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, - .setLayoutCount = 0u, - .pSetLayouts = nullptr, - .pushConstantRangeCount = 0u, - .pPushConstantRanges = nullptr, - }; - - vkc(vk_create_pipeline_layout(m_device, &layout_info, nullptr, &m_layout)); - - auto attachment_description = VkAttachmentDescription { - .format = context.swapchain().get_format(), - .samples = VK_SAMPLE_COUNT_1_BIT, - .loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR, - .storeOp = VK_ATTACHMENT_STORE_OP_STORE, - .stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE, - .stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE, - .initialLayout = VK_IMAGE_LAYOUT_UNDEFINED, - .finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, - }; - - auto color_attachment_ref = VkAttachmentReference { - .attachment = 0, - .layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, - }; - - auto subpass_description = VkSubpassDescription { - .pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS, - .colorAttachmentCount = 1u, - .pColorAttachments = &color_attachment_ref, - }; - - auto pass_dependency = VkSubpassDependency { - .srcSubpass = VK_SUBPASS_EXTERNAL, - .dstSubpass = 0u, - .srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, - .dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, - .srcAccessMask = 0u, - .dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, - }; - - auto renderpass_info = VkRenderPassCreateInfo { - .sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO, - .attachmentCount = 1u, - .pAttachments = &attachment_description, - .subpassCount = 1u, - .pSubpasses = &subpass_description, - .dependencyCount = 1u, - .pDependencies = &pass_dependency, - }; - - vkc(vk_create_render_pass(m_device, &renderpass_info, nullptr, &m_pass)); - - auto pipeline_info = VkGraphicsPipelineCreateInfo { - .sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO, - .stageCount = static_cast(shader_stages.size()), - .pStages = shader_stages.data(), - .pVertexInputState = &vertex_input, - .pInputAssemblyState = &input_assembly, - .pViewportState = &viewport_state, - .pRasterizationState = &rasterization, - .pMultisampleState = &multisampling, - .pDepthStencilState = nullptr, - .pColorBlendState = &color_blend, - .pDynamicState = &dynamic_state, - .layout = m_layout, - .renderPass = m_pass, - .subpass = 0u, - .basePipelineHandle = VK_NULL_HANDLE, - .basePipelineIndex = -1, - }; - - vkc(vk_create_graphics_pipelines( - m_device, - VK_NULL_HANDLE, - 1u, - &pipeline_info, - nullptr, - &m_pipeline - )); - - vk_destroy_shader_module(m_device, vertex_module, nullptr); - vk_destroy_shader_module(m_device, fragment_module, nullptr); - - m_framebuffers = context.swapchain().create_framebuffers_for_pass(m_pass); - } - - ~Pass() - { - if (!m_device) - { - return; - } - - for (auto &framebuffer : m_framebuffers) - { - vk_destroy_frame_buffer(m_device, framebuffer, nullptr); - } - - vk_destroy_pipeline(m_device, m_pipeline, nullptr); - vk_destroy_render_pass(m_device, m_pass, nullptr); - vk_destroy_pipeline_layout(m_device, m_layout, nullptr); - } - - Pass(Pass &&) = default; - - Pass(const Pass &) = delete; - - auto operator=(Pass &&) -> Pass & = default; - - auto operator=(const Pass &) -> Pass & = delete; - - void replace_swapchain(const Swapchain &swapchain) - { - if (!m_device) - { - return; - } - - vk_device_wait_idle(m_device); - for (auto &framebuffer : m_framebuffers) - { - vk_destroy_frame_buffer(m_device, framebuffer, nullptr); - } - - m_framebuffers = swapchain.create_framebuffers_for_pass(m_pass); - } - - [[nodiscard]] auto get_pass() -> VkRenderPass - { - return m_pass; - } - - [[nodiscard]] auto get_pipeline() -> VkPipeline - { - return m_pipeline; - } - - [[nodiscard]] auto get_framebuffers() -> std::vector & - { - return m_framebuffers; - } - -private: - auto create_module(lt::assets::Blob blob) -> VkShaderModule - { - auto info = VkShaderModuleCreateInfo { - .sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO, - .codeSize = blob.size(), - .pCode = reinterpret_cast(blob.data()) // NOLINT - }; - - auto *module = VkShaderModule { VK_NULL_HANDLE }; - vkc(vk_create_shader_module(m_device, &info, nullptr, &module)); - - return module; - } - - memory::NullOnMove m_device = VK_NULL_HANDLE; - - memory::NullOnMove m_pipeline = VK_NULL_HANDLE; - - memory::NullOnMove m_pass = VK_NULL_HANDLE; - - memory::NullOnMove m_layout = VK_NULL_HANDLE; - - std::vector m_framebuffers; -}; - -} // namespace lt::renderer::vk diff --git a/modules/renderer/private/vk/renderer/renderer.cpp b/modules/renderer/private/vk/renderer/renderer.cpp deleted file mode 100644 index e69de29..0000000 diff --git a/modules/renderer/private/vk/renderer/renderer.hpp b/modules/renderer/private/vk/renderer/renderer.hpp deleted file mode 100644 index 70ca0e1..0000000 --- a/modules/renderer/private/vk/renderer/renderer.hpp +++ /dev/null @@ -1,355 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include