78 display_service->submit_and_present = [
this](
79 const std::shared_ptr<void>& window_ptr,
80 uint64_t primary_cmd_bits) {
81 auto window = std::static_pointer_cast<Window>(window_ptr);
82 vk::CommandBuffer primary_cmd = *
reinterpret_cast<vk::CommandBuffer*
>(&primary_cmd_bits);
87 display_service->wait_idle = [
this]() {
91 display_service->resize_surface = [
this](
const std::shared_ptr<void>& window_ptr, uint32_t width, uint32_t height) {
92 auto window = std::static_pointer_cast<Window>(window_ptr);
93 window->set_size(width, height);
95 if (ctx.window == window) {
96 ctx.needs_recreation =
true;
102 display_service->get_swapchain_image_count = [
this](
const std::shared_ptr<void>& window_ptr) -> uint32_t {
103 auto window = std::static_pointer_cast<Window>(window_ptr);
105 if (ctx.window == window) {
106 return static_cast<uint32_t
>(ctx.swapchain->get_image_count());
112 display_service->get_swapchain_format = [
this](
const std::shared_ptr<void>& window_ptr) -> uint32_t {
113 auto window = std::static_pointer_cast<Window>(window_ptr);
115 if (ctx.window == window) {
116 return static_cast<int>(ctx.swapchain->get_image_format());
122 display_service->get_swapchain_extent = [
this](
123 const std::shared_ptr<void>& window_ptr,
125 uint32_t& out_height) {
126 auto window = std::static_pointer_cast<Window>(window_ptr);
129 if (context && context->swapchain) {
130 auto extent = context->swapchain->get_extent();
131 out_width = extent.width;
132 out_height = extent.height;
139 display_service->acquire_next_swapchain_image = [
this](
const std::shared_ptr<void>& window_ptr) -> uint64_t {
140 auto window = std::static_pointer_cast<Window>(window_ptr);
144 "Window '{}' not registered for swapchain acquisition",
145 window->get_create_info().title);
150 size_t frame_index = ctx->current_frame;
151 auto& in_flight = ctx->in_flight[frame_index];
152 auto& image_available = ctx->image_available[frame_index];
154 if (device.waitForFences(1, &in_flight, VK_TRUE, UINT64_MAX) == vk::Result::eTimeout) {
156 "Fence timeout during swapchain acquisition for window '{}'",
157 window->get_create_info().title);
161 auto image_index_opt = ctx->swapchain->acquire_next_image(image_available);
162 if (!image_index_opt.has_value()) {
163 ctx->needs_recreation =
true;
167 ctx->current_image_index = image_index_opt.value();
169 if (device.resetFences(1, &in_flight) != vk::Result::eSuccess) {
171 "Failed to reset fence for window '{}'",
172 window->get_create_info().title);
176 const auto& images = ctx->swapchain->get_images();
177 VkImage raw = images[ctx->current_image_index];
178 return reinterpret_cast<uint64_t
>(raw);
181 display_service->get_current_image_view = [
this](
const std::shared_ptr<void>& window_ptr) ->
void* {
182 auto window = std::static_pointer_cast<Window>(window_ptr);
185 if (!context || !context->swapchain) {
189 const auto& image_views = context->swapchain->get_image_views();
190 if (context->current_image_index >= image_views.size()) {
192 "Invalid current_image_index {} for window '{}' (swapchain has {} images)",
193 context->current_image_index,
194 window->get_create_info().title,
199 static thread_local vk::ImageView view;
200 view = image_views[context->current_image_index];
201 return static_cast<void*
>(&view);
204 display_service->get_current_swapchain_image = [
this](
const std::shared_ptr<void>& window_ptr) -> uint64_t {
205 auto window = std::static_pointer_cast<Window>(window_ptr);
207 if (!ctx || !ctx->swapchain)
210 const auto& images = ctx->swapchain->get_images();
211 if (ctx->current_image_index >= images.size())
214 return reinterpret_cast<uint64_t
>(
static_cast<VkImage
>(images[ctx->current_image_index]));
217 display_service->readback_swapchain_region = [
this](
218 const std::shared_ptr<void>& window_ptr,
222 uint32_t pixel_width,
223 uint32_t pixel_height,
224 size_t byte_count) ->
bool {
225 auto window = std::static_pointer_cast<Window>(window_ptr);
228 if (!ctx || !ctx->swapchain) {
230 "readback_swapchain_region: window '{}' has no swapchain",
231 window->get_create_info().title);
237 "readback_swapchain_region: resource manager not set");
241 const auto& images = ctx->swapchain->get_images();
242 if (ctx->current_image_index >= images.size()) {
244 "readback_swapchain_region: invalid image index {} for '{}'",
245 ctx->current_image_index, window->get_create_info().title);
249 auto proxy = std::make_shared<VKImage>(
250 pixel_width, pixel_height, 1U,
251 ctx->swapchain->get_image_format(),
258 res.
image = images[ctx->current_image_index];
259 proxy->set_image_resources(res);
260 proxy->set_current_layout(vk::ImageLayout::ePresentSrcKHR);
266 vk::ImageLayout::ePresentSrcKHR,
267 vk::PipelineStageFlagBits::eBottomOfPipe);
272 display_service->ensure_depth_attachment = [
this](
const std::shared_ptr<void>& window_ptr) {
273 auto window = std::static_pointer_cast<Window>(window_ptr);
277 "ensure_depth_attachment: window '{}' not registered",
278 window->get_create_info().title);
284 display_service->get_depth_image_view = [
this](
const std::shared_ptr<void>& window_ptr) ->
void* {
285 auto window = std::static_pointer_cast<Window>(window_ptr);
287 if (!ctx || !ctx->depth_image || !ctx->depth_image->is_initialized()) {
290 static thread_local vk::ImageView view;
291 view = ctx->depth_image->get_image_view();
292 return static_cast<void*
>(&view);
295 display_service->get_depth_format = [
this](
const std::shared_ptr<void>& window_ptr) -> uint32_t {
296 auto window = std::static_pointer_cast<Window>(window_ptr);
298 if (!ctx || !ctx->depth_image || !ctx->depth_image->is_initialized()) {
299 return static_cast<uint32_t
>(vk::Format::eUndefined);
301 return static_cast<uint32_t
>(ctx->depth_image->get_format());
464 auto extent = ctx.
swapchain->get_extent();
468 && ctx.
depth_image->get_height() == extent.height) {
472 auto device = m_context.get_device();
476 const auto& res = ctx.
depth_image->get_image_resources();
477 if (res.image_view) {
478 device.destroyImageView(res.image_view);
481 device.freeMemory(res.memory);
484 device.destroyImage(res.image);
490 extent.width, extent.height, 1,
491 vk::Format::eD32Sfloat,
492 VKImage::Usage::DEPTH_STENCIL,
493 VKImage::Type::TYPE_2D,
497 m_resource_manager->initialize_image(ctx.
depth_image);
501 "Failed to create depth image for window '{}'",
502 ctx.
window->get_create_info().title);
507 m_resource_manager->transition_image_layout(
509 vk::ImageLayout::eUndefined,
510 vk::ImageLayout::eDepthStencilAttachmentOptimal,
512 vk::ImageAspectFlagBits::eDepth);
513 ctx.
depth_image->set_current_layout(vk::ImageLayout::eDepthStencilAttachmentOptimal);
516 "Created depth image for window '{}': {}x{}, D32_SFLOAT",
517 ctx.
window->get_create_info().title,
518 extent.width, extent.height);
546 if (!window || !window->get_state().is_visible || !window->get_rendering_buffers().empty()) {
551 auto device = m_context.get_device();
554 auto& in_flight = ctx.
in_flight[frame_index];
561 "Clear command buffer not allocated for window '{}'",
562 window->get_create_info().title);
565 cmd_buffer.reset({});
567 if (device.waitForFences(1, &in_flight, VK_TRUE, UINT64_MAX) == vk::Result::eTimeout) {
571 auto image_index_opt = ctx.
swapchain->acquire_next_image(image_available);
572 if (!image_index_opt.has_value()) {
578 if (device.resetFences(1, &in_flight) != vk::Result::eSuccess) {
582 vk::CommandBufferBeginInfo begin_info {};
583 begin_info.flags = vk::CommandBufferUsageFlagBits::eOneTimeSubmit;
584 cmd_buffer.begin(begin_info);
586 const auto& image_views = ctx.
swapchain->get_image_views();
587 auto extent = ctx.
swapchain->get_extent();
589 vk::RenderingAttachmentInfo color_attachment {};
591 color_attachment.imageLayout = vk::ImageLayout::eColorAttachmentOptimal;
592 color_attachment.loadOp = vk::AttachmentLoadOp::eClear;
593 color_attachment.storeOp = vk::AttachmentStoreOp::eStore;
594 color_attachment.clearValue.color = vk::ClearColorValue(
595 window->get_create_info().clear_color);
597 vk::RenderingInfo rendering_info {};
598 rendering_info.renderArea = vk::Rect2D { { 0, 0 }, extent };
599 rendering_info.layerCount = 1;
600 rendering_info.colorAttachmentCount = 1;
601 rendering_info.pColorAttachments = &color_attachment;
603 vk::ImageMemoryBarrier barrier {};
604 barrier.oldLayout = vk::ImageLayout::eUndefined;
605 barrier.newLayout = vk::ImageLayout::eColorAttachmentOptimal;
606 barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
607 barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
609 barrier.subresourceRange.aspectMask = vk::ImageAspectFlagBits::eColor;
610 barrier.subresourceRange.levelCount = 1;
611 barrier.subresourceRange.layerCount = 1;
612 barrier.srcAccessMask = {};
613 barrier.dstAccessMask = vk::AccessFlagBits::eColorAttachmentWrite;
615 cmd_buffer.pipelineBarrier(
616 vk::PipelineStageFlagBits::eTopOfPipe,
617 vk::PipelineStageFlagBits::eColorAttachmentOutput,
618 {}, {}, {}, barrier);
620 cmd_buffer.beginRendering(rendering_info);
621 cmd_buffer.endRendering();
623 barrier.oldLayout = vk::ImageLayout::eColorAttachmentOptimal;
624 barrier.newLayout = vk::ImageLayout::ePresentSrcKHR;
625 barrier.srcAccessMask = vk::AccessFlagBits::eColorAttachmentWrite;
626 barrier.dstAccessMask = {};
628 cmd_buffer.pipelineBarrier(
629 vk::PipelineStageFlagBits::eColorAttachmentOutput,
630 vk::PipelineStageFlagBits::eBottomOfPipe,
631 {}, {}, {}, barrier);
635 submit_and_present(window, cmd_buffer);
639 }
catch (
const std::exception& e) {
641 "Failed to render empty window '{}': {}",
642 window->get_create_info().title, e.what());
646void BackendWindowHandler::submit_and_present(
647 const std::shared_ptr<Window>& window,
648 const vk::CommandBuffer& command_buffer)
650 auto* ctx = find_window_context(window);
653 "Window not registered for submit_and_present");
657 auto graphics_queue = m_context.get_graphics_queue();
659 size_t frame_index = ctx->current_frame;
660 auto& in_flight = ctx->in_flight[frame_index];
661 auto& image_available = ctx->image_available[frame_index];
662 auto& render_finished = ctx->render_finished[frame_index];
664 vk::SubmitInfo submit_info {};
665 vk::PipelineStageFlags wait_stages[] = { vk::PipelineStageFlagBits::eColorAttachmentOutput };
666 submit_info.waitSemaphoreCount = 1;
667 submit_info.pWaitSemaphores = &image_available;
668 submit_info.pWaitDstStageMask = wait_stages;
669 submit_info.commandBufferCount = 1;
670 submit_info.pCommandBuffers = &command_buffer;
671 submit_info.signalSemaphoreCount = 1;
672 submit_info.pSignalSemaphores = &render_finished;
675 auto result = graphics_queue.submit(1, &submit_info, in_flight);
676 }
catch (
const vk::SystemError& e) {
678 "Failed to submit primary command buffer: {}", e.what());
682 bool present_success = ctx->swapchain->present(
683 ctx->current_image_index, render_finished, graphics_queue);
685 if (!present_success) {
686 ctx->needs_recreation =
true;
689 ctx->current_frame = (frame_index + 1) % ctx->in_flight.size();
692 "Window '{}': frame submitted and presented",
693 window->get_create_info().title);
bool update_present_family(vk::SurfaceKHR surface)
Update presentation support for a surface.
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.