Vulkan: support WaitForIdle before swap chain construction.

This commit is contained in:
Philip Rideout
2019-02-01 09:55:23 -08:00
parent d1aecf864f
commit 6556345941
5 changed files with 45 additions and 41 deletions
+11 -1
View File
@@ -11,7 +11,7 @@
</style>
**Vulkan Backend**
*Last updated: 27 July 2018*
*Last updated: 31 Jan 2019*
![](../../docs/images/filament_logo.png)
@@ -81,6 +81,16 @@ override; it should have no private methods other than the handle allocators. Th
complexity because it must implement every method in `DriverAPI` while dealing with Vulkan itself,
which has an extremely low-level API.
## Command buffers
We have a separate `VkCommandBuffer` for each element in the swap context. This allows us to
populate command buffer "A" while the GPU is busy consuming command buffer "B". Thus, we typically
have two persistent `VkCommandBuffer` objects: for each element in the swap chain.
However, note that we can receive Driver API requests before the SwapChain has been created. One
typical example is uploading texture data. For these types of operations, we simply create a one-off
command buffer (which is supposedly a lightweight operation in Vulkan) and execute work on it.
## Handle types
This sole purpose of `VulkanHandles.h` is to declare structs that descend from the hardware handles
+2 -1
View File
@@ -228,7 +228,8 @@ void VulkanDriver::beginFrame(int64_t monotonic_clock_ns, uint32_t frameId) {
// VulkanDriver, such as reclaiming memory. There are two sets of work queues: one in the
// global context, and one in the per-cmdbuffer contexts. Crucially, we have begun the frame
// but not the render pass; we cannot perform arbitrary work during the render pass.
performPendingWork(mContext, swapContext, swapContext.cmdbuffer);
performPendingWork(swapContext.pendingWork, swapContext.cmdbuffer);
performPendingWork(mContext.pendingWork, swapContext.cmdbuffer);
// Free old unused objects.
mStagePool.gc();
+29 -36
View File
@@ -788,24 +788,21 @@ void waitForIdle(VulkanContext& context) {
return;
}
// If there's no surface, then there's no command buffer.
if (!context.currentSurface) {
return;
}
// First, wait for submitted command buffer(s) to finish.
VkFence fences[2];
uint32_t nfences = 0;
auto& surfaceContext = *context.currentSurface;
for (auto& swapContext : surfaceContext.swapContexts) {
assert(nfences < 2);
if (swapContext.submitted && swapContext.fence) {
fences[nfences++] = swapContext.fence;
swapContext.submitted = false;
if (context.currentSurface) {
VkFence fences[2];
uint32_t nfences = 0;
auto& surfaceContext = *context.currentSurface;
for (auto& swapContext : surfaceContext.swapContexts) {
assert(nfences < 2);
if (swapContext.submitted && swapContext.fence) {
fences[nfences++] = swapContext.fence;
swapContext.submitted = false;
}
}
if (nfences > 0) {
vkWaitForFences(context.device, nfences, fences, VK_FALSE, ~0ull);
}
}
if (nfences > 0) {
vkWaitForFences(context.device, nfences, fences, VK_FALSE, ~0ull);
}
// If we don't have any pending work, we're done.
@@ -847,15 +844,18 @@ void waitForIdle(VulkanContext& context) {
utils::slog.e << "Unexpected daisychaining of pending work." << utils::io::endl;
break;
}
for (auto& swapContext : context.currentSurface->swapContexts) {
vkBeginCommandBuffer(cmdbuffer, &beginInfo);
performPendingWork(context, swapContext, cmdbuffer);
vkEndCommandBuffer(cmdbuffer);
vkQueueSubmit(context.graphicsQueue, 1, &submitInfo, fence);
vkWaitForFences(context.device, 1, &fence, VK_FALSE, UINT64_MAX);
vkResetFences(context.device, 1, &fence);
vkResetCommandBuffer(cmdbuffer, 0);
vkBeginCommandBuffer(cmdbuffer, &beginInfo);
if (context.currentSurface) {
for (auto& swapContext : context.currentSurface->swapContexts) {
performPendingWork(swapContext.pendingWork, cmdbuffer);
}
}
performPendingWork(context.pendingWork, cmdbuffer);
vkEndCommandBuffer(cmdbuffer);
vkQueueSubmit(context.graphicsQueue, 1, &submitInfo, fence);
vkWaitForFences(context.device, 1, &fence, VK_FALSE, UINT64_MAX);
vkResetFences(context.device, 1, &fence);
vkResetCommandBuffer(cmdbuffer, 0);
}
vkFreeCommandBuffers(context.device, context.commandPool, 1, &cmdbuffer);
vkDestroyFence(context.device, fence, VKALLOC);
@@ -917,18 +917,11 @@ void releaseCommandBuffer(VulkanContext& context) {
swapContext.submitted = true;
}
void performPendingWork(VulkanContext& context, SwapContext& swapContext, VkCommandBuffer cmdbuf) {
// First, execute pending tasks that are specific to this swap context. Copy the tasks into a
// local queue first, which allows newly added tasks to be deferred until the next frame.
decltype(swapContext.pendingWork) tasks;
tasks.swap(swapContext.pendingWork);
for (auto& callback : tasks) {
callback(cmdbuf);
}
// Next, execute the global pending work. Again, we copy the work queue into a local queue
// to allow tasks to re-add themselves.
tasks.clear();
tasks.swap(context.pendingWork);
void performPendingWork(VulkanTaskQueue& work, VkCommandBuffer cmdbuf) {
// Copy the tasks into a local queue first, which allows newly added tasks to be deferred until
// the next frame.
VulkanTaskQueue tasks;
tasks.swap(work);
for (auto& callback : tasks) {
callback(cmdbuf);
}
@@ -122,7 +122,7 @@ VkFrontFace getFrontFace(bool inverseFrontFaces);
void waitForIdle(VulkanContext& context);
void acquireCommandBuffer(VulkanContext& context);
void releaseCommandBuffer(VulkanContext& context);
void performPendingWork(VulkanContext& context, SwapContext& swapContext, VkCommandBuffer cmdbuf);
void performPendingWork(VulkanTaskQueue& work, VkCommandBuffer cmdbuf);
void flushCommandBuffer(VulkanContext& context);
VkFormat findSupportedFormat(VulkanContext& context, const std::vector<VkFormat>& candidates,
VkImageTiling tiling, VkFormatFeatureFlags features);
+2 -2
View File
@@ -514,7 +514,7 @@ void VulkanTexture::update2DImage(const PixelBufferDescriptor& data, uint32_t wi
copyBufferToImage(cmd, stage->buffer, textureImage, width, height, nullptr, miplevel);
transitionImageLayout(cmd, textureImage, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, miplevel, 1);
getSwapContext(mContext).pendingWork.emplace_back([this, stage] (VkCommandBuffer) {
mContext.pendingWork.emplace_back([this, stage] (VkCommandBuffer) {
mStagePool.releaseStage(stage);
});
};
@@ -556,7 +556,7 @@ void VulkanTexture::updateCubeImage(const PixelBufferDescriptor& data,
copyBufferToImage(cmd, stage->buffer, textureImage, width, height, &faceOffsets, miplevel);
transitionImageLayout(cmd, textureImage, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, miplevel, 6);
getSwapContext(mContext).pendingWork.emplace_back([this, stage] (VkCommandBuffer) {
mContext.pendingWork.emplace_back([this, stage] (VkCommandBuffer) {
mStagePool.releaseStage(stage);
});
};