Revert "vulkan: async readPixels (#6605)" (#6644)

This reverts commit 6f25e8ae5a.

Reason for revert: breaks clients that were expecting synchronous readPixels for vulkan
This commit is contained in:
Powei Feng
2023-03-16 12:55:19 -07:00
committed by GitHub
parent fdc2ab30c0
commit ae12e3a51c
6 changed files with 149 additions and 443 deletions

View File

@@ -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

View File

@@ -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,

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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