Compare commits

...

3 Commits

Author SHA1 Message Date
402cdd43da Make the constructor and destructor of Application private
Application is a singleton now, no need to have it be constructed
outside of the().

Signed-off-by: Slendi <slendi@socopon.com>
2026-01-11 17:45:10 +02:00
fc66ce2fd3 Make Application into a singleton
Signed-off-by: Slendi <slendi@socopon.com>
2026-01-11 17:44:02 +02:00
46f5fab55e Fix initializers, more stuff
Signed-off-by: Slendi <slendi@socopon.com>
2026-01-11 16:55:10 +02:00
13 changed files with 241 additions and 168 deletions

View File

@@ -418,6 +418,9 @@ Application::Application()
} }
m_renderer = std::make_unique<VulkanRenderer>(m_window, m_logger); m_renderer = std::make_unique<VulkanRenderer>(m_window, m_logger);
m_window_focused
= (SDL_GetWindowFlags(m_window) & SDL_WINDOW_INPUT_FOCUS) != 0;
m_renderer->set_antialiasing_immediate( m_renderer->set_antialiasing_immediate(
VulkanRenderer::AntiAliasingKind::MSAA_4X); VulkanRenderer::AntiAliasingKind::MSAA_4X);
@@ -480,13 +483,13 @@ auto Application::asset_directory() -> std::filesystem::path
if (auto const *xdg_data_dirs = getenv("XDG_DATA_DIRS"); if (auto const *xdg_data_dirs = getenv("XDG_DATA_DIRS");
xdg_data_dirs && *xdg_data_dirs) { xdg_data_dirs && *xdg_data_dirs) {
std::string_view dirs_view { xdg_data_dirs }; std::string_view dirs_view { xdg_data_dirs };
size_t start = 0; size_t start { 0 };
while (start <= dirs_view.size()) { while (start <= dirs_view.size()) {
size_t end = dirs_view.find(':', start); size_t end { dirs_view.find(':', start) };
if (end == std::string_view::npos) { if (end == std::string_view::npos) {
end = dirs_view.size(); end = dirs_view.size();
} }
auto segment = dirs_view.substr(start, end - start); auto segment { dirs_view.substr(start, end - start) };
if (!segment.empty()) { if (!segment.empty()) {
add_xdg_path(std::filesystem::path { segment }); add_xdg_path(std::filesystem::path { segment });
} }
@@ -497,7 +500,7 @@ auto Application::asset_directory() -> std::filesystem::path
add_xdg_path("/usr/share"); add_xdg_path("/usr/share");
} }
auto base_dir = binary_directory(); auto base_dir { binary_directory() };
candidates.emplace_back(base_dir / "assets"); candidates.emplace_back(base_dir / "assets");
candidates.emplace_back(base_dir / "../assets"); candidates.emplace_back(base_dir / "../assets");
@@ -515,9 +518,9 @@ auto Application::asset_directory() -> std::filesystem::path
auto Application::init_test_meshes() -> void auto Application::init_test_meshes() -> void
{ {
auto assets_dir = asset_directory(); auto assets_dir { asset_directory() };
auto mesh_path = assets_dir / "basicmesh.glb"; auto mesh_path { assets_dir / "basicmesh.glb" };
auto meshes = Mesh::load_gltf_meshes(*m_renderer, mesh_path); auto meshes { Mesh::load_gltf_meshes(*m_renderer, mesh_path) };
if (!meshes) { if (!meshes) {
m_logger.err("Failed to load test mesh: {}", mesh_path.string()); m_logger.err("Failed to load test mesh: {}", mesh_path.string());
return; return;
@@ -548,6 +551,7 @@ auto Application::run() -> void
{ {
GZoneScopedN("Input"); GZoneScopedN("Input");
m_key_state_previous = m_key_state;
process_libinput_events(); process_libinput_events();
while (SDL_PollEvent(&e)) { while (SDL_PollEvent(&e)) {
@@ -561,6 +565,18 @@ auto Application::run() -> void
static_cast<uint32_t>(height)); static_cast<uint32_t>(height));
clamp_mouse_to_window(width, height); clamp_mouse_to_window(width, height);
forward_to_imgui = true; forward_to_imgui = true;
} else if (e.type == SDL_EVENT_WINDOW_FOCUS_GAINED) {
m_window_focused = true;
forward_to_imgui = true;
} else if (e.type == SDL_EVENT_WINDOW_FOCUS_LOST) {
m_window_focused = false;
m_ctrl_pressed_count = 0;
m_key_state.fill(false);
m_key_state_previous.fill(false);
m_mouse_dx = 0.0;
m_mouse_dy = 0.0;
forward_to_imgui = true;
} else if (e.type == SDL_EVENT_MOUSE_MOTION) { } else if (e.type == SDL_EVENT_MOUSE_MOTION) {
m_mouse_x = e.motion.x; m_mouse_x = e.motion.x;
m_mouse_y = e.motion.y; m_mouse_y = e.motion.y;
@@ -583,6 +599,15 @@ auto Application::run() -> void
} }
} }
bool const ctrl_down { is_key_down(KEY_LEFTCTRL)
|| is_key_down(KEY_RIGHTCTRL) };
{
bool const shift_down { is_key_down(KEY_LEFTSHIFT)
|| is_key_down(KEY_RIGHTSHIFT) };
if (ctrl_down && shift_down && is_key_pressed(KEY_Q))
m_running = false;
}
{ {
GZoneScopedN("CameraUpdate"); GZoneScopedN("CameraUpdate");
@@ -623,19 +648,26 @@ auto Application::run() -> void
if (camera_up.magnitude() == 0.0f) if (camera_up.magnitude() == 0.0f)
camera_up = world_up; camera_up = world_up;
auto forward_dir { smath::Vec3 {
look_dir.x(), 0.0f, look_dir.z() } };
if (forward_dir.magnitude() > 0.0f)
forward_dir = forward_dir.normalized_safe();
smath::Vec3 move_dir {}; smath::Vec3 move_dir {};
if (is_key_pressed(KEY_W))
move_dir += look_dir; if (!ctrl_down) {
if (is_key_pressed(KEY_S)) if (is_key_down(KEY_W))
move_dir -= look_dir; move_dir += forward_dir;
if (is_key_pressed(KEY_D)) if (is_key_down(KEY_S))
move_dir += right; move_dir -= forward_dir;
if (is_key_pressed(KEY_A)) if (is_key_down(KEY_D))
move_dir -= right; move_dir += right;
if (is_key_pressed(KEY_SPACE)) if (is_key_down(KEY_A))
move_dir += world_up; move_dir -= right;
if (is_key_pressed(KEY_LEFTSHIFT)) if (is_key_down(KEY_SPACE))
move_dir -= world_up; move_dir += world_up;
if (is_key_down(KEY_LEFTSHIFT))
move_dir -= world_up;
}
if (move_dir.magnitude() > 0.0f) { if (move_dir.magnitude() > 0.0f) {
constexpr float move_speed { 10.0f }; constexpr float move_speed { 10.0f };
@@ -795,11 +827,11 @@ auto Application::run() -> void
projection[1][1] *= -1; projection[1][1] *= -1;
auto view_projection { projection * view }; auto view_projection { projection * view };
auto skybox_view = view; auto skybox_view { view };
skybox_view[3][0] = 0.0f; skybox_view[3][0] = 0.0f;
skybox_view[3][1] = 0.0f; skybox_view[3][1] = 0.0f;
skybox_view[3][2] = 0.0f; skybox_view[3][2] = 0.0f;
m_skybox.draw(gl, projection * skybox_view); m_skybox.draw(gl, *m_renderer, projection * skybox_view);
gl.set_transform(view_projection); gl.set_transform(view_projection);
@@ -930,6 +962,9 @@ auto Application::handle_keyboard_event(libinput_event_keyboard *event) -> void
auto const state { libinput_event_keyboard_get_key_state(event) }; auto const state { libinput_event_keyboard_get_key_state(event) };
bool const pressed { state == LIBINPUT_KEY_STATE_PRESSED }; bool const pressed { state == LIBINPUT_KEY_STATE_PRESSED };
if (!m_window_focused)
return;
if (key == KEY_LEFTCTRL || key == KEY_RIGHTCTRL) { if (key == KEY_LEFTCTRL || key == KEY_RIGHTCTRL) {
if (pressed) { if (pressed) {
++m_ctrl_pressed_count; ++m_ctrl_pressed_count;
@@ -978,9 +1013,8 @@ auto Application::handle_keyboard_event(libinput_event_keyboard *event) -> void
} }
if (m_show_imgui && pressed) { if (m_show_imgui && pressed) {
bool const shift_pressed { is_key_pressed(KEY_LEFTSHIFT) bool const shift_pressed { is_key_down(KEY_LEFTSHIFT)
|| is_key_pressed(KEY_RIGHTSHIFT) || is_key_down(KEY_RIGHTSHIFT) || (key == KEY_LEFTSHIFT && pressed)
|| (key == KEY_LEFTSHIFT && pressed)
|| (key == KEY_RIGHTSHIFT && pressed) }; || (key == KEY_RIGHTSHIFT && pressed) };
if (auto ch { linux_key_to_char(key, shift_pressed) }) if (auto ch { linux_key_to_char(key, shift_pressed) })
@@ -1013,11 +1047,38 @@ auto Application::mouse_captured(bool new_state) -> void
m_mouse_captured = new_state && !m_show_imgui; m_mouse_captured = new_state && !m_show_imgui;
} }
auto Application::is_key_pressed(uint32_t key) const -> bool auto Application::is_key_down(uint32_t key) const -> bool
{ {
if (key >= m_key_state.size()) if (key >= m_key_state.size())
return false; return false;
return m_key_state[key]; return m_key_state[key];
} }
auto Application::is_key_up(uint32_t key) const -> bool
{
if (key >= m_key_state.size())
return true;
return !m_key_state[key];
}
auto Application::is_key_pressed(uint32_t key) const -> bool
{
if (key >= m_key_state.size())
return false;
return m_key_state[key] && !m_key_state_previous[key];
}
auto Application::is_key_released(uint32_t key) const -> bool
{
if (key >= m_key_state.size())
return false;
return !m_key_state[key] && m_key_state_previous[key];
}
auto Application::the() -> Application &
{
static Application self {};
return self;
}
} // namespace Lunar } // namespace Lunar

View File

@@ -24,18 +24,23 @@ namespace Lunar {
struct VulkanRenderer; struct VulkanRenderer;
struct Application { struct Application {
Application();
~Application();
auto run() -> void; auto run() -> void;
auto binary_directory() const -> std::filesystem::path; auto binary_directory() const -> std::filesystem::path;
auto mouse_captured(bool new_state) -> void; auto mouse_captured(bool new_state) -> void;
auto mouse_captured() const -> bool { return m_mouse_captured; } auto mouse_captured() const -> bool { return m_mouse_captured; }
auto toggle_mouse_captured() -> void { mouse_captured(!m_mouse_captured); } auto toggle_mouse_captured() -> void { mouse_captured(!m_mouse_captured); }
auto is_key_down(uint32_t key) const -> bool;
auto is_key_up(uint32_t key) const -> bool;
auto is_key_pressed(uint32_t key) const -> bool; auto is_key_pressed(uint32_t key) const -> bool;
auto is_key_released(uint32_t key) const -> bool;
static auto the() -> Application &;
private: private:
Application();
~Application();
auto init_input() -> void; auto init_input() -> void;
auto init_test_meshes() -> void; auto init_test_meshes() -> void;
auto asset_directory() -> std::filesystem::path; auto asset_directory() -> std::filesystem::path;
@@ -56,6 +61,7 @@ private:
bool m_running { true }; bool m_running { true };
bool m_mouse_captured { false }; bool m_mouse_captured { false };
bool m_show_imgui { false }; bool m_show_imgui { false };
bool m_window_focused { true };
int m_ctrl_pressed_count { 0 }; int m_ctrl_pressed_count { 0 };
std::uint32_t m_screenshot_index { 0 }; std::uint32_t m_screenshot_index { 0 };
@@ -66,6 +72,7 @@ private:
float m_mouse_sensitivity { 0.001f }; float m_mouse_sensitivity { 0.001f };
std::array<bool, KEY_MAX + 1> m_key_state {}; std::array<bool, KEY_MAX + 1> m_key_state {};
std::array<bool, KEY_MAX + 1> m_key_state_previous {};
Camera m_camera; Camera m_camera;
PolarCoordinate m_cursor; PolarCoordinate m_cursor;

View File

@@ -59,11 +59,11 @@ namespace Lunar {
CPUTexture::CPUTexture(std::filesystem::path const &path) CPUTexture::CPUTexture(std::filesystem::path const &path)
{ {
int width_out = 0; int width_out { 0 };
int height_out = 0; int height_out { 0 };
int channels_out = 0; int channels_out { 0 };
stbi_uc *data = stbi_load(path.string().c_str(), &width_out, &height_out, stbi_uc *data { stbi_load(path.string().c_str(), &width_out, &height_out,
&channels_out, STBI_rgb_alpha); &channels_out, STBI_rgb_alpha) };
if (!data) { if (!data) {
throw std::runtime_error( throw std::runtime_error(
std::format("Failed to load texture: {}", path.string())); std::format("Failed to load texture: {}", path.string()));

View File

@@ -78,7 +78,7 @@ auto DescriptorAllocatorGrowable::destroy_pools(VkDevice dev) -> void
auto DescriptorAllocatorGrowable::allocate(Logger &logger, VkDevice dev, auto DescriptorAllocatorGrowable::allocate(Logger &logger, VkDevice dev,
VkDescriptorSetLayout layout, void *p_next) -> VkDescriptorSet VkDescriptorSetLayout layout, void *p_next) -> VkDescriptorSet
{ {
auto pool_to_use = get_pool(dev); auto pool_to_use { get_pool(dev) };
VkDescriptorSetAllocateInfo alloci {}; VkDescriptorSetAllocateInfo alloci {};
alloci.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO; alloci.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;

View File

@@ -61,7 +61,7 @@ auto Mesh::load_gltf_meshes(
{ {
renderer.logger().debug("Loading GLTF from file: {}", path); renderer.logger().debug("Loading GLTF from file: {}", path);
auto data = fastgltf::GltfDataBuffer::FromPath(path); auto data { fastgltf::GltfDataBuffer::FromPath(path) };
if (data.error() != fastgltf::Error::None) { if (data.error() != fastgltf::Error::None) {
renderer.logger().err("Failed to open glTF file: {} (error {})", path, renderer.logger().err("Failed to open glTF file: {} (error {})", path,
fastgltf::to_underlying(data.error())); fastgltf::to_underlying(data.error()));
@@ -98,7 +98,7 @@ auto Mesh::load_gltf_meshes(
new_surface.count = static_cast<uint32_t>( new_surface.count = static_cast<uint32_t>(
gltf.accessors[p.indicesAccessor.value()].count); gltf.accessors[p.indicesAccessor.value()].count);
size_t initial_vertex = vertices.size(); size_t initial_vertex { vertices.size() };
{ // Indices { // Indices
auto &accessor = gltf.accessors[p.indicesAccessor.value()]; auto &accessor = gltf.accessors[p.indicesAccessor.value()];
@@ -128,7 +128,7 @@ auto Mesh::load_gltf_meshes(
if (auto attr = p.findAttribute("NORMAL")) { // Normals if (auto attr = p.findAttribute("NORMAL")) { // Normals
auto &accessor = gltf.accessors[attr->accessorIndex]; auto &accessor = gltf.accessors[attr->accessorIndex];
size_t local_index = 0; size_t local_index { 0 };
for (auto normal : for (auto normal :
fastgltf::iterateAccessor<smath::Vec3>(gltf, accessor)) { fastgltf::iterateAccessor<smath::Vec3>(gltf, accessor)) {
vertices[initial_vertex + local_index].normal = normal; vertices[initial_vertex + local_index].normal = normal;
@@ -138,7 +138,7 @@ auto Mesh::load_gltf_meshes(
if (auto attr = p.findAttribute("TEXCOORD_0")) { // UVs if (auto attr = p.findAttribute("TEXCOORD_0")) { // UVs
auto &accessor = gltf.accessors[attr->accessorIndex]; auto &accessor = gltf.accessors[attr->accessorIndex];
size_t local_index = 0; size_t local_index { 0 };
for (auto uv : for (auto uv :
fastgltf::iterateAccessor<smath::Vec2>(gltf, accessor)) { fastgltf::iterateAccessor<smath::Vec2>(gltf, accessor)) {
uv.unpack(vertices[initial_vertex + local_index].u, uv.unpack(vertices[initial_vertex + local_index].u,
@@ -149,7 +149,7 @@ auto Mesh::load_gltf_meshes(
if (auto attr = p.findAttribute("COLOR_0")) { // Colors if (auto attr = p.findAttribute("COLOR_0")) { // Colors
auto &accessor = gltf.accessors[attr->accessorIndex]; auto &accessor = gltf.accessors[attr->accessorIndex];
size_t local_index = 0; size_t local_index { 0 };
switch (accessor.type) { switch (accessor.type) {
case fastgltf::AccessorType::Vec3: { case fastgltf::AccessorType::Vec3: {

View File

@@ -34,7 +34,7 @@
static std::filesystem::path get_log_path(std::string_view app_name) static std::filesystem::path get_log_path(std::string_view app_name)
{ {
#ifdef _WIN32 #ifdef _WIN32
PWSTR path = nullptr; PWSTR path { nullptr };
SHGetKnownFolderPath(FOLDERID_LocalAppData, 0, nullptr, &path); SHGetKnownFolderPath(FOLDERID_LocalAppData, 0, nullptr, &path);
std::wstring wpath(path); std::wstring wpath(path);
CoTaskMemFree(path); CoTaskMemFree(path);
@@ -70,7 +70,7 @@ static int compress_file(std::filesystem::path const &input_path,
std::vector<char> buffer(chunk_size); std::vector<char> buffer(chunk_size);
while (in) { while (in) {
in.read(buffer.data(), static_cast<std::streamsize>(buffer.size())); in.read(buffer.data(), static_cast<std::streamsize>(buffer.size()));
std::streamsize bytes = in.gcount(); std::streamsize bytes { in.gcount() };
if (bytes > 0) if (bytes > 0)
gzwrite(out, buffer.data(), static_cast<unsigned int>(bytes)); gzwrite(out, buffer.data(), static_cast<unsigned int>(bytes));
} }
@@ -99,20 +99,20 @@ Logger::Logger(std::string_view app_name)
if (!file.is_regular_file()) if (!file.is_regular_file())
continue; continue;
auto name = file.path().filename().stem().string(); auto name { file.path().filename().stem().string() };
constexpr std::string_view prefix = "log_"; constexpr std::string_view prefix = "log_";
if (name.rfind(prefix, 0) != 0) { if (name.rfind(prefix, 0) != 0) {
continue; continue;
} }
int v = std::stoi(name.substr(prefix.size())); int v { std::stoi(name.substr(prefix.size())) };
if (v > max) if (v > max)
max = v; max = v;
auto ext = file.path().filename().extension().string(); auto ext { file.path().filename().extension().string() };
if (ext == ".txt") { if (ext == ".txt") {
auto np = file.path(); auto np { file.path() };
np.replace_extension(ext + ".gz"); np.replace_extension(ext + ".gz");
compress_file(file.path(), np); compress_file(file.path(), np);
} }
@@ -153,7 +153,7 @@ static std::string get_current_time_string()
void Logger::log(Level level, std::string_view msg) void Logger::log(Level level, std::string_view msg)
{ {
auto time_str = get_current_time_string(); auto time_str { get_current_time_string() };
std::string level_str; std::string level_str;
switch (level) { switch (level) {
case Logger::Level::Debug: case Logger::Level::Debug:

View File

@@ -27,13 +27,13 @@ auto Pipeline::Builder::set_push_constant_ranges(
auto Pipeline::Builder::build_compute( auto Pipeline::Builder::build_compute(
vk::PipelineShaderStageCreateInfo const &stage) -> Pipeline vk::PipelineShaderStageCreateInfo const &stage) -> Pipeline
{ {
auto pipeline_layout = build_layout(); auto pipeline_layout { build_layout() };
vk::ComputePipelineCreateInfo pipeline_ci {}; vk::ComputePipelineCreateInfo pipeline_ci {};
pipeline_ci.layout = pipeline_layout.get(); pipeline_ci.layout = pipeline_layout.get();
pipeline_ci.stage = stage; pipeline_ci.stage = stage;
auto pipeline_ret = m_device.createComputePipelineUnique({}, pipeline_ci); auto pipeline_ret { m_device.createComputePipelineUnique({}, pipeline_ci) };
VK_CHECK(m_logger, pipeline_ret.result); VK_CHECK(m_logger, pipeline_ret.result);
return Pipeline { return Pipeline {
@@ -46,14 +46,14 @@ auto Pipeline::Builder::build_graphics(
std::function<GraphicsPipelineBuilder &(GraphicsPipelineBuilder &)> const std::function<GraphicsPipelineBuilder &(GraphicsPipelineBuilder &)> const
&configure) -> Pipeline &configure) -> Pipeline
{ {
auto pipeline_layout = build_layout(); auto pipeline_layout { build_layout() };
auto builder = GraphicsPipelineBuilder { m_logger }; auto builder { GraphicsPipelineBuilder { m_logger } };
builder.set_pipeline_layout( builder.set_pipeline_layout(
static_cast<VkPipelineLayout>(pipeline_layout.get())); static_cast<VkPipelineLayout>(pipeline_layout.get()));
configure(builder); configure(builder);
auto pipeline_handle = builder.build(static_cast<VkDevice>(m_device)); auto pipeline_handle { builder.build(static_cast<VkDevice>(m_device)) };
vk::UniquePipeline pipeline_unique(pipeline_handle, vk::UniquePipeline pipeline_unique(pipeline_handle,
vk::detail::ObjectDestroy<vk::Device, vk::detail::ObjectDestroy<vk::Device,
VULKAN_HPP_DEFAULT_DISPATCHER_TYPE>(m_device)); VULKAN_HPP_DEFAULT_DISPATCHER_TYPE>(m_device));

View File

@@ -23,7 +23,7 @@ struct FaceOffset {
uint32_t y; uint32_t y;
}; };
constexpr std::array<FaceOffset, 6> kCrossOffsets { constexpr std::array<FaceOffset, 6> CROSS_OFFSETS {
FaceOffset { 2, 1 }, // +X FaceOffset { 2, 1 }, // +X
FaceOffset { 0, 1 }, // -X FaceOffset { 0, 1 }, // -X
FaceOffset { 1, 0 }, // +Y FaceOffset { 1, 0 }, // +Y
@@ -34,6 +34,83 @@ constexpr std::array<FaceOffset, 6> kCrossOffsets {
} // namespace } // namespace
auto Skybox::rebuild_pipeline(VulkanRenderer &renderer) -> bool
{
Pipeline::Builder pipeline_builder { renderer.device(), renderer.logger() };
uint8_t skybox_vert_shader_data[] {
#embed "skybox_vert.spv"
};
auto skybox_vert_shader
= vkutil::load_shader_module(std::span<uint8_t>(skybox_vert_shader_data,
sizeof(skybox_vert_shader_data)),
renderer.device());
if (!skybox_vert_shader) {
renderer.logger().err("Failed to load skybox vert shader");
return false;
}
uint8_t skybox_frag_shader_data[] {
#embed "skybox_frag.spv"
};
auto skybox_frag_shader
= vkutil::load_shader_module(std::span<uint8_t>(skybox_frag_shader_data,
sizeof(skybox_frag_shader_data)),
renderer.device());
if (!skybox_frag_shader) {
renderer.logger().err("Failed to load skybox frag shader");
return false;
}
vk::PushConstantRange push_constant_range {};
push_constant_range.stageFlags = vk::ShaderStageFlagBits::eVertex;
push_constant_range.offset = 0;
push_constant_range.size = sizeof(SkyboxPushConstants);
std::array push_constant_ranges { push_constant_range };
pipeline_builder.set_push_constant_ranges(push_constant_ranges);
std::array descriptor_set_layouts {
renderer.single_image_descriptor_layout()
};
pipeline_builder.set_descriptor_set_layouts(descriptor_set_layouts);
VkVertexInputBindingDescription binding {};
binding.binding = 0;
binding.stride = sizeof(Vertex);
binding.inputRate = VK_VERTEX_INPUT_RATE_VERTEX;
VkVertexInputAttributeDescription attribute {};
attribute.location = 0;
attribute.binding = 0;
attribute.format = VK_FORMAT_R32G32B32_SFLOAT;
attribute.offset = offsetof(Vertex, position);
std::array bindings { binding };
std::array attributes { attribute };
m_pipeline = pipeline_builder.build_graphics(
[&](GraphicsPipelineBuilder &builder) -> GraphicsPipelineBuilder & {
builder.set_vertex_input(bindings, attributes);
return builder
.set_shaders(skybox_vert_shader.get(), skybox_frag_shader.get())
.set_input_topology(VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST)
.set_polygon_mode(VK_POLYGON_MODE_FILL)
.set_cull_mode(
VK_CULL_MODE_FRONT_BIT, VK_FRONT_FACE_COUNTER_CLOCKWISE)
.set_multisampling(
static_cast<VkSampleCountFlagBits>(renderer.msaa_samples()))
.disable_blending()
.enable_depth_testing(false, VK_COMPARE_OP_LESS_OR_EQUAL)
.set_color_attachment_format(
static_cast<VkFormat>(renderer.draw_image_format()))
.set_depth_format(
static_cast<VkFormat>(renderer.depth_image_format()));
});
m_pipeline_samples = renderer.msaa_samples();
return true;
}
auto Skybox::init(VulkanRenderer &renderer, std::filesystem::path const &path) auto Skybox::init(VulkanRenderer &renderer, std::filesystem::path const &path)
-> void -> void
{ {
@@ -61,10 +138,10 @@ auto Skybox::init(VulkanRenderer &renderer, std::filesystem::path const &path)
uint32_t const face_size = texture.width / 4; uint32_t const face_size = texture.width / 4;
size_t const face_bytes = static_cast<size_t>(face_size) * face_size * 4; size_t const face_bytes = static_cast<size_t>(face_size) * face_size * 4;
std::vector<uint8_t> cubemap_pixels(face_bytes * kCrossOffsets.size()); std::vector<uint8_t> cubemap_pixels(face_bytes * CROSS_OFFSETS.size());
for (size_t face = 0; face < kCrossOffsets.size(); ++face) { for (size_t face = 0; face < CROSS_OFFSETS.size(); ++face) {
auto const offset = kCrossOffsets[face]; auto const offset = CROSS_OFFSETS[face];
for (uint32_t y = 0; y < face_size; ++y) { for (uint32_t y = 0; y < face_size; ++y) {
for (uint32_t x = 0; x < face_size; ++x) { for (uint32_t x = 0; x < face_size; ++x) {
uint32_t const src_x = offset.x * face_size + x; uint32_t const src_x = offset.x * face_size + x;
@@ -108,7 +185,9 @@ auto Skybox::init(VulkanRenderer &renderer, std::filesystem::path const &path)
vk::DescriptorSetAllocateInfo alloc_info {}; vk::DescriptorSetAllocateInfo alloc_info {};
alloc_info.descriptorPool = m_descriptor_pool.get(); alloc_info.descriptorPool = m_descriptor_pool.get();
alloc_info.descriptorSetCount = 1; alloc_info.descriptorSetCount = 1;
vk::DescriptorSetLayout layout = renderer.single_image_descriptor_layout(); vk::DescriptorSetLayout layout {
renderer.single_image_descriptor_layout()
};
alloc_info.pSetLayouts = &layout; alloc_info.pSetLayouts = &layout;
m_descriptor_set m_descriptor_set
= renderer.device().allocateDescriptorSets(alloc_info).front(); = renderer.device().allocateDescriptorSets(alloc_info).front();
@@ -183,79 +262,11 @@ auto Skybox::init(VulkanRenderer &renderer, std::filesystem::path const &path)
m_index_count = static_cast<uint32_t>(indices.size()); m_index_count = static_cast<uint32_t>(indices.size());
m_cube_mesh = renderer.upload_mesh(indices, vertices); m_cube_mesh = renderer.upload_mesh(indices, vertices);
Pipeline::Builder pipeline_builder { renderer.device(), renderer.logger() }; if (!rebuild_pipeline(renderer)) {
uint8_t skybox_vert_shader_data[] {
#embed "skybox_vert.spv"
};
auto skybox_vert_shader
= vkutil::load_shader_module(std::span<uint8_t>(skybox_vert_shader_data,
sizeof(skybox_vert_shader_data)),
renderer.device());
if (!skybox_vert_shader) {
renderer.logger().err("Failed to load skybox vert shader");
ok = false; ok = false;
return; return;
} }
uint8_t skybox_frag_shader_data[] {
#embed "skybox_frag.spv"
};
auto skybox_frag_shader
= vkutil::load_shader_module(std::span<uint8_t>(skybox_frag_shader_data,
sizeof(skybox_frag_shader_data)),
renderer.device());
if (!skybox_frag_shader) {
renderer.logger().err("Failed to load skybox frag shader");
ok = false;
return;
}
vk::PushConstantRange push_constant_range {};
push_constant_range.stageFlags = vk::ShaderStageFlagBits::eVertex;
push_constant_range.offset = 0;
push_constant_range.size = sizeof(SkyboxPushConstants);
std::array push_constant_ranges { push_constant_range };
pipeline_builder.set_push_constant_ranges(push_constant_ranges);
std::array descriptor_set_layouts {
renderer.single_image_descriptor_layout()
};
pipeline_builder.set_descriptor_set_layouts(descriptor_set_layouts);
VkVertexInputBindingDescription binding {};
binding.binding = 0;
binding.stride = sizeof(Vertex);
binding.inputRate = VK_VERTEX_INPUT_RATE_VERTEX;
VkVertexInputAttributeDescription attribute {};
attribute.location = 0;
attribute.binding = 0;
attribute.format = VK_FORMAT_R32G32B32_SFLOAT;
attribute.offset = offsetof(Vertex, position);
std::array bindings { binding };
std::array attributes { attribute };
m_pipeline = pipeline_builder.build_graphics(
[&](GraphicsPipelineBuilder &builder) -> GraphicsPipelineBuilder & {
builder.set_vertex_input(bindings, attributes);
return builder
.set_shaders(skybox_vert_shader.get(), skybox_frag_shader.get())
.set_input_topology(VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST)
.set_polygon_mode(VK_POLYGON_MODE_FILL)
.set_cull_mode(
VK_CULL_MODE_FRONT_BIT, VK_FRONT_FACE_COUNTER_CLOCKWISE)
.set_multisampling(
static_cast<VkSampleCountFlagBits>(renderer.msaa_samples()))
.disable_blending()
.enable_depth_testing(false, VK_COMPARE_OP_LESS_OR_EQUAL)
.set_color_attachment_format(
static_cast<VkFormat>(renderer.draw_image_format()))
.set_depth_format(
static_cast<VkFormat>(renderer.depth_image_format()));
});
ok = true; ok = true;
} }
@@ -273,6 +284,7 @@ auto Skybox::destroy(VulkanRenderer &renderer) -> void
m_sampler.reset(); m_sampler.reset();
m_descriptor_pool.reset(); m_descriptor_pool.reset();
m_pipeline.reset(); m_pipeline.reset();
m_pipeline_samples = vk::SampleCountFlagBits::e1;
m_descriptor_set = vk::DescriptorSet {}; m_descriptor_set = vk::DescriptorSet {};
m_cube_mesh = {}; m_cube_mesh = {};
m_cubemap = {}; m_cubemap = {};
@@ -280,14 +292,21 @@ auto Skybox::destroy(VulkanRenderer &renderer) -> void
ok = false; ok = false;
} }
auto Skybox::draw(VulkanRenderer::GL &gl, smath::Mat4 const &mvp) -> void auto Skybox::draw(VulkanRenderer::GL &gl, VulkanRenderer &renderer,
smath::Mat4 const &mvp) -> void
{ {
if (!ok) { if (!ok) {
return; return;
} }
if (m_pipeline_samples != renderer.msaa_samples()) {
if (!rebuild_pipeline(renderer)) {
return;
}
}
SkyboxPushConstants push_constants { mvp }; SkyboxPushConstants push_constants { mvp };
auto bytes = std::as_bytes(std::span { &push_constants, 1 }); auto bytes { std::as_bytes(std::span { &push_constants, 1 }) };
gl.draw_indexed(m_pipeline, m_descriptor_set, m_cube_mesh.vertex_buffer, gl.draw_indexed(m_pipeline, m_descriptor_set, m_cube_mesh.vertex_buffer,
m_cube_mesh.index_buffer, m_index_count, bytes); m_cube_mesh.index_buffer, m_index_count, bytes);
} }

View File

@@ -17,15 +17,19 @@ struct Skybox {
auto init(VulkanRenderer &renderer, std::filesystem::path const &path) auto init(VulkanRenderer &renderer, std::filesystem::path const &path)
-> void; -> void;
auto destroy(VulkanRenderer &renderer) -> void; auto destroy(VulkanRenderer &renderer) -> void;
auto draw(VulkanRenderer::GL &gl, smath::Mat4 const &mvp) -> void; auto draw(VulkanRenderer::GL &gl, VulkanRenderer &renderer,
smath::Mat4 const &mvp) -> void;
private: private:
auto rebuild_pipeline(VulkanRenderer &renderer) -> bool;
Pipeline m_pipeline {}; Pipeline m_pipeline {};
GPUMeshBuffers m_cube_mesh {}; GPUMeshBuffers m_cube_mesh {};
AllocatedImage m_cubemap {}; AllocatedImage m_cubemap {};
vk::UniqueSampler m_sampler {}; vk::UniqueSampler m_sampler {};
vk::UniqueDescriptorPool m_descriptor_pool {}; vk::UniqueDescriptorPool m_descriptor_pool {};
vk::DescriptorSet m_descriptor_set {}; vk::DescriptorSet m_descriptor_set {};
vk::SampleCountFlagBits m_pipeline_samples { vk::SampleCountFlagBits::e1 };
uint32_t m_index_count { 0 }; uint32_t m_index_count { 0 };
}; };

View File

@@ -96,7 +96,7 @@ struct PolarCoordinate {
smath::Vec3 to_vec3() const smath::Vec3 to_vec3() const
{ {
float sin_phi = std::sin(phi); float sin_phi { std::sin(phi) };
return smath::Vec3 { r * sin_phi * std::cos(theta), r * std::cos(phi), return smath::Vec3 { r * sin_phi * std::cos(theta), r * std::cos(phi),
r * sin_phi * std::sin(theta) }; r * sin_phi * std::sin(theta) };

View File

@@ -29,7 +29,7 @@ template<typename F> privDefer<F> defer_func(F f) { return privDefer<F>(f); }
#define VK_CHECK(logger, x) \ #define VK_CHECK(logger, x) \
do { \ do { \
auto err { x }; \ auto err { x }; \
auto result = vk::Result(err); \ auto result { vk::Result(err) }; \
if (result != vk::Result::eSuccess) { \ if (result != vk::Result::eSuccess) { \
(logger).err("Detected Vulkan error: {}", vk::to_string(result)); \ (logger).err("Detected Vulkan error: {}", vk::to_string(result)); \
throw std::runtime_error("Vulkan error"); \ throw std::runtime_error("Vulkan error"); \

View File

@@ -290,7 +290,7 @@ auto VulkanRenderer::GL::flush() -> void
VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER) VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER)
.update_set(m_renderer.m_vkb.dev.device, image_set); .update_set(m_renderer.m_vkb.dev.device, image_set);
auto vk_image_set = vk::DescriptorSet { image_set }; auto vk_image_set { vk::DescriptorSet { image_set } };
cmd.bindDescriptorSets(vk::PipelineBindPoint::eGraphics, cmd.bindDescriptorSets(vk::PipelineBindPoint::eGraphics,
m_active_pipeline->get_layout(), 0, vk_image_set, {}); m_active_pipeline->get_layout(), 0, vk_image_set, {});
@@ -423,22 +423,16 @@ auto VulkanRenderer::GL::draw_sphere(smath::Vec3 center, float radius,
float const pi = 3.14159265358979323846f; float const pi = 3.14159265358979323846f;
// Use caller color if provided, otherwise keep current GL color state.
if (sphere_color.has_value()) if (sphere_color.has_value())
color(*sphere_color); color(*sphere_color);
// Build as latitude strips
for (int y = 0; y < rings; y++) { for (int y = 0; y < rings; y++) {
float const v0 = static_cast<float>(y) / static_cast<float>(rings); float const v = static_cast<float>(y + 1) / static_cast<float>(rings);
float const v1 = static_cast<float>(y + 1) / static_cast<float>(rings);
float const theta0 = v0 * pi; float const theta = v * pi;
float const theta1 = v1 * pi;
float const sin0 = std::sin(theta0); float const s = std::sin(theta);
float const cos0 = std::cos(theta0); float const c = std::cos(theta);
float const sin1 = std::sin(theta1);
float const cos1 = std::cos(theta1);
begin(GeometryKind::TriangleStrip); begin(GeometryKind::TriangleStrip);
@@ -452,21 +446,11 @@ auto VulkanRenderer::GL::draw_sphere(smath::Vec3 center, float radius,
// Vertex on ring y+1 // Vertex on ring y+1
{ {
smath::Vec3 n { sin1 * cp, cos1, sin1 * sp }; smath::Vec3 n { s * cp, c, s * sp };
normal(n); normal(n);
uv(smath::Vec2 { u, 1.0f - v1 }); uv(smath::Vec2 { u, 1.0f - v });
smath::Vec3 p = center + n * radius; smath::Vec3 p { center + n * radius };
vert(p);
}
// Vertex on ring y
{
smath::Vec3 n { sin0 * cp, cos0, sin0 * sp };
normal(n);
uv(smath::Vec2 { u, 1.0f - v0 });
smath::Vec3 p = center + n * radius;
vert(p); vert(p);
} }
} }
@@ -501,7 +485,7 @@ auto VulkanRenderer::GL::draw_mesh(GPUMeshBuffers const &mesh,
VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER) VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER)
.update_set(m_renderer.m_vkb.dev.device, image_set); .update_set(m_renderer.m_vkb.dev.device, image_set);
auto vk_image_set = vk::DescriptorSet { image_set }; auto vk_image_set { vk::DescriptorSet { image_set } };
m_cmd.bindDescriptorSets(vk::PipelineBindPoint::eGraphics, m_cmd.bindDescriptorSets(vk::PipelineBindPoint::eGraphics,
mesh_pipeline.get_layout(), 0, vk_image_set, {}); mesh_pipeline.get_layout(), 0, vk_image_set, {});
@@ -541,7 +525,7 @@ auto VulkanRenderer::GL::draw_indexed(Pipeline &pipeline,
push_constants.data()); push_constants.data());
} }
vk::DeviceSize offset = 0; vk::DeviceSize offset { 0 };
cmd.bindVertexBuffers(0, vertex_buffer.buffer, offset); cmd.bindVertexBuffers(0, vertex_buffer.buffer, offset);
cmd.bindIndexBuffer(index_buffer.buffer, 0, vk::IndexType::eUint32); cmd.bindIndexBuffer(index_buffer.buffer, 0, vk::IndexType::eUint32);
cmd.drawIndexed(index_count, 1, 0, 0, 0); cmd.drawIndexed(index_count, 1, 0, 0, 0);
@@ -881,7 +865,7 @@ auto VulkanRenderer::vk_init() -> void
void *user_data) { void *user_data) {
auto renderer { reinterpret_cast<VulkanRenderer *>(user_data) }; auto renderer { reinterpret_cast<VulkanRenderer *>(user_data) };
auto level = Logger::Level::Debug; auto level { Logger::Level::Debug };
if (message_severity if (message_severity
& VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT) { & VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT) {
level = Logger::Level::Error; level = Logger::Level::Error;
@@ -1254,7 +1238,7 @@ auto VulkanRenderer::imgui_init() -> void
{ VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT, 1000 }, { VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT, 1000 },
}; };
VkDescriptorPoolCreateInfo pool_info = {}; VkDescriptorPoolCreateInfo pool_info {};
pool_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO; pool_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO;
pool_info.flags = VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT; pool_info.flags = VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT;
pool_info.maxSets = 1000; pool_info.maxSets = 1000;
@@ -1267,7 +1251,7 @@ auto VulkanRenderer::imgui_init() -> void
ImGui_ImplSDL3_InitForVulkan(m_window); ImGui_ImplSDL3_InitForVulkan(m_window);
ImGui_ImplVulkan_InitInfo init_info = {}; ImGui_ImplVulkan_InitInfo init_info {};
init_info.Instance = m_vkb.instance; init_info.Instance = m_vkb.instance;
init_info.PhysicalDevice = m_vkb.phys_dev.physical_device; init_info.PhysicalDevice = m_vkb.phys_dev.physical_device;
init_info.Device = m_vkb.dev.device; init_info.Device = m_vkb.dev.device;
@@ -1281,7 +1265,8 @@ auto VulkanRenderer::imgui_init() -> void
= VK_STRUCTURE_TYPE_PIPELINE_RENDERING_CREATE_INFO; = VK_STRUCTURE_TYPE_PIPELINE_RENDERING_CREATE_INFO;
init_info.PipelineInfoMain.PipelineRenderingCreateInfo.colorAttachmentCount init_info.PipelineInfoMain.PipelineRenderingCreateInfo.colorAttachmentCount
= 1; = 1;
auto swapchain_format = static_cast<VkFormat>(m_vk.swapchain_image_format); auto swapchain_format { static_cast<VkFormat>(
m_vk.swapchain_image_format) };
init_info.PipelineInfoMain.PipelineRenderingCreateInfo init_info.PipelineInfoMain.PipelineRenderingCreateInfo
.pColorAttachmentFormats .pColorAttachmentFormats
= &swapchain_format; = &swapchain_format;
@@ -1412,7 +1397,7 @@ auto VulkanRenderer::render(std::function<void(GL &)> const &record) -> void
emit_tracy_frame_image(frame); emit_tracy_frame_image(frame);
#endif #endif
auto raw_fence = static_cast<VkFence>(frame.render_fence.get()); auto raw_fence { static_cast<VkFence>(frame.render_fence.get()) };
VK_CHECK(m_logger, vkResetFences(m_vkb.dev.device, 1, &raw_fence)); VK_CHECK(m_logger, vkResetFences(m_vkb.dev.device, 1, &raw_fence));
auto const acquire_result = m_device.acquireNextImageKHR( auto const acquire_result = m_device.acquireNextImageKHR(
@@ -1642,7 +1627,7 @@ auto VulkanRenderer::create_swapchain(uint32_t width, uint32_t height) -> void
m_vk.swapchain = m_vkb.swapchain.swapchain; m_vk.swapchain = m_vkb.swapchain.swapchain;
m_vk.swapchain_extent = vk::Extent2D { m_vkb.swapchain.extent.width, m_vk.swapchain_extent = vk::Extent2D { m_vkb.swapchain.extent.width,
m_vkb.swapchain.extent.height }; m_vkb.swapchain.extent.height };
auto images = m_vkb.swapchain.get_images().value(); auto images { m_vkb.swapchain.get_images().value() };
m_vk.swapchain_images.assign(images.begin(), images.end()); m_vk.swapchain_images.assign(images.begin(), images.end());
m_vk.swapchain_image_views.clear(); m_vk.swapchain_image_views.clear();
@@ -2394,7 +2379,8 @@ auto VulkanRenderer::upload_mesh(
void *data = info.pMappedData; void *data = info.pMappedData;
bool mapped_here { false }; bool mapped_here { false };
if (!data) { if (!data) {
VkResult res = vmaMapMemory(m_vk.allocator, staging.allocation, &data); VkResult res { vmaMapMemory(
m_vk.allocator, staging.allocation, &data) };
assert(res == VK_SUCCESS); assert(res == VK_SUCCESS);
mapped_here = true; mapped_here = true;
} }

View File

@@ -1,7 +1,3 @@
#include "src/Application.h" #include "src/Application.h"
auto main() -> int auto main() -> int { Lunar::Application::the().run(); }
{
Lunar::Application app {};
app.run();
}