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->destroy_buffer = [
this](
const std::shared_ptr<void>& vk_buf) {
34 auto buffer = std::static_pointer_cast<Buffers::VKBuffer>(vk_buf);
38 buffer_service->get_buffer_device_address = [
this](
const std::shared_ptr<void>& vk_buf) -> uint64_t {
39 auto buffer = std::static_pointer_cast<Buffers::VKBuffer>(vk_buf);
41 return static_cast<uint64_t
>(address);
44 buffer_service->execute_immediate = [
this](
const std::function<void(
void*)>& recorder) {
46 recorder(
static_cast<void*
>(
cmd));
50 buffer_service->record_deferred = [
this](
const std::function<void(
void*)>& recorder) {
52 recorder(
static_cast<void*
>(
cmd));
56 buffer_service->flush_range = [
this](
void* memory,
size_t offset,
size_t size) {
57 vk::DeviceMemory mem(
reinterpret_cast<VkDeviceMemory
>(memory));
58 vk::MappedMemoryRange range { mem, offset, size == 0 ? VK_WHOLE_SIZE : size };
59 if (
auto result =
m_context.
get_device().flushMappedMemoryRanges(1, &range); result != vk::Result::eSuccess) {
61 "Failed to flush mapped memory range: {}", vk::to_string(result));
65 buffer_service->invalidate_range = [
this](
void* memory,
size_t offset,
size_t size) {
66 vk::DeviceMemory mem(
reinterpret_cast<VkDeviceMemory
>(memory));
67 vk::MappedMemoryRange range { mem, offset, size == 0 ? VK_WHOLE_SIZE : size };
68 if (
auto result =
m_context.
get_device().invalidateMappedMemoryRanges(1, &range); result != vk::Result::eSuccess) {
70 "Failed to invalidate mapped memory range: {}", vk::to_string(result));
74 buffer_service->map_buffer = [
this](
void* memory,
size_t offset,
size_t size) ->
void* {
75 vk::DeviceMemory mem(
reinterpret_cast<VkDeviceMemory
>(memory));
79 buffer_service->unmap_buffer = [
this](
void* memory) {
80 vk::DeviceMemory mem(
reinterpret_cast<VkDeviceMemory
>(memory));
84 buffer_service->copy_buffer = [
this](
void* src,
void* dst,
size_t size,
size_t src_off,
size_t dst_off) {
85 vk::Buffer s(
static_cast<VkBuffer
>(src));
86 vk::Buffer d(
static_cast<VkBuffer
>(dst));
88 vk::BufferCopy region { src_off, dst_off, size };
89 cmd.copyBuffer(s, d, 1, ®ion);
93 buffer_service->execute_fenced = [
this](
const std::function<void(
void*)>& recorder)
94 -> std::shared_ptr<void> {
97 recorder(
static_cast<void*
>(
cmd));
102 vk::FenceCreateInfo fence_info {};
103 vk::Fence
fence = device.createFence(fence_info);
105 vk::SubmitInfo submit_info {};
106 submit_info.commandBufferCount = 1;
107 submit_info.pCommandBuffers = &
cmd;
110 result != vk::Result::eSuccess) {
112 "execute_fenced: queue submit failed: {}", vk::to_string(result));
113 device.destroyFence(
fence);
118 auto handle = std::make_shared<FencedSubmission>();
120 handle->fence =
fence;
124 buffer_service->wait_fenced = [
this](
const std::shared_ptr<void>& handle) {
127 auto sub = std::static_pointer_cast<FencedSubmission>(handle);
132 if (
auto result = device.waitForFences(1, &sub->fence, VK_TRUE, UINT64_MAX);
133 result != vk::Result::eSuccess) {
135 "wait_fenced: waitForFences failed: {}", vk::to_string(result));
139 buffer_service->release_fenced = [
this](
const std::shared_ptr<void>& handle) {
142 auto sub = std::static_pointer_cast<FencedSubmission>(handle);
146 device.destroyFence(sub->fence);
147 sub->fence =
nullptr;
155 buffer_service->copy_buffer_fenced = [
this, buffer_service](
void* src,
void* dst,
size_t size,
size_t src_off,
size_t dst_off)
156 -> std::shared_ptr<void> {
157 vk::Buffer s(
static_cast<VkBuffer
>(src));
158 vk::Buffer d(
static_cast<VkBuffer
>(dst));
159 return buffer_service->execute_fenced([&](
void* cmd_ptr) {
160 vk::CommandBuffer
cmd(
static_cast<VkCommandBuffer
>(cmd_ptr));
161 vk::BufferCopy region { src_off, dst_off,
static_cast<vk::DeviceSize
>(size) };
162 cmd.copyBuffer(s, d, 1, ®ion);
171 "Attempted to initialize null VulkanBuffer");
175 if (buffer->is_initialized()) {
177 "VulkanBuffer already initialized, skipping");
181 vk::BufferCreateInfo buffer_info {};
182 buffer_info.size = buffer->get_size_bytes();
183 buffer_info.usage = buffer->get_usage_flags();
184 buffer_info.sharingMode = vk::SharingMode::eExclusive;
186 vk::Buffer vk_buffer;
189 }
catch (
const vk::SystemError& e) {
193 std::source_location::current(),
194 "Failed to create VkBuffer: " + std::string(e.what()));
197 vk::MemoryRequirements mem_requirements;
200 vk::MemoryAllocateInfo alloc_info;
201 alloc_info.allocationSize = mem_requirements.size;
204 mem_requirements.memoryTypeBits,
205 vk::MemoryPropertyFlags(buffer->get_memory_properties()));
207 vk::DeviceMemory memory;
210 }
catch (
const vk::SystemError& e) {
215 std::source_location::current(),
216 "Failed to allocate VkDeviceMemory: " + std::string(e.what()));
221 }
catch (
const vk::SystemError& e) {
228 std::source_location::current(),
229 "Failed to bind buffer memory: " + std::string(e.what()));
232 void* mapped_ptr =
nullptr;
233 if (buffer->is_host_visible()) {
236 }
catch (
const vk::SystemError& e) {
243 std::source_location::current(),
244 "Failed to map buffer memory: " + std::string(e.what()));
249 buffer->set_buffer_resources(resources);
253 "VulkanBuffer initialized: {} bytes, modality: {}, VkBuffer: {:p}",
254 buffer->get_size_bytes(),
256 (
void*)buffer->get_buffer());
263 "Attempted to cleanup null VulkanBuffer");
272 auto& res = it->get()->get_buffer_resources();
274 if (res.mapped_ptr) {
278 if (res.index_buffer) {
282 if (res.index_memory) {
295 "VulkanBuffer cleaned up: {:p}",
static_cast<void*
>(res.buffer));
303 auto& resources = buffer_wrapper->get_buffer_resources();
304 auto dirty_ranges = buffer_wrapper->get_and_clear_dirty_ranges();
305 if (!dirty_ranges.empty()) {
306 for (
auto [offset, size] : dirty_ranges) {
307 vk::MappedMemoryRange range;
308 range.memory = resources.memory;
309 range.offset = offset;
311 if (
auto result =
m_context.
get_device().flushMappedMemoryRanges(1, &range); result != vk::Result::eSuccess) {
313 "Failed to flush mapped memory range: {}", vk::to_string(result));
317 "Flushed {} dirty ranges for buffer {:p}", dirty_ranges.size(),
318 (
void*)buffer_wrapper->get_buffer());
321 auto invalid_ranges = buffer_wrapper->get_and_clear_invalid_ranges();
322 if (!invalid_ranges.empty()) {
323 for (
auto [offset, size] : invalid_ranges) {
324 vk::MappedMemoryRange range;
325 range.memory = buffer_wrapper->get_buffer_resources().memory;
326 range.offset = offset;
328 if (
auto result =
m_context.
get_device().invalidateMappedMemoryRanges(1, &range); result != vk::Result::eSuccess) {
330 "Failed to invalidate mapped memory range: {}", vk::to_string(result));
334 "Invalidated {} ranges for buffer {:p}", invalid_ranges.size(),
335 (
void*)buffer_wrapper->get_buffer());
341 const std::shared_ptr<Buffers::VKBuffer>& buffer)
const
343 if (!buffer || !buffer->is_initialized()) {
345 "get_buffer_device_address: buffer not initialized");
349 vk::BufferDeviceAddressInfo info {};
350 info.buffer = buffer->get_buffer();
359 "Attempted to initialize null VKImage");
363 if (
image->is_initialized()) {
365 "VKImage already initialized, skipping");
373 vk::ImageCreateInfo image_info {};
375 switch (
image->get_type()) {
377 image_info.imageType = vk::ImageType::e1D;
381 image_info.imageType = vk::ImageType::e2D;
384 image_info.imageType = vk::ImageType::e3D;
388 image_info.extent.width =
image->get_width();
389 image_info.extent.height =
image->get_height();
390 image_info.extent.depth =
image->get_depth();
391 image_info.mipLevels =
image->get_mip_levels();
392 image_info.arrayLayers =
image->get_array_layers();
393 image_info.format =
image->get_format();
394 image_info.tiling = vk::ImageTiling::eOptimal;
395 image_info.initialLayout = vk::ImageLayout::eUndefined;
396 image_info.usage =
image->get_usage_flags();
397 image_info.sharingMode = vk::SharingMode::eExclusive;
398 image_info.samples = vk::SampleCountFlagBits::e1;
400 ? vk::ImageCreateFlagBits::eCubeCompatible
401 : vk::ImageCreateFlags {};
406 }
catch (
const vk::SystemError& e) {
410 std::source_location::current(),
411 "Failed to create VkImage: " + std::string(e.what()));
418 vk::MemoryRequirements mem_requirements;
421 vk::MemoryAllocateInfo alloc_info {};
422 alloc_info.allocationSize = mem_requirements.size;
424 mem_requirements.memoryTypeBits,
425 image->get_memory_properties());
427 vk::DeviceMemory memory;
430 }
catch (
const vk::SystemError& e) {
435 std::source_location::current(),
436 "Failed to allocate VkDeviceMemory for image: " + std::string(e.what()));
445 }
catch (
const vk::SystemError& e) {
451 std::source_location::current(),
452 "Failed to bind memory to VkImage: " + std::string(e.what()));
459 vk::ImageViewCreateInfo view_info {};
461 switch (
image->get_type()) {
463 view_info.viewType = (
image->get_array_layers() > 1)
464 ? vk::ImageViewType::e1DArray
465 : vk::ImageViewType::e1D;
468 view_info.viewType = (
image->get_array_layers() > 1)
469 ? vk::ImageViewType::e2DArray
470 : vk::ImageViewType::e2D;
473 view_info.viewType = vk::ImageViewType::e3D;
476 view_info.viewType = vk::ImageViewType::eCube;
480 view_info.image = vk_image;
481 view_info.format =
image->get_format();
482 view_info.subresourceRange.aspectMask =
image->get_aspect_flags();
483 view_info.subresourceRange.baseMipLevel = 0;
484 view_info.subresourceRange.levelCount =
image->get_mip_levels();
485 view_info.subresourceRange.baseArrayLayer = 0;
486 view_info.subresourceRange.layerCount =
image->get_array_layers();
488 view_info.components.r = vk::ComponentSwizzle::eIdentity;
489 view_info.components.g = vk::ComponentSwizzle::eIdentity;
490 view_info.components.b = vk::ComponentSwizzle::eIdentity;
491 view_info.components.a = vk::ComponentSwizzle::eIdentity;
493 vk::ImageView image_view;
496 }
catch (
const vk::SystemError& e) {
502 std::source_location::current(),
503 "Failed to create VkImageView: " + std::string(e.what()));
511 resources.
image = vk_image;
512 resources.image_view = image_view;
513 resources.memory = memory;
514 resources.sampler =
nullptr;
516 image->set_image_resources(resources);
517 image->set_current_layout(vk::ImageLayout::eUndefined);
520 "VKImage initialized: {}x{}x{}, format: {}, {} mips, {} layers",
522 vk::to_string(
image->get_format()),
523 image->get_mip_levels(),
image->get_array_layers());
532 const auto& resources =
image->get_image_resources();
534 if (resources.image_view) {
538 if (resources.image) {
542 if (resources.memory) {
547 "VKImage cleaned up");
552 vk::ImageLayout old_layout,
553 vk::ImageLayout new_layout,
555 uint32_t array_layers,
556 vk::ImageAspectFlags aspect_flags)
559 vk::ImageMemoryBarrier barrier {};
560 barrier.oldLayout = old_layout;
561 barrier.newLayout = new_layout;
562 barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
563 barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
564 barrier.image =
image;
565 barrier.subresourceRange.aspectMask = aspect_flags;
566 barrier.subresourceRange.baseMipLevel = 0;
567 barrier.subresourceRange.levelCount = mip_levels;
568 barrier.subresourceRange.baseArrayLayer = 0;
569 barrier.subresourceRange.layerCount = array_layers;
571 vk::PipelineStageFlags src_stage;
572 vk::PipelineStageFlags dst_stage;
574 if (old_layout == vk::ImageLayout::eUndefined && new_layout == vk::ImageLayout::eTransferDstOptimal) {
575 barrier.srcAccessMask = vk::AccessFlags {};
576 barrier.dstAccessMask = vk::AccessFlagBits::eTransferWrite;
577 src_stage = vk::PipelineStageFlagBits::eTopOfPipe;
578 dst_stage = vk::PipelineStageFlagBits::eTransfer;
579 }
else if (old_layout == vk::ImageLayout::eTransferDstOptimal && new_layout == vk::ImageLayout::eShaderReadOnlyOptimal) {
580 barrier.srcAccessMask = vk::AccessFlagBits::eTransferWrite;
581 barrier.dstAccessMask = vk::AccessFlagBits::eShaderRead;
582 src_stage = vk::PipelineStageFlagBits::eTransfer;
583 dst_stage = vk::PipelineStageFlagBits::eFragmentShader;
584 }
else if (old_layout == vk::ImageLayout::eUndefined && new_layout == vk::ImageLayout::eShaderReadOnlyOptimal) {
585 barrier.srcAccessMask = vk::AccessFlags {};
586 barrier.dstAccessMask = vk::AccessFlagBits::eShaderRead;
587 src_stage = vk::PipelineStageFlagBits::eTopOfPipe;
588 dst_stage = vk::PipelineStageFlagBits::eFragmentShader;
589 }
else if (old_layout == vk::ImageLayout::eUndefined && new_layout == vk::ImageLayout::eColorAttachmentOptimal) {
590 barrier.srcAccessMask = vk::AccessFlags {};
591 barrier.dstAccessMask = vk::AccessFlagBits::eColorAttachmentWrite;
592 src_stage = vk::PipelineStageFlagBits::eTopOfPipe;
593 dst_stage = vk::PipelineStageFlagBits::eColorAttachmentOutput;
594 }
else if (old_layout == vk::ImageLayout::eUndefined && new_layout == vk::ImageLayout::eDepthStencilAttachmentOptimal) {
595 barrier.srcAccessMask = vk::AccessFlags {};
596 barrier.dstAccessMask = vk::AccessFlagBits::eDepthStencilAttachmentRead | vk::AccessFlagBits::eDepthStencilAttachmentWrite;
597 src_stage = vk::PipelineStageFlagBits::eTopOfPipe;
598 dst_stage = vk::PipelineStageFlagBits::eEarlyFragmentTests;
599 }
else if (old_layout == vk::ImageLayout::eUndefined && new_layout == vk::ImageLayout::eGeneral) {
600 barrier.srcAccessMask = vk::AccessFlags {};
601 barrier.dstAccessMask = vk::AccessFlagBits::eShaderRead | vk::AccessFlagBits::eShaderWrite;
602 src_stage = vk::PipelineStageFlagBits::eTopOfPipe;
603 dst_stage = vk::PipelineStageFlagBits::eComputeShader;
605 barrier.srcAccessMask = vk::AccessFlagBits::eMemoryRead | vk::AccessFlagBits::eMemoryWrite;
606 barrier.dstAccessMask = vk::AccessFlagBits::eMemoryRead | vk::AccessFlagBits::eMemoryWrite;
607 src_stage = vk::PipelineStageFlagBits::eAllCommands;
608 dst_stage = vk::PipelineStageFlagBits::eAllCommands;
611 "Using generic image layout transition");
615 src_stage, dst_stage,
616 vk::DependencyFlags {},
624 "Image layout transitioned: {} -> {}",
625 vk::to_string(old_layout), vk::to_string(new_layout));
629 std::shared_ptr<VKImage>
image,
633 if (!
image || !data) {
635 "Invalid parameters for upload_image_data");
639 auto staging = std::make_shared<Buffers::VKBuffer>(
646 void* mapped = staging->get_mapped_ptr();
649 "Failed to map staging buffer for image upload");
654 std::memcpy(mapped, data, size);
655 staging->mark_dirty_range(0, size);
657 auto& resources = staging->get_buffer_resources();
658 vk::MappedMemoryRange range { resources.memory, 0, VK_WHOLE_SIZE };
660 if (
auto result =
m_context.
get_device().flushMappedMemoryRanges(1, &range); result != vk::Result::eSuccess) {
662 "Failed to flush mapped memory range: {}", vk::to_string(result));
666 vk::ImageMemoryBarrier barrier {};
667 barrier.oldLayout =
image->get_current_layout();
668 barrier.newLayout = vk::ImageLayout::eTransferDstOptimal;
669 barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
670 barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
671 barrier.image =
image->get_image();
672 barrier.subresourceRange.aspectMask =
image->get_aspect_flags();
673 barrier.subresourceRange.baseMipLevel = 0;
674 barrier.subresourceRange.levelCount =
image->get_mip_levels();
675 barrier.subresourceRange.baseArrayLayer = 0;
676 barrier.subresourceRange.layerCount =
image->get_array_layers();
677 barrier.srcAccessMask = vk::AccessFlagBits::eShaderRead;
678 barrier.dstAccessMask = vk::AccessFlagBits::eTransferWrite;
681 vk::PipelineStageFlagBits::eFragmentShader,
682 vk::PipelineStageFlagBits::eTransfer,
683 vk::DependencyFlags {},
684 0,
nullptr, 0,
nullptr, 1, &barrier);
686 vk::BufferImageCopy region {};
687 region.bufferOffset = 0;
688 region.bufferRowLength = 0;
689 region.bufferImageHeight = 0;
690 region.imageSubresource.aspectMask =
image->get_aspect_flags();
691 region.imageSubresource.mipLevel = 0;
692 region.imageSubresource.baseArrayLayer = 0;
693 region.imageSubresource.layerCount =
image->get_array_layers();
694 region.imageOffset = vk::Offset3D { 0, 0, 0 };
695 region.imageExtent = vk::Extent3D {
701 cmd.copyBufferToImage(
702 staging->get_buffer(),
704 vk::ImageLayout::eTransferDstOptimal,
707 barrier.oldLayout = vk::ImageLayout::eTransferDstOptimal;
708 barrier.newLayout = vk::ImageLayout::eShaderReadOnlyOptimal;
709 barrier.srcAccessMask = vk::AccessFlagBits::eTransferWrite;
710 barrier.dstAccessMask = vk::AccessFlagBits::eShaderRead;
713 vk::PipelineStageFlagBits::eTransfer,
714 vk::PipelineStageFlagBits::eFragmentShader,
715 vk::DependencyFlags {},
716 0,
nullptr, 0,
nullptr, 1, &barrier);
719 image->set_current_layout(vk::ImageLayout::eShaderReadOnlyOptimal);
722 "Uploaded {} bytes to image {}x{}",
723 size,
image->get_width(),
image->get_height());
727 std::shared_ptr<VKImage>
image,
730 const std::shared_ptr<Buffers::VKBuffer>& staging,
bool deferred)
732 if (!
image || !data || !staging) {
734 "Invalid parameters for upload_image_data_with_staging");
738 void* mapped = staging->get_mapped_ptr();
741 "upload_image_data_with_staging: staging buffer has no mapped pointer");
745 std::memcpy(mapped, data, size);
746 staging->mark_dirty_range(0, size);
748 auto& resources = staging->get_buffer_resources();
749 vk::MappedMemoryRange range { resources.memory, 0, VK_WHOLE_SIZE };
752 result != vk::Result::eSuccess) {
754 "upload_image_data_with_staging: flush failed: {}", vk::to_string(result));
757 auto record_command = [&](vk::CommandBuffer
cmd) {
758 vk::ImageMemoryBarrier barrier {};
759 barrier.oldLayout =
image->get_current_layout();
760 barrier.newLayout = vk::ImageLayout::eTransferDstOptimal;
761 barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
762 barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
763 barrier.image =
image->get_image();
764 barrier.subresourceRange = {
765 image->get_aspect_flags(), 0,
766 image->get_mip_levels(), 0,
767 image->get_array_layers()
769 barrier.srcAccessMask = vk::AccessFlagBits::eShaderRead;
770 barrier.dstAccessMask = vk::AccessFlagBits::eTransferWrite;
773 vk::PipelineStageFlagBits::eFragmentShader,
774 vk::PipelineStageFlagBits::eTransfer,
775 {}, 0,
nullptr, 0,
nullptr, 1, &barrier);
777 vk::BufferImageCopy region {};
778 region.imageSubresource.aspectMask =
image->get_aspect_flags();
779 region.imageSubresource.layerCount =
image->get_array_layers();
780 region.imageOffset = vk::Offset3D { 0, 0, 0 };
781 region.imageExtent = vk::Extent3D {
787 cmd.copyBufferToImage(
788 staging->get_buffer(),
790 vk::ImageLayout::eTransferDstOptimal,
793 barrier.oldLayout = vk::ImageLayout::eTransferDstOptimal;
794 barrier.newLayout = vk::ImageLayout::eShaderReadOnlyOptimal;
795 barrier.srcAccessMask = vk::AccessFlagBits::eTransferWrite;
796 barrier.dstAccessMask = vk::AccessFlagBits::eShaderRead;
799 vk::PipelineStageFlagBits::eTransfer,
800 vk::PipelineStageFlagBits::eFragmentShader,
801 {}, 0,
nullptr, 0,
nullptr, 1, &barrier);
810 image->set_current_layout(vk::ImageLayout::eShaderReadOnlyOptimal);
813 "upload_image_data_with_staging: {} bytes to image {}x{}",
814 size,
image->get_width(),
image->get_height());
818 std::shared_ptr<VKImage>
image,
821 vk::ImageLayout restore_layout,
822 vk::PipelineStageFlags restore_stage)
824 if (!
image || !data) {
826 "Invalid parameters for download_image_data");
830 auto staging = std::make_shared<Buffers::VKBuffer>(
838 vk::ImageMemoryBarrier barrier {};
839 barrier.oldLayout =
image->get_current_layout();
840 barrier.newLayout = vk::ImageLayout::eTransferSrcOptimal;
841 barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
842 barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
843 barrier.image =
image->get_image();
844 barrier.subresourceRange.aspectMask =
image->get_aspect_flags();
845 barrier.subresourceRange.baseMipLevel = 0;
846 barrier.subresourceRange.levelCount =
image->get_mip_levels();
847 barrier.subresourceRange.baseArrayLayer = 0;
848 barrier.subresourceRange.layerCount =
image->get_array_layers();
849 barrier.srcAccessMask = vk::AccessFlagBits::eShaderRead;
850 barrier.dstAccessMask = vk::AccessFlagBits::eTransferRead;
853 vk::PipelineStageFlagBits::eFragmentShader,
854 vk::PipelineStageFlagBits::eTransfer,
855 vk::DependencyFlags {}, {}, {}, barrier);
857 vk::BufferImageCopy region {};
858 region.bufferOffset = 0;
859 region.bufferRowLength = 0;
860 region.bufferImageHeight = 0;
861 region.imageSubresource.aspectMask =
image->get_aspect_flags();
862 region.imageSubresource.mipLevel = 0;
863 region.imageSubresource.baseArrayLayer = 0;
864 region.imageSubresource.layerCount =
image->get_array_layers();
865 region.imageOffset = vk::Offset3D { 0, 0, 0 };
866 region.imageExtent = vk::Extent3D {
872 cmd.copyImageToBuffer(
874 vk::ImageLayout::eTransferSrcOptimal,
875 staging->get_buffer(),
878 barrier.oldLayout = vk::ImageLayout::eTransferSrcOptimal;
879 barrier.newLayout = restore_layout;
880 barrier.srcAccessMask = vk::AccessFlagBits::eTransferRead;
881 barrier.dstAccessMask = vk::AccessFlagBits::eMemoryRead;
884 vk::PipelineStageFlagBits::eTransfer,
886 vk::DependencyFlags {}, {}, {}, barrier);
889 staging->mark_invalid_range(0, size);
890 auto& resources = staging->get_buffer_resources();
891 vk::MappedMemoryRange range { resources.memory, 0, VK_WHOLE_SIZE };
894 result != vk::Result::eSuccess) {
896 "Failed to invalidate mapped memory range: {}", vk::to_string(result));
899 if (
void* mapped = staging->get_mapped_ptr()) {
900 std::memcpy(data, mapped, size);
906 "Downloaded {} bytes from image {}x{}",
907 size,
image->get_width(),
image->get_height());
912 vk::SamplerAddressMode address_mode,
913 float max_anisotropy)
916 auto hash_combine = [](
size_t& seed,
size_t value) {
917 seed ^= value + 0x9e3779b9 + (seed << 6) + (seed >> 2);
920 hash_combine(hash,
static_cast<size_t>(filter));
921 hash_combine(hash,
static_cast<size_t>(address_mode));
922 hash_combine(hash, std::hash<float> {}(max_anisotropy));
927 "Reusing cached sampler (hash: 0x{:X})", hash);
931 vk::SamplerCreateInfo sampler_info;
932 sampler_info.magFilter = filter;
933 sampler_info.minFilter = filter;
934 sampler_info.mipmapMode = vk::SamplerMipmapMode::eLinear;
935 sampler_info.addressModeU = address_mode;
936 sampler_info.addressModeV = address_mode;
937 sampler_info.addressModeW = address_mode;
938 sampler_info.mipLodBias = 0.0F;
939 sampler_info.anisotropyEnable = max_anisotropy > 0.0F;
940 sampler_info.maxAnisotropy = max_anisotropy;
941 sampler_info.compareEnable = VK_FALSE;
942 sampler_info.compareOp = vk::CompareOp::eAlways;
943 sampler_info.minLod = 0.0F;
944 sampler_info.maxLod = VK_LOD_CLAMP_NONE;
945 sampler_info.borderColor = vk::BorderColor::eFloatOpaqueBlack;
946 sampler_info.unnormalizedCoordinates = VK_FALSE;
951 }
catch (
const vk::SystemError& e) {
953 "Failed to create sampler: {}", e.what());
960 "Created sampler (filter: {}, address: {}, anisotropy: {}, hash: 0x{:X})",
961 vk::to_string(filter), vk::to_string(address_mode), max_anisotropy, hash);
973 if (it->second == sampler) {
982 "Destroyed sampler");
987 vk::PhysicalDeviceMemoryProperties mem_properties;
990 for (uint32_t i = 0; i < mem_properties.memoryTypeCount; i++) {
991 if ((type_filter & (1 << i)) && (mem_properties.memoryTypes[i].propertyFlags & properties) == properties) {
996 error<std::runtime_error>(
999 std::source_location::current(),
1000 "Failed to find suitable memory type");
1026 vk::SemaphoreCreateInfo sem_info {};
1030 vk::SubmitInfo submit {};
1031 submit.commandBufferCount = 1;
1032 submit.pCommandBuffers = &
cmd;
1033 submit.signalSemaphoreCount = 1;
1038 "Failed to submit deferred command buffer");
1061 if (buffer && buffer->is_initialized()) {
1071 auto hash_combine = [](
size_t& seed,
size_t value) {
1072 seed ^= value + 0x9e3779b9 + (seed << 6) + (seed >> 2);
1075 hash_combine(hash,
static_cast<size_t>(filter));
1076 hash_combine(hash,
static_cast<size_t>(address_mode));
1077 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,...)
@ STAGING
Host-visible staging buffer (CPU-writable, eTransferSrc|Dst)
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 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.
void download_image_data(std::shared_ptr< VKImage > image, void *data, size_t size, vk::ImageLayout restore_layout=vk::ImageLayout::eShaderReadOnlyOptimal, vk::PipelineStageFlags restore_stage=vk::PipelineStageFlagBits::eFragmentShader)
Download data from an image into a caller-supplied buffer.
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 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.
Raw Vulkan handles owned by a VKBuffer instance.
Vulkan image resource handles.