diff --git a/include/mbgl/vulkan/context.hpp b/include/mbgl/vulkan/context.hpp index 1b3f43fea..357caaf99 100644 --- a/include/mbgl/vulkan/context.hpp +++ b/include/mbgl/vulkan/context.hpp @@ -149,6 +149,7 @@ public: const vk::UniquePipelineLayout& getPushConstantPipelineLayout(); uint8_t getCurrentFrameResourceIndex() const { return frameResourceIndex; } + vk::UniqueCommandBuffer& getCommandBuffer() { return frameResources[frameResourceIndex].commandBuffer; } void enqueueDeletion(DeletionTask&& function); void submitOneTimeCommand(const std::function& function); void submitOneTimeCommand(const vk::UniqueCommandBuffer& buffer); diff --git a/include/mbgl/vulkan/renderable_resource.hpp b/include/mbgl/vulkan/renderable_resource.hpp index 9c45f4440..303fb1841 100644 --- a/include/mbgl/vulkan/renderable_resource.hpp +++ b/include/mbgl/vulkan/renderable_resource.hpp @@ -47,6 +47,7 @@ protected: void setColorFormat(vk::Format format); void setDepthFormat(vk::Format format); + void copySurfaceToReadTexture(); void swap() override; public: @@ -77,7 +78,7 @@ public: void init(uint32_t w, uint32_t h); void recreateSwapchain(); - void enableSurfaceRead(bool value = true); + void queueSurfaceRead(); std::shared_ptr readImage(); protected: @@ -104,6 +105,7 @@ protected: int32_t surfaceTransformPollingInterval{-1}; bool surfaceRead{false}; + std::unique_ptr readTexture{nullptr}; }; class Renderable : public gfx::Renderable { diff --git a/include/mbgl/vulkan/texture2d.hpp b/include/mbgl/vulkan/texture2d.hpp index 2de4fe6ec..887565719 100644 --- a/include/mbgl/vulkan/texture2d.hpp +++ b/include/mbgl/vulkan/texture2d.hpp @@ -87,7 +87,17 @@ public: const vk::Sampler& getVulkanSampler(); void copyImage(vk::Image image, Size imageSize, uint16_t xOffset = 0, uint16_t yOffset = 0); + void copyImage(vk::Image image, + Size imageSize, + uint16_t xOffset, + uint16_t yOffset, + const vk::UniqueCommandBuffer& commandBuffer); void blitImage(vk::Image image, Size imageSize, uint16_t xOffset = 0, uint16_t yOffset = 0); + void blitImage(vk::Image image, + Size imageSize, + uint16_t xOffset, + uint16_t yOffset, + const vk::UniqueCommandBuffer& commandBuffer); std::shared_ptr readImage(); private: diff --git a/platform/android/MapLibreAndroid/src/cpp/android_vulkan_renderer_backend.cpp b/platform/android/MapLibreAndroid/src/cpp/android_vulkan_renderer_backend.cpp index 01c6599d7..d9fc7e2f5 100644 --- a/platform/android/MapLibreAndroid/src/cpp/android_vulkan_renderer_backend.cpp +++ b/platform/android/MapLibreAndroid/src/cpp/android_vulkan_renderer_backend.cpp @@ -83,11 +83,23 @@ void AndroidVulkanRendererBackend::resizeFramebuffer(int, int) { } } -void AndroidVulkanRendererBackend::enableFramebufferRead(bool value) { - getResource().enableSurfaceRead(); +void AndroidVulkanRendererBackend::enableFramebufferRead(bool) { + MBGL_VERIFY_THREAD(tid); + + if (!hasResource()) { + return; + } + + getResource().queueSurfaceRead(); } PremultipliedImage AndroidVulkanRendererBackend::readFramebuffer() { + MBGL_VERIFY_THREAD(tid); + + if (!hasResource()) { + return PremultipliedImage(); + } + return std::move(*getResource().readImage()); } diff --git a/platform/android/MapLibreAndroid/src/cpp/android_vulkan_renderer_backend.hpp b/platform/android/MapLibreAndroid/src/cpp/android_vulkan_renderer_backend.hpp index 0a4791c9d..46a196ce5 100644 --- a/platform/android/MapLibreAndroid/src/cpp/android_vulkan_renderer_backend.hpp +++ b/platform/android/MapLibreAndroid/src/cpp/android_vulkan_renderer_backend.hpp @@ -41,6 +41,8 @@ protected: protected: ANativeWindow* window; + + MBGL_STORE_THREAD(tid); }; } // namespace android diff --git a/src/mbgl/vulkan/renderable_resource.cpp b/src/mbgl/vulkan/renderable_resource.cpp index eac09b0ad..7acccaf64 100644 --- a/src/mbgl/vulkan/renderable_resource.cpp +++ b/src/mbgl/vulkan/renderable_resource.cpp @@ -27,6 +27,8 @@ SurfaceRenderableResource::~SurfaceRenderableResource() { depthAllocation.reset(); colorAllocations.clear(); + + readTexture.reset(); } void SurfaceRenderableResource::initColor(uint32_t w, uint32_t h) { @@ -346,7 +348,17 @@ void SurfaceRenderableResource::setDepthFormat(vk::Format format) { void SurfaceRenderableResource::swap() { auto& context = static_cast(backend.getContext()); + + if (surfaceRead) { + copySurfaceToReadTexture(); + } + context.submitFrame(); + + if (surfaceRead) { + context.waitFrame(); + surfaceRead = false; + } } const vk::Image SurfaceRenderableResource::getAcquiredImage() const { @@ -460,89 +472,106 @@ void SurfaceRenderableResource::recreateSwapchain() { acquireSemaphores.clear(); presentSemaphores.clear(); + readTexture.reset(); + init(extent.width, extent.height); } -void SurfaceRenderableResource::enableSurfaceRead(bool value) { - if (surfaceRead == value) { +void SurfaceRenderableResource::queueSurfaceRead() { + if (surfaceRead) { return; } - surfaceRead = value; + surfaceRead = true; backend.getContext().requestSurfaceUpdate(false); } -std::shared_ptr SurfaceRenderableResource::readImage() { +void SurfaceRenderableResource::copySurfaceToReadTexture() { auto& contextImpl = backend.getContext(); const auto& dispatcher = backend.getDispatcher(); const auto& physicalDevice = backend.getPhysicalDevice(); - auto texture = std::make_unique(contextImpl); - texture->setFormat(gfx::TexturePixelType::RGBA, gfx::TextureChannelDataType::UnsignedByte); - texture->setUsage(Texture2DUsage::Read); - texture->setSize({extent.width, extent.height}); + if (!readTexture) { + readTexture = std::make_unique(contextImpl); + readTexture->setFormat(gfx::TexturePixelType::RGBA, gfx::TextureChannelDataType::UnsignedByte); + readTexture->setUsage(Texture2DUsage::Read); + } - contextImpl.waitFrame(); + readTexture->setSize({extent.width, extent.height}); const auto swapchainImage = getAcquiredImage(); + auto& commandBuffer = contextImpl.getCommandBuffer(); if (surface) { - contextImpl.submitOneTimeCommand([&](const vk::UniqueCommandBuffer& buffer) { - const auto barrier = vk::ImageMemoryBarrier() - .setImage(swapchainImage) - .setOldLayout(vk::ImageLayout::ePresentSrcKHR) - .setNewLayout(vk::ImageLayout::eTransferSrcOptimal) - .setSrcAccessMask({}) - .setDstAccessMask(vk::AccessFlagBits::eTransferWrite) - .setSrcQueueFamilyIndex(VK_QUEUE_FAMILY_IGNORED) - .setDstQueueFamilyIndex(VK_QUEUE_FAMILY_IGNORED) - .setSubresourceRange({vk::ImageAspectFlagBits::eColor, 0, 1, 0, 1}); + const auto barrier = vk::ImageMemoryBarrier() + .setImage(swapchainImage) + .setOldLayout(vk::ImageLayout::ePresentSrcKHR) + .setNewLayout(vk::ImageLayout::eTransferSrcOptimal) + .setSrcAccessMask({}) + .setDstAccessMask(vk::AccessFlagBits::eTransferWrite) + .setSrcQueueFamilyIndex(VK_QUEUE_FAMILY_IGNORED) + .setDstQueueFamilyIndex(VK_QUEUE_FAMILY_IGNORED) + .setSubresourceRange({vk::ImageAspectFlagBits::eColor, 0, 1, 0, 1}); - buffer->pipelineBarrier(vk::PipelineStageFlagBits::eTopOfPipe, - vk::PipelineStageFlagBits::eTransfer, - {}, - nullptr, - nullptr, - barrier, - dispatcher); - }); + commandBuffer->pipelineBarrier(vk::PipelineStageFlagBits::eTopOfPipe, + vk::PipelineStageFlagBits::eTransfer, + {}, + nullptr, + nullptr, + barrier, + dispatcher); } - bool useBlit = surface && - (physicalDevice.getFormatProperties(colorFormat, dispatcher).optimalTilingFeatures & - vk::FormatFeatureFlagBits::eBlitSrc && - physicalDevice.getFormatProperties(texture->getVulkanFormat(), dispatcher).linearTilingFeatures & - vk::FormatFeatureFlagBits::eBlitDst); + bool useBlit = + surface && + (physicalDevice.getFormatProperties(colorFormat, dispatcher).optimalTilingFeatures & + vk::FormatFeatureFlagBits::eBlitSrc && + physicalDevice.getFormatProperties(readTexture->getVulkanFormat(), dispatcher).linearTilingFeatures & + vk::FormatFeatureFlagBits::eBlitDst); if (useBlit) { - texture->blitImage(swapchainImage, texture->getSize()); + readTexture->blitImage(swapchainImage, readTexture->getSize(), 0, 0, commandBuffer); } else { - texture->copyImage(swapchainImage, texture->getSize()); + readTexture->copyImage(swapchainImage, readTexture->getSize(), 0, 0, commandBuffer); } if (surface) { - contextImpl.submitOneTimeCommand([&](const vk::UniqueCommandBuffer& buffer) { - const auto barrier = vk::ImageMemoryBarrier() - .setImage(swapchainImage) - .setOldLayout(vk::ImageLayout::eTransferSrcOptimal) - .setNewLayout(vk::ImageLayout::ePresentSrcKHR) - .setSrcAccessMask(vk::AccessFlagBits::eMemoryRead) - .setDstAccessMask(vk::AccessFlagBits::eTransferWrite) - .setSrcQueueFamilyIndex(VK_QUEUE_FAMILY_IGNORED) - .setDstQueueFamilyIndex(VK_QUEUE_FAMILY_IGNORED) - .setSubresourceRange({vk::ImageAspectFlagBits::eColor, 0, 1, 0, 1}); + const auto barrier = vk::ImageMemoryBarrier() + .setImage(swapchainImage) + .setOldLayout(vk::ImageLayout::eTransferSrcOptimal) + .setNewLayout(vk::ImageLayout::ePresentSrcKHR) + .setSrcAccessMask(vk::AccessFlagBits::eMemoryRead) + .setDstAccessMask(vk::AccessFlagBits::eTransferWrite) + .setSrcQueueFamilyIndex(VK_QUEUE_FAMILY_IGNORED) + .setDstQueueFamilyIndex(VK_QUEUE_FAMILY_IGNORED) + .setSubresourceRange({vk::ImageAspectFlagBits::eColor, 0, 1, 0, 1}); - buffer->pipelineBarrier(vk::PipelineStageFlagBits::eTopOfPipe, - vk::PipelineStageFlagBits::eTransfer, - {}, - nullptr, - nullptr, - barrier, - dispatcher); - }); + commandBuffer->pipelineBarrier(vk::PipelineStageFlagBits::eTopOfPipe, + vk::PipelineStageFlagBits::eTransfer, + {}, + nullptr, + nullptr, + barrier, + dispatcher); + } +} + +std::shared_ptr SurfaceRenderableResource::readImage() { + if (!readTexture) { + return nullptr; } - const auto image = texture->readImage(); + const auto& dispatcher = backend.getDispatcher(); + const auto& physicalDevice = backend.getPhysicalDevice(); + + bool useBlit = + surface && + (physicalDevice.getFormatProperties(colorFormat, dispatcher).optimalTilingFeatures & + vk::FormatFeatureFlagBits::eBlitSrc && + physicalDevice.getFormatProperties(readTexture->getVulkanFormat(), dispatcher).linearTilingFeatures & + vk::FormatFeatureFlagBits::eBlitDst); + + const auto image = readTexture->readImage(); // swizzle pixels to RGBA if (!useBlit && colorFormat == vk::Format::eB8G8R8A8Unorm) { diff --git a/src/mbgl/vulkan/texture2d.cpp b/src/mbgl/vulkan/texture2d.cpp index 01da8c151..6b7b69ed2 100644 --- a/src/mbgl/vulkan/texture2d.cpp +++ b/src/mbgl/vulkan/texture2d.cpp @@ -558,55 +558,77 @@ const vk::Sampler& Texture2D::getVulkanSampler() { } void Texture2D::copyImage(vk::Image image, Size imageSize, uint16_t xOffset, uint16_t yOffset) { - if (!image) return; - - create(); + if (!image) { + return; + } context.submitOneTimeCommand([&](const vk::UniqueCommandBuffer& commandBuffer) { - const auto copyInfo = vk::ImageCopy() - .setSrcSubresource({vk::ImageAspectFlagBits::eColor, 0, 0, 1}) - .setDstSubresource({vk::ImageAspectFlagBits::eColor, 0, 0, 1}) - .setExtent({imageSize.width, imageSize.height, 1}) - .setDstOffset({xOffset, yOffset, 0}); - - transitionToTransferWriteLayout(commandBuffer); - commandBuffer->copyImage(image, - vk::ImageLayout::eTransferSrcOptimal, - imageAllocation->image, - imageLayout, - copyInfo, - context.getBackend().getDispatcher()); - transitionToGeneralLayout(commandBuffer); + copyImage(image, imageSize, xOffset, yOffset, commandBuffer); }); } -void Texture2D::blitImage(vk::Image image, Size imageSize, uint16_t xOffset, uint16_t yOffset) { - if (!image) return; +void Texture2D::copyImage( + vk::Image image, Size imageSize, uint16_t xOffset, uint16_t yOffset, const vk::UniqueCommandBuffer& commandBuffer) { + if (!image) { + return; + } create(); - context.submitOneTimeCommand([&](const vk::UniqueCommandBuffer& commandBuffer) { - const std::array offsets = { - vk::Offset3D{xOffset, yOffset, 0}, - {static_cast(imageSize.width), static_cast(imageSize.height), 1}}; - const auto blitInfo = vk::ImageBlit() - .setSrcOffsets(offsets) - .setDstOffsets(offsets) - .setSrcSubresource({vk::ImageAspectFlagBits::eColor, 0, 0, 1}) - .setDstSubresource({vk::ImageAspectFlagBits::eColor, 0, 0, 1}); + const auto copyInfo = vk::ImageCopy() + .setSrcSubresource({vk::ImageAspectFlagBits::eColor, 0, 0, 1}) + .setDstSubresource({vk::ImageAspectFlagBits::eColor, 0, 0, 1}) + .setExtent({imageSize.width, imageSize.height, 1}) + .setDstOffset({xOffset, yOffset, 0}); - transitionToTransferWriteLayout(commandBuffer); - commandBuffer->blitImage(image, - vk::ImageLayout::eTransferSrcOptimal, - imageAllocation->image, - imageLayout, - blitInfo, - vk::Filter::eNearest, - context.getBackend().getDispatcher()); - transitionToGeneralLayout(commandBuffer); + transitionToTransferWriteLayout(commandBuffer); + commandBuffer->copyImage(image, + vk::ImageLayout::eTransferSrcOptimal, + imageAllocation->image, + imageLayout, + copyInfo, + context.getBackend().getDispatcher()); + transitionToGeneralLayout(commandBuffer); +} + +void Texture2D::blitImage(vk::Image image, Size imageSize, uint16_t xOffset, uint16_t yOffset) { + if (!image) { + return; + } + + context.submitOneTimeCommand([&](const vk::UniqueCommandBuffer& commandBuffer) { + blitImage(image, imageSize, xOffset, yOffset, commandBuffer); }); } +void Texture2D::blitImage( + vk::Image image, Size imageSize, uint16_t xOffset, uint16_t yOffset, const vk::UniqueCommandBuffer& commandBuffer) { + if (!image) { + return; + } + + create(); + + const std::array offsets = { + vk::Offset3D{xOffset, yOffset, 0}, + {static_cast(imageSize.width), static_cast(imageSize.height), 1}}; + const auto blitInfo = vk::ImageBlit() + .setSrcOffsets(offsets) + .setDstOffsets(offsets) + .setSrcSubresource({vk::ImageAspectFlagBits::eColor, 0, 0, 1}) + .setDstSubresource({vk::ImageAspectFlagBits::eColor, 0, 0, 1}); + + transitionToTransferWriteLayout(commandBuffer); + commandBuffer->blitImage(image, + vk::ImageLayout::eTransferSrcOptimal, + imageAllocation->image, + imageLayout, + blitInfo, + vk::Filter::eNearest, + context.getBackend().getDispatcher()); + transitionToGeneralLayout(commandBuffer); +} + std::shared_ptr Texture2D::readImage() { if (!imageData) { imageData = std::make_shared();