21 buffer_service->initialize_buffer = [
this](
const std::shared_ptr<void>& vk_buf) ->
void {
22 auto buffer = std::static_pointer_cast<Buffers::VKBuffer>(vk_buf);
26 buffer_service->destroy_buffer = [
this](
const std::shared_ptr<void>& vk_buf) {
27 auto buffer = std::static_pointer_cast<Buffers::VKBuffer>(vk_buf);
31 buffer_service->execute_immediate = [
this](
const std::function<void(
void*)>& recorder) {
33 recorder(
static_cast<void*
>(cmd));
37 buffer_service->record_deferred = [
this](
const std::function<void(
void*)>& recorder) {
39 recorder(
static_cast<void*
>(cmd));
43 buffer_service->flush_range = [
this](
void* memory,
size_t offset,
size_t size) {
44 vk::DeviceMemory mem(
reinterpret_cast<VkDeviceMemory
>(memory));
45 vk::MappedMemoryRange range { mem, offset, size == 0 ? VK_WHOLE_SIZE : size };
46 if (
auto result =
m_context.
get_device().flushMappedMemoryRanges(1, &range); result != vk::Result::eSuccess) {
48 "Failed to flush mapped memory range: {}", vk::to_string(result));
52 buffer_service->invalidate_range = [
this](
void* memory,
size_t offset,
size_t size) {
53 vk::DeviceMemory mem(
reinterpret_cast<VkDeviceMemory
>(memory));
54 vk::MappedMemoryRange range { mem, offset, size == 0 ? VK_WHOLE_SIZE : size };
55 if (
auto result =
m_context.
get_device().invalidateMappedMemoryRanges(1, &range); result != vk::Result::eSuccess) {
57 "Failed to invalidate mapped memory range: {}", vk::to_string(result));
61 buffer_service->map_buffer = [
this](
void* memory,
size_t offset,
size_t size) ->
void* {
62 vk::DeviceMemory mem(
reinterpret_cast<VkDeviceMemory
>(memory));
66 buffer_service->unmap_buffer = [
this](
void* memory) {
67 vk::DeviceMemory mem(
reinterpret_cast<VkDeviceMemory
>(memory));
76 "Attempted to initialize null VulkanBuffer");
80 if (buffer->is_initialized()) {
82 "VulkanBuffer already initialized, skipping");
86 vk::BufferCreateInfo buffer_info {};
87 buffer_info.size = buffer->get_size_bytes();
88 buffer_info.usage = buffer->get_usage_flags();
89 buffer_info.sharingMode = vk::SharingMode::eExclusive;
94 }
catch (
const vk::SystemError& e) {
98 std::source_location::current(),
99 "Failed to create VkBuffer: " + std::string(e.what()));
102 vk::MemoryRequirements mem_requirements;
105 vk::MemoryAllocateInfo alloc_info;
106 alloc_info.allocationSize = mem_requirements.size;
109 mem_requirements.memoryTypeBits,
110 vk::MemoryPropertyFlags(buffer->get_memory_properties()));
112 vk::DeviceMemory memory;
115 }
catch (
const vk::SystemError& e) {
120 std::source_location::current(),
121 "Failed to allocate VkDeviceMemory: " + std::string(e.what()));
126 }
catch (
const vk::SystemError& e) {
133 std::source_location::current(),
134 "Failed to bind buffer memory: " + std::string(e.what()));
137 void* mapped_ptr =
nullptr;
138 if (buffer->is_host_visible()) {
141 }
catch (
const vk::SystemError& e) {
148 std::source_location::current(),
149 "Failed to map buffer memory: " + std::string(e.what()));
154 buffer->set_buffer_resources(resources);
158 "VulkanBuffer initialized: {} bytes, modality: {}, VkBuffer: {:p}",
159 buffer->get_size_bytes(),
161 (
void*)buffer->get_buffer());
200 auto& resources = buffer_wrapper->get_buffer_resources();
201 auto dirty_ranges = buffer_wrapper->get_and_clear_dirty_ranges();
202 if (!dirty_ranges.empty()) {
203 for (
auto [offset, size] : dirty_ranges) {
204 vk::MappedMemoryRange range;
205 range.memory = resources.memory;
206 range.offset = offset;
208 if (
auto result =
m_context.
get_device().flushMappedMemoryRanges(1, &range); result != vk::Result::eSuccess) {
210 "Failed to flush mapped memory range: {}", vk::to_string(result));
214 "Flushed {} dirty ranges for buffer {:p}", dirty_ranges.size(),
215 (
void*)buffer_wrapper->get_buffer());
218 auto invalid_ranges = buffer_wrapper->get_and_clear_invalid_ranges();
219 if (!invalid_ranges.empty()) {
220 for (
auto [offset, size] : invalid_ranges) {
221 vk::MappedMemoryRange range;
222 range.memory = buffer_wrapper->get_buffer_resources().memory;
223 range.offset = offset;
225 if (
auto result =
m_context.
get_device().invalidateMappedMemoryRanges(1, &range); result != vk::Result::eSuccess) {
227 "Failed to invalidate mapped memory range: {}", vk::to_string(result));
231 "Invalidated {} ranges for buffer {:p}", invalid_ranges.size(),
232 (
void*)buffer_wrapper->get_buffer());
241 "Attempted to initialize null VKImage");
245 if (image->is_initialized()) {
247 "VKImage already initialized, skipping");
255 vk::ImageCreateInfo image_info {};
257 switch (image->get_type()) {
259 image_info.imageType = vk::ImageType::e1D;
263 image_info.imageType = vk::ImageType::e2D;
266 image_info.imageType = vk::ImageType::e3D;
270 image_info.extent.width = image->get_width();
271 image_info.extent.height = image->get_height();
272 image_info.extent.depth = image->get_depth();
273 image_info.mipLevels = image->get_mip_levels();
274 image_info.arrayLayers = image->get_array_layers();
275 image_info.format = image->get_format();
276 image_info.tiling = vk::ImageTiling::eOptimal;
277 image_info.initialLayout = vk::ImageLayout::eUndefined;
278 image_info.usage = image->get_usage_flags();
279 image_info.sharingMode = vk::SharingMode::eExclusive;
280 image_info.samples = vk::SampleCountFlagBits::e1;
282 ? vk::ImageCreateFlagBits::eCubeCompatible
283 : vk::ImageCreateFlags {};
288 }
catch (
const vk::SystemError& e) {
292 std::source_location::current(),
293 "Failed to create VkImage: " + std::string(e.what()));
300 vk::MemoryRequirements mem_requirements;
303 vk::MemoryAllocateInfo alloc_info {};
304 alloc_info.allocationSize = mem_requirements.size;
306 mem_requirements.memoryTypeBits,
307 image->get_memory_properties());
309 vk::DeviceMemory memory;
312 }
catch (
const vk::SystemError& e) {
317 std::source_location::current(),
318 "Failed to allocate VkDeviceMemory for image: " + std::string(e.what()));
327 }
catch (
const vk::SystemError& e) {
333 std::source_location::current(),
334 "Failed to bind memory to VkImage: " + std::string(e.what()));
341 vk::ImageViewCreateInfo view_info {};
343 switch (image->get_type()) {
345 view_info.viewType = (image->get_array_layers() > 1)
346 ? vk::ImageViewType::e1DArray
347 : vk::ImageViewType::e1D;
350 view_info.viewType = (image->get_array_layers() > 1)
351 ? vk::ImageViewType::e2DArray
352 : vk::ImageViewType::e2D;
355 view_info.viewType = vk::ImageViewType::e3D;
358 view_info.viewType = vk::ImageViewType::eCube;
362 view_info.image = vk_image;
363 view_info.format = image->get_format();
364 view_info.subresourceRange.aspectMask = image->get_aspect_flags();
365 view_info.subresourceRange.baseMipLevel = 0;
366 view_info.subresourceRange.levelCount = image->get_mip_levels();
367 view_info.subresourceRange.baseArrayLayer = 0;
368 view_info.subresourceRange.layerCount = image->get_array_layers();
370 view_info.components.r = vk::ComponentSwizzle::eIdentity;
371 view_info.components.g = vk::ComponentSwizzle::eIdentity;
372 view_info.components.b = vk::ComponentSwizzle::eIdentity;
373 view_info.components.a = vk::ComponentSwizzle::eIdentity;
375 vk::ImageView image_view;
378 }
catch (
const vk::SystemError& e) {
384 std::source_location::current(),
385 "Failed to create VkImageView: " + std::string(e.what()));
393 resources.
image = vk_image;
394 resources.image_view = image_view;
395 resources.memory = memory;
396 resources.sampler =
nullptr;
398 image->set_image_resources(resources);
399 image->set_current_layout(vk::ImageLayout::eUndefined);
402 "VKImage initialized: {}x{}x{}, format: {}, {} mips, {} layers",
403 image->get_width(), image->get_height(), image->get_depth(),
404 vk::to_string(image->get_format()),
405 image->get_mip_levels(), image->get_array_layers());
434 vk::ImageLayout old_layout,
435 vk::ImageLayout new_layout,
437 uint32_t array_layers,
438 vk::ImageAspectFlags aspect_flags)
441 vk::ImageMemoryBarrier barrier {};
442 barrier.oldLayout = old_layout;
443 barrier.newLayout = new_layout;
444 barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
445 barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
446 barrier.image = image;
447 barrier.subresourceRange.aspectMask = aspect_flags;
448 barrier.subresourceRange.baseMipLevel = 0;
449 barrier.subresourceRange.levelCount = mip_levels;
450 barrier.subresourceRange.baseArrayLayer = 0;
451 barrier.subresourceRange.layerCount = array_layers;
453 vk::PipelineStageFlags src_stage;
454 vk::PipelineStageFlags dst_stage;
456 if (old_layout == vk::ImageLayout::eUndefined && new_layout == vk::ImageLayout::eTransferDstOptimal) {
457 barrier.srcAccessMask = vk::AccessFlags {};
458 barrier.dstAccessMask = vk::AccessFlagBits::eTransferWrite;
459 src_stage = vk::PipelineStageFlagBits::eTopOfPipe;
460 dst_stage = vk::PipelineStageFlagBits::eTransfer;
461 }
else if (old_layout == vk::ImageLayout::eTransferDstOptimal && new_layout == vk::ImageLayout::eShaderReadOnlyOptimal) {
462 barrier.srcAccessMask = vk::AccessFlagBits::eTransferWrite;
463 barrier.dstAccessMask = vk::AccessFlagBits::eShaderRead;
464 src_stage = vk::PipelineStageFlagBits::eTransfer;
465 dst_stage = vk::PipelineStageFlagBits::eFragmentShader;
466 }
else if (old_layout == vk::ImageLayout::eUndefined && new_layout == vk::ImageLayout::eShaderReadOnlyOptimal) {
467 barrier.srcAccessMask = vk::AccessFlags {};
468 barrier.dstAccessMask = vk::AccessFlagBits::eShaderRead;
469 src_stage = vk::PipelineStageFlagBits::eTopOfPipe;
470 dst_stage = vk::PipelineStageFlagBits::eFragmentShader;
471 }
else if (old_layout == vk::ImageLayout::eUndefined && new_layout == vk::ImageLayout::eColorAttachmentOptimal) {
472 barrier.srcAccessMask = vk::AccessFlags {};
473 barrier.dstAccessMask = vk::AccessFlagBits::eColorAttachmentWrite;
474 src_stage = vk::PipelineStageFlagBits::eTopOfPipe;
475 dst_stage = vk::PipelineStageFlagBits::eColorAttachmentOutput;
476 }
else if (old_layout == vk::ImageLayout::eUndefined && new_layout == vk::ImageLayout::eDepthStencilAttachmentOptimal) {
477 barrier.srcAccessMask = vk::AccessFlags {};
478 barrier.dstAccessMask = vk::AccessFlagBits::eDepthStencilAttachmentRead | vk::AccessFlagBits::eDepthStencilAttachmentWrite;
479 src_stage = vk::PipelineStageFlagBits::eTopOfPipe;
480 dst_stage = vk::PipelineStageFlagBits::eEarlyFragmentTests;
481 }
else if (old_layout == vk::ImageLayout::eUndefined && new_layout == vk::ImageLayout::eGeneral) {
482 barrier.srcAccessMask = vk::AccessFlags {};
483 barrier.dstAccessMask = vk::AccessFlagBits::eShaderRead | vk::AccessFlagBits::eShaderWrite;
484 src_stage = vk::PipelineStageFlagBits::eTopOfPipe;
485 dst_stage = vk::PipelineStageFlagBits::eComputeShader;
487 barrier.srcAccessMask = vk::AccessFlagBits::eMemoryRead | vk::AccessFlagBits::eMemoryWrite;
488 barrier.dstAccessMask = vk::AccessFlagBits::eMemoryRead | vk::AccessFlagBits::eMemoryWrite;
489 src_stage = vk::PipelineStageFlagBits::eAllCommands;
490 dst_stage = vk::PipelineStageFlagBits::eAllCommands;
493 "Using generic image layout transition");
497 src_stage, dst_stage,
498 vk::DependencyFlags {},
506 "Image layout transitioned: {} -> {}",
507 vk::to_string(old_layout), vk::to_string(new_layout));
511 std::shared_ptr<VKImage> image,
515 if (!image || !data) {
517 "Invalid parameters for upload_image_data");
521 auto staging = std::make_shared<Buffers::VKBuffer>(
528 void* mapped = staging->get_mapped_ptr();
531 "Failed to map staging buffer for image upload");
536 std::memcpy(mapped, data, size);
537 staging->mark_dirty_range(0, size);
539 auto& resources = staging->get_buffer_resources();
540 vk::MappedMemoryRange range { resources.memory, 0, VK_WHOLE_SIZE };
542 if (
auto result =
m_context.
get_device().flushMappedMemoryRanges(1, &range); result != vk::Result::eSuccess) {
544 "Failed to flush mapped memory range: {}", vk::to_string(result));
548 vk::ImageMemoryBarrier barrier {};
549 barrier.oldLayout = image->get_current_layout();
550 barrier.newLayout = vk::ImageLayout::eTransferDstOptimal;
551 barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
552 barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
553 barrier.image = image->get_image();
554 barrier.subresourceRange.aspectMask = image->get_aspect_flags();
555 barrier.subresourceRange.baseMipLevel = 0;
556 barrier.subresourceRange.levelCount = image->get_mip_levels();
557 barrier.subresourceRange.baseArrayLayer = 0;
558 barrier.subresourceRange.layerCount = image->get_array_layers();
559 barrier.srcAccessMask = vk::AccessFlagBits::eShaderRead;
560 barrier.dstAccessMask = vk::AccessFlagBits::eTransferWrite;
563 vk::PipelineStageFlagBits::eFragmentShader,
564 vk::PipelineStageFlagBits::eTransfer,
565 vk::DependencyFlags {},
566 0,
nullptr, 0,
nullptr, 1, &barrier);
568 vk::BufferImageCopy region {};
569 region.bufferOffset = 0;
570 region.bufferRowLength = 0;
571 region.bufferImageHeight = 0;
572 region.imageSubresource.aspectMask = image->get_aspect_flags();
573 region.imageSubresource.mipLevel = 0;
574 region.imageSubresource.baseArrayLayer = 0;
575 region.imageSubresource.layerCount = image->get_array_layers();
576 region.imageOffset = vk::Offset3D { 0, 0, 0 };
577 region.imageExtent = vk::Extent3D {
583 cmd.copyBufferToImage(
584 staging->get_buffer(),
586 vk::ImageLayout::eTransferDstOptimal,
589 barrier.oldLayout = vk::ImageLayout::eTransferDstOptimal;
590 barrier.newLayout = vk::ImageLayout::eShaderReadOnlyOptimal;
591 barrier.srcAccessMask = vk::AccessFlagBits::eTransferWrite;
592 barrier.dstAccessMask = vk::AccessFlagBits::eShaderRead;
595 vk::PipelineStageFlagBits::eTransfer,
596 vk::PipelineStageFlagBits::eFragmentShader,
597 vk::DependencyFlags {},
598 0,
nullptr, 0,
nullptr, 1, &barrier);
601 image->set_current_layout(vk::ImageLayout::eShaderReadOnlyOptimal);
604 "Uploaded {} bytes to image {}x{}",
605 size, image->get_width(), image->get_height());
609 std::shared_ptr<VKImage> image,
612 const std::shared_ptr<Buffers::VKBuffer>& staging)
614 if (!image || !data || !staging) {
616 "Invalid parameters for upload_image_data_with_staging");
620 void* mapped = staging->get_mapped_ptr();
623 "upload_image_data_with_staging: staging buffer has no mapped pointer");
627 std::memcpy(mapped, data, size);
628 staging->mark_dirty_range(0, size);
630 auto& resources = staging->get_buffer_resources();
631 vk::MappedMemoryRange range { resources.memory, 0, VK_WHOLE_SIZE };
634 result != vk::Result::eSuccess) {
636 "upload_image_data_with_staging: flush failed: {}", vk::to_string(result));
640 vk::ImageMemoryBarrier barrier {};
641 barrier.oldLayout = image->get_current_layout();
642 barrier.newLayout = vk::ImageLayout::eTransferDstOptimal;
643 barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
644 barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
645 barrier.image = image->get_image();
646 barrier.subresourceRange = {
647 image->get_aspect_flags(), 0,
648 image->get_mip_levels(), 0,
649 image->get_array_layers()
651 barrier.srcAccessMask = vk::AccessFlagBits::eShaderRead;
652 barrier.dstAccessMask = vk::AccessFlagBits::eTransferWrite;
655 vk::PipelineStageFlagBits::eFragmentShader,
656 vk::PipelineStageFlagBits::eTransfer,
657 {}, 0,
nullptr, 0,
nullptr, 1, &barrier);
659 vk::BufferImageCopy region {};
660 region.imageSubresource.aspectMask = image->get_aspect_flags();
661 region.imageSubresource.layerCount = image->get_array_layers();
662 region.imageOffset = vk::Offset3D { 0, 0, 0 };
663 region.imageExtent = vk::Extent3D {
669 cmd.copyBufferToImage(
670 staging->get_buffer(),
672 vk::ImageLayout::eTransferDstOptimal,
675 barrier.oldLayout = vk::ImageLayout::eTransferDstOptimal;
676 barrier.newLayout = vk::ImageLayout::eShaderReadOnlyOptimal;
677 barrier.srcAccessMask = vk::AccessFlagBits::eTransferWrite;
678 barrier.dstAccessMask = vk::AccessFlagBits::eShaderRead;
681 vk::PipelineStageFlagBits::eTransfer,
682 vk::PipelineStageFlagBits::eFragmentShader,
683 {}, 0,
nullptr, 0,
nullptr, 1, &barrier);
686 image->set_current_layout(vk::ImageLayout::eShaderReadOnlyOptimal);
689 "upload_image_data_with_staging: {} bytes to image {}x{}",
690 size, image->get_width(), image->get_height());
694 std::shared_ptr<VKImage> image,
697 vk::ImageLayout restore_layout,
698 vk::PipelineStageFlags restore_stage)
700 if (!image || !data) {
702 "Invalid parameters for download_image_data");
706 auto staging = std::make_shared<Buffers::VKBuffer>(
714 vk::ImageMemoryBarrier barrier {};
715 barrier.oldLayout = image->get_current_layout();
716 barrier.newLayout = vk::ImageLayout::eTransferSrcOptimal;
717 barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
718 barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
719 barrier.image = image->get_image();
720 barrier.subresourceRange.aspectMask = image->get_aspect_flags();
721 barrier.subresourceRange.baseMipLevel = 0;
722 barrier.subresourceRange.levelCount = image->get_mip_levels();
723 barrier.subresourceRange.baseArrayLayer = 0;
724 barrier.subresourceRange.layerCount = image->get_array_layers();
725 barrier.srcAccessMask = vk::AccessFlagBits::eShaderRead;
726 barrier.dstAccessMask = vk::AccessFlagBits::eTransferRead;
729 vk::PipelineStageFlagBits::eFragmentShader,
730 vk::PipelineStageFlagBits::eTransfer,
731 vk::DependencyFlags {}, {}, {}, 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.copyImageToBuffer(
750 vk::ImageLayout::eTransferSrcOptimal,
751 staging->get_buffer(),
754 barrier.oldLayout = vk::ImageLayout::eTransferSrcOptimal;
755 barrier.newLayout = restore_layout;
756 barrier.srcAccessMask = vk::AccessFlagBits::eTransferRead;
757 barrier.dstAccessMask = vk::AccessFlagBits::eMemoryRead;
760 vk::PipelineStageFlagBits::eTransfer,
762 vk::DependencyFlags {}, {}, {}, barrier);
765 staging->mark_invalid_range(0, size);
766 auto& resources = staging->get_buffer_resources();
767 vk::MappedMemoryRange range { resources.memory, 0, VK_WHOLE_SIZE };
770 result != vk::Result::eSuccess) {
772 "Failed to invalidate mapped memory range: {}", vk::to_string(result));
775 if (
void* mapped = staging->get_mapped_ptr()) {
776 std::memcpy(data, mapped, size);
782 "Downloaded {} bytes from image {}x{}",
783 size, image->get_width(), image->get_height());
788 vk::SamplerAddressMode address_mode,
789 float max_anisotropy)
792 auto hash_combine = [](
size_t& seed,
size_t value) {
793 seed ^= value + 0x9e3779b9 + (seed << 6) + (seed >> 2);
796 hash_combine(hash,
static_cast<size_t>(filter));
797 hash_combine(hash,
static_cast<size_t>(address_mode));
798 hash_combine(hash, std::hash<float> {}(max_anisotropy));
803 "Reusing cached sampler (hash: 0x{:X})", hash);
807 vk::SamplerCreateInfo sampler_info;
808 sampler_info.magFilter = filter;
809 sampler_info.minFilter = filter;
810 sampler_info.mipmapMode = vk::SamplerMipmapMode::eLinear;
811 sampler_info.addressModeU = address_mode;
812 sampler_info.addressModeV = address_mode;
813 sampler_info.addressModeW = address_mode;
814 sampler_info.mipLodBias = 0.0F;
815 sampler_info.anisotropyEnable = max_anisotropy > 0.0F;
816 sampler_info.maxAnisotropy = max_anisotropy;
817 sampler_info.compareEnable = VK_FALSE;
818 sampler_info.compareOp = vk::CompareOp::eAlways;
819 sampler_info.minLod = 0.0F;
820 sampler_info.maxLod = VK_LOD_CLAMP_NONE;
821 sampler_info.borderColor = vk::BorderColor::eFloatOpaqueBlack;
822 sampler_info.unnormalizedCoordinates = VK_FALSE;
827 }
catch (
const vk::SystemError& e) {
829 "Failed to create sampler: {}", e.what());
836 "Created sampler (filter: {}, address: {}, anisotropy: {}, hash: 0x{:X})",
837 vk::to_string(filter), vk::to_string(address_mode), max_anisotropy, hash);
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.