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->execute_fenced = [
this](
const std::function<void(
void*)>& recorder)
85 -> std::shared_ptr<void> {
88 recorder(
static_cast<void*
>(
cmd));
93 vk::FenceCreateInfo fence_info {};
94 vk::Fence
fence = device.createFence(fence_info);
96 vk::SubmitInfo submit_info {};
97 submit_info.commandBufferCount = 1;
98 submit_info.pCommandBuffers = &
cmd;
101 result != vk::Result::eSuccess) {
103 "execute_fenced: queue submit failed: {}", vk::to_string(result));
104 device.destroyFence(
fence);
109 auto handle = std::make_shared<FencedSubmission>();
111 handle->fence =
fence;
115 buffer_service->wait_fenced = [
this](
const std::shared_ptr<void>& handle) {
118 auto sub = std::static_pointer_cast<FencedSubmission>(handle);
123 if (
auto result = device.waitForFences(1, &sub->fence, VK_TRUE, UINT64_MAX);
124 result != vk::Result::eSuccess) {
126 "wait_fenced: waitForFences failed: {}", vk::to_string(result));
130 buffer_service->release_fenced = [
this](
const std::shared_ptr<void>& handle) {
133 auto sub = std::static_pointer_cast<FencedSubmission>(handle);
137 device.destroyFence(sub->fence);
138 sub->fence =
nullptr;
151 "Attempted to initialize null VulkanBuffer");
155 if (buffer->is_initialized()) {
157 "VulkanBuffer already initialized, skipping");
161 vk::BufferCreateInfo buffer_info {};
162 buffer_info.size = buffer->get_size_bytes();
163 buffer_info.usage = buffer->get_usage_flags();
164 buffer_info.sharingMode = vk::SharingMode::eExclusive;
166 vk::Buffer vk_buffer;
169 }
catch (
const vk::SystemError& e) {
173 std::source_location::current(),
174 "Failed to create VkBuffer: " + std::string(e.what()));
177 vk::MemoryRequirements mem_requirements;
180 vk::MemoryAllocateInfo alloc_info;
181 alloc_info.allocationSize = mem_requirements.size;
184 mem_requirements.memoryTypeBits,
185 vk::MemoryPropertyFlags(buffer->get_memory_properties()));
187 vk::DeviceMemory memory;
190 }
catch (
const vk::SystemError& e) {
195 std::source_location::current(),
196 "Failed to allocate VkDeviceMemory: " + std::string(e.what()));
201 }
catch (
const vk::SystemError& e) {
208 std::source_location::current(),
209 "Failed to bind buffer memory: " + std::string(e.what()));
212 void* mapped_ptr =
nullptr;
213 if (buffer->is_host_visible()) {
216 }
catch (
const vk::SystemError& e) {
223 std::source_location::current(),
224 "Failed to map buffer memory: " + std::string(e.what()));
229 buffer->set_buffer_resources(resources);
233 "VulkanBuffer initialized: {} bytes, modality: {}, VkBuffer: {:p}",
234 buffer->get_size_bytes(),
236 (
void*)buffer->get_buffer());
243 "Attempted to cleanup null VulkanBuffer");
252 auto& res = it->get()->get_buffer_resources();
254 if (res.mapped_ptr) {
258 if (res.index_buffer) {
262 if (res.index_memory) {
275 "VulkanBuffer cleaned up: {:p}",
static_cast<void*
>(res.buffer));
283 auto& resources = buffer_wrapper->get_buffer_resources();
284 auto dirty_ranges = buffer_wrapper->get_and_clear_dirty_ranges();
285 if (!dirty_ranges.empty()) {
286 for (
auto [offset,
size] : dirty_ranges) {
287 vk::MappedMemoryRange range;
288 range.memory = resources.memory;
289 range.offset = offset;
291 if (
auto result =
m_context.
get_device().flushMappedMemoryRanges(1, &range); result != vk::Result::eSuccess) {
293 "Failed to flush mapped memory range: {}", vk::to_string(result));
297 "Flushed {} dirty ranges for buffer {:p}", dirty_ranges.size(),
298 (
void*)buffer_wrapper->get_buffer());
301 auto invalid_ranges = buffer_wrapper->get_and_clear_invalid_ranges();
302 if (!invalid_ranges.empty()) {
303 for (
auto [offset,
size] : invalid_ranges) {
304 vk::MappedMemoryRange range;
305 range.memory = buffer_wrapper->get_buffer_resources().memory;
306 range.offset = offset;
308 if (
auto result =
m_context.
get_device().invalidateMappedMemoryRanges(1, &range); result != vk::Result::eSuccess) {
310 "Failed to invalidate mapped memory range: {}", vk::to_string(result));
314 "Invalidated {} ranges for buffer {:p}", invalid_ranges.size(),
315 (
void*)buffer_wrapper->get_buffer());
321 const std::shared_ptr<Buffers::VKBuffer>& buffer)
const
323 if (!buffer || !buffer->is_initialized()) {
325 "get_buffer_device_address: buffer not initialized");
329 vk::BufferDeviceAddressInfo info {};
330 info.buffer = buffer->get_buffer();
339 "Attempted to initialize null VKImage");
343 if (
image->is_initialized()) {
345 "VKImage already initialized, skipping");
353 vk::ImageCreateInfo image_info {};
355 switch (
image->get_type()) {
357 image_info.imageType = vk::ImageType::e1D;
361 image_info.imageType = vk::ImageType::e2D;
364 image_info.imageType = vk::ImageType::e3D;
368 image_info.extent.width =
image->get_width();
369 image_info.extent.height =
image->get_height();
370 image_info.extent.depth =
image->get_depth();
371 image_info.mipLevels =
image->get_mip_levels();
372 image_info.arrayLayers =
image->get_array_layers();
373 image_info.format =
image->get_format();
374 image_info.tiling = vk::ImageTiling::eOptimal;
375 image_info.initialLayout = vk::ImageLayout::eUndefined;
376 image_info.usage =
image->get_usage_flags();
377 image_info.sharingMode = vk::SharingMode::eExclusive;
378 image_info.samples = vk::SampleCountFlagBits::e1;
380 ? vk::ImageCreateFlagBits::eCubeCompatible
381 : vk::ImageCreateFlags {};
386 }
catch (
const vk::SystemError& e) {
390 std::source_location::current(),
391 "Failed to create VkImage: " + std::string(e.what()));
398 vk::MemoryRequirements mem_requirements;
401 vk::MemoryAllocateInfo alloc_info {};
402 alloc_info.allocationSize = mem_requirements.size;
404 mem_requirements.memoryTypeBits,
405 image->get_memory_properties());
407 vk::DeviceMemory memory;
410 }
catch (
const vk::SystemError& e) {
415 std::source_location::current(),
416 "Failed to allocate VkDeviceMemory for image: " + std::string(e.what()));
425 }
catch (
const vk::SystemError& e) {
431 std::source_location::current(),
432 "Failed to bind memory to VkImage: " + std::string(e.what()));
439 vk::ImageViewCreateInfo view_info {};
441 switch (
image->get_type()) {
443 view_info.viewType = (
image->get_array_layers() > 1)
444 ? vk::ImageViewType::e1DArray
445 : vk::ImageViewType::e1D;
448 view_info.viewType = (
image->get_array_layers() > 1)
449 ? vk::ImageViewType::e2DArray
450 : vk::ImageViewType::e2D;
453 view_info.viewType = vk::ImageViewType::e3D;
456 view_info.viewType = vk::ImageViewType::eCube;
460 view_info.image = vk_image;
461 view_info.format =
image->get_format();
462 view_info.subresourceRange.aspectMask =
image->get_aspect_flags();
463 view_info.subresourceRange.baseMipLevel = 0;
464 view_info.subresourceRange.levelCount =
image->get_mip_levels();
465 view_info.subresourceRange.baseArrayLayer = 0;
466 view_info.subresourceRange.layerCount =
image->get_array_layers();
468 view_info.components.r = vk::ComponentSwizzle::eIdentity;
469 view_info.components.g = vk::ComponentSwizzle::eIdentity;
470 view_info.components.b = vk::ComponentSwizzle::eIdentity;
471 view_info.components.a = vk::ComponentSwizzle::eIdentity;
473 vk::ImageView image_view;
476 }
catch (
const vk::SystemError& e) {
482 std::source_location::current(),
483 "Failed to create VkImageView: " + std::string(e.what()));
491 resources.
image = vk_image;
492 resources.image_view = image_view;
493 resources.memory = memory;
494 resources.sampler =
nullptr;
496 image->set_image_resources(resources);
497 image->set_current_layout(vk::ImageLayout::eUndefined);
500 "VKImage initialized: {}x{}x{}, format: {}, {} mips, {} layers",
502 vk::to_string(
image->get_format()),
503 image->get_mip_levels(),
image->get_array_layers());
512 const auto& resources =
image->get_image_resources();
514 if (resources.image_view) {
518 if (resources.image) {
522 if (resources.memory) {
527 "VKImage cleaned up");
532 vk::ImageLayout old_layout,
533 vk::ImageLayout new_layout,
535 uint32_t array_layers,
536 vk::ImageAspectFlags aspect_flags)
539 vk::ImageMemoryBarrier barrier {};
540 barrier.oldLayout = old_layout;
541 barrier.newLayout = new_layout;
542 barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
543 barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
544 barrier.image =
image;
545 barrier.subresourceRange.aspectMask = aspect_flags;
546 barrier.subresourceRange.baseMipLevel = 0;
547 barrier.subresourceRange.levelCount = mip_levels;
548 barrier.subresourceRange.baseArrayLayer = 0;
549 barrier.subresourceRange.layerCount = array_layers;
551 vk::PipelineStageFlags src_stage;
552 vk::PipelineStageFlags dst_stage;
554 if (old_layout == vk::ImageLayout::eUndefined && new_layout == vk::ImageLayout::eTransferDstOptimal) {
555 barrier.srcAccessMask = vk::AccessFlags {};
556 barrier.dstAccessMask = vk::AccessFlagBits::eTransferWrite;
557 src_stage = vk::PipelineStageFlagBits::eTopOfPipe;
558 dst_stage = vk::PipelineStageFlagBits::eTransfer;
559 }
else if (old_layout == vk::ImageLayout::eTransferDstOptimal && new_layout == vk::ImageLayout::eShaderReadOnlyOptimal) {
560 barrier.srcAccessMask = vk::AccessFlagBits::eTransferWrite;
561 barrier.dstAccessMask = vk::AccessFlagBits::eShaderRead;
562 src_stage = vk::PipelineStageFlagBits::eTransfer;
563 dst_stage = vk::PipelineStageFlagBits::eFragmentShader;
564 }
else if (old_layout == vk::ImageLayout::eUndefined && new_layout == vk::ImageLayout::eShaderReadOnlyOptimal) {
565 barrier.srcAccessMask = vk::AccessFlags {};
566 barrier.dstAccessMask = vk::AccessFlagBits::eShaderRead;
567 src_stage = vk::PipelineStageFlagBits::eTopOfPipe;
568 dst_stage = vk::PipelineStageFlagBits::eFragmentShader;
569 }
else if (old_layout == vk::ImageLayout::eUndefined && new_layout == vk::ImageLayout::eColorAttachmentOptimal) {
570 barrier.srcAccessMask = vk::AccessFlags {};
571 barrier.dstAccessMask = vk::AccessFlagBits::eColorAttachmentWrite;
572 src_stage = vk::PipelineStageFlagBits::eTopOfPipe;
573 dst_stage = vk::PipelineStageFlagBits::eColorAttachmentOutput;
574 }
else if (old_layout == vk::ImageLayout::eUndefined && new_layout == vk::ImageLayout::eDepthStencilAttachmentOptimal) {
575 barrier.srcAccessMask = vk::AccessFlags {};
576 barrier.dstAccessMask = vk::AccessFlagBits::eDepthStencilAttachmentRead | vk::AccessFlagBits::eDepthStencilAttachmentWrite;
577 src_stage = vk::PipelineStageFlagBits::eTopOfPipe;
578 dst_stage = vk::PipelineStageFlagBits::eEarlyFragmentTests;
579 }
else if (old_layout == vk::ImageLayout::eUndefined && new_layout == vk::ImageLayout::eGeneral) {
580 barrier.srcAccessMask = vk::AccessFlags {};
581 barrier.dstAccessMask = vk::AccessFlagBits::eShaderRead | vk::AccessFlagBits::eShaderWrite;
582 src_stage = vk::PipelineStageFlagBits::eTopOfPipe;
583 dst_stage = vk::PipelineStageFlagBits::eComputeShader;
585 barrier.srcAccessMask = vk::AccessFlagBits::eMemoryRead | vk::AccessFlagBits::eMemoryWrite;
586 barrier.dstAccessMask = vk::AccessFlagBits::eMemoryRead | vk::AccessFlagBits::eMemoryWrite;
587 src_stage = vk::PipelineStageFlagBits::eAllCommands;
588 dst_stage = vk::PipelineStageFlagBits::eAllCommands;
591 "Using generic image layout transition");
595 src_stage, dst_stage,
596 vk::DependencyFlags {},
604 "Image layout transitioned: {} -> {}",
605 vk::to_string(old_layout), vk::to_string(new_layout));
609 std::shared_ptr<VKImage>
image,
613 if (!
image || !data) {
615 "Invalid parameters for upload_image_data");
619 auto staging = std::make_shared<Buffers::VKBuffer>(
626 void* mapped = staging->get_mapped_ptr();
629 "Failed to map staging buffer for image upload");
634 std::memcpy(mapped, data,
size);
635 staging->mark_dirty_range(0,
size);
637 auto& resources = staging->get_buffer_resources();
638 vk::MappedMemoryRange range { resources.memory, 0, VK_WHOLE_SIZE };
640 if (
auto result =
m_context.
get_device().flushMappedMemoryRanges(1, &range); result != vk::Result::eSuccess) {
642 "Failed to flush mapped memory range: {}", vk::to_string(result));
646 vk::ImageMemoryBarrier barrier {};
647 barrier.oldLayout =
image->get_current_layout();
648 barrier.newLayout = vk::ImageLayout::eTransferDstOptimal;
649 barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
650 barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
651 barrier.image =
image->get_image();
652 barrier.subresourceRange.aspectMask =
image->get_aspect_flags();
653 barrier.subresourceRange.baseMipLevel = 0;
654 barrier.subresourceRange.levelCount =
image->get_mip_levels();
655 barrier.subresourceRange.baseArrayLayer = 0;
656 barrier.subresourceRange.layerCount =
image->get_array_layers();
657 barrier.srcAccessMask = vk::AccessFlagBits::eShaderRead;
658 barrier.dstAccessMask = vk::AccessFlagBits::eTransferWrite;
661 vk::PipelineStageFlagBits::eFragmentShader,
662 vk::PipelineStageFlagBits::eTransfer,
663 vk::DependencyFlags {},
664 0,
nullptr, 0,
nullptr, 1, &barrier);
666 vk::BufferImageCopy region {};
667 region.bufferOffset = 0;
668 region.bufferRowLength = 0;
669 region.bufferImageHeight = 0;
670 region.imageSubresource.aspectMask =
image->get_aspect_flags();
671 region.imageSubresource.mipLevel = 0;
672 region.imageSubresource.baseArrayLayer = 0;
673 region.imageSubresource.layerCount =
image->get_array_layers();
674 region.imageOffset = vk::Offset3D { 0, 0, 0 };
675 region.imageExtent = vk::Extent3D {
681 cmd.copyBufferToImage(
682 staging->get_buffer(),
684 vk::ImageLayout::eTransferDstOptimal,
687 barrier.oldLayout = vk::ImageLayout::eTransferDstOptimal;
688 barrier.newLayout = vk::ImageLayout::eShaderReadOnlyOptimal;
689 barrier.srcAccessMask = vk::AccessFlagBits::eTransferWrite;
690 barrier.dstAccessMask = vk::AccessFlagBits::eShaderRead;
693 vk::PipelineStageFlagBits::eTransfer,
694 vk::PipelineStageFlagBits::eFragmentShader,
695 vk::DependencyFlags {},
696 0,
nullptr, 0,
nullptr, 1, &barrier);
699 image->set_current_layout(vk::ImageLayout::eShaderReadOnlyOptimal);
702 "Uploaded {} bytes to image {}x{}",
707 std::shared_ptr<VKImage>
image,
710 const std::shared_ptr<Buffers::VKBuffer>& staging)
712 if (!
image || !data || !staging) {
714 "Invalid parameters for upload_image_data_with_staging");
718 void* mapped = staging->get_mapped_ptr();
721 "upload_image_data_with_staging: staging buffer has no mapped pointer");
725 std::memcpy(mapped, data,
size);
726 staging->mark_dirty_range(0,
size);
728 auto& resources = staging->get_buffer_resources();
729 vk::MappedMemoryRange range { resources.memory, 0, VK_WHOLE_SIZE };
732 result != vk::Result::eSuccess) {
734 "upload_image_data_with_staging: flush failed: {}", vk::to_string(result));
738 vk::ImageMemoryBarrier barrier {};
739 barrier.oldLayout =
image->get_current_layout();
740 barrier.newLayout = vk::ImageLayout::eTransferDstOptimal;
741 barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
742 barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
743 barrier.image =
image->get_image();
744 barrier.subresourceRange = {
745 image->get_aspect_flags(), 0,
746 image->get_mip_levels(), 0,
747 image->get_array_layers()
749 barrier.srcAccessMask = vk::AccessFlagBits::eShaderRead;
750 barrier.dstAccessMask = vk::AccessFlagBits::eTransferWrite;
753 vk::PipelineStageFlagBits::eFragmentShader,
754 vk::PipelineStageFlagBits::eTransfer,
755 {}, 0,
nullptr, 0,
nullptr, 1, &barrier);
757 vk::BufferImageCopy region {};
758 region.imageSubresource.aspectMask =
image->get_aspect_flags();
759 region.imageSubresource.layerCount =
image->get_array_layers();
760 region.imageOffset = vk::Offset3D { 0, 0, 0 };
761 region.imageExtent = vk::Extent3D {
767 cmd.copyBufferToImage(
768 staging->get_buffer(),
770 vk::ImageLayout::eTransferDstOptimal,
773 barrier.oldLayout = vk::ImageLayout::eTransferDstOptimal;
774 barrier.newLayout = vk::ImageLayout::eShaderReadOnlyOptimal;
775 barrier.srcAccessMask = vk::AccessFlagBits::eTransferWrite;
776 barrier.dstAccessMask = vk::AccessFlagBits::eShaderRead;
779 vk::PipelineStageFlagBits::eTransfer,
780 vk::PipelineStageFlagBits::eFragmentShader,
781 {}, 0,
nullptr, 0,
nullptr, 1, &barrier);
784 image->set_current_layout(vk::ImageLayout::eShaderReadOnlyOptimal);
787 "upload_image_data_with_staging: {} bytes to image {}x{}",
792 std::shared_ptr<VKImage>
image,
795 vk::ImageLayout restore_layout,
796 vk::PipelineStageFlags restore_stage)
798 if (!
image || !data) {
800 "Invalid parameters for download_image_data");
804 auto staging = std::make_shared<Buffers::VKBuffer>(
812 vk::ImageMemoryBarrier barrier {};
813 barrier.oldLayout =
image->get_current_layout();
814 barrier.newLayout = vk::ImageLayout::eTransferSrcOptimal;
815 barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
816 barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
817 barrier.image =
image->get_image();
818 barrier.subresourceRange.aspectMask =
image->get_aspect_flags();
819 barrier.subresourceRange.baseMipLevel = 0;
820 barrier.subresourceRange.levelCount =
image->get_mip_levels();
821 barrier.subresourceRange.baseArrayLayer = 0;
822 barrier.subresourceRange.layerCount =
image->get_array_layers();
823 barrier.srcAccessMask = vk::AccessFlagBits::eShaderRead;
824 barrier.dstAccessMask = vk::AccessFlagBits::eTransferRead;
827 vk::PipelineStageFlagBits::eFragmentShader,
828 vk::PipelineStageFlagBits::eTransfer,
829 vk::DependencyFlags {}, {}, {}, barrier);
831 vk::BufferImageCopy region {};
832 region.bufferOffset = 0;
833 region.bufferRowLength = 0;
834 region.bufferImageHeight = 0;
835 region.imageSubresource.aspectMask =
image->get_aspect_flags();
836 region.imageSubresource.mipLevel = 0;
837 region.imageSubresource.baseArrayLayer = 0;
838 region.imageSubresource.layerCount =
image->get_array_layers();
839 region.imageOffset = vk::Offset3D { 0, 0, 0 };
840 region.imageExtent = vk::Extent3D {
846 cmd.copyImageToBuffer(
848 vk::ImageLayout::eTransferSrcOptimal,
849 staging->get_buffer(),
852 barrier.oldLayout = vk::ImageLayout::eTransferSrcOptimal;
853 barrier.newLayout = restore_layout;
854 barrier.srcAccessMask = vk::AccessFlagBits::eTransferRead;
855 barrier.dstAccessMask = vk::AccessFlagBits::eMemoryRead;
858 vk::PipelineStageFlagBits::eTransfer,
860 vk::DependencyFlags {}, {}, {}, barrier);
863 staging->mark_invalid_range(0,
size);
864 auto& resources = staging->get_buffer_resources();
865 vk::MappedMemoryRange range { resources.memory, 0, VK_WHOLE_SIZE };
868 result != vk::Result::eSuccess) {
870 "Failed to invalidate mapped memory range: {}", vk::to_string(result));
873 if (
void* mapped = staging->get_mapped_ptr()) {
874 std::memcpy(data, mapped,
size);
880 "Downloaded {} bytes from image {}x{}",
886 vk::SamplerAddressMode address_mode,
887 float max_anisotropy)
890 auto hash_combine = [](
size_t& seed,
size_t value) {
891 seed ^= value + 0x9e3779b9 + (seed << 6) + (seed >> 2);
894 hash_combine(hash,
static_cast<size_t>(filter));
895 hash_combine(hash,
static_cast<size_t>(address_mode));
896 hash_combine(hash, std::hash<float> {}(max_anisotropy));
901 "Reusing cached sampler (hash: 0x{:X})", hash);
905 vk::SamplerCreateInfo sampler_info;
906 sampler_info.magFilter = filter;
907 sampler_info.minFilter = filter;
908 sampler_info.mipmapMode = vk::SamplerMipmapMode::eLinear;
909 sampler_info.addressModeU = address_mode;
910 sampler_info.addressModeV = address_mode;
911 sampler_info.addressModeW = address_mode;
912 sampler_info.mipLodBias = 0.0F;
913 sampler_info.anisotropyEnable = max_anisotropy > 0.0F;
914 sampler_info.maxAnisotropy = max_anisotropy;
915 sampler_info.compareEnable = VK_FALSE;
916 sampler_info.compareOp = vk::CompareOp::eAlways;
917 sampler_info.minLod = 0.0F;
918 sampler_info.maxLod = VK_LOD_CLAMP_NONE;
919 sampler_info.borderColor = vk::BorderColor::eFloatOpaqueBlack;
920 sampler_info.unnormalizedCoordinates = VK_FALSE;
925 }
catch (
const vk::SystemError& e) {
927 "Failed to create sampler: {}", e.what());
934 "Created sampler (filter: {}, address: {}, anisotropy: {}, hash: 0x{:X})",
935 vk::to_string(filter), vk::to_string(address_mode), max_anisotropy, hash);
947 if (it->second == sampler) {
956 "Destroyed sampler");
961 vk::PhysicalDeviceMemoryProperties mem_properties;
964 for (uint32_t i = 0; i < mem_properties.memoryTypeCount; i++) {
965 if ((type_filter & (1 << i)) && (mem_properties.memoryTypes[i].propertyFlags & properties) == properties) {
970 error<std::runtime_error>(
973 std::source_location::current(),
974 "Failed to find suitable memory type");
1003 if (buffer && buffer->is_initialized()) {
1013 auto hash_combine = [](
size_t& seed,
size_t value) {
1014 seed ^= value + 0x9e3779b9 + (seed << 6) + (seed >> 2);
1017 hash_combine(hash,
static_cast<size_t>(filter));
1018 hash_combine(hash,
static_cast<size_t>(address_mode));
1019 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)
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 upload_image_data_with_staging(std::shared_ptr< VKImage > image, const void *data, size_t size, const std::shared_ptr< Buffers::VKBuffer > &staging)
Upload image data using a caller-supplied persistent staging buffer.
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)
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 end_single_time_commands(vk::CommandBuffer command_buffer, vk::Queue queue)
End and submit single-time command.
void free_command_buffer(vk::CommandBuffer command_buffer)
Free a command buffer back to the pool.
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.