Commit 892d1805 by Geoff Lang Committed by Commit Bot

Vulkan: Have the WindowSurfaceVk own the submit semaphores.

Chaining of submit semaphores is only needed for window surfaces because they are required for the first usage of the swap chain image and final present of the image. Move ownership of the submit semaphores from RendererVk to WindowSurfaceVk and update all calls to finish and flush to be piped through a ContextVk which tracks the currently bound window surface. BUG=angleproject:2464 Change-Id: I4b3083124d7910a5dee297afc219e3a3f28057f2 Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/1542257 Commit-Queue: Geoff Lang <geofflang@chromium.org> Reviewed-by: 's avatarJamie Madill <jmadill@chromium.org>
parent 1af744b5
...@@ -179,7 +179,7 @@ angle::Result BufferVk::getIndexRange(const gl::Context *context, ...@@ -179,7 +179,7 @@ angle::Result BufferVk::getIndexRange(const gl::Context *context,
TRACE_EVENT0("gpu.angle", "BufferVk::getIndexRange"); TRACE_EVENT0("gpu.angle", "BufferVk::getIndexRange");
// Needed before reading buffer or we could get stale data. // Needed before reading buffer or we could get stale data.
ANGLE_TRY(renderer->finish(contextVk)); ANGLE_TRY(contextVk->finishImpl());
// TODO(jmadill): Consider keeping a shadow system memory copy in some cases. // TODO(jmadill): Consider keeping a shadow system memory copy in some cases.
ASSERT(mBuffer.valid()); ASSERT(mBuffer.valid());
......
...@@ -27,6 +27,7 @@ ...@@ -27,6 +27,7 @@
#include "libANGLE/renderer/vulkan/RendererVk.h" #include "libANGLE/renderer/vulkan/RendererVk.h"
#include "libANGLE/renderer/vulkan/SamplerVk.h" #include "libANGLE/renderer/vulkan/SamplerVk.h"
#include "libANGLE/renderer/vulkan/ShaderVk.h" #include "libANGLE/renderer/vulkan/ShaderVk.h"
#include "libANGLE/renderer/vulkan/SurfaceVk.h"
#include "libANGLE/renderer/vulkan/SyncVk.h" #include "libANGLE/renderer/vulkan/SyncVk.h"
#include "libANGLE/renderer/vulkan/TextureVk.h" #include "libANGLE/renderer/vulkan/TextureVk.h"
#include "libANGLE/renderer/vulkan/TransformFeedbackVk.h" #include "libANGLE/renderer/vulkan/TransformFeedbackVk.h"
...@@ -72,6 +73,7 @@ ContextVk::ContextVk(const gl::State &state, gl::ErrorSet *errorSet, RendererVk ...@@ -72,6 +73,7 @@ ContextVk::ContextVk(const gl::State &state, gl::ErrorSet *errorSet, RendererVk
vk::Context(renderer), vk::Context(renderer),
mCurrentPipeline(nullptr), mCurrentPipeline(nullptr),
mCurrentDrawMode(gl::PrimitiveMode::InvalidEnum), mCurrentDrawMode(gl::PrimitiveMode::InvalidEnum),
mCurrentWindowSurface(nullptr),
mVertexArray(nullptr), mVertexArray(nullptr),
mDrawFramebuffer(nullptr), mDrawFramebuffer(nullptr),
mProgram(nullptr), mProgram(nullptr),
...@@ -117,7 +119,7 @@ ContextVk::~ContextVk() = default; ...@@ -117,7 +119,7 @@ ContextVk::~ContextVk() = default;
void ContextVk::onDestroy(const gl::Context *context) void ContextVk::onDestroy(const gl::Context *context)
{ {
// Force a flush on destroy. // Force a flush on destroy.
(void)mRenderer->finish(this); (void)finishImpl();
mDriverUniformsSetLayout.reset(); mDriverUniformsSetLayout.reset();
mIncompleteTextures.onDestroy(context); mIncompleteTextures.onDestroy(context);
...@@ -190,12 +192,38 @@ angle::Result ContextVk::initialize() ...@@ -190,12 +192,38 @@ angle::Result ContextVk::initialize()
angle::Result ContextVk::flush(const gl::Context *context) angle::Result ContextVk::flush(const gl::Context *context)
{ {
return mRenderer->flush(this); return flushImpl();
}
angle::Result ContextVk::flushImpl()
{
const vk::Semaphore *waitSemaphore = nullptr;
const vk::Semaphore *signalSemaphore = nullptr;
if (mCurrentWindowSurface && !mRenderer->getCommandGraph()->empty())
{
ANGLE_TRY(mCurrentWindowSurface->generateSemaphoresForFlush(this, &waitSemaphore,
&signalSemaphore));
}
return mRenderer->flush(this, waitSemaphore, signalSemaphore);
} }
angle::Result ContextVk::finish(const gl::Context *context) angle::Result ContextVk::finish(const gl::Context *context)
{ {
return mRenderer->finish(this); return finishImpl();
}
angle::Result ContextVk::finishImpl()
{
const vk::Semaphore *waitSemaphore = nullptr;
const vk::Semaphore *signalSemaphore = nullptr;
if (mCurrentWindowSurface && !mRenderer->getCommandGraph()->empty())
{
ANGLE_TRY(mCurrentWindowSurface->generateSemaphoresForFlush(this, &waitSemaphore,
&signalSemaphore));
}
return mRenderer->finish(this, waitSemaphore, signalSemaphore);
} }
angle::Result ContextVk::setupDraw(const gl::Context *context, angle::Result ContextVk::setupDraw(const gl::Context *context,
...@@ -959,6 +987,15 @@ angle::Result ContextVk::onMakeCurrent(const gl::Context *context) ...@@ -959,6 +987,15 @@ angle::Result ContextVk::onMakeCurrent(const gl::Context *context)
drawSurface != nullptr && mRenderer->getFeatures().flipViewportY && drawSurface != nullptr && mRenderer->getFeatures().flipViewportY &&
!IsMaskFlagSet(drawSurface->getOrientation(), EGL_SURFACE_ORIENTATION_INVERT_Y_ANGLE); !IsMaskFlagSet(drawSurface->getOrientation(), EGL_SURFACE_ORIENTATION_INVERT_Y_ANGLE);
if (drawSurface && drawSurface->getType() == EGL_WINDOW_BIT)
{
mCurrentWindowSurface = GetImplAs<WindowSurfaceVk>(drawSurface);
}
else
{
mCurrentWindowSurface = nullptr;
}
const gl::State &glState = context->getState(); const gl::State &glState = context->getState();
updateFlipViewportDrawFramebuffer(glState); updateFlipViewportDrawFramebuffer(glState);
updateFlipViewportReadFramebuffer(glState); updateFlipViewportReadFramebuffer(glState);
......
...@@ -25,6 +25,7 @@ struct FeaturesVk; ...@@ -25,6 +25,7 @@ struct FeaturesVk;
namespace rx namespace rx
{ {
class RendererVk; class RendererVk;
class WindowSurfaceVk;
class ContextVk : public ContextImpl, public vk::Context, public vk::CommandBufferOwner class ContextVk : public ContextImpl, public vk::Context, public vk::CommandBufferOwner
{ {
...@@ -38,7 +39,9 @@ class ContextVk : public ContextImpl, public vk::Context, public vk::CommandBuff ...@@ -38,7 +39,9 @@ class ContextVk : public ContextImpl, public vk::Context, public vk::CommandBuff
// Flush and finish. // Flush and finish.
angle::Result flush(const gl::Context *context) override; angle::Result flush(const gl::Context *context) override;
angle::Result flushImpl();
angle::Result finish(const gl::Context *context) override; angle::Result finish(const gl::Context *context) override;
angle::Result finishImpl();
// Drawing methods. // Drawing methods.
angle::Result drawArrays(const gl::Context *context, angle::Result drawArrays(const gl::Context *context,
...@@ -288,6 +291,8 @@ class ContextVk : public ContextImpl, public vk::Context, public vk::CommandBuff ...@@ -288,6 +291,8 @@ class ContextVk : public ContextImpl, public vk::Context, public vk::CommandBuff
vk::PipelineHelper *mCurrentPipeline; vk::PipelineHelper *mCurrentPipeline;
gl::PrimitiveMode mCurrentDrawMode; gl::PrimitiveMode mCurrentDrawMode;
WindowSurfaceVk *mCurrentWindowSurface;
// Keep a cached pipeline description structure that can be used to query the pipeline cache. // Keep a cached pipeline description structure that can be used to query the pipeline cache.
// Kept in a pointer so allocations can be aligned, and structs can be portably packed. // Kept in a pointer so allocations can be aligned, and structs can be portably packed.
std::unique_ptr<vk::GraphicsPipelineDesc> mGraphicsPipelineDesc; std::unique_ptr<vk::GraphicsPipelineDesc> mGraphicsPipelineDesc;
......
...@@ -84,7 +84,8 @@ DeviceImpl *DisplayVk::createDevice() ...@@ -84,7 +84,8 @@ DeviceImpl *DisplayVk::createDevice()
egl::Error DisplayVk::waitClient(const gl::Context *context) egl::Error DisplayVk::waitClient(const gl::Context *context)
{ {
TRACE_EVENT0("gpu.angle", "DisplayVk::waitClient"); TRACE_EVENT0("gpu.angle", "DisplayVk::waitClient");
return angle::ToEGL(mRenderer->finish(this), this, EGL_BAD_ACCESS); ContextVk *contextVk = vk::GetImpl(context);
return angle::ToEGL(contextVk->finishImpl(), this, EGL_BAD_ACCESS);
} }
egl::Error DisplayVk::waitNative(const gl::Context *context, EGLint engine) egl::Error DisplayVk::waitNative(const gl::Context *context, EGLint engine)
......
...@@ -1346,7 +1346,6 @@ angle::Result FramebufferVk::readPixelsImpl(ContextVk *contextVk, ...@@ -1346,7 +1346,6 @@ angle::Result FramebufferVk::readPixelsImpl(ContextVk *contextVk,
void *pixels) void *pixels)
{ {
TRACE_EVENT0("gpu.angle", "FramebufferVk::readPixelsImpl"); TRACE_EVENT0("gpu.angle", "FramebufferVk::readPixelsImpl");
RendererVk *renderer = contextVk->getRenderer();
ANGLE_TRY(renderTarget->ensureImageInitialized(contextVk)); ANGLE_TRY(renderTarget->ensureImageInitialized(contextVk));
...@@ -1393,7 +1392,7 @@ angle::Result FramebufferVk::readPixelsImpl(ContextVk *contextVk, ...@@ -1393,7 +1392,7 @@ angle::Result FramebufferVk::readPixelsImpl(ContextVk *contextVk,
// Triggers a full finish. // Triggers a full finish.
// TODO(jmadill): Don't block on asynchronous readback. // TODO(jmadill): Don't block on asynchronous readback.
ANGLE_TRY(renderer->finish(contextVk)); ANGLE_TRY(contextVk->finishImpl());
// The buffer we copied to needs to be invalidated before we read from it because its not been // The buffer we copied to needs to be invalidated before we read from it because its not been
// created with the host coherent bit. // created with the host coherent bit.
......
...@@ -110,7 +110,7 @@ angle::Result QueryVk::getResult(const gl::Context *context, bool wait) ...@@ -110,7 +110,7 @@ angle::Result QueryVk::getResult(const gl::Context *context, bool wait)
// has pending work should flush begin too. // has pending work should flush begin too.
if (mQueryHelper.hasPendingWork(renderer)) if (mQueryHelper.hasPendingWork(renderer))
{ {
ANGLE_TRY(renderer->flush(contextVk)); ANGLE_TRY(contextVk->flushImpl());
ASSERT(!mQueryHelperTimeElapsedBegin.hasPendingWork(renderer)); ASSERT(!mQueryHelperTimeElapsedBegin.hasPendingWork(renderer));
ASSERT(!mQueryHelper.hasPendingWork(renderer)); ASSERT(!mQueryHelper.hasPendingWork(renderer));
......
...@@ -77,8 +77,12 @@ class RendererVk : angle::NonCopyable ...@@ -77,8 +77,12 @@ class RendererVk : angle::NonCopyable
VkSurfaceKHR surface, VkSurfaceKHR surface,
uint32_t *presentQueueOut); uint32_t *presentQueueOut);
angle::Result finish(vk::Context *context); angle::Result finish(vk::Context *context,
angle::Result flush(vk::Context *context); const vk::Semaphore *waitSemaphore,
const vk::Semaphore *signalSemaphore);
angle::Result flush(vk::Context *context,
const vk::Semaphore *waitSemaphore,
const vk::Semaphore *signalSemaphore);
const vk::CommandPool &getCommandPool() const; const vk::CommandPool &getCommandPool() const;
...@@ -149,8 +153,6 @@ class RendererVk : angle::NonCopyable ...@@ -149,8 +153,6 @@ class RendererVk : angle::NonCopyable
angle::Result syncPipelineCacheVk(DisplayVk *displayVk); angle::Result syncPipelineCacheVk(DisplayVk *displayVk);
vk::DynamicSemaphorePool *getDynamicSemaphorePool() { return &mSubmitSemaphorePool; }
// Request a semaphore, that is expected to be signaled externally. The next submission will // Request a semaphore, that is expected to be signaled externally. The next submission will
// wait on it. // wait on it.
angle::Result allocateSubmitWaitSemaphore(vk::Context *context, angle::Result allocateSubmitWaitSemaphore(vk::Context *context,
...@@ -221,10 +223,6 @@ class RendererVk : angle::NonCopyable ...@@ -221,10 +223,6 @@ class RendererVk : angle::NonCopyable
angle::Result initializeDevice(DisplayVk *displayVk, uint32_t queueFamilyIndex); angle::Result initializeDevice(DisplayVk *displayVk, uint32_t queueFamilyIndex);
void ensureCapsInitialized() const; void ensureCapsInitialized() const;
void getSubmitWaitSemaphores(
vk::Context *context,
angle::FixedVector<VkSemaphore, kMaxWaitSemaphores> *waitSemaphores,
angle::FixedVector<VkPipelineStageFlags, kMaxWaitSemaphores> *waitStageMasks);
angle::Result submitFrame(vk::Context *context, angle::Result submitFrame(vk::Context *context,
const VkSubmitInfo &submitInfo, const VkSubmitInfo &submitInfo,
vk::PrimaryCommandBuffer &&commandBuffer); vk::PrimaryCommandBuffer &&commandBuffer);
...@@ -234,7 +232,9 @@ class RendererVk : angle::NonCopyable ...@@ -234,7 +232,9 @@ class RendererVk : angle::NonCopyable
void initPipelineCacheVkKey(); void initPipelineCacheVkKey();
angle::Result initPipelineCache(DisplayVk *display); angle::Result initPipelineCache(DisplayVk *display);
angle::Result synchronizeCpuGpuTime(vk::Context *context); angle::Result synchronizeCpuGpuTime(vk::Context *context,
const vk::Semaphore *waitSemaphore,
const vk::Semaphore *signalSemaphore);
angle::Result traceGpuEventImpl(vk::Context *context, angle::Result traceGpuEventImpl(vk::Context *context,
vk::PrimaryCommandBuffer *commandBuffer, vk::PrimaryCommandBuffer *commandBuffer,
char phase, char phase,
...@@ -307,26 +307,6 @@ class RendererVk : angle::NonCopyable ...@@ -307,26 +307,6 @@ class RendererVk : angle::NonCopyable
// A cache of VkFormatProperties as queried from the device over time. // A cache of VkFormatProperties as queried from the device over time.
std::array<VkFormatProperties, vk::kNumVkFormats> mFormatProperties; std::array<VkFormatProperties, vk::kNumVkFormats> mFormatProperties;
// mSubmitWaitSemaphores is a list of specifically requested semaphores to be waited on before a
// command buffer submission, for example, semaphores signaled by vkAcquireNextImageKHR.
// After first use, the list is automatically cleared. This is a vector to support concurrent
// rendering to multiple surfaces.
//
// Note that with multiple contexts present, this may result in a context waiting on image
// acquisition even if it doesn't render to that surface. If CommandGraphs are separated by
// context or share group for example, this could be moved to the one that actually uses the
// image.
angle::FixedVector<vk::SemaphoreHelper, kMaxExternalSemaphores> mSubmitWaitSemaphores;
// mSubmitLastSignaledSemaphore shows which semaphore was last signaled by submission. This can
// be set to nullptr if retrieved to be waited on outside RendererVk, such
// as by the surface before presentation. Each submission waits on the
// previously signaled semaphore (as well as any in mSubmitWaitSemaphores)
// and allocates a new semaphore to signal.
vk::SemaphoreHelper mSubmitLastSignaledSemaphore;
// A pool of semaphores used to support the aforementioned mid-frame submissions.
vk::DynamicSemaphorePool mSubmitSemaphorePool;
// mSubmitFence is the fence that's going to be signaled at the next submission. This is used // mSubmitFence is the fence that's going to be signaled at the next submission. This is used
// to support SyncVk objects, which may outlive the context (as EGLSync objects). // to support SyncVk objects, which may outlive the context (as EGLSync objects).
// //
......
...@@ -274,6 +274,37 @@ WindowSurfaceVk::SwapchainImage::SwapchainImage(SwapchainImage &&other) ...@@ -274,6 +274,37 @@ WindowSurfaceVk::SwapchainImage::SwapchainImage(SwapchainImage &&other)
framebuffer(std::move(other.framebuffer)) framebuffer(std::move(other.framebuffer))
{} {}
WindowSurfaceVk::SwapHistory::SwapHistory() = default;
WindowSurfaceVk::SwapHistory::SwapHistory(SwapHistory &&other)
{
*this = std::move(other);
}
WindowSurfaceVk::SwapHistory &WindowSurfaceVk::SwapHistory::operator=(SwapHistory &&other)
{
std::swap(serial, other.serial);
std::swap(semaphores, other.semaphores);
std::swap(swapchain, other.swapchain);
return *this;
}
WindowSurfaceVk::SwapHistory::~SwapHistory() = default;
void WindowSurfaceVk::SwapHistory::destroy(VkDevice device)
{
if (swapchain != VK_NULL_HANDLE)
{
vkDestroySwapchainKHR(device, swapchain, nullptr);
swapchain = VK_NULL_HANDLE;
}
for (vk::Semaphore &semaphore : semaphores)
{
semaphore.destroy(device);
}
semaphores.clear();
}
WindowSurfaceVk::WindowSurfaceVk(const egl::SurfaceState &surfaceState, WindowSurfaceVk::WindowSurfaceVk(const egl::SurfaceState &surfaceState,
EGLNativeWindowType window, EGLNativeWindowType window,
EGLint width, EGLint width,
...@@ -312,17 +343,13 @@ void WindowSurfaceVk::destroy(const egl::Display *display) ...@@ -312,17 +343,13 @@ void WindowSurfaceVk::destroy(const egl::Display *display)
// we delete the window surface. // we delete the window surface.
(void)present(displayVk, nullptr, 0, swapchainOutOfDate); (void)present(displayVk, nullptr, 0, swapchainOutOfDate);
// We might not need to flush the pipe here. // We might not need to flush the pipe here.
(void)renderer->finish(displayVk); (void)renderer->finish(displayVk, nullptr, nullptr);
releaseSwapchainImages(renderer); releaseSwapchainImages(renderer);
for (SwapHistory &swap : mSwapHistory) for (SwapHistory &swap : mSwapHistory)
{ {
if (swap.swapchain != VK_NULL_HANDLE) swap.destroy(device);
{
vkDestroySwapchainKHR(device, swap.swapchain, nullptr);
swap.swapchain = VK_NULL_HANDLE;
}
} }
if (mSwapchain) if (mSwapchain)
...@@ -336,6 +363,12 @@ void WindowSurfaceVk::destroy(const egl::Display *display) ...@@ -336,6 +363,12 @@ void WindowSurfaceVk::destroy(const egl::Display *display)
vkDestroySurfaceKHR(instance, mSurface, nullptr); vkDestroySurfaceKHR(instance, mSurface, nullptr);
mSurface = VK_NULL_HANDLE; mSurface = VK_NULL_HANDLE;
} }
for (vk::Semaphore &flushSemaphore : mFlushSemaphoreChain)
{
flushSemaphore.destroy(device);
}
mFlushSemaphoreChain.clear();
} }
egl::Error WindowSurfaceVk::initialize(const egl::Display *display) egl::Error WindowSurfaceVk::initialize(const egl::Display *display)
...@@ -675,15 +708,11 @@ angle::Result WindowSurfaceVk::present(DisplayVk *displayVk, ...@@ -675,15 +708,11 @@ angle::Result WindowSurfaceVk::present(DisplayVk *displayVk,
RendererVk *renderer = displayVk->getRenderer(); RendererVk *renderer = displayVk->getRenderer();
// Throttle the submissions to avoid getting too far ahead of the GPU. // Throttle the submissions to avoid getting too far ahead of the GPU.
SwapHistory &swap = mSwapHistory[mCurrentSwapHistoryIndex];
{ {
TRACE_EVENT0("gpu.angle", "WindowSurfaceVk::present: Throttle CPU"); TRACE_EVENT0("gpu.angle", "WindowSurfaceVk::present: Throttle CPU");
SwapHistory &swap = mSwapHistory[mCurrentSwapHistoryIndex];
ANGLE_TRY(renderer->finishToSerial(displayVk, swap.serial)); ANGLE_TRY(renderer->finishToSerial(displayVk, swap.serial));
if (swap.swapchain != VK_NULL_HANDLE) swap.destroy(renderer->getDevice());
{
vkDestroySwapchainKHR(renderer->getDevice(), swap.swapchain, nullptr);
swap.swapchain = VK_NULL_HANDLE;
}
} }
SwapchainImage &image = mSwapchainImages[mCurrentSwapchainImageIndex]; SwapchainImage &image = mSwapchainImages[mCurrentSwapchainImageIndex];
...@@ -693,23 +722,23 @@ angle::Result WindowSurfaceVk::present(DisplayVk *displayVk, ...@@ -693,23 +722,23 @@ angle::Result WindowSurfaceVk::present(DisplayVk *displayVk,
image.image.changeLayout(VK_IMAGE_ASPECT_COLOR_BIT, vk::ImageLayout::Present, swapCommands); image.image.changeLayout(VK_IMAGE_ASPECT_COLOR_BIT, vk::ImageLayout::Present, swapCommands);
ANGLE_TRY(renderer->flush(displayVk)); const vk::Semaphore *waitSemaphore = nullptr;
const vk::Semaphore *signalSemaphore = nullptr;
if (!renderer->getCommandGraph()->empty())
{
ANGLE_TRY(generateSemaphoresForFlush(displayVk, &waitSemaphore, &signalSemaphore));
}
// Remember the serial of the last submission. ANGLE_TRY(renderer->flush(displayVk, waitSemaphore, signalSemaphore));
mSwapHistory[mCurrentSwapHistoryIndex].serial = renderer->getLastSubmittedQueueSerial();
++mCurrentSwapHistoryIndex;
mCurrentSwapHistoryIndex =
mCurrentSwapHistoryIndex == mSwapHistory.size() ? 0 : mCurrentSwapHistoryIndex;
// Ask the renderer what semaphore it signaled in the last flush. // The semaphore chain must at least have the semaphore returned by vkAquireImage in it. It will
const vk::Semaphore *commandsCompleteSemaphore = // likely have more based on how much work was flushed this frame.
renderer->getSubmitLastSignaledSemaphore(displayVk); ASSERT(!mFlushSemaphoreChain.empty());
VkPresentInfoKHR presentInfo = {}; VkPresentInfoKHR presentInfo = {};
presentInfo.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR; presentInfo.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR;
presentInfo.waitSemaphoreCount = commandsCompleteSemaphore ? 1 : 0; presentInfo.waitSemaphoreCount = 1;
presentInfo.pWaitSemaphores = presentInfo.pWaitSemaphores = mFlushSemaphoreChain.back().ptr();
commandsCompleteSemaphore ? commandsCompleteSemaphore->ptr() : nullptr;
presentInfo.swapchainCount = 1; presentInfo.swapchainCount = 1;
presentInfo.pSwapchains = &mSwapchain; presentInfo.pSwapchains = &mSwapchain;
presentInfo.pImageIndices = &mCurrentSwapchainImageIndex; presentInfo.pImageIndices = &mCurrentSwapchainImageIndex;
...@@ -747,6 +776,13 @@ angle::Result WindowSurfaceVk::present(DisplayVk *displayVk, ...@@ -747,6 +776,13 @@ angle::Result WindowSurfaceVk::present(DisplayVk *displayVk,
presentInfo.pNext = &presentRegions; presentInfo.pNext = &presentRegions;
} }
// Update the swap history for this presentation
swap.serial = renderer->getLastSubmittedQueueSerial();
swap.semaphores = std::move(mFlushSemaphoreChain);
++mCurrentSwapHistoryIndex;
mCurrentSwapHistoryIndex =
mCurrentSwapHistoryIndex == mSwapHistory.size() ? 0 : mCurrentSwapHistoryIndex;
VkResult result = vkQueuePresentKHR(renderer->getQueue(), &presentInfo); VkResult result = vkQueuePresentKHR(renderer->getQueue(), &presentInfo);
// If SUBOPTIMAL/OUT_OF_DATE is returned, it's ok, we just need to recreate the swapchain before // If SUBOPTIMAL/OUT_OF_DATE is returned, it's ok, we just need to recreate the swapchain before
...@@ -787,14 +823,18 @@ angle::Result WindowSurfaceVk::swapImpl(DisplayVk *displayVk, EGLint *rects, EGL ...@@ -787,14 +823,18 @@ angle::Result WindowSurfaceVk::swapImpl(DisplayVk *displayVk, EGLint *rects, EGL
angle::Result WindowSurfaceVk::nextSwapchainImage(DisplayVk *displayVk) angle::Result WindowSurfaceVk::nextSwapchainImage(DisplayVk *displayVk)
{ {
VkDevice device = displayVk->getDevice(); VkDevice device = displayVk->getDevice();
RendererVk *renderer = displayVk->getRenderer();
const vk::Semaphore *acquireNextImageSemaphore = nullptr; vk::Semaphore aquireImageSemaphore;
ANGLE_TRY(renderer->allocateSubmitWaitSemaphore(displayVk, &acquireNextImageSemaphore)); ANGLE_VK_TRY(displayVk, aquireImageSemaphore.init(device));
ANGLE_VK_TRY(displayVk, vkAcquireNextImageKHR(device, mSwapchain, UINT64_MAX, ANGLE_VK_TRY(displayVk, vkAcquireNextImageKHR(device, mSwapchain, UINT64_MAX,
acquireNextImageSemaphore->getHandle(), aquireImageSemaphore.getHandle(), VK_NULL_HANDLE,
VK_NULL_HANDLE, &mCurrentSwapchainImageIndex)); &mCurrentSwapchainImageIndex));
// After presenting, the flush semaphore chain is cleared. The semaphore returned by
// vkAcquireNextImage will start a new chain.
ASSERT(mFlushSemaphoreChain.empty());
mFlushSemaphoreChain.push_back(std::move(aquireImageSemaphore));
SwapchainImage &image = mSwapchainImages[mCurrentSwapchainImageIndex]; SwapchainImage &image = mSwapchainImages[mCurrentSwapchainImageIndex];
...@@ -852,11 +892,7 @@ angle::Result WindowSurfaceVk::resizeSwapHistory(DisplayVk *displayVk, size_t im ...@@ -852,11 +892,7 @@ angle::Result WindowSurfaceVk::resizeSwapHistory(DisplayVk *displayVk, size_t im
SwapHistory &swap = mSwapHistory[historyIndex]; SwapHistory &swap = mSwapHistory[historyIndex];
ANGLE_TRY(renderer->finishToSerial(displayVk, swap.serial)); ANGLE_TRY(renderer->finishToSerial(displayVk, swap.serial));
if (swap.swapchain != VK_NULL_HANDLE) swap.destroy(renderer->getDevice());
{
vkDestroySwapchainKHR(renderer->getDevice(), swap.swapchain, nullptr);
swap.swapchain = VK_NULL_HANDLE;
}
} }
} }
...@@ -869,7 +905,7 @@ angle::Result WindowSurfaceVk::resizeSwapHistory(DisplayVk *displayVk, size_t im ...@@ -869,7 +905,7 @@ angle::Result WindowSurfaceVk::resizeSwapHistory(DisplayVk *displayVk, size_t im
size_t historyIndex = size_t historyIndex =
(mCurrentSwapHistoryIndex + mSwapHistory.size() - i - 1) % mSwapHistory.size(); (mCurrentSwapHistoryIndex + mSwapHistory.size() - i - 1) % mSwapHistory.size();
size_t resizedHistoryIndex = imageCount - i - 1; size_t resizedHistoryIndex = imageCount - i - 1;
resizedHistory[resizedHistoryIndex] = mSwapHistory[historyIndex]; resizedHistory[resizedHistoryIndex] = std::move(mSwapHistory[historyIndex]);
} }
// Set this as the new history. Note that after rearranging in either case, the oldest history // Set this as the new history. Note that after rearranging in either case, the oldest history
...@@ -1035,6 +1071,25 @@ angle::Result WindowSurfaceVk::getCurrentFramebuffer(vk::Context *context, ...@@ -1035,6 +1071,25 @@ angle::Result WindowSurfaceVk::getCurrentFramebuffer(vk::Context *context,
return angle::Result::Continue; return angle::Result::Continue;
} }
angle::Result WindowSurfaceVk::generateSemaphoresForFlush(vk::Context *context,
const vk::Semaphore **outWaitSemaphore,
const vk::Semaphore **outSignalSempahore)
{
// The flush semaphore chain should always start with a semaphore in it, created by the
// vkAquireImage call. This semaphore must be waited on before any rendering to the swap chain
// image can occur.
ASSERT(!mFlushSemaphoreChain.empty());
vk::Semaphore nextSemaphore;
ANGLE_VK_TRY(context, nextSemaphore.init(context->getDevice()));
mFlushSemaphoreChain.push_back(std::move(nextSemaphore));
*outWaitSemaphore = &mFlushSemaphoreChain[mFlushSemaphoreChain.size() - 2];
*outSignalSempahore = &mFlushSemaphoreChain[mFlushSemaphoreChain.size() - 1];
return angle::Result::Continue;
}
angle::Result WindowSurfaceVk::initializeContents(const gl::Context *context, angle::Result WindowSurfaceVk::initializeContents(const gl::Context *context,
const gl::ImageIndex &imageIndex) const gl::ImageIndex &imageIndex)
{ {
......
...@@ -136,6 +136,10 @@ class WindowSurfaceVk : public SurfaceImpl ...@@ -136,6 +136,10 @@ class WindowSurfaceVk : public SurfaceImpl
const vk::RenderPass &compatibleRenderPass, const vk::RenderPass &compatibleRenderPass,
vk::Framebuffer **framebufferOut); vk::Framebuffer **framebufferOut);
angle::Result generateSemaphoresForFlush(vk::Context *context,
const vk::Semaphore **outWaitSemaphore,
const vk::Semaphore **outSignalSempahore);
protected: protected:
EGLNativeWindowType mNativeWindowType; EGLNativeWindowType mNativeWindowType;
VkSurfaceKHR mSurface; VkSurfaceKHR mSurface;
...@@ -190,12 +194,38 @@ class WindowSurfaceVk : public SurfaceImpl ...@@ -190,12 +194,38 @@ class WindowSurfaceVk : public SurfaceImpl
std::vector<SwapchainImage> mSwapchainImages; std::vector<SwapchainImage> mSwapchainImages;
// Each time vkPresent is called, a wait semaphore is needed to know when the work to render the
// frame is done. For ANGLE to know when that is, it needs to add a signal semaphore to each
// flush. Conversely, before being able to use a swap chain image, ANGLE needs to wait on the
// semaphore returned by vkAcquireNextImage.
//
// We build a chain of semaphores starting with the semaphore returned by vkAcquireNextImageKHR
// and ending with the semaphore provided to vkPresent. Each time generateSemaphoresForFlush is
// called, a new semaphore is created and appended to mFlushSemaphoreChain. The second last
// semaphore is used as a wait semaphore and the last one is used as a signal semaphore for the
// flush.
//
// The semaphore chain is cleared after every call to present and a new one is started once
// vkAquireImage is called.
//
// We don't need a semaphore chain for offscreen surfaces or surfaceless rendering because the
// results cannot affect the images in a swap chain.
std::vector<vk::Semaphore> mFlushSemaphoreChain;
// A circular buffer, with the same size as mSwapchainImages (N), that stores the serial of the // A circular buffer, with the same size as mSwapchainImages (N), that stores the serial of the
// renderer on every swap. The CPU is throttled by waiting for the Nth previous serial to // renderer on every swap. The CPU is throttled by waiting for the Nth previous serial to
// finish. Old swapchains are scheduled to be destroyed at the same time. // finish. Old swapchains are scheduled to be destroyed at the same time.
struct SwapHistory struct SwapHistory : angle::NonCopyable
{ {
SwapHistory();
SwapHistory(SwapHistory &&other);
SwapHistory &operator=(SwapHistory &&other);
~SwapHistory();
void destroy(VkDevice device);
Serial serial; Serial serial;
std::vector<vk::Semaphore> semaphores;
VkSwapchainKHR swapchain = VK_NULL_HANDLE; VkSwapchainKHR swapchain = VK_NULL_HANDLE;
}; };
std::vector<SwapHistory> mSwapHistory; std::vector<SwapHistory> mSwapHistory;
......
...@@ -79,7 +79,7 @@ angle::Result FenceSyncVk::clientWait(vk::Context *context, ...@@ -79,7 +79,7 @@ angle::Result FenceSyncVk::clientWait(vk::Context *context,
if (flushCommands && contextVk) if (flushCommands && contextVk)
{ {
ANGLE_TRY(contextVk->getRenderer()->flush(contextVk)); ANGLE_TRY(contextVk->flushImpl());
} }
// Wait on the fence that's expected to be signaled on the first vkQueueSubmit after // Wait on the fence that's expected to be signaled on the first vkQueueSubmit after
......
...@@ -970,7 +970,7 @@ angle::Result TextureVk::copyImageDataToBuffer(ContextVk *contextVk, ...@@ -970,7 +970,7 @@ angle::Result TextureVk::copyImageDataToBuffer(ContextVk *contextVk,
copyBufferHandle, 1, &region); copyBufferHandle, 1, &region);
// Explicitly finish. If new use cases arise where we don't want to block we can change this. // Explicitly finish. If new use cases arise where we don't want to block we can change this.
ANGLE_TRY(contextVk->getRenderer()->finish(contextVk)); ANGLE_TRY(contextVk->finishImpl());
return angle::Result::Continue; return angle::Result::Continue;
} }
......
...@@ -264,7 +264,7 @@ angle::Result VertexArrayVk::convertVertexBufferCpu(ContextVk *contextVk, ...@@ -264,7 +264,7 @@ angle::Result VertexArrayVk::convertVertexBufferCpu(ContextVk *contextVk,
{ {
TRACE_EVENT0("gpu.angle", "VertexArrayVk::convertVertexBufferCpu"); TRACE_EVENT0("gpu.angle", "VertexArrayVk::convertVertexBufferCpu");
// Needed before reading buffer or we could get stale data. // Needed before reading buffer or we could get stale data.
ANGLE_TRY(contextVk->getRenderer()->finish(contextVk)); ANGLE_TRY(contextVk->finishImpl());
unsigned srcFormatSize = vertexFormat.angleFormat().pixelBytes; unsigned srcFormatSize = vertexFormat.angleFormat().pixelBytes;
unsigned dstFormatSize = vertexFormat.bufferFormat().pixelBytes; unsigned dstFormatSize = vertexFormat.bufferFormat().pixelBytes;
...@@ -483,7 +483,7 @@ angle::Result VertexArrayVk::syncDirtyAttrib(ContextVk *contextVk, ...@@ -483,7 +483,7 @@ angle::Result VertexArrayVk::syncDirtyAttrib(ContextVk *contextVk,
if (anyVertexBufferConvertedOnGpu && renderer->getFeatures().flushAfterVertexConversion) if (anyVertexBufferConvertedOnGpu && renderer->getFeatures().flushAfterVertexConversion)
{ {
ANGLE_TRY(renderer->flush(contextVk)); ANGLE_TRY(contextVk->flushImpl());
} }
return angle::Result::Continue; return angle::Result::Continue;
...@@ -678,7 +678,7 @@ angle::Result VertexArrayVk::updateIndexTranslation(ContextVk *contextVk, ...@@ -678,7 +678,7 @@ angle::Result VertexArrayVk::updateIndexTranslation(ContextVk *contextVk,
TRACE_EVENT0("gpu.angle", "VertexArrayVk::updateIndexTranslation"); TRACE_EVENT0("gpu.angle", "VertexArrayVk::updateIndexTranslation");
// Needed before reading buffer or we could get stale data. // Needed before reading buffer or we could get stale data.
ANGLE_TRY(renderer->finish(contextVk)); ANGLE_TRY(contextVk->finishImpl());
ASSERT(type == gl::DrawElementsType::UnsignedByte); ASSERT(type == gl::DrawElementsType::UnsignedByte);
// Unsigned bytes don't have direct support in Vulkan so we have to expand the // Unsigned bytes don't have direct support in Vulkan so we have to expand the
......
...@@ -970,7 +970,7 @@ angle::Result LineLoopHelper::getIndexBufferForElementArrayBuffer(ContextVk *con ...@@ -970,7 +970,7 @@ angle::Result LineLoopHelper::getIndexBufferForElementArrayBuffer(ContextVk *con
{ {
TRACE_EVENT0("gpu.angle", "LineLoopHelper::getIndexBufferForElementArrayBuffer"); TRACE_EVENT0("gpu.angle", "LineLoopHelper::getIndexBufferForElementArrayBuffer");
// Needed before reading buffer or we could get stale data. // Needed before reading buffer or we could get stale data.
ANGLE_TRY(contextVk->getRenderer()->finish(contextVk)); ANGLE_TRY(contextVk->finishImpl());
void *srcDataMapping = nullptr; void *srcDataMapping = nullptr;
ANGLE_TRY(elementArrayBufferVk->mapImpl(contextVk, &srcDataMapping)); ANGLE_TRY(elementArrayBufferVk->mapImpl(contextVk, &srcDataMapping));
......
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