@@ -590,8 +590,9 @@ void Image::CopyImageWithBuffer(Image& source, Buffer& buffer) {
590590}
591591
592592void Image::CopyMip (Image& source, uint32_t mip, uint32_t layer) {
593- EXIT_IF (m_scheduler == nullptr || source.backing .samples != backing.samples ||
594- mip >= backing.mip_levels || layer >= backing.layers );
593+ EXIT_IF (m_scheduler == nullptr || m_graphics == nullptr ||
594+ source.backing .samples != backing.samples || mip >= backing.mip_levels ||
595+ layer >= backing.layers );
595596 m_scheduler->EndRendering ();
596597 const auto width = std::max (backing.extent .width >> mip, 1u );
597598 const auto height = std::max (backing.extent .height >> mip, 1u );
@@ -600,24 +601,59 @@ void Image::CopyMip(Image& source, uint32_t mip, uint32_t layer) {
600601 const auto [source_layers, destination_layers] = SanitizeCopyLayers (source, *this , depth);
601602 const auto aspects = FullAspectMask (source.backing .format );
602603 EXIT_IF (aspects != FullAspectMask (backing.format ));
603- std::array<vk::ImageCopy, 2 > copies {};
604- uint32_t copy_count = 0 ;
605- for (const auto aspect: {vk::ImageAspectFlagBits::eColor, vk::ImageAspectFlagBits::eDepth,
606- vk::ImageAspectFlagBits::eStencil}) {
607- if (!static_cast <bool >(aspects & aspect)) {
604+ auto command = m_scheduler->Current ().Handle ();
605+ Transit (vk::ImageLayout::eTransferDstOptimal, vk::AccessFlagBits2::eTransferWrite, {}, command);
606+ source.Transit (vk::ImageLayout::eTransferSrcOptimal, vk::AccessFlagBits2::eTransferRead, {},
607+ command);
608+
609+ const auto non_stencil = aspects & ~vk::ImageAspectFlagBits::eStencil;
610+ for (const auto aspect: {vk::ImageAspectFlagBits::eColor, vk::ImageAspectFlagBits::eDepth}) {
611+ if (!static_cast <bool >(non_stencil & aspect)) {
608612 continue ;
609613 }
610- auto & copy = copies[copy_count++] ;
614+ vk::ImageCopy copy {} ;
611615 copy.srcSubresource = {aspect, 0 , 0 , source_layers};
612616 copy.dstSubresource = {aspect, mip, layer, destination_layers};
613617 copy.extent = {width, height, depth};
618+ command.copyImage (source.backing .image , vk::ImageLayout::eTransferSrcOptimal, backing.image ,
619+ vk::ImageLayout::eTransferDstOptimal, 1 , ©);
620+ }
621+
622+ // Some AMD hosts drop stencil during image-to-image copies of packed D*S8
623+ // formats. Route the stencil plane through a scratch buffer instead.
624+ if (static_cast <bool >(aspects & vk::ImageAspectFlagBits::eStencil)) {
625+ const auto stencil_bytes =
626+ static_cast <uint64_t >(width) * height * depth * source_layers;
627+ EXIT_IF (stencil_bytes == 0 );
628+ Buffer scratch (*m_graphics, *m_scheduler, MemoryUsage::DeviceLocal, 0 ,
629+ vk::BufferUsageFlagBits::eTransferSrc | vk::BufferUsageFlagBits::eTransferDst,
630+ stencil_bytes);
631+ vk::BufferImageCopy region {};
632+ region.imageSubresource = {vk::ImageAspectFlagBits::eStencil, 0 , 0 , source_layers};
633+ region.imageExtent = {width, height, depth};
634+ command.copyImageToBuffer (source.backing .image , vk::ImageLayout::eTransferSrcOptimal,
635+ scratch.Handle (), 1 , ®ion);
636+
637+ vk::BufferMemoryBarrier2 buffer_barrier {};
638+ buffer_barrier.srcStageMask = vk::PipelineStageFlagBits2::eTransfer;
639+ buffer_barrier.srcAccessMask = vk::AccessFlagBits2::eTransferWrite;
640+ buffer_barrier.dstStageMask = vk::PipelineStageFlagBits2::eTransfer;
641+ buffer_barrier.dstAccessMask = vk::AccessFlagBits2::eTransferRead;
642+ buffer_barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED ;
643+ buffer_barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED ;
644+ buffer_barrier.buffer = scratch.Handle ();
645+ buffer_barrier.size = stencil_bytes;
646+ vk::DependencyInfo dependency {};
647+ dependency.bufferMemoryBarrierCount = 1 ;
648+ dependency.pBufferMemoryBarriers = &buffer_barrier;
649+ command.pipelineBarrier2 (dependency);
650+
651+ region.imageSubresource = {vk::ImageAspectFlagBits::eStencil, mip, layer,
652+ destination_layers};
653+ command.copyBufferToImage (scratch.Handle (), backing.image ,
654+ vk::ImageLayout::eTransferDstOptimal, 1 , ®ion);
614655 }
615- auto command = m_scheduler->Current ().Handle ();
616- Transit (vk::ImageLayout::eTransferDstOptimal, vk::AccessFlagBits2::eTransferWrite, {}, command);
617- source.Transit (vk::ImageLayout::eTransferSrcOptimal, vk::AccessFlagBits2::eTransferRead, {},
618- command);
619- command.copyImage (source.backing .image , vk::ImageLayout::eTransferSrcOptimal, backing.image ,
620- vk::ImageLayout::eTransferDstOptimal, copy_count, copies.data ());
656+
621657 Transit (vk::ImageLayout::eGeneral,
622658 vk::AccessFlagBits2::eShaderRead | vk::AccessFlagBits2::eTransferRead, {}, command);
623659}
0 commit comments