Commit 7ae70d8f by jchen10 Committed by Commit Bot

ParallelCompile: Parallelize D3D linking

This adds a new linking state to Program. If a Program is in linking state, on the one hand the foreground thread may continue issuing more GL calls, and on the other hand the background linking threads may be accessing Program internally too. Without a proper constraint there must be conflicts between them. For this purpose, we block any further GL calls to Program until it's actually linked. In addition, we prohibit parallel linking an active program, so that ProgramD3D does not have to worry about such similar conflicts. Also changes the WorkerThread to support limiting the number of concurrently running worker threads. BUG=chromium:849576 Change-Id: I52618647539323f8bf27201320bdf7301c4982e6 Reviewed-on: https://chromium-review.googlesource.com/1127495 Commit-Queue: Jie A Chen <jie.a.chen@intel.com> Reviewed-by: 's avatarJamie Madill <jmadill@chromium.org>
parent ea926a36
...@@ -356,7 +356,8 @@ Context::Context(rx::EGLImplFactory *implFactory, ...@@ -356,7 +356,8 @@ Context::Context(rx::EGLImplFactory *implFactory,
mDrawFramebufferObserverBinding(this, kDrawFramebufferSubjectIndex), mDrawFramebufferObserverBinding(this, kDrawFramebufferSubjectIndex),
mReadFramebufferObserverBinding(this, kReadFramebufferSubjectIndex), mReadFramebufferObserverBinding(this, kReadFramebufferSubjectIndex),
mScratchBuffer(1000u), mScratchBuffer(1000u),
mZeroFilledBuffer(1000u) mZeroFilledBuffer(1000u),
mThreadPool(nullptr)
{ {
// Needed to solve a Clang warning of unused variables. // Needed to solve a Clang warning of unused variables.
ANGLE_UNUSED_VARIABLE(mSavedArgsType); ANGLE_UNUSED_VARIABLE(mSavedArgsType);
...@@ -577,6 +578,8 @@ egl::Error Context::onDestroy(const egl::Display *display) ...@@ -577,6 +578,8 @@ egl::Error Context::onDestroy(const egl::Display *display)
mState.mFramebuffers->release(this); mState.mFramebuffers->release(this);
mState.mPipelines->release(this); mState.mPipelines->release(this);
mThreadPool.reset();
mImplementation->onDestroy(this); mImplementation->onDestroy(this);
return egl::NoError(); return egl::NoError();
...@@ -3368,6 +3371,8 @@ void Context::updateCaps() ...@@ -3368,6 +3371,8 @@ void Context::updateCaps()
mValidBufferBindings.set(BufferBinding::DrawIndirect); mValidBufferBindings.set(BufferBinding::DrawIndirect);
mValidBufferBindings.set(BufferBinding::DispatchIndirect); mValidBufferBindings.set(BufferBinding::DispatchIndirect);
} }
mThreadPool = angle::WorkerThreadPool::Create(mExtensions.parallelShaderCompile);
} }
void Context::initWorkarounds() void Context::initWorkarounds()
...@@ -5558,8 +5563,20 @@ void Context::linkProgram(GLuint program) ...@@ -5558,8 +5563,20 @@ void Context::linkProgram(GLuint program)
Program *programObject = getProgram(program); Program *programObject = getProgram(program);
ASSERT(programObject); ASSERT(programObject);
handleError(programObject->link(this)); handleError(programObject->link(this));
mGLState.onProgramExecutableChange(programObject);
mStateCache.onProgramExecutableChange(this); // Don't parallel link a program which is active in any GL contexts. With this assumption, we
// don't need to worry that:
// 1. Draw calls after link use the new executable code or the old one depending on the link
// result.
// 2. When a backend program, e.g., ProgramD3D is linking, other backend classes like
// StateManager11, Renderer11, etc., may have a chance to make unexpected calls to
// ProgramD3D.
if (programObject->isInUse())
{
// isLinked() which forces to resolve linking, will be called.
mGLState.onProgramExecutableChange(programObject);
mStateCache.onProgramExecutableChange(this);
}
} }
void Context::releaseShaderCompiler() void Context::releaseShaderCompiler()
...@@ -7551,7 +7568,14 @@ GLenum Context::getConvertedRenderbufferFormat(GLenum internalformat) const ...@@ -7551,7 +7568,14 @@ GLenum Context::getConvertedRenderbufferFormat(GLenum internalformat) const
void Context::maxShaderCompilerThreads(GLuint count) void Context::maxShaderCompilerThreads(GLuint count)
{ {
GLuint oldCount = mGLState.getMaxShaderCompilerThreads();
mGLState.setMaxShaderCompilerThreads(count); mGLState.setMaxShaderCompilerThreads(count);
// A count of zero specifies a request for no parallel compiling or linking.
if ((oldCount == 0 || count == 0) && (oldCount != 0 || count != 0))
{
mThreadPool = angle::WorkerThreadPool::Create(count > 0);
}
mThreadPool->setMaxThreads(count);
} }
bool Context::isGLES1() const bool Context::isGLES1() const
......
...@@ -1538,6 +1538,7 @@ class Context final : public egl::LabeledObject, angle::NonCopyable, public angl ...@@ -1538,6 +1538,7 @@ class Context final : public egl::LabeledObject, angle::NonCopyable, public angl
// GL_KHR_parallel_shader_compile // GL_KHR_parallel_shader_compile
void maxShaderCompilerThreads(GLuint count); void maxShaderCompilerThreads(GLuint count);
std::shared_ptr<angle::WorkerThreadPool> getWorkerThreadPool() const { return mThreadPool; }
const StateCache &getStateCache() const { return mStateCache; } const StateCache &getStateCache() const { return mStateCache; }
...@@ -1692,6 +1693,8 @@ class Context final : public egl::LabeledObject, angle::NonCopyable, public angl ...@@ -1692,6 +1693,8 @@ class Context final : public egl::LabeledObject, angle::NonCopyable, public angl
// Not really a property of context state. The size and contexts change per-api-call. // Not really a property of context state. The size and contexts change per-api-call.
mutable angle::ScratchBuffer mScratchBuffer; mutable angle::ScratchBuffer mScratchBuffer;
mutable angle::ScratchBuffer mZeroFilledBuffer; mutable angle::ScratchBuffer mZeroFilledBuffer;
std::shared_ptr<angle::WorkerThreadPool> mThreadPool;
}; };
template <typename T> template <typename T>
......
...@@ -13,6 +13,7 @@ ...@@ -13,6 +13,7 @@
#include "common/angleutils.h" #include "common/angleutils.h"
#include "libANGLE/State.h" #include "libANGLE/State.h"
#include "libANGLE/Version.h" #include "libANGLE/Version.h"
#include "libANGLE/WorkerThread.h"
#include "libANGLE/params.h" #include "libANGLE/params.h"
namespace gl namespace gl
......
...@@ -471,12 +471,12 @@ class Program final : angle::NonCopyable, public LabeledObject ...@@ -471,12 +471,12 @@ class Program final : angle::NonCopyable, public LabeledObject
Program(rx::GLImplFactory *factory, ShaderProgramManager *manager, GLuint handle); Program(rx::GLImplFactory *factory, ShaderProgramManager *manager, GLuint handle);
void onDestroy(const Context *context); void onDestroy(const Context *context);
GLuint id() const { return mHandle; } GLuint id() const;
void setLabel(const std::string &label) override; void setLabel(const std::string &label) override;
const std::string &getLabel() const override; const std::string &getLabel() const override;
rx::ProgramImpl *getImplementation() const { return mProgram; } rx::ProgramImpl *getImplementation() const;
void attachShader(Shader *shader); void attachShader(Shader *shader);
void detachShader(const Context *context, Shader *shader); void detachShader(const Context *context, Shader *shader);
...@@ -496,8 +496,15 @@ class Program final : angle::NonCopyable, public LabeledObject ...@@ -496,8 +496,15 @@ class Program final : angle::NonCopyable, public LabeledObject
GLint components, GLint components,
const GLfloat *coeffs); const GLfloat *coeffs);
// KHR_parallel_shader_compile
// Try to link the program asynchrously. As a result, background threads may be launched to
// execute the linking tasks concurrently.
Error link(const Context *context); Error link(const Context *context);
bool isLinked() const { return mLinked; }
// Peek whether there is any running linking tasks.
bool isLinking() const;
bool isLinked() const;
bool hasLinkedShaderStage(ShaderType shaderType) const bool hasLinkedShaderStage(ShaderType shaderType) const
{ {
...@@ -536,15 +543,12 @@ class Program final : angle::NonCopyable, public LabeledObject ...@@ -536,15 +543,12 @@ class Program final : angle::NonCopyable, public LabeledObject
GLchar *name) const; GLchar *name) const;
GLint getActiveAttributeCount() const; GLint getActiveAttributeCount() const;
GLint getActiveAttributeMaxLength() const; GLint getActiveAttributeMaxLength() const;
const std::vector<sh::Attribute> &getAttributes() const { return mState.mAttributes; } const std::vector<sh::Attribute> &getAttributes() const;
GLint getFragDataLocation(const std::string &name) const; GLint getFragDataLocation(const std::string &name) const;
size_t getOutputResourceCount() const; size_t getOutputResourceCount() const;
const std::vector<GLenum> &getOutputVariableTypes() const const std::vector<GLenum> &getOutputVariableTypes() const;
{ DrawBufferMask getActiveOutputVariables() const;
return mState.mOutputVariableTypes;
}
DrawBufferMask getActiveOutputVariables() const { return mState.mActiveOutputVariables; }
void getActiveUniform(GLuint index, void getActiveUniform(GLuint index,
GLsizei bufsize, GLsizei bufsize,
...@@ -558,16 +562,8 @@ class Program final : angle::NonCopyable, public LabeledObject ...@@ -558,16 +562,8 @@ class Program final : angle::NonCopyable, public LabeledObject
bool isValidUniformLocation(GLint location) const; bool isValidUniformLocation(GLint location) const;
const LinkedUniform &getUniformByLocation(GLint location) const; const LinkedUniform &getUniformByLocation(GLint location) const;
const VariableLocation &getUniformLocation(GLint location) const; const VariableLocation &getUniformLocation(GLint location) const;
const std::vector<VariableLocation> &getUniformLocations() const const std::vector<VariableLocation> &getUniformLocations() const;
{ const LinkedUniform &getUniformByIndex(GLuint index) const;
return mState.mUniformLocations;
}
const LinkedUniform &getUniformByIndex(GLuint index) const
{
ASSERT(index < static_cast<size_t>(mState.mUniforms.size()));
return mState.mUniforms[index];
}
const BufferVariable &getBufferVariableByIndex(GLuint index) const; const BufferVariable &getBufferVariableByIndex(GLuint index) const;
...@@ -678,6 +674,7 @@ class Program final : angle::NonCopyable, public LabeledObject ...@@ -678,6 +674,7 @@ class Program final : angle::NonCopyable, public LabeledObject
void addRef(); void addRef();
void release(const Context *context); void release(const Context *context);
unsigned int getRefCount() const; unsigned int getRefCount() const;
bool isInUse() const { return getRefCount() != 0; }
void flagForDeletion(); void flagForDeletion();
bool isFlaggedForDeletion() const; bool isFlaggedForDeletion() const;
...@@ -697,34 +694,16 @@ class Program final : angle::NonCopyable, public LabeledObject ...@@ -697,34 +694,16 @@ class Program final : angle::NonCopyable, public LabeledObject
bool isValidated() const; bool isValidated() const;
bool samplesFromTexture(const State &state, GLuint textureID) const; bool samplesFromTexture(const State &state, GLuint textureID) const;
const AttributesMask &getActiveAttribLocationsMask() const const AttributesMask &getActiveAttribLocationsMask() const;
{ const std::vector<SamplerBinding> &getSamplerBindings() const;
return mState.mActiveAttribLocationsMask; const std::vector<ImageBinding> &getImageBindings() const;
} const sh::WorkGroupSize &getComputeShaderLocalSize() const;
PrimitiveMode getGeometryShaderInputPrimitiveType() const;
const std::vector<SamplerBinding> &getSamplerBindings() const PrimitiveMode getGeometryShaderOutputPrimitiveType() const;
{ GLint getGeometryShaderInvocations() const;
return mState.mSamplerBindings; GLint getGeometryShaderMaxVertices() const;
}
const std::vector<ImageBinding> &getImageBindings() const { return mState.mImageBindings; } const ProgramState &getState() const;
const sh::WorkGroupSize &getComputeShaderLocalSize() const
{
return mState.mComputeShaderLocalSize;
}
PrimitiveMode getGeometryShaderInputPrimitiveType() const
{
return mState.mGeometryShaderInputPrimitiveType;
}
PrimitiveMode getGeometryShaderOutputPrimitiveType() const
{
return mState.mGeometryShaderOutputPrimitiveType;
}
GLint getGeometryShaderInvocations() const { return mState.mGeometryShaderInvocations; }
GLint getGeometryShaderMaxVertices() const { return mState.mGeometryShaderMaxVertices; }
const ProgramState &getState() const { return mState; }
static LinkMismatchError LinkValidateVariablesBase( static LinkMismatchError LinkValidateVariablesBase(
const sh::ShaderVariable &variable1, const sh::ShaderVariable &variable1,
...@@ -745,21 +724,18 @@ class Program final : angle::NonCopyable, public LabeledObject ...@@ -745,21 +724,18 @@ class Program final : angle::NonCopyable, public LabeledObject
const sh::Attribute &getInputResource(GLuint index) const; const sh::Attribute &getInputResource(GLuint index) const;
const sh::OutputVariable &getOutputResource(GLuint index) const; const sh::OutputVariable &getOutputResource(GLuint index) const;
const ProgramBindings &getAttributeBindings() const { return mAttributeBindings; } const ProgramBindings &getAttributeBindings() const;
const ProgramBindings &getUniformLocationBindings() const { return mUniformLocationBindings; } const ProgramBindings &getUniformLocationBindings() const;
const ProgramBindings &getFragmentInputBindings() const { return mFragmentInputBindings; } const ProgramBindings &getFragmentInputBindings() const;
int getNumViews() const { return mState.getNumViews(); } int getNumViews() const;
bool usesMultiview() const { return mState.usesMultiview(); } bool usesMultiview() const;
ComponentTypeMask getDrawBufferTypeMask() const { return mState.mDrawBufferTypeMask; } ComponentTypeMask getDrawBufferTypeMask() const;
ComponentTypeMask getAttributesTypeMask() const { return mState.mAttributesTypeMask; } ComponentTypeMask getAttributesTypeMask() const;
AttributesMask getAttributesMask() const { return mState.mAttributesMask; } AttributesMask getAttributesMask() const;
const std::vector<GLsizei> &getTransformFeedbackStrides() const const std::vector<GLsizei> &getTransformFeedbackStrides() const;
{
return mState.mTransformFeedbackStrides;
}
const ActiveTextureMask &getActiveSamplersMask() const { return mState.mActiveSamplersMask; } const ActiveTextureMask &getActiveSamplersMask() const { return mState.mActiveSamplersMask; }
...@@ -769,6 +745,8 @@ class Program final : angle::NonCopyable, public LabeledObject ...@@ -769,6 +745,8 @@ class Program final : angle::NonCopyable, public LabeledObject
} }
private: private:
struct LinkingState;
~Program() override; ~Program() override;
void unlink(); void unlink();
...@@ -857,6 +835,10 @@ class Program final : angle::NonCopyable, public LabeledObject ...@@ -857,6 +835,10 @@ class Program final : angle::NonCopyable, public LabeledObject
GLuint getSamplerUniformBinding(const VariableLocation &uniformLocation) const; GLuint getSamplerUniformBinding(const VariableLocation &uniformLocation) const;
bool validateSamplersImpl(InfoLog *infoLog, const Caps &caps); bool validateSamplersImpl(InfoLog *infoLog, const Caps &caps);
// Try to resolve linking.
void resolveLink() const;
// Block until linking is finished and resolve it.
void resolveLinkImpl();
ProgramState mState; ProgramState mState;
rx::ProgramImpl *mProgram; rx::ProgramImpl *mProgram;
...@@ -873,6 +855,7 @@ class Program final : angle::NonCopyable, public LabeledObject ...@@ -873,6 +855,7 @@ class Program final : angle::NonCopyable, public LabeledObject
ProgramBindings mFragmentInputBindings; ProgramBindings mFragmentInputBindings;
bool mLinked; bool mLinked;
std::unique_ptr<LinkingState> mLinkingState;
bool mDeleteStatus; // Flag to indicate that the program can be deleted when no longer in use bool mDeleteStatus; // Flag to indicate that the program can be deleted when no longer in use
unsigned int mRefCount; unsigned int mRefCount;
......
...@@ -10,148 +10,205 @@ ...@@ -10,148 +10,205 @@
#include "libANGLE/WorkerThread.h" #include "libANGLE/WorkerThread.h"
#if (ANGLE_STD_ASYNC_WORKERS == ANGLE_ENABLED)
#include <condition_variable>
#include <future>
#include <mutex>
#include <queue>
#include <thread>
#endif // (ANGLE_STD_ASYNC_WORKERS == ANGLE_ENABLED)
namespace angle namespace angle
{ {
namespace priv WaitableEvent::WaitableEvent() = default;
{ WaitableEvent::~WaitableEvent() = default;
// SingleThreadedWorkerPool implementation.
SingleThreadedWorkerPool::SingleThreadedWorkerPool(size_t maxThreads)
: WorkerThreadPoolBase(maxThreads)
{
}
SingleThreadedWorkerPool::~SingleThreadedWorkerPool() WorkerThreadPool::WorkerThreadPool() = default;
{ WorkerThreadPool::~WorkerThreadPool() = default;
}
SingleThreadedWaitableEvent SingleThreadedWorkerPool::postWorkerTaskImpl(Closure *task) class SingleThreadedWaitableEvent final : public WaitableEvent
{ {
(*task)(); public:
return SingleThreadedWaitableEvent(EventResetPolicy::Automatic, EventInitialState::Signaled); SingleThreadedWaitableEvent() = default;
} ~SingleThreadedWaitableEvent() = default;
// SingleThreadedWaitableEvent implementation. void wait() override;
SingleThreadedWaitableEvent::SingleThreadedWaitableEvent() bool isReady() override;
: SingleThreadedWaitableEvent(EventResetPolicy::Automatic, EventInitialState::NonSignaled) };
{
}
SingleThreadedWaitableEvent::SingleThreadedWaitableEvent(EventResetPolicy resetPolicy, void SingleThreadedWaitableEvent::wait()
EventInitialState initialState)
: WaitableEventBase(resetPolicy, initialState)
{ {
} }
SingleThreadedWaitableEvent::~SingleThreadedWaitableEvent() bool SingleThreadedWaitableEvent::isReady()
{ {
return true;
} }
SingleThreadedWaitableEvent::SingleThreadedWaitableEvent(SingleThreadedWaitableEvent &&other) class SingleThreadedWorkerPool final : public WorkerThreadPool
: WaitableEventBase(std::move(other))
{ {
} public:
std::shared_ptr<WaitableEvent> postWorkerTask(std::shared_ptr<Closure> task) override;
void setMaxThreads(size_t maxThreads) override;
};
SingleThreadedWaitableEvent &SingleThreadedWaitableEvent::operator=( // SingleThreadedWorkerPool implementation.
SingleThreadedWaitableEvent &&other) std::shared_ptr<WaitableEvent> SingleThreadedWorkerPool::postWorkerTask(
{ std::shared_ptr<Closure> task)
return copyBase(std::move(other));
}
void SingleThreadedWaitableEvent::resetImpl()
{
mSignaled = false;
}
void SingleThreadedWaitableEvent::waitImpl()
{ {
(*task)();
return std::make_shared<SingleThreadedWaitableEvent>();
} }
void SingleThreadedWaitableEvent::signalImpl() void SingleThreadedWorkerPool::setMaxThreads(size_t maxThreads)
{ {
mSignaled = true;
} }
#if (ANGLE_STD_ASYNC_WORKERS == ANGLE_ENABLED) #if (ANGLE_STD_ASYNC_WORKERS == ANGLE_ENABLED)
// AsyncWorkerPool implementation. class AsyncWaitableEvent final : public WaitableEvent
AsyncWorkerPool::AsyncWorkerPool(size_t maxThreads) : WorkerThreadPoolBase(maxThreads)
{ {
} public:
AsyncWaitableEvent() : mIsPending(true) {}
~AsyncWaitableEvent() = default;
AsyncWorkerPool::~AsyncWorkerPool() void wait() override;
{ bool isReady() override;
}
AsyncWaitableEvent AsyncWorkerPool::postWorkerTaskImpl(Closure *task) private:
{ friend class AsyncWorkerPool;
auto future = std::async(std::launch::async, [task] { (*task)(); }); void setFuture(std::future<void> &&future);
AsyncWaitableEvent waitable(EventResetPolicy::Automatic, EventInitialState::NonSignaled); // To block wait() when the task is stil in queue to be run.
// Also to protect the concurrent accesses from both main thread and
// background threads to the member fields.
std::mutex mMutex;
waitable.setFuture(std::move(future)); bool mIsPending;
std::condition_variable mCondition;
std::future<void> mFuture;
};
return waitable; void AsyncWaitableEvent::setFuture(std::future<void> &&future)
}
// AsyncWaitableEvent implementation.
AsyncWaitableEvent::AsyncWaitableEvent()
: AsyncWaitableEvent(EventResetPolicy::Automatic, EventInitialState::NonSignaled)
{ {
mFuture = std::move(future);
} }
AsyncWaitableEvent::AsyncWaitableEvent(EventResetPolicy resetPolicy, EventInitialState initialState) void AsyncWaitableEvent::wait()
: WaitableEventBase(resetPolicy, initialState)
{ {
} {
std::unique_lock<std::mutex> lock(mMutex);
mCondition.wait(lock, [this] { return !mIsPending; });
}
AsyncWaitableEvent::~AsyncWaitableEvent() ASSERT(mFuture.valid());
{ mFuture.wait();
} }
AsyncWaitableEvent::AsyncWaitableEvent(AsyncWaitableEvent &&other) bool AsyncWaitableEvent::isReady()
: WaitableEventBase(std::move(other)), mFuture(std::move(other.mFuture))
{ {
std::lock_guard<std::mutex> lock(mMutex);
if (mIsPending)
{
return false;
}
ASSERT(mFuture.valid());
return mFuture.wait_for(std::chrono::seconds(0)) == std::future_status::ready;
} }
AsyncWaitableEvent &AsyncWaitableEvent::operator=(AsyncWaitableEvent &&other) class AsyncWorkerPool final : public WorkerThreadPool
{ {
std::swap(mFuture, other.mFuture); public:
return copyBase(std::move(other)); AsyncWorkerPool(size_t maxThreads) : mMaxThreads(maxThreads), mRunningThreads(0){};
} ~AsyncWorkerPool() = default;
void AsyncWaitableEvent::setFuture(std::future<void> &&future) std::shared_ptr<WaitableEvent> postWorkerTask(std::shared_ptr<Closure> task) override;
{ void setMaxThreads(size_t maxThreads) override;
mFuture = std::move(future);
} private:
void checkToRunPendingTasks();
// To protect the concurrent accesses from both main thread and background
// threads to the member fields.
std::mutex mMutex;
size_t mMaxThreads;
size_t mRunningThreads;
std::queue<std::pair<std::shared_ptr<AsyncWaitableEvent>, std::shared_ptr<Closure>>> mTaskQueue;
};
void AsyncWaitableEvent::resetImpl() // AsyncWorkerPool implementation.
std::shared_ptr<WaitableEvent> AsyncWorkerPool::postWorkerTask(std::shared_ptr<Closure> task)
{ {
mSignaled = false; ASSERT(mMaxThreads > 0);
mFuture = std::future<void>();
auto waitable = std::make_shared<AsyncWaitableEvent>();
{
std::lock_guard<std::mutex> lock(mMutex);
mTaskQueue.push(std::make_pair(waitable, task));
}
checkToRunPendingTasks();
return waitable;
} }
void AsyncWaitableEvent::waitImpl() void AsyncWorkerPool::setMaxThreads(size_t maxThreads)
{ {
if (mSignaled || !mFuture.valid())
{ {
return; std::lock_guard<std::mutex> lock(mMutex);
mMaxThreads = (maxThreads == 0xFFFFFFFF ? std::thread::hardware_concurrency() : maxThreads);
} }
checkToRunPendingTasks();
mFuture.wait();
signal();
} }
void AsyncWaitableEvent::signalImpl() void AsyncWorkerPool::checkToRunPendingTasks()
{ {
mSignaled = true; std::lock_guard<std::mutex> lock(mMutex);
while (mRunningThreads < mMaxThreads && !mTaskQueue.empty())
if (mResetPolicy == EventResetPolicy::Automatic)
{ {
reset(); auto task = mTaskQueue.front();
mTaskQueue.pop();
auto waitable = task.first;
auto closure = task.second;
auto future = std::async(std::launch::async, [closure, this] {
(*closure)();
{
std::lock_guard<std::mutex> lock(mMutex);
ASSERT(mRunningThreads != 0);
--mRunningThreads;
}
checkToRunPendingTasks();
});
++mRunningThreads;
{
std::lock_guard<std::mutex> waitableLock(waitable->mMutex);
waitable->mIsPending = false;
waitable->setFuture(std::move(future));
}
waitable->mCondition.notify_all();
} }
} }
#endif // (ANGLE_STD_ASYNC_WORKERS == ANGLE_ENABLED) #endif // (ANGLE_STD_ASYNC_WORKERS == ANGLE_ENABLED)
} // namespace priv // static
std::shared_ptr<WorkerThreadPool> WorkerThreadPool::Create(bool multithreaded)
{
std::shared_ptr<WorkerThreadPool> pool(nullptr);
#if (ANGLE_STD_ASYNC_WORKERS == ANGLE_ENABLED)
if (multithreaded)
{
pool = std::shared_ptr<WorkerThreadPool>(static_cast<WorkerThreadPool *>(
new AsyncWorkerPool(std::thread::hardware_concurrency())));
}
#endif
if (!pool)
{
return std::shared_ptr<WorkerThreadPool>(
static_cast<WorkerThreadPool *>(new SingleThreadedWorkerPool()));
}
return pool;
}
} // namespace angle } // namespace angle
...@@ -12,31 +12,14 @@ ...@@ -12,31 +12,14 @@
#define LIBANGLE_WORKER_THREAD_H_ #define LIBANGLE_WORKER_THREAD_H_
#include <array> #include <array>
#include <memory>
#include <vector> #include <vector>
#include "common/debug.h" #include "common/debug.h"
#include "libANGLE/features.h" #include "libANGLE/features.h"
#if (ANGLE_STD_ASYNC_WORKERS == ANGLE_ENABLED)
#include <future>
#endif // (ANGLE_STD_ASYNC_WORKERS == ANGLE_ENABLED)
namespace angle namespace angle
{ {
// Indicates whether a WaitableEvent should automatically reset the event state after a single
// waiting thread has been released or remain signaled until reset() is manually invoked.
enum class EventResetPolicy
{
Manual,
Automatic
};
// Specify the initial state on creation.
enum class EventInitialState
{
NonSignaled,
Signaled
};
// A callback function with no return value and no arguments. // A callback function with no return value and no arguments.
class Closure class Closure
...@@ -46,239 +29,47 @@ class Closure ...@@ -46,239 +29,47 @@ class Closure
virtual void operator()() = 0; virtual void operator()() = 0;
}; };
namespace priv
{
// An event that we can wait on, useful for joining worker threads. // An event that we can wait on, useful for joining worker threads.
template <typename Impl> class WaitableEvent : angle::NonCopyable
class WaitableEventBase : angle::NonCopyable
{ {
public: public:
WaitableEventBase(EventResetPolicy resetPolicy, EventInitialState initialState); WaitableEvent();
virtual ~WaitableEvent();
WaitableEventBase(WaitableEventBase &&other);
// Puts the event in the un-signaled state.
void reset();
// Waits indefinitely for the event to be signaled. // Waits indefinitely for the event to be signaled.
void wait(); virtual void wait() = 0;
// Puts the event in the signaled state, causing any thread blocked on Wait to be woken up.
// The event state is reset to non-signaled after a waiting thread has been released.
void signal();
protected: // Peeks whether the event is ready. If ready, wait() will not block.
Impl &copyBase(Impl &&other); virtual bool isReady() = 0;
template <size_t Count> template <size_t Count>
static size_t WaitManyBase(std::array<Impl, Count> *waitables); static void WaitMany(std::array<std::shared_ptr<WaitableEvent>, Count> *waitables)
EventResetPolicy mResetPolicy;
bool mSignaled;
};
template <typename Impl>
WaitableEventBase<Impl>::WaitableEventBase(EventResetPolicy resetPolicy,
EventInitialState initialState)
: mResetPolicy(resetPolicy), mSignaled(initialState == EventInitialState::Signaled)
{
}
template <typename Impl>
WaitableEventBase<Impl>::WaitableEventBase(WaitableEventBase &&other)
: mResetPolicy(other.mResetPolicy), mSignaled(other.mSignaled)
{
}
template <typename Impl>
void WaitableEventBase<Impl>::reset()
{
static_cast<Impl *>(this)->resetImpl();
}
template <typename Impl>
void WaitableEventBase<Impl>::wait()
{
static_cast<Impl *>(this)->waitImpl();
}
template <typename Impl>
void WaitableEventBase<Impl>::signal()
{
static_cast<Impl *>(this)->signalImpl();
}
template <typename Impl>
template <size_t Count>
// static
size_t WaitableEventBase<Impl>::WaitManyBase(std::array<Impl, Count> *waitables)
{
ASSERT(Count > 0);
for (size_t index = 0; index < Count; ++index)
{ {
(*waitables)[index].wait(); ASSERT(Count > 0);
for (size_t index = 0; index < Count; ++index)
{
(*waitables)[index]->wait();
}
} }
return 0;
}
template <typename Impl>
Impl &WaitableEventBase<Impl>::copyBase(Impl &&other)
{
std::swap(mSignaled, other.mSignaled);
std::swap(mResetPolicy, other.mResetPolicy);
return *static_cast<Impl *>(this);
}
class SingleThreadedWaitableEvent : public WaitableEventBase<SingleThreadedWaitableEvent>
{
public:
SingleThreadedWaitableEvent();
SingleThreadedWaitableEvent(EventResetPolicy resetPolicy, EventInitialState initialState);
~SingleThreadedWaitableEvent();
SingleThreadedWaitableEvent(SingleThreadedWaitableEvent &&other);
SingleThreadedWaitableEvent &operator=(SingleThreadedWaitableEvent &&other);
void resetImpl();
void waitImpl();
void signalImpl();
// Wait, synchronously, on multiple events.
// returns the index of a WaitableEvent which has been signaled.
template <size_t Count>
static size_t WaitMany(std::array<SingleThreadedWaitableEvent, Count> *waitables);
}; };
template <size_t Count>
// static
size_t SingleThreadedWaitableEvent::WaitMany(
std::array<SingleThreadedWaitableEvent, Count> *waitables)
{
return WaitableEventBase<SingleThreadedWaitableEvent>::WaitManyBase(waitables);
}
#if (ANGLE_STD_ASYNC_WORKERS == ANGLE_ENABLED)
class AsyncWaitableEvent : public WaitableEventBase<AsyncWaitableEvent>
{
public:
AsyncWaitableEvent();
AsyncWaitableEvent(EventResetPolicy resetPolicy, EventInitialState initialState);
~AsyncWaitableEvent();
AsyncWaitableEvent(AsyncWaitableEvent &&other);
AsyncWaitableEvent &operator=(AsyncWaitableEvent &&other);
void resetImpl();
void waitImpl();
void signalImpl();
// Wait, synchronously, on multiple events.
// returns the index of a WaitableEvent which has been signaled.
template <size_t Count>
static size_t WaitMany(std::array<AsyncWaitableEvent, Count> *waitables);
private:
friend class AsyncWorkerPool;
void setFuture(std::future<void> &&future);
std::future<void> mFuture;
};
template <size_t Count>
// static
size_t AsyncWaitableEvent::WaitMany(std::array<AsyncWaitableEvent, Count> *waitables)
{
return WaitableEventBase<AsyncWaitableEvent>::WaitManyBase(waitables);
}
#endif // (ANGLE_STD_ASYNC_WORKERS == ANGLE_ENABLED)
// The traits class allows the the thread pool to return the "Typed" waitable event from postTask.
// Otherwise postTask would always think it returns the current active type, so the unit tests
// could not run on multiple worker types in the same compilation.
template <typename Impl>
struct WorkerThreadPoolTraits;
class SingleThreadedWorkerPool;
template <>
struct WorkerThreadPoolTraits<SingleThreadedWorkerPool>
{
using WaitableEventType = SingleThreadedWaitableEvent;
};
#if (ANGLE_STD_ASYNC_WORKERS == ANGLE_ENABLED)
class AsyncWorkerPool;
template <>
struct WorkerThreadPoolTraits<AsyncWorkerPool>
{
using WaitableEventType = AsyncWaitableEvent;
};
#endif // (ANGLE_STD_ASYNC_WORKERS == ANGLE_ENABLED)
// Request WorkerThreads from the WorkerThreadPool. Each pool can keep worker threads around so // Request WorkerThreads from the WorkerThreadPool. Each pool can keep worker threads around so
// we avoid the costly spin up and spin down time. // we avoid the costly spin up and spin down time.
template <typename Impl> class WorkerThreadPool : angle::NonCopyable
class WorkerThreadPoolBase : angle::NonCopyable
{ {
public: public:
WorkerThreadPoolBase(size_t maxThreads); WorkerThreadPool();
~WorkerThreadPoolBase(); virtual ~WorkerThreadPool();
using WaitableEventType = typename WorkerThreadPoolTraits<Impl>::WaitableEventType; static std::shared_ptr<WorkerThreadPool> Create(bool multithreaded);
// Returns an event to wait on for the task to finish. // Returns an event to wait on for the task to finish.
// If the pool fails to create the task, returns null. // If the pool fails to create the task, returns null.
WaitableEventType postWorkerTask(Closure *task); virtual std::shared_ptr<WaitableEvent> postWorkerTask(std::shared_ptr<Closure> task) = 0;
};
template <typename Impl>
WorkerThreadPoolBase<Impl>::WorkerThreadPoolBase(size_t maxThreads)
{
}
template <typename Impl>
WorkerThreadPoolBase<Impl>::~WorkerThreadPoolBase()
{
}
template <typename Impl>
typename WorkerThreadPoolBase<Impl>::WaitableEventType WorkerThreadPoolBase<Impl>::postWorkerTask(
Closure *task)
{
return static_cast<Impl *>(this)->postWorkerTaskImpl(task);
}
class SingleThreadedWorkerPool : public WorkerThreadPoolBase<SingleThreadedWorkerPool>
{
public:
SingleThreadedWorkerPool(size_t maxThreads);
~SingleThreadedWorkerPool();
SingleThreadedWaitableEvent postWorkerTaskImpl(Closure *task); virtual void setMaxThreads(size_t maxThreads) = 0;
}; };
#if (ANGLE_STD_ASYNC_WORKERS == ANGLE_ENABLED)
class AsyncWorkerPool : public WorkerThreadPoolBase<AsyncWorkerPool>
{
public:
AsyncWorkerPool(size_t maxThreads);
~AsyncWorkerPool();
AsyncWaitableEvent postWorkerTaskImpl(Closure *task);
};
#endif // (ANGLE_STD_ASYNC_WORKERS == ANGLE_ENABLED)
} // namespace priv
#if (ANGLE_STD_ASYNC_WORKERS == ANGLE_ENABLED)
using WaitableEvent = priv::AsyncWaitableEvent;
using WorkerThreadPool = priv::AsyncWorkerPool;
#else
using WaitableEvent = priv::SingleThreadedWaitableEvent;
using WorkerThreadPool = priv::SingleThreadedWorkerPool;
#endif // (ANGLE_STD_ASYNC_WORKERS == ANGLE_ENABLED)
} // namespace angle } // namespace angle
#endif // LIBANGLE_WORKER_THREAD_H_ #endif // LIBANGLE_WORKER_THREAD_H_
...@@ -16,23 +16,8 @@ using namespace angle; ...@@ -16,23 +16,8 @@ using namespace angle;
namespace namespace
{ {
template <typename T>
class WorkerPoolTest : public ::testing::Test
{
public:
T workerPool = {4};
};
#if (ANGLE_STD_ASYNC_WORKERS == ANGLE_ENABLED)
using WorkerPoolTypes = ::testing::Types<priv::AsyncWorkerPool, priv::SingleThreadedWorkerPool>;
#else
using WorkerPoolTypes = ::testing::Types<priv::SingleThreadedWorkerPool>;
#endif // (ANGLE_STD_ASYNC_WORKERS == ANGLE_ENABLED)
TYPED_TEST_CASE(WorkerPoolTest, WorkerPoolTypes);
// Tests simple worker pool application. // Tests simple worker pool application.
TYPED_TEST(WorkerPoolTest, SimpleTask) TEST(WorkerPoolTest, SimpleTask)
{ {
class TestTask : public Closure class TestTask : public Closure
{ {
...@@ -42,17 +27,23 @@ TYPED_TEST(WorkerPoolTest, SimpleTask) ...@@ -42,17 +27,23 @@ TYPED_TEST(WorkerPoolTest, SimpleTask)
bool fired = false; bool fired = false;
}; };
std::array<TestTask, 4> tasks; std::array<std::shared_ptr<WorkerThreadPool>, 2> pools = {
std::array<typename TypeParam::WaitableEventType, 4> waitables = {{ {WorkerThreadPool::Create(false), WorkerThreadPool::Create(true)}};
this->workerPool.postWorkerTask(&tasks[0]), this->workerPool.postWorkerTask(&tasks[1]), for (auto &pool : pools)
this->workerPool.postWorkerTask(&tasks[2]), this->workerPool.postWorkerTask(&tasks[3]),
}};
TypeParam::WaitableEventType::WaitMany(&waitables);
for (const auto &task : tasks)
{ {
EXPECT_TRUE(task.fired); std::array<std::shared_ptr<TestTask>, 4> tasks = {
{std::make_shared<TestTask>(), std::make_shared<TestTask>(),
std::make_shared<TestTask>(), std::make_shared<TestTask>()}};
std::array<std::shared_ptr<WaitableEvent>, 4> waitables = {
{pool->postWorkerTask(tasks[0]), pool->postWorkerTask(tasks[1]),
pool->postWorkerTask(tasks[2]), pool->postWorkerTask(tasks[3])}};
WaitableEvent::WaitMany(&waitables);
for (const auto &task : tasks)
{
EXPECT_TRUE(task->fired);
}
} }
} }
......
...@@ -1019,6 +1019,9 @@ void QueryProgramiv(const Context *context, const Program *program, GLenum pname ...@@ -1019,6 +1019,9 @@ void QueryProgramiv(const Context *context, const Program *program, GLenum pname
case GL_LINK_STATUS: case GL_LINK_STATUS:
*params = program->isLinked(); *params = program->isLinked();
return; return;
case GL_COMPLETION_STATUS_KHR:
*params = program->isLinking() ? GL_FALSE : GL_TRUE;
return;
case GL_VALIDATE_STATUS: case GL_VALIDATE_STATUS:
*params = program->isValidated(); *params = program->isValidated();
return; return;
...@@ -1162,6 +1165,11 @@ void QueryShaderiv(const Context *context, Shader *shader, GLenum pname, GLint * ...@@ -1162,6 +1165,11 @@ void QueryShaderiv(const Context *context, Shader *shader, GLenum pname, GLint *
case GL_COMPILE_STATUS: case GL_COMPILE_STATUS:
*params = shader->isCompiled(context) ? GL_TRUE : GL_FALSE; *params = shader->isCompiled(context) ? GL_TRUE : GL_FALSE;
return; return;
case GL_COMPLETION_STATUS_KHR:
// TODO(jie.a.chen@intel.com): Parallelize shader compilation.
// http://crbug.com/849576
*params = shader->isCompiled(context) ? GL_TRUE : GL_FALSE;
return;
case GL_INFO_LOG_LENGTH: case GL_INFO_LOG_LENGTH:
*params = shader->getInfoLogLength(context); *params = shader->getInfoLogLength(context);
return; return;
......
...@@ -30,6 +30,35 @@ struct BlockMemberInfo; ...@@ -30,6 +30,35 @@ struct BlockMemberInfo;
namespace rx namespace rx
{ {
// Provides a mechanism to access the result of asynchronous linking.
class LinkEvent : angle::NonCopyable
{
public:
virtual ~LinkEvent(){};
// Please be aware that these methods may be called under a gl::Context other
// than the one where the LinkEvent was created.
//
// Waits until the linking is actually done. Returns true if the linking
// succeeded, false otherwise.
virtual bool wait() = 0;
// Peeks whether the linking is still ongoing.
virtual bool isLinking() = 0;
};
// Wraps an already done linking.
class LinkEventDone final : public LinkEvent
{
public:
LinkEventDone(const gl::LinkResult &result) : mResult(result) {}
bool wait() override { return (!mResult.isError() && mResult.getResult()); }
bool isLinking() override { return false; }
private:
gl::LinkResult mResult;
};
class ProgramImpl : angle::NonCopyable class ProgramImpl : angle::NonCopyable
{ {
public: public:
...@@ -44,9 +73,9 @@ class ProgramImpl : angle::NonCopyable ...@@ -44,9 +73,9 @@ class ProgramImpl : angle::NonCopyable
virtual void setBinaryRetrievableHint(bool retrievable) = 0; virtual void setBinaryRetrievableHint(bool retrievable) = 0;
virtual void setSeparable(bool separable) = 0; virtual void setSeparable(bool separable) = 0;
virtual gl::LinkResult link(const gl::Context *context, virtual std::unique_ptr<LinkEvent> link(const gl::Context *context,
const gl::ProgramLinkedResources &resources, const gl::ProgramLinkedResources &resources,
gl::InfoLog &infoLog) = 0; gl::InfoLog &infoLog) = 0;
virtual GLboolean validate(const gl::Caps &caps, gl::InfoLog *infoLog) = 0; virtual GLboolean validate(const gl::Caps &caps, gl::InfoLog *infoLog) = 0;
virtual void setUniform1fv(GLint location, GLsizei count, const GLfloat *v) = 0; virtual void setUniform1fv(GLint location, GLsizei count, const GLfloat *v) = 0;
......
...@@ -196,9 +196,9 @@ class ProgramD3D : public ProgramImpl ...@@ -196,9 +196,9 @@ class ProgramD3D : public ProgramImpl
ShaderExecutableD3D **outExectuable, ShaderExecutableD3D **outExectuable,
gl::InfoLog *infoLog); gl::InfoLog *infoLog);
angle::Result getComputeExecutable(ShaderExecutableD3D **outExecutable); angle::Result getComputeExecutable(ShaderExecutableD3D **outExecutable);
gl::LinkResult link(const gl::Context *context, std::unique_ptr<LinkEvent> link(const gl::Context *context,
const gl::ProgramLinkedResources &resources, const gl::ProgramLinkedResources &resources,
gl::InfoLog &infoLog) override; gl::InfoLog &infoLog) override;
GLboolean validate(const gl::Caps &caps, gl::InfoLog *infoLog) override; GLboolean validate(const gl::Caps &caps, gl::InfoLog *infoLog) override;
void setPathFragmentInputGen(const std::string &inputName, void setPathFragmentInputGen(const std::string &inputName,
...@@ -311,6 +311,7 @@ class ProgramD3D : public ProgramImpl ...@@ -311,6 +311,7 @@ class ProgramD3D : public ProgramImpl
class GetVertexExecutableTask; class GetVertexExecutableTask;
class GetPixelExecutableTask; class GetPixelExecutableTask;
class GetGeometryExecutableTask; class GetGeometryExecutableTask;
class GraphicsProgramLinkEvent;
class VertexExecutable class VertexExecutable
{ {
...@@ -451,7 +452,8 @@ class ProgramD3D : public ProgramImpl ...@@ -451,7 +452,8 @@ class ProgramD3D : public ProgramImpl
GLboolean transpose, GLboolean transpose,
const GLfloat *value); const GLfloat *value);
gl::LinkResult compileProgramExecutables(const gl::Context *context, gl::InfoLog &infoLog); std::unique_ptr<LinkEvent> compileProgramExecutables(const gl::Context *context,
gl::InfoLog &infoLog);
gl::LinkResult compileComputeExecutable(const gl::Context *context, gl::InfoLog &infoLog); gl::LinkResult compileComputeExecutable(const gl::Context *context, gl::InfoLog &infoLog);
void gatherTransformFeedbackVaryings(const gl::VaryingPacking &varyings, void gatherTransformFeedbackVaryings(const gl::VaryingPacking &varyings,
......
...@@ -39,8 +39,7 @@ RendererD3D::RendererD3D(egl::Display *display) ...@@ -39,8 +39,7 @@ RendererD3D::RendererD3D(egl::Display *display)
mCapsInitialized(false), mCapsInitialized(false),
mWorkaroundsInitialized(false), mWorkaroundsInitialized(false),
mDisjoint(false), mDisjoint(false),
mDeviceLost(false), mDeviceLost(false)
mWorkerThreadPool(4)
{ {
} }
...@@ -171,11 +170,6 @@ const gl::Limitations &RendererD3D::getNativeLimitations() const ...@@ -171,11 +170,6 @@ const gl::Limitations &RendererD3D::getNativeLimitations() const
return mNativeLimitations; return mNativeLimitations;
} }
angle::WorkerThreadPool *RendererD3D::getWorkerThreadPool()
{
return &mWorkerThreadPool;
}
Serial RendererD3D::generateSerial() Serial RendererD3D::generateSerial()
{ {
return mSerialFactory.generate(); return mSerialFactory.generate();
......
...@@ -17,7 +17,6 @@ ...@@ -17,7 +17,6 @@
#include "libANGLE/ContextState.h" #include "libANGLE/ContextState.h"
#include "libANGLE/Device.h" #include "libANGLE/Device.h"
#include "libANGLE/Version.h" #include "libANGLE/Version.h"
#include "libANGLE/WorkerThread.h"
#include "libANGLE/angletypes.h" #include "libANGLE/angletypes.h"
#include "libANGLE/formatutils.h" #include "libANGLE/formatutils.h"
#include "libANGLE/renderer/d3d/VertexDataManager.h" #include "libANGLE/renderer/d3d/VertexDataManager.h"
...@@ -384,8 +383,6 @@ class RendererD3D : public BufferFactoryD3D ...@@ -384,8 +383,6 @@ class RendererD3D : public BufferFactoryD3D
angle::Result initRenderTarget(const gl::Context *context, RenderTargetD3D *renderTarget); angle::Result initRenderTarget(const gl::Context *context, RenderTargetD3D *renderTarget);
angle::WorkerThreadPool *getWorkerThreadPool();
virtual angle::Result getIncompleteTexture(const gl::Context *context, virtual angle::Result getIncompleteTexture(const gl::Context *context,
gl::TextureType type, gl::TextureType type,
gl::Texture **textureOut) = 0; gl::Texture **textureOut) = 0;
...@@ -427,8 +424,6 @@ class RendererD3D : public BufferFactoryD3D ...@@ -427,8 +424,6 @@ class RendererD3D : public BufferFactoryD3D
bool mDisjoint; bool mDisjoint;
bool mDeviceLost; bool mDeviceLost;
angle::WorkerThreadPool mWorkerThreadPool;
SerialFactory mSerialFactory; SerialFactory mSerialFactory;
}; };
......
...@@ -125,9 +125,17 @@ void ProgramGL::setSeparable(bool separable) ...@@ -125,9 +125,17 @@ void ProgramGL::setSeparable(bool separable)
mFunctions->programParameteri(mProgramID, GL_PROGRAM_SEPARABLE, separable ? GL_TRUE : GL_FALSE); mFunctions->programParameteri(mProgramID, GL_PROGRAM_SEPARABLE, separable ? GL_TRUE : GL_FALSE);
} }
gl::LinkResult ProgramGL::link(const gl::Context *context, std::unique_ptr<LinkEvent> ProgramGL::link(const gl::Context *context,
const gl::ProgramLinkedResources &resources, const gl::ProgramLinkedResources &resources,
gl::InfoLog &infoLog) gl::InfoLog &infoLog)
{
// TODO(jie.a.chen@intel.com): Parallelize linking.
return std::make_unique<LinkEventDone>(linkImpl(context, resources, infoLog));
}
gl::LinkResult ProgramGL::linkImpl(const gl::Context *context,
const gl::ProgramLinkedResources &resources,
gl::InfoLog &infoLog)
{ {
preLink(); preLink();
......
...@@ -38,9 +38,9 @@ class ProgramGL : public ProgramImpl ...@@ -38,9 +38,9 @@ class ProgramGL : public ProgramImpl
void setBinaryRetrievableHint(bool retrievable) override; void setBinaryRetrievableHint(bool retrievable) override;
void setSeparable(bool separable) override; void setSeparable(bool separable) override;
gl::LinkResult link(const gl::Context *contextImpl, std::unique_ptr<LinkEvent> link(const gl::Context *contextImpl,
const gl::ProgramLinkedResources &resources, const gl::ProgramLinkedResources &resources,
gl::InfoLog &infoLog) override; gl::InfoLog &infoLog) override;
GLboolean validate(const gl::Caps &caps, gl::InfoLog *infoLog) override; GLboolean validate(const gl::Caps &caps, gl::InfoLog *infoLog) override;
void setUniform1fv(GLint location, GLsizei count, const GLfloat *v) override; void setUniform1fv(GLint location, GLsizei count, const GLfloat *v) override;
...@@ -88,6 +88,10 @@ class ProgramGL : public ProgramImpl ...@@ -88,6 +88,10 @@ class ProgramGL : public ProgramImpl
void preLink(); void preLink();
bool checkLinkStatus(gl::InfoLog &infoLog); bool checkLinkStatus(gl::InfoLog &infoLog);
void postLink(); void postLink();
gl::LinkResult linkImpl(const gl::Context *contextImpl,
const gl::ProgramLinkedResources &resources,
gl::InfoLog &infoLog);
void reapplyUBOBindingsIfNeeded(const gl::Context *context); void reapplyUBOBindingsIfNeeded(const gl::Context *context);
bool getUniformBlockSize(const std::string &blockName, bool getUniformBlockSize(const std::string &blockName,
......
...@@ -41,11 +41,11 @@ void ProgramNULL::setSeparable(bool separable) ...@@ -41,11 +41,11 @@ void ProgramNULL::setSeparable(bool separable)
{ {
} }
gl::LinkResult ProgramNULL::link(const gl::Context *contextImpl, std::unique_ptr<LinkEvent> ProgramNULL::link(const gl::Context *contextImpl,
const gl::ProgramLinkedResources &resources, const gl::ProgramLinkedResources &resources,
gl::InfoLog &infoLog) gl::InfoLog &infoLog)
{ {
return true; return std::make_unique<LinkEventDone>(true);
} }
GLboolean ProgramNULL::validate(const gl::Caps &caps, gl::InfoLog *infoLog) GLboolean ProgramNULL::validate(const gl::Caps &caps, gl::InfoLog *infoLog)
......
...@@ -28,9 +28,9 @@ class ProgramNULL : public ProgramImpl ...@@ -28,9 +28,9 @@ class ProgramNULL : public ProgramImpl
void setBinaryRetrievableHint(bool retrievable) override; void setBinaryRetrievableHint(bool retrievable) override;
void setSeparable(bool separable) override; void setSeparable(bool separable) override;
gl::LinkResult link(const gl::Context *context, std::unique_ptr<LinkEvent> link(const gl::Context *context,
const gl::ProgramLinkedResources &resources, const gl::ProgramLinkedResources &resources,
gl::InfoLog &infoLog) override; gl::InfoLog &infoLog) override;
GLboolean validate(const gl::Caps &caps, gl::InfoLog *infoLog) override; GLboolean validate(const gl::Caps &caps, gl::InfoLog *infoLog) override;
void setUniform1fv(GLint location, GLsizei count, const GLfloat *v) override; void setUniform1fv(GLint location, GLsizei count, const GLfloat *v) override;
......
...@@ -250,9 +250,18 @@ void ProgramVk::setSeparable(bool separable) ...@@ -250,9 +250,18 @@ void ProgramVk::setSeparable(bool separable)
UNIMPLEMENTED(); UNIMPLEMENTED();
} }
gl::LinkResult ProgramVk::link(const gl::Context *glContext, std::unique_ptr<LinkEvent> ProgramVk::link(const gl::Context *glContext,
const gl::ProgramLinkedResources &resources, const gl::ProgramLinkedResources &resources,
gl::InfoLog &infoLog) gl::InfoLog &infoLog)
{
// TODO(jie.a.chen@intel.com): Parallelize linking.
// http://crbug.com/849576
return std::make_unique<LinkEventDone>(linkImpl(glContext, resources, infoLog));
}
gl::LinkResult ProgramVk::linkImpl(const gl::Context *glContext,
const gl::ProgramLinkedResources &resources,
gl::InfoLog &infoLog)
{ {
ContextVk *contextVk = vk::GetImpl(glContext); ContextVk *contextVk = vk::GetImpl(glContext);
RendererVk *renderer = contextVk->getRenderer(); RendererVk *renderer = contextVk->getRenderer();
......
...@@ -32,9 +32,9 @@ class ProgramVk : public ProgramImpl ...@@ -32,9 +32,9 @@ class ProgramVk : public ProgramImpl
void setBinaryRetrievableHint(bool retrievable) override; void setBinaryRetrievableHint(bool retrievable) override;
void setSeparable(bool separable) override; void setSeparable(bool separable) override;
gl::LinkResult link(const gl::Context *context, std::unique_ptr<LinkEvent> link(const gl::Context *context,
const gl::ProgramLinkedResources &resources, const gl::ProgramLinkedResources &resources,
gl::InfoLog &infoLog) override; gl::InfoLog &infoLog) override;
GLboolean validate(const gl::Caps &caps, gl::InfoLog *infoLog) override; GLboolean validate(const gl::Caps &caps, gl::InfoLog *infoLog) override;
void setUniform1fv(GLint location, GLsizei count, const GLfloat *v) override; void setUniform1fv(GLint location, GLsizei count, const GLfloat *v) override;
...@@ -136,6 +136,9 @@ class ProgramVk : public ProgramImpl ...@@ -136,6 +136,9 @@ class ProgramVk : public ProgramImpl
template <typename T> template <typename T>
void setUniformImpl(GLint location, GLsizei count, const T *v, GLenum entryPointType); void setUniformImpl(GLint location, GLsizei count, const T *v, GLenum entryPointType);
gl::LinkResult linkImpl(const gl::Context *context,
const gl::ProgramLinkedResources &resources,
gl::InfoLog &infoLog);
// State for the default uniform blocks. // State for the default uniform blocks.
struct DefaultUniformBlock final : private angle::NonCopyable struct DefaultUniformBlock final : private angle::NonCopyable
......
...@@ -4368,6 +4368,7 @@ bool ValidateGetProgramivBase(Context *context, GLuint program, GLenum pname, GL ...@@ -4368,6 +4368,7 @@ bool ValidateGetProgramivBase(Context *context, GLuint program, GLenum pname, GL
{ {
case GL_DELETE_STATUS: case GL_DELETE_STATUS:
case GL_LINK_STATUS: case GL_LINK_STATUS:
case GL_COMPLETION_STATUS_KHR:
case GL_VALIDATE_STATUS: case GL_VALIDATE_STATUS:
case GL_INFO_LOG_LENGTH: case GL_INFO_LOG_LENGTH:
case GL_ATTACHED_SHADERS: case GL_ATTACHED_SHADERS:
...@@ -5309,6 +5310,7 @@ bool ValidateGetShaderivBase(Context *context, GLuint shader, GLenum pname, GLsi ...@@ -5309,6 +5310,7 @@ bool ValidateGetShaderivBase(Context *context, GLuint shader, GLenum pname, GLsi
case GL_SHADER_TYPE: case GL_SHADER_TYPE:
case GL_DELETE_STATUS: case GL_DELETE_STATUS:
case GL_COMPILE_STATUS: case GL_COMPILE_STATUS:
case GL_COMPLETION_STATUS_KHR:
case GL_INFO_LOG_LENGTH: case GL_INFO_LOG_LENGTH:
case GL_SHADER_SOURCE_LENGTH: case GL_SHADER_SOURCE_LENGTH:
break; break;
......
...@@ -8,6 +8,8 @@ ...@@ -8,6 +8,8 @@
#include "test_utils/ANGLETest.h" #include "test_utils/ANGLETest.h"
#include "random_utils.h"
using namespace angle; using namespace angle;
namespace namespace
...@@ -43,6 +45,59 @@ class ParallelShaderCompileTest : public ANGLETest ...@@ -43,6 +45,59 @@ class ParallelShaderCompileTest : public ANGLETest
} }
return true; return true;
} }
class ClearColorWithDraw
{
public:
ClearColorWithDraw(GLubyte color) : mColor(color, color, color, 255) {}
bool compileAndLink()
{
mProgram =
CompileProgramParallel(insertRandomString(essl1_shaders::vs::Simple()),
insertRandomString(essl1_shaders::fs::UniformColor()));
return (mProgram != 0);
}
bool isLinkCompleted()
{
GLint status;
glGetProgramiv(mProgram, GL_COMPLETION_STATUS_KHR, &status);
return (status == GL_TRUE);
}
void drawAndVerify(ParallelShaderCompileTest *test)
{
glClearColor(0, 0, 0, 0);
glClear(GL_COLOR_BUFFER_BIT);
glDisable(GL_DEPTH_TEST);
glUseProgram(mProgram);
ASSERT_GL_NO_ERROR();
GLint colorUniformLocation =
glGetUniformLocation(mProgram, essl1_shaders::ColorUniform());
ASSERT_NE(colorUniformLocation, -1);
auto normalizeColor = mColor.toNormalizedVector();
glUniform4fv(colorUniformLocation, 1, normalizeColor.data());
test->drawQuad(mProgram, essl1_shaders::PositionAttrib(), 0.5f);
EXPECT_PIXEL_COLOR_EQ(test->getWindowWidth() / 2, test->getWindowHeight() / 2, mColor);
glUseProgram(0);
glDeleteProgram(mProgram);
ASSERT_GL_NO_ERROR();
}
private:
std::string insertRandomString(const std::string &source)
{
RNG rng;
std::ostringstream ostream;
ostream << "// Random string to fool program cache: " << rng.randomInt() << "\n"
<< source;
return ostream.str();
}
GLColor mColor;
GLuint mProgram;
};
}; };
// Test basic functionality of GL_KHR_parallel_shader_compile // Test basic functionality of GL_KHR_parallel_shader_compile
...@@ -58,6 +113,38 @@ TEST_P(ParallelShaderCompileTest, Basic) ...@@ -58,6 +113,38 @@ TEST_P(ParallelShaderCompileTest, Basic)
EXPECT_EQ(8, count); EXPECT_EQ(8, count);
} }
// Test to compile and link many programs in parallel.
TEST_P(ParallelShaderCompileTest, LinkAndDrawManyPrograms)
{
ANGLE_SKIP_TEST_IF(!ensureParallelShaderCompileExtensionAvailable());
std::vector<std::unique_ptr<ClearColorWithDraw>> tasks;
constexpr int kTaskCount = 32;
for (int i = 0; i < kTaskCount; ++i)
{
std::unique_ptr<ClearColorWithDraw> task(new ClearColorWithDraw(i * 255 / kTaskCount));
bool isLinking = task->compileAndLink();
ASSERT_TRUE(isLinking);
tasks.push_back(std::move(task));
}
constexpr unsigned int kPollInterval = 100;
while (!tasks.empty())
{
for (unsigned int i = 0; i < tasks.size();)
{
auto &task = tasks[i];
if (task->isLinkCompleted())
{
task->drawAndVerify(this);
tasks.erase(tasks.begin() + i);
continue;
}
++i;
}
Sleep(kPollInterval);
}
}
ANGLE_INSTANTIATE_TEST(ParallelShaderCompileTest, ANGLE_INSTANTIATE_TEST(ParallelShaderCompileTest,
ES2_D3D9(), ES2_D3D9(),
ES2_D3D11(), ES2_D3D11(),
......
...@@ -126,12 +126,11 @@ GLuint CompileProgramWithTransformFeedback( ...@@ -126,12 +126,11 @@ GLuint CompileProgramWithTransformFeedback(
transformFeedbackVaryings, bufferMode); transformFeedbackVaryings, bufferMode);
} }
GLuint CompileProgramWithGSAndTransformFeedback( static GLuint CompileAndLinkProgram(const std::string &vsSource,
const std::string &vsSource, const std::string &gsSource,
const std::string &gsSource, const std::string &fsSource,
const std::string &fsSource, const std::vector<std::string> &transformFeedbackVaryings,
const std::vector<std::string> &transformFeedbackVaryings, GLenum bufferMode)
GLenum bufferMode)
{ {
GLuint program = glCreateProgram(); GLuint program = glCreateProgram();
...@@ -182,6 +181,22 @@ GLuint CompileProgramWithGSAndTransformFeedback( ...@@ -182,6 +181,22 @@ GLuint CompileProgramWithGSAndTransformFeedback(
glLinkProgram(program); glLinkProgram(program);
return program;
}
GLuint CompileProgramWithGSAndTransformFeedback(
const std::string &vsSource,
const std::string &gsSource,
const std::string &fsSource,
const std::vector<std::string> &transformFeedbackVaryings,
GLenum bufferMode)
{
GLuint program =
CompileAndLinkProgram(vsSource, gsSource, fsSource, transformFeedbackVaryings, bufferMode);
if (program == 0)
{
return 0;
}
return CheckLinkStatusAndReturnProgram(program, true); return CheckLinkStatusAndReturnProgram(program, true);
} }
...@@ -190,6 +205,12 @@ GLuint CompileProgram(const std::string &vsSource, const std::string &fsSource) ...@@ -190,6 +205,12 @@ GLuint CompileProgram(const std::string &vsSource, const std::string &fsSource)
return CompileProgramWithGS(vsSource, "", fsSource); return CompileProgramWithGS(vsSource, "", fsSource);
} }
GLuint CompileProgramParallel(const std::string &vsSource, const std::string &fsSource)
{
std::vector<std::string> emptyVector;
return CompileAndLinkProgram(vsSource, "", fsSource, emptyVector, GL_NONE);
}
GLuint CompileProgramWithGS(const std::string &vsSource, GLuint CompileProgramWithGS(const std::string &vsSource,
const std::string &gsSource, const std::string &gsSource,
const std::string &fsSource) const std::string &fsSource)
......
...@@ -34,6 +34,9 @@ CompileProgramWithGSAndTransformFeedback(const std::string &vsSource, ...@@ -34,6 +34,9 @@ CompileProgramWithGSAndTransformFeedback(const std::string &vsSource,
const std::vector<std::string> &transformFeedbackVaryings, const std::vector<std::string> &transformFeedbackVaryings,
GLenum bufferMode); GLenum bufferMode);
ANGLE_EXPORT GLuint CompileProgram(const std::string &vsSource, const std::string &fsSource); ANGLE_EXPORT GLuint CompileProgram(const std::string &vsSource, const std::string &fsSource);
ANGLE_EXPORT GLuint CompileProgramParallel(const std::string &vsSource,
const std::string &fsSource);
ANGLE_EXPORT GLuint CompileProgramWithGS(const std::string &vsSource, ANGLE_EXPORT GLuint CompileProgramWithGS(const std::string &vsSource,
const std::string &gsSource, const std::string &gsSource,
const std::string &fsSource); const std::string &fsSource);
......
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