refactor(surface&renderer): resizing issues
Some checks reported errors
continuous-integration/drone/push Build was killed

This commit is contained in:
light7734 2025-10-03 13:26:07 +03:30
parent b05762c95b
commit 9ca94dc7d7
Signed by: light7734
GPG key ID: 8C30176798F1A6BA
8 changed files with 176 additions and 17 deletions

View file

@ -74,6 +74,7 @@ System::System(CreateInfo info)
: m_registry(std::move(info.registry))
, m_stats(info.system_stats)
, m_context(create_scope<vk::Context>(info.surface_entity))
, m_surface_entity(info.surface_entity)
{
m_validation_observer = new vk::ValidationObserver();
// ensure(m_stats, "Failed to initialize system: null stats");
@ -102,12 +103,22 @@ void System::on_unregister()
void System::tick(app::TickInfo tick)
{
if (!m_renderer->draw(m_frame_idx))
for (const auto &event : m_surface_entity.get<surface::SurfaceComponent>().peek_events())
{
if (std::holds_alternative<surface::ResizedEvent>(event))
{
m_context->recreate_swapchain();
m_renderer->replace_swapchain(m_context->swapchain());
m_pass->replace_swapchain(m_context->swapchain());
}
}
if (m_renderer->draw(m_frame_idx))
{
m_context->recreate_swapchain();
m_renderer->replace_swapchain(m_context->swapchain());
m_pass->replace_swapchain(m_context->swapchain());
m_renderer->draw(m_frame_idx);
m_renderer->draw(m_frame_idx); // don't drop the frame
}
m_frame_idx = (m_frame_idx + 1) % vk::Renderer::max_frames_in_flight;

View file

@ -7,9 +7,7 @@
namespace lt::renderer::vk {
Swapchain::Swapchain(const Device &device, const Surface &surface)
: m_device(device.vk())
, m_resolution(surface.get_framebuffer_size())
Swapchain::Swapchain(const Device &device, const Surface &surface): m_device(device.vk())
{
static auto idx = 0u;
auto *physical_device = device.physical();
@ -41,7 +39,7 @@ Swapchain::Swapchain(const Device &device, const Surface &surface)
.minImageCount = get_optimal_image_count(capabilities, desired_swapchain_image_count),
.imageFormat = surface_format.format,
.imageColorSpace = surface_format.colorSpace,
.imageExtent = surface.get_framebuffer_size(),
.imageExtent = capabilities.currentExtent,
.imageArrayLayers = 1u,
.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT,
.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE,
@ -53,6 +51,7 @@ Swapchain::Swapchain(const Device &device, const Surface &surface)
.clipped = VK_TRUE,
.oldSwapchain = nullptr,
};
m_resolution = capabilities.currentExtent;
vkc(vk_create_swapchain_khr(device.vk(), &create_info, nullptr, &m_swapchain));
vkc(vk_device_wait_idle(device.vk()));

View file

@ -255,7 +255,6 @@ public:
private:
auto create_module(lt::assets::Blob blob) -> VkShaderModule
{
log_dbg("BLOB SIZE: {}", blob.size());
auto info = VkShaderModuleCreateInfo {
.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO,
.codeSize = blob.size(),

View file

@ -85,7 +85,6 @@ public:
}
}
m_submit_semaphores.resize(context.swapchain().get_image_count());
for (auto idx = 0; auto &semaphore : m_submit_semaphores)
{
@ -152,7 +151,7 @@ public:
auto result = vk_acquire_next_image_khr(
m_device,
m_swapchain,
UINT64_MAX,
1000000ul,
aquire_semaphore,
VK_NULL_HANDLE,
&image_idx
@ -183,6 +182,7 @@ public:
vkc(vk_queue_submit(m_graphics_queue, 1u, &submit_info, flight_fence));
VkResult res;
auto present_info = VkPresentInfoKHR {
.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR,
.waitSemaphoreCount = 1u,
@ -190,7 +190,7 @@ public:
.swapchainCount = 1u,
.pSwapchains = &m_swapchain,
.pImageIndices = &image_idx,
.pResults = nullptr,
.pResults = &res,
};
vk_queue_present_khr(m_present_queue, &present_info);
@ -210,6 +210,68 @@ public:
m_swapchain = swapchain.vk();
m_resolution = swapchain.get_resolution();
ensure(m_swapchain, "Failed to replace renderer's swapchain: null swapchain");
for (auto [semaphore, fence] :
std::views::zip(m_aquire_image_semaphores, m_in_flight_fences))
{
vk_destroy_semaphore(m_device, semaphore, nullptr);
vk_destroy_fence(m_device, fence, nullptr);
}
for (auto &semaphore : m_submit_semaphores)
{
vk_destroy_semaphore(m_device, semaphore, nullptr);
}
auto semaphore_info = VkSemaphoreCreateInfo {
.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO,
};
auto fence_info = VkFenceCreateInfo {
.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO,
.flags = VK_FENCE_CREATE_SIGNALED_BIT,
};
for (auto idx : std::views::iota(0u, max_frames_in_flight))
{
vkc(vk_create_semaphore(
m_device,
&semaphore_info,
nullptr,
&m_aquire_image_semaphores[idx]
));
vkc(vk_create_fence(m_device, &fence_info, nullptr, &m_in_flight_fences[idx]));
set_object_name(
m_device,
m_aquire_image_semaphores[idx].get(),
"aquire semaphore {}",
idx
);
set_object_name(m_device, m_in_flight_fences[idx].get(), "frame fence {}", idx);
{
const auto name = std::format("frame fence {}", idx);
auto debug_info = VkDebugUtilsObjectNameInfoEXT {
.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_OBJECT_NAME_INFO_EXT,
.objectType = VK_OBJECT_TYPE_FENCE,
.objectHandle = reinterpret_cast<uint64_t>(
static_cast<VkFence_T *>(m_in_flight_fences[idx].get())
),
.pObjectName = name.c_str(),
};
vk_set_debug_object_name(m_device, &debug_info);
}
}
m_submit_semaphores.resize(swapchain.get_image_count());
for (auto idx = 0; auto &semaphore : m_submit_semaphores)
{
vkc(vk_create_semaphore(m_device, &semaphore_info, nullptr, &semaphore));
set_object_name(m_device, semaphore.get(), "submit semaphore {}", idx++);
}
}
private:

View file

@ -59,6 +59,8 @@ private:
Ref<ecs::Registry> m_registry;
ecs::Entity m_surface_entity;
Ref<app::SystemStats> m_stats;
Scope<class vk::Context> m_context;
@ -67,7 +69,6 @@ private:
Scope<class vk::Renderer> m_renderer;
app::TickResult m_last_tick_result {};
uint32_t m_frame_idx {};

View file

@ -15,6 +15,7 @@ target_link_libraries(surface PUBLIC
PRIVATE
logger
lt_debug
time
)
add_test_module(surface system.test.cpp)

View file

@ -2,6 +2,7 @@
#include <surface/events/mouse.hpp>
#include <surface/requests/surface.hpp>
#include <surface/system.hpp>
#include <time/timer.hpp>
//
#include <X11/Xlib.h>
@ -11,6 +12,15 @@
namespace lt::surface {
template<int EventType>
int XEventTypeEquals(Display *, XEvent *event, XPointer winptr)
{
return (
event->type == EventType
&& *(reinterpret_cast<Window *>(winptr)) == reinterpret_cast<XAnyEvent *>(event)->window
);
}
template<class... Ts>
struct overloads: Ts...
{
@ -274,6 +284,7 @@ void System::handle_events(SurfaceComponent &surface)
const auto new_height = event.xconfigure.height;
if (prev_width != new_width || prev_height != new_height)
{
log_dbg("resized: {} - {}", new_width, new_height);
surface.m_resolution.x = new_width;
surface.m_resolution.y = new_height;
queue.emplace_back<ResizedEvent>(ResizedEvent {
@ -332,18 +343,95 @@ void System::modify_resolution(SurfaceComponent &surface, const ModifyResolution
{
surface.m_resolution = request.resolution;
const auto &[display, window, _] = surface.get_native_data();
auto &[display, window, _] = surface.m_native_data;
const auto &[width, height] = request.resolution;
XResizeWindow(display, window, width, height);
// XResizeWindow(display, window, width, height);
// get baseline serial number for X requests generated from XResizeWindow
uint64_t serial = NextRequest(display);
// request a new window size from the X server
XResizeWindow(display, window, static_cast<uint32_t>(width), static_cast<uint32_t>(height));
// flush output queue and wait for X server to processes the request
XSync(display, False);
// The documentation for XResizeWindow includes this important note:
//
// If the override-redirect flag of the window is False and some
// other client has selected SubstructureRedirectMask on the parent,
// the X server generates a ConfigureRequest event, and no further
// processing is performed.
//
// What this means, essentially, is that if this window is a top-level
// window, then it's the window manager (the "other client") that is
// responsible for changing this window's size. So when we call
// XResizeWindow() on a top-level window, then instead of resizing
// the window immediately, the X server informs the window manager,
// and then the window manager sets our new size (usually it will be
// the size we asked for). We receive a ConfigureNotify event when
// our new size has been set.
constexpr auto lifespan = std::chrono::milliseconds { 10 };
auto timer = Timer {};
auto event = XEvent {};
while (!XCheckIfEvent(
display,
&event,
XEventTypeEquals<ConfigureNotify>,
reinterpret_cast<XPointer>(&window) // NOLINT
)
|| event.xconfigure.serial < serial)
{
std::this_thread::sleep_for(std::chrono::microseconds { 100 });
if (timer.elapsed_time() > lifespan)
{
log_err("Timed out waiting for XResizeWindow's event");
return;
}
}
// We don't need to update the component's state and handle the event in this funcion.
// Since handle_requests is called before handle_events.
// So we just put the event back into the queue and move on.
XPutBackEvent(display, &event);
XSync(display, False);
XFlush(display);
}
void System::modify_position(SurfaceComponent &surface, const ModifyPositionRequest &request)
{
surface.m_position = request.position;
const auto &[display, window, _] = surface.get_native_data();
auto &[display, window, _] = surface.m_native_data;
const auto &[x, y] = request.position;
// get baseline serial number for X requests generated from XResizeWindow
uint64_t serial = NextRequest(display);
XMoveWindow(display, window, static_cast<int>(x), static_cast<int>(y));
// flush output queue and wait for X server to processes the request
XSync(display, False);
constexpr auto lifespan = std::chrono::milliseconds { 10 };
auto timer = Timer {};
auto event = XEvent {};
while (!XCheckIfEvent(
display,
&event,
XEventTypeEquals<ConfigureNotify>,
reinterpret_cast<XPointer>(&window) // NOLINT
)
|| event.xconfigure.serial < serial)
{
std::this_thread::sleep_for(std::chrono::microseconds { 100 });
if (timer.elapsed_time() > lifespan)
{
log_err("Timed out waiting for XMoveWindow's event");
return;
}
}
// We don't need to update the component's state and handle the event in this funcion.
// Since handle_requests is called before handle_events.
// So we just put the event back into the queue and move on.
XPutBackEvent(display, &event);
XSync(display, False);
}
void System::modify_visiblity(SurfaceComponent &surface, const ModifyVisibilityRequest &request)
@ -363,9 +451,8 @@ void System::modify_visiblity(SurfaceComponent &surface, const ModifyVisibilityR
void System::tick(app::TickInfo tick)
{
for (auto &dense : m_registry->view<SurfaceComponent>())
for (auto &[id, surface] : m_registry->view<SurfaceComponent>())
{
auto &surface = dense.second;
handle_requests(surface);
handle_events(surface);
}

View file

@ -45,7 +45,6 @@ function (add_library_module libname)
target_link_libraries(${libname} PUBLIC base)
endif ()
endif ()
endfunction ()
function (add_executable_module exename)