diff --git a/src/libANGLE/Program.cpp b/src/libANGLE/Program.cpp index 6544d5c5b..1ef902a6c 100644 --- a/src/libANGLE/Program.cpp +++ b/src/libANGLE/Program.cpp @@ -1121,13 +1121,25 @@ void Program::bindFragmentOutputIndex(GLuint index, const char *name) angle::Result Program::link(const Context *context) { + // Lock the shaders before linking, to prevent them from being modified during a following + // recompile + ScopedShaderLinkLocks shaderLocks; + for (ShaderType shaderType : angle::AllEnums()) + { + gl::Shader *shader = mState.mAttachedShaders[shaderType]; + if (shader) + { + shaderLocks[shaderType] = shader->lockAndGetScopedShaderLinkLock(); + } + } + const angle::FrontendFeatures &frontendFeatures = context->getFrontendFeatures(); if (frontendFeatures.dumpShaderSource.enabled) { dumpProgramInfo(); } - angle::Result result = linkImpl(context); + angle::Result result = linkImpl(context, &shaderLocks); // Avoid having two ProgramExecutables if the link failed and the Program had successfully // linked previously. @@ -1142,7 +1154,7 @@ angle::Result Program::link(const Context *context) // The attached shaders are checked for linking errors by matching up their variables. // Uniform, input and output variables get collected. // The code gets compiled into binaries. -angle::Result Program::linkImpl(const Context *context) +angle::Result Program::linkImpl(const Context *context, ScopedShaderLinkLocks *shaderLocks) { ASSERT(!mLinkingState); // Don't make any local variables pointing to anything within the ProgramExecutable, since @@ -1315,7 +1327,8 @@ angle::Result Program::linkImpl(const Context *context) mLinkingState = std::move(linkingState); mLinkingState->linkingFromBinary = false; mLinkingState->programHash = programHash; - mLinkingState->linkEvent = mProgram->link(context, resources, infoLog, mergedVaryings); + mLinkingState->linkEvent = + mProgram->link(context, resources, infoLog, mergedVaryings, shaderLocks); // Must be after mProgram->link() to avoid misleading the linker about output variables. mState.updateProgramInterfaceInputs(context); diff --git a/src/libANGLE/Program.h b/src/libANGLE/Program.h index e7ff494ec..21a26406d 100644 --- a/src/libANGLE/Program.h +++ b/src/libANGLE/Program.h @@ -818,7 +818,7 @@ class Program final : public LabeledObject, public angle::Subject void unlink(); void deleteSelf(const Context *context); - angle::Result linkImpl(const Context *context); + angle::Result linkImpl(const Context *context, ScopedShaderLinkLocks *shaderLocks); bool linkValidateShaders(const Context *context, InfoLog &infoLog); bool linkAttributes(const Context *context, InfoLog &infoLog); diff --git a/src/libANGLE/Shader.cpp b/src/libANGLE/Shader.cpp index ae5443579..2651e6ae8 100644 --- a/src/libANGLE/Shader.cpp +++ b/src/libANGLE/Shader.cpp @@ -152,6 +152,10 @@ Shader::Shader(ShaderProgramManager *manager, void Shader::onDestroy(const gl::Context *context) { + // There cannot be any link jobs using this shader. Because that means the shader must be + // attached to a program, and ref counting prevents it from being destroyed. + ASSERT(mLinkJobsInProgress == 0); + resolveCompile(context); mImplementation->destroy(); mBoundCompiler.set(context, nullptr); @@ -391,6 +395,9 @@ void Shader::compile(const Context *context) { resolveCompile(context); + // Do not modify the compiled state while there are link jobs in progress. + waitForLinkJobs(); + mState.mCompiledShaderState.translatedSource.clear(); mState.mCompiledShaderState.compiledBinary.clear(); mInfoLog.clear(); @@ -1000,4 +1007,20 @@ void Shader::setShaderKey(const Context *context, angle::base::SHA1HashBytes(shaderKey.data(), shaderKey.size(), mShaderHash.data()); } +void Shader::waitForLinkJobs() +{ + // No known application recompiles shaders right after linking programs. This case is thus + // purely for conformance, and performance is irrelevant. + // + // Wait until there are no pending link jobs. + while (mLinkJobsInProgress > 0) + { + WARN() << "Detected shader recompilation while a link job is in progress. This is " + "inefficient, do not reuse shaders."; + std::this_thread::sleep_for(std::chrono::microseconds(1)); + } + + // There cannot be more link jobs added in the meantime, because of the share group lock. + ASSERT(mLinkJobsInProgress == 0); +} } // namespace gl diff --git a/src/libANGLE/Shader.h b/src/libANGLE/Shader.h index 38ab93a59..2ee3e7e6e 100644 --- a/src/libANGLE/Shader.h +++ b/src/libANGLE/Shader.h @@ -62,6 +62,30 @@ enum class CompileStatus COMPILED, }; +class [[nodiscard]] ScopedShaderLinkLock : angle::NonCopyable +{ + public: + ScopedShaderLinkLock() : mShader(nullptr) {} + ScopedShaderLinkLock(Shader *shader); + ~ScopedShaderLinkLock(); + + void swap(ScopedShaderLinkLock &other) { std::swap(mShader, other.mShader); } + ScopedShaderLinkLock(ScopedShaderLinkLock &&other) + { + mShader = other.mShader; + other.mShader = nullptr; + } + ScopedShaderLinkLock &operator=(ScopedShaderLinkLock &&other) + { + std::swap(mShader, other.mShader); + return *this; + } + + private: + Shader *mShader; +}; +using ScopedShaderLinkLocks = gl::ShaderMap; + class ShaderState final : angle::NonCopyable { public: @@ -288,6 +312,15 @@ class Shader final : angle::NonCopyable, public LabeledObject return; } + void onProgramLinkBegin() { ++mLinkJobsInProgress; } + void onProgramLinkEnd() + { + ASSERT(mLinkJobsInProgress > 0); + --mLinkJobsInProgress; + ASSERT(mLinkJobsInProgress >= 0); + } + ScopedShaderLinkLock lockAndGetScopedShaderLinkLock() { return ScopedShaderLinkLock(this); } + private: struct CompilingState; @@ -310,6 +343,8 @@ class Shader final : angle::NonCopyable, public LabeledObject const ShShaderOutput &outputType, const ShBuiltInResources &resources); + void waitForLinkJobs(); + ShaderState mState; std::unique_ptr mImplementation; const gl::Limitations mRendererLimitations; @@ -328,8 +363,25 @@ class Shader final : angle::NonCopyable, public LabeledObject GLuint mCurrentMaxComputeWorkGroupInvocations; unsigned int mMaxComputeSharedMemory; + + // Number of programs that are currently linking using this shader. If the shader is + // recompiled, it will block until all existing link jobs are finished. + std::atomic_int mLinkJobsInProgress; }; +inline ScopedShaderLinkLock::ScopedShaderLinkLock(Shader *shader) : mShader(shader) +{ + ASSERT(shader != nullptr); + mShader->onProgramLinkBegin(); +} +inline ScopedShaderLinkLock::~ScopedShaderLinkLock() +{ + if (mShader != nullptr) + { + mShader->onProgramLinkEnd(); + } +} + const char *GetShaderTypeString(ShaderType type); std::string GetShaderDumpFileDirectory(); std::string GetShaderDumpFileName(size_t shaderHash); diff --git a/src/libANGLE/renderer/ProgramImpl.h b/src/libANGLE/renderer/ProgramImpl.h index 18448a1c4..4151e0807 100644 --- a/src/libANGLE/renderer/ProgramImpl.h +++ b/src/libANGLE/renderer/ProgramImpl.h @@ -85,8 +85,9 @@ class ProgramImpl : angle::NonCopyable virtual std::unique_ptr link(const gl::Context *context, const gl::ProgramLinkedResources &resources, gl::InfoLog &infoLog, - const gl::ProgramMergedVaryings &mergedVaryings) = 0; - virtual GLboolean validate(const gl::Caps &caps, gl::InfoLog *infoLog) = 0; + const gl::ProgramMergedVaryings &mergedVaryings, + gl::ScopedShaderLinkLocks *shaderLocks) = 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 setUniform2fv(GLint location, GLsizei count, const GLfloat *v) = 0; diff --git a/src/libANGLE/renderer/d3d/ProgramD3D.cpp b/src/libANGLE/renderer/d3d/ProgramD3D.cpp index f76e62568..ba2e0c04c 100644 --- a/src/libANGLE/renderer/d3d/ProgramD3D.cpp +++ b/src/libANGLE/renderer/d3d/ProgramD3D.cpp @@ -564,8 +564,10 @@ uint8_t ProgramD3DMetadata::getCullDistanceArraySize() const class ProgramD3D::GetExecutableTask : public Closure, public d3d::Context { public: - GetExecutableTask(const gl::Context *context, ProgramD3D *program) - : mProgram(program), mContext(context) + GetExecutableTask(const gl::Context *context, + ProgramD3D *program, + gl::ScopedShaderLinkLock &&shaderLock) + : mProgram(program), mContext(context), mShaderLock(std::move(shaderLock)) {} virtual angle::Result run() = 0; @@ -608,6 +610,7 @@ class ProgramD3D::GetExecutableTask : public Closure, public d3d::Context const char *mStoredFunction = nullptr; unsigned int mStoredLine = 0; const gl::Context *mContext = nullptr; + gl::ScopedShaderLinkLock mShaderLock; }; // ProgramD3D Implementation @@ -889,7 +892,7 @@ class ProgramD3D::LoadBinaryTask : public ProgramD3D::GetExecutableTask ProgramD3D *program, gl::BinaryInputStream *stream, gl::InfoLog &infoLog) - : ProgramD3D::GetExecutableTask(context, program) + : ProgramD3D::GetExecutableTask(context, program, gl::ScopedShaderLinkLock()) { ASSERT(mProgram); ASSERT(stream); @@ -1723,13 +1726,17 @@ angle::Result ProgramD3D::getGeometryExecutableForPrimitiveType(d3d::Context *co class ProgramD3D::GetVertexExecutableTask : public ProgramD3D::GetExecutableTask { public: - GetVertexExecutableTask(const gl::Context *context, ProgramD3D *program) - : GetExecutableTask(context, program) + GetVertexExecutableTask(const gl::Context *context, + ProgramD3D *program, + gl::ScopedShaderLinkLock &&shaderLock) + : GetExecutableTask(context, program, std::move(shaderLock)) {} angle::Result run() override { ANGLE_TRACE_EVENT0("gpu.angle", "ProgramD3D::GetVertexExecutableTask::run"); + gl::ScopedShaderLinkLock unlockAtEnd(std::move(mShaderLock)); + ANGLE_TRY(mProgram->getVertexExecutableForCachedInputLayout(this, &mExecutable, &mInfoLog)); return angle::Result::Continue; @@ -1747,12 +1754,17 @@ void ProgramD3D::updateCachedInputLayoutFromShader(const gl::Context *context) class ProgramD3D::GetPixelExecutableTask : public ProgramD3D::GetExecutableTask { public: - GetPixelExecutableTask(const gl::Context *context, ProgramD3D *program) - : GetExecutableTask(context, program) + GetPixelExecutableTask(const gl::Context *context, + ProgramD3D *program, + gl::ScopedShaderLinkLock &&shaderLock) + : GetExecutableTask(context, program, std::move(shaderLock)) {} angle::Result run() override { ANGLE_TRACE_EVENT0("gpu.angle", "ProgramD3D::GetPixelExecutableTask::run"); + + gl::ScopedShaderLinkLock unlockAtEnd(std::move(mShaderLock)); + if (!mProgram->mState.getAttachedShader(gl::ShaderType::Fragment)) { return angle::Result::Continue; @@ -1798,13 +1810,17 @@ class ProgramD3D::GetGeometryExecutableTask : public ProgramD3D::GetExecutableTa public: GetGeometryExecutableTask(const gl::Context *context, ProgramD3D *program, - const gl::State &state) - : GetExecutableTask(context, program), mState(state) + const gl::State &state, + gl::ScopedShaderLinkLock &&shaderLock) + : GetExecutableTask(context, program, std::move(shaderLock)), mState(state) {} angle::Result run() override { ANGLE_TRACE_EVENT0("gpu.angle", "ProgramD3D::GetGeometryExecutableTask::run"); + + gl::ScopedShaderLinkLock unlockAtEnd(std::move(mShaderLock)); + // Auto-generate the geometry shader here, if we expect to be using point rendering in // D3D11. if (mProgram->usesGeometryShader(mState, gl::PrimitiveMode::Points)) @@ -1823,12 +1839,17 @@ class ProgramD3D::GetGeometryExecutableTask : public ProgramD3D::GetExecutableTa class ProgramD3D::GetComputeExecutableTask : public ProgramD3D::GetExecutableTask { public: - GetComputeExecutableTask(const gl::Context *context, ProgramD3D *program) - : GetExecutableTask(context, program) + GetComputeExecutableTask(const gl::Context *context, + ProgramD3D *program, + gl::ScopedShaderLinkLock &&shaderLock) + : GetExecutableTask(context, program, std::move(shaderLock)) {} angle::Result run() override { ANGLE_TRACE_EVENT0("gpu.angle", "ProgramD3D::GetComputeExecutableTask::run"); + + gl::ScopedShaderLinkLock unlockAtEnd(std::move(mShaderLock)); + mProgram->updateCachedImage2DBindLayoutFromShader(gl::ShaderType::Compute); ShaderExecutableD3D *computeExecutable = nullptr; ANGLE_TRY(mProgram->getComputeExecutableForImage2DBindLayout( @@ -1973,8 +1994,10 @@ class ProgramD3D::ComputeProgramLinkEvent final : public LinkEvent std::shared_ptr mWaitEvent; }; -std::unique_ptr ProgramD3D::compileProgramExecutables(const gl::Context *context, - gl::InfoLog &infoLog) +std::unique_ptr ProgramD3D::compileProgramExecutables( + const gl::Context *context, + gl::InfoLog &infoLog, + gl::ScopedShaderLinkLocks *shaderLocks) { ANGLE_TRACE_EVENT0("gpu.angle", "ProgramD3D::compileProgramExecutables"); // Ensure the compiler is initialized to avoid race conditions. @@ -1984,10 +2007,12 @@ std::unique_ptr ProgramD3D::compileProgramExecutables(const gl::Conte return std::make_unique(result); } - auto vertexTask = std::make_shared(context, this); - auto pixelTask = std::make_shared(context, this); - auto geometryTask = - std::make_shared(context, this, context->getState()); + auto vertexTask = std::make_shared( + context, this, std::move((*shaderLocks)[gl::ShaderType::Vertex])); + auto pixelTask = std::make_shared( + context, this, std::move((*shaderLocks)[gl::ShaderType::Fragment])); + auto geometryTask = std::make_shared( + context, this, context->getState(), std::move((*shaderLocks)[gl::ShaderType::Geometry])); bool useGS = usesGeometryShader(context->getState(), gl::PrimitiveMode::Points); gl::Shader *vertexShader = mState.getAttachedShader(gl::ShaderType::Vertex); gl::Shader *fragmentShader = mState.getAttachedShader(gl::ShaderType::Fragment); @@ -2000,8 +2025,10 @@ std::unique_ptr ProgramD3D::compileProgramExecutables(const gl::Conte vertexShaderD3D, fragmentShaderD3D); } -std::unique_ptr ProgramD3D::compileComputeExecutable(const gl::Context *context, - gl::InfoLog &infoLog) +std::unique_ptr ProgramD3D::compileComputeExecutable( + const gl::Context *context, + gl::InfoLog &infoLog, + gl::ScopedShaderLinkLocks *shaderLocks) { ANGLE_TRACE_EVENT0("gpu.angle", "ProgramD3D::compileComputeExecutable"); // Ensure the compiler is initialized to avoid race conditions. @@ -2010,7 +2037,8 @@ std::unique_ptr ProgramD3D::compileComputeExecutable(const gl::Contex { return std::make_unique(result); } - auto computeTask = std::make_shared(context, this); + auto computeTask = std::make_shared( + context, this, std::move((*shaderLocks)[gl::ShaderType::Compute])); std::shared_ptr waitableEvent; @@ -2082,7 +2110,8 @@ angle::Result ProgramD3D::getComputeExecutableForImage2DBindLayout( std::unique_ptr ProgramD3D::link(const gl::Context *context, const gl::ProgramLinkedResources &resources, gl::InfoLog &infoLog, - const gl::ProgramMergedVaryings & /*mergedVaryings*/) + const gl::ProgramMergedVaryings & /*mergedVaryings*/, + gl::ScopedShaderLinkLocks *shaderLocks) { ANGLE_TRACE_EVENT0("gpu.angle", "ProgramD3D::link"); const auto &data = context->getState(); @@ -2111,7 +2140,7 @@ std::unique_ptr ProgramD3D::link(const gl::Context *context, defineUniformsAndAssignRegisters(context); - return compileComputeExecutable(context, infoLog); + return compileComputeExecutable(context, infoLog, shaderLocks); } else { @@ -2213,7 +2242,7 @@ std::unique_ptr ProgramD3D::link(const gl::Context *context, updateCachedInputLayoutFromShader(context); } - return compileProgramExecutables(context, infoLog); + return compileProgramExecutables(context, infoLog, shaderLocks); } } diff --git a/src/libANGLE/renderer/d3d/ProgramD3D.h b/src/libANGLE/renderer/d3d/ProgramD3D.h index 0191207cd..95fefaf61 100644 --- a/src/libANGLE/renderer/d3d/ProgramD3D.h +++ b/src/libANGLE/renderer/d3d/ProgramD3D.h @@ -245,7 +245,8 @@ class ProgramD3D : public ProgramImpl std::unique_ptr link(const gl::Context *context, const gl::ProgramLinkedResources &resources, gl::InfoLog &infoLog, - const gl::ProgramMergedVaryings &mergedVaryings) override; + const gl::ProgramMergedVaryings &mergedVaryings, + gl::ScopedShaderLinkLocks *shaderLocks) override; GLboolean validate(const gl::Caps &caps, gl::InfoLog *infoLog) override; void updateUniformBufferCache(const gl::Caps &caps); @@ -511,9 +512,11 @@ class ProgramD3D : public ProgramImpl const GLfloat *value); std::unique_ptr compileProgramExecutables(const gl::Context *context, - gl::InfoLog &infoLog); + gl::InfoLog &infoLog, + gl::ScopedShaderLinkLocks *shaderLocks); std::unique_ptr compileComputeExecutable(const gl::Context *context, - gl::InfoLog &infoLog); + gl::InfoLog &infoLog, + gl::ScopedShaderLinkLocks *shaderLocks); angle::Result loadBinaryShaderExecutables(d3d::Context *contextD3D, gl::BinaryInputStream *stream, diff --git a/src/libANGLE/renderer/gl/ProgramGL.cpp b/src/libANGLE/renderer/gl/ProgramGL.cpp index c3a11632f..aa933d922 100644 --- a/src/libANGLE/renderer/gl/ProgramGL.cpp +++ b/src/libANGLE/renderer/gl/ProgramGL.cpp @@ -139,9 +139,22 @@ class ProgramGL::LinkTask final : public angle::Closure LinkTask(LinkImplFunctor &&functor) : mLinkImplFunctor(functor), mFallbackToMainContext(false) {} + void setShaderLocks(gl::ScopedShaderLinkLocks *shaderLocks) { mShaderLocks.swap(*shaderLocks); } + void operator()() override { ANGLE_TRACE_EVENT0("gpu.angle", "ProgramGL::LinkTask::run"); + + // Unlock the shaders at the end of the task. + // + // Note that there is a race condition if fallback to main is needed: by the time + // resolveLink() is done, which calls LinkEventGL::wait() and subsequently link on the main + // thread, the shader may get recompiled and the program would end up linking the new + // shaders. This is not easily fixable with the current architecture, as there is no + // guarantee resolveLink() is called before the following shader recompilation. + gl::ScopedShaderLinkLocks unlockAtEnd; + unlockAtEnd.swap(mShaderLocks); + mFallbackToMainContext = mLinkImplFunctor(mInfoLog); } @@ -152,6 +165,8 @@ class ProgramGL::LinkTask final : public angle::Closure LinkImplFunctor mLinkImplFunctor; bool mFallbackToMainContext; std::string mInfoLog; + + gl::ScopedShaderLinkLocks mShaderLocks; }; using PostLinkImplFunctor = std::function; @@ -224,7 +239,8 @@ class ProgramGL::LinkEventGL final : public LinkEvent std::unique_ptr ProgramGL::link(const gl::Context *context, const gl::ProgramLinkedResources &resources, gl::InfoLog &infoLog, - const gl::ProgramMergedVaryings & /*mergedVaryings*/) + const gl::ProgramMergedVaryings & /*mergedVaryings*/, + gl::ScopedShaderLinkLocks *shaderLocks) { ANGLE_TRACE_EVENT0("gpu.angle", "ProgramGL::link"); @@ -472,15 +488,22 @@ std::unique_ptr ProgramGL::link(const gl::Context *context, return angle::Result::Continue; }; + // |shaderLocks| is ignored except when the actual link is done in a job. They will be unlocked + // by the caller. The post-link task does not use the shader's compile state in those cases + // either. + if (mRenderer->hasNativeParallelCompile()) { mFunctions->linkProgram(mProgramID); return std::make_unique(postLinkImplTask, mFunctions, mProgramID); } - else if (workerPool->isAsync() && + else if (workerPool->isAsync() && !mFeatures.disableWorkerContexts.enabled && (!mFeatures.dontRelinkProgramsInParallel.enabled || !mLinkedInParallel)) { + // Make sure the shaders are locked until the task is complete. + linkTask->setShaderLocks(shaderLocks); + mLinkedInParallel = true; return std::make_unique(workerPool, linkTask, postLinkImplTask); } diff --git a/src/libANGLE/renderer/gl/ProgramGL.h b/src/libANGLE/renderer/gl/ProgramGL.h index 574e0bc5d..58cc9967d 100644 --- a/src/libANGLE/renderer/gl/ProgramGL.h +++ b/src/libANGLE/renderer/gl/ProgramGL.h @@ -46,7 +46,8 @@ class ProgramGL : public ProgramImpl std::unique_ptr link(const gl::Context *contextImpl, const gl::ProgramLinkedResources &resources, gl::InfoLog &infoLog, - const gl::ProgramMergedVaryings &mergedVaryings) override; + const gl::ProgramMergedVaryings &mergedVaryings, + gl::ScopedShaderLinkLocks *shaderLocks) override; GLboolean validate(const gl::Caps &caps, gl::InfoLog *infoLog) override; void setUniform1fv(GLint location, GLsizei count, const GLfloat *v) override; diff --git a/src/libANGLE/renderer/gl/RendererGL.cpp b/src/libANGLE/renderer/gl/RendererGL.cpp index 2bda5e8fc..1773e6279 100644 --- a/src/libANGLE/renderer/gl/RendererGL.cpp +++ b/src/libANGLE/renderer/gl/RendererGL.cpp @@ -397,10 +397,7 @@ void RendererGL::framebufferFetchBarrier() bool RendererGL::bindWorkerContext(std::string *infoLog) { - if (mFeatures.disableWorkerContexts.enabled) - { - return false; - } + ASSERT(!mFeatures.disableWorkerContexts.enabled); std::lock_guard lock(mWorkerMutex); std::unique_ptr workerContext; diff --git a/src/libANGLE/renderer/gl/ShaderGL.cpp b/src/libANGLE/renderer/gl/ShaderGL.cpp index 09a07663e..ff545e9fc 100644 --- a/src/libANGLE/renderer/gl/ShaderGL.cpp +++ b/src/libANGLE/renderer/gl/ShaderGL.cpp @@ -424,7 +424,7 @@ std::shared_ptr ShaderGL::compile(const gl::Context *conte result); } } - else if (workerThreadPool->isAsync()) + else if (workerThreadPool->isAsync() && !features.disableWorkerContexts.enabled) { auto compileAndCheckShaderInWorkerFunctor = [this](const char *source) { return compileAndCheckShaderInWorker(source); diff --git a/src/libANGLE/renderer/metal/ProgramMtl.h b/src/libANGLE/renderer/metal/ProgramMtl.h index 8ff8b9e27..e0e9944d0 100644 --- a/src/libANGLE/renderer/metal/ProgramMtl.h +++ b/src/libANGLE/renderer/metal/ProgramMtl.h @@ -130,7 +130,8 @@ class ProgramMtl : public ProgramImpl std::unique_ptr link(const gl::Context *context, const gl::ProgramLinkedResources &resources, gl::InfoLog &infoLog, - const gl::ProgramMergedVaryings &mergedVaryings) override; + const gl::ProgramMergedVaryings &mergedVaryings, + gl::ScopedShaderLinkLocks *shaderLocks) override; GLboolean validate(const gl::Caps &caps, gl::InfoLog *infoLog) override; void setUniform1fv(GLint location, GLsizei count, const GLfloat *v) override; @@ -267,7 +268,8 @@ class ProgramMtl : public ProgramImpl void linkResources(const gl::Context *context, const gl::ProgramLinkedResources &resources); std::unique_ptr compileMslShaderLibs(const gl::Context *context, - gl::InfoLog &infoLog); + gl::InfoLog &infoLog, + gl::ScopedShaderLinkLocks *shaderLocks); mtl::BufferPool *getBufferPool(ContextMtl *context); diff --git a/src/libANGLE/renderer/metal/ProgramMtl.mm b/src/libANGLE/renderer/metal/ProgramMtl.mm index 1476a1993..a1774c940 100644 --- a/src/libANGLE/renderer/metal/ProgramMtl.mm +++ b/src/libANGLE/renderer/metal/ProgramMtl.mm @@ -625,7 +625,9 @@ std::unique_ptr ProgramMtl::load(const gl::Context *context, ANGLE_PARALLEL_LINK_TRY(loadDefaultUniformBlocksInfo(context, stream)); ANGLE_PARALLEL_LINK_TRY(loadInterfaceBlockInfo(context, stream)); - return compileMslShaderLibs(context, infoLog); + // Shaders are unused + gl::ScopedShaderLinkLocks noOpShaderLocks; + return compileMslShaderLibs(context, infoLog, &noOpShaderLocks); } void ProgramMtl::save(const gl::Context *context, gl::BinaryOutputStream *stream) @@ -646,7 +648,8 @@ void ProgramMtl::setSeparable(bool separable) std::unique_ptr ProgramMtl::link(const gl::Context *context, const gl::ProgramLinkedResources &resources, gl::InfoLog &infoLog, - const gl::ProgramMergedVaryings &mergedVaryings) + const gl::ProgramMergedVaryings &mergedVaryings, + gl::ScopedShaderLinkLocks *shaderLocks) { ContextMtl *contextMtl = mtl::GetImpl(context); @@ -666,7 +669,7 @@ std::unique_ptr ProgramMtl::link(const gl::Context *context, mState.getExecutable().getTransformFeedbackBufferCount())); mMslXfbOnlyVertexShaderInfo = mMslShaderTranslateInfo[gl::ShaderType::Vertex]; - return compileMslShaderLibs(context, infoLog); + return compileMslShaderLibs(context, infoLog, shaderLocks); } void ProgramMtl::linkUpdateHasFlatAttributes(const gl::Context *context) @@ -700,14 +703,18 @@ class ProgramMtl::CompileMslTask final : public angle::Closure public: CompileMslTask(ContextMtl *context, mtl::TranslatedShaderInfo *translatedMslInfo, - const std::map &substitutionMacros) + const std::map &substitutionMacros, + gl::ScopedShaderLinkLock &&shaderLock) : mContext(context), mTranslatedMslInfo(translatedMslInfo), - mSubstitutionMacros(substitutionMacros) + mSubstitutionMacros(substitutionMacros), + mShaderLock(std::move(shaderLock)) {} void operator()() override { + gl::ScopedShaderLinkLock unlockAtEnd(std::move(mShaderLock)); + mResult = CreateMslShaderLib(mContext, mInfoLog, mTranslatedMslInfo, mSubstitutionMacros); } @@ -726,6 +733,7 @@ class ProgramMtl::CompileMslTask final : public angle::Closure gl::InfoLog mInfoLog; mtl::TranslatedShaderInfo *mTranslatedMslInfo; std::map mSubstitutionMacros; + gl::ScopedShaderLinkLock mShaderLock; angle::Result mResult = angle::Result::Continue; }; @@ -767,7 +775,8 @@ class ProgramMtl::ProgramLinkEvent final : public LinkEvent }; std::unique_ptr ProgramMtl::compileMslShaderLibs(const gl::Context *context, - gl::InfoLog &infoLog) + gl::InfoLog &infoLog, + gl::ScopedShaderLinkLocks *shaderLocks) { ANGLE_TRACE_EVENT0("gpu.angle", "ProgramMtl::compileMslShaderLibs"); @@ -792,12 +801,13 @@ std::unique_ptr ProgramMtl::compileMslShaderLibs(const gl::Context *c { if (asyncCompile) { - auto task = - std::make_shared(contextMtl, translateInfo, macros); + auto task = std::make_shared( + contextMtl, translateInfo, macros, std::move((*shaderLocks)[shaderType])); asyncTasks.push_back(task); } else { + // Ignore |shaderLocks|, the caller will unlock them. ANGLE_PARALLEL_LINK_TRY( CreateMslShaderLib(contextMtl, infoLog, translateInfo, macros)); } diff --git a/src/libANGLE/renderer/null/ProgramNULL.cpp b/src/libANGLE/renderer/null/ProgramNULL.cpp index 2d5b81a9b..f6870a98a 100644 --- a/src/libANGLE/renderer/null/ProgramNULL.cpp +++ b/src/libANGLE/renderer/null/ProgramNULL.cpp @@ -34,7 +34,8 @@ void ProgramNULL::setSeparable(bool separable) {} std::unique_ptr ProgramNULL::link(const gl::Context *contextImpl, const gl::ProgramLinkedResources &resources, gl::InfoLog &infoLog, - const gl::ProgramMergedVaryings & /*mergedVaryings*/) + const gl::ProgramMergedVaryings & /*mergedVaryings*/, + gl::ScopedShaderLinkLocks *shaderLocks) { return std::make_unique(angle::Result::Continue); } diff --git a/src/libANGLE/renderer/null/ProgramNULL.h b/src/libANGLE/renderer/null/ProgramNULL.h index f37a1e063..51555e6a5 100644 --- a/src/libANGLE/renderer/null/ProgramNULL.h +++ b/src/libANGLE/renderer/null/ProgramNULL.h @@ -31,7 +31,8 @@ class ProgramNULL : public ProgramImpl std::unique_ptr link(const gl::Context *context, const gl::ProgramLinkedResources &resources, gl::InfoLog &infoLog, - const gl::ProgramMergedVaryings &mergedVaryings) override; + const gl::ProgramMergedVaryings &mergedVaryings, + gl::ScopedShaderLinkLocks *shaderLocks) override; GLboolean validate(const gl::Caps &caps, gl::InfoLog *infoLog) override; void setUniform1fv(GLint location, GLsizei count, const GLfloat *v) override; diff --git a/src/libANGLE/renderer/vulkan/ProgramVk.cpp b/src/libANGLE/renderer/vulkan/ProgramVk.cpp index 0223c3da0..52d07469e 100644 --- a/src/libANGLE/renderer/vulkan/ProgramVk.cpp +++ b/src/libANGLE/renderer/vulkan/ProgramVk.cpp @@ -29,6 +29,7 @@ class LinkTask final : public vk::Context, public angle::Closure const gl::ProgramState &state, const gl::ProgramExecutable &glExecutable, ProgramExecutableVk *executable, + gl::ScopedShaderLinkLocks *shaderLocks, bool isGLES1, vk::PipelineRobustness pipelineRobustness, vk::PipelineProtectedAccess pipelineProtectedAccess) @@ -39,7 +40,9 @@ class LinkTask final : public vk::Context, public angle::Closure mIsGLES1(isGLES1), mPipelineRobustness(pipelineRobustness), mPipelineProtectedAccess(pipelineProtectedAccess) - {} + { + mShaderLocks.swap(*shaderLocks); + } void operator()() override; @@ -83,6 +86,7 @@ class LinkTask final : public vk::Context, public angle::Closure const gl::ProgramState &mState; const gl::ProgramExecutable &mGlExecutable; ProgramExecutableVk *mExecutable; + gl::ScopedShaderLinkLocks mShaderLocks; bool mIsGLES1; vk::PipelineRobustness mPipelineRobustness; vk::PipelineProtectedAccess mPipelineProtectedAccess; @@ -101,6 +105,10 @@ void LinkTask::operator()() { ANGLE_TRACE_EVENT0("gpu.angle", "ProgramVk::LinkTask::run"); + // Unlock the shaders at the end of the task. + gl::ScopedShaderLinkLocks unlockAtEnd; + unlockAtEnd.swap(mShaderLocks); + // Warm up the pipeline cache by creating a few placeholder pipelines. This is not done for // separable programs, and is deferred to when the program pipeline is finalized. // @@ -309,7 +317,8 @@ void ProgramVk::setSeparable(bool separable) std::unique_ptr ProgramVk::link(const gl::Context *context, const gl::ProgramLinkedResources &resources, gl::InfoLog &infoLog, - const gl::ProgramMergedVaryings &mergedVaryings) + const gl::ProgramMergedVaryings &mergedVaryings, + gl::ScopedShaderLinkLocks *shaderLocks) { ANGLE_TRACE_EVENT0("gpu.angle", "ProgramVk::link"); @@ -358,7 +367,7 @@ std::unique_ptr ProgramVk::link(const gl::Context *context, } std::shared_ptr linkTask = std::make_shared( - contextVk->getRenderer(), mState, programExecutable, &mExecutable, + contextVk->getRenderer(), mState, programExecutable, &mExecutable, shaderLocks, context->getState().isGLES1(), contextVk->pipelineRobustness(), contextVk->pipelineProtectedAccess()); return std::make_unique(context->getShaderCompileThreadPool(), linkTask); diff --git a/src/libANGLE/renderer/vulkan/ProgramVk.h b/src/libANGLE/renderer/vulkan/ProgramVk.h index 85747060e..25906770a 100644 --- a/src/libANGLE/renderer/vulkan/ProgramVk.h +++ b/src/libANGLE/renderer/vulkan/ProgramVk.h @@ -39,7 +39,8 @@ class ProgramVk : public ProgramImpl std::unique_ptr link(const gl::Context *context, const gl::ProgramLinkedResources &resources, gl::InfoLog &infoLog, - const gl::ProgramMergedVaryings &mergedVaryings) override; + const gl::ProgramMergedVaryings &mergedVaryings, + gl::ScopedShaderLinkLocks *shaderLocks) override; GLboolean validate(const gl::Caps &caps, gl::InfoLog *infoLog) override; angle::Result syncState(const gl::Context *context,