diff --git a/filament/docs/Vulkan.md.html b/filament/docs/Vulkan.md.html
index 81467464ed..75e2724f26 100644
--- a/filament/docs/Vulkan.md.html
+++ b/filament/docs/Vulkan.md.html
@@ -11,7 +11,7 @@
**Vulkan Backend**
- *Last updated: 27 July 2018*
+ *Last updated: 31 Jan 2019*

@@ -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
diff --git a/filament/src/driver/vulkan/VulkanDriver.cpp b/filament/src/driver/vulkan/VulkanDriver.cpp
index e983435caf..3d04413313 100644
--- a/filament/src/driver/vulkan/VulkanDriver.cpp
+++ b/filament/src/driver/vulkan/VulkanDriver.cpp
@@ -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();
diff --git a/filament/src/driver/vulkan/VulkanDriverImpl.cpp b/filament/src/driver/vulkan/VulkanDriverImpl.cpp
index 747154f0f7..15ceb7b358 100644
--- a/filament/src/driver/vulkan/VulkanDriverImpl.cpp
+++ b/filament/src/driver/vulkan/VulkanDriverImpl.cpp
@@ -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);
}
diff --git a/filament/src/driver/vulkan/VulkanDriverImpl.h b/filament/src/driver/vulkan/VulkanDriverImpl.h
index 3225ec9a8f..51b8687e54 100644
--- a/filament/src/driver/vulkan/VulkanDriverImpl.h
+++ b/filament/src/driver/vulkan/VulkanDriverImpl.h
@@ -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& candidates,
VkImageTiling tiling, VkFormatFeatureFlags features);
diff --git a/filament/src/driver/vulkan/VulkanHandles.cpp b/filament/src/driver/vulkan/VulkanHandles.cpp
index 550fd1babb..d45bf38a76 100644
--- a/filament/src/driver/vulkan/VulkanHandles.cpp
+++ b/filament/src/driver/vulkan/VulkanHandles.cpp
@@ -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);
});
};