Render empty windows with clear color.
For windows that are registered for processing but have no buffers attached, this performs a minimal clear pass so the window is visible and responsive to input events.
543{
544 auto window = ctx.window;
545
546 if (!window || !window->get_state().is_visible || !window->get_rendering_buffers().empty()) {
547 return;
548 }
549
550 try {
552
553 size_t frame_index = ctx.current_frame;
554 auto& in_flight = ctx.in_flight[frame_index];
555 auto& image_available = ctx.image_available[frame_index];
556 auto& render_finished = ctx.render_finished[frame_index];
557
558 auto cmd_buffer = ctx.clear_command_buffers[frame_index];
559 if (!cmd_buffer) {
561 "Clear command buffer not allocated for window '{}'",
562 window->get_create_info().title);
563 return;
564 }
565 cmd_buffer.reset({});
566
567 if (device.waitForFences(1, &in_flight, VK_TRUE, UINT64_MAX) == vk::Result::eTimeout) {
568 return;
569 }
570
571 auto image_index_opt = ctx.swapchain->acquire_next_image(image_available);
572 if (!image_index_opt.has_value()) {
573 ctx.needs_recreation = true;
574 return;
575 }
576 ctx.current_image_index = image_index_opt.value();
577
578 if (device.resetFences(1, &in_flight) != vk::Result::eSuccess) {
579 return;
580 }
581
582 vk::CommandBufferBeginInfo begin_info {};
583 begin_info.flags = vk::CommandBufferUsageFlagBits::eOneTimeSubmit;
584 cmd_buffer.begin(begin_info);
585
586 const auto& image_views = ctx.swapchain->get_image_views();
587 auto extent = ctx.swapchain->get_extent();
588
589 vk::RenderingAttachmentInfo color_attachment {};
590 color_attachment.imageView = image_views[ctx.current_image_index];
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);
596
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;
602
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;
608 barrier.image = ctx.swapchain->get_images()[ctx.current_image_index];
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;
614
615 cmd_buffer.pipelineBarrier(
616 vk::PipelineStageFlagBits::eTopOfPipe,
617 vk::PipelineStageFlagBits::eColorAttachmentOutput,
618 {}, {}, {}, barrier);
619
620 cmd_buffer.beginRendering(rendering_info);
621 cmd_buffer.endRendering();
622
623 barrier.oldLayout = vk::ImageLayout::eColorAttachmentOptimal;
624 barrier.newLayout = vk::ImageLayout::ePresentSrcKHR;
625 barrier.srcAccessMask = vk::AccessFlagBits::eColorAttachmentWrite;
626 barrier.dstAccessMask = {};
627
628 cmd_buffer.pipelineBarrier(
629 vk::PipelineStageFlagBits::eColorAttachmentOutput,
630 vk::PipelineStageFlagBits::eBottomOfPipe,
631 {}, {}, {}, barrier);
632
633 cmd_buffer.end();
634
636
637 ctx.current_frame = (ctx.current_frame + 1) % ctx.in_flight.size();
638
639 } catch (const std::exception& e) {
641 "Failed to render empty window '{}': {}",
642 window->get_create_info().title, e.what());
643 }
644}
#define MF_RT_ERROR(comp, ctx,...)
void submit_and_present(const std::shared_ptr< Window > &window, const vk::CommandBuffer &command_buffer)
vk::Device get_device() const
Get logical device.
@ GraphicsCallback
Graphics/visual rendering callback - frame-rate real-time.
@ Core
Core engine, backend, subsystems.