This reverts commit 6f25e8ae5a.
Reason for revert: breaks clients that were expecting synchronous readPixels for vulkan
This commit is contained in:
@@ -179,15 +179,12 @@ if (FILAMENT_SUPPORTS_VULKAN)
|
||||
src/vulkan/VulkanPipelineCache.cpp
|
||||
src/vulkan/VulkanPipelineCache.h
|
||||
src/vulkan/VulkanPlatform.cpp
|
||||
src/vulkan/VulkanReadPixels.cpp
|
||||
src/vulkan/VulkanReadPixels.h
|
||||
src/vulkan/VulkanSamplerCache.cpp
|
||||
src/vulkan/VulkanSamplerCache.h
|
||||
src/vulkan/VulkanStagePool.cpp
|
||||
src/vulkan/VulkanStagePool.h
|
||||
src/vulkan/VulkanSwapChain.cpp
|
||||
src/vulkan/VulkanSwapChain.h
|
||||
src/vulkan/VulkanTaskHandler.h
|
||||
src/vulkan/VulkanTexture.cpp
|
||||
src/vulkan/VulkanTexture.h
|
||||
src/vulkan/VulkanUtility.cpp
|
||||
|
||||
@@ -75,8 +75,6 @@ VulkanDriver::VulkanDriver(VulkanPlatform* platform,
|
||||
mContext.commands->setObserver(&mPipelineCache);
|
||||
mPipelineCache.setDevice(mContext.device, mContext.allocator);
|
||||
mPipelineCache.setDummyTexture(mContext.emptyTexture->getPrimaryImageView());
|
||||
|
||||
mReadPixels.initialize(mContext.device);
|
||||
}
|
||||
|
||||
VulkanDriver::~VulkanDriver() noexcept = default;
|
||||
@@ -136,9 +134,6 @@ void VulkanDriver::terminate() {
|
||||
|
||||
void VulkanDriver::tick(int) {
|
||||
mContext.commands->updateFences();
|
||||
|
||||
// Handle any posted tasks
|
||||
runTaskHandler();
|
||||
}
|
||||
|
||||
// Garbage collection should not occur too frequently, only about once per frame. Internally, the
|
||||
@@ -1330,18 +1325,152 @@ void VulkanDriver::stopCapture(int) {
|
||||
|
||||
}
|
||||
|
||||
void VulkanDriver::readPixels(Handle<HwRenderTarget> src, uint32_t x, uint32_t y, uint32_t width,
|
||||
uint32_t height, PixelBufferDescriptor&& pbd) {
|
||||
void VulkanDriver::readPixels(Handle<HwRenderTarget> src, uint32_t x, uint32_t y,
|
||||
uint32_t width, uint32_t height, PixelBufferDescriptor&& pbd) {
|
||||
const VkDevice device = mContext.device;
|
||||
VulkanRenderTarget* srcTarget = handle_cast<VulkanRenderTarget*>(src);
|
||||
mReadPixels.run(
|
||||
srcTarget, x, y, width, height, mContext.graphicsQueueFamilyIndex, std::move(pbd),
|
||||
getTaskHandler(),
|
||||
[&context = mContext](uint32_t reqs, VkFlags flags) {
|
||||
return context.selectMemoryType(reqs, flags);
|
||||
},
|
||||
[this](PixelBufferDescriptor&& pbd) {
|
||||
this->scheduleDestroy(std::move(pbd));
|
||||
});
|
||||
VulkanTexture* srcTexture = srcTarget->getColor(0).texture;
|
||||
assert_invariant(srcTexture);
|
||||
const VkFormat srcFormat = srcTexture->getVkFormat();
|
||||
const bool swizzle = srcFormat == VK_FORMAT_B8G8R8A8_UNORM;
|
||||
|
||||
// Create a host visible, linearly tiled image as a staging area.
|
||||
|
||||
VkImageCreateInfo imageInfo {
|
||||
.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
|
||||
.imageType = VK_IMAGE_TYPE_2D,
|
||||
.format = srcFormat,
|
||||
.extent = { width, height, 1 },
|
||||
.mipLevels = 1,
|
||||
.arrayLayers = 1,
|
||||
.samples = VK_SAMPLE_COUNT_1_BIT,
|
||||
.tiling = VK_IMAGE_TILING_LINEAR,
|
||||
.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT,
|
||||
.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED,
|
||||
};
|
||||
|
||||
VkImage stagingImage;
|
||||
vkCreateImage(device, &imageInfo, VKALLOC, &stagingImage);
|
||||
|
||||
VkMemoryRequirements memReqs;
|
||||
VkDeviceMemory stagingMemory;
|
||||
vkGetImageMemoryRequirements(device, stagingImage, &memReqs);
|
||||
VkMemoryAllocateInfo allocInfo = {
|
||||
.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
|
||||
.allocationSize = memReqs.size,
|
||||
.memoryTypeIndex = mContext.selectMemoryType(memReqs.memoryTypeBits,
|
||||
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT |
|
||||
VK_MEMORY_PROPERTY_HOST_CACHED_BIT)
|
||||
};
|
||||
|
||||
vkAllocateMemory(device, &allocInfo, nullptr, &stagingMemory);
|
||||
vkBindImageMemory(device, stagingImage, stagingMemory, 0);
|
||||
|
||||
// TODO: don't flush/wait here, this should be asynchronous
|
||||
|
||||
mContext.commands->flush();
|
||||
mContext.commands->wait();
|
||||
|
||||
// Transition the staging image layout.
|
||||
|
||||
const VkCommandBuffer cmdbuffer = mContext.commands->get().cmdbuffer;
|
||||
|
||||
transitionImageLayout(cmdbuffer, {
|
||||
.image = stagingImage,
|
||||
.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED,
|
||||
.newLayout = VK_IMAGE_LAYOUT_GENERAL,
|
||||
.subresources = {
|
||||
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
|
||||
.baseMipLevel = 0,
|
||||
.levelCount = 1,
|
||||
.baseArrayLayer = 0,
|
||||
.layerCount = 1,
|
||||
},
|
||||
.srcStage = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT,
|
||||
.srcAccessMask = 0,
|
||||
.dstStage = VK_PIPELINE_STAGE_TRANSFER_BIT,
|
||||
.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT,
|
||||
});
|
||||
|
||||
const VulkanAttachment srcAttachment = srcTarget->getColor(0);
|
||||
|
||||
VkImageCopy imageCopyRegion = {
|
||||
.srcSubresource = {
|
||||
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
|
||||
.mipLevel = srcAttachment.level,
|
||||
.baseArrayLayer = srcAttachment.layer,
|
||||
.layerCount = 1,
|
||||
},
|
||||
.srcOffset = {
|
||||
.x = (int32_t) x,
|
||||
.y = (int32_t) (srcTarget->getExtent().height - (height + y)),
|
||||
},
|
||||
.dstSubresource = {
|
||||
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
|
||||
.layerCount = 1,
|
||||
},
|
||||
.extent = {
|
||||
.width = width,
|
||||
.height = height,
|
||||
.depth = 1,
|
||||
},
|
||||
};
|
||||
|
||||
// Transition the source image layout (which might be the swap chain)
|
||||
|
||||
const VkImageSubresourceRange srcRange = {
|
||||
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
|
||||
.baseMipLevel = srcAttachment.level,
|
||||
.levelCount = 1,
|
||||
.baseArrayLayer = srcAttachment.layer,
|
||||
.layerCount = 1,
|
||||
};
|
||||
|
||||
srcTexture->transitionLayout(cmdbuffer, srcRange, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL);
|
||||
|
||||
// Perform the copy into the staging area. At this point we know that the src layout is
|
||||
// TRANSFER_SRC_OPTIMAL and the staging area is GENERAL.
|
||||
|
||||
UTILS_UNUSED_IN_RELEASE VkExtent2D srcExtent = srcAttachment.getExtent2D();
|
||||
assert_invariant(imageCopyRegion.srcOffset.x + imageCopyRegion.extent.width <= srcExtent.width);
|
||||
assert_invariant(imageCopyRegion.srcOffset.y + imageCopyRegion.extent.height <= srcExtent.height);
|
||||
|
||||
vkCmdCopyImage(cmdbuffer, srcAttachment.getImage(),
|
||||
VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, stagingImage, VK_IMAGE_LAYOUT_GENERAL,
|
||||
1, &imageCopyRegion);
|
||||
|
||||
// Restore the source image layout. Between driver API calls, color images are always kept in
|
||||
// UNDEFINED layout or in their "usage default" layout (see comment for getDefaultImageLayout).
|
||||
|
||||
srcTexture->transitionLayout(cmdbuffer, srcRange,
|
||||
getDefaultImageLayout(TextureUsage::COLOR_ATTACHMENT));
|
||||
|
||||
// TODO: don't flush/wait here -- we should do this asynchronously
|
||||
|
||||
// Flush and wait.
|
||||
mContext.commands->flush();
|
||||
mContext.commands->wait();
|
||||
|
||||
VkImageSubresource subResource { .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT };
|
||||
VkSubresourceLayout subResourceLayout;
|
||||
vkGetImageSubresourceLayout(device, stagingImage, &subResource, &subResourceLayout);
|
||||
|
||||
// Map image memory so we can start copying from it.
|
||||
|
||||
const uint8_t* srcPixels;
|
||||
vkMapMemory(device, stagingMemory, 0, VK_WHOLE_SIZE, 0, (void**) &srcPixels);
|
||||
srcPixels += subResourceLayout.offset;
|
||||
|
||||
if (!DataReshaper::reshapeImage(&pbd, getComponentType(srcFormat), getComponentCount(srcFormat),
|
||||
srcPixels, subResourceLayout.rowPitch, width, height, swizzle)) {
|
||||
utils::slog.e << "Unsupported PixelDataFormat or PixelDataType" << utils::io::endl;
|
||||
}
|
||||
|
||||
vkUnmapMemory(device, stagingMemory);
|
||||
vkDestroyImage(device, stagingImage, nullptr);
|
||||
vkFreeMemory(device, stagingMemory, nullptr);
|
||||
|
||||
scheduleDestroy(std::move(pbd));
|
||||
}
|
||||
|
||||
void VulkanDriver::readBufferSubData(backend::BufferObjectHandle boh,
|
||||
|
||||
@@ -23,10 +23,8 @@
|
||||
#include "VulkanConstants.h"
|
||||
#include "VulkanContext.h"
|
||||
#include "VulkanFboCache.h"
|
||||
#include "VulkanReadPixels.h"
|
||||
#include "VulkanSamplerCache.h"
|
||||
#include "VulkanStagePool.h"
|
||||
#include "VulkanTaskHandler.h"
|
||||
#include "VulkanUtility.h"
|
||||
|
||||
#include "private/backend/Driver.h"
|
||||
@@ -41,14 +39,11 @@ namespace filament::backend {
|
||||
class VulkanPlatform;
|
||||
struct VulkanSamplerGroup;
|
||||
|
||||
class VulkanDriver final : public DriverBase, private VulkanTaskHandler::Host {
|
||||
class VulkanDriver final : public DriverBase {
|
||||
public:
|
||||
static Driver* create(VulkanPlatform* platform,
|
||||
const char* const* ppEnabledExtensions, uint32_t enabledExtensionCount, const Platform::DriverConfig& driverConfig) noexcept;
|
||||
|
||||
VulkanDriver(VulkanDriver const&) = delete;
|
||||
VulkanDriver& operator = (VulkanDriver const&) = delete;
|
||||
|
||||
private:
|
||||
|
||||
void debugCommandBegin(CommandStream* cmds, bool synchronous, const char* methodName) noexcept override;
|
||||
@@ -78,6 +73,9 @@ private:
|
||||
|
||||
#include "private/backend/DriverAPI.inc"
|
||||
|
||||
VulkanDriver(VulkanDriver const&) = delete;
|
||||
VulkanDriver& operator = (VulkanDriver const&) = delete;
|
||||
|
||||
private:
|
||||
|
||||
HandleAllocatorVK mHandleAllocator;
|
||||
@@ -153,7 +151,6 @@ private:
|
||||
VulkanSamplerCache mSamplerCache;
|
||||
VulkanBlitter mBlitter;
|
||||
VulkanSamplerGroup* mSamplerBindings[VulkanPipelineCache::SAMPLER_BINDING_COUNT] = {};
|
||||
VulkanReadPixels mReadPixels;
|
||||
};
|
||||
|
||||
} // namespace filament::backend
|
||||
|
||||
@@ -1,261 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2023 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "VulkanReadPixels.h"
|
||||
|
||||
#include "DataReshaper.h"
|
||||
#include "VulkanHandles.h"
|
||||
#include "VulkanTaskHandler.h"
|
||||
#include "VulkanTexture.h"
|
||||
|
||||
#include <utils/Log.h>
|
||||
|
||||
using namespace bluevk;
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
VulkanReadPixels::~VulkanReadPixels() noexcept {
|
||||
assert_invariant(mDevice != VK_NULL_HANDLE);
|
||||
if (mCommandPool == VK_NULL_HANDLE) {
|
||||
return;
|
||||
}
|
||||
vkDestroyCommandPool(mDevice, mCommandPool, VKALLOC);
|
||||
}
|
||||
|
||||
void VulkanReadPixels::initialize(VkDevice device) {
|
||||
mDevice = device;
|
||||
}
|
||||
|
||||
void VulkanReadPixels::run(VulkanRenderTarget const* srcTarget, uint32_t const x, uint32_t const y,
|
||||
uint32_t const width, uint32_t const height, uint32_t const graphicsQueueFamilyIndex,
|
||||
PixelBufferDescriptor&& pbd, VulkanTaskHandler& taskHandler,
|
||||
SelecteMemoryFunction const& selectMemoryFunc,
|
||||
OnReadCompleteFunction const& readCompleteFunc) {
|
||||
assert_invariant(mDevice != VK_NULL_HANDLE);
|
||||
|
||||
VkDevice device = mDevice;
|
||||
|
||||
if (mCommandPool == VK_NULL_HANDLE) {
|
||||
// Create a command pool if one has not been created.
|
||||
VkCommandPoolCreateInfo createInfo = {
|
||||
.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO,
|
||||
.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT
|
||||
| VK_COMMAND_POOL_CREATE_TRANSIENT_BIT,
|
||||
.queueFamilyIndex = graphicsQueueFamilyIndex,
|
||||
};
|
||||
vkCreateCommandPool(device, &createInfo, VKALLOC, &mCommandPool);
|
||||
}
|
||||
VkCommandPool cmdpool = mCommandPool;
|
||||
|
||||
VulkanTexture* srcTexture = srcTarget->getColor(0).texture;
|
||||
assert_invariant(srcTexture);
|
||||
VkFormat const srcFormat = srcTexture->getVkFormat();
|
||||
bool const swizzle
|
||||
= srcFormat == VK_FORMAT_B8G8R8A8_UNORM || srcFormat == VK_FORMAT_B8G8R8A8_SRGB;
|
||||
|
||||
// Create a host visible, linearly tiled image as a staging area.
|
||||
VkImageCreateInfo const imageInfo{
|
||||
.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
|
||||
.imageType = VK_IMAGE_TYPE_2D,
|
||||
.format = srcFormat,
|
||||
.extent = {width, height, 1},
|
||||
.mipLevels = 1,
|
||||
.arrayLayers = 1,
|
||||
.samples = VK_SAMPLE_COUNT_1_BIT,
|
||||
.tiling = VK_IMAGE_TILING_LINEAR,
|
||||
.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT,
|
||||
.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED,
|
||||
};
|
||||
|
||||
VkImage stagingImage;
|
||||
vkCreateImage(device, &imageInfo, VKALLOC, &stagingImage);
|
||||
|
||||
VkMemoryRequirements memReqs;
|
||||
VkDeviceMemory stagingMemory;
|
||||
vkGetImageMemoryRequirements(device, stagingImage, &memReqs);
|
||||
VkMemoryAllocateInfo const allocInfo = {.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
|
||||
.allocationSize = memReqs.size,
|
||||
.memoryTypeIndex = selectMemoryFunc(memReqs.memoryTypeBits,
|
||||
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT
|
||||
| VK_MEMORY_PROPERTY_HOST_CACHED_BIT)};
|
||||
|
||||
vkAllocateMemory(device, &allocInfo, VKALLOC, &stagingMemory);
|
||||
vkBindImageMemory(device, stagingImage, stagingMemory, 0);
|
||||
|
||||
VkCommandBuffer cmdbuffer;
|
||||
VkCommandBufferAllocateInfo const allocateInfo{
|
||||
.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO,
|
||||
.commandPool = cmdpool,
|
||||
.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY,
|
||||
.commandBufferCount = 1,
|
||||
};
|
||||
vkAllocateCommandBuffers(device, &allocateInfo, &cmdbuffer);
|
||||
|
||||
VkCommandBufferBeginInfo const binfo{
|
||||
.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,
|
||||
.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT,
|
||||
};
|
||||
vkBeginCommandBuffer(cmdbuffer, &binfo);
|
||||
|
||||
transitionImageLayout(cmdbuffer, {
|
||||
.image = stagingImage,
|
||||
.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED,
|
||||
.newLayout = VK_IMAGE_LAYOUT_GENERAL,
|
||||
.subresources = {
|
||||
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
|
||||
.baseMipLevel = 0,
|
||||
.levelCount = 1,
|
||||
.baseArrayLayer = 0,
|
||||
.layerCount = 1,
|
||||
},
|
||||
.srcStage = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT,
|
||||
.srcAccessMask = 0,
|
||||
.dstStage = VK_PIPELINE_STAGE_TRANSFER_BIT,
|
||||
.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT,
|
||||
});
|
||||
|
||||
VulkanAttachment const srcAttachment = srcTarget->getColor(0);
|
||||
|
||||
VkImageCopy const imageCopyRegion = {
|
||||
.srcSubresource = {
|
||||
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
|
||||
.mipLevel = srcAttachment.level,
|
||||
.baseArrayLayer = srcAttachment.layer,
|
||||
.layerCount = 1,
|
||||
},
|
||||
.srcOffset = {
|
||||
.x = (int32_t) x,
|
||||
.y = (int32_t) (srcTarget->getExtent().height - (height + y)),
|
||||
},
|
||||
.dstSubresource = {
|
||||
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
|
||||
.layerCount = 1,
|
||||
},
|
||||
.extent = {
|
||||
.width = width,
|
||||
.height = height,
|
||||
.depth = 1,
|
||||
},
|
||||
};
|
||||
|
||||
// Transition the source image layout (which might be the swap chain)
|
||||
VkImageSubresourceRange const srcRange = {
|
||||
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
|
||||
.baseMipLevel = srcAttachment.level,
|
||||
.levelCount = 1,
|
||||
.baseArrayLayer = srcAttachment.layer,
|
||||
.layerCount = 1,
|
||||
};
|
||||
|
||||
srcTexture->transitionLayout(cmdbuffer, srcRange, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL);
|
||||
|
||||
// Perform the copy into the staging area. At this point we know that the src layout is
|
||||
// TRANSFER_SRC_OPTIMAL and the staging area is GENERAL.
|
||||
UTILS_UNUSED_IN_RELEASE VkExtent2D srcExtent = srcAttachment.getExtent2D();
|
||||
assert_invariant(imageCopyRegion.srcOffset.x + imageCopyRegion.extent.width <= srcExtent.width);
|
||||
assert_invariant(
|
||||
imageCopyRegion.srcOffset.y + imageCopyRegion.extent.height <= srcExtent.height);
|
||||
|
||||
vkCmdCopyImage(cmdbuffer, srcAttachment.getImage(), VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
|
||||
stagingImage, VK_IMAGE_LAYOUT_GENERAL, 1, &imageCopyRegion);
|
||||
|
||||
// Restore the source image layout. Between driver API calls, color images are always kept in
|
||||
// UNDEFINED layout or in their "usage default" layout (see comment for getDefaultImageLayout).
|
||||
srcTexture->transitionLayout(cmdbuffer, srcRange,
|
||||
getDefaultImageLayout(TextureUsage::COLOR_ATTACHMENT));
|
||||
|
||||
vkEndCommandBuffer(cmdbuffer);
|
||||
|
||||
VkQueue queue;
|
||||
vkGetDeviceQueue(device, graphicsQueueFamilyIndex, 0, &queue);
|
||||
|
||||
VkSubmitInfo const submitInfo{
|
||||
.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO,
|
||||
.waitSemaphoreCount = 0,
|
||||
.pWaitSemaphores = VK_NULL_HANDLE,
|
||||
.pWaitDstStageMask = VK_NULL_HANDLE,
|
||||
.commandBufferCount = 1,
|
||||
.pCommandBuffers = &cmdbuffer,
|
||||
.signalSemaphoreCount = 0,
|
||||
.pSignalSemaphores = VK_NULL_HANDLE,
|
||||
};
|
||||
VkFence fence;
|
||||
VkFenceCreateInfo const fenceCreateInfo{
|
||||
.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO,
|
||||
};
|
||||
vkCreateFence(device, &fenceCreateInfo, VKALLOC, &fence);
|
||||
vkQueueSubmit(queue, 1, &submitInfo, fence);
|
||||
|
||||
auto* const pUserBuffer = new PixelBufferDescriptor(std::move(pbd));
|
||||
auto const waitTaskId = taskHandler.createTask(
|
||||
[device, width, height, swizzle, srcFormat, fence, stagingImage, stagingMemory, cmdpool,
|
||||
cmdbuffer, pUserBuffer, readCompleteFunc,
|
||||
&taskHandler](VulkanTaskHandler::TaskId taskId, void* data) mutable {
|
||||
PixelBufferDescriptor& p = *pUserBuffer;
|
||||
vkWaitForFences(device, 1, &fence, VK_TRUE, UINT64_MAX);
|
||||
VkResult status = vkGetFenceStatus(device, fence);
|
||||
|
||||
// Fence hasn't been reached. Try waiting again.
|
||||
if (status == VK_NOT_READY) {
|
||||
taskHandler.post(taskId);
|
||||
return;
|
||||
}
|
||||
|
||||
// Need to abort the readPixels if the device is lost.
|
||||
if (status == VK_ERROR_DEVICE_LOST) {
|
||||
utils::slog.e << "Device lost while in VulkanReadPixels::run"
|
||||
<< utils::io::endl;
|
||||
taskHandler.completed(taskId);
|
||||
|
||||
// Try to free the pbd anyway
|
||||
readCompleteFunc(std::move(p));
|
||||
delete pUserBuffer;
|
||||
return;
|
||||
}
|
||||
|
||||
VkImageSubresource subResource{.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT};
|
||||
VkSubresourceLayout subResourceLayout;
|
||||
vkGetImageSubresourceLayout(device, stagingImage, &subResource, &subResourceLayout);
|
||||
|
||||
// Map image memory so that we can start copying from it.
|
||||
uint8_t const* srcPixels;
|
||||
vkMapMemory(device, stagingMemory, 0, VK_WHOLE_SIZE, 0, (void**) &srcPixels);
|
||||
srcPixels += subResourceLayout.offset;
|
||||
|
||||
if (!DataReshaper::reshapeImage(&p, getComponentType(srcFormat),
|
||||
getComponentCount(srcFormat), srcPixels,
|
||||
static_cast<int>(subResourceLayout.rowPitch), static_cast<int>(width),
|
||||
static_cast<int>(height), swizzle)) {
|
||||
utils::slog.e << "Unsupported PixelDataFormat or PixelDataType"
|
||||
<< utils::io::endl;
|
||||
}
|
||||
|
||||
vkUnmapMemory(device, stagingMemory);
|
||||
vkDestroyImage(device, stagingImage, VKALLOC);
|
||||
vkFreeMemory(device, stagingMemory, VKALLOC);
|
||||
vkDestroyFence(device, fence, VKALLOC);
|
||||
vkFreeCommandBuffers(device, cmdpool, 1, &cmdbuffer);
|
||||
readCompleteFunc(std::move(p));
|
||||
delete pUserBuffer;
|
||||
|
||||
taskHandler.completed(taskId);
|
||||
},
|
||||
nullptr);
|
||||
|
||||
taskHandler.post(waitTaskId);
|
||||
}
|
||||
|
||||
}// namespace filament::backend
|
||||
@@ -1,52 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2023 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef TNT_FILAMENT_BACKEND_VULKANREADPIXELS_H
|
||||
#define TNT_FILAMENT_BACKEND_VULKANREADPIXELS_H
|
||||
|
||||
#include "VulkanTaskHandler.h"
|
||||
#include "private/backend/Driver.h"
|
||||
|
||||
#include <bluevk/BlueVK.h>
|
||||
#include <math/vec4.h>
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
struct VulkanContext;
|
||||
struct VulkanRenderTarget;
|
||||
|
||||
class VulkanReadPixels {
|
||||
public:
|
||||
using OnReadCompleteFunction = std::function<void(PixelBufferDescriptor&&)>;
|
||||
using SelecteMemoryFunction = std::function<uint32_t(uint32_t, VkFlags)>;
|
||||
|
||||
~VulkanReadPixels() noexcept;
|
||||
|
||||
void initialize(VkDevice device);
|
||||
|
||||
void run(VulkanRenderTarget const* srcTarget, uint32_t x, uint32_t y, uint32_t width,
|
||||
uint32_t height, uint32_t graphicsQueueFamilyIndex, PixelBufferDescriptor&& pbd,
|
||||
VulkanTaskHandler& taskHandler, SelecteMemoryFunction const& selectMemoryFunc,
|
||||
OnReadCompleteFunction const& readCompleteFunc);
|
||||
|
||||
private:
|
||||
VkDevice mDevice = VK_NULL_HANDLE;
|
||||
VkCommandPool mCommandPool = VK_NULL_HANDLE;
|
||||
};
|
||||
|
||||
}// namespace filament::backend
|
||||
|
||||
#endif//TNT_FILAMENT_BACKEND_VULKANREADPIXELS_H
|
||||
@@ -1,104 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2023 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef TNT_FILAMENT_BACKEND_VULKANTASKHANDLER_H
|
||||
#define TNT_FILAMENT_BACKEND_VULKANTASKHANDLER_H
|
||||
|
||||
#include "utils/Panic.h"
|
||||
|
||||
#include <functional>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <queue>
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
class VulkanTaskHandler {
|
||||
public:
|
||||
// The Host class is meant to be called on the thread that processes the tasks. It has access to
|
||||
// the handle function.
|
||||
class Host {
|
||||
public:
|
||||
Host(Host const&) = delete;
|
||||
Host& operator=(Host const&) = delete;
|
||||
protected:
|
||||
Host()
|
||||
: mHandler(std::make_unique<VulkanTaskHandler>()) {}
|
||||
|
||||
void runTaskHandler() noexcept {
|
||||
mHandler->handle();
|
||||
}
|
||||
|
||||
VulkanTaskHandler& getTaskHandler() noexcept {
|
||||
return *mHandler;
|
||||
}
|
||||
private:
|
||||
std::unique_ptr<VulkanTaskHandler> mHandler;
|
||||
};
|
||||
|
||||
using TaskId = uint32_t;
|
||||
using TaskFunc = std::function<void(TaskId, void*)>;
|
||||
using Task = std::pair<TaskFunc, void*>;
|
||||
|
||||
TaskId createTask(TaskFunc const& func, void* data) noexcept {
|
||||
TaskId const id = mNextTaskId++;
|
||||
mTasks[id] = std::pair(func, data);
|
||||
return id;
|
||||
}
|
||||
|
||||
// Task that will never be put on the queue again should be marked as completed by calling this
|
||||
// function.
|
||||
void completed(TaskId taskId) noexcept {
|
||||
assert_invariant(mTasks.find(taskId) != mTasks.end());
|
||||
mTasks.erase(taskId);
|
||||
}
|
||||
|
||||
void post(TaskId taskId) noexcept {
|
||||
assert_invariant(mTasks.find(taskId) != mTasks.end());
|
||||
mTaskQueue.push(taskId);
|
||||
}
|
||||
|
||||
VulkanTaskHandler() = default;
|
||||
|
||||
VulkanTaskHandler(VulkanTaskHandler const&) = delete;
|
||||
VulkanTaskHandler& operator=(VulkanTaskHandler const&) = delete;
|
||||
|
||||
private:
|
||||
inline void handle() {
|
||||
while (!mTaskQueue.empty()) {
|
||||
auto taskId = mTaskQueue.front();
|
||||
mTaskQueue.pop();
|
||||
// It is possible for taskIds in the queue to refer to a task that has already been
|
||||
// completed. Just ignore.
|
||||
if (mTasks.find(taskId) == mTasks.end()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
auto& [func, data] = mTasks[taskId];
|
||||
func(taskId, data);
|
||||
}
|
||||
}
|
||||
|
||||
std::queue<TaskId> mTaskQueue;
|
||||
std::map<TaskId, Task> mTasks;
|
||||
uint32_t mNextTaskId = 0;
|
||||
|
||||
friend class Host;
|
||||
};
|
||||
|
||||
}// namespace filament::backend
|
||||
|
||||
#endif// TNT_FILAMENT_BACKEND_VULKANTASKHANDLER_H
|
||||
Reference in New Issue
Block a user