MayaFlux 0.5.0
Digital-First Multimedia Processing Framework
Loading...
Searching...
No Matches

◆ capture_frame()

void MayaFlux::Core::BackendWindowHandler::capture_frame ( WindowRenderContext ctx)
private

Capture the current frame from a window's swapchain.

Parameters
ctxWindow render context to capture from

This initiates an asynchronous readback of the current swapchain image into CPU-accessible memory. The result is stored in the capture state for retrieval via the display service.

Definition at line 916 of file BackendWindowHandler.cpp.

917{
918 if (!ctx.swapchain || ctx.window->get_rendering_buffers().empty()
919 || !ctx.window->is_capture_enabled())
920 return;
921
922 auto dev = m_context.get_device();
923 const auto ext = ctx.swapchain->get_extent();
924 const vk::Format fmt = ctx.swapchain->get_image_format();
925 const uint32_t bpp = Core::vk_format_bytes_per_pixel(fmt);
926 const vk::Image src = ctx.swapchain->get_images()[ctx.current_image_index];
927
928 if (!ctx.capture) {
930 }
931
932 auto& slot = *ctx.capture->slots[ctx.capture->slot_index];
933
934 if (slot.pending.load(std::memory_order_acquire))
935 return;
936
937 if (slot.image && slot.extent != ext) {
938 dev.destroyImage(slot.image);
939 dev.freeMemory(slot.mem);
940 slot.image = vk::Image {};
941 slot.mem = vk::DeviceMemory {};
942 }
943
944 if (!slot.image) {
945 vk::ImageCreateInfo ici {};
946 ici.imageType = vk::ImageType::e2D;
947 ici.format = fmt;
948 ici.extent = vk::Extent3D { ext.width, ext.height, 1 };
949 ici.arrayLayers = 1;
950 ici.mipLevels = 1;
951 ici.samples = vk::SampleCountFlagBits::e1;
952 ici.tiling = vk::ImageTiling::eLinear;
953 ici.usage = vk::ImageUsageFlagBits::eTransferDst;
954
955 slot.image = dev.createImage(ici);
956
957 auto req = dev.getImageMemoryRequirements(slot.image);
958 vk::MemoryAllocateInfo mai {};
959 mai.allocationSize = req.size;
960 mai.memoryTypeIndex = m_resource_manager->find_memory_type(
961 req.memoryTypeBits,
962 vk::MemoryPropertyFlagBits::eHostVisible | vk::MemoryPropertyFlagBits::eHostCoherent);
963
964 slot.mem = dev.allocateMemory(mai);
965 dev.bindImageMemory(slot.image, slot.mem, 0);
966 slot.extent = ext;
967 }
968
969 slot.cmd.reset({});
970 vk::CommandBufferBeginInfo bi {};
971 bi.flags = vk::CommandBufferUsageFlagBits::eOneTimeSubmit;
972 slot.cmd.begin(bi);
973
974 vk::ImageMemoryBarrier b {};
975 b.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
976 b.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
977 b.subresourceRange = { vk::ImageAspectFlagBits::eColor, 0, 1, 0, 1 };
978
979 b.image = slot.image;
980 b.oldLayout = vk::ImageLayout::eUndefined;
981 b.newLayout = vk::ImageLayout::eTransferDstOptimal;
982 b.srcAccessMask = {};
983 b.dstAccessMask = vk::AccessFlagBits::eTransferWrite;
984 slot.cmd.pipelineBarrier(vk::PipelineStageFlagBits::eTransfer,
985 vk::PipelineStageFlagBits::eTransfer, {}, {}, {}, b);
986
987 b.image = src;
988 b.oldLayout = vk::ImageLayout::ePresentSrcKHR;
989 b.newLayout = vk::ImageLayout::eTransferSrcOptimal;
990 b.srcAccessMask = vk::AccessFlagBits::eMemoryRead;
991 b.dstAccessMask = vk::AccessFlagBits::eTransferRead;
992 slot.cmd.pipelineBarrier(vk::PipelineStageFlagBits::eTransfer,
993 vk::PipelineStageFlagBits::eTransfer, {}, {}, {}, b);
994
995 vk::ImageCopy copy {};
996 copy.srcSubresource = { vk::ImageAspectFlagBits::eColor, 0, 0, 1 };
997 copy.dstSubresource = { vk::ImageAspectFlagBits::eColor, 0, 0, 1 };
998 copy.extent = vk::Extent3D { ext.width, ext.height, 1 };
999 slot.cmd.copyImage(src, vk::ImageLayout::eTransferSrcOptimal,
1000 slot.image, vk::ImageLayout::eTransferDstOptimal, 1, &copy);
1001
1002 b.image = slot.image;
1003 b.oldLayout = vk::ImageLayout::eTransferDstOptimal;
1004 b.newLayout = vk::ImageLayout::eGeneral;
1005 b.srcAccessMask = vk::AccessFlagBits::eTransferWrite;
1006 b.dstAccessMask = vk::AccessFlagBits::eMemoryRead;
1007 slot.cmd.pipelineBarrier(vk::PipelineStageFlagBits::eTransfer,
1008 vk::PipelineStageFlagBits::eTransfer, {}, {}, {}, b);
1009
1010 b.image = src;
1011 b.oldLayout = vk::ImageLayout::eTransferSrcOptimal;
1012 b.newLayout = vk::ImageLayout::ePresentSrcKHR;
1013 b.srcAccessMask = vk::AccessFlagBits::eTransferRead;
1014 b.dstAccessMask = vk::AccessFlagBits::eMemoryRead;
1015 slot.cmd.pipelineBarrier(vk::PipelineStageFlagBits::eTransfer,
1016 vk::PipelineStageFlagBits::eTransfer, {}, {}, {}, b);
1017
1018 slot.cmd.end();
1019
1020 (void)dev.resetFences(1, &slot.fence);
1021
1022 vk::SubmitInfo si {};
1023 si.commandBufferCount = 1;
1024 si.pCommandBuffers = &slot.cmd;
1025
1026 slot.pending.store(true, std::memory_order_release);
1027
1028 if (auto result = m_context.get_graphics_queue().submit(1, &si, slot.fence); result != vk::Result::eSuccess) {
1030 "Failed to submit capture command buffer for window '{}': {}",
1031 ctx.window->get_create_info().title, vk::to_string(result));
1032 slot.pending.store(false, std::memory_order_release);
1033 return;
1034 }
1035
1036 ctx.capture->slot_index = (ctx.capture->slot_index + 1) % ctx.capture->slots.size();
1037}
#define MF_RT_ERROR(comp, ctx,...)
size_t b
uint32_t find_memory_type(uint32_t type_filter, vk::MemoryPropertyFlags properties) const
Find a suitable memory type for Vulkan buffer allocation.
void ensure_capture_state(WindowRenderContext &ctx)
Ensure capture state is initialized for a window context.
vk::Device get_device() const
Get logical device.
Definition VKContext.hpp:49
vk::Queue get_graphics_queue() const
Get graphics queue.
Definition VKContext.hpp:54
uint32_t vk_format_bytes_per_pixel(vk::Format fmt)
Byte width of a single pixel for a given Vulkan format.
@ GraphicsCallback
Graphics/visual rendering callback - frame-rate real-time.
@ Core
Core engine, backend, subsystems.

References b, MayaFlux::Core::WindowRenderContext::capture, MayaFlux::Journal::Core, MayaFlux::Core::WindowRenderContext::current_image_index, MayaFlux::Journal::GraphicsCallback, MF_RT_ERROR, MayaFlux::Core::WindowRenderContext::swapchain, MayaFlux::Core::vk_format_bytes_per_pixel(), and MayaFlux::Core::WindowRenderContext::window.

+ Here is the call graph for this function: