Hash :
9d99f85f
Author :
Date :
2023-09-14T15:01:14
Add multithreaded texture upload test Bug: angleproject:8341 Change-Id: I8561a64fb28681509a1d65c490888dfa1a423835 Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/4864467 Reviewed-by: Yuxin Hu <yuxinhu@google.com> Commit-Queue: Shahbaz Youssefi <syoussefi@chromium.org> Reviewed-by: Charlie Lao <cclao@google.com>
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 95 96 97 98 99 100 101 102 103 104 105
//
// 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;
}
// Expect increasing order to reduce risk of race conditions / deadlocks.
if (*mCurrentStep > waitStep)
{
FATAL() << "waitForStep requires increasing order. mCurrentStep="
<< (int)*mCurrentStep << ", waitStep=" << (int)waitStep;
}
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[]);
void RunLockStepThreadsWithSize(EGLWindow *window,
EGLint width,
EGLint height,
size_t threadCount,
LockStepThreadFunc threadFuncs[]);
} // namespace angle