refactor(surface&renderer): resizing issues
Some checks reported errors
continuous-integration/drone/push Build was killed
Some checks reported errors
continuous-integration/drone/push Build was killed
This commit is contained in:
parent
b05762c95b
commit
9ca94dc7d7
8 changed files with 176 additions and 17 deletions
|
@ -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;
|
||||
|
|
|
@ -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()));
|
||||
|
|
|
@ -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(),
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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 {};
|
||||
|
|
|
@ -15,6 +15,7 @@ target_link_libraries(surface PUBLIC
|
|||
PRIVATE
|
||||
logger
|
||||
lt_debug
|
||||
time
|
||||
)
|
||||
|
||||
add_test_module(surface system.test.cpp)
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -45,7 +45,6 @@ function (add_library_module libname)
|
|||
target_link_libraries(${libname} PUBLIC base)
|
||||
endif ()
|
||||
endif ()
|
||||
|
||||
endfunction ()
|
||||
|
||||
function (add_executable_module exename)
|
||||
|
|
Loading…
Add table
Reference in a new issue