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.
785{
786 auto window = ctx.window;
787
788 if (!window || !window->get_state().is_visible || !window->get_rendering_buffers().empty()) {
789 return;
790 }
791
792 try {
794
795 size_t frame_index = ctx.current_frame;
796 auto& in_flight = ctx.in_flight[frame_index];
797 auto& image_available = ctx.image_available[frame_index];
798 auto& render_finished = ctx.render_finished[frame_index];
799
800 auto cmd_buffer = ctx.clear_command_buffers[frame_index];
801 if (!cmd_buffer) {
803 "Clear command buffer not allocated for window '{}'",
804 window->get_create_info().title);
805 return;
806 }
807 cmd_buffer.reset({});
808
809 if (device.waitForFences(1, &in_flight, VK_TRUE, UINT64_MAX) == vk::Result::eTimeout) {
810 return;
811 }
812
813 auto image_index_opt = ctx.swapchain->acquire_next_image(image_available);
814 if (!image_index_opt.has_value()) {
815 ctx.needs_recreation = true;
816 return;
817 }
818 ctx.current_image_index = image_index_opt.value();
819
820 if (device.resetFences(1, &in_flight) != vk::Result::eSuccess) {
821 return;
822 }
823
824 vk::CommandBufferBeginInfo begin_info {};
825 begin_info.flags = vk::CommandBufferUsageFlagBits::eOneTimeSubmit;
826 cmd_buffer.begin(begin_info);
827
828 const auto& image_views = ctx.swapchain->get_image_views();
829 auto extent = ctx.swapchain->get_extent();
830
831 vk::RenderingAttachmentInfo color_attachment {};
832 color_attachment.imageView = image_views[ctx.current_image_index];
833 color_attachment.imageLayout = vk::ImageLayout::eColorAttachmentOptimal;
834 color_attachment.loadOp = vk::AttachmentLoadOp::eClear;
835 color_attachment.storeOp = vk::AttachmentStoreOp::eStore;
836 color_attachment.clearValue.color = vk::ClearColorValue(
837 window->get_create_info().clear_color);
838
839 vk::RenderingInfo rendering_info {};
840 rendering_info.renderArea = vk::Rect2D { { 0, 0 }, extent };
841 rendering_info.layerCount = 1;
842 rendering_info.colorAttachmentCount = 1;
843 rendering_info.pColorAttachments = &color_attachment;
844
845 vk::ImageMemoryBarrier barrier {};
846 barrier.oldLayout = vk::ImageLayout::eUndefined;
847 barrier.newLayout = vk::ImageLayout::eColorAttachmentOptimal;
848 barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
849 barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
850 barrier.image = ctx.swapchain->get_images()[ctx.current_image_index];
851 barrier.subresourceRange.aspectMask = vk::ImageAspectFlagBits::eColor;
852 barrier.subresourceRange.levelCount = 1;
853 barrier.subresourceRange.layerCount = 1;
854 barrier.srcAccessMask = {};
855 barrier.dstAccessMask = vk::AccessFlagBits::eColorAttachmentWrite;
856
857 cmd_buffer.pipelineBarrier(
858 vk::PipelineStageFlagBits::eTopOfPipe,
859 vk::PipelineStageFlagBits::eColorAttachmentOutput,
860 {}, {}, {}, barrier);
861
862 cmd_buffer.beginRendering(rendering_info);
863 cmd_buffer.endRendering();
864
865 barrier.oldLayout = vk::ImageLayout::eColorAttachmentOptimal;
866 barrier.newLayout = vk::ImageLayout::ePresentSrcKHR;
867 barrier.srcAccessMask = vk::AccessFlagBits::eColorAttachmentWrite;
868 barrier.dstAccessMask = {};
869
870 cmd_buffer.pipelineBarrier(
871 vk::PipelineStageFlagBits::eColorAttachmentOutput,
872 vk::PipelineStageFlagBits::eBottomOfPipe,
873 {}, {}, {}, barrier);
874
875 cmd_buffer.end();
876
878
879 ctx.current_frame = (ctx.current_frame + 1) % ctx.in_flight.size();
880
881 } catch (const std::exception& e) {
883 "Failed to render empty window '{}': {}",
884 window->get_create_info().title, e.what());
885 }
886}
#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.