D3D: Implement multi-thread shader compilation.
Choose to use std::async for now to implement multi-threaded compiles.
This should work across platforms and also be usable for other
threading tasks. Note that std::async does not have a good way to
wait for multiple std::futures. Also the Linux compile of std::async
is broken due to a bug in an old STL version, so disable it on this
platform.
The implementation uses a static polymorphism approach, which should
have very good performance (no virtual calls).
This design leaves the door open for other future implementations,
such as a Win32 thread pool, or one based on angle::Platform.
BUG=angleproject:422
Change-Id: Ia2f13c3af0339efaca1d19b40b3e08ecca61b8e8
Reviewed-on: https://chromium-review.googlesource.com/413712
Commit-Queue: Jamie Madill <jmadill@chromium.org>
Reviewed-by: Geoff Lang <geofflang@chromium.org>
diff --git a/src/libANGLE/renderer/d3d/ProgramD3D.cpp b/src/libANGLE/renderer/d3d/ProgramD3D.cpp
index d00a873..723a5c5 100644
--- a/src/libANGLE/renderer/d3d/ProgramD3D.cpp
+++ b/src/libANGLE/renderer/d3d/ProgramD3D.cpp
@@ -24,6 +24,8 @@
#include "libANGLE/renderer/d3d/VaryingPacking.h"
#include "libANGLE/renderer/d3d/VertexDataManager.h"
+using namespace angle;
+
namespace rx
{
@@ -1147,14 +1149,10 @@
gl::InfoLog tempInfoLog;
gl::InfoLog *currentInfoLog = infoLog ? infoLog : &tempInfoLog;
- gl::Error error = mRenderer->compileToExecutable(
+ ANGLE_TRY(mRenderer->compileToExecutable(
*currentInfoLog, finalPixelHLSL, SHADER_PIXEL, mStreamOutVaryings,
(mState.getTransformFeedbackBufferMode() == GL_SEPARATE_ATTRIBS), mPixelWorkarounds,
- &pixelExecutable);
- if (error.isError())
- {
- return error;
- }
+ &pixelExecutable));
if (pixelExecutable)
{
@@ -1168,7 +1166,7 @@
}
*outExectuable = pixelExecutable;
- return gl::Error(GL_NO_ERROR);
+ return gl::NoError();
}
gl::Error ProgramD3D::getVertexExecutableForInputLayout(const gl::InputLayout &inputLayout,
@@ -1196,14 +1194,10 @@
gl::InfoLog tempInfoLog;
gl::InfoLog *currentInfoLog = infoLog ? infoLog : &tempInfoLog;
- gl::Error error = mRenderer->compileToExecutable(
+ ANGLE_TRY(mRenderer->compileToExecutable(
*currentInfoLog, finalVertexHLSL, SHADER_VERTEX, mStreamOutVaryings,
(mState.getTransformFeedbackBufferMode() == GL_SEPARATE_ATTRIBS), mVertexWorkarounds,
- &vertexExecutable);
- if (error.isError())
- {
- return error;
- }
+ &vertexExecutable));
if (vertexExecutable)
{
@@ -1218,7 +1212,7 @@
}
*outExectuable = vertexExecutable;
- return gl::Error(GL_NO_ERROR);
+ return gl::NoError();
}
gl::Error ProgramD3D::getGeometryExecutableForPrimitiveType(const gl::ContextState &data,
@@ -1274,26 +1268,116 @@
return error;
}
-LinkResult ProgramD3D::compileProgramExecutables(const gl::ContextState &data, gl::InfoLog &infoLog)
+class ProgramD3D::GetExecutableTask : public Closure
{
- const gl::InputLayout &defaultInputLayout =
- GetDefaultInputLayoutFromShader(mState.getAttachedVertexShader());
- ShaderExecutableD3D *defaultVertexExecutable = nullptr;
- ANGLE_TRY(
- getVertexExecutableForInputLayout(defaultInputLayout, &defaultVertexExecutable, &infoLog));
-
- std::vector<GLenum> defaultPixelOutput = GetDefaultOutputLayoutFromShader(getPixelShaderKey());
- ShaderExecutableD3D *defaultPixelExecutable = nullptr;
- ANGLE_TRY(
- getPixelExecutableForOutputLayout(defaultPixelOutput, &defaultPixelExecutable, &infoLog));
-
- // Auto-generate the geometry shader here, if we expect to be using point rendering in D3D11.
- ShaderExecutableD3D *pointGS = nullptr;
- if (usesGeometryShader(GL_POINTS))
+ public:
+ GetExecutableTask(ProgramD3D *program)
+ : mProgram(program), mError(GL_NO_ERROR), mInfoLog(), mResult(nullptr)
{
- getGeometryExecutableForPrimitiveType(data, GL_POINTS, &pointGS, &infoLog);
}
+ virtual gl::Error run() = 0;
+
+ void operator()() override { mError = run(); }
+
+ const gl::Error &getError() const { return mError; }
+ const gl::InfoLog &getInfoLog() const { return mInfoLog; }
+ ShaderExecutableD3D *getResult() { return mResult; }
+
+ protected:
+ ProgramD3D *mProgram;
+ gl::Error mError;
+ gl::InfoLog mInfoLog;
+ ShaderExecutableD3D *mResult;
+};
+
+class ProgramD3D::GetVertexExecutableTask : public ProgramD3D::GetExecutableTask
+{
+ public:
+ GetVertexExecutableTask(ProgramD3D *program) : GetExecutableTask(program) {}
+ gl::Error run() override
+ {
+ const auto &defaultInputLayout =
+ GetDefaultInputLayoutFromShader(mProgram->mState.getAttachedVertexShader());
+
+ ANGLE_TRY(
+ mProgram->getVertexExecutableForInputLayout(defaultInputLayout, &mResult, &mInfoLog));
+
+ return gl::NoError();
+ }
+};
+
+class ProgramD3D::GetPixelExecutableTask : public ProgramD3D::GetExecutableTask
+{
+ public:
+ GetPixelExecutableTask(ProgramD3D *program) : GetExecutableTask(program) {}
+ gl::Error run() override
+ {
+ const auto &defaultPixelOutput =
+ GetDefaultOutputLayoutFromShader(mProgram->getPixelShaderKey());
+
+ ANGLE_TRY(
+ mProgram->getPixelExecutableForOutputLayout(defaultPixelOutput, &mResult, &mInfoLog));
+
+ return gl::NoError();
+ }
+};
+
+class ProgramD3D::GetGeometryExecutableTask : public ProgramD3D::GetExecutableTask
+{
+ public:
+ GetGeometryExecutableTask(ProgramD3D *program, const gl::ContextState &contextState)
+ : GetExecutableTask(program), mContextState(contextState)
+ {
+ }
+
+ gl::Error run() override
+ {
+ // Auto-generate the geometry shader here, if we expect to be using point rendering in
+ // D3D11.
+ if (mProgram->usesGeometryShader(GL_POINTS))
+ {
+ ANGLE_TRY(mProgram->getGeometryExecutableForPrimitiveType(mContextState, GL_POINTS,
+ &mResult, &mInfoLog));
+ }
+
+ return gl::NoError();
+ }
+
+ private:
+ const gl::ContextState &mContextState;
+};
+
+LinkResult ProgramD3D::compileProgramExecutables(const gl::ContextState &contextState,
+ gl::InfoLog &infoLog)
+{
+ // Ensure the compiler is initialized to avoid race conditions.
+ ANGLE_TRY(mRenderer->ensureHLSLCompilerInitialized());
+
+ WorkerThreadPool *workerPool = mRenderer->getWorkerThreadPool();
+
+ GetVertexExecutableTask vertexTask(this);
+ GetPixelExecutableTask pixelTask(this);
+ GetGeometryExecutableTask geometryTask(this, contextState);
+
+ std::array<WaitableEvent, 3> waitEvents = {{workerPool->postWorkerTask(&vertexTask),
+ workerPool->postWorkerTask(&pixelTask),
+ workerPool->postWorkerTask(&geometryTask)}};
+
+ WaitableEvent::WaitMany(&waitEvents);
+
+ infoLog << vertexTask.getInfoLog().str();
+ infoLog << pixelTask.getInfoLog().str();
+ infoLog << geometryTask.getInfoLog().str();
+
+ ANGLE_TRY(vertexTask.getError());
+ ANGLE_TRY(pixelTask.getError());
+ ANGLE_TRY(geometryTask.getError());
+
+ ShaderExecutableD3D *defaultVertexExecutable = vertexTask.getResult();
+ ShaderExecutableD3D *defaultPixelExecutable = pixelTask.getResult();
+ ShaderExecutableD3D *pointGS = geometryTask.getResult();
+
const ShaderD3D *vertexShaderD3D = GetImplAs<ShaderD3D>(mState.getAttachedVertexShader());
if (usesGeometryShader(GL_POINTS) && pointGS)
@@ -1528,11 +1612,7 @@
{
ASSERT(!mDirtySamplerMapping);
- gl::Error error = mRenderer->applyUniforms(*this, drawMode, mD3DUniforms);
- if (error.isError())
- {
- return error;
- }
+ ANGLE_TRY(mRenderer->applyUniforms(*this, drawMode, mD3DUniforms));
for (D3DUniform *d3dUniform : mD3DUniforms)
{
@@ -2173,7 +2253,7 @@
mCachedInputLayout.clear();
const auto &vertexAttributes = state.getVertexArray()->getVertexAttributes();
- for (unsigned int locationIndex : angle::IterateBitSet(mState.getActiveAttribLocationsMask()))
+ for (unsigned int locationIndex : IterateBitSet(mState.getActiveAttribLocationsMask()))
{
int d3dSemantic = mAttribLocationToD3DSemantic[locationIndex];