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::eColorAttachmentOptimal) {
467 barrier.srcAccessMask = vk::AccessFlags {};
468 barrier.dstAccessMask = vk::AccessFlagBits::eColorAttachmentWrite;
469 src_stage = vk::PipelineStageFlagBits::eTopOfPipe;
470 dst_stage = vk::PipelineStageFlagBits::eColorAttachmentOutput;
471 }
else if (old_layout == vk::ImageLayout::eUndefined && new_layout == vk::ImageLayout::eDepthStencilAttachmentOptimal) {
472 barrier.srcAccessMask = vk::AccessFlags {};
473 barrier.dstAccessMask = vk::AccessFlagBits::eDepthStencilAttachmentRead | vk::AccessFlagBits::eDepthStencilAttachmentWrite;
474 src_stage = vk::PipelineStageFlagBits::eTopOfPipe;
475 dst_stage = vk::PipelineStageFlagBits::eEarlyFragmentTests;
476 }
else if (old_layout == vk::ImageLayout::eUndefined && new_layout == vk::ImageLayout::eGeneral) {
477 barrier.srcAccessMask = vk::AccessFlags {};
478 barrier.dstAccessMask = vk::AccessFlagBits::eShaderRead | vk::AccessFlagBits::eShaderWrite;
479 src_stage = vk::PipelineStageFlagBits::eTopOfPipe;
480 dst_stage = vk::PipelineStageFlagBits::eComputeShader;
482 barrier.srcAccessMask = vk::AccessFlagBits::eMemoryRead | vk::AccessFlagBits::eMemoryWrite;
483 barrier.dstAccessMask = vk::AccessFlagBits::eMemoryRead | vk::AccessFlagBits::eMemoryWrite;
484 src_stage = vk::PipelineStageFlagBits::eAllCommands;
485 dst_stage = vk::PipelineStageFlagBits::eAllCommands;
488 "Using generic image layout transition");
492 src_stage, dst_stage,
493 vk::DependencyFlags {},
501 "Image layout transitioned: {} -> {}",
502 vk::to_string(old_layout), vk::to_string(new_layout));
506 std::shared_ptr<VKImage> image,
510 if (!image || !data) {
512 "Invalid parameters for upload_image_data");
516 auto staging = std::make_shared<Buffers::VKBuffer>(
523 void* mapped = staging->get_mapped_ptr();
526 "Failed to map staging buffer for image upload");
531 std::memcpy(mapped, data, size);
532 staging->mark_dirty_range(0, size);
534 auto& resources = staging->get_buffer_resources();
535 vk::MappedMemoryRange range { resources.memory, 0, VK_WHOLE_SIZE };
537 if (
auto result =
m_context.
get_device().flushMappedMemoryRanges(1, &range); result != vk::Result::eSuccess) {
539 "Failed to flush mapped memory range: {}", vk::to_string(result));
543 vk::ImageMemoryBarrier barrier {};
544 barrier.oldLayout = image->get_current_layout();
545 barrier.newLayout = vk::ImageLayout::eTransferDstOptimal;
546 barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
547 barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
548 barrier.image = image->get_image();
549 barrier.subresourceRange.aspectMask = image->get_aspect_flags();
550 barrier.subresourceRange.baseMipLevel = 0;
551 barrier.subresourceRange.levelCount = image->get_mip_levels();
552 barrier.subresourceRange.baseArrayLayer = 0;
553 barrier.subresourceRange.layerCount = image->get_array_layers();
554 barrier.srcAccessMask = vk::AccessFlagBits::eShaderRead;
555 barrier.dstAccessMask = vk::AccessFlagBits::eTransferWrite;
558 vk::PipelineStageFlagBits::eFragmentShader,
559 vk::PipelineStageFlagBits::eTransfer,
560 vk::DependencyFlags {},
561 0,
nullptr, 0,
nullptr, 1, &barrier);
563 vk::BufferImageCopy region {};
564 region.bufferOffset = 0;
565 region.bufferRowLength = 0;
566 region.bufferImageHeight = 0;
567 region.imageSubresource.aspectMask = image->get_aspect_flags();
568 region.imageSubresource.mipLevel = 0;
569 region.imageSubresource.baseArrayLayer = 0;
570 region.imageSubresource.layerCount = image->get_array_layers();
571 region.imageOffset = vk::Offset3D { 0, 0, 0 };
572 region.imageExtent = vk::Extent3D {
578 cmd.copyBufferToImage(
579 staging->get_buffer(),
581 vk::ImageLayout::eTransferDstOptimal,
584 barrier.oldLayout = vk::ImageLayout::eTransferDstOptimal;
585 barrier.newLayout = vk::ImageLayout::eShaderReadOnlyOptimal;
586 barrier.srcAccessMask = vk::AccessFlagBits::eTransferWrite;
587 barrier.dstAccessMask = vk::AccessFlagBits::eShaderRead;
590 vk::PipelineStageFlagBits::eTransfer,
591 vk::PipelineStageFlagBits::eFragmentShader,
592 vk::DependencyFlags {},
593 0,
nullptr, 0,
nullptr, 1, &barrier);
596 image->set_current_layout(vk::ImageLayout::eShaderReadOnlyOptimal);
599 "Uploaded {} bytes to image {}x{}",
600 size, image->get_width(), image->get_height());
604 std::shared_ptr<VKImage> image,
608 if (!image || !data) {
610 "Invalid parameters for download_image_data");
614 auto staging = std::make_shared<Buffers::VKBuffer>(
622 vk::ImageMemoryBarrier barrier {};
623 barrier.oldLayout = image->get_current_layout();
624 barrier.newLayout = vk::ImageLayout::eTransferSrcOptimal;
625 barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
626 barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
627 barrier.image = image->get_image();
628 barrier.subresourceRange.aspectMask = image->get_aspect_flags();
629 barrier.subresourceRange.baseMipLevel = 0;
630 barrier.subresourceRange.levelCount = image->get_mip_levels();
631 barrier.subresourceRange.baseArrayLayer = 0;
632 barrier.subresourceRange.layerCount = image->get_array_layers();
633 barrier.srcAccessMask = vk::AccessFlagBits::eShaderRead;
634 barrier.dstAccessMask = vk::AccessFlagBits::eTransferRead;
637 vk::PipelineStageFlagBits::eFragmentShader,
638 vk::PipelineStageFlagBits::eTransfer,
639 vk::DependencyFlags {},
640 0,
nullptr, 0,
nullptr, 1, &barrier);
642 vk::BufferImageCopy region {};
643 region.bufferOffset = 0;
644 region.bufferRowLength = 0;
645 region.bufferImageHeight = 0;
646 region.imageSubresource.aspectMask = image->get_aspect_flags();
647 region.imageSubresource.mipLevel = 0;
648 region.imageSubresource.baseArrayLayer = 0;
649 region.imageSubresource.layerCount = image->get_array_layers();
650 region.imageOffset = vk::Offset3D { 0, 0, 0 };
651 region.imageExtent = vk::Extent3D {
657 cmd.copyImageToBuffer(
659 vk::ImageLayout::eTransferSrcOptimal,
660 staging->get_buffer(),
663 barrier.oldLayout = vk::ImageLayout::eTransferSrcOptimal;
664 barrier.newLayout = image->get_current_layout();
665 barrier.srcAccessMask = vk::AccessFlagBits::eTransferRead;
666 barrier.dstAccessMask = vk::AccessFlagBits::eShaderRead;
669 vk::PipelineStageFlagBits::eTransfer,
670 vk::PipelineStageFlagBits::eFragmentShader,
671 vk::DependencyFlags {},
672 0,
nullptr, 0,
nullptr, 1, &barrier);
675 staging->mark_invalid_range(0, size);
676 auto& resources = staging->get_buffer_resources();
677 vk::MappedMemoryRange range { resources.memory, 0, VK_WHOLE_SIZE };
679 if (
auto result =
m_context.
get_device().invalidateMappedMemoryRanges(1, &range); result != vk::Result::eSuccess) {
681 "Failed to invalidate mapped memory range: {}", vk::to_string(result));
684 void* mapped = staging->get_mapped_ptr();
686 std::memcpy(data, mapped, size);
692 "Downloaded {} bytes from image {}x{}",
693 size, image->get_width(), image->get_height());
698 vk::SamplerAddressMode address_mode,
699 float max_anisotropy)
702 auto hash_combine = [](
size_t& seed,
size_t value) {
703 seed ^= value + 0x9e3779b9 + (seed << 6) + (seed >> 2);
706 hash_combine(hash,
static_cast<size_t>(filter));
707 hash_combine(hash,
static_cast<size_t>(address_mode));
708 hash_combine(hash, std::hash<float> {}(max_anisotropy));
713 "Reusing cached sampler (hash: 0x{:X})", hash);
717 vk::SamplerCreateInfo sampler_info;
718 sampler_info.magFilter = filter;
719 sampler_info.minFilter = filter;
720 sampler_info.mipmapMode = vk::SamplerMipmapMode::eLinear;
721 sampler_info.addressModeU = address_mode;
722 sampler_info.addressModeV = address_mode;
723 sampler_info.addressModeW = address_mode;
724 sampler_info.mipLodBias = 0.0F;
725 sampler_info.anisotropyEnable = max_anisotropy > 0.0F;
726 sampler_info.maxAnisotropy = max_anisotropy;
727 sampler_info.compareEnable = VK_FALSE;
728 sampler_info.compareOp = vk::CompareOp::eAlways;
729 sampler_info.minLod = 0.0F;
730 sampler_info.maxLod = VK_LOD_CLAMP_NONE;
731 sampler_info.borderColor = vk::BorderColor::eFloatOpaqueBlack;
732 sampler_info.unnormalizedCoordinates = VK_FALSE;
737 }
catch (
const vk::SystemError& e) {
739 "Failed to create sampler: {}", e.what());
746 "Created sampler (filter: {}, address: {}, anisotropy: {}, hash: 0x{:X})",
747 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.