Commit a9c60e9f by Jamie Madill Committed by Commit Bot

Vulkan: Allow for multiple frames in-flight.

This should give us the ability to send off more than one frame to the presentation engine at once. Instead of using a single pair of Semaphores to lock each surface, we make a Semaphore pair per Swapchain image. BUG=angleproject:1898 Change-Id: I9e833ed9969a79617d0a8968b0d5a25c27139e87 Reviewed-on: https://chromium-review.googlesource.com/672149 Commit-Queue: Jamie Madill <jmadill@chromium.org> Reviewed-by: 's avatarFrank Henigman <fjhenigman@chromium.org>
parent db342276
...@@ -37,7 +37,7 @@ VkPresentModeKHR GetDesiredPresentMode(const std::vector<VkPresentModeKHR> &pres ...@@ -37,7 +37,7 @@ VkPresentModeKHR GetDesiredPresentMode(const std::vector<VkPresentModeKHR> &pres
ASSERT(!presentModes.empty()); ASSERT(!presentModes.empty());
// Use FIFO mode for v-sync, since it throttles you to the display rate. Mailbox is more // Use FIFO mode for v-sync, since it throttles you to the display rate. Mailbox is more
// similar to triple-buffering. For now we hard-code Mailbox for perf tseting. // similar to triple-buffering. For now we hard-code Mailbox for perf testing.
// TODO(jmadill): Properly select present mode and re-create display if changed. // TODO(jmadill): Properly select present mode and re-create display if changed.
VkPresentModeKHR bestChoice = VK_PRESENT_MODE_MAILBOX_KHR; VkPresentModeKHR bestChoice = VK_PRESENT_MODE_MAILBOX_KHR;
...@@ -183,23 +183,17 @@ void WindowSurfaceVk::destroy(const egl::Display *display) ...@@ -183,23 +183,17 @@ void WindowSurfaceVk::destroy(const egl::Display *display)
rendererVk->finish(); rendererVk->finish();
mImageAvailableSemaphore.destroy(device); mAcquireNextImageSemaphore.destroy(device);
mRenderingCompleteSemaphore.destroy(device);
for (auto &imageView : mSwapchainImageViews) for (auto &swapchainImage : mSwapchainImages)
{ {
imageView.destroy(device); // Although we don't own the swapchain image handles, we need to keep our shutdown clean.
} swapchainImage.image.reset();
// Although we don't own the swapchain image handles, we need to keep our shutdown clean.
for (auto &image : mSwapchainImages)
{
image.reset();
}
for (auto &framebuffer : mSwapchainFramebuffers) swapchainImage.imageView.destroy(device);
{ swapchainImage.framebuffer.destroy(device);
framebuffer.destroy(device); swapchainImage.imageAcquiredSemaphore.destroy(device);
swapchainImage.commandsCompleteSemaphore.destroy(device);
} }
if (mSwapchain) if (mSwapchain)
...@@ -365,7 +359,8 @@ vk::Error WindowSurfaceVk::initializeImpl(RendererVk *renderer) ...@@ -365,7 +359,8 @@ vk::Error WindowSurfaceVk::initializeImpl(RendererVk *renderer)
transparentBlack.float32[3] = 0.0f; transparentBlack.float32[3] = 0.0f;
mSwapchainImages.resize(imageCount); mSwapchainImages.resize(imageCount);
mSwapchainImageViews.resize(imageCount);
ANGLE_TRY(mAcquireNextImageSemaphore.init(device));
for (uint32_t imageIndex = 0; imageIndex < imageCount; ++imageIndex) for (uint32_t imageIndex = 0; imageIndex < imageCount; ++imageIndex)
{ {
...@@ -397,15 +392,16 @@ vk::Error WindowSurfaceVk::initializeImpl(RendererVk *renderer) ...@@ -397,15 +392,16 @@ vk::Error WindowSurfaceVk::initializeImpl(RendererVk *renderer)
commandBuffer); commandBuffer);
commandBuffer->clearSingleColorImage(image, transparentBlack); commandBuffer->clearSingleColorImage(image, transparentBlack);
mSwapchainImages[imageIndex].retain(device, std::move(image)); auto &member = mSwapchainImages[imageIndex];
mSwapchainImageViews[imageIndex].retain(device, std::move(imageView));
member.image.retain(device, std::move(image));
member.imageView.retain(device, std::move(imageView));
ANGLE_TRY(member.imageAcquiredSemaphore.init(device));
ANGLE_TRY(member.commandsCompleteSemaphore.init(device));
} }
ANGLE_TRY(renderer->submitAndFinishCommandBuffer(commandBuffer)); ANGLE_TRY(renderer->submitAndFinishCommandBuffer(commandBuffer));
ANGLE_TRY(mImageAvailableSemaphore.init(device));
ANGLE_TRY(mRenderingCompleteSemaphore.init(device));
// Get the first available swapchain iamge. // Get the first available swapchain iamge.
ANGLE_TRY(nextSwapchainImage(renderer)); ANGLE_TRY(nextSwapchainImage(renderer));
...@@ -429,20 +425,20 @@ egl::Error WindowSurfaceVk::swap(const gl::Context *context) ...@@ -429,20 +425,20 @@ egl::Error WindowSurfaceVk::swap(const gl::Context *context)
FramebufferVk *framebufferVk = GetImplAs<FramebufferVk>(mState.defaultFramebuffer); FramebufferVk *framebufferVk = GetImplAs<FramebufferVk>(mState.defaultFramebuffer);
framebufferVk->endRenderPass(currentCB); framebufferVk->endRenderPass(currentCB);
auto *image = &mSwapchainImages[mCurrentSwapchainImageIndex]; auto &image = mSwapchainImages[mCurrentSwapchainImageIndex];
image->changeLayoutWithStages(VK_IMAGE_ASPECT_COLOR_BIT, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, image.image.changeLayoutWithStages(VK_IMAGE_ASPECT_COLOR_BIT, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR,
VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT,
VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, currentCB); VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, currentCB);
ANGLE_TRY(renderer->submitCommandsWithSync(currentCB, mImageAvailableSemaphore, ANGLE_TRY(renderer->submitCommandsWithSync(currentCB, image.imageAcquiredSemaphore,
mRenderingCompleteSemaphore)); image.commandsCompleteSemaphore));
VkPresentInfoKHR presentInfo; VkPresentInfoKHR presentInfo;
presentInfo.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR; presentInfo.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR;
presentInfo.pNext = nullptr; presentInfo.pNext = nullptr;
presentInfo.waitSemaphoreCount = 1; presentInfo.waitSemaphoreCount = 1;
presentInfo.pWaitSemaphores = mRenderingCompleteSemaphore.ptr(); presentInfo.pWaitSemaphores = image.commandsCompleteSemaphore.ptr();
presentInfo.swapchainCount = 1; presentInfo.swapchainCount = 1;
presentInfo.pSwapchains = &mSwapchain; presentInfo.pSwapchains = &mSwapchain;
presentInfo.pImageIndices = &mCurrentSwapchainImageIndex; presentInfo.pImageIndices = &mCurrentSwapchainImageIndex;
...@@ -460,13 +456,20 @@ vk::Error WindowSurfaceVk::nextSwapchainImage(RendererVk *renderer) ...@@ -460,13 +456,20 @@ vk::Error WindowSurfaceVk::nextSwapchainImage(RendererVk *renderer)
{ {
VkDevice device = renderer->getDevice(); VkDevice device = renderer->getDevice();
ANGLE_VK_TRY(vkAcquireNextImageKHR(device, mSwapchain, std::numeric_limits<uint64_t>::max(), // Use a timeout of zero for AcquireNextImage so we don't actually block.
mImageAvailableSemaphore.getHandle(), VK_NULL_HANDLE, // TODO(jmadill): We should handle VK_NOT_READY and block until we can acquire the image.
ANGLE_VK_TRY(vkAcquireNextImageKHR(device, mSwapchain, 0,
mAcquireNextImageSemaphore.getHandle(), VK_NULL_HANDLE,
&mCurrentSwapchainImageIndex)); &mCurrentSwapchainImageIndex));
auto &image = mSwapchainImages[mCurrentSwapchainImageIndex];
// Swap the unused swapchain semaphore and the now active spare semaphore.
std::swap(image.imageAcquiredSemaphore, mAcquireNextImageSemaphore);
// Update RenderTarget pointers. // Update RenderTarget pointers.
mRenderTarget.image = &mSwapchainImages[mCurrentSwapchainImageIndex]; mRenderTarget.image = &image.image;
mRenderTarget.imageView = &mSwapchainImageViews[mCurrentSwapchainImageIndex]; mRenderTarget.imageView = &image.imageView;
return vk::NoError(); return vk::NoError();
} }
...@@ -544,10 +547,12 @@ gl::ErrorOrResult<vk::Framebuffer *> WindowSurfaceVk::getCurrentFramebuffer( ...@@ -544,10 +547,12 @@ gl::ErrorOrResult<vk::Framebuffer *> WindowSurfaceVk::getCurrentFramebuffer(
VkDevice device, VkDevice device,
const vk::RenderPass &compatibleRenderPass) const vk::RenderPass &compatibleRenderPass)
{ {
if (!mSwapchainFramebuffers.empty()) auto &currentFramebuffer = mSwapchainImages[mCurrentSwapchainImageIndex].framebuffer;
if (currentFramebuffer.valid())
{ {
// Validation layers should detect if the render pass is really compatible. // Validation layers should detect if the render pass is really compatible.
return &mSwapchainFramebuffers[mCurrentSwapchainImageIndex]; return &currentFramebuffer;
} }
VkFramebufferCreateInfo framebufferInfo; VkFramebufferCreateInfo framebufferInfo;
...@@ -563,22 +568,18 @@ gl::ErrorOrResult<vk::Framebuffer *> WindowSurfaceVk::getCurrentFramebuffer( ...@@ -563,22 +568,18 @@ gl::ErrorOrResult<vk::Framebuffer *> WindowSurfaceVk::getCurrentFramebuffer(
framebufferInfo.height = static_cast<uint32_t>(mRenderTarget.extents.height); framebufferInfo.height = static_cast<uint32_t>(mRenderTarget.extents.height);
framebufferInfo.layers = 1; framebufferInfo.layers = 1;
mSwapchainFramebuffers.resize(mSwapchainImageViews.size()); for (auto &swapchainImage : mSwapchainImages)
for (size_t imageIndex = 0; imageIndex < mSwapchainFramebuffers.size(); ++imageIndex)
{ {
const auto &imageView = mSwapchainImageViews[imageIndex]; framebufferInfo.pAttachments = swapchainImage.imageView.ptr();
VkImageView imageViewHandle = imageView.getHandle();
framebufferInfo.pAttachments = &imageViewHandle;
vk::Framebuffer framebuffer; vk::Framebuffer framebuffer;
ANGLE_TRY(framebuffer.init(device, framebufferInfo)); ANGLE_TRY(framebuffer.init(device, framebufferInfo));
mSwapchainFramebuffers[imageIndex].retain(device, std::move(framebuffer)); swapchainImage.framebuffer.retain(device, std::move(framebuffer));
} }
// We should only initialize framebuffers on the first swap. ASSERT(currentFramebuffer.valid());
ASSERT(mCurrentSwapchainImageIndex == 0u); return &currentFramebuffer;
return &mSwapchainFramebuffers[mCurrentSwapchainImageIndex];
} }
} // namespace rx } // namespace rx
...@@ -111,13 +111,26 @@ class WindowSurfaceVk : public SurfaceImpl, public ResourceVk ...@@ -111,13 +111,26 @@ class WindowSurfaceVk : public SurfaceImpl, public ResourceVk
VkSwapchainKHR mSwapchain; VkSwapchainKHR mSwapchain;
RenderTargetVk mRenderTarget; RenderTargetVk mRenderTarget;
vk::Semaphore mImageAvailableSemaphore;
vk::Semaphore mRenderingCompleteSemaphore;
uint32_t mCurrentSwapchainImageIndex; uint32_t mCurrentSwapchainImageIndex;
std::vector<vk::Image> mSwapchainImages;
std::vector<vk::ImageView> mSwapchainImageViews; // When acquiring a new image for rendering, we keep a 'spare' semaphore. We pass this extra
std::vector<vk::Framebuffer> mSwapchainFramebuffers; // semaphore to VkAcquireNextImage, then hand it to the next available SwapchainImage when
// the command completes. We then make the old semaphore in the new SwapchainImage the spare
// semaphore, since we know the image is no longer using it. This avoids the chicken and egg
// problem with needing to know the next available image index before we acquire it.
vk::Semaphore mAcquireNextImageSemaphore;
struct SwapchainImage
{
vk::Image image;
vk::ImageView imageView;
vk::Framebuffer framebuffer;
vk::Semaphore imageAcquiredSemaphore;
vk::Semaphore commandsCompleteSemaphore;
};
std::vector<SwapchainImage> mSwapchainImages;
}; };
} // namespace rx } // namespace rx
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment