Skip to content

Commit db29e62

Browse files
birkskyumpre-commit-ci[bot]adrian-cojocarulouwers
authored
texture2d - getVulkanImage and support for Texture2DUsage::Attachment (maplibre#3632)
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Adrian Cojocaru <adrian.cojocaru92@yahoo.com> Co-authored-by: Bart Louwers <bart@emeel.net>
1 parent eaba10a commit db29e62

3 files changed

Lines changed: 100 additions & 18 deletions

File tree

include/mbgl/vulkan/texture2d.hpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ class Texture2D : public gfx::Texture2D {
8080

8181
const vk::ImageLayout& getVulkanImageLayout() const { return imageLayout; }
8282
const vk::UniqueImageView& getVulkanImageView() const { return imageAllocation->imageView; }
83+
const vk::Image& getVulkanImage() const { return imageAllocation->image; }
8384
const vk::Sampler& getVulkanSampler();
8485

8586
void copyImage(vk::Image image);

src/mbgl/vulkan/offscreen_texture.cpp

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,8 +46,12 @@ class OffscreenTextureResource final : public RenderableResource {
4646
const vk::UniqueFramebuffer& getFramebuffer() const override { return framebuffer; };
4747

4848
PremultipliedImage readStillImage() {
49-
assert(false);
50-
return {};
49+
if (!colorTexture) {
50+
return {};
51+
}
52+
53+
const auto& image = static_cast<Texture2D&>(*colorTexture).readImage();
54+
return image ? image->clone() : PremultipliedImage();
5155
}
5256

5357
gfx::Texture2DPtr& getTexture() {

src/mbgl/vulkan/texture2d.cpp

Lines changed: 93 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -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

553630
uint32_t Texture2D::getMipLevels() const {

0 commit comments

Comments
 (0)