72 display_service->present_frame = [
this](
const std::shared_ptr<void>& window_ptr, uint64_t command_buffer_bits) {
73 auto window = std::static_pointer_cast<Window>(window_ptr);
77 "Window '{}' not registered for presentation", window->get_create_info().title);
80 ctx->command_buffer = *
reinterpret_cast<vk::CommandBuffer*
>(&command_buffer_bits);
84 display_service->wait_idle = [
this]() {
88 display_service->resize_surface = [
this](
const std::shared_ptr<void>& window_ptr, uint32_t width, uint32_t height) {
89 auto window = std::static_pointer_cast<Window>(window_ptr);
90 window->set_size(width, height);
92 if (ctx.window == window) {
93 ctx.needs_recreation =
true;
99 display_service->get_swapchain_image_count = [
this](
const std::shared_ptr<void>& window_ptr) -> uint32_t {
100 auto window = std::static_pointer_cast<Window>(window_ptr);
102 if (ctx.window == window) {
103 return static_cast<uint32_t
>(ctx.swapchain->get_image_count());
109 display_service->get_swapchain_format = [
this](
const std::shared_ptr<void>& window_ptr) -> uint32_t {
110 auto window = std::static_pointer_cast<Window>(window_ptr);
112 if (ctx.window == window) {
113 return static_cast<int>(ctx.swapchain->get_image_format());
119 display_service->get_current_framebuffer = [
this](
const std::shared_ptr<void>& window_ptr) ->
void* {
120 auto window = std::static_pointer_cast<Window>(window_ptr);
123 if (!context || context->framebuffers.empty()) {
127 size_t frame_index = context->current_frame % context->framebuffers.size();
128 auto& handle = *context->framebuffers[frame_index];
129 return static_cast<void*
>(&handle);
132 display_service->get_swapchain_extent = [
this](
133 const std::shared_ptr<void>& window_ptr,
135 uint32_t& out_height) {
136 auto window = std::static_pointer_cast<Window>(window_ptr);
139 if (context && context->swapchain) {
140 auto extent = context->swapchain->get_extent();
141 out_width = extent.width;
142 out_height = extent.height;
149 display_service->get_window_render_pass = [
this](
const std::shared_ptr<void>& window_ptr) ->
void* {
150 auto window = std::static_pointer_cast<Window>(window_ptr);
153 if (!context || !context->render_pass) {
157 vk::RenderPass rp = context->render_pass->get();
158 return static_cast<void*
>(rp);
161 display_service->attach_render_pass = [
this](
162 const std::shared_ptr<void>& window_ptr,
163 const std::shared_ptr<void>& render_pass_handle) ->
bool {
164 auto window = std::static_pointer_cast<Window>(window_ptr);
165 auto render_pass = std::static_pointer_cast<Core::VKRenderPass>(render_pass_handle);
193 "Failed to create Vulkan surface for window '{}'",
194 window->get_create_info().title);
200 "No presentation support for window '{}'", window->get_create_info().title);
208 config.
swapchain = std::make_unique<VKSwapchain>();
212 "Failed to create swapchain for window '{}'", window->get_create_info().title);
218 "Failed to create sync objects for window '{}'",
219 window->get_create_info().title);
226 window->set_graphics_registered(
true);
228 window->set_event_callback([
this, window_ptr = window](
const WindowEvent& event) {
230 auto* config = find_window_context(window_ptr);
232 config->needs_recreation = true;
238 "Registered window '{}' for graphics processing", window->get_create_info().title);
245 auto device = m_context.get_device();
246 uint32_t image_count = config.
swapchain->get_image_count();
253 vk::SemaphoreCreateInfo semaphore_info {};
254 vk::FenceCreateInfo fence_info {};
255 fence_info.flags = vk::FenceCreateFlagBits::eSignaled;
257 for (uint32_t i = 0; i < image_count; ++i) {
263 i = device.createFence(fence_info);
268 config.
render_pass = std::make_unique<VKRenderPass>();
271 "Failed to create render pass");
276 auto extent = config.
swapchain->get_extent();
277 const auto& image_views = config.
swapchain->get_image_views();
279 for (uint32_t i = 0; i < image_count; ++i) {
280 config.
framebuffers[i] = std::make_unique<VKFramebuffer>();
282 std::vector<vk::ImageView> attachments = { image_views[i] };
291 "Failed to create framebuffer {}", i);
297 }
catch (
const vk::SystemError& e) {
299 "Failed to create sync objects: {}", e.what());
320 const auto& state = context.
window->get_state();
324 framebuffer->cleanup(m_context.get_device());
330 context.
render_pass->cleanup(m_context.get_device());
334 if (!context.
swapchain->recreate(state.current_width, state.current_height)) {
336 "Failed to recreate swapchain for window '{}'",
337 context.
window->get_create_info().title);
342 context.
render_pass = std::make_unique<VKRenderPass>();
344 m_context.get_device(),
345 context.
swapchain->get_image_format())) {
347 "Failed to recreate render pass for window '{}'",
348 context.
window->get_create_info().title);
353 uint32_t image_count = context.
swapchain->get_image_count();
355 auto extent = context.
swapchain->get_extent();
356 const auto& image_views = context.
swapchain->get_image_views();
358 for (uint32_t i = 0; i < image_count; ++i) {
359 context.
framebuffers[i] = std::make_unique<VKFramebuffer>();
360 std::vector<vk::ImageView> attachments = { image_views[i] };
363 m_context.get_device(),
369 "Failed to recreate framebuffer {} for window '{}'",
370 i, context.
window->get_create_info().title);
380 auto device = m_context.get_device();
381 auto graphics_queue = m_context.get_graphics_queue();
384 auto& in_flight = context.
in_flight[frame_index];
386 if (device.waitForFences(1, &in_flight, VK_TRUE, UINT64_MAX) == vk::Result::eTimeout) {
388 "Skipping frame rendering for window '{}' due to in-flight fence timeout",
389 context.
window->get_create_info().title);
392 if (device.resetFences(1, &in_flight) != vk::Result::eSuccess) {
394 "Failed to reset in-flight fence for window '{}'",
395 context.
window->get_create_info().title);
400 if (!image_index_opt.has_value()) {
404 uint32_t image_index = image_index_opt.value();
409 vk::CommandBuffer cmd = m_command_manager.allocate_command_buffer();
410 vk::CommandBufferBeginInfo begin_info {};
411 begin_info.flags = vk::CommandBufferUsageFlagBits::eOneTimeSubmit;
412 cmd.begin(begin_info);
414 vk::RenderPassBeginInfo render_pass_info {};
415 render_pass_info.renderPass = context.
render_pass->get();
416 render_pass_info.framebuffer = context.
framebuffers[image_index]->get();
417 render_pass_info.renderArea.offset = vk::Offset2D { 0, 0 };
418 render_pass_info.renderArea.extent = context.
swapchain->get_extent();
420 vk::ClearValue clear_color {};
421 clear_color.color = vk::ClearColorValue(std::array<float, 4> { 0.0F, 0.1F, 0.2F, 1.0F });
422 render_pass_info.clearValueCount = 1;
423 render_pass_info.pClearValues = &clear_color;
425 cmd.beginRenderPass(render_pass_info, vk::SubpassContents::eInline);
430 vk::SubmitInfo submit_info {};
431 vk::PipelineStageFlags wait_stages[] = { vk::PipelineStageFlagBits::eColorAttachmentOutput };
432 submit_info.waitSemaphoreCount = 1;
433 submit_info.pWaitSemaphores = &image_available;
434 submit_info.pWaitDstStageMask = wait_stages;
435 submit_info.commandBufferCount = 1;
436 submit_info.pCommandBuffers = &cmd;
437 submit_info.signalSemaphoreCount = 1;
438 submit_info.pSignalSemaphores = &render_finished;
441 auto result = graphics_queue.submit(1, &submit_info, in_flight);
442 }
catch (
const vk::SystemError& e) {
444 "Failed to submit draw command buffer: {}", e.what());
445 m_command_manager.free_command_buffer(cmd);
449 bool present_success = context.
swapchain->present(image_index, render_finished, graphics_queue);
450 if (!present_success) {
457void BackendWindowHandler::render_window(
const std::shared_ptr<Window>& window)
459 auto* context = find_window_context(window);
462 "No context found for window '{}'",
463 window->get_create_info().title);
467 auto device = m_context.get_device();
468 auto graphics_queue = m_context.get_graphics_queue();
470 size_t frame_index = context->current_frame;
471 auto& in_flight = context->in_flight[frame_index];
472 auto& image_available = context->image_available[frame_index];
473 auto& render_finished = context->render_finished[frame_index];
475 if (device.waitForFences(1, &in_flight, VK_TRUE, UINT64_MAX) == vk::Result::eTimeout) {
477 "Skipping frame rendering for window '{}' due to in-flight fence timeout",
478 context->window->get_create_info().title);
482 auto image_index_opt = context->swapchain->acquire_next_image(image_available);
483 if (!image_index_opt.has_value()) {
484 context->needs_recreation =
true;
487 uint32_t image_index = image_index_opt.value();
489 if (device.resetFences(1, &in_flight) != vk::Result::eSuccess) {
491 "Failed to reset in-flight fence for window '{}'",
492 context->window->get_create_info().title);
496 vk::SubmitInfo submit_info {};
497 vk::PipelineStageFlags wait_stages[] = { vk::PipelineStageFlagBits::eColorAttachmentOutput };
498 submit_info.waitSemaphoreCount = 1;
499 submit_info.pWaitSemaphores = &image_available;
500 submit_info.pWaitDstStageMask = wait_stages;
501 submit_info.commandBufferCount = 1;
502 submit_info.pCommandBuffers = &(context->command_buffer);
503 submit_info.signalSemaphoreCount = 1;
504 submit_info.pSignalSemaphores = &render_finished;
507 auto result = graphics_queue.submit(1, &submit_info, in_flight);
508 }
catch (
const std::exception& e) {
512 std::source_location::current(),
513 "Unexpected error during command buffer submission: {}",
518 bool present_success = context->swapchain->present(image_index, render_finished, graphics_queue);
519 if (!present_success) {
520 context->needs_recreation =
true;
523 context->current_frame = (frame_index + 1) % context->in_flight.size();
562bool BackendWindowHandler::attach_render_pass(
563 const std::shared_ptr<Window>& window,
564 const std::shared_ptr<Core::VKRenderPass>& render_pass)
566 auto* context = find_window_context(window);
569 "Window not registered");
575 "Cannot attach null render pass");
579 m_context.wait_idle();
581 for (
auto& fb : context->framebuffers) {
583 fb->cleanup(m_context.get_device());
585 context->framebuffers.clear();
587 if (context->render_pass) {
588 context->render_pass->cleanup(m_context.get_device());
590 context->render_pass = render_pass;
591 context->user_render_pass_attached =
true;
593 uint32_t image_count = context->swapchain->get_image_count();
594 context->framebuffers.resize(image_count);
595 auto extent = context->swapchain->get_extent();
596 const auto& image_views = context->swapchain->get_image_views();
598 for (uint32_t i = 0; i < image_count; ++i) {
599 context->framebuffers[i] = std::make_unique<VKFramebuffer>();
600 std::vector<vk::ImageView> attachments = { image_views[i] };
602 if (!context->framebuffers[i]->create(
603 m_context.get_device(),
609 "Failed to create framebuffer {} with user render pass", i);
615 "Attached user render pass to window '{}' - recreated {} framebuffers",
616 window->get_create_info().title, image_count);
bool update_present_family(vk::SurfaceKHR surface)
Update presentation support for a surface.
vk::Device get_device() const
Get logical device.
vk::SurfaceKHR create_surface(std::shared_ptr< Window > window)
Create surface from window's native handles.
void destroy_surface(vk::SurfaceKHR surface)
Destroy a specific surface Called when window is unregistered.
High-level wrapper for Vulkan instance and device.
std::vector< vk::Semaphore > image_available
std::shared_ptr< Window > window
std::vector< vk::Fence > in_flight
std::unique_ptr< VKSwapchain > swapchain
std::vector< std::unique_ptr< VKFramebuffer > > framebuffers
void cleanup(VKContext &context)
std::vector< vk::Semaphore > render_finished
std::shared_ptr< VKRenderPass > render_pass