mirror of
https://github.com/slendidev/lunar.git
synced 2026-01-30 16:28:58 +02:00
@@ -2,9 +2,11 @@
|
||||
|
||||
#include <algorithm>
|
||||
#include <cerrno>
|
||||
#include <chrono>
|
||||
#include <cmath>
|
||||
#include <cstdint>
|
||||
#include <fcntl.h>
|
||||
#include <format>
|
||||
#include <iostream>
|
||||
#include <numbers>
|
||||
#include <optional>
|
||||
@@ -12,6 +14,23 @@
|
||||
#include <stdexcept>
|
||||
#include <unistd.h>
|
||||
|
||||
#if defined(__clang__)
|
||||
# pragma clang diagnostic push
|
||||
# pragma clang diagnostic ignored "-Wreserved-identifier"
|
||||
# pragma clang diagnostic ignored "-Wimplicit-fallthrough"
|
||||
# pragma clang diagnostic ignored "-Wcast-qual"
|
||||
# pragma clang diagnostic ignored "-Wmissing-field-initializers"
|
||||
# pragma clang diagnostic ignored "-Wused-but-marked-unused"
|
||||
# pragma clang diagnostic ignored "-Wmissing-prototypes"
|
||||
# pragma clang diagnostic ignored "-Wextra-semi-stmt"
|
||||
# pragma clang diagnostic ignored "-Wimplicit-int-conversion"
|
||||
#endif
|
||||
#define STB_IMAGE_WRITE_IMPLEMENTATION
|
||||
#include "../thirdparty/stb/stb_image_write.h"
|
||||
#if defined(__clang__)
|
||||
# pragma clang diagnostic pop
|
||||
#endif
|
||||
|
||||
#include <SDL3/SDL_events.h>
|
||||
#include <SDL3/SDL_init.h>
|
||||
#include <SDL3/SDL_mouse.h>
|
||||
@@ -428,9 +447,7 @@ auto Application::run() -> void
|
||||
uint64_t last { 0 };
|
||||
float fps { 0.0f };
|
||||
while (m_running) {
|
||||
#if defined(TRACY_ENABLE)
|
||||
ZoneScopedN("Frame");
|
||||
#endif
|
||||
GZoneScopedN("Frame");
|
||||
uint64_t now { SDL_GetTicks() };
|
||||
uint64_t dt { now - last };
|
||||
float dt_seconds { static_cast<float>(dt) / 1000.0f };
|
||||
@@ -439,233 +456,246 @@ auto Application::run() -> void
|
||||
if (dt > 0)
|
||||
fps = 1000.0f / (float)dt;
|
||||
|
||||
process_libinput_events();
|
||||
{
|
||||
GZoneScopedN("Input");
|
||||
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) {
|
||||
int width {}, height {};
|
||||
SDL_GetWindowSize(m_window, &width, &height);
|
||||
m_renderer->resize(static_cast<uint32_t>(width),
|
||||
static_cast<uint32_t>(height));
|
||||
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;
|
||||
m_mouse_dx = e.motion.xrel;
|
||||
m_mouse_dy = e.motion.yrel;
|
||||
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;
|
||||
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) {
|
||||
int width {}, height {};
|
||||
SDL_GetWindowSize(m_window, &width, &height);
|
||||
m_renderer->resize(static_cast<uint32_t>(width),
|
||||
static_cast<uint32_t>(height));
|
||||
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;
|
||||
m_mouse_dx = e.motion.xrel;
|
||||
m_mouse_dy = e.motion.yrel;
|
||||
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;
|
||||
}
|
||||
|
||||
if (forward_to_imgui)
|
||||
ImGui_ImplSDL3_ProcessEvent(&e);
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
GZoneScopedN("CameraUpdate");
|
||||
|
||||
auto const target_offset { m_camera.target - m_camera.position };
|
||||
auto const target_distance { target_offset.magnitude() };
|
||||
auto const target_polar { PolarCoordinate::from_vec3(
|
||||
target_offset) };
|
||||
// Keep cursor angles in sync with externally-updated targets so
|
||||
// view aligns to the target direction. Preserve radius when the
|
||||
// target sits on top of the camera to avoid collapsing it.
|
||||
if (target_distance > 0.0f) {
|
||||
m_cursor.r = target_distance;
|
||||
m_cursor.theta = target_polar.theta;
|
||||
m_cursor.phi = target_polar.phi;
|
||||
}
|
||||
|
||||
if (forward_to_imgui)
|
||||
ImGui_ImplSDL3_ProcessEvent(&e);
|
||||
bool rotated_this_frame { false };
|
||||
if (mouse_captured()) {
|
||||
constexpr float phi_epsilon { smath::deg(5.0f) };
|
||||
|
||||
m_cursor.theta
|
||||
+= static_cast<float>(m_mouse_dx) * m_mouse_sensitivity;
|
||||
m_cursor.phi
|
||||
+= static_cast<float>(m_mouse_dy) * m_mouse_sensitivity;
|
||||
m_cursor.phi = std::clamp(m_cursor.phi, phi_epsilon,
|
||||
std::numbers::pi_v<float> - phi_epsilon);
|
||||
rotated_this_frame = (m_mouse_dx != 0.0 || m_mouse_dy != 0.0);
|
||||
}
|
||||
|
||||
auto look_dir { m_cursor.to_vec3().normalized_safe() };
|
||||
if (!rotated_this_frame && target_distance > 0.0f) {
|
||||
look_dir = target_offset.normalized_safe();
|
||||
}
|
||||
if (look_dir.magnitude() == 0.0f)
|
||||
look_dir = smath::Vec3 { 0.0f, 0.0f, -1.0f };
|
||||
smath::Vec3 const world_up { 0.0f, 1.0f, 0.0f };
|
||||
auto right { look_dir.cross(world_up).normalized_safe() };
|
||||
if (right.magnitude() == 0.0f)
|
||||
right = smath::Vec3 { 1.0f, 0.0f, 0.0f };
|
||||
auto camera_up { right.cross(look_dir).normalized_safe() };
|
||||
if (camera_up.magnitude() == 0.0f)
|
||||
camera_up = world_up;
|
||||
|
||||
smath::Vec3 move_dir {};
|
||||
if (is_key_pressed(KEY_W))
|
||||
move_dir += look_dir;
|
||||
if (is_key_pressed(KEY_S))
|
||||
move_dir -= look_dir;
|
||||
if (is_key_pressed(KEY_D))
|
||||
move_dir += right;
|
||||
if (is_key_pressed(KEY_A))
|
||||
move_dir -= right;
|
||||
if (is_key_pressed(KEY_SPACE))
|
||||
move_dir += world_up;
|
||||
if (is_key_pressed(KEY_LEFTSHIFT))
|
||||
move_dir -= world_up;
|
||||
|
||||
if (move_dir.magnitude() > 0.0f) {
|
||||
constexpr float move_speed { 10.0f };
|
||||
move_dir = move_dir.normalized_safe();
|
||||
m_camera.position += move_dir * (move_speed * dt_seconds);
|
||||
}
|
||||
|
||||
if (!m_show_imgui) {
|
||||
m_camera.up = camera_up;
|
||||
auto const distance = target_distance > 0.0f
|
||||
? target_distance
|
||||
: std::max(1.0f, m_cursor.r);
|
||||
m_camera.target = m_camera.position + look_dir * distance;
|
||||
}
|
||||
|
||||
m_mouse_dx = 0.0;
|
||||
m_mouse_dy = 0.0;
|
||||
}
|
||||
|
||||
auto const target_offset { m_camera.target - m_camera.position };
|
||||
auto const target_distance { target_offset.magnitude() };
|
||||
auto const target_polar { PolarCoordinate::from_vec3(target_offset) };
|
||||
// Keep cursor angles in sync with externally-updated targets so view
|
||||
// aligns to the target direction. Preserve radius when the target sits
|
||||
// on top of the camera to avoid collapsing it.
|
||||
if (target_distance > 0.0f) {
|
||||
m_cursor.r = target_distance;
|
||||
m_cursor.theta = target_polar.theta;
|
||||
m_cursor.phi = target_polar.phi;
|
||||
}
|
||||
{
|
||||
GZoneScopedN("ImGui");
|
||||
ImGui_ImplSDL3_NewFrame();
|
||||
ImGui_ImplVulkan_NewFrame();
|
||||
|
||||
bool rotated_this_frame { false };
|
||||
if (mouse_captured()) {
|
||||
constexpr float phi_epsilon { smath::deg(5.0f) };
|
||||
ImGui::NewFrame();
|
||||
|
||||
m_cursor.theta
|
||||
+= static_cast<float>(m_mouse_dx) * m_mouse_sensitivity;
|
||||
m_cursor.phi
|
||||
+= static_cast<float>(m_mouse_dy) * m_mouse_sensitivity;
|
||||
m_cursor.phi = std::clamp(m_cursor.phi, phi_epsilon,
|
||||
std::numbers::pi_v<float> - phi_epsilon);
|
||||
rotated_this_frame = (m_mouse_dx != 0.0 || m_mouse_dy != 0.0);
|
||||
}
|
||||
|
||||
auto look_dir { m_cursor.to_vec3().normalized_safe() };
|
||||
if (!rotated_this_frame && target_distance > 0.0f) {
|
||||
look_dir = target_offset.normalized_safe();
|
||||
}
|
||||
if (look_dir.magnitude() == 0.0f)
|
||||
look_dir = smath::Vec3 { 0.0f, 0.0f, -1.0f };
|
||||
smath::Vec3 const world_up { 0.0f, 1.0f, 0.0f };
|
||||
auto right { look_dir.cross(world_up).normalized_safe() };
|
||||
if (right.magnitude() == 0.0f)
|
||||
right = smath::Vec3 { 1.0f, 0.0f, 0.0f };
|
||||
auto camera_up { right.cross(look_dir).normalized_safe() };
|
||||
if (camera_up.magnitude() == 0.0f)
|
||||
camera_up = world_up;
|
||||
|
||||
smath::Vec3 move_dir {};
|
||||
if (is_key_pressed(KEY_W))
|
||||
move_dir += look_dir;
|
||||
if (is_key_pressed(KEY_S))
|
||||
move_dir -= look_dir;
|
||||
if (is_key_pressed(KEY_D))
|
||||
move_dir += right;
|
||||
if (is_key_pressed(KEY_A))
|
||||
move_dir -= right;
|
||||
if (is_key_pressed(KEY_SPACE))
|
||||
move_dir += world_up;
|
||||
if (is_key_pressed(KEY_LEFTSHIFT))
|
||||
move_dir -= world_up;
|
||||
|
||||
if (move_dir.magnitude() > 0.0f) {
|
||||
constexpr float move_speed { 10.0f };
|
||||
move_dir = move_dir.normalized_safe();
|
||||
m_camera.position += move_dir * (move_speed * dt_seconds);
|
||||
}
|
||||
|
||||
if (!m_show_imgui) {
|
||||
m_camera.up = camera_up;
|
||||
auto const distance = target_distance > 0.0f
|
||||
? target_distance
|
||||
: std::max(1.0f, m_cursor.r);
|
||||
m_camera.target = m_camera.position + look_dir * distance;
|
||||
}
|
||||
|
||||
m_mouse_dx = 0.0;
|
||||
m_mouse_dy = 0.0;
|
||||
|
||||
ImGui_ImplSDL3_NewFrame();
|
||||
ImGui_ImplVulkan_NewFrame();
|
||||
|
||||
ImGui::NewFrame();
|
||||
|
||||
ImGui::SetNextWindowSize({ 300, 100 });
|
||||
ImGui::SetNextWindowPos({ 0, 0 });
|
||||
ImGui::PushStyleColor(ImGuiCol_WindowBg, ImVec4 { 0, 0, 0, 0.5f });
|
||||
bool debug_open { ImGui::Begin("Debug Info", nullptr,
|
||||
ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize) };
|
||||
if (debug_open) {
|
||||
ImGui::Text("%s", std::format("FPS: {:.2f}", fps).c_str());
|
||||
ImGui::Text("%s",
|
||||
std::format("Cam pos: ({:.2f}, {:.2f}, {:.2f})",
|
||||
m_camera.position.x(), m_camera.position.y(),
|
||||
m_camera.position.z())
|
||||
.c_str());
|
||||
ImGui::Text("%s",
|
||||
std::format("Cam tgt: ({:.2f}, {:.2f}, {:.2f})",
|
||||
m_camera.target.x(), m_camera.target.y(),
|
||||
m_camera.target.z())
|
||||
.c_str());
|
||||
ImGui::Text("%s",
|
||||
std::format("Cam up: ({:.2f}, {:.2f}, {:.2f})",
|
||||
m_camera.up.x(), m_camera.up.y(), m_camera.up.z())
|
||||
.c_str());
|
||||
ImGui::Text("%s",
|
||||
std::format("Cursor r/theta/phi: {:.2f}, {:.2f}, {:.2f}",
|
||||
m_cursor.r, m_cursor.theta, m_cursor.phi)
|
||||
.c_str());
|
||||
}
|
||||
ImGui::End();
|
||||
ImGui::PopStyleColor();
|
||||
|
||||
if (m_show_imgui) {
|
||||
ImGui::SetNextWindowCollapsed(true, ImGuiCond_Once);
|
||||
ImGui::ShowDemoWindow();
|
||||
|
||||
ImGui::SetNextWindowSize({ 300, -1 }, ImGuiCond_Once);
|
||||
bool fun_menu_open { ImGui::Begin("Fun menu") };
|
||||
if (fun_menu_open) {
|
||||
static std::array<char const *, 4> const aa_items {
|
||||
"None",
|
||||
"MSAA 2X",
|
||||
"MSAA 4X",
|
||||
"MSAA 8X",
|
||||
};
|
||||
int selected_item {
|
||||
static_cast<int>(m_renderer->antialiasing()),
|
||||
};
|
||||
if (ImGui::Combo("Antialiasing", &selected_item,
|
||||
aa_items.data(), aa_items.size())) {
|
||||
m_renderer->set_antialiasing(
|
||||
static_cast<VulkanRenderer::AntiAliasingKind>(
|
||||
selected_item));
|
||||
}
|
||||
|
||||
if (ImGui::CollapsingHeader("Camera",
|
||||
ImGuiTreeNodeFlags_Framed
|
||||
| ImGuiTreeNodeFlags_SpanAvailWidth
|
||||
| ImGuiTreeNodeFlags_DefaultOpen)) {
|
||||
auto const camera_offset { m_camera.target
|
||||
- m_camera.position };
|
||||
auto const camera_distance { camera_offset.magnitude() };
|
||||
auto const camera_direction {
|
||||
camera_offset.normalized_safe()
|
||||
};
|
||||
|
||||
ImGui::SliderFloat("Mouse sensitivity",
|
||||
&m_mouse_sensitivity, 0.0001f, 0.01f, "%.4f");
|
||||
constexpr float position_step { 0.05f };
|
||||
constexpr float target_step { 0.05f };
|
||||
bool position_changed { ImGui::DragFloat3(
|
||||
"Pos", m_camera.position.data(), position_step) };
|
||||
bool target_changed { ImGui::DragFloat3(
|
||||
"Target", m_camera.target.data(), target_step) };
|
||||
ImGui::DragFloat3("Up", m_camera.up.data());
|
||||
|
||||
if (position_changed && !target_changed) {
|
||||
auto offset { m_cursor.to_vec3() };
|
||||
auto const preserve_distance { camera_distance > 0.0f
|
||||
? camera_distance
|
||||
: offset.magnitude() };
|
||||
|
||||
if (offset.magnitude() == 0.0f
|
||||
&& camera_direction.magnitude() > 0.0f) {
|
||||
offset = camera_direction * preserve_distance;
|
||||
}
|
||||
|
||||
if (offset.magnitude() > 0.0f) {
|
||||
m_camera.target = m_camera.position + offset;
|
||||
}
|
||||
}
|
||||
|
||||
if (target_changed && !position_changed) {
|
||||
auto const new_offset { m_camera.target
|
||||
- m_camera.position };
|
||||
auto new_direction { new_offset.normalized_safe() };
|
||||
auto const preserve_distance { camera_distance > 0.0f
|
||||
? camera_distance
|
||||
: new_offset.magnitude() };
|
||||
|
||||
if (new_direction.magnitude() == 0.0f) {
|
||||
new_direction = camera_direction;
|
||||
}
|
||||
|
||||
if (new_direction.magnitude() > 0.0f
|
||||
&& preserve_distance > 0.0f) {
|
||||
m_camera.position = m_camera.target
|
||||
- new_direction * preserve_distance;
|
||||
}
|
||||
}
|
||||
}
|
||||
ImGui::SetNextWindowSize({ 300, 100 });
|
||||
ImGui::SetNextWindowPos({ 0, 0 });
|
||||
ImGui::PushStyleColor(ImGuiCol_WindowBg, ImVec4 { 0, 0, 0, 0.5f });
|
||||
bool debug_open { ImGui::Begin("Debug Info", nullptr,
|
||||
ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize) };
|
||||
if (debug_open) {
|
||||
ImGui::Text("%s", std::format("FPS: {:.2f}", fps).c_str());
|
||||
ImGui::Text("%s",
|
||||
std::format("Cam pos: ({:.2f}, {:.2f}, {:.2f})",
|
||||
m_camera.position.x(), m_camera.position.y(),
|
||||
m_camera.position.z())
|
||||
.c_str());
|
||||
ImGui::Text("%s",
|
||||
std::format("Cam tgt: ({:.2f}, {:.2f}, {:.2f})",
|
||||
m_camera.target.x(), m_camera.target.y(),
|
||||
m_camera.target.z())
|
||||
.c_str());
|
||||
ImGui::Text("%s",
|
||||
std::format("Cam up: ({:.2f}, {:.2f}, {:.2f})",
|
||||
m_camera.up.x(), m_camera.up.y(), m_camera.up.z())
|
||||
.c_str());
|
||||
ImGui::Text("%s",
|
||||
std::format("Cursor r/theta/phi: {:.2f}, {:.2f}, {:.2f}",
|
||||
m_cursor.r, m_cursor.theta, m_cursor.phi)
|
||||
.c_str());
|
||||
}
|
||||
ImGui::End();
|
||||
ImGui::PopStyleColor();
|
||||
|
||||
if (m_show_imgui) {
|
||||
ImGui::SetNextWindowCollapsed(true, ImGuiCond_Once);
|
||||
ImGui::ShowDemoWindow();
|
||||
|
||||
ImGui::SetNextWindowSize({ 300, -1 }, ImGuiCond_Once);
|
||||
bool fun_menu_open { ImGui::Begin("Fun menu") };
|
||||
if (fun_menu_open) {
|
||||
static std::array<char const *, 4> const aa_items {
|
||||
"None",
|
||||
"MSAA 2X",
|
||||
"MSAA 4X",
|
||||
"MSAA 8X",
|
||||
};
|
||||
int selected_item {
|
||||
static_cast<int>(m_renderer->antialiasing()),
|
||||
};
|
||||
if (ImGui::Combo("Antialiasing", &selected_item,
|
||||
aa_items.data(), aa_items.size())) {
|
||||
m_renderer->set_antialiasing(
|
||||
static_cast<VulkanRenderer::AntiAliasingKind>(
|
||||
selected_item));
|
||||
}
|
||||
|
||||
if (ImGui::CollapsingHeader("Camera",
|
||||
ImGuiTreeNodeFlags_Framed
|
||||
| ImGuiTreeNodeFlags_SpanAvailWidth
|
||||
| ImGuiTreeNodeFlags_DefaultOpen)) {
|
||||
auto const camera_offset { m_camera.target
|
||||
- m_camera.position };
|
||||
auto const camera_distance {
|
||||
camera_offset.magnitude()
|
||||
};
|
||||
auto const camera_direction {
|
||||
camera_offset.normalized_safe()
|
||||
};
|
||||
|
||||
ImGui::SliderFloat("Mouse sensitivity",
|
||||
&m_mouse_sensitivity, 0.0001f, 0.01f, "%.4f");
|
||||
constexpr float position_step { 0.05f };
|
||||
constexpr float target_step { 0.05f };
|
||||
bool position_changed { ImGui::DragFloat3(
|
||||
"Pos", m_camera.position.data(), position_step) };
|
||||
bool target_changed { ImGui::DragFloat3(
|
||||
"Target", m_camera.target.data(), target_step) };
|
||||
ImGui::DragFloat3("Up", m_camera.up.data());
|
||||
|
||||
if (position_changed && !target_changed) {
|
||||
auto offset { m_cursor.to_vec3() };
|
||||
auto const preserve_distance {
|
||||
camera_distance > 0.0f ? camera_distance
|
||||
: offset.magnitude()
|
||||
};
|
||||
|
||||
if (offset.magnitude() == 0.0f
|
||||
&& camera_direction.magnitude() > 0.0f) {
|
||||
offset = camera_direction * preserve_distance;
|
||||
}
|
||||
|
||||
if (offset.magnitude() > 0.0f) {
|
||||
m_camera.target = m_camera.position + offset;
|
||||
}
|
||||
}
|
||||
|
||||
if (target_changed && !position_changed) {
|
||||
auto const new_offset { m_camera.target
|
||||
- m_camera.position };
|
||||
auto new_direction { new_offset.normalized_safe() };
|
||||
auto const preserve_distance {
|
||||
camera_distance > 0.0f ? camera_distance
|
||||
: new_offset.magnitude()
|
||||
};
|
||||
|
||||
if (new_direction.magnitude() == 0.0f) {
|
||||
new_direction = camera_direction;
|
||||
}
|
||||
|
||||
if (new_direction.magnitude() > 0.0f
|
||||
&& preserve_distance > 0.0f) {
|
||||
m_camera.position = m_camera.target
|
||||
- new_direction * preserve_distance;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
ImGui::End();
|
||||
}
|
||||
|
||||
ImGui::Render();
|
||||
}
|
||||
|
||||
ImGui::Render();
|
||||
|
||||
m_renderer->render([&](VulkanRenderer::GL &gl) {
|
||||
#if defined(TRACY_ENABLE)
|
||||
ZoneScopedN("Render");
|
||||
#endif
|
||||
GZoneScopedN("Render");
|
||||
auto view { smath::matrix_look_at(
|
||||
m_camera.position, m_camera.target, m_camera.up) };
|
||||
auto const draw_extent = m_renderer->draw_extent();
|
||||
@@ -821,6 +851,34 @@ auto Application::handle_keyboard_event(libinput_event_keyboard *event) -> void
|
||||
mouse_captured(!new_show_imgui);
|
||||
}
|
||||
|
||||
if (pressed && key == KEY_F12) {
|
||||
auto screenshot { std::optional<VulkanRenderer::ScreenshotPixels> {} };
|
||||
if (m_renderer) {
|
||||
screenshot = m_renderer->get_screenshot_pixels();
|
||||
}
|
||||
|
||||
if (!screenshot) {
|
||||
m_logger.warn("Screenshot not ready");
|
||||
return;
|
||||
}
|
||||
|
||||
auto const extent { screenshot->extent };
|
||||
auto const stride { static_cast<int>(extent.width * 4) };
|
||||
auto const index { m_screenshot_index++ };
|
||||
auto const now { std::chrono::system_clock::now() };
|
||||
auto filename { std::format(
|
||||
"screenshot_{:%Y%m%d_%H%M%S}_{:04}.png", now, index) };
|
||||
int const result { stbi_write_png(filename.c_str(),
|
||||
static_cast<int>(extent.width), static_cast<int>(extent.height), 4,
|
||||
screenshot->pixels.data(), stride) };
|
||||
|
||||
if (result == 0) {
|
||||
m_logger.err("Failed to write screenshot {}", filename);
|
||||
} else {
|
||||
m_logger.info("Saved screenshot {}", filename);
|
||||
}
|
||||
}
|
||||
|
||||
if (auto imgui_key { linux_key_to_imgui(key) }) {
|
||||
if (m_show_imgui)
|
||||
ImGui::GetIO().AddKeyEvent(*imgui_key, pressed);
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
|
||||
#include <SDL3/SDL_video.h>
|
||||
@@ -48,6 +49,7 @@ private:
|
||||
bool m_mouse_captured { false };
|
||||
bool m_show_imgui { false };
|
||||
int m_ctrl_pressed_count { 0 };
|
||||
std::uint32_t m_screenshot_index { 0 };
|
||||
|
||||
double m_mouse_x { 0.0 };
|
||||
double m_mouse_y { 0.0 };
|
||||
|
||||
11
src/Types.h
11
src/Types.h
@@ -1,6 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
#include <cmath>
|
||||
#include <cstdint>
|
||||
#include <vector>
|
||||
|
||||
#include <smath.hpp>
|
||||
#include <vk_mem_alloc.h>
|
||||
@@ -33,6 +35,15 @@ struct FrameData {
|
||||
|
||||
DeletionQueue deletion_queue;
|
||||
DescriptorAllocatorGrowable frame_descriptors;
|
||||
AllocatedBuffer frame_image_buffer {};
|
||||
vk::Extent2D frame_image_extent {};
|
||||
std::vector<std::uint8_t> frame_image_rgba;
|
||||
bool frame_image_ready { false };
|
||||
bool tracy_frame_ready { false };
|
||||
AllocatedBuffer screenshot_buffer {};
|
||||
vk::Extent2D screenshot_extent {};
|
||||
std::vector<std::uint8_t> screenshot_rgba;
|
||||
bool screenshot_ready { false };
|
||||
};
|
||||
|
||||
struct Vertex {
|
||||
|
||||
@@ -36,6 +36,14 @@ template<typename F> privDefer<F> defer_func(F f) { return privDefer<F>(f); }
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#if defined(TRACY_ENABLE)
|
||||
# define GZoneScopedN(name) ZoneScopedN(name)
|
||||
#else
|
||||
# define GZoneScopedN(name) \
|
||||
do { \
|
||||
} while (0)
|
||||
#endif
|
||||
|
||||
namespace vkutil {
|
||||
|
||||
auto transition_image(vk::CommandBuffer cmd, vk::Image image,
|
||||
|
||||
@@ -1,11 +1,14 @@
|
||||
#define VULKAN_HPP_DEFAULT_DISPATCH_LOADER_DYNAMIC_STORAGE
|
||||
#include "VulkanRenderer.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
#include <cmath>
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
#include <format>
|
||||
#include <iostream>
|
||||
#include <limits>
|
||||
#include <optional>
|
||||
#include <print>
|
||||
#include <stdexcept>
|
||||
@@ -18,12 +21,15 @@
|
||||
#include <imgui_impl_sdl3.h>
|
||||
#include <imgui_impl_vulkan.h>
|
||||
|
||||
#if defined(TRACY_ENABLE)
|
||||
# include <tracy/Tracy.hpp>
|
||||
#endif
|
||||
|
||||
#include "DescriptorLayoutBuilder.h"
|
||||
#include "DescriptorWriter.h"
|
||||
#include "GraphicsPipelineBuilder.h"
|
||||
#include "Util.h"
|
||||
|
||||
VULKAN_HPP_DEFAULT_DISPATCH_LOADER_DYNAMIC_STORAGE
|
||||
VULKAN_HPP_DEFAULT_DISPATCH_LOADER_DYNAMIC_STORAGE;
|
||||
|
||||
namespace Lunar {
|
||||
|
||||
@@ -633,6 +639,11 @@ VulkanRenderer::~VulkanRenderer()
|
||||
m_vk.default_sampler_linear.reset();
|
||||
m_vk.default_sampler_nearest.reset();
|
||||
|
||||
if (m_latest_screenshot) {
|
||||
destroy_image(*m_latest_screenshot);
|
||||
m_latest_screenshot.reset();
|
||||
}
|
||||
|
||||
destroy_swapchain();
|
||||
destroy_draw_image();
|
||||
destroy_msaa_color_image();
|
||||
@@ -1369,6 +1380,10 @@ auto VulkanRenderer::render(std::function<void(GL &)> const &record) -> void
|
||||
m_device.waitForFences(frame.render_fence.get(), true, 1'000'000'000));
|
||||
frame.deletion_queue.flush();
|
||||
frame.frame_descriptors.clear_pools(m_vkb.dev.device);
|
||||
emit_frame_screenshot(frame);
|
||||
#if defined(TRACY_ENABLE)
|
||||
emit_tracy_frame_image(frame);
|
||||
#endif
|
||||
|
||||
auto raw_fence = static_cast<VkFence>(frame.render_fence.get());
|
||||
VK_CHECK(m_logger, vkResetFences(m_vkb.dev.device, 1, &raw_fence));
|
||||
@@ -1438,9 +1453,92 @@ auto VulkanRenderer::render(std::function<void(GL &)> const &record) -> void
|
||||
|
||||
draw_imgui(cmd, m_vk.swapchain_image_views.at(swapchain_image_idx).get());
|
||||
|
||||
vkutil::transition_image(cmd, m_vk.swapchain_images[swapchain_image_idx],
|
||||
vkutil::transition_image(cmd, m_vk.swapchain_images.at(swapchain_image_idx),
|
||||
vk::ImageLayout::eColorAttachmentOptimal,
|
||||
vk::ImageLayout::ePresentSrcKHR);
|
||||
vk::ImageLayout::eTransferSrcOptimal);
|
||||
|
||||
if (frame.screenshot_buffer.buffer) {
|
||||
vk::BufferImageCopy screenshot_copy {};
|
||||
screenshot_copy.imageSubresource.aspectMask
|
||||
= vk::ImageAspectFlagBits::eColor;
|
||||
screenshot_copy.imageSubresource.mipLevel = 0;
|
||||
screenshot_copy.imageSubresource.baseArrayLayer = 0;
|
||||
screenshot_copy.imageSubresource.layerCount = 1;
|
||||
screenshot_copy.imageExtent
|
||||
= vk::Extent3D { m_vk.swapchain_extent.width,
|
||||
m_vk.swapchain_extent.height, 1 };
|
||||
|
||||
cmd.copyImageToBuffer(m_vk.swapchain_images.at(swapchain_image_idx),
|
||||
vk::ImageLayout::eTransferSrcOptimal,
|
||||
frame.screenshot_buffer.buffer, screenshot_copy);
|
||||
frame.screenshot_ready = true;
|
||||
} else {
|
||||
frame.screenshot_ready = false;
|
||||
}
|
||||
|
||||
#if defined(TRACY_ENABLE)
|
||||
constexpr std::uint64_t tracy_frame_stride { 10 };
|
||||
bool const tracy_capture { TracyIsConnected
|
||||
&& (m_vk.frame_number % tracy_frame_stride == 0) };
|
||||
frame.tracy_frame_ready = false;
|
||||
frame.frame_image_ready = false;
|
||||
if (tracy_capture && frame.frame_image_buffer.buffer
|
||||
&& m_vk.tracy_capture_image.image) {
|
||||
vkutil::transition_image(cmd, m_vk.tracy_capture_image.image,
|
||||
m_vk.tracy_capture_image_layout,
|
||||
vk::ImageLayout::eTransferDstOptimal);
|
||||
m_vk.tracy_capture_image_layout = vk::ImageLayout::eTransferDstOptimal;
|
||||
|
||||
vk::ImageBlit blit_region {};
|
||||
blit_region.srcSubresource.aspectMask = vk::ImageAspectFlagBits::eColor;
|
||||
blit_region.srcSubresource.mipLevel = 0;
|
||||
blit_region.srcSubresource.baseArrayLayer = 0;
|
||||
blit_region.srcSubresource.layerCount = 1;
|
||||
blit_region.srcOffsets[0] = vk::Offset3D { 0, 0, 0 };
|
||||
blit_region.srcOffsets[1]
|
||||
= vk::Offset3D { static_cast<int32_t>(m_vk.swapchain_extent.width),
|
||||
static_cast<int32_t>(m_vk.swapchain_extent.height), 1 };
|
||||
blit_region.dstSubresource.aspectMask = vk::ImageAspectFlagBits::eColor;
|
||||
blit_region.dstSubresource.mipLevel = 0;
|
||||
blit_region.dstSubresource.baseArrayLayer = 0;
|
||||
blit_region.dstSubresource.layerCount = 1;
|
||||
blit_region.dstOffsets[0] = vk::Offset3D { 0, 0, 0 };
|
||||
blit_region.dstOffsets[1] = vk::Offset3D {
|
||||
static_cast<int32_t>(m_vk.tracy_capture_extent.width),
|
||||
static_cast<int32_t>(m_vk.tracy_capture_extent.height), 1
|
||||
};
|
||||
|
||||
cmd.blitImage(m_vk.swapchain_images.at(swapchain_image_idx),
|
||||
vk::ImageLayout::eTransferSrcOptimal,
|
||||
m_vk.tracy_capture_image.image,
|
||||
vk::ImageLayout::eTransferDstOptimal, blit_region,
|
||||
vk::Filter::eLinear);
|
||||
|
||||
vkutil::transition_image(cmd, m_vk.tracy_capture_image.image,
|
||||
m_vk.tracy_capture_image_layout,
|
||||
vk::ImageLayout::eTransferSrcOptimal);
|
||||
m_vk.tracy_capture_image_layout = vk::ImageLayout::eTransferSrcOptimal;
|
||||
|
||||
vk::BufferImageCopy copy_region {};
|
||||
copy_region.imageSubresource.aspectMask
|
||||
= vk::ImageAspectFlagBits::eColor;
|
||||
copy_region.imageSubresource.mipLevel = 0;
|
||||
copy_region.imageSubresource.baseArrayLayer = 0;
|
||||
copy_region.imageSubresource.layerCount = 1;
|
||||
copy_region.imageExtent
|
||||
= vk::Extent3D { m_vk.tracy_capture_extent.width,
|
||||
m_vk.tracy_capture_extent.height, 1 };
|
||||
|
||||
cmd.copyImageToBuffer(m_vk.tracy_capture_image.image,
|
||||
vk::ImageLayout::eTransferSrcOptimal,
|
||||
frame.frame_image_buffer.buffer, copy_region);
|
||||
frame.frame_image_ready = true;
|
||||
frame.tracy_frame_ready = true;
|
||||
}
|
||||
#endif
|
||||
|
||||
vkutil::transition_image(cmd, m_vk.swapchain_images.at(swapchain_image_idx),
|
||||
vk::ImageLayout::eTransferSrcOptimal, vk::ImageLayout::ePresentSrcKHR);
|
||||
|
||||
cmd.end();
|
||||
|
||||
@@ -1503,7 +1601,9 @@ auto VulkanRenderer::create_swapchain(uint32_t width, uint32_t height) -> void
|
||||
})
|
||||
.set_desired_present_mode(VK_PRESENT_MODE_FIFO_KHR)
|
||||
.set_desired_extent(width, height)
|
||||
.add_image_usage_flags(VK_IMAGE_USAGE_TRANSFER_DST_BIT)
|
||||
.set_image_usage_flags(VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT
|
||||
| VK_IMAGE_USAGE_TRANSFER_DST_BIT
|
||||
| VK_IMAGE_USAGE_TRANSFER_SRC_BIT)
|
||||
.build() };
|
||||
if (!swapchain_ret) {
|
||||
std::println(std::cerr, "Failed to create swapchain. Error: {}",
|
||||
@@ -1536,6 +1636,10 @@ auto VulkanRenderer::create_swapchain(uint32_t width, uint32_t height) -> void
|
||||
for (auto &semaphore : m_vk.present_semaphores) {
|
||||
semaphore = m_device.createSemaphoreUnique(semaphore_ci);
|
||||
}
|
||||
ensure_screenshot_buffers(m_vk.swapchain_extent);
|
||||
#if defined(TRACY_ENABLE)
|
||||
ensure_tracy_frame_buffers(m_vk.swapchain_extent);
|
||||
#endif
|
||||
}
|
||||
|
||||
auto VulkanRenderer::create_draw_image(uint32_t width, uint32_t height) -> void
|
||||
@@ -1652,6 +1756,11 @@ auto VulkanRenderer::destroy_swapchain() -> void
|
||||
if (!m_vk.swapchain)
|
||||
return;
|
||||
|
||||
destroy_screenshot_buffers();
|
||||
#if defined(TRACY_ENABLE)
|
||||
destroy_tracy_frame_buffers();
|
||||
#endif
|
||||
|
||||
m_vk.present_semaphores.clear();
|
||||
m_device.destroySwapchainKHR(m_vk.swapchain);
|
||||
|
||||
@@ -1662,11 +1771,292 @@ auto VulkanRenderer::destroy_swapchain() -> void
|
||||
m_vk.swapchain_extent = vk::Extent2D { 0, 0 };
|
||||
}
|
||||
|
||||
auto VulkanRenderer::create_image(vk::Extent3D size, vk::Format format,
|
||||
auto VulkanRenderer::ensure_screenshot_buffers(vk::Extent2D extent) -> void
|
||||
{
|
||||
if (extent.width == 0 || extent.height == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto const byte_count { static_cast<size_t>(extent.width)
|
||||
* static_cast<size_t>(extent.height) * 4 };
|
||||
|
||||
for (auto &frame : m_vk.frames) {
|
||||
auto const same_extent { frame.screenshot_buffer.buffer
|
||||
&& frame.screenshot_extent.width == extent.width
|
||||
&& frame.screenshot_extent.height == extent.height };
|
||||
if (!same_extent && frame.screenshot_buffer.buffer) {
|
||||
destroy_buffer(frame.screenshot_buffer);
|
||||
frame.screenshot_buffer = AllocatedBuffer {};
|
||||
}
|
||||
|
||||
if (!same_extent) {
|
||||
frame.screenshot_buffer = create_buffer(byte_count,
|
||||
vk::BufferUsageFlagBits::eTransferDst,
|
||||
VMA_MEMORY_USAGE_GPU_TO_CPU);
|
||||
frame.screenshot_extent = extent;
|
||||
frame.screenshot_rgba.resize(byte_count);
|
||||
}
|
||||
|
||||
frame.screenshot_ready = false;
|
||||
}
|
||||
}
|
||||
|
||||
auto VulkanRenderer::destroy_screenshot_buffers() -> void
|
||||
{
|
||||
for (auto &frame : m_vk.frames) {
|
||||
if (frame.screenshot_buffer.buffer) {
|
||||
destroy_buffer(frame.screenshot_buffer);
|
||||
frame.screenshot_buffer = AllocatedBuffer {};
|
||||
}
|
||||
frame.screenshot_extent = vk::Extent2D {};
|
||||
frame.screenshot_rgba.clear();
|
||||
frame.screenshot_ready = false;
|
||||
}
|
||||
|
||||
m_latest_screenshot_pixels.clear();
|
||||
m_latest_screenshot_extent = vk::Extent2D {};
|
||||
if (m_latest_screenshot) {
|
||||
destroy_image(*m_latest_screenshot);
|
||||
m_latest_screenshot.reset();
|
||||
}
|
||||
}
|
||||
|
||||
auto VulkanRenderer::emit_frame_screenshot(FrameData &frame) -> void
|
||||
{
|
||||
if (!frame.screenshot_ready) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto const extent { frame.screenshot_extent };
|
||||
if (extent.width == 0 || extent.height == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto const byte_count { static_cast<size_t>(extent.width)
|
||||
* static_cast<size_t>(extent.height) * 4 };
|
||||
if (!frame.screenshot_buffer.buffer) {
|
||||
return;
|
||||
}
|
||||
|
||||
VmaAllocationInfo info {};
|
||||
vmaGetAllocationInfo(
|
||||
m_vk.allocator, frame.screenshot_buffer.allocation, &info);
|
||||
|
||||
void *mapped { info.pMappedData };
|
||||
bool mapped_here { false };
|
||||
if (!mapped) {
|
||||
auto const map_result { vmaMapMemory(
|
||||
m_vk.allocator, frame.screenshot_buffer.allocation, &mapped) };
|
||||
if (map_result != VK_SUCCESS) {
|
||||
return;
|
||||
}
|
||||
mapped_here = true;
|
||||
}
|
||||
|
||||
auto *source { static_cast<std::uint8_t *>(mapped) };
|
||||
auto &destination { frame.screenshot_rgba };
|
||||
if (destination.size() != byte_count) {
|
||||
destination.resize(byte_count);
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < byte_count; i += 4) {
|
||||
destination[i] = source[i + 2];
|
||||
destination[i + 1] = source[i + 1];
|
||||
destination[i + 2] = source[i];
|
||||
destination[i + 3] = source[i + 3];
|
||||
}
|
||||
|
||||
auto const screenshot_flags { vk::ImageUsageFlagBits::eSampled };
|
||||
auto const screenshot_extent { vk::Extent3D {
|
||||
extent.width, extent.height, 1 } };
|
||||
|
||||
if (m_latest_screenshot) {
|
||||
destroy_image(*m_latest_screenshot);
|
||||
m_latest_screenshot.reset();
|
||||
}
|
||||
|
||||
m_latest_screenshot = create_image(destination.data(), screenshot_extent,
|
||||
vk::Format::eR8G8B8A8Unorm, screenshot_flags);
|
||||
m_latest_screenshot_pixels = destination;
|
||||
m_latest_screenshot_extent = extent;
|
||||
|
||||
if (mapped_here) {
|
||||
vmaUnmapMemory(m_vk.allocator, frame.screenshot_buffer.allocation);
|
||||
}
|
||||
|
||||
frame.screenshot_ready = false;
|
||||
}
|
||||
|
||||
#if defined(TRACY_ENABLE)
|
||||
namespace {
|
||||
|
||||
[[nodiscard]] auto tracy_capture_extent(vk::Extent2D extent) -> vk::Extent2D
|
||||
{
|
||||
constexpr uint32_t max_width { 320 };
|
||||
constexpr uint32_t max_height { 180 };
|
||||
|
||||
auto width { std::min(extent.width, max_width) };
|
||||
auto height { std::min(extent.height, max_height) };
|
||||
|
||||
width -= width % 4;
|
||||
height -= height % 4;
|
||||
|
||||
return vk::Extent2D { width, height };
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
auto VulkanRenderer::ensure_tracy_frame_buffers(vk::Extent2D extent) -> void
|
||||
{
|
||||
auto const capture_extent { tracy_capture_extent(extent) };
|
||||
if (capture_extent.width == 0 || capture_extent.height == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto const byte_count { static_cast<size_t>(capture_extent.width)
|
||||
* static_cast<size_t>(capture_extent.height) * 4 };
|
||||
|
||||
if (m_vk.tracy_capture_extent.width != capture_extent.width
|
||||
|| m_vk.tracy_capture_extent.height != capture_extent.height) {
|
||||
if (m_vk.tracy_capture_image.image) {
|
||||
destroy_image(m_vk.tracy_capture_image);
|
||||
}
|
||||
|
||||
auto const flags { vk::ImageUsageFlagBits::eTransferDst
|
||||
| vk::ImageUsageFlagBits::eTransferSrc
|
||||
| vk::ImageUsageFlagBits::eColorAttachment
|
||||
| vk::ImageUsageFlagBits::eSampled };
|
||||
auto const capture_size { vk::Extent3D {
|
||||
capture_extent.width, capture_extent.height, 1 } };
|
||||
m_vk.tracy_capture_image = create_image_no_view(
|
||||
capture_size, m_vk.swapchain_image_format, flags);
|
||||
m_vk.tracy_capture_image_layout = vk::ImageLayout::eUndefined;
|
||||
m_vk.tracy_capture_extent = capture_extent;
|
||||
}
|
||||
|
||||
for (auto &frame : m_vk.frames) {
|
||||
auto const same_extent { frame.frame_image_buffer.buffer
|
||||
&& frame.frame_image_extent.width == capture_extent.width
|
||||
&& frame.frame_image_extent.height == capture_extent.height };
|
||||
if (!same_extent && frame.frame_image_buffer.buffer) {
|
||||
destroy_buffer(frame.frame_image_buffer);
|
||||
frame.frame_image_buffer = AllocatedBuffer {};
|
||||
}
|
||||
|
||||
if (!same_extent) {
|
||||
frame.frame_image_buffer = create_buffer(byte_count,
|
||||
vk::BufferUsageFlagBits::eTransferDst,
|
||||
VMA_MEMORY_USAGE_GPU_TO_CPU);
|
||||
frame.frame_image_extent = capture_extent;
|
||||
frame.frame_image_rgba.resize(byte_count);
|
||||
}
|
||||
|
||||
frame.frame_image_ready = false;
|
||||
frame.tracy_frame_ready = false;
|
||||
}
|
||||
}
|
||||
|
||||
auto VulkanRenderer::destroy_tracy_frame_buffers() -> void
|
||||
{
|
||||
for (auto &frame : m_vk.frames) {
|
||||
if (frame.frame_image_buffer.buffer) {
|
||||
destroy_buffer(frame.frame_image_buffer);
|
||||
frame.frame_image_buffer = AllocatedBuffer {};
|
||||
}
|
||||
frame.frame_image_extent = vk::Extent2D {};
|
||||
frame.frame_image_rgba.clear();
|
||||
frame.frame_image_ready = false;
|
||||
frame.tracy_frame_ready = false;
|
||||
}
|
||||
|
||||
if (m_vk.tracy_capture_image.image) {
|
||||
destroy_image(m_vk.tracy_capture_image);
|
||||
m_vk.tracy_capture_image = AllocatedImage {};
|
||||
}
|
||||
m_vk.tracy_capture_image_layout = vk::ImageLayout::eUndefined;
|
||||
m_vk.tracy_capture_extent = vk::Extent2D {};
|
||||
}
|
||||
|
||||
auto VulkanRenderer::emit_tracy_frame_image(FrameData &frame) -> void
|
||||
{
|
||||
if (!frame.frame_image_ready) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto const extent { frame.frame_image_extent };
|
||||
if (extent.width == 0 || extent.height == 0) {
|
||||
return;
|
||||
}
|
||||
if (extent.width % 4 != 0 || extent.height % 4 != 0) {
|
||||
return;
|
||||
}
|
||||
if (extent.width > std::numeric_limits<std::uint16_t>::max()
|
||||
|| extent.height > std::numeric_limits<std::uint16_t>::max()) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto const byte_count { static_cast<size_t>(extent.width)
|
||||
* static_cast<size_t>(extent.height) * 4 };
|
||||
if (!frame.frame_image_buffer.buffer) {
|
||||
return;
|
||||
}
|
||||
|
||||
VmaAllocationInfo info {};
|
||||
vmaGetAllocationInfo(
|
||||
m_vk.allocator, frame.frame_image_buffer.allocation, &info);
|
||||
|
||||
void *mapped { info.pMappedData };
|
||||
bool mapped_here { false };
|
||||
if (!mapped) {
|
||||
auto const map_result { vmaMapMemory(
|
||||
m_vk.allocator, frame.frame_image_buffer.allocation, &mapped) };
|
||||
if (map_result != VK_SUCCESS) {
|
||||
return;
|
||||
}
|
||||
mapped_here = true;
|
||||
}
|
||||
|
||||
auto *source { static_cast<std::uint8_t *>(mapped) };
|
||||
auto &destination { frame.frame_image_rgba };
|
||||
if (destination.size() != byte_count) {
|
||||
destination.resize(byte_count);
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < byte_count; i += 4) {
|
||||
destination[i] = source[i + 2];
|
||||
destination[i + 1] = source[i + 1];
|
||||
destination[i + 2] = source[i];
|
||||
destination[i + 3] = source[i + 3];
|
||||
}
|
||||
|
||||
if (!frame.tracy_frame_ready || !TracyIsConnected) {
|
||||
frame.frame_image_ready = false;
|
||||
frame.tracy_frame_ready = false;
|
||||
if (mapped_here) {
|
||||
vmaUnmapMemory(m_vk.allocator, frame.frame_image_buffer.allocation);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
auto const frame_offset { static_cast<std::uint8_t>(
|
||||
m_vk.frames.size() - 1) };
|
||||
FrameImage(destination.data(), static_cast<std::uint16_t>(extent.width),
|
||||
static_cast<std::uint16_t>(extent.height), frame_offset, false);
|
||||
|
||||
if (mapped_here) {
|
||||
vmaUnmapMemory(m_vk.allocator, frame.frame_image_buffer.allocation);
|
||||
}
|
||||
|
||||
frame.frame_image_ready = false;
|
||||
frame.tracy_frame_ready = false;
|
||||
}
|
||||
#endif
|
||||
|
||||
auto VulkanRenderer::create_image_no_view(vk::Extent3D size, vk::Format format,
|
||||
vk::ImageUsageFlags flags, vk::SampleCountFlagBits samples, bool mipmapped)
|
||||
-> AllocatedImage
|
||||
{
|
||||
AllocatedImage new_image;
|
||||
AllocatedImage new_image {};
|
||||
new_image.format = format;
|
||||
new_image.extent = size;
|
||||
|
||||
@@ -1688,6 +2078,16 @@ auto VulkanRenderer::create_image(vk::Extent3D size, vk::Format format,
|
||||
reinterpret_cast<VkImage *>(&new_image.image),
|
||||
&new_image.allocation, nullptr));
|
||||
|
||||
return new_image;
|
||||
}
|
||||
|
||||
auto VulkanRenderer::create_image(vk::Extent3D size, vk::Format format,
|
||||
vk::ImageUsageFlags flags, vk::SampleCountFlagBits samples, bool mipmapped)
|
||||
-> AllocatedImage
|
||||
{
|
||||
AllocatedImage new_image { create_image_no_view(
|
||||
size, format, flags, samples, mipmapped) };
|
||||
|
||||
vk::ImageAspectFlags aspect_flag { vk::ImageAspectFlagBits::eColor };
|
||||
if (format == vk::Format::eD32Sfloat) {
|
||||
aspect_flag = vk::ImageAspectFlagBits::eDepth;
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <cstdint>
|
||||
#include <functional>
|
||||
#include <mutex>
|
||||
#include <optional>
|
||||
#include <span>
|
||||
#include <variant>
|
||||
#include <vector>
|
||||
|
||||
@@ -30,14 +32,13 @@ struct GPUDrawPushConstants {
|
||||
constexpr unsigned FRAME_OVERLAP = 2;
|
||||
|
||||
struct VulkanRenderer {
|
||||
enum class AntiAliasingKind {
|
||||
NONE,
|
||||
MSAA_2X,
|
||||
MSAA_4X,
|
||||
MSAA_8X,
|
||||
struct ScreenshotPixels {
|
||||
std::span<std::uint8_t const> pixels;
|
||||
vk::Extent2D extent;
|
||||
};
|
||||
|
||||
struct GL {
|
||||
|
||||
enum class GeometryKind {
|
||||
Triangles,
|
||||
TriangleStrip,
|
||||
@@ -116,6 +117,13 @@ struct VulkanRenderer {
|
||||
std::vector<uint32_t> m_indices;
|
||||
};
|
||||
|
||||
enum class AntiAliasingKind {
|
||||
NONE,
|
||||
MSAA_2X,
|
||||
MSAA_4X,
|
||||
MSAA_8X,
|
||||
};
|
||||
|
||||
VulkanRenderer(SDL_Window *window, Logger &logger);
|
||||
~VulkanRenderer();
|
||||
|
||||
@@ -160,11 +168,33 @@ struct VulkanRenderer {
|
||||
auto mesh_pipeline() -> Pipeline & { return m_vk.mesh_pipeline; }
|
||||
auto triangle_pipeline() -> Pipeline & { return m_vk.triangle_pipeline; }
|
||||
auto gl_api() -> GL & { return gl; }
|
||||
auto get_screenshot() const -> std::optional<AllocatedImage>
|
||||
{
|
||||
return m_latest_screenshot;
|
||||
}
|
||||
auto get_screenshot_pixels() const
|
||||
-> std::optional<VulkanRenderer::ScreenshotPixels>
|
||||
{
|
||||
if (m_latest_screenshot_pixels.empty()
|
||||
|| m_latest_screenshot_extent.width == 0
|
||||
|| m_latest_screenshot_extent.height == 0) {
|
||||
return {};
|
||||
}
|
||||
|
||||
auto const span { std::span<std::uint8_t const> {
|
||||
m_latest_screenshot_pixels.data(),
|
||||
m_latest_screenshot_pixels.size() } };
|
||||
return ScreenshotPixels { span, m_latest_screenshot_extent };
|
||||
}
|
||||
|
||||
auto logger() const -> Logger & { return m_logger; }
|
||||
|
||||
GL gl;
|
||||
|
||||
std::optional<AllocatedImage> m_latest_screenshot {};
|
||||
std::vector<std::uint8_t> m_latest_screenshot_pixels {};
|
||||
vk::Extent2D m_latest_screenshot_extent {};
|
||||
|
||||
private:
|
||||
struct RenderCommand {
|
||||
struct SetAntiAliasing {
|
||||
@@ -197,10 +227,22 @@ private:
|
||||
auto destroy_msaa_color_image() -> void;
|
||||
auto recreate_swapchain(uint32_t width, uint32_t height) -> void;
|
||||
auto destroy_swapchain() -> void;
|
||||
auto ensure_screenshot_buffers(vk::Extent2D extent) -> void;
|
||||
auto destroy_screenshot_buffers() -> void;
|
||||
auto emit_frame_screenshot(FrameData &frame) -> void;
|
||||
#if defined(TRACY_ENABLE)
|
||||
auto ensure_tracy_frame_buffers(vk::Extent2D extent) -> void;
|
||||
auto destroy_tracy_frame_buffers() -> void;
|
||||
auto emit_tracy_frame_image(FrameData &frame) -> void;
|
||||
#endif
|
||||
auto create_image(vk::Extent3D size, vk::Format format,
|
||||
vk::ImageUsageFlags flags,
|
||||
vk::SampleCountFlagBits samples = vk::SampleCountFlagBits::e1,
|
||||
bool mipmapped = false) -> AllocatedImage;
|
||||
auto create_image_no_view(vk::Extent3D size, vk::Format format,
|
||||
vk::ImageUsageFlags flags,
|
||||
vk::SampleCountFlagBits samples = vk::SampleCountFlagBits::e1,
|
||||
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;
|
||||
@@ -248,6 +290,13 @@ private:
|
||||
vk::ImageLayout msaa_color_image_layout { vk::ImageLayout::eUndefined };
|
||||
AllocatedImage depth_image {};
|
||||
vk::ImageLayout depth_image_layout { vk::ImageLayout::eUndefined };
|
||||
#if defined(TRACY_ENABLE)
|
||||
AllocatedImage tracy_capture_image {};
|
||||
vk::ImageLayout tracy_capture_image_layout {
|
||||
vk::ImageLayout::eUndefined
|
||||
};
|
||||
vk::Extent2D tracy_capture_extent {};
|
||||
#endif
|
||||
vk::Extent2D draw_extent {};
|
||||
AntiAliasingKind antialiasing_kind { AntiAliasingKind::NONE };
|
||||
vk::SampleCountFlagBits msaa_samples { vk::SampleCountFlagBits::e1 };
|
||||
|
||||
Reference in New Issue
Block a user