@@ -288,7 +288,7 @@ void Texture2D::createTexture() {
288288
289289 case Texture2DUsage::Attachment:
290290 imageUsage = vk::ImageUsageFlags () | vk::ImageUsageFlagBits::eColorAttachment |
291- vk::ImageUsageFlagBits::eSampled;
291+ vk::ImageUsageFlagBits::eSampled | vk::ImageUsageFlagBits::eTransferSrc ;
292292 imageTiling = vk::ImageTiling::eOptimal;
293293 break ;
294294
@@ -524,30 +524,107 @@ std::shared_ptr<PremultipliedImage> Texture2D::readImage() {
524524
525525 // check for offset/padding
526526 const auto & device = context.getBackend ().getDevice ();
527- const auto & layout = device->getImageSubresourceLayout (imageAllocation->image ,
528- vk::ImageSubresource (vk::ImageAspectFlagBits::eColor, 0 , 0 ),
529- context.getBackend ().getDispatcher ());
527+ const auto & dispatcher = context.getBackend ().getDispatcher ();
530528
531529 imageData->resize (size);
532530 const auto & imageSize = getDataSize ();
533531
534- void * mappedData_ = nullptr ;
535- vmaMapMemory (context.getBackend ().getAllocator (), imageAllocation->allocation , &mappedData_);
532+ // Check if this is an attachment texture that needs staging buffer
533+ if (textureUsage == Texture2DUsage::Attachment) {
534+ // For optimal tiling, we need to copy to a staging buffer first
535+ const auto & allocator = context.getBackend ().getAllocator ();
536+
537+ // Create staging buffer
538+ const auto bufferInfo = vk::BufferCreateInfo ()
539+ .setSize (imageSize)
540+ .setUsage (vk::BufferUsageFlagBits::eTransferDst)
541+ .setSharingMode (vk::SharingMode::eExclusive);
542+
543+ VmaAllocationCreateInfo allocationInfo = {};
544+ allocationInfo.usage = VMA_MEMORY_USAGE_AUTO_PREFER_HOST ;
545+ allocationInfo.requiredFlags = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT ;
546+ allocationInfo.flags = VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT |
547+ VMA_ALLOCATION_CREATE_MAPPED_BIT ;
548+
549+ SharedBufferAllocation bufferAllocation = std::make_shared<BufferAllocation>(allocator);
550+ if (!bufferAllocation->create (allocationInfo, bufferInfo)) {
551+ mbgl::Log::Error (mbgl::Event::Render, " Vulkan readImage staging buffer allocation failed" );
552+ return nullptr ;
553+ }
554+
555+ // Copy image to staging buffer
556+ context.submitOneTimeCommand ([&](const vk::UniqueCommandBuffer& commandBuffer) {
557+ // Transition image layout for reading
558+ const auto barrier = vk::ImageMemoryBarrier ()
559+ .setImage (imageAllocation->image )
560+ .setOldLayout (imageLayout)
561+ .setNewLayout (vk::ImageLayout::eTransferSrcOptimal)
562+ .setSrcAccessMask (vk::AccessFlagBits::eColorAttachmentWrite)
563+ .setDstAccessMask (vk::AccessFlagBits::eTransferRead)
564+ .setSrcQueueFamilyIndex (VK_QUEUE_FAMILY_IGNORED )
565+ .setDstQueueFamilyIndex (VK_QUEUE_FAMILY_IGNORED )
566+ .setSubresourceRange ({vk::ImageAspectFlagBits::eColor, 0 , 1 , 0 , 1 });
567+
568+ commandBuffer->pipelineBarrier (vk::PipelineStageFlagBits::eColorAttachmentOutput,
569+ vk::PipelineStageFlagBits::eTransfer,
570+ {},
571+ nullptr ,
572+ nullptr ,
573+ barrier,
574+ dispatcher);
575+
576+ imageLayout = barrier.newLayout ;
577+
578+ // Copy image to buffer
579+ const auto region = vk::BufferImageCopy ()
580+ .setBufferOffset (0 )
581+ .setBufferRowLength (0 )
582+ .setBufferImageHeight (0 )
583+ .setImageSubresource (
584+ vk::ImageSubresourceLayers (vk::ImageAspectFlagBits::eColor, 0 , 0 , 1 ))
585+ .setImageOffset ({0 , 0 , 0 })
586+ .setImageExtent ({size.width , size.height , 1 });
587+
588+ commandBuffer->copyImageToBuffer (imageAllocation->image ,
589+ vk::ImageLayout::eTransferSrcOptimal,
590+ bufferAllocation->buffer ,
591+ region,
592+ dispatcher);
593+ });
594+
595+ // Map staging buffer and copy to image data
596+ vmaMapMemory (allocator, bufferAllocation->allocation , &bufferAllocation->mappedBuffer );
597+ memcpy (imageData->data .get (), bufferAllocation->mappedBuffer , imageSize);
598+ vmaUnmapMemory (allocator, bufferAllocation->allocation );
536599
537- uint8_t * mappedData = reinterpret_cast <uint8_t *>(mappedData_) + layout.offset ;
600+ return imageData;
601+ } else if (textureUsage == Texture2DUsage::Read) {
602+ // For linear tiling (Read usage), use direct memory mapping
603+ const auto & layout = device->getImageSubresourceLayout (
604+ imageAllocation->image , vk::ImageSubresource (vk::ImageAspectFlagBits::eColor, 0 , 0 ), dispatcher);
538605
539- if (imageSize == layout.arrayPitch ) {
540- memcpy (imageData->data .get (), mappedData, imageSize);
541- } else {
542- auto rowSize = static_cast <uint32_t >(size.width * getPixelStride ());
543- for (uint32_t i = 0 ; i < size.height ; ++i) {
544- memcpy (imageData->data .get () + rowSize * i, mappedData + layout.rowPitch * i, rowSize);
606+ void * mappedData_ = nullptr ;
607+ vmaMapMemory (context.getBackend ().getAllocator (), imageAllocation->allocation , &mappedData_);
608+
609+ uint8_t * mappedData = reinterpret_cast <uint8_t *>(mappedData_) + layout.offset ;
610+
611+ if (imageSize == layout.arrayPitch ) {
612+ memcpy (imageData->data .get (), mappedData, imageSize);
613+ } else {
614+ auto rowSize = static_cast <uint32_t >(size.width * getPixelStride ());
615+ for (uint32_t i = 0 ; i < size.height ; ++i) {
616+ memcpy (imageData->data .get () + rowSize * i, mappedData + layout.rowPitch * i, rowSize);
617+ }
545618 }
546- }
547619
548- vmaUnmapMemory (context.getBackend ().getAllocator (), imageAllocation->allocation );
620+ vmaUnmapMemory (context.getBackend ().getAllocator (), imageAllocation->allocation );
549621
550- return imageData;
622+ return imageData;
623+ } else {
624+ // not readable
625+ assert (false );
626+ return imageData;
627+ }
551628}
552629
553630uint32_t Texture2D::getMipLevels () const {
0 commit comments