Hash :
3dfc8004
Author :
Date :
2022-06-08T14:24:48
Vulkan: Optimize sync followed by swap Previously, inserting a sync object immediately caused a submission. That was done in https://chromium-review.googlesource.com/c/angle/angle/+/3200274 to be able to wait until the sync object is signaled without having to wait for whatever is recorded after it until a flush naturally happens. Some applications issue a glFenceSync right before eglSwapBuffers. The submission incurred by glFenceSync disallowed the optimizations that eglSwapBuffers would have done, leading to performance degradations. This could have been avoided if glFenceSync was issued right after eglSwapBuffers, but that's not the case with a number of applications. In this change, when a fence is inserted: - For EGL sync objects, a submission is issued regardless - For GL sync objects, a submission is issued if there is no render pass open - For GL sync objects, the submission is deferred if there is an open render pass. This is done by marking the render pass closed, and flagging the context as having a deferred flash. If the context that issued the fence sync issues another draw call, the render pass is naturally closed and the submission is performed. If the context that issued the fence sync causes a submission, it would have a chance to modify the render pass before doing so. For example, it could apply swapchain optimizations before swapping, or add a resolve attachment for blit. If the context that issued the fence sync doesn't cause a submission before another context tries to access it (get status, wait, etc), the other context will flush its render pass and cause a submission on its behalf. This is possible because the deferral of submission is done only for GL sync objects, and those are only accessible by other contexts in the same share group. Bug: angleproject:7379 Change-Id: I3dd1c1bfd575206d730dd9ee2e33ba2254318521 Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/3695520 Reviewed-by: Charlie Lao <cclao@google.com> Commit-Queue: Shahbaz Youssefi <syoussefi@chromium.org> Reviewed-by: Jamie Madill <jmadill@chromium.org>
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94
//
// Copyright 2021 The ANGLE Project Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// MultiThreadSteps.h:
// Synchronization help for tests that use multiple threads.
#include "gl_raii.h"
#include <atomic>
#include <condition_variable>
#include <functional>
#include <mutex>
#include <thread>
class EGLWindow;
namespace angle
{
namespace
{
// The following class is used by tests that need multiple threads that coordinate their actions
// via an enum of "steps". This enum is the template type E. The enum must have at least the
// following values:
//
// - Finish This value indicates that one thread has finished its last step and is cleaning up.
// The other thread waits for this before it does its last step and cleans up.
// - Abort This value indicates that one thread encountered a GL error and has exited. This
// will cause the other thread (that is waiting for a different step) to also abort.
//
// This class is RAII. It is declared at the top of a thread, and will be deconstructed at the end
// of the thread's outer block. If the thread encounters a GL error, the deconstructor will abort
// the other thread using the E:Abort step.
template <typename E>
class ThreadSynchronization
{
public:
ThreadSynchronization(E *currentStep, std::mutex *mutex, std::condition_variable *condVar)
: mCurrentStep(currentStep), mMutex(mutex), mCondVar(condVar)
{}
~ThreadSynchronization()
{
bool isAborting = false;
{
// If the other thread isn't finished, cause it to abort.
std::unique_lock<std::mutex> lock(*mMutex);
isAborting = *mCurrentStep != E::Finish;
if (isAborting)
{
*mCurrentStep = E::Abort;
}
}
mCondVar->notify_all();
}
// Helper functions to synchronize the threads so that the operations are executed in the
// specific order the test is written for.
bool waitForStep(E waitStep)
{
std::unique_lock<std::mutex> lock(*mMutex);
while (*mCurrentStep != waitStep)
{
// If necessary, abort execution as the other thread has encountered a GL error.
if (*mCurrentStep == E::Abort)
{
return false;
}
mCondVar->wait(lock);
}
return true;
}
void nextStep(E newStep)
{
{
std::unique_lock<std::mutex> lock(*mMutex);
*mCurrentStep = newStep;
}
mCondVar->notify_all();
}
private:
E *mCurrentStep;
std::mutex *mMutex;
std::condition_variable *mCondVar;
};
} // anonymous namespace
using LockStepThreadFunc = std::function<void(EGLDisplay, EGLSurface, EGLContext)>;
void RunLockStepThreads(EGLWindow *window, size_t threadCount, LockStepThreadFunc threadFuncs[]);
} // namespace angle