feat(renderer): vulkan triangle
This commit is contained in:
parent
01db551fa9
commit
ebf1f54d31
20 changed files with 694 additions and 56 deletions
|
@ -6,6 +6,8 @@ add_library_module(renderer
|
|||
vk/context/device.cpp
|
||||
vk/context/swapchain.cpp
|
||||
vk/context/context.cpp
|
||||
vk/renderer/pass.cpp
|
||||
vk/renderer/renderer.cpp
|
||||
vk/pipeline.cpp
|
||||
)
|
||||
|
||||
|
@ -14,6 +16,8 @@ PUBLIC
|
|||
app
|
||||
ecs
|
||||
memory
|
||||
assets
|
||||
time
|
||||
PRIVATE
|
||||
surface
|
||||
pthread
|
||||
|
@ -28,6 +32,8 @@ add_test_module(renderer
|
|||
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
|
||||
|
|
|
@ -2,4 +2,16 @@
|
|||
|
||||
namespace lt::renderer {
|
||||
|
||||
void System::on_register()
|
||||
{
|
||||
}
|
||||
|
||||
void System::on_unregister()
|
||||
{
|
||||
}
|
||||
|
||||
void System::tick(app::TickInfo tick)
|
||||
{
|
||||
}
|
||||
|
||||
} // namespace lt::renderer
|
||||
|
|
|
@ -3,9 +3,8 @@
|
|||
|
||||
namespace lt::renderer::vk {
|
||||
|
||||
Context::Context(const ecs::Entity &surface_entity, Ref<app::SystemStats> system_stats)
|
||||
: m_stats(std::move(system_stats))
|
||||
, m_surface(surface_entity)
|
||||
Context::Context(const ecs::Entity &surface_entity)
|
||||
: m_surface(surface_entity)
|
||||
, m_device(m_surface)
|
||||
, m_swapchain(m_device, m_surface)
|
||||
{
|
||||
|
|
|
@ -18,7 +18,7 @@ using memory::NullOnMove;
|
|||
class Context
|
||||
{
|
||||
public:
|
||||
Context(const ecs::Entity &surface_entity, Ref<app::SystemStats> system_stats);
|
||||
Context(const ecs::Entity &surface_entity);
|
||||
|
||||
[[nodiscard]] auto instance() const -> VkInstance
|
||||
{
|
||||
|
@ -36,8 +36,6 @@ public:
|
|||
}
|
||||
|
||||
private:
|
||||
Ref<app::SystemStats> m_stats;
|
||||
|
||||
Surface m_surface;
|
||||
|
||||
Device m_device;
|
||||
|
|
|
@ -10,9 +10,12 @@ 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);
|
||||
initialize_queue(surface);
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
Device::~Device()
|
||||
|
@ -54,12 +57,20 @@ void Device::initialize_logical_device()
|
|||
{
|
||||
const float priorities = .0f;
|
||||
|
||||
auto queue_info = VkDeviceQueueCreateInfo {
|
||||
.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO,
|
||||
.queueFamilyIndex = find_suitable_queue_family(),
|
||||
.queueCount = 1u,
|
||||
.pQueuePriorities = &priorities,
|
||||
};
|
||||
auto queue_infos = std::vector<VkDeviceQueueCreateInfo> {};
|
||||
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 {};
|
||||
|
||||
|
@ -69,8 +80,8 @@ void Device::initialize_logical_device()
|
|||
|
||||
auto device_info = VkDeviceCreateInfo {
|
||||
.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO,
|
||||
.queueCreateInfoCount = 1,
|
||||
.pQueueCreateInfos = &queue_info,
|
||||
.queueCreateInfoCount = static_cast<uint32_t>(queue_infos.size()),
|
||||
.pQueueCreateInfos = queue_infos.data(),
|
||||
.enabledExtensionCount = static_cast<uint32_t>(extensions.size()),
|
||||
.ppEnabledExtensionNames = extensions.data(),
|
||||
.pEnabledFeatures = &physical_device_features,
|
||||
|
@ -84,30 +95,28 @@ void Device::initialize_logical_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<VkQueueFamilyProperties>(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;
|
||||
// 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<VkQueueFamilyProperties>(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(const Surface &surface)
|
||||
void Device::initialize_queue_indices(const Surface &surface)
|
||||
{
|
||||
vk_get_device_queue(m_device, find_suitable_queue_family(), 0, &m_queue);
|
||||
|
||||
auto count = uint32_t { 0u };
|
||||
vk_get_physical_device_queue_family_properties(m_physical_device, &count, nullptr);
|
||||
|
||||
|
|
|
@ -35,12 +35,22 @@ public:
|
|||
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(const class Surface &surface);
|
||||
void initialize_queue_indices(const class Surface &surface);
|
||||
|
||||
[[nodiscard]] auto find_suitable_queue_family() const -> uint32_t;
|
||||
|
||||
|
@ -49,7 +59,9 @@ private:
|
|||
|
||||
memory::NullOnMove<VkDevice> m_device = VK_NULL_HANDLE;
|
||||
|
||||
memory::NullOnMove<VkQueue> m_queue = VK_NULL_HANDLE;
|
||||
memory::NullOnMove<VkQueue> m_graphics_queue = VK_NULL_HANDLE;
|
||||
|
||||
memory::NullOnMove<VkQueue> m_present_queue = VK_NULL_HANDLE;
|
||||
|
||||
uint32_t m_graphics_queue_family_index = VK_QUEUE_FAMILY_IGNORED;
|
||||
|
||||
|
|
|
@ -86,6 +86,8 @@ PFN_vkCmdDraw vk_cmd_draw {};
|
|||
PFN_vkCmdSetViewport vk_cmd_set_viewport {};
|
||||
PFN_vkCmdSetScissor vk_cmd_set_scissors {};
|
||||
|
||||
PFN_vkResetCommandBuffer vk_reset_command_buffer {};
|
||||
|
||||
PFN_vkGetPhysicalDeviceSurfaceSupportKHR vk_get_physical_device_surface_support {};
|
||||
PFN_vkGetPhysicalDeviceSurfaceCapabilitiesKHR vk_get_physical_device_surface_capabilities {};
|
||||
PFN_vkGetPhysicalDeviceSurfaceFormatsKHR vk_get_physical_device_surface_formats {};
|
||||
|
@ -370,6 +372,7 @@ void Instance::load_device_functions_impl(VkDevice device)
|
|||
load_fn(vk_cmd_draw, "vkCmdDraw");
|
||||
load_fn(vk_cmd_set_viewport, "vkCmdSetViewport");
|
||||
load_fn(vk_cmd_set_scissors, "vkCmdSetScissor");
|
||||
load_fn(vk_reset_command_buffer, "vkResetCommandBuffer");
|
||||
}
|
||||
|
||||
auto parse_message_type(VkDebugUtilsMessageTypeFlagsEXT message_types) -> const char *
|
||||
|
|
|
@ -7,7 +7,9 @@
|
|||
|
||||
namespace lt::renderer::vk {
|
||||
|
||||
Swapchain::Swapchain(const Device &device, const Surface &surface): m_device(device.vk())
|
||||
Swapchain::Swapchain(const Device &device, const Surface &surface)
|
||||
: m_device(device.vk())
|
||||
, m_resolution(surface.get_framebuffer_size())
|
||||
{
|
||||
auto *physical_device = device.physical();
|
||||
|
||||
|
@ -30,6 +32,7 @@ Swapchain::Swapchain(const Device &device, const Surface &surface): m_device(dev
|
|||
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,
|
||||
|
@ -45,7 +48,7 @@ Swapchain::Swapchain(const Device &device, const Surface &surface): m_device(dev
|
|||
.pQueueFamilyIndices = queue_indices.data(),
|
||||
.preTransform = capabilities.currentTransform,
|
||||
.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR,
|
||||
.presentMode = VK_PRESENT_MODE_FIFO_RELAXED_KHR, // TODO(Light): parameterize
|
||||
.presentMode = VK_PRESENT_MODE_FIFO_KHR, // TODO(Light): parameterize
|
||||
.clipped = VK_TRUE,
|
||||
.oldSwapchain = nullptr,
|
||||
};
|
||||
|
@ -84,6 +87,7 @@ Swapchain::Swapchain(const Device &device, const Surface &surface): m_device(dev
|
|||
|
||||
vkc(vk_create_image_view(device.vk(), &create_info, nullptr, &view));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Swapchain::~Swapchain()
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
#pragma once
|
||||
|
||||
#include <memory/pointer_types/null_on_move.hpp>
|
||||
#include <renderer/vk/debug/validation.hpp>
|
||||
#include <renderer/vk/vulkan.hpp>
|
||||
|
||||
namespace lt::renderer::vk {
|
||||
|
@ -20,6 +21,44 @@ public:
|
|||
|
||||
auto operator=(const Swapchain &) const -> Swapchain & = delete;
|
||||
|
||||
[[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 create_framebuffers_for_pass(VkRenderPass pass) const
|
||||
-> std::vector<VkFramebuffer>
|
||||
{
|
||||
auto framebuffers = std::vector<VkFramebuffer>(m_swapchain_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_swapchain_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,
|
||||
|
@ -33,6 +72,10 @@ private:
|
|||
std::vector<VkImage> m_swapchain_images;
|
||||
|
||||
std::vector<VkImageView> m_swapchain_image_views;
|
||||
|
||||
VkExtent2D m_resolution;
|
||||
|
||||
VkFormat m_format;
|
||||
};
|
||||
|
||||
} // namespace lt::renderer::vk
|
||||
|
|
|
@ -24,7 +24,7 @@ public:
|
|||
.resolution = constants::resolution,
|
||||
});
|
||||
|
||||
m_context = create_ref<Context>(*m_surface_entity, m_stats);
|
||||
m_context = create_ref<Context>(*m_surface_entity);
|
||||
}
|
||||
|
||||
[[nodiscard]] auto context() -> Ref<Context>
|
||||
|
@ -38,8 +38,6 @@ public:
|
|||
}
|
||||
|
||||
private:
|
||||
Ref<app::SystemStats> m_stats;
|
||||
|
||||
Ref<ecs::Registry> m_registry;
|
||||
|
||||
Ref<surface::System> m_surface_system;
|
||||
|
|
0
modules/renderer/private/vk/renderer/pass.cpp
Normal file
0
modules/renderer/private/vk/renderer/pass.cpp
Normal file
280
modules/renderer/private/vk/renderer/pass.hpp
Normal file
280
modules/renderer/private/vk/renderer/pass.hpp
Normal file
|
@ -0,0 +1,280 @@
|
|||
#pragma once
|
||||
|
||||
#include <assets/shader.hpp>
|
||||
#include <renderer/vk/context/context.hpp>
|
||||
#include <renderer/vk/debug/validation.hpp>
|
||||
|
||||
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, 2> {
|
||||
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<VkDynamicState, 2> {
|
||||
VK_DYNAMIC_STATE_VIEWPORT,
|
||||
VK_DYNAMIC_STATE_SCISSOR,
|
||||
};
|
||||
|
||||
auto dynamic_state = VkPipelineDynamicStateCreateInfo {
|
||||
.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO,
|
||||
.dynamicStateCount = static_cast<uint32_t>(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 = VkViewport {
|
||||
.x = 0u,
|
||||
.y = 0u,
|
||||
.width = static_cast<float>(context.swapchain().get_resolution().width),
|
||||
.height = static_cast<float>(context.swapchain().get_resolution().height),
|
||||
.minDepth = 0.0f,
|
||||
.maxDepth = 0.0f,
|
||||
};
|
||||
|
||||
auto scissor = VkRect2D {
|
||||
.offset = { 0u, 0u },
|
||||
.extent = context.swapchain().get_resolution(),
|
||||
};
|
||||
|
||||
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 {
|
||||
.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT
|
||||
| VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT,
|
||||
.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,
|
||||
};
|
||||
|
||||
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<uint32_t>(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;
|
||||
|
||||
[[nodiscard]] auto get_pass() -> VkRenderPass
|
||||
{
|
||||
return m_pass;
|
||||
}
|
||||
|
||||
[[nodiscard]] auto get_pipeline() -> VkPipeline
|
||||
{
|
||||
return m_pipeline;
|
||||
}
|
||||
|
||||
[[nodiscard]] auto get_framebuffers() -> std::vector<VkFramebuffer> &
|
||||
{
|
||||
return m_framebuffers;
|
||||
}
|
||||
|
||||
private:
|
||||
auto create_module(lt::assets::Blob blob) -> VkShaderModule
|
||||
{
|
||||
log_dbg("BLOB SIZE: {}", blob.size());
|
||||
auto info = VkShaderModuleCreateInfo {
|
||||
.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO,
|
||||
.codeSize = blob.size(),
|
||||
.pCode = reinterpret_cast<const uint32_t *>(blob.data()) // NOLINT
|
||||
};
|
||||
|
||||
auto *module = VkShaderModule { VK_NULL_HANDLE };
|
||||
vkc(vk_create_shader_module(m_device, &info, nullptr, &module));
|
||||
|
||||
return module;
|
||||
}
|
||||
|
||||
memory::NullOnMove<VkDevice> m_device = VK_NULL_HANDLE;
|
||||
|
||||
memory::NullOnMove<VkPipeline> m_pipeline = VK_NULL_HANDLE;
|
||||
|
||||
memory::NullOnMove<VkRenderPass> m_pass = VK_NULL_HANDLE;
|
||||
|
||||
memory::NullOnMove<VkPipelineLayout> m_layout = VK_NULL_HANDLE;
|
||||
|
||||
std::vector<VkFramebuffer> m_framebuffers;
|
||||
};
|
||||
|
||||
} // namespace lt::renderer::vk
|
20
modules/renderer/private/vk/renderer/pass.test.cpp
Normal file
20
modules/renderer/private/vk/renderer/pass.test.cpp
Normal file
|
@ -0,0 +1,20 @@
|
|||
#include <renderer/vk/renderer/pass.hpp>
|
||||
#include <renderer/vk/test_utils.hpp>
|
||||
|
||||
using ::lt::assets::ShaderAsset;
|
||||
using ::lt::renderer::vk::Pass;
|
||||
|
||||
Suite raii = "pass_raii"_suite = [] {
|
||||
Case { "happy path won't throw" } = [] {
|
||||
auto observer = ValidationObserver {};
|
||||
auto [context, _] = create_context();
|
||||
|
||||
std::ignore = Pass {
|
||||
context,
|
||||
ShaderAsset { "./data/test_assets/triangle.vert.asset" },
|
||||
ShaderAsset { "./data/test_assets/triangle.frag.asset" },
|
||||
};
|
||||
|
||||
expect_false(observer.had_any_messages());
|
||||
};
|
||||
};
|
0
modules/renderer/private/vk/renderer/renderer.cpp
Normal file
0
modules/renderer/private/vk/renderer/renderer.cpp
Normal file
203
modules/renderer/private/vk/renderer/renderer.hpp
Normal file
203
modules/renderer/private/vk/renderer/renderer.hpp
Normal file
|
@ -0,0 +1,203 @@
|
|||
#pragma once
|
||||
|
||||
#include <renderer/vk/context/context.hpp>
|
||||
#include <renderer/vk/debug/validation.hpp>
|
||||
#include <renderer/vk/renderer/pass.hpp>
|
||||
#include <time/timer.hpp>
|
||||
|
||||
namespace lt::renderer::vk {
|
||||
|
||||
class Renderer
|
||||
{
|
||||
public:
|
||||
Renderer(Context &context, Ref<Pass> pass)
|
||||
: m_device(context.device().vk())
|
||||
, m_graphics_queue(context.device().get_graphics_queue())
|
||||
, m_present_queue(context.device().get_present_queue())
|
||||
, m_swapchain(context.swapchain().vk())
|
||||
, m_pass(std::move(pass))
|
||||
, m_resolution(context.swapchain().get_resolution())
|
||||
{
|
||||
auto pool_info = VkCommandPoolCreateInfo {
|
||||
.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO,
|
||||
.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT,
|
||||
.queueFamilyIndex = context.device().get_family_indices()[0],
|
||||
|
||||
};
|
||||
vkc(vk_create_command_pool(m_device, &pool_info, nullptr, &m_pool));
|
||||
|
||||
auto cmd_info = VkCommandBufferAllocateInfo {
|
||||
.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO,
|
||||
.commandPool = m_pool,
|
||||
.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY,
|
||||
.commandBufferCount = 1u,
|
||||
};
|
||||
vkc(vk_allocate_command_buffers(m_device, &cmd_info, &m_cmd));
|
||||
|
||||
auto semaphore_info = VkSemaphoreCreateInfo {
|
||||
.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO,
|
||||
};
|
||||
|
||||
auto fence_info = VkFenceCreateInfo {
|
||||
.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO,
|
||||
.flags = VK_FENCE_CREATE_SIGNALED_BIT,
|
||||
};
|
||||
|
||||
vkc(vk_create_semaphore(m_device, &semaphore_info, nullptr, &m_image_available_semaphore));
|
||||
vkc(vk_create_semaphore(m_device, &semaphore_info, nullptr, &m_render_finished_semaphore));
|
||||
vkc(vk_create_fence(m_device, &fence_info, nullptr, &m_in_flight_fence));
|
||||
};
|
||||
|
||||
void 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));
|
||||
|
||||
static auto timer = Timer {};
|
||||
|
||||
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<float>(m_resolution.width),
|
||||
.height = static_cast<float>(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));
|
||||
}
|
||||
|
||||
void draw()
|
||||
{
|
||||
try
|
||||
{
|
||||
vkc(vk_wait_for_fences(m_device, 1u, &m_in_flight_fence, VK_TRUE, UINT64_MAX));
|
||||
vkc(vk_reset_fences(m_device, 1u, &m_in_flight_fence));
|
||||
|
||||
auto image_idx = uint32_t {};
|
||||
vkc(vk_acquire_next_image_khr(
|
||||
m_device,
|
||||
m_swapchain,
|
||||
UINT64_MAX,
|
||||
m_image_available_semaphore,
|
||||
VK_NULL_HANDLE,
|
||||
&image_idx
|
||||
));
|
||||
|
||||
vkc(vk_reset_command_buffer(m_cmd, {}));
|
||||
record_cmd(m_cmd, image_idx);
|
||||
|
||||
auto wait_stage = VkPipelineStageFlags {
|
||||
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT
|
||||
};
|
||||
auto submit_info = VkSubmitInfo {
|
||||
.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO,
|
||||
.waitSemaphoreCount = 1u,
|
||||
.pWaitSemaphores = &m_image_available_semaphore,
|
||||
.pWaitDstStageMask = &wait_stage,
|
||||
.commandBufferCount = 1u,
|
||||
.pCommandBuffers = &m_cmd,
|
||||
.signalSemaphoreCount = 1u,
|
||||
.pSignalSemaphores = &m_render_finished_semaphore,
|
||||
};
|
||||
|
||||
vkc(vk_queue_submit(m_graphics_queue, 1u, &submit_info, m_in_flight_fence));
|
||||
|
||||
auto present_info = VkPresentInfoKHR {
|
||||
.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR,
|
||||
.waitSemaphoreCount = 1u,
|
||||
.pWaitSemaphores = &m_render_finished_semaphore,
|
||||
.swapchainCount = 1u,
|
||||
.pSwapchains = &m_swapchain,
|
||||
.pImageIndices = &image_idx,
|
||||
.pResults = nullptr,
|
||||
};
|
||||
|
||||
vk_queue_present_khr(m_present_queue, &present_info);
|
||||
}
|
||||
catch (const std::exception &exp)
|
||||
{
|
||||
log_dbg("EXCEPTION: {}", exp.what());
|
||||
}
|
||||
}
|
||||
|
||||
~Renderer()
|
||||
{
|
||||
if (!m_device)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
vk_destroy_semaphore(m_device, m_render_finished_semaphore, nullptr);
|
||||
vk_destroy_semaphore(m_device, m_image_available_semaphore, nullptr);
|
||||
vk_destroy_fence(m_device, m_in_flight_fence, nullptr);
|
||||
vk_destroy_command_pool(m_device, m_pool, nullptr);
|
||||
}
|
||||
|
||||
Renderer(Renderer &&) = default;
|
||||
|
||||
Renderer(const Renderer &) = delete;
|
||||
|
||||
auto operator=(Renderer &&) -> Renderer & = default;
|
||||
|
||||
auto operator=(const Renderer &) -> Renderer & = delete;
|
||||
|
||||
private:
|
||||
memory::NullOnMove<VkDevice> m_device = VK_NULL_HANDLE;
|
||||
|
||||
memory::NullOnMove<VkCommandPool> m_pool = VK_NULL_HANDLE;
|
||||
|
||||
memory::NullOnMove<VkCommandBuffer> m_cmd = VK_NULL_HANDLE;
|
||||
|
||||
memory::NullOnMove<VkSemaphore> m_image_available_semaphore = VK_NULL_HANDLE;
|
||||
|
||||
memory::NullOnMove<VkSemaphore> m_render_finished_semaphore = VK_NULL_HANDLE;
|
||||
|
||||
memory::NullOnMove<VkFence> m_in_flight_fence = VK_NULL_HANDLE;
|
||||
|
||||
memory::NullOnMove<VkSwapchainKHR> m_swapchain = VK_NULL_HANDLE;
|
||||
|
||||
memory::NullOnMove<VkQueue> m_graphics_queue = VK_NULL_HANDLE;
|
||||
|
||||
memory::NullOnMove<VkQueue> m_present_queue = VK_NULL_HANDLE;
|
||||
|
||||
Ref<Pass> m_pass;
|
||||
|
||||
VkExtent2D m_resolution;
|
||||
};
|
||||
|
||||
} // namespace lt::renderer::vk
|
29
modules/renderer/private/vk/renderer/renderer.test.cpp
Normal file
29
modules/renderer/private/vk/renderer/renderer.test.cpp
Normal file
|
@ -0,0 +1,29 @@
|
|||
#include <renderer/vk/renderer/renderer.hpp>
|
||||
#include <renderer/vk/test_utils.hpp>
|
||||
|
||||
using ::lt::assets::ShaderAsset;
|
||||
using ::lt::renderer::vk::Pass;
|
||||
using ::lt::renderer::vk::Renderer;
|
||||
|
||||
Suite raii = "renderer_raii"_suite = [] {
|
||||
Case { "happy path won't throw" } = [] {
|
||||
auto observer = ValidationObserver {};
|
||||
auto [context, _] = create_context();
|
||||
|
||||
auto pass = lt::create_ref<Pass>(
|
||||
context,
|
||||
ShaderAsset { "./data/test_assets/triangle.vert.asset" },
|
||||
ShaderAsset { "./data/test_assets/triangle.frag.asset" }
|
||||
);
|
||||
|
||||
|
||||
auto renderer = Renderer(context, pass);
|
||||
|
||||
for (;;)
|
||||
{
|
||||
renderer.draw();
|
||||
}
|
||||
|
||||
expect_false(observer.had_any_messages());
|
||||
};
|
||||
};
|
|
@ -1,6 +1,7 @@
|
|||
#pragma once
|
||||
|
||||
#include <ranges>
|
||||
#include <renderer/vk/context/context.hpp>
|
||||
#include <renderer/vk/context/surface.hpp>
|
||||
#include <renderer/vk/debug/messenger.hpp>
|
||||
#include <surface/components.hpp>
|
||||
|
@ -32,7 +33,7 @@ public:
|
|||
ValidationObserver()
|
||||
: m_messenger(
|
||||
Messenger::CreateInfo {
|
||||
.severity = all_severity,
|
||||
.severity = static_cast<Messenger::Severity>(warning | error),
|
||||
.type = lt::renderer::vk::Messenger::all_type,
|
||||
.callback = &callback,
|
||||
.user_data = &m_had_any_messages,
|
||||
|
@ -67,6 +68,22 @@ private:
|
|||
bool m_had_any_messages = false;
|
||||
};
|
||||
|
||||
[[nodiscard]] inline auto create_context()
|
||||
-> std::pair<lt::renderer::vk::Context, lt::surface::System>
|
||||
{
|
||||
using lt::surface::SurfaceComponent;
|
||||
|
||||
auto registry = lt::create_ref<lt::ecs::Registry>();
|
||||
auto entity = lt::ecs::Entity { registry, registry->create_entity() };
|
||||
auto surface_system = lt::surface::System(registry);
|
||||
entity.add<SurfaceComponent>(SurfaceComponent::CreateInfo {
|
||||
.title = "",
|
||||
.resolution = constants::resolution,
|
||||
});
|
||||
|
||||
return { lt::renderer::vk::Context { entity }, std::move(surface_system) };
|
||||
}
|
||||
|
||||
template<>
|
||||
struct std::formatter<VkExtent2D>
|
||||
{
|
||||
|
|
|
@ -86,6 +86,8 @@ extern PFN_vkCmdBindPipeline vk_cmd_bind_pipeline;
|
|||
extern PFN_vkCmdDraw vk_cmd_draw;
|
||||
extern PFN_vkCmdSetViewport vk_cmd_set_viewport;
|
||||
extern PFN_vkCmdSetScissor vk_cmd_set_scissors;
|
||||
|
||||
extern PFN_vkResetCommandBuffer vk_reset_command_buffer;
|
||||
// NOLINTEND(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
|
||||
} // namespace lt::renderer::vk
|
||||
|
|
9
modules/renderer/public/components/skybox.hpp
Normal file
9
modules/renderer/public/components/skybox.hpp
Normal file
|
@ -0,0 +1,9 @@
|
|||
#pragma once
|
||||
|
||||
namespace lt::renderer {
|
||||
|
||||
struct SolidColor
|
||||
{
|
||||
};
|
||||
|
||||
} // namespace lt::renderer
|
|
@ -12,14 +12,16 @@ public:
|
|||
struct CreateInfo
|
||||
{
|
||||
Ref<ecs::Registry> registry;
|
||||
|
||||
ecs::Entity surface_entity;
|
||||
|
||||
Ref<app::SystemStats> system_stats;
|
||||
};
|
||||
|
||||
[[nodiscard]] System(CreateInfo info)
|
||||
: m_registry(std::move(info.registry))
|
||||
, m_stats(info.system_stats)
|
||||
, m_context(info.surface_entity, info.system_stats)
|
||||
, m_context(info.surface_entity)
|
||||
{
|
||||
ensure(m_stats, "Failed to initialize system: null stats");
|
||||
ensure(m_registry, "Failed to initialize renderer system: null registry");
|
||||
|
@ -35,19 +37,11 @@ public:
|
|||
|
||||
auto operator=(const System &) -> System & = delete;
|
||||
|
||||
void on_register() override
|
||||
{
|
||||
}
|
||||
void on_register() override;
|
||||
|
||||
void on_unregister() override
|
||||
{
|
||||
}
|
||||
void on_unregister() override;
|
||||
|
||||
void get_validation_state();
|
||||
|
||||
void tick(app::TickInfo tick) override
|
||||
{
|
||||
}
|
||||
void tick(app::TickInfo tick) override;
|
||||
|
||||
[[nodiscard]] auto get_stats() const -> const app::SystemStats &
|
||||
{
|
||||
|
|
Loading…
Add table
Reference in a new issue