Fix surface snapshot timing (#4339)
This commit is contained in:
@@ -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<void(const vk::UniqueCommandBuffer&)>& function);
|
||||
void submitOneTimeCommand(const vk::UniqueCommandBuffer& buffer);
|
||||
|
||||
@@ -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<PremultipliedImage> readImage();
|
||||
|
||||
protected:
|
||||
@@ -104,6 +105,7 @@ protected:
|
||||
|
||||
int32_t surfaceTransformPollingInterval{-1};
|
||||
bool surfaceRead{false};
|
||||
std::unique_ptr<Texture2D> readTexture{nullptr};
|
||||
};
|
||||
|
||||
class Renderable : public gfx::Renderable {
|
||||
|
||||
@@ -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<PremultipliedImage> readImage();
|
||||
|
||||
private:
|
||||
|
||||
@@ -83,11 +83,23 @@ void AndroidVulkanRendererBackend::resizeFramebuffer(int, int) {
|
||||
}
|
||||
}
|
||||
|
||||
void AndroidVulkanRendererBackend::enableFramebufferRead(bool value) {
|
||||
getResource<AndroidVulkanRenderableResource>().enableSurfaceRead();
|
||||
void AndroidVulkanRendererBackend::enableFramebufferRead(bool) {
|
||||
MBGL_VERIFY_THREAD(tid);
|
||||
|
||||
if (!hasResource()) {
|
||||
return;
|
||||
}
|
||||
|
||||
getResource<AndroidVulkanRenderableResource>().queueSurfaceRead();
|
||||
}
|
||||
|
||||
PremultipliedImage AndroidVulkanRendererBackend::readFramebuffer() {
|
||||
MBGL_VERIFY_THREAD(tid);
|
||||
|
||||
if (!hasResource()) {
|
||||
return PremultipliedImage();
|
||||
}
|
||||
|
||||
return std::move(*getResource<AndroidVulkanRenderableResource>().readImage());
|
||||
}
|
||||
|
||||
|
||||
@@ -41,6 +41,8 @@ protected:
|
||||
|
||||
protected:
|
||||
ANativeWindow* window;
|
||||
|
||||
MBGL_STORE_THREAD(tid);
|
||||
};
|
||||
|
||||
} // namespace android
|
||||
|
||||
@@ -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<Context&>(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<Context>().requestSurfaceUpdate(false);
|
||||
}
|
||||
|
||||
std::shared_ptr<PremultipliedImage> SurfaceRenderableResource::readImage() {
|
||||
void SurfaceRenderableResource::copySurfaceToReadTexture() {
|
||||
auto& contextImpl = backend.getContext<Context>();
|
||||
const auto& dispatcher = backend.getDispatcher();
|
||||
const auto& physicalDevice = backend.getPhysicalDevice();
|
||||
|
||||
auto texture = std::make_unique<Texture2D>(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<Texture2D>(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<PremultipliedImage> 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) {
|
||||
|
||||
@@ -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<vk::Offset3D, 2> offsets = {
|
||||
vk::Offset3D{xOffset, yOffset, 0},
|
||||
{static_cast<int32_t>(imageSize.width), static_cast<int32_t>(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<vk::Offset3D, 2> offsets = {
|
||||
vk::Offset3D{xOffset, yOffset, 0},
|
||||
{static_cast<int32_t>(imageSize.width), static_cast<int32_t>(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<PremultipliedImage> Texture2D::readImage() {
|
||||
if (!imageData) {
|
||||
imageData = std::make_shared<PremultipliedImage>();
|
||||
|
||||
Reference in New Issue
Block a user