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: Jamie Madill <jmadill@chromium.org>
diff --git a/src/libANGLE/renderer/d3d/ProgramD3D.cpp b/src/libANGLE/renderer/d3d/ProgramD3D.cpp
index 1cf03f6..ef3264d 100644
--- a/src/libANGLE/renderer/d3d/ProgramD3D.cpp
+++ b/src/libANGLE/renderer/d3d/ProgramD3D.cpp
@@ -1447,72 +1447,135 @@
return angle::Result::Continue();
}
-gl::LinkResult ProgramD3D::compileProgramExecutables(const gl::Context *context,
- gl::InfoLog &infoLog)
+// The LinkEvent implementation for linking a rendering(VS, FS, GS) program.
+class ProgramD3D::GraphicsProgramLinkEvent final : public LinkEvent
+{
+ public:
+ GraphicsProgramLinkEvent(gl::InfoLog &infoLog,
+ std::shared_ptr<WorkerThreadPool> workerPool,
+ std::shared_ptr<ProgramD3D::GetVertexExecutableTask> vertexTask,
+ std::shared_ptr<ProgramD3D::GetPixelExecutableTask> pixelTask,
+ std::shared_ptr<ProgramD3D::GetGeometryExecutableTask> geometryTask,
+ bool useGS,
+ const ShaderD3D *vertexShader,
+ const ShaderD3D *fragmentShader)
+ : mInfoLog(infoLog),
+ mWorkerPool(workerPool),
+ mVertexTask(vertexTask),
+ mPixelTask(pixelTask),
+ mGeometryTask(geometryTask),
+ mWaitEvents(
+ {{std::shared_ptr<WaitableEvent>(workerPool->postWorkerTask(mVertexTask)),
+ std::shared_ptr<WaitableEvent>(workerPool->postWorkerTask(mPixelTask)),
+ std::shared_ptr<WaitableEvent>(workerPool->postWorkerTask(mGeometryTask))}}),
+ mUseGS(useGS),
+ mVertexShader(vertexShader),
+ mFragmentShader(fragmentShader)
+ {
+ }
+
+ bool wait() override
+ {
+ WaitableEvent::WaitMany(&mWaitEvents);
+
+ if (!checkTask(mVertexTask.get()) || !checkTask(mPixelTask.get()) ||
+ !checkTask(mGeometryTask.get()))
+ {
+ return false;
+ }
+
+ ShaderExecutableD3D *defaultVertexExecutable = mVertexTask->getExecutable();
+ ShaderExecutableD3D *defaultPixelExecutable = mPixelTask->getExecutable();
+ ShaderExecutableD3D *pointGS = mGeometryTask->getExecutable();
+
+ if (mUseGS && pointGS)
+ {
+ // Geometry shaders are currently only used internally, so there is no corresponding
+ // shader object at the interface level. For now the geometry shader debug info is
+ // prepended to the vertex shader.
+ mVertexShader->appendDebugInfo("// GEOMETRY SHADER BEGIN\n\n");
+ mVertexShader->appendDebugInfo(pointGS->getDebugInfo());
+ mVertexShader->appendDebugInfo("\nGEOMETRY SHADER END\n\n\n");
+ }
+
+ if (defaultVertexExecutable)
+ {
+ mVertexShader->appendDebugInfo(defaultVertexExecutable->getDebugInfo());
+ }
+
+ if (defaultPixelExecutable)
+ {
+ mFragmentShader->appendDebugInfo(defaultPixelExecutable->getDebugInfo());
+ }
+
+ bool isLinked = (defaultVertexExecutable && defaultPixelExecutable && (!mUseGS || pointGS));
+ if (!isLinked)
+ {
+ mInfoLog << "Failed to create D3D Shaders";
+ }
+ return isLinked;
+ }
+
+ bool isLinking() override
+ {
+ for (auto &event : mWaitEvents)
+ {
+ if (!event->isReady())
+ {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private:
+ bool checkTask(ProgramD3D::GetExecutableTask *task)
+ {
+ if (!task->getInfoLog().empty())
+ {
+ mInfoLog << task->getInfoLog().str();
+ }
+ auto result = task->getResult();
+ if (result.isError())
+ {
+ return false;
+ }
+ return true;
+ }
+
+ gl::InfoLog &mInfoLog;
+ std::shared_ptr<WorkerThreadPool> mWorkerPool;
+ std::shared_ptr<ProgramD3D::GetVertexExecutableTask> mVertexTask;
+ std::shared_ptr<ProgramD3D::GetPixelExecutableTask> mPixelTask;
+ std::shared_ptr<ProgramD3D::GetGeometryExecutableTask> mGeometryTask;
+ std::array<std::shared_ptr<WaitableEvent>, 3> mWaitEvents;
+ bool mUseGS;
+ const ShaderD3D *mVertexShader;
+ const ShaderD3D *mFragmentShader;
+};
+
+std::unique_ptr<LinkEvent> ProgramD3D::compileProgramExecutables(const gl::Context *context,
+ gl::InfoLog &infoLog)
{
// Ensure the compiler is initialized to avoid race conditions.
- ANGLE_TRY(mRenderer->ensureHLSLCompilerInitialized(context));
-
- WorkerThreadPool *workerPool = mRenderer->getWorkerThreadPool();
-
- GetVertexExecutableTask vertexTask(this, context);
- GetPixelExecutableTask pixelTask(this, context);
- GetGeometryExecutableTask geometryTask(this, context);
-
- std::array<WaitableEvent, 3> waitEvents = {{workerPool->postWorkerTask(&vertexTask),
- workerPool->postWorkerTask(&pixelTask),
- workerPool->postWorkerTask(&geometryTask)}};
-
- WaitableEvent::WaitMany(&waitEvents);
-
- if (!vertexTask.getInfoLog().empty())
+ gl::Error result = mRenderer->ensureHLSLCompilerInitialized(context);
+ if (result.isError())
{
- infoLog << vertexTask.getInfoLog().str();
- }
- if (!pixelTask.getInfoLog().empty())
- {
- infoLog << pixelTask.getInfoLog().str();
- }
- if (!geometryTask.getInfoLog().empty())
- {
- infoLog << geometryTask.getInfoLog().str();
+ return std::make_unique<LinkEventDone>(result);
}
- ANGLE_TRY(vertexTask.getResult());
- ANGLE_TRY(pixelTask.getResult());
- ANGLE_TRY(geometryTask.getResult());
-
- ShaderExecutableD3D *defaultVertexExecutable = vertexTask.getExecutable();
- ShaderExecutableD3D *defaultPixelExecutable = pixelTask.getExecutable();
- ShaderExecutableD3D *pointGS = geometryTask.getExecutable();
-
+ auto vertexTask = std::make_shared<GetVertexExecutableTask>(this, context);
+ auto pixelTask = std::make_shared<GetPixelExecutableTask>(this, context);
+ auto geometryTask = std::make_shared<GetGeometryExecutableTask>(this, context);
+ bool useGS = usesGeometryShader(gl::PrimitiveMode::Points);
const ShaderD3D *vertexShaderD3D =
GetImplAs<ShaderD3D>(mState.getAttachedShader(gl::ShaderType::Vertex));
+ const ShaderD3D *fragmentShaderD3D =
+ GetImplAs<ShaderD3D>(mState.getAttachedShader(gl::ShaderType::Fragment));
- if (usesGeometryShader(gl::PrimitiveMode::Points) && pointGS)
- {
- // Geometry shaders are currently only used internally, so there is no corresponding shader
- // object at the interface level. For now the geometry shader debug info is prepended to
- // the vertex shader.
- vertexShaderD3D->appendDebugInfo("// GEOMETRY SHADER BEGIN\n\n");
- vertexShaderD3D->appendDebugInfo(pointGS->getDebugInfo());
- vertexShaderD3D->appendDebugInfo("\nGEOMETRY SHADER END\n\n\n");
- }
-
- if (defaultVertexExecutable)
- {
- vertexShaderD3D->appendDebugInfo(defaultVertexExecutable->getDebugInfo());
- }
-
- if (defaultPixelExecutable)
- {
- const ShaderD3D *fragmentShaderD3D =
- GetImplAs<ShaderD3D>(mState.getAttachedShader(gl::ShaderType::Fragment));
- fragmentShaderD3D->appendDebugInfo(defaultPixelExecutable->getDebugInfo());
- }
-
- return (defaultVertexExecutable && defaultPixelExecutable &&
- (!usesGeometryShader(gl::PrimitiveMode::Points) || pointGS));
+ return std::make_unique<GraphicsProgramLinkEvent>(infoLog, context->getWorkerThreadPool(),
+ vertexTask, pixelTask, geometryTask, useGS,
+ vertexShaderD3D, fragmentShaderD3D);
}
gl::LinkResult ProgramD3D::compileComputeExecutable(const gl::Context *context,
@@ -1546,9 +1609,9 @@
return mComputeExecutable.get() != nullptr;
}
-gl::LinkResult ProgramD3D::link(const gl::Context *context,
- const gl::ProgramLinkedResources &resources,
- gl::InfoLog &infoLog)
+std::unique_ptr<LinkEvent> ProgramD3D::link(const gl::Context *context,
+ const gl::ProgramLinkedResources &resources,
+ gl::InfoLog &infoLog)
{
const auto &data = context->getContextState();
@@ -1565,17 +1628,18 @@
mShaderUniformsDirty.set(gl::ShaderType::Compute);
defineUniformsAndAssignRegisters(context);
+ linkResources(context, resources);
+
gl::LinkResult result = compileComputeExecutable(context, infoLog);
if (result.isError())
{
infoLog << result.getError().getMessage();
- return result;
}
else if (!result.getResult())
{
infoLog << "Failed to create D3D compute shader.";
- return result;
}
+ return std::make_unique<LinkEventDone>(result);
}
else
{
@@ -1600,7 +1664,7 @@
if (shadersD3D[gl::ShaderType::Fragment]->usesFrontFacing())
{
infoLog << "The current renderer doesn't support gl_FrontFacing";
- return false;
+ return std::make_unique<LinkEventDone>(false);
}
}
@@ -1632,22 +1696,10 @@
gatherTransformFeedbackVaryings(resources.varyingPacking, builtins[gl::ShaderType::Vertex]);
- gl::LinkResult result = compileProgramExecutables(context, infoLog);
- if (result.isError())
- {
- infoLog << result.getError().getMessage();
- return result;
- }
- else if (!result.getResult())
- {
- infoLog << "Failed to create D3D shaders.";
- return result;
- }
+ linkResources(context, resources);
+
+ return compileProgramExecutables(context, infoLog);
}
-
- linkResources(context, resources);
-
- return true;
}
GLboolean ProgramD3D::validate(const gl::Caps & /*caps*/, gl::InfoLog * /*infoLog*/)