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_registry(std::move(info.registry))
|
||||||
, m_stats(info.system_stats)
|
, m_stats(info.system_stats)
|
||||||
, m_context(create_scope<vk::Context>(info.surface_entity))
|
, m_context(create_scope<vk::Context>(info.surface_entity))
|
||||||
|
, m_surface_entity(info.surface_entity)
|
||||||
{
|
{
|
||||||
m_validation_observer = new vk::ValidationObserver();
|
m_validation_observer = new vk::ValidationObserver();
|
||||||
// ensure(m_stats, "Failed to initialize system: null stats");
|
// ensure(m_stats, "Failed to initialize system: null stats");
|
||||||
|
@ -102,12 +103,22 @@ void System::on_unregister()
|
||||||
|
|
||||||
void System::tick(app::TickInfo tick)
|
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_context->recreate_swapchain();
|
||||||
m_renderer->replace_swapchain(m_context->swapchain());
|
m_renderer->replace_swapchain(m_context->swapchain());
|
||||||
m_pass->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;
|
m_frame_idx = (m_frame_idx + 1) % vk::Renderer::max_frames_in_flight;
|
||||||
|
|
|
@ -7,9 +7,7 @@
|
||||||
|
|
||||||
namespace lt::renderer::vk {
|
namespace lt::renderer::vk {
|
||||||
|
|
||||||
Swapchain::Swapchain(const Device &device, const Surface &surface)
|
Swapchain::Swapchain(const Device &device, const Surface &surface): m_device(device.vk())
|
||||||
: m_device(device.vk())
|
|
||||||
, m_resolution(surface.get_framebuffer_size())
|
|
||||||
{
|
{
|
||||||
static auto idx = 0u;
|
static auto idx = 0u;
|
||||||
auto *physical_device = device.physical();
|
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),
|
.minImageCount = get_optimal_image_count(capabilities, desired_swapchain_image_count),
|
||||||
.imageFormat = surface_format.format,
|
.imageFormat = surface_format.format,
|
||||||
.imageColorSpace = surface_format.colorSpace,
|
.imageColorSpace = surface_format.colorSpace,
|
||||||
.imageExtent = surface.get_framebuffer_size(),
|
.imageExtent = capabilities.currentExtent,
|
||||||
.imageArrayLayers = 1u,
|
.imageArrayLayers = 1u,
|
||||||
.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT,
|
.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT,
|
||||||
.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE,
|
.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE,
|
||||||
|
@ -53,6 +51,7 @@ Swapchain::Swapchain(const Device &device, const Surface &surface)
|
||||||
.clipped = VK_TRUE,
|
.clipped = VK_TRUE,
|
||||||
.oldSwapchain = nullptr,
|
.oldSwapchain = nullptr,
|
||||||
};
|
};
|
||||||
|
m_resolution = capabilities.currentExtent;
|
||||||
|
|
||||||
vkc(vk_create_swapchain_khr(device.vk(), &create_info, nullptr, &m_swapchain));
|
vkc(vk_create_swapchain_khr(device.vk(), &create_info, nullptr, &m_swapchain));
|
||||||
vkc(vk_device_wait_idle(device.vk()));
|
vkc(vk_device_wait_idle(device.vk()));
|
||||||
|
|
|
@ -255,7 +255,6 @@ public:
|
||||||
private:
|
private:
|
||||||
auto create_module(lt::assets::Blob blob) -> VkShaderModule
|
auto create_module(lt::assets::Blob blob) -> VkShaderModule
|
||||||
{
|
{
|
||||||
log_dbg("BLOB SIZE: {}", blob.size());
|
|
||||||
auto info = VkShaderModuleCreateInfo {
|
auto info = VkShaderModuleCreateInfo {
|
||||||
.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO,
|
.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO,
|
||||||
.codeSize = blob.size(),
|
.codeSize = blob.size(),
|
||||||
|
|
|
@ -85,7 +85,6 @@ public:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
m_submit_semaphores.resize(context.swapchain().get_image_count());
|
m_submit_semaphores.resize(context.swapchain().get_image_count());
|
||||||
for (auto idx = 0; auto &semaphore : m_submit_semaphores)
|
for (auto idx = 0; auto &semaphore : m_submit_semaphores)
|
||||||
{
|
{
|
||||||
|
@ -152,7 +151,7 @@ public:
|
||||||
auto result = vk_acquire_next_image_khr(
|
auto result = vk_acquire_next_image_khr(
|
||||||
m_device,
|
m_device,
|
||||||
m_swapchain,
|
m_swapchain,
|
||||||
UINT64_MAX,
|
1000000ul,
|
||||||
aquire_semaphore,
|
aquire_semaphore,
|
||||||
VK_NULL_HANDLE,
|
VK_NULL_HANDLE,
|
||||||
&image_idx
|
&image_idx
|
||||||
|
@ -183,6 +182,7 @@ public:
|
||||||
|
|
||||||
vkc(vk_queue_submit(m_graphics_queue, 1u, &submit_info, flight_fence));
|
vkc(vk_queue_submit(m_graphics_queue, 1u, &submit_info, flight_fence));
|
||||||
|
|
||||||
|
VkResult res;
|
||||||
auto present_info = VkPresentInfoKHR {
|
auto present_info = VkPresentInfoKHR {
|
||||||
.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR,
|
.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR,
|
||||||
.waitSemaphoreCount = 1u,
|
.waitSemaphoreCount = 1u,
|
||||||
|
@ -190,7 +190,7 @@ public:
|
||||||
.swapchainCount = 1u,
|
.swapchainCount = 1u,
|
||||||
.pSwapchains = &m_swapchain,
|
.pSwapchains = &m_swapchain,
|
||||||
.pImageIndices = &image_idx,
|
.pImageIndices = &image_idx,
|
||||||
.pResults = nullptr,
|
.pResults = &res,
|
||||||
};
|
};
|
||||||
|
|
||||||
vk_queue_present_khr(m_present_queue, &present_info);
|
vk_queue_present_khr(m_present_queue, &present_info);
|
||||||
|
@ -210,6 +210,68 @@ public:
|
||||||
m_swapchain = swapchain.vk();
|
m_swapchain = swapchain.vk();
|
||||||
m_resolution = swapchain.get_resolution();
|
m_resolution = swapchain.get_resolution();
|
||||||
ensure(m_swapchain, "Failed to replace renderer's swapchain: null swapchain");
|
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:
|
private:
|
||||||
|
|
|
@ -59,6 +59,8 @@ private:
|
||||||
|
|
||||||
Ref<ecs::Registry> m_registry;
|
Ref<ecs::Registry> m_registry;
|
||||||
|
|
||||||
|
ecs::Entity m_surface_entity;
|
||||||
|
|
||||||
Ref<app::SystemStats> m_stats;
|
Ref<app::SystemStats> m_stats;
|
||||||
|
|
||||||
Scope<class vk::Context> m_context;
|
Scope<class vk::Context> m_context;
|
||||||
|
@ -67,7 +69,6 @@ private:
|
||||||
|
|
||||||
Scope<class vk::Renderer> m_renderer;
|
Scope<class vk::Renderer> m_renderer;
|
||||||
|
|
||||||
|
|
||||||
app::TickResult m_last_tick_result {};
|
app::TickResult m_last_tick_result {};
|
||||||
|
|
||||||
uint32_t m_frame_idx {};
|
uint32_t m_frame_idx {};
|
||||||
|
|
|
@ -15,6 +15,7 @@ target_link_libraries(surface PUBLIC
|
||||||
PRIVATE
|
PRIVATE
|
||||||
logger
|
logger
|
||||||
lt_debug
|
lt_debug
|
||||||
|
time
|
||||||
)
|
)
|
||||||
|
|
||||||
add_test_module(surface system.test.cpp)
|
add_test_module(surface system.test.cpp)
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
#include <surface/events/mouse.hpp>
|
#include <surface/events/mouse.hpp>
|
||||||
#include <surface/requests/surface.hpp>
|
#include <surface/requests/surface.hpp>
|
||||||
#include <surface/system.hpp>
|
#include <surface/system.hpp>
|
||||||
|
#include <time/timer.hpp>
|
||||||
|
|
||||||
//
|
//
|
||||||
#include <X11/Xlib.h>
|
#include <X11/Xlib.h>
|
||||||
|
@ -11,6 +12,15 @@
|
||||||
|
|
||||||
namespace lt::surface {
|
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>
|
template<class... Ts>
|
||||||
struct overloads: Ts...
|
struct overloads: Ts...
|
||||||
{
|
{
|
||||||
|
@ -274,6 +284,7 @@ void System::handle_events(SurfaceComponent &surface)
|
||||||
const auto new_height = event.xconfigure.height;
|
const auto new_height = event.xconfigure.height;
|
||||||
if (prev_width != new_width || prev_height != new_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.x = new_width;
|
||||||
surface.m_resolution.y = new_height;
|
surface.m_resolution.y = new_height;
|
||||||
queue.emplace_back<ResizedEvent>(ResizedEvent {
|
queue.emplace_back<ResizedEvent>(ResizedEvent {
|
||||||
|
@ -332,18 +343,95 @@ void System::modify_resolution(SurfaceComponent &surface, const ModifyResolution
|
||||||
{
|
{
|
||||||
surface.m_resolution = request.resolution;
|
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;
|
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)
|
void System::modify_position(SurfaceComponent &surface, const ModifyPositionRequest &request)
|
||||||
{
|
{
|
||||||
surface.m_position = request.position;
|
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;
|
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));
|
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)
|
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)
|
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_requests(surface);
|
||||||
handle_events(surface);
|
handle_events(surface);
|
||||||
}
|
}
|
||||||
|
|
|
@ -45,7 +45,6 @@ function (add_library_module libname)
|
||||||
target_link_libraries(${libname} PUBLIC base)
|
target_link_libraries(${libname} PUBLIC base)
|
||||||
endif ()
|
endif ()
|
||||||
endif ()
|
endif ()
|
||||||
|
|
||||||
endfunction ()
|
endfunction ()
|
||||||
|
|
||||||
function (add_executable_module exename)
|
function (add_executable_module exename)
|
||||||
|
|
Loading…
Add table
Reference in a new issue