Compare commits

...

4 Commits

Author SHA1 Message Date
608dc583be libinput for keyboard input :3
Signed-off-by: Slendi <slendi@socopon.com>
2025-12-11 23:47:00 +02:00
c7283c8faf Create Pipeline struct
Signed-off-by: Slendi <slendi@socopon.com>
2025-12-11 23:04:30 +02:00
054a196b13 Move to C++ vulkan API
Signed-off-by: Slendi <slendi@socopon.com>
2025-12-11 22:46:55 +02:00
c856e0414c Textures
Signed-off-by: Slendi <slendi@socopon.com>
2025-12-11 21:12:12 +02:00
16 changed files with 1107 additions and 696 deletions

View File

@@ -34,6 +34,7 @@
wayland
zlib
sdl3
libinput
];
in
{

View File

@@ -23,6 +23,8 @@ vulkan_dep = dependency('vulkan')
openxr_dep = dependency('openxr')
zlib_dep = dependency('zlib')
sdl3_dep = dependency('sdl3')
libinput_dep = dependency('libinput')
libudev_dep = dependency('libudev')
imgui_src = files(
'thirdparty/imgui/imgui.cpp',
'thirdparty/imgui/imgui_draw.cpp',
@@ -71,6 +73,15 @@ add_project_arguments(
'-Wno-switch-enum',
'-Wno-sign-conversion',
'-Wno-documentation',
'-Wno-float-equal',
'-Wno-cast-function-type-strict',
'-Wno-exit-time-destructors',
'-Wno-zero-as-null-pointer-constant',
'-Wno-unused-macros',
'-Wno-suggest-override',
'-Wno-macro-redefined',
'-DVULKAN_HPP_DEFAULT_DISPATCH_LOADER_DYNAMIC_STORAGE',
'-DVULKAN_HPP_DISPATCH_LOADER_DYNAMIC=1',
],
language : 'cpp'
)
@@ -111,6 +122,7 @@ exe = executable('vr-compositor',
'src/DescriptorAllocator.cpp',
'src/GraphicsPipelineBuilder.cpp',
'src/DescriptorAllocatorGrowable.cpp',
'src/Pipeline.cpp',
'src/Loader.cpp',
'src/DescriptorWriter.cpp',
'src/VulkanRenderer.cpp',
@@ -130,6 +142,8 @@ exe = executable('vr-compositor',
zlib_dep,
sdl3_dep,
fastgltf_dep,
libinput_dep,
libudev_dep,
],
cpp_args: [
'--embed-dir=' + join_paths(meson.project_build_root(), 'shaders')

View File

@@ -19,6 +19,7 @@ shader_sources = files(
'triangle.vert',
'triangle_mesh.frag',
'triangle_mesh.vert',
'tex_image.frag',
)
spirv_shaders = []

13
shaders/tex_image.frag Normal file
View File

@@ -0,0 +1,13 @@
#version 450
layout (location = 0) in vec3 in_color;
layout (location = 1) in vec2 in_uv;
layout (location = 0) out vec4 out_frag_color;
layout (set = 0, binding = 0) uniform sampler2D tex;
void main() {
out_frag_color = texture(tex, in_uv) * vec4(in_color, 1.0f);
}

View File

@@ -2,7 +2,7 @@
#extension GL_EXT_buffer_reference : require
layout (location = 0) out vec3 out_color;
layout (location = 1) out vec3 out_uv;
layout (location = 1) out vec2 out_uv;
struct Vertex {
vec3 position;

View File

@@ -1,20 +1,204 @@
#include "Application.h"
#include <algorithm>
#include <cerrno>
#include <cstdint>
#include <fcntl.h>
#include <iostream>
#include <optional>
#include <print>
#include <stdexcept>
#include <unistd.h>
#include <SDL3/SDL_events.h>
#include <SDL3/SDL_init.h>
#include <SDL3/SDL_mouse.h>
#include <SDL3/SDL_timer.h>
#include <SDL3/SDL_video.h>
#include <imgui_impl_sdl3.h>
#include <imgui_impl_vulkan.h>
#include <libinput.h>
#include <libudev.h>
#include <linux/input-event-codes.h>
#include "Util.h"
#include "VulkanRenderer.h"
namespace {
int open_restricted(char const *path, int flags, void * /*user_data*/)
{
int fd { open(path, flags | O_CLOEXEC) };
return fd < 0 ? -errno : fd;
}
void close_restricted(int fd, void * /*user_data*/) { close(fd); }
libinput_interface const g_libinput_interface {
.open_restricted = open_restricted,
.close_restricted = close_restricted,
};
auto linux_key_to_imgui(uint32_t keycode) -> std::optional<ImGuiKey>
{
switch (keycode) {
case KEY_ESC:
return ImGuiKey_Escape;
case KEY_TAB:
return ImGuiKey_Tab;
case KEY_ENTER:
return ImGuiKey_Enter;
case KEY_BACKSPACE:
return ImGuiKey_Backspace;
case KEY_SPACE:
return ImGuiKey_Space;
case KEY_LEFTSHIFT:
return ImGuiKey_LeftShift;
case KEY_RIGHTSHIFT:
return ImGuiKey_RightShift;
case KEY_LEFTCTRL:
return ImGuiKey_LeftCtrl;
case KEY_RIGHTCTRL:
return ImGuiKey_RightCtrl;
case KEY_LEFTALT:
return ImGuiKey_LeftAlt;
case KEY_RIGHTALT:
return ImGuiKey_RightAlt;
case KEY_LEFTMETA:
return ImGuiKey_LeftSuper;
case KEY_RIGHTMETA:
return ImGuiKey_RightSuper;
case KEY_CAPSLOCK:
return ImGuiKey_CapsLock;
case KEY_NUMLOCK:
return ImGuiKey_NumLock;
case KEY_SCROLLLOCK:
return ImGuiKey_ScrollLock;
case KEY_UP:
return ImGuiKey_UpArrow;
case KEY_DOWN:
return ImGuiKey_DownArrow;
case KEY_LEFT:
return ImGuiKey_LeftArrow;
case KEY_RIGHT:
return ImGuiKey_RightArrow;
case KEY_HOME:
return ImGuiKey_Home;
case KEY_END:
return ImGuiKey_End;
case KEY_PAGEUP:
return ImGuiKey_PageUp;
case KEY_PAGEDOWN:
return ImGuiKey_PageDown;
case KEY_INSERT:
return ImGuiKey_Insert;
case KEY_DELETE:
return ImGuiKey_Delete;
case KEY_F1:
return ImGuiKey_F1;
case KEY_F2:
return ImGuiKey_F2;
case KEY_F3:
return ImGuiKey_F3;
case KEY_F4:
return ImGuiKey_F4;
case KEY_F5:
return ImGuiKey_F5;
case KEY_F6:
return ImGuiKey_F6;
case KEY_F7:
return ImGuiKey_F7;
case KEY_F8:
return ImGuiKey_F8;
case KEY_F9:
return ImGuiKey_F9;
case KEY_F10:
return ImGuiKey_F10;
case KEY_F11:
return ImGuiKey_F11;
case KEY_F12:
return ImGuiKey_F12;
case KEY_0:
return ImGuiKey_0;
case KEY_1:
return ImGuiKey_1;
case KEY_2:
return ImGuiKey_2;
case KEY_3:
return ImGuiKey_3;
case KEY_4:
return ImGuiKey_4;
case KEY_5:
return ImGuiKey_5;
case KEY_6:
return ImGuiKey_6;
case KEY_7:
return ImGuiKey_7;
case KEY_8:
return ImGuiKey_8;
case KEY_9:
return ImGuiKey_9;
case KEY_A:
return ImGuiKey_A;
case KEY_B:
return ImGuiKey_B;
case KEY_C:
return ImGuiKey_C;
case KEY_D:
return ImGuiKey_D;
case KEY_E:
return ImGuiKey_E;
case KEY_F:
return ImGuiKey_F;
case KEY_G:
return ImGuiKey_G;
case KEY_H:
return ImGuiKey_H;
case KEY_I:
return ImGuiKey_I;
case KEY_J:
return ImGuiKey_J;
case KEY_K:
return ImGuiKey_K;
case KEY_L:
return ImGuiKey_L;
case KEY_M:
return ImGuiKey_M;
case KEY_N:
return ImGuiKey_N;
case KEY_O:
return ImGuiKey_O;
case KEY_P:
return ImGuiKey_P;
case KEY_Q:
return ImGuiKey_Q;
case KEY_R:
return ImGuiKey_R;
case KEY_S:
return ImGuiKey_S;
case KEY_T:
return ImGuiKey_T;
case KEY_U:
return ImGuiKey_U;
case KEY_V:
return ImGuiKey_V;
case KEY_W:
return ImGuiKey_W;
case KEY_X:
return ImGuiKey_X;
case KEY_Y:
return ImGuiKey_Y;
case KEY_Z:
return ImGuiKey_Z;
default:
return std::nullopt;
}
}
} // namespace
namespace Lunar {
Application::Application()
@@ -33,6 +217,8 @@ Application::Application()
m_renderer = std::make_unique<VulkanRenderer>(m_window, m_logger);
init_input();
mouse_captured(true);
m_logger.info("App init done!");
@@ -42,6 +228,8 @@ Application::~Application()
{
m_renderer.reset();
shutdown_input();
SDL_DestroyWindow(m_window);
SDL_Quit();
@@ -65,7 +253,10 @@ auto Application::run() -> void
if (dt > 0)
fps = 1000.0f / (float)dt;
process_libinput_events();
while (SDL_PollEvent(&e)) {
bool forward_to_imgui { false };
if (e.type == SDL_EVENT_QUIT) {
m_running = false;
} else if (e.type == SDL_EVENT_WINDOW_RESIZED) {
@@ -73,14 +264,25 @@ auto Application::run() -> void
SDL_GetWindowSize(m_window, &width, &height);
m_renderer->resize(static_cast<uint32_t>(width),
static_cast<uint32_t>(height));
} else if (e.type == SDL_EVENT_KEY_DOWN && e.key.repeat == false) {
if (e.key.key == SDLK_F11 && e.key.mod & SDL_KMOD_LCTRL) {
mouse_captured(!mouse_captured());
m_show_imgui = !mouse_captured();
}
clamp_mouse_to_window(width, height);
forward_to_imgui = true;
} else if (e.type == SDL_EVENT_MOUSE_MOTION) {
m_mouse_x = e.motion.x;
m_mouse_y = e.motion.y;
forward_to_imgui = true;
} else if (e.type == SDL_EVENT_MOUSE_BUTTON_DOWN
|| e.type == SDL_EVENT_MOUSE_BUTTON_UP) {
m_mouse_x = e.button.x;
m_mouse_y = e.button.y;
forward_to_imgui = true;
} else if (e.type == SDL_EVENT_MOUSE_WHEEL) {
m_mouse_x = e.wheel.mouse_x;
m_mouse_y = e.wheel.mouse_y;
forward_to_imgui = true;
}
ImGui_ImplSDL3_ProcessEvent(&e);
if (forward_to_imgui)
ImGui_ImplSDL3_ProcessEvent(&e);
}
ImGui_ImplSDL3_NewFrame();
@@ -89,6 +291,7 @@ auto Application::run() -> void
ImGui::NewFrame();
if (m_show_imgui) {
ImGui::SetNextWindowCollapsed(true, ImGuiCond_Once);
ImGui::ShowDemoWindow();
ImGui::SetNextWindowSize({ 100, 50 });
@@ -109,6 +312,111 @@ auto Application::run() -> void
}
}
auto Application::init_input() -> void
{
m_udev = udev_new();
if (!m_udev) {
m_logger.err("Failed to create udev context");
throw std::runtime_error("App init fail");
}
m_libinput
= libinput_udev_create_context(&g_libinput_interface, this, m_udev);
if (!m_libinput) {
m_logger.err("Failed to create libinput context");
shutdown_input();
throw std::runtime_error("App init fail");
}
if (libinput_udev_assign_seat(m_libinput, "seat0") != 0) {
m_logger.err("Failed to assign libinput seat");
shutdown_input();
throw std::runtime_error("App init fail");
}
int width {}, height {};
SDL_GetWindowSize(m_window, &width, &height);
float mouse_x {}, mouse_y {};
SDL_GetMouseState(&mouse_x, &mouse_y);
m_mouse_x = mouse_x;
m_mouse_y = mouse_y;
ImGui::GetIO().AddMousePosEvent(
static_cast<float>(m_mouse_x), static_cast<float>(m_mouse_y));
}
auto Application::shutdown_input() -> void
{
if (m_libinput) {
libinput_unref(m_libinput);
m_libinput = nullptr;
}
if (m_udev) {
udev_unref(m_udev);
m_udev = nullptr;
}
}
auto Application::process_libinput_events() -> void
{
if (!m_libinput)
return;
if (int const rc { libinput_dispatch(m_libinput) }; rc != 0) {
m_logger.err("libinput_dispatch failed ({})", rc);
return;
}
for (libinput_event *event { libinput_get_event(m_libinput) };
event != nullptr; event = libinput_get_event(m_libinput)) {
switch (libinput_event_get_type(event)) {
case LIBINPUT_EVENT_KEYBOARD_KEY:
handle_keyboard_event(libinput_event_get_keyboard_event(event));
break;
default:
break;
}
libinput_event_destroy(event);
}
}
auto Application::handle_keyboard_event(libinput_event_keyboard *event) -> void
{
uint32_t const key { libinput_event_keyboard_get_key(event) };
auto const state { libinput_event_keyboard_get_key_state(event) };
bool const pressed { state == LIBINPUT_KEY_STATE_PRESSED };
if (key == KEY_LEFTCTRL || key == KEY_RIGHTCTRL) {
if (pressed) {
++m_ctrl_pressed_count;
} else if (m_ctrl_pressed_count > 0) {
--m_ctrl_pressed_count;
}
}
if (pressed && key == KEY_F11 && m_ctrl_pressed_count > 0) {
mouse_captured(!mouse_captured());
m_show_imgui = !mouse_captured();
}
if (auto imgui_key { linux_key_to_imgui(key) }) {
ImGui::GetIO().AddKeyEvent(*imgui_key, pressed);
}
}
auto Application::clamp_mouse_to_window(int width, int height) -> void
{
double const max_x { std::max(0.0, static_cast<double>(width - 1)) };
double const max_y { std::max(0.0, static_cast<double>(height - 1)) };
m_mouse_x = std::clamp(m_mouse_x, 0.0, max_x);
m_mouse_y = std::clamp(m_mouse_y, 0.0, max_y);
ImGui::GetIO().AddMousePosEvent(
static_cast<float>(m_mouse_x), static_cast<float>(m_mouse_y));
}
auto Application::mouse_captured(bool new_state) -> void
{
if (!SDL_SetWindowRelativeMouseMode(m_window, new_state)) {

View File

@@ -8,6 +8,11 @@
#include <imgui.h>
struct libinput;
struct libinput_event_keyboard;
struct libinput_event_pointer;
struct udev;
namespace Lunar {
struct VulkanRenderer;
@@ -23,13 +28,26 @@ struct Application {
auto toggle_mouse_captured() -> void { mouse_captured(!m_mouse_captured); }
private:
auto init_input() -> void;
auto shutdown_input() -> void;
auto process_libinput_events() -> void;
auto handle_keyboard_event(libinput_event_keyboard *event) -> void;
auto clamp_mouse_to_window(int width, int height) -> void;
SDL_Window *m_window { nullptr };
Logger m_logger { "Lunar" };
std::unique_ptr<VulkanRenderer> m_renderer;
udev *m_udev { nullptr };
libinput *m_libinput { nullptr };
bool m_running { true };
bool m_mouse_captured { false };
bool m_show_imgui { false };
int m_ctrl_pressed_count { 0 };
double m_mouse_x { 0.0 };
double m_mouse_y { 0.0 };
};
} // namespace Lunar

View File

@@ -41,10 +41,11 @@ auto GraphicsPipelineBuilder::set_shaders(VkShaderModule vs, VkShaderModule fs)
{
m_shader_stages.clear();
m_shader_stages.emplace_back(
vkinit::pipeline_shader_stage(VK_SHADER_STAGE_VERTEX_BIT, vs));
m_shader_stages.emplace_back(
vkinit::pipeline_shader_stage(VK_SHADER_STAGE_FRAGMENT_BIT, fs));
m_shader_stages.emplace_back(vkinit::pipeline_shader_stage(
static_cast<vk::ShaderStageFlagBits>(VK_SHADER_STAGE_VERTEX_BIT), vs));
m_shader_stages.emplace_back(vkinit::pipeline_shader_stage(
static_cast<vk::ShaderStageFlagBits>(VK_SHADER_STAGE_FRAGMENT_BIT),
fs));
return *this;
}

View File

@@ -178,7 +178,7 @@ auto Mesh::load_gltf_meshes(
}
}
constexpr bool OVERRIDE_COLORS = true;
constexpr bool OVERRIDE_COLORS = false;
if (OVERRIDE_COLORS) {
for (auto &vtx : vertices) {
vtx.color = smath::Vec4(vtx.normal, 1.f);

79
src/Pipeline.cpp Normal file
View File

@@ -0,0 +1,79 @@
#include "Pipeline.h"
#include "Util.h"
namespace Lunar {
Pipeline::Builder::Builder(vk::Device device, Logger &logger)
: m_device(device)
, m_logger(logger)
{
}
auto Pipeline::Builder::set_descriptor_set_layouts(
std::span<vk::DescriptorSetLayout const> layouts) -> Builder &
{
m_set_layouts.assign(layouts.begin(), layouts.end());
return *this;
}
auto Pipeline::Builder::set_push_constant_ranges(
std::span<vk::PushConstantRange const> ranges) -> Builder &
{
m_push_constant_ranges.assign(ranges.begin(), ranges.end());
return *this;
}
auto Pipeline::Builder::build_compute(
vk::PipelineShaderStageCreateInfo const &stage) -> Pipeline
{
auto pipeline_layout = build_layout();
vk::ComputePipelineCreateInfo pipeline_ci {};
pipeline_ci.layout = pipeline_layout.get();
pipeline_ci.stage = stage;
auto pipeline_ret = m_device.createComputePipelineUnique({}, pipeline_ci);
VK_CHECK(m_logger, pipeline_ret.result);
return Pipeline {
.pipeline = std::move(pipeline_ret.value),
.layout = std::move(pipeline_layout),
};
}
auto Pipeline::Builder::build_graphics(
std::function<GraphicsPipelineBuilder &(GraphicsPipelineBuilder &)> const
&configure) -> Pipeline
{
auto pipeline_layout = build_layout();
auto builder = GraphicsPipelineBuilder { m_logger };
builder.set_pipeline_layout(
static_cast<VkPipelineLayout>(pipeline_layout.get()));
configure(builder);
auto pipeline_handle = builder.build(static_cast<VkDevice>(m_device));
vk::UniquePipeline pipeline_unique(pipeline_handle,
vk::detail::ObjectDestroy<vk::Device,
VULKAN_HPP_DEFAULT_DISPATCHER_TYPE>(m_device));
return Pipeline {
.pipeline = std::move(pipeline_unique),
.layout = std::move(pipeline_layout),
};
}
auto Pipeline::Builder::build_layout() -> vk::UniquePipelineLayout
{
vk::PipelineLayoutCreateInfo layout_ci {};
layout_ci.setLayoutCount = static_cast<uint32_t>(m_set_layouts.size());
layout_ci.pSetLayouts = m_set_layouts.data();
layout_ci.pushConstantRangeCount
= static_cast<uint32_t>(m_push_constant_ranges.size());
layout_ci.pPushConstantRanges = m_push_constant_ranges.data();
return m_device.createPipelineLayoutUnique(layout_ci);
}
} // namespace Lunar

49
src/Pipeline.h Normal file
View File

@@ -0,0 +1,49 @@
#pragma once
#include <functional>
#include <span>
#include <vector>
#include <vulkan/vulkan.hpp>
#include "GraphicsPipelineBuilder.h"
#include "Logger.h"
namespace Lunar {
struct Pipeline {
vk::UniquePipeline pipeline {};
vk::UniquePipelineLayout layout {};
auto get() const -> vk::Pipeline { return pipeline.get(); }
auto get_layout() const -> vk::PipelineLayout { return layout.get(); }
auto reset() -> void
{
pipeline.reset();
layout.reset();
}
struct Builder {
Builder(vk::Device device, Logger &logger);
auto set_descriptor_set_layouts(
std::span<vk::DescriptorSetLayout const> layouts) -> Builder &;
auto set_push_constant_ranges(
std::span<vk::PushConstantRange const> ranges) -> Builder &;
auto build_compute(vk::PipelineShaderStageCreateInfo const &stage)
-> Pipeline;
auto build_graphics(std::function<GraphicsPipelineBuilder &(
GraphicsPipelineBuilder &)> const &configure) -> Pipeline;
private:
auto build_layout() -> vk::UniquePipelineLayout;
vk::Device m_device {};
Logger &m_logger;
std::vector<vk::DescriptorSetLayout> m_set_layouts {};
std::vector<vk::PushConstantRange> m_push_constant_ranges {};
};
};
} // namespace Lunar

View File

@@ -2,7 +2,7 @@
#include <smath.hpp>
#include <vk_mem_alloc.h>
#include <vulkan/vulkan_core.h>
#include <vulkan/vulkan.hpp>
#include "DeletionQueue.h"
#include "DescriptorAllocatorGrowable.h"
@@ -10,24 +10,24 @@
namespace Lunar {
struct AllocatedImage {
VkImage image;
VkImageView image_view;
vk::Image image;
vk::ImageView image_view;
VmaAllocation allocation;
VkExtent3D extent;
VkFormat format;
vk::Extent3D extent;
vk::Format format;
};
struct AllocatedBuffer {
VkBuffer buffer;
vk::Buffer buffer;
VmaAllocation allocation;
VmaAllocationInfo info;
};
struct FrameData {
VkCommandPool command_pool;
VkCommandBuffer main_command_buffer;
VkSemaphore swapchain_semaphore;
VkFence render_fence;
vk::UniqueCommandPool command_pool;
vk::UniqueCommandBuffer main_command_buffer;
vk::UniqueSemaphore swapchain_semaphore;
vk::UniqueFence render_fence;
DeletionQueue deletion_queue;
DescriptorAllocatorGrowable frame_descriptors;
@@ -43,7 +43,7 @@ struct Vertex {
struct GPUMeshBuffers {
AllocatedBuffer index_buffer, vertex_buffer;
VkDeviceAddress vertex_buffer_address;
vk::DeviceAddress vertex_buffer_address;
};
struct GPUSceneData {

View File

@@ -4,140 +4,110 @@
namespace vkutil {
auto transition_image(VkCommandBuffer cmd, VkImage image,
VkImageLayout current_layout, VkImageLayout new_layout) -> void
auto transition_image(vk::CommandBuffer cmd, vk::Image image,
vk::ImageLayout current_layout, vk::ImageLayout new_layout) -> void
{
VkImageAspectFlags aspect_mask
= (new_layout == VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_OPTIMAL)
? VK_IMAGE_ASPECT_DEPTH_BIT
: VK_IMAGE_ASPECT_COLOR_BIT;
auto aspect_mask = (new_layout == vk::ImageLayout::eDepthAttachmentOptimal)
? vk::ImageAspectFlagBits::eDepth
: vk::ImageAspectFlagBits::eColor;
VkImageMemoryBarrier image_barrier {
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
.pNext = nullptr,
vk::ImageMemoryBarrier image_barrier {};
image_barrier.srcAccessMask = vk::AccessFlagBits::eMemoryWrite;
image_barrier.dstAccessMask
= vk::AccessFlagBits::eMemoryWrite | vk::AccessFlagBits::eMemoryRead;
image_barrier.oldLayout = current_layout;
image_barrier.newLayout = new_layout;
image_barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
image_barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
image_barrier.image = image;
image_barrier.subresourceRange.aspectMask = aspect_mask;
image_barrier.subresourceRange.baseMipLevel = 0;
image_barrier.subresourceRange.levelCount = 1;
image_barrier.subresourceRange.baseArrayLayer = 0;
image_barrier.subresourceRange.layerCount = 1;
.srcAccessMask = VK_ACCESS_MEMORY_WRITE_BIT,
.dstAccessMask
= VK_ACCESS_MEMORY_WRITE_BIT | VK_ACCESS_MEMORY_READ_BIT,
.oldLayout = current_layout,
.newLayout = new_layout,
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.image = image,
.subresourceRange = {
.aspectMask = aspect_mask,
.baseMipLevel = 0,
.levelCount = 1,
.baseArrayLayer = 0,
.layerCount = 1,
},
};
vkCmdPipelineBarrier(cmd, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT,
VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, 0, 0, nullptr, 0, nullptr, 1,
&image_barrier);
cmd.pipelineBarrier(vk::PipelineStageFlagBits::eAllCommands,
vk::PipelineStageFlagBits::eAllCommands, {}, {}, {}, image_barrier);
}
auto copy_image_to_image(VkCommandBuffer cmd, VkImage source,
VkImage destination, VkExtent2D src_size, VkExtent2D dst_size) -> void
auto copy_image_to_image(vk::CommandBuffer cmd, vk::Image source,
vk::Image destination, vk::Extent2D src_size, vk::Extent2D dst_size) -> void
{
VkImageBlit2 blit_region {};
blit_region.sType = VK_STRUCTURE_TYPE_IMAGE_BLIT_2;
blit_region.pNext = nullptr;
vk::ImageBlit2 blit_region {};
blit_region.srcOffsets[0] = vk::Offset3D { 0, 0, 0 };
blit_region.srcOffsets[1]
= vk::Offset3D { static_cast<int32_t>(src_size.width),
static_cast<int32_t>(src_size.height), 1 };
blit_region.srcOffsets[0] = { 0, 0, 0 };
blit_region.srcOffsets[1] = { static_cast<int32_t>(src_size.width),
static_cast<int32_t>(src_size.height), 1 };
blit_region.dstOffsets[0] = vk::Offset3D { 0, 0, 0 };
blit_region.dstOffsets[1]
= vk::Offset3D { static_cast<int32_t>(dst_size.width),
static_cast<int32_t>(dst_size.height), 1 };
blit_region.dstOffsets[0] = { 0, 0, 0 };
blit_region.dstOffsets[1] = { static_cast<int32_t>(dst_size.width),
static_cast<int32_t>(dst_size.height), 1 };
blit_region.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
blit_region.srcSubresource.aspectMask = vk::ImageAspectFlagBits::eColor;
blit_region.srcSubresource.baseArrayLayer = 0;
blit_region.srcSubresource.layerCount = 1;
blit_region.srcSubresource.mipLevel = 0;
blit_region.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
blit_region.dstSubresource.aspectMask = vk::ImageAspectFlagBits::eColor;
blit_region.dstSubresource.baseArrayLayer = 0;
blit_region.dstSubresource.layerCount = 1;
blit_region.dstSubresource.mipLevel = 0;
VkBlitImageInfo2 blit_info {};
blit_info.sType = VK_STRUCTURE_TYPE_BLIT_IMAGE_INFO_2;
blit_info.pNext = nullptr;
vk::BlitImageInfo2 blit_info {};
blit_info.dstImage = destination;
blit_info.dstImageLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
blit_info.dstImageLayout = vk::ImageLayout::eTransferDstOptimal;
blit_info.srcImage = source;
blit_info.srcImageLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL;
blit_info.filter = VK_FILTER_LINEAR;
blit_info.srcImageLayout = vk::ImageLayout::eTransferSrcOptimal;
blit_info.filter = vk::Filter::eLinear;
blit_info.regionCount = 1;
blit_info.pRegions = &blit_region;
vkCmdBlitImage2(cmd, &blit_info);
cmd.blitImage2(blit_info);
}
auto load_shader_module(std::span<uint8_t> spirv_data, VkDevice device,
VkShaderModule *out_shader_module) -> bool
auto load_shader_module(std::span<uint8_t> spirv_data, vk::Device device)
-> vk::UniqueShaderModule
{
if (!device || !out_shader_module)
return false;
if (!device || spirv_data.empty() || (spirv_data.size() % 4) != 0) {
return {};
}
if (spirv_data.empty() || (spirv_data.size() % 4) != 0)
return false;
VkShaderModuleCreateInfo create_info {};
create_info.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO;
create_info.pNext = nullptr;
create_info.flags = 0;
vk::ShaderModuleCreateInfo create_info {};
create_info.codeSize = spirv_data.size();
create_info.pCode = reinterpret_cast<uint32_t const *>(spirv_data.data());
VkResult const res = vkCreateShaderModule(
device, &create_info, nullptr, out_shader_module);
if (res != VK_SUCCESS) {
*out_shader_module = VK_NULL_HANDLE;
return false;
try {
return device.createShaderModuleUnique(create_info);
} catch (vk::SystemError const &) {
return {};
}
return true;
}
} // namespace vkutil
namespace vkinit {
auto image_create_info(VkFormat format, VkImageUsageFlags usage_flags,
VkExtent3D extent) -> VkImageCreateInfo
auto image_create_info(vk::Format format, vk::ImageUsageFlags usage_flags,
vk::Extent3D extent) -> vk::ImageCreateInfo
{
VkImageCreateInfo info = {};
info.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
info.pNext = nullptr;
info.imageType = VK_IMAGE_TYPE_2D;
vk::ImageCreateInfo info {};
info.imageType = vk::ImageType::e2D;
info.format = format;
info.extent = extent;
info.mipLevels = 1;
info.arrayLayers = 1;
info.samples = VK_SAMPLE_COUNT_1_BIT;
info.tiling = VK_IMAGE_TILING_OPTIMAL;
info.samples = vk::SampleCountFlagBits::e1;
info.tiling = vk::ImageTiling::eOptimal;
info.usage = usage_flags;
return info;
}
auto imageview_create_info(VkFormat format, VkImage image,
VkImageAspectFlags aspect_flags) -> VkImageViewCreateInfo
auto imageview_create_info(vk::Format format, vk::Image image,
vk::ImageAspectFlags aspect_flags) -> vk::ImageViewCreateInfo
{
VkImageViewCreateInfo info = {};
info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
info.pNext = nullptr;
info.viewType = VK_IMAGE_VIEW_TYPE_2D;
vk::ImageViewCreateInfo info {};
info.viewType = vk::ImageViewType::e2D;
info.image = image;
info.format = format;
info.subresourceRange.baseMipLevel = 0;
@@ -145,27 +115,22 @@ auto imageview_create_info(VkFormat format, VkImage image,
info.subresourceRange.baseArrayLayer = 0;
info.subresourceRange.layerCount = 1;
info.subresourceRange.aspectMask = aspect_flags;
return info;
}
auto command_buffer_submit_info(VkCommandBuffer cmd)
-> VkCommandBufferSubmitInfo
auto command_buffer_submit_info(vk::CommandBuffer cmd)
-> vk::CommandBufferSubmitInfo
{
VkCommandBufferSubmitInfo info {};
info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_SUBMIT_INFO;
info.pNext = nullptr;
vk::CommandBufferSubmitInfo info {};
info.commandBuffer = cmd;
info.deviceMask = 0;
return info;
}
auto semaphore_submit_info(VkPipelineStageFlags2 stage_mask,
VkSemaphore semaphore) -> VkSemaphoreSubmitInfo
auto semaphore_submit_info(vk::PipelineStageFlags2 stage_mask,
vk::Semaphore semaphore) -> vk::SemaphoreSubmitInfo
{
VkSemaphoreSubmitInfo info {};
info.sType = VK_STRUCTURE_TYPE_SEMAPHORE_SUBMIT_INFO;
info.pNext = nullptr;
vk::SemaphoreSubmitInfo info {};
info.semaphore = semaphore;
info.value = 0;
info.stageMask = stage_mask;
@@ -173,14 +138,11 @@ auto semaphore_submit_info(VkPipelineStageFlags2 stage_mask,
return info;
}
auto submit_info2(VkCommandBufferSubmitInfo *cmd_info,
VkSemaphoreSubmitInfo *wait_semaphore_info,
VkSemaphoreSubmitInfo *signal_semaphore_info) -> VkSubmitInfo2
auto submit_info2(vk::CommandBufferSubmitInfo *cmd_info,
vk::SemaphoreSubmitInfo *wait_semaphore_info,
vk::SemaphoreSubmitInfo *signal_semaphore_info) -> vk::SubmitInfo2
{
VkSubmitInfo2 info {};
info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO_2;
info.pNext = nullptr;
info.flags = 0;
vk::SubmitInfo2 info {};
info.waitSemaphoreInfoCount = wait_semaphore_info ? 1u : 0u;
info.pWaitSemaphoreInfos = wait_semaphore_info;
info.commandBufferInfoCount = cmd_info ? 1u : 0u;
@@ -190,18 +152,15 @@ auto submit_info2(VkCommandBufferSubmitInfo *cmd_info,
return info;
}
auto attachment_info(VkImageView view, VkClearValue *clear,
VkImageLayout layout) -> VkRenderingAttachmentInfo
auto attachment_info(vk::ImageView view, vk::ClearValue *clear,
vk::ImageLayout layout) -> vk::RenderingAttachmentInfo
{
VkRenderingAttachmentInfo color_at {};
color_at.sType = VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO;
color_at.pNext = nullptr;
vk::RenderingAttachmentInfo color_at {};
color_at.imageView = view;
color_at.imageLayout = layout;
color_at.loadOp
= clear ? VK_ATTACHMENT_LOAD_OP_CLEAR : VK_ATTACHMENT_LOAD_OP_LOAD;
color_at.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
= clear ? vk::AttachmentLoadOp::eClear : vk::AttachmentLoadOp::eLoad;
color_at.storeOp = vk::AttachmentStoreOp::eStore;
if (clear) {
color_at.clearValue = *clear;
}
@@ -209,27 +168,22 @@ auto attachment_info(VkImageView view, VkClearValue *clear,
return color_at;
}
auto pipeline_shader_stage(VkShaderStageFlagBits stage, VkShaderModule module)
-> VkPipelineShaderStageCreateInfo
auto pipeline_shader_stage(vk::ShaderStageFlagBits stage,
vk::ShaderModule module) -> vk::PipelineShaderStageCreateInfo
{
VkPipelineShaderStageCreateInfo stage_ci {};
stage_ci.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
stage_ci.pNext = nullptr;
vk::PipelineShaderStageCreateInfo stage_ci {};
stage_ci.stage = stage;
stage_ci.module = module;
stage_ci.pName = "main";
return stage_ci;
}
auto render_info(VkExtent2D extent, VkRenderingAttachmentInfo const *color_att,
VkRenderingAttachmentInfo const *depth_att) -> VkRenderingInfo
auto render_info(vk::Extent2D extent,
vk::RenderingAttachmentInfo const *color_att,
vk::RenderingAttachmentInfo const *depth_att) -> vk::RenderingInfo
{
VkRenderingInfo render_info {};
render_info.sType = VK_STRUCTURE_TYPE_RENDERING_INFO;
render_info.pNext = nullptr;
render_info.flags = 0;
render_info.renderArea = { {}, extent };
vk::RenderingInfo render_info {};
render_info.renderArea = vk::Rect2D { {}, extent };
render_info.layerCount = 1;
render_info.colorAttachmentCount = color_att ? 1 : 0;
render_info.pColorAttachments = color_att;
@@ -237,19 +191,15 @@ auto render_info(VkExtent2D extent, VkRenderingAttachmentInfo const *color_att,
return render_info;
}
auto depth_attachment_info(VkImageView view, VkImageLayout layout)
-> VkRenderingAttachmentInfo
auto depth_attachment_info(vk::ImageView view, vk::ImageLayout layout)
-> vk::RenderingAttachmentInfo
{
VkRenderingAttachmentInfo depth_att {};
depth_att.sType = VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO;
depth_att.pNext = nullptr;
vk::RenderingAttachmentInfo depth_att {};
depth_att.imageView = view;
depth_att.imageLayout = layout;
depth_att.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
depth_att.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
depth_att.loadOp = vk::AttachmentLoadOp::eClear;
depth_att.storeOp = vk::AttachmentStoreOp::eStore;
depth_att.clearValue.depthStencil.depth = 1.f;
return depth_att;
}

View File

@@ -3,7 +3,7 @@
#include <span>
#include <vulkan/vk_enum_string_helper.h>
#include <vulkan/vulkan.h>
#include <vulkan/vulkan.hpp>
template<typename F> struct privDefer {
F f;
@@ -29,46 +29,49 @@ template<typename F> privDefer<F> defer_func(F f) { return privDefer<F>(f); }
#define VK_CHECK(logger, x) \
do { \
VkResult err { x }; \
if (err) { \
(logger).err("Detected Vulkan error: {}", string_VkResult(err)); \
auto err { x }; \
auto result = vk::Result(err); \
if (result != vk::Result::eSuccess) { \
(logger).err("Detected Vulkan error: {}", vk::to_string(result)); \
throw std::runtime_error("Vulkan error"); \
} \
} while (0)
namespace vkutil {
auto transition_image(VkCommandBuffer cmd, VkImage image,
VkImageLayout current_layout, VkImageLayout new_layout) -> void;
auto copy_image_to_image(VkCommandBuffer cmd, VkImage source,
VkImage destination, VkExtent2D src_size, VkExtent2D dst_size) -> void;
auto load_shader_module(std::span<uint8_t> spirv_data, VkDevice device,
VkShaderModule *out_shader_module) -> bool;
auto transition_image(vk::CommandBuffer cmd, vk::Image image,
vk::ImageLayout current_layout, vk::ImageLayout new_layout) -> void;
auto copy_image_to_image(vk::CommandBuffer cmd, vk::Image source,
vk::Image destination, vk::Extent2D src_size, vk::Extent2D dst_size)
-> void;
auto load_shader_module(std::span<uint8_t> spirv_data, vk::Device device)
-> vk::UniqueShaderModule;
} // namespace vkutil
namespace vkinit {
auto image_create_info(VkFormat format, VkImageUsageFlags usage_flags,
VkExtent3D extent) -> VkImageCreateInfo;
auto imageview_create_info(VkFormat format, VkImage image,
VkImageAspectFlags aspect_flags) -> VkImageViewCreateInfo;
auto command_buffer_submit_info(VkCommandBuffer cmd)
-> VkCommandBufferSubmitInfo;
auto semaphore_submit_info(VkPipelineStageFlags2 stage_mask,
VkSemaphore semaphore) -> VkSemaphoreSubmitInfo;
auto submit_info2(VkCommandBufferSubmitInfo *cmd_info,
VkSemaphoreSubmitInfo *wait_semaphore_info,
VkSemaphoreSubmitInfo *signal_semaphore_info) -> VkSubmitInfo2;
auto attachment_info(VkImageView view, VkClearValue *clear,
VkImageLayout layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL)
-> VkRenderingAttachmentInfo;
auto pipeline_shader_stage(VkShaderStageFlagBits stage, VkShaderModule module)
-> VkPipelineShaderStageCreateInfo;
auto render_info(VkExtent2D extent, VkRenderingAttachmentInfo const *color_att,
VkRenderingAttachmentInfo const *depth_att) -> VkRenderingInfo;
auto depth_attachment_info(VkImageView view,
VkImageLayout layout = VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_OPTIMAL)
-> VkRenderingAttachmentInfo;
auto image_create_info(vk::Format format, vk::ImageUsageFlags usage_flags,
vk::Extent3D extent) -> vk::ImageCreateInfo;
auto imageview_create_info(vk::Format format, vk::Image image,
vk::ImageAspectFlags aspect_flags) -> vk::ImageViewCreateInfo;
auto command_buffer_submit_info(vk::CommandBuffer cmd)
-> vk::CommandBufferSubmitInfo;
auto semaphore_submit_info(vk::PipelineStageFlags2 stage_mask,
vk::Semaphore semaphore) -> vk::SemaphoreSubmitInfo;
auto submit_info2(vk::CommandBufferSubmitInfo *cmd_info,
vk::SemaphoreSubmitInfo *wait_semaphore_info,
vk::SemaphoreSubmitInfo *signal_semaphore_info) -> vk::SubmitInfo2;
auto attachment_info(vk::ImageView view, vk::ClearValue *clear,
vk::ImageLayout layout = vk::ImageLayout::eColorAttachmentOptimal)
-> vk::RenderingAttachmentInfo;
auto pipeline_shader_stage(vk::ShaderStageFlagBits stage,
vk::ShaderModule module) -> vk::PipelineShaderStageCreateInfo;
auto render_info(vk::Extent2D extent,
vk::RenderingAttachmentInfo const *color_att,
vk::RenderingAttachmentInfo const *depth_att) -> vk::RenderingInfo;
auto depth_attachment_info(vk::ImageView view,
vk::ImageLayout layout = vk::ImageLayout::eDepthAttachmentOptimal)
-> vk::RenderingAttachmentInfo;
} // namespace vkinit

File diff suppressed because it is too large Load Diff

View File

@@ -7,19 +7,20 @@
#include <VkBootstrap.h>
#include <smath.hpp>
#include <vk_mem_alloc.h>
#include <vulkan/vulkan_core.h>
#include <vulkan/vulkan.hpp>
#include "DeletionQueue.h"
#include "DescriptorAllocator.h"
#include "Loader.h"
#include "Logger.h"
#include "Pipeline.h"
#include "Types.h"
namespace Lunar {
struct GPUDrawPushConstants {
smath::Mat4 world_matrix;
VkDeviceAddress vertex_buffer;
vk::DeviceAddress vertex_buffer;
};
constexpr unsigned FRAME_OVERLAP = 2;
@@ -31,7 +32,7 @@ struct VulkanRenderer {
auto render() -> void;
auto resize(uint32_t width, uint32_t height) -> void;
auto immediate_submit(std::function<void(VkCommandBuffer cmd)> &&function)
auto immediate_submit(std::function<void(vk::CommandBuffer cmd)> &&function)
-> void;
auto upload_mesh(std::span<uint32_t> indices, std::span<Vertex> vertices)
-> GPUMeshBuffers;
@@ -51,9 +52,10 @@ private:
auto imgui_init() -> void;
auto default_data_init() -> void;
auto draw_background(VkCommandBuffer cmd) -> void;
auto draw_geometry(VkCommandBuffer cmd) -> void;
auto draw_imgui(VkCommandBuffer cmd, VkImageView target_image_view) -> void;
auto draw_background(vk::CommandBuffer cmd) -> void;
auto draw_geometry(vk::CommandBuffer cmd) -> void;
auto draw_imgui(vk::CommandBuffer cmd, vk::ImageView target_image_view)
-> void;
auto create_swapchain(uint32_t width, uint32_t height) -> void;
auto create_draw_image(uint32_t width, uint32_t height) -> void;
@@ -63,16 +65,20 @@ private:
auto destroy_depth_image() -> void;
auto recreate_swapchain(uint32_t width, uint32_t height) -> void;
auto destroy_swapchain() -> void;
auto create_image(VkExtent3D size, VkFormat format, VkImageUsageFlags flags,
bool mipmapped = false) -> AllocatedImage;
auto create_image(void const *data, VkExtent3D size, VkFormat format,
VkImageUsageFlags flags, bool mipmapped = false) -> AllocatedImage;
auto create_image(vk::Extent3D size, vk::Format format,
vk::ImageUsageFlags flags, bool mipmapped = false) -> AllocatedImage;
auto create_image(void const *data, vk::Extent3D size, vk::Format format,
vk::ImageUsageFlags flags, bool mipmapped = false) -> AllocatedImage;
auto destroy_image(AllocatedImage const &img) -> void;
auto create_buffer(size_t alloc_size, VkBufferUsageFlags usage,
auto create_buffer(size_t alloc_size, vk::BufferUsageFlags usage,
VmaMemoryUsage memory_usage) -> AllocatedBuffer;
auto destroy_buffer(AllocatedBuffer const &buffer) -> void;
vk::Instance m_instance {};
vk::PhysicalDevice m_physical_device {};
vk::Device m_device {};
struct {
vkb::Instance instance;
vkb::PhysicalDevice phys_dev;
@@ -86,50 +92,47 @@ private:
return frames.at(frame_number % frames.size());
}
VkSwapchainKHR swapchain { VK_NULL_HANDLE };
VkSurfaceKHR surface { nullptr };
VkFormat swapchain_image_format;
vk::SwapchainKHR swapchain {};
vk::SurfaceKHR surface {};
vk::Format swapchain_image_format {};
uint32_t graphics_queue_family { 0 };
VkQueue graphics_queue { nullptr };
vk::Queue graphics_queue {};
std::vector<VkImage> swapchain_images;
std::vector<VkImageView> swapchain_image_views;
std::vector<VkSemaphore> present_semaphores;
VkExtent2D swapchain_extent;
std::vector<vk::Image> swapchain_images;
std::vector<vk::UniqueImageView> swapchain_image_views;
std::vector<vk::UniqueSemaphore> present_semaphores;
vk::Extent2D swapchain_extent;
std::array<FrameData, FRAME_OVERLAP> frames;
AllocatedImage draw_image {};
AllocatedImage depth_image {};
VkExtent2D draw_extent {};
vk::Extent2D draw_extent {};
VmaAllocator allocator;
DescriptorAllocator descriptor_allocator;
VkDescriptorSet draw_image_descriptors;
VkDescriptorSetLayout draw_image_descriptor_layout;
VkDescriptorSet draw_image_descriptors {};
vk::DescriptorSetLayout draw_image_descriptor_layout {};
GPUSceneData scene_data {};
VkDescriptorSetLayout gpu_scene_data_descriptor_layout;
vk::DescriptorSetLayout gpu_scene_data_descriptor_layout {};
VkPipeline gradient_pipeline {};
VkPipelineLayout gradient_pipeline_layout {};
vk::DescriptorSetLayout single_image_descriptor_layout {};
VkPipeline triangle_pipeline {};
VkPipelineLayout triangle_pipeline_layout {};
VkPipeline mesh_pipeline {};
VkPipelineLayout mesh_pipeline_layout {};
Pipeline gradient_pipeline;
Pipeline triangle_pipeline;
Pipeline mesh_pipeline;
GPUMeshBuffers rectangle;
VkDescriptorPool imgui_descriptor_pool { VK_NULL_HANDLE };
vk::UniqueDescriptorPool imgui_descriptor_pool;
DeletionQueue deletion_queue;
VkFence imm_fence {};
VkCommandBuffer imm_command_buffer {};
VkCommandPool imm_command_pool {};
vk::UniqueFence imm_fence;
vk::UniqueCommandBuffer imm_command_buffer;
vk::UniqueCommandPool imm_command_pool;
uint64_t frame_number { 0 };
@@ -140,8 +143,8 @@ private:
AllocatedImage gray_image {};
AllocatedImage error_image {};
VkSampler default_sampler_linear;
VkSampler default_sampler_nearest;
vk::UniqueSampler default_sampler_linear;
vk::UniqueSampler default_sampler_nearest;
} m_vk;
SDL_Window *m_window { nullptr };