14 struct FencedSubmission {
15 vk::CommandBuffer
cmd;
22 , m_command_manager(command_manager)
28 buffer_service->initialize_buffer = [
this](
const std::shared_ptr<void>& vk_buf) ->
void {
29 auto buffer = std::static_pointer_cast<Buffers::VKBuffer>(vk_buf);
33 buffer_service->allocate_raw_buffer = [
this](
36 uint32_t memory_property_flags,
40 void*& out_mapped_ptr) ->
void {
43 void* mapped =
nullptr;
46 vk::BufferUsageFlags(usage_flags),
47 vk::MemoryPropertyFlags(memory_property_flags),
50 out_buffer =
static_cast<VkBuffer
>(buf);
51 out_memory =
static_cast<VkDeviceMemory
>(mem);
52 out_mapped_ptr = mapped;
55 buffer_service->destroy_buffer = [
this](
const std::shared_ptr<void>& vk_buf) {
56 auto buffer = std::static_pointer_cast<Buffers::VKBuffer>(vk_buf);
60 buffer_service->get_buffer_device_address = [
this](
const std::shared_ptr<void>& vk_buf) -> uint64_t {
61 auto buffer = std::static_pointer_cast<Buffers::VKBuffer>(vk_buf);
63 return static_cast<uint64_t
>(address);
66 buffer_service->execute_immediate = [
this](
const std::function<void(
void*)>& recorder) {
68 recorder(
static_cast<void*
>(
cmd));
72 buffer_service->record_deferred = [
this](
const std::function<void(
void*)>& recorder) {
74 recorder(
static_cast<void*
>(
cmd));
78 buffer_service->flush_range = [
this](
void* memory,
size_t offset,
size_t size) {
79 vk::DeviceMemory mem(
reinterpret_cast<VkDeviceMemory
>(memory));
80 vk::MappedMemoryRange range { mem,
offset, size == 0 ? VK_WHOLE_SIZE : size };
81 if (
auto result =
m_context.
get_device().flushMappedMemoryRanges(1, &range); result != vk::Result::eSuccess) {
83 "Failed to flush mapped memory range: {}", vk::to_string(result));
87 buffer_service->invalidate_range = [
this](
void* memory,
size_t offset,
size_t size) {
88 vk::DeviceMemory mem(
reinterpret_cast<VkDeviceMemory
>(memory));
89 vk::MappedMemoryRange range { mem,
offset, size == 0 ? VK_WHOLE_SIZE : size };
90 if (
auto result =
m_context.
get_device().invalidateMappedMemoryRanges(1, &range); result != vk::Result::eSuccess) {
92 "Failed to invalidate mapped memory range: {}", vk::to_string(result));
96 buffer_service->map_buffer = [
this](
void* memory,
size_t offset,
size_t size) ->
void* {
97 vk::DeviceMemory mem(
reinterpret_cast<VkDeviceMemory
>(memory));
101 buffer_service->unmap_buffer = [
this](
void* memory) {
102 vk::DeviceMemory mem(
reinterpret_cast<VkDeviceMemory
>(memory));
106 buffer_service->copy_buffer = [
this](
void* src,
void* dst,
size_t size,
size_t src_off,
size_t dst_off) {
107 vk::Buffer s(
static_cast<VkBuffer
>(src));
108 vk::Buffer d(
static_cast<VkBuffer
>(dst));
110 vk::BufferCopy region { src_off, dst_off, size };
111 cmd.copyBuffer(s, d, 1, ®ion);
115 buffer_service->execute_fenced = [
this](
const std::function<void(
void*)>& recorder)
116 -> std::shared_ptr<void> {
119 recorder(
static_cast<void*
>(
cmd));
124 vk::FenceCreateInfo fence_info {};
125 vk::Fence
fence = device.createFence(fence_info);
127 vk::SubmitInfo submit_info {};
128 submit_info.commandBufferCount = 1;
129 submit_info.pCommandBuffers = &
cmd;
132 result != vk::Result::eSuccess) {
134 "execute_fenced: queue submit failed: {}", vk::to_string(result));
135 device.destroyFence(
fence);
140 auto handle = std::make_shared<FencedSubmission>();
142 handle->fence =
fence;
146 buffer_service->wait_fenced = [
this](
const std::shared_ptr<void>& handle) {
149 auto sub = std::static_pointer_cast<FencedSubmission>(handle);
154 if (
auto result = device.waitForFences(1, &sub->fence, VK_TRUE, UINT64_MAX);
155 result != vk::Result::eSuccess) {
157 "wait_fenced: waitForFences failed: {}", vk::to_string(result));
161 buffer_service->release_fenced = [
this](
const std::shared_ptr<void>& handle) {
164 auto sub = std::static_pointer_cast<FencedSubmission>(handle);
168 device.destroyFence(sub->fence);
169 sub->fence =
nullptr;
177 buffer_service->copy_buffer_fenced = [
this, buffer_service](
void* src,
void* dst,
size_t size,
size_t src_off,
size_t dst_off)
178 -> std::shared_ptr<void> {
179 vk::Buffer s(
static_cast<VkBuffer
>(src));
180 vk::Buffer d(
static_cast<VkBuffer
>(dst));
181 return buffer_service->execute_fenced([&](
void* cmd_ptr) {
182 vk::CommandBuffer
cmd(
static_cast<VkCommandBuffer
>(cmd_ptr));
183 vk::BufferCopy region { src_off, dst_off,
static_cast<vk::DeviceSize
>(size) };
184 cmd.copyBuffer(s, d, 1, ®ion);
191 vk::BufferUsageFlags usage,
192 vk::MemoryPropertyFlags memory_properties,
194 vk::Buffer& out_buffer,
195 vk::DeviceMemory& out_memory,
196 void*& out_mapped_ptr)
198 vk::BufferCreateInfo buffer_info {};
199 buffer_info.size = size_bytes;
200 buffer_info.usage = usage;
201 buffer_info.sharingMode = vk::SharingMode::eExclusive;
205 }
catch (
const vk::SystemError& e) {
209 std::source_location::current(),
210 "Failed to create raw VkBuffer: " + std::string(e.what()));
213 vk::MemoryRequirements mem_requirements =
m_context.
get_device().getBufferMemoryRequirements(out_buffer);
215 vk::MemoryAllocateInfo alloc_info;
216 alloc_info.allocationSize = mem_requirements.size;
217 alloc_info.memoryTypeIndex =
find_memory_type(mem_requirements.memoryTypeBits, memory_properties);
221 }
catch (
const vk::SystemError& e) {
226 std::source_location::current(),
227 "Failed to allocate raw VkDeviceMemory: " + std::string(e.what()));
232 }
catch (
const vk::SystemError& e) {
238 std::source_location::current(),
239 "Failed to bind raw buffer memory: " + std::string(e.what()));
242 out_mapped_ptr =
nullptr;
246 }
catch (
const vk::SystemError& e) {
252 std::source_location::current(),
253 "Failed to map raw buffer memory: " + std::string(e.what()));
262 "Attempted to initialize null VKBuffer");
265 if (buffer->is_initialized()) {
267 "VKBuffer already initialized, skipping");
271 vk::Buffer vk_buffer;
272 vk::DeviceMemory memory;
273 void* mapped_ptr =
nullptr;
276 buffer->get_size_bytes(),
277 buffer->get_usage_flags(),
278 vk::MemoryPropertyFlags(buffer->get_memory_properties()),
279 buffer->is_host_visible(),
280 vk_buffer, memory, mapped_ptr);
282 auto& resources = buffer->get_buffer_resources();
283 resources.buffer = vk_buffer;
284 resources.memory = memory;
285 resources.mapped_ptr = mapped_ptr;
290 "VKBuffer initialized: {} bytes, modality: {}, VkBuffer: {:p}",
291 buffer->get_size_bytes(),
293 (
void*)buffer->get_buffer());
300 "Attempted to cleanup null VKBuffer");
309 auto& res = it->get()->get_buffer_resources();
311 if (res.mapped_ptr) {
315 if (res.index_buffer) {
319 if (res.index_memory) {
332 "VKBuffer cleaned up: {:p}",
static_cast<void*
>(res.buffer));
340 auto& resources = buffer_wrapper->get_buffer_resources();
341 auto dirty_ranges = buffer_wrapper->get_and_clear_dirty_ranges();
342 if (!dirty_ranges.empty()) {
343 for (
auto [
offset, size] : dirty_ranges) {
344 vk::MappedMemoryRange range;
345 range.memory = resources.memory;
348 if (
auto result =
m_context.
get_device().flushMappedMemoryRanges(1, &range); result != vk::Result::eSuccess) {
350 "Failed to flush mapped memory range: {}", vk::to_string(result));
354 "Flushed {} dirty ranges for buffer {:p}", dirty_ranges.size(),
355 (
void*)buffer_wrapper->get_buffer());
358 auto invalid_ranges = buffer_wrapper->get_and_clear_invalid_ranges();
359 if (!invalid_ranges.empty()) {
360 for (
auto [
offset, size] : invalid_ranges) {
361 vk::MappedMemoryRange range;
362 range.memory = buffer_wrapper->get_buffer_resources().memory;
365 if (
auto result =
m_context.
get_device().invalidateMappedMemoryRanges(1, &range); result != vk::Result::eSuccess) {
367 "Failed to invalidate mapped memory range: {}", vk::to_string(result));
371 "Invalidated {} ranges for buffer {:p}", invalid_ranges.size(),
372 (
void*)buffer_wrapper->get_buffer());
378 const std::shared_ptr<Buffers::VKBuffer>& buffer)
const
380 if (!buffer || !buffer->is_initialized()) {
382 "get_buffer_device_address: buffer not initialized");
386 vk::BufferDeviceAddressInfo info {};
387 info.buffer = buffer->get_buffer();
396 "Attempted to initialize null VKImage");
400 if (
image->is_initialized()) {
402 "VKImage already initialized, skipping");
410 vk::ImageCreateInfo image_info {};
412 switch (
image->get_type()) {
414 image_info.imageType = vk::ImageType::e1D;
418 image_info.imageType = vk::ImageType::e2D;
421 image_info.imageType = vk::ImageType::e3D;
425 image_info.extent.width =
image->get_width();
426 image_info.extent.height =
image->get_height();
427 image_info.extent.depth =
image->get_depth();
428 image_info.mipLevels =
image->get_mip_levels();
429 image_info.arrayLayers =
image->get_array_layers();
430 image_info.format =
image->get_format();
431 image_info.tiling = vk::ImageTiling::eOptimal;
432 image_info.initialLayout = vk::ImageLayout::eUndefined;
433 image_info.usage =
image->get_usage_flags();
434 image_info.sharingMode = vk::SharingMode::eExclusive;
435 image_info.samples = vk::SampleCountFlagBits::e1;
437 ? vk::ImageCreateFlagBits::eCubeCompatible
438 : vk::ImageCreateFlags {};
443 }
catch (
const vk::SystemError& e) {
447 std::source_location::current(),
448 "Failed to create VkImage: " + std::string(e.what()));
455 vk::MemoryRequirements mem_requirements;
458 vk::MemoryAllocateInfo alloc_info {};
459 alloc_info.allocationSize = mem_requirements.size;
461 mem_requirements.memoryTypeBits,
462 image->get_memory_properties());
464 vk::DeviceMemory memory;
467 }
catch (
const vk::SystemError& e) {
472 std::source_location::current(),
473 "Failed to allocate VkDeviceMemory for image: " + std::string(e.what()));
482 }
catch (
const vk::SystemError& e) {
488 std::source_location::current(),
489 "Failed to bind memory to VkImage: " + std::string(e.what()));
496 vk::ImageViewCreateInfo view_info {};
498 switch (
image->get_type()) {
500 view_info.viewType = (
image->get_array_layers() > 1)
501 ? vk::ImageViewType::e1DArray
502 : vk::ImageViewType::e1D;
505 view_info.viewType = (
image->get_array_layers() > 1)
506 ? vk::ImageViewType::e2DArray
507 : vk::ImageViewType::e2D;
510 view_info.viewType = vk::ImageViewType::e3D;
513 view_info.viewType = vk::ImageViewType::eCube;
517 view_info.image = vk_image;
518 view_info.format =
image->get_format();
519 view_info.subresourceRange.aspectMask =
image->get_aspect_flags();
520 view_info.subresourceRange.baseMipLevel = 0;
521 view_info.subresourceRange.levelCount =
image->get_mip_levels();
522 view_info.subresourceRange.baseArrayLayer = 0;
523 view_info.subresourceRange.layerCount =
image->get_array_layers();
525 view_info.components.r = vk::ComponentSwizzle::eIdentity;
526 view_info.components.g = vk::ComponentSwizzle::eIdentity;
527 view_info.components.b = vk::ComponentSwizzle::eIdentity;
528 view_info.components.a = vk::ComponentSwizzle::eIdentity;
530 vk::ImageView image_view;
533 }
catch (
const vk::SystemError& e) {
539 std::source_location::current(),
540 "Failed to create VkImageView: " + std::string(e.what()));
548 resources.
image = vk_image;
549 resources.image_view = image_view;
550 resources.memory = memory;
551 resources.sampler =
nullptr;
553 image->set_image_resources(resources);
554 image->set_current_layout(vk::ImageLayout::eUndefined);
557 "VKImage initialized: {}x{}x{}, format: {}, {} mips, {} layers",
559 vk::to_string(
image->get_format()),
560 image->get_mip_levels(),
image->get_array_layers());
569 const auto& resources =
image->get_image_resources();
571 if (resources.image_view) {
575 if (resources.image) {
579 if (resources.memory) {
584 "VKImage cleaned up");
589 vk::ImageLayout old_layout,
590 vk::ImageLayout new_layout,
592 uint32_t array_layers,
593 vk::ImageAspectFlags aspect_flags)
596 vk::ImageMemoryBarrier barrier {};
597 barrier.oldLayout = old_layout;
598 barrier.newLayout = new_layout;
599 barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
600 barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
601 barrier.image =
image;
602 barrier.subresourceRange.aspectMask = aspect_flags;
603 barrier.subresourceRange.baseMipLevel = 0;
604 barrier.subresourceRange.levelCount = mip_levels;
605 barrier.subresourceRange.baseArrayLayer = 0;
606 barrier.subresourceRange.layerCount = array_layers;
608 vk::PipelineStageFlags src_stage;
609 vk::PipelineStageFlags dst_stage;
611 if (old_layout == vk::ImageLayout::eUndefined && new_layout == vk::ImageLayout::eTransferDstOptimal) {
612 barrier.srcAccessMask = vk::AccessFlags {};
613 barrier.dstAccessMask = vk::AccessFlagBits::eTransferWrite;
614 src_stage = vk::PipelineStageFlagBits::eTopOfPipe;
615 dst_stage = vk::PipelineStageFlagBits::eTransfer;
616 }
else if (old_layout == vk::ImageLayout::eTransferDstOptimal && new_layout == vk::ImageLayout::eShaderReadOnlyOptimal) {
617 barrier.srcAccessMask = vk::AccessFlagBits::eTransferWrite;
618 barrier.dstAccessMask = vk::AccessFlagBits::eShaderRead;
619 src_stage = vk::PipelineStageFlagBits::eTransfer;
620 dst_stage = vk::PipelineStageFlagBits::eFragmentShader;
621 }
else if (old_layout == vk::ImageLayout::eUndefined && new_layout == vk::ImageLayout::eShaderReadOnlyOptimal) {
622 barrier.srcAccessMask = vk::AccessFlags {};
623 barrier.dstAccessMask = vk::AccessFlagBits::eShaderRead;
624 src_stage = vk::PipelineStageFlagBits::eTopOfPipe;
625 dst_stage = vk::PipelineStageFlagBits::eFragmentShader;
626 }
else if (old_layout == vk::ImageLayout::eUndefined && new_layout == vk::ImageLayout::eColorAttachmentOptimal) {
627 barrier.srcAccessMask = vk::AccessFlags {};
628 barrier.dstAccessMask = vk::AccessFlagBits::eColorAttachmentWrite;
629 src_stage = vk::PipelineStageFlagBits::eTopOfPipe;
630 dst_stage = vk::PipelineStageFlagBits::eColorAttachmentOutput;
631 }
else if (old_layout == vk::ImageLayout::eUndefined && new_layout == vk::ImageLayout::eDepthStencilAttachmentOptimal) {
632 barrier.srcAccessMask = vk::AccessFlags {};
633 barrier.dstAccessMask = vk::AccessFlagBits::eDepthStencilAttachmentRead | vk::AccessFlagBits::eDepthStencilAttachmentWrite;
634 src_stage = vk::PipelineStageFlagBits::eTopOfPipe;
635 dst_stage = vk::PipelineStageFlagBits::eEarlyFragmentTests;
636 }
else if (old_layout == vk::ImageLayout::eUndefined && new_layout == vk::ImageLayout::eGeneral) {
637 barrier.srcAccessMask = vk::AccessFlags {};
638 barrier.dstAccessMask = vk::AccessFlagBits::eShaderRead | vk::AccessFlagBits::eShaderWrite;
639 src_stage = vk::PipelineStageFlagBits::eTopOfPipe;
640 dst_stage = vk::PipelineStageFlagBits::eComputeShader;
641 }
else if (old_layout == vk::ImageLayout::eGeneral && new_layout == vk::ImageLayout::eShaderReadOnlyOptimal) {
642 barrier.srcAccessMask = vk::AccessFlagBits::eShaderWrite;
643 barrier.dstAccessMask = vk::AccessFlagBits::eShaderRead;
644 src_stage = vk::PipelineStageFlagBits::eComputeShader;
645 dst_stage = vk::PipelineStageFlagBits::eComputeShader;
646 }
else if (old_layout == vk::ImageLayout::eShaderReadOnlyOptimal && new_layout == vk::ImageLayout::eGeneral) {
647 barrier.srcAccessMask = vk::AccessFlagBits::eShaderRead;
648 barrier.dstAccessMask = vk::AccessFlagBits::eShaderWrite;
649 src_stage = vk::PipelineStageFlagBits::eComputeShader;
650 dst_stage = vk::PipelineStageFlagBits::eComputeShader;
652 barrier.srcAccessMask = vk::AccessFlagBits::eMemoryRead | vk::AccessFlagBits::eMemoryWrite;
653 barrier.dstAccessMask = vk::AccessFlagBits::eMemoryRead | vk::AccessFlagBits::eMemoryWrite;
654 src_stage = vk::PipelineStageFlagBits::eAllCommands;
655 dst_stage = vk::PipelineStageFlagBits::eAllCommands;
658 "Using generic image layout transition");
662 src_stage, dst_stage,
663 vk::DependencyFlags {},
671 "Image layout transitioned: {} -> {}",
672 vk::to_string(old_layout), vk::to_string(new_layout));
676 std::shared_ptr<VKImage>
image,
680 if (!
image || !data) {
682 "Invalid parameters for upload_image_data");
686 auto staging = std::make_shared<Buffers::VKBuffer>(
688 Buffers::VKBuffer::Usage::STAGING,
693 void* mapped = staging->get_mapped_ptr();
696 "Failed to map staging buffer for image upload");
701 std::memcpy(mapped, data, size);
702 staging->mark_dirty_range(0, size);
704 auto& resources = staging->get_buffer_resources();
705 vk::MappedMemoryRange range { resources.memory, 0, VK_WHOLE_SIZE };
707 if (
auto result =
m_context.
get_device().flushMappedMemoryRanges(1, &range); result != vk::Result::eSuccess) {
709 "Failed to flush mapped memory range: {}", vk::to_string(result));
713 vk::ImageMemoryBarrier barrier {};
714 barrier.oldLayout =
image->get_current_layout();
715 barrier.newLayout = vk::ImageLayout::eTransferDstOptimal;
716 barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
717 barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
718 barrier.image =
image->get_image();
719 barrier.subresourceRange.aspectMask =
image->get_aspect_flags();
720 barrier.subresourceRange.baseMipLevel = 0;
721 barrier.subresourceRange.levelCount =
image->get_mip_levels();
722 barrier.subresourceRange.baseArrayLayer = 0;
723 barrier.subresourceRange.layerCount =
image->get_array_layers();
724 barrier.srcAccessMask = vk::AccessFlagBits::eShaderRead;
725 barrier.dstAccessMask = vk::AccessFlagBits::eTransferWrite;
728 vk::PipelineStageFlagBits::eFragmentShader,
729 vk::PipelineStageFlagBits::eTransfer,
730 vk::DependencyFlags {},
731 0,
nullptr, 0,
nullptr, 1, &barrier);
733 vk::BufferImageCopy region {};
734 region.bufferOffset = 0;
735 region.bufferRowLength = 0;
736 region.bufferImageHeight = 0;
737 region.imageSubresource.aspectMask =
image->get_aspect_flags();
738 region.imageSubresource.mipLevel = 0;
739 region.imageSubresource.baseArrayLayer = 0;
740 region.imageSubresource.layerCount =
image->get_array_layers();
741 region.imageOffset = vk::Offset3D { 0, 0, 0 };
742 region.imageExtent = vk::Extent3D {
748 cmd.copyBufferToImage(
749 staging->get_buffer(),
751 vk::ImageLayout::eTransferDstOptimal,
754 barrier.oldLayout = vk::ImageLayout::eTransferDstOptimal;
755 barrier.newLayout = vk::ImageLayout::eShaderReadOnlyOptimal;
756 barrier.srcAccessMask = vk::AccessFlagBits::eTransferWrite;
757 barrier.dstAccessMask = vk::AccessFlagBits::eShaderRead;
760 vk::PipelineStageFlagBits::eTransfer,
761 vk::PipelineStageFlagBits::eFragmentShader,
762 vk::DependencyFlags {},
763 0,
nullptr, 0,
nullptr, 1, &barrier);
766 image->set_current_layout(vk::ImageLayout::eShaderReadOnlyOptimal);
769 "Uploaded {} bytes to image {}x{}",
770 size,
image->get_width(),
image->get_height());
774 std::shared_ptr<VKImage>
image,
777 const std::shared_ptr<Buffers::VKBuffer>& staging,
bool deferred)
779 if (!
image || !data || !staging) {
781 "Invalid parameters for upload_image_data_with_staging");
785 void* mapped = staging->get_mapped_ptr();
788 "upload_image_data_with_staging: staging buffer has no mapped pointer");
792 std::memcpy(mapped, data, size);
793 staging->mark_dirty_range(0, size);
795 auto& resources = staging->get_buffer_resources();
796 vk::MappedMemoryRange range { resources.memory, 0, VK_WHOLE_SIZE };
799 result != vk::Result::eSuccess) {
801 "upload_image_data_with_staging: flush failed: {}", vk::to_string(result));
804 auto record_command = [&](vk::CommandBuffer
cmd) {
805 vk::ImageMemoryBarrier barrier {};
806 barrier.oldLayout =
image->get_current_layout();
807 barrier.newLayout = vk::ImageLayout::eTransferDstOptimal;
808 barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
809 barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
810 barrier.image =
image->get_image();
811 barrier.subresourceRange = {
812 image->get_aspect_flags(), 0,
813 image->get_mip_levels(), 0,
814 image->get_array_layers()
816 barrier.srcAccessMask = vk::AccessFlagBits::eShaderRead;
817 barrier.dstAccessMask = vk::AccessFlagBits::eTransferWrite;
820 vk::PipelineStageFlagBits::eFragmentShader,
821 vk::PipelineStageFlagBits::eTransfer,
822 {}, 0,
nullptr, 0,
nullptr, 1, &barrier);
824 vk::BufferImageCopy region {};
825 region.imageSubresource.aspectMask =
image->get_aspect_flags();
826 region.imageSubresource.layerCount =
image->get_array_layers();
827 region.imageOffset = vk::Offset3D { 0, 0, 0 };
828 region.imageExtent = vk::Extent3D {
834 cmd.copyBufferToImage(
835 staging->get_buffer(),
837 vk::ImageLayout::eTransferDstOptimal,
840 barrier.oldLayout = vk::ImageLayout::eTransferDstOptimal;
841 barrier.newLayout = vk::ImageLayout::eShaderReadOnlyOptimal;
842 barrier.srcAccessMask = vk::AccessFlagBits::eTransferWrite;
843 barrier.dstAccessMask = vk::AccessFlagBits::eShaderRead;
846 vk::PipelineStageFlagBits::eTransfer,
847 vk::PipelineStageFlagBits::eFragmentShader,
848 {}, 0,
nullptr, 0,
nullptr, 1, &barrier);
857 image->set_current_layout(vk::ImageLayout::eShaderReadOnlyOptimal);
860 "upload_image_data_with_staging: {} bytes to image {}x{}",
861 size,
image->get_width(),
image->get_height());
865 std::shared_ptr<VKImage>
image,
868 const std::shared_ptr<Buffers::VKBuffer>& staging,
870 vk::ImageLayout restore_layout,
871 vk::PipelineStageFlags restore_stage)
873 if (!
image || !data) {
875 "download_image_data: invalid parameters");
879 std::shared_ptr<Buffers::VKBuffer> local_staging;
882 local_staging = std::make_shared<Buffers::VKBuffer>(
884 Buffers::VKBuffer::Usage::STAGING,
887 active_staging = local_staging.get();
889 active_staging = staging.get();
892 auto record_command = [&](vk::CommandBuffer
cmd) {
893 vk::ImageMemoryBarrier barrier {};
894 barrier.oldLayout =
image->get_current_layout();
895 barrier.newLayout = vk::ImageLayout::eTransferSrcOptimal;
896 barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
897 barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
898 barrier.image =
image->get_image();
899 barrier.subresourceRange.aspectMask =
image->get_aspect_flags();
900 barrier.subresourceRange.baseMipLevel = 0;
901 barrier.subresourceRange.levelCount =
image->get_mip_levels();
902 barrier.subresourceRange.baseArrayLayer = 0;
903 barrier.subresourceRange.layerCount =
image->get_array_layers();
904 barrier.srcAccessMask = vk::AccessFlagBits::eShaderWrite | vk::AccessFlagBits::eShaderRead;
905 barrier.dstAccessMask = vk::AccessFlagBits::eTransferRead;
908 vk::PipelineStageFlagBits::eFragmentShader | vk::PipelineStageFlagBits::eComputeShader,
909 vk::PipelineStageFlagBits::eTransfer,
910 vk::DependencyFlags {}, {}, {}, barrier);
912 vk::BufferImageCopy region {};
913 region.bufferOffset = 0;
914 region.bufferRowLength = 0;
915 region.bufferImageHeight = 0;
916 region.imageSubresource.aspectMask =
image->get_aspect_flags();
917 region.imageSubresource.mipLevel = 0;
918 region.imageSubresource.baseArrayLayer = 0;
919 region.imageSubresource.layerCount =
image->get_array_layers();
920 region.imageOffset = vk::Offset3D { 0, 0, 0 };
921 region.imageExtent = vk::Extent3D {
927 cmd.copyImageToBuffer(
929 vk::ImageLayout::eTransferSrcOptimal,
930 active_staging->get_buffer(),
933 barrier.oldLayout = vk::ImageLayout::eTransferSrcOptimal;
934 barrier.newLayout = restore_layout;
935 barrier.srcAccessMask = vk::AccessFlagBits::eTransferRead;
936 barrier.dstAccessMask = vk::AccessFlagBits::eMemoryRead;
939 vk::PipelineStageFlagBits::eTransfer,
941 vk::DependencyFlags {}, {}, {}, barrier);
944 if (deferred && staging) {
946 image->set_current_layout(restore_layout);
956 vk::FenceCreateInfo fence_info {};
957 vk::Fence
fence = device.createFence(fence_info);
959 vk::SubmitInfo submit_info {};
960 submit_info.commandBufferCount = 1;
961 submit_info.pCommandBuffers = &
cmd;
964 result != vk::Result::eSuccess) {
966 "download_image_data: queue submit failed: {}", vk::to_string(result));
967 device.destroyFence(
fence);
974 image->set_current_layout(restore_layout);
976 if (
auto result = device.waitForFences(1, &
fence, VK_TRUE, UINT64_MAX);
977 result != vk::Result::eSuccess) {
979 "download_image_data: waitForFences failed: {}", vk::to_string(result));
982 vk::MappedMemoryRange range { active_staging->get_buffer_resources().memory, 0, VK_WHOLE_SIZE };
983 if (
auto result = device.invalidateMappedMemoryRanges(1, &range);
984 result != vk::Result::eSuccess) {
986 "download_image_data: invalidate failed: {}", vk::to_string(result));
989 void* mapped = active_staging->get_mapped_ptr();
991 std::memcpy(data, mapped, size);
994 "download_image_data: staging buffer has no mapped pointer");
997 device.destroyFence(
fence);
1004 "download_image_data: {} bytes from image {}x{}",
1005 size,
image->get_width(),
image->get_height());
1010 vk::SamplerAddressMode address_mode,
1011 float max_anisotropy)
1014 auto hash_combine = [](
size_t& seed,
size_t value) {
1015 seed ^=
value + 0x9e3779b9 + (seed << 6) + (seed >> 2);
1018 hash_combine(hash,
static_cast<size_t>(filter));
1019 hash_combine(hash,
static_cast<size_t>(address_mode));
1020 hash_combine(hash, std::hash<float> {}(max_anisotropy));
1025 "Reusing cached sampler (hash: 0x{:X})", hash);
1029 vk::SamplerCreateInfo sampler_info;
1030 sampler_info.magFilter = filter;
1031 sampler_info.minFilter = filter;
1032 sampler_info.mipmapMode = vk::SamplerMipmapMode::eLinear;
1033 sampler_info.addressModeU = address_mode;
1034 sampler_info.addressModeV = address_mode;
1035 sampler_info.addressModeW = address_mode;
1036 sampler_info.mipLodBias = 0.0F;
1037 sampler_info.anisotropyEnable = max_anisotropy > 0.0F;
1038 sampler_info.maxAnisotropy = max_anisotropy;
1039 sampler_info.compareEnable = VK_FALSE;
1040 sampler_info.compareOp = vk::CompareOp::eAlways;
1041 sampler_info.minLod = 0.0F;
1042 sampler_info.maxLod = VK_LOD_CLAMP_NONE;
1043 sampler_info.borderColor = vk::BorderColor::eFloatOpaqueBlack;
1044 sampler_info.unnormalizedCoordinates = VK_FALSE;
1046 vk::Sampler sampler;
1049 }
catch (
const vk::SystemError& e) {
1051 "Failed to create sampler: {}", e.what());
1058 "Created sampler (filter: {}, address: {}, anisotropy: {}, hash: 0x{:X})",
1059 vk::to_string(filter), vk::to_string(address_mode), max_anisotropy, hash);
1071 if (it->second == sampler) {
1080 "Destroyed sampler");
1085 vk::PhysicalDeviceMemoryProperties mem_properties;
1088 for (uint32_t i = 0; i < mem_properties.memoryTypeCount; i++) {
1089 if ((type_filter & (1 << i)) && (mem_properties.memoryTypes[i].propertyFlags & properties) == properties) {
1094 error<std::runtime_error>(
1097 std::source_location::current(),
1098 "Failed to find suitable memory type");
1124 vk::SemaphoreCreateInfo sem_info {};
1128 vk::SubmitInfo submit {};
1129 submit.commandBufferCount = 1;
1130 submit.pCommandBuffers = &
cmd;
1131 submit.signalSemaphoreCount = 1;
1136 "Failed to submit deferred command buffer");
1159 if (buffer && buffer->is_initialized()) {
1169 auto hash_combine = [](
size_t& seed,
size_t value) {
1170 seed ^=
value + 0x9e3779b9 + (seed << 6) + (seed >> 2);
1173 hash_combine(hash,
static_cast<size_t>(filter));
1174 hash_combine(hash,
static_cast<size_t>(address_mode));
1175 hash_combine(hash, std::hash<float> {}(max_anisotropy));
#define MF_INFO(comp, ctx,...)
#define MF_ERROR(comp, ctx,...)
#define MF_WARN(comp, ctx,...)
#define MF_DEBUG(comp, ctx,...)
Vulkan-backed buffer wrapper used in processing chains.
vk::Semaphore m_deferred_semaphore
void cleanup_image(const std::shared_ptr< VKImage > &image)
Cleanup a VKImage (destroy view, image, and free memory)
void cleanup_buffer(const std::shared_ptr< Buffers::VKBuffer > &buffer)
Cleanup a buffer and release associated resources.
void flush_pending_buffer_operations()
Flush any pending buffer operations (e.g., uploads/downloads)
vk::DeviceAddress get_buffer_device_address(const std::shared_ptr< Buffers::VKBuffer > &buffer) const
Query the Vulkan device address of an initialized BDA-capable buffer.
size_t compute_sampler_hash(vk::Filter filter, vk::SamplerAddressMode address_mode, float max_anisotropy) const
void download_image_data(std::shared_ptr< VKImage > image, void *data, size_t size, const std::shared_ptr< Buffers::VKBuffer > &staging=nullptr, bool deferred=false, vk::ImageLayout restore_layout=vk::ImageLayout::eShaderReadOnlyOptimal, vk::PipelineStageFlags restore_stage=vk::PipelineStageFlagBits::eFragmentShader)
Download image data to a host pointer.
void execute_immediate_commands(const std::function< void(vk::CommandBuffer)> &recorder)
Execute immediate command recording for buffer operations.
BackendResourceManager(VKContext &context, VKCommandManager &command_manager)
std::unordered_map< size_t, vk::Sampler > m_sampler_cache
void initialize_image(const std::shared_ptr< VKImage > &image)
Initialize a VKImage (allocate VkImage, memory, and create image view)
void destroy_sampler(vk::Sampler sampler)
Destroy sampler.
vk::Sampler create_sampler(vk::Filter filter=vk::Filter::eLinear, vk::SamplerAddressMode address_mode=vk::SamplerAddressMode::eRepeat, float max_anisotropy=0.0F)
Create sampler.
uint32_t find_memory_type(uint32_t type_filter, vk::MemoryPropertyFlags properties) const
Find a suitable memory type for Vulkan buffer allocation.
VKCommandManager & m_command_manager
void transition_image_layout(vk::Image image, vk::ImageLayout old_layout, vk::ImageLayout new_layout, uint32_t mip_levels=1, uint32_t array_layers=1, vk::ImageAspectFlags aspect_flags=vk::ImageAspectFlagBits::eColor)
Transition image layout using a pipeline barrier.
void initialize_buffer(const std::shared_ptr< Buffers::VKBuffer > &buffer)
Initialize a buffer for use with the graphics backend.
void allocate_raw_buffer(size_t size_bytes, vk::BufferUsageFlags usage, vk::MemoryPropertyFlags memory_properties, bool host_visible, vk::Buffer &out_buffer, vk::DeviceMemory &out_memory, void *&out_mapped_ptr)
Allocate a raw VkBuffer/VkDeviceMemory pair without an owning VKBuffer object.
void setup_backend_service(const std::shared_ptr< Registry::Service::BufferService > &buffer_service)
vk::Semaphore flush_deferred_commands()
Flush deferred commands and return a semaphore that signals when they are complete.
void record_deferred_commands(const std::function< void(vk::CommandBuffer)> &recorder)
Record deferred command recording for buffer operations.
void upload_image_data(std::shared_ptr< VKImage > image, const void *data, size_t size)
Upload data to an image (creates staging buffer internally)
std::vector< std::shared_ptr< Buffers::VKBuffer > > m_managed_buffers
void reset_deferred()
Reset deferred command buffer and clear pending state.
void record_deferred_commands(const std::function< void(vk::CommandBuffer)> &recorder)
Record deferred commands for later submission.
void end_single_time_commands(vk::CommandBuffer command_buffer, vk::Queue queue)
End and submit single-time command.
bool has_deferred_commands() const
End and submit single-time command for compute operations.
void free_command_buffer(vk::CommandBuffer command_buffer)
Free a command buffer back to the pool.
vk::CommandBuffer get_deferred_cmd() const
Get the deferred command buffer (if any)
vk::CommandBuffer begin_single_time_commands()
Begin single-time command (for transfers, etc.)
Manages Vulkan command pools and command buffers.
vk::Device get_device() const
Get logical device.
vk::Queue get_graphics_queue() const
Get graphics queue.
vk::PhysicalDevice get_physical_device() const
Get physical device.
High-level wrapper for Vulkan instance and device.
@ GraphicsBackend
Graphics/visual rendering backend (Vulkan, OpenGL)
@ Core
Core engine, backend, subsystems.
@ IMAGE_COLOR
2D RGB/RGBA image
std::string_view modality_to_string(DataModality modality)
Convert DataModality enum to string representation.
Vulkan image resource handles.