diff --git a/include/platform/autogen/FrontendFeatures_autogen.h b/include/platform/autogen/FrontendFeatures_autogen.h index e53944460..0f58c2dc3 100644 --- a/include/platform/autogen/FrontendFeatures_autogen.h +++ b/include/platform/autogen/FrontendFeatures_autogen.h @@ -161,6 +161,13 @@ struct FrontendFeatures : FeatureSetBase &members, "http://anglebug.com/8280" }; + FeatureInfo linkJobIsNotThreadSafe = { + "linkJobIsNotThreadSafe", + FeatureCategory::FrontendWorkarounds, + "If true, parts of the link job cannot be parallelized", + &members, "http://anglebug.com/8297" + }; + }; inline FrontendFeatures::FrontendFeatures() = default; diff --git a/include/platform/frontend_features.json b/include/platform/frontend_features.json index 3ad6a904d..2626918fc 100644 --- a/include/platform/frontend_features.json +++ b/include/platform/frontend_features.json @@ -161,6 +161,14 @@ "Check the filesystem for translated shaders to use instead of the shader translator's" ], "issue": "http://anglebug.com/8280" + }, + { + "name": "link_job_is_not_thread_safe", + "category": "Workarounds", + "description": [ + "If true, parts of the link job cannot be parallelized" + ], + "issue": "http://anglebug.com/8297" } ] } diff --git a/src/common/CompiledShaderState.cpp b/src/common/CompiledShaderState.cpp index 88e7dda9b..fa2f56acf 100644 --- a/src/common/CompiledShaderState.cpp +++ b/src/common/CompiledShaderState.cpp @@ -182,6 +182,7 @@ void LoadShInterfaceBlock(gl::BinaryInputStream *stream, sh::InterfaceBlock *blo CompiledShaderState::CompiledShaderState(gl::ShaderType type) : shaderType(type), + successfullyCompiled(false), shaderVersion(100), hasClipDistance(false), hasDiscard(false), diff --git a/src/common/CompiledShaderState.h b/src/common/CompiledShaderState.h index b5abaa320..f835e50dd 100644 --- a/src/common/CompiledShaderState.h +++ b/src/common/CompiledShaderState.h @@ -54,6 +54,8 @@ struct CompiledShaderState const gl::ShaderType shaderType; + bool successfullyCompiled; + int shaderVersion; std::string translatedSource; sh::BinaryBlob compiledBinary; diff --git a/src/libANGLE/Context.cpp b/src/libANGLE/Context.cpp index 305cb1325..0e0897b8c 100644 --- a/src/libANGLE/Context.cpp +++ b/src/libANGLE/Context.cpp @@ -9374,6 +9374,11 @@ std::shared_ptr Context::getShaderCompileThreadPool() c return mDisplay->getSingleThreadPool(); } +std::shared_ptr Context::getSingleThreadPool() const +{ + return mDisplay->getSingleThreadPool(); +} + std::shared_ptr Context::getWorkerThreadPool() const { return mDisplay->getMultiThreadPool(); diff --git a/src/libANGLE/Context.h b/src/libANGLE/Context.h index 9d0656b14..b8d006990 100644 --- a/src/libANGLE/Context.h +++ b/src/libANGLE/Context.h @@ -700,6 +700,9 @@ class Context final : public egl::LabeledObject, angle::NonCopyable, public angl // GL_KHR_parallel_shader_compile std::shared_ptr getShaderCompileThreadPool() const; + // Single-threaded pool; runs everything instantly + std::shared_ptr getSingleThreadPool() const; + // Generic multithread pool. std::shared_ptr getWorkerThreadPool() const; diff --git a/src/libANGLE/Program.cpp b/src/libANGLE/Program.cpp index f6a4fcc7f..97fc5e546 100644 --- a/src/libANGLE/Program.cpp +++ b/src/libANGLE/Program.cpp @@ -35,6 +35,7 @@ #include "libANGLE/renderer/ContextImpl.h" #include "libANGLE/renderer/GLImplFactory.h" #include "libANGLE/renderer/ProgramImpl.h" +#include "libANGLE/trace.h" #include "platform/PlatformMethods.h" #include "platform/autogen/FrontendFeatures_autogen.h" @@ -372,6 +373,34 @@ const std::string GetResourceName(const T &resource) return resourceName; } + +// 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 angle::Result wait(const gl::Context *context) = 0; + // Peeks whether the linking is still ongoing. + virtual bool isLinking() = 0; +}; + +// Wraps an already done linking. +class LinkEventDone final : public LinkEvent +{ + public: + LinkEventDone(angle::Result result) : mResult(result) {} + angle::Result wait(const gl::Context *context) override { return mResult; } + bool isLinking() override { return false; } + + private: + angle::Result mResult; +}; } // anonymous namespace const char *GetLinkMismatchErrorString(LinkMismatchError linkError) @@ -515,7 +544,7 @@ struct Program::LinkingState LinkingVariables linkingVariables; ProgramLinkedResources resources; egl::BlobCache::Key programHash; - std::unique_ptr linkEvent; + std::unique_ptr linkEvent; bool linkingFromBinary; }; @@ -976,6 +1005,182 @@ ShaderType ProgramState::getAttachedTransformFeedbackStage() const return ShaderType::Vertex; } +// The common portion of parallel link and load jobs +class Program::MainLinkLoadTask : public angle::Closure +{ + public: + MainLinkLoadTask(const std::shared_ptr &subTaskWorkerPool, + ProgramState *state, + std::shared_ptr &&linkTask) + : mSubTaskWorkerPool(subTaskWorkerPool), mState(*state), mLinkTask(std::move(linkTask)) + { + ASSERT(subTaskWorkerPool.get()); + } + ~MainLinkLoadTask() override = default; + + angle::Result getResult(const Context *context) + { + InfoLog &infoLog = mState.getExecutable().getInfoLog(); + + ANGLE_TRY(mResult); + ANGLE_TRY(mLinkTask->getResult(context, infoLog)); + + for (const std::shared_ptr &task : mSubTasks) + { + ANGLE_TRY(task->getResult(context, infoLog)); + } + + return angle::Result::Continue; + } + + void waitSubTasks() { angle::WaitableEvent::WaitMany(&mSubTaskWaitableEvents); } + + bool areSubTasksLinking() + { + return !mLinkTask->isLinkingInternally() && + !angle::WaitableEvent::AllReady(&mSubTaskWaitableEvents); + } + + protected: + void scheduleSubTasks(const std::vector> &subTasks) + { + mSubTaskWaitableEvents.reserve(subTasks.size()); + for (const std::shared_ptr &subTask : subTasks) + { + mSubTaskWaitableEvents.push_back(mSubTaskWorkerPool->postWorkerTask(subTask)); + } + } + + std::shared_ptr mSubTaskWorkerPool; + ProgramState &mState; + std::shared_ptr mLinkTask; + + // Subtask wait events + std::vector> mSubTasks; + std::vector> mSubTaskWaitableEvents; + + // The result of the front-end portion of the link. The backend's result is retrieved via + // mLinkTask->getResult(). The subtask results are retrieved via mSubTasks similarly. + angle::Result mResult; +}; + +class Program::MainLinkTask final : public Program::MainLinkLoadTask +{ + public: + MainLinkTask(const std::shared_ptr &subTaskWorkerPool, + const Caps &caps, + const Limitations &limitations, + const Version &clientVersion, + bool isWebGL, + Program *program, + ProgramState *state, + LinkingVariables *linkingVariables, + ProgramLinkedResources *resources, + std::shared_ptr &&linkTask) + : MainLinkLoadTask(subTaskWorkerPool, state, std::move(linkTask)), + mCaps(caps), + mLimitations(limitations), + mClientVersion(clientVersion), + mIsWebGL(isWebGL), + mProgram(program), + mLinkingVariables(linkingVariables), + mResources(resources) + {} + ~MainLinkTask() override = default; + + void operator()() override { mResult = linkImpl(); } + + private: + angle::Result linkImpl(); + + // State needed for link + const Caps &mCaps; + const Limitations &mLimitations; + const Version mClientVersion; + const bool mIsWebGL; + Program *mProgram; + LinkingVariables *mLinkingVariables; + ProgramLinkedResources *mResources; +}; + +class Program::MainLoadTask final : public Program::MainLinkLoadTask +{ + public: + MainLoadTask(const std::shared_ptr &subTaskWorkerPool, + Program *program, + ProgramState *state, + std::shared_ptr &&loadTask) + : MainLinkLoadTask(subTaskWorkerPool, state, std::move(loadTask)) + {} + ~MainLoadTask() override = default; + + void operator()() override { mResult = loadImpl(); } + + private: + angle::Result loadImpl(); +}; + +class Program::MainLinkLoadEvent final : public LinkEvent +{ + public: + MainLinkLoadEvent(const std::shared_ptr &linkTask, + const std::shared_ptr &waitEvent) + : mLinkTask(linkTask), mWaitableEvent(waitEvent) + {} + ~MainLinkLoadEvent() override {} + + angle::Result wait(const gl::Context *context) override + { + ANGLE_TRACE_EVENT0("gpu.angle", "Program::MainLinkLoadEvent::wait"); + + mWaitableEvent->wait(); + mLinkTask->waitSubTasks(); + + return mLinkTask->getResult(context); + } + bool isLinking() override + { + return !mWaitableEvent->isReady() || mLinkTask->areSubTasksLinking(); + } + + private: + std::shared_ptr mLinkTask; + std::shared_ptr mWaitableEvent; +}; + +angle::Result Program::MainLinkTask::linkImpl() +{ + ProgramMergedVaryings mergedVaryings; + + // Do the front-end portion of the link. + ANGLE_TRY(mProgram->linkJobImpl(mCaps, mLimitations, mClientVersion, mIsWebGL, + mLinkingVariables, mResources, &mergedVaryings)); + + // Next, do the backend portion of the link. If there are any subtasks to be scheduled, they + // are collected now. + std::vector> subTasks = + mLinkTask->link(*mResources, mergedVaryings); + + // Must be after backend's link to avoid misleading the linker about input/output variables. + mState.updateProgramInterfaceInputs(); + mState.updateProgramInterfaceOutputs(); + + // Schedule the subtasks + scheduleSubTasks(subTasks); + + return angle::Result::Continue; +} + +angle::Result Program::MainLoadTask::loadImpl() +{ + std::vector> subTasks = mLinkTask->load(); + + // Schedule the subtasks + scheduleSubTasks(subTasks); + + return angle::Result::Continue; +} + Program::Program(rx::GLImplFactory *factory, ShaderProgramManager *manager, ShaderProgramID handle) : mSerial(factory->generateSerial()), mState(factory), @@ -1111,6 +1316,8 @@ void Program::bindFragmentOutputIndex(GLuint index, const char *name) void Program::makeNewExecutable(const Context *context) { + ASSERT(!mLinkingState); + // Unlink the program, but do not clear the validation-related caching yet, since we can still // use the previously linked program if linking the shaders fails. mLinked = false; @@ -1119,7 +1326,7 @@ void Program::makeNewExecutable(const Context *context) // By default, set the link event as failing. If link succeeds, it will be replaced by the // appropriate event. - mLinkingState->linkEvent = std::make_unique(angle::Result::Stop); + mLinkingState->linkEvent = std::make_unique(angle::Result::Stop); InstallExecutable( context, @@ -1191,7 +1398,7 @@ angle::Result Program::linkImpl(const Context *context) mState.mInfoLog.reset(); // Validate we have properly attached shaders before checking the cache. - if (!linkValidateShaders(context)) + if (!linkValidateShaders()) { return angle::Result::Continue; } @@ -1224,41 +1431,76 @@ angle::Result Program::linkImpl(const Context *context) const Version &clientVersion = context->getClientVersion(); const bool isWebGL = context->isWebGL(); + // Ask the backend to prepare the link task. + std::shared_ptr linkTask; + ANGLE_TRY(mProgram->link(context, &linkTask)); + + std::unique_ptr linkingState = std::make_unique(); + + // Prepare the main link job + std::shared_ptr mainLinkTask(new MainLinkTask( + context->getShaderCompileThreadPool(), caps, limitations, clientVersion, isWebGL, this, + &mState, &linkingState->linkingVariables, &linkingState->resources, std::move(linkTask))); + + // While the subtasks are currently always thread-safe, the main task is not safe on all + // backends. A front-end feature selects whether the single-threaded pool must be used. + std::shared_ptr mainLinkWorkerPool = + context->getFrontendFeatures().linkJobIsNotThreadSafe.enabled + ? context->getSingleThreadPool() + : context->getShaderCompileThreadPool(); + + // TODO: add the possibility to perform this in an unlocked tail call. http://anglebug.com/8297 + std::shared_ptr mainLinkEvent = + mainLinkWorkerPool->postWorkerTask(mainLinkTask); + + mLinkingState = std::move(linkingState); + mLinkingState->linkingFromBinary = false; + mLinkingState->programHash = programHash; + mLinkingState->linkEvent = std::make_unique(mainLinkTask, mainLinkEvent); + + return angle::Result::Continue; +} + +angle::Result Program::linkJobImpl(const Caps &caps, + const Limitations &limitations, + const Version &clientVersion, + bool isWebGL, + LinkingVariables *linkingVariables, + ProgramLinkedResources *resources, + ProgramMergedVaryings *mergedVaryingsOut) +{ // Cache load failed, fall through to normal linking. unlink(); // Re-link shaders after the unlink call. linkShaders(); - ProgramMergedVaryings mergedVaryings; - LinkingVariables &linkingVariables = mLinkingState->linkingVariables; - ProgramLinkedResources &resources = mLinkingState->resources; - - linkingVariables.initForProgram(mState); - resources.init(&mState.mExecutable->mUniformBlocks, &mState.mExecutable->mUniforms, - &mState.mExecutable->mUniformNames, &mState.mExecutable->mUniformMappedNames, - &mState.mExecutable->mShaderStorageBlocks, &mState.mExecutable->mBufferVariables, - &mState.mExecutable->mAtomicCounterBuffers); + linkingVariables->initForProgram(mState); + resources->init(&mState.mExecutable->mUniformBlocks, &mState.mExecutable->mUniforms, + &mState.mExecutable->mUniformNames, &mState.mExecutable->mUniformMappedNames, + &mState.mExecutable->mShaderStorageBlocks, + &mState.mExecutable->mBufferVariables, + &mState.mExecutable->mAtomicCounterBuffers); updateLinkedShaderStages(); - InitUniformBlockLinker(mState, &resources.uniformBlockLinker); - InitShaderStorageBlockLinker(mState, &resources.shaderStorageBlockLinker); + InitUniformBlockLinker(mState, &resources->uniformBlockLinker); + InitShaderStorageBlockLinker(mState, &resources->shaderStorageBlockLinker); if (mState.mAttachedShaders[ShaderType::Compute]) { GLuint combinedImageUniforms = 0; - if (!linkUniforms(caps, clientVersion, &resources.unusedUniforms, &combinedImageUniforms)) + if (!linkUniforms(caps, clientVersion, &resources->unusedUniforms, &combinedImageUniforms)) { - return angle::Result::Continue; + return angle::Result::Stop; } GLuint combinedShaderStorageBlocks = 0u; if (!LinkValidateProgramInterfaceBlocks( caps, clientVersion, isWebGL, mState.mExecutable->getLinkedShaderStages(), - resources, mState.mInfoLog, &combinedShaderStorageBlocks)) + *resources, mState.mInfoLog, &combinedShaderStorageBlocks)) { - return angle::Result::Continue; + return angle::Result::Stop; } // [OpenGL ES 3.1] Chapter 8.22 Page 203: @@ -1274,38 +1516,38 @@ angle::Result Program::linkImpl(const Context *context) "and active fragment shader outputs exceeds " "MAX_COMBINED_SHADER_OUTPUT_RESOURCES (" << caps.maxCombinedShaderOutputResources << ")"; - return angle::Result::Continue; + return angle::Result::Stop; } } else { if (!linkAttributes(caps, limitations, isWebGL)) { - return angle::Result::Continue; + return angle::Result::Stop; } if (!linkVaryings()) { - return angle::Result::Continue; + return angle::Result::Stop; } GLuint combinedImageUniforms = 0; - if (!linkUniforms(caps, clientVersion, &resources.unusedUniforms, &combinedImageUniforms)) + if (!linkUniforms(caps, clientVersion, &resources->unusedUniforms, &combinedImageUniforms)) { - return angle::Result::Continue; + return angle::Result::Stop; } GLuint combinedShaderStorageBlocks = 0u; if (!LinkValidateProgramInterfaceBlocks( caps, clientVersion, isWebGL, mState.mExecutable->getLinkedShaderStages(), - resources, mState.mInfoLog, &combinedShaderStorageBlocks)) + *resources, mState.mInfoLog, &combinedShaderStorageBlocks)) { - return angle::Result::Continue; + return angle::Result::Stop; } - if (!LinkValidateProgramGlobalNames(mState.mInfoLog, getExecutable(), linkingVariables)) + if (!LinkValidateProgramGlobalNames(mState.mInfoLog, getExecutable(), *linkingVariables)) { - return angle::Result::Continue; + return angle::Result::Stop; } const SharedCompiledShaderState &vertexShader = mState.mAttachedShaders[ShaderType::Vertex]; @@ -1325,7 +1567,7 @@ angle::Result Program::linkImpl(const Context *context) fragmentShader->activeOutputVariables, fragmentShader->shaderVersion, mState.mFragmentOutputLocations, mState.mFragmentOutputIndexes)) { - return angle::Result::Continue; + return angle::Result::Stop; } mState.mExecutable->mPODStruct.hasDiscard = fragmentShader->hasDiscard; @@ -1336,25 +1578,17 @@ angle::Result Program::linkImpl(const Context *context) mState.mExecutable->mPODStruct.specConstUsageBits |= fragmentShader->specConstUsageBits; } - mergedVaryings = GetMergedVaryingsFromLinkingVariables(linkingVariables); + *mergedVaryingsOut = GetMergedVaryingsFromLinkingVariables(*linkingVariables); if (!mState.mExecutable->linkMergedVaryings(caps, limitations, clientVersion, isWebGL, - mergedVaryings, linkingVariables, isSeparable(), - &resources.varyingPacking)) + *mergedVaryingsOut, *linkingVariables, + isSeparable(), &resources->varyingPacking)) { - return angle::Result::Continue; + return angle::Result::Stop; } } mState.mExecutable->saveLinkedStateInfo(mState); - mLinkingState->linkingFromBinary = false; - mLinkingState->programHash = programHash; - mLinkingState->linkEvent = mProgram->link(context, resources, std::move(mergedVaryings)); - - // Must be after mProgram->link() to avoid misleading the linker about output variables. - mState.updateProgramInterfaceInputs(); - mState.updateProgramInterfaceOutputs(); - return angle::Result::Continue; } @@ -1556,8 +1790,36 @@ angle::Result Program::loadBinary(const Context *context, mDirtyBits.set(uniformBlockIndex); } + // If load returns incomplete, we know for sure that the binary is not compatible with the + // backend. The loaded binary could have been read from the on-disk shader cache and be + // corrupted or serialized with different revision and subsystem id than the currently loaded + // backend. Returning 'Incomplete' to the caller results in link happening using the original + // shader sources. + std::shared_ptr loadTask; + angle::Result result = mProgram->load(context, &stream, &loadTask); + if (result == angle::Result::Incomplete) + { + return angle::Result::Incomplete; + } + ANGLE_TRY(result); + + std::unique_ptr loadEvent; + if (loadTask) + { + std::shared_ptr mainLoadTask(new MainLoadTask( + context->getShaderCompileThreadPool(), this, &mState, std::move(loadTask))); + + std::shared_ptr mainLoadEvent = + context->getShaderCompileThreadPool()->postWorkerTask(mainLoadTask); + loadEvent = std::make_unique(mainLoadTask, mainLoadEvent); + } + else + { + loadEvent = std::make_unique(angle::Result::Continue); + } + mLinkingState->linkingFromBinary = true; - mLinkingState->linkEvent = mProgram->load(context, &stream); + mLinkingState->linkEvent = std::move(loadEvent); *successOut = true; @@ -2732,7 +2994,7 @@ GLsizei Program::getTransformFeedbackVaryingMaxLength() const } } -bool Program::linkValidateShaders(const Context *context) +bool Program::linkValidateShaders() { const ShaderMap &shaders = mState.mAttachedShaders; @@ -2754,19 +3016,15 @@ bool Program::linkValidateShaders(const Context *context) Optional version; for (ShaderType shaderType : kAllGraphicsShaderTypes) { - Shader *shaderObj = getAttachedShader(shaderType); - ASSERT(!shaderObj || shaderObj->getType() == shaderType); - const SharedCompiledShaderState &shader = shaders[shaderType]; ASSERT(!shader || shader->shaderType == shaderType); - if (!shaderObj) + if (!shader) { - ASSERT(!shader); continue; } - if (!shaderObj->isCompiled(context)) + if (!shader->successfullyCompiled) { mState.mInfoLog << ShaderTypeToString(shaderType) << " shader is not compiled."; return false; diff --git a/src/libANGLE/Program.h b/src/libANGLE/Program.h index 1d664c0ed..c606f4e8e 100644 --- a/src/libANGLE/Program.h +++ b/src/libANGLE/Program.h @@ -851,7 +851,15 @@ class Program final : public LabeledObject, public angle::Subject } private: + class MainLinkLoadTask; + class MainLoadTask; + class MainLinkTask; + class MainLinkLoadEvent; + friend class ProgramPipeline; + friend class MainLinkLoadTask; + friend class MainLoadTask; + friend class MainLinkTask; struct LinkingState; ~Program() override; @@ -863,10 +871,17 @@ class Program final : public LabeledObject, public angle::Subject void deleteSelf(const Context *context); angle::Result linkImpl(const Context *context); + angle::Result linkJobImpl(const Caps &caps, + const Limitations &limitations, + const Version &clientVersion, + bool isWebGL, + LinkingVariables *linkingVariables, + ProgramLinkedResources *resources, + ProgramMergedVaryings *mergedVaryingsOut); void makeNewExecutable(const Context *context); - bool linkValidateShaders(const Context *context); + bool linkValidateShaders(); void linkShaders(); bool linkAttributes(const Caps &caps, const Limitations &limitations, bool webglCompatibility); bool linkVaryings(); diff --git a/src/libANGLE/Shader.cpp b/src/libANGLE/Shader.cpp index 4dfff796a..8703dcf60 100644 --- a/src/libANGLE/Shader.cpp +++ b/src/libANGLE/Shader.cpp @@ -615,6 +615,8 @@ void Shader::resolveCompile(const Context *context) bool success = mCompilingState->compileEvent->postTranslate(&mInfoLog); mState.mCompileStatus = success ? CompileStatus::COMPILED : CompileStatus::NOT_COMPILED; + mState.mCompiledState->successfullyCompiled = success; + MemoryShaderCache *shaderCache = context->getMemoryShaderCache(); if (success && shaderCache) { @@ -772,7 +774,8 @@ angle::Result Shader::loadBinaryImpl(const Context *context, // Only successfully-compiled shaders are serialized. If deserialization is successful, we can // assume the CompileStatus. - mState.mCompileStatus = CompileStatus::COMPILED; + mState.mCompileStatus = CompileStatus::COMPILED; + mState.mCompiledState->successfullyCompiled = true; return angle::Result::Continue; } diff --git a/src/libANGLE/renderer/ProgramImpl.cpp b/src/libANGLE/renderer/ProgramImpl.cpp index 9ede4a882..fb02beebc 100644 --- a/src/libANGLE/renderer/ProgramImpl.cpp +++ b/src/libANGLE/renderer/ProgramImpl.cpp @@ -9,6 +9,22 @@ namespace rx { +std::vector> LinkTask::link( + const gl::ProgramLinkedResources &resources, + const gl::ProgramMergedVaryings &mergedVaryings) +{ + UNREACHABLE(); + return {}; +} +std::vector> LinkTask::load() +{ + UNREACHABLE(); + return {}; +} +bool LinkTask::isLinkingInternally() +{ + return false; +} angle::Result ProgramImpl::onLabelUpdate(const gl::Context *context) { diff --git a/src/libANGLE/renderer/ProgramImpl.h b/src/libANGLE/renderer/ProgramImpl.h index 451ad9b93..3f07753b2 100644 --- a/src/libANGLE/renderer/ProgramImpl.h +++ b/src/libANGLE/renderer/ProgramImpl.h @@ -10,11 +10,13 @@ #define LIBANGLE_RENDERER_PROGRAMIMPL_H_ #include "common/BinaryStream.h" +#include "common/WorkerThread.h" #include "common/angleutils.h" #include "libANGLE/Constants.h" #include "libANGLE/Program.h" #include "libANGLE/Shader.h" +#include #include namespace gl @@ -30,44 +32,47 @@ struct BlockMemberInfo; namespace rx { - -// Provides a mechanism to access the result of asynchronous linking. -class LinkEvent : angle::NonCopyable +// The link job is split as such: +// +// - Front-end link +// - Back-end link +// - Independent back-end link subtasks (typically native driver compile jobs) +// - Post-link finalization +// +// Each step depends on the previous. These steps are executed as such: +// +// 1. Program::link calls into ProgramImpl::link +// - ProgramImpl::link runs whatever needs the Context, such as releasing resources +// - ProgramImpl::link returns a LinkTask +// 2. Program::link implements a closure that calls the front-end link and passes the results to +// the backend's LinkTask. +// 3. The LinkTask potentially returns a set of LinkSubTasks to be scheduled by the worker pool +// 4. Once the link is resolved, the post-link finalization is run +// +// In the above, steps 1 and 4 are done under the share group lock. Steps 2 and 3 can be done in +// threads or without holding the share group lock if the backend supports it. +class LinkSubTask : public angle::Closure { 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 angle::Result wait(const gl::Context *context) = 0; - // Peeks whether the linking is still ongoing. - virtual bool isLinking() = 0; + ~LinkSubTask() override = default; + virtual angle::Result getResult(const gl::Context *context, gl::InfoLog &infoLog) = 0; }; - -// Wraps an already done linking. -class LinkEventDone final : public LinkEvent +class LinkTask { public: - LinkEventDone(angle::Result result) : mResult(result) {} - angle::Result wait(const gl::Context *context) override; - bool isLinking() override; + virtual ~LinkTask() = default; + // Used for link() + virtual std::vector> link( + const gl::ProgramLinkedResources &resources, + const gl::ProgramMergedVaryings &mergedVaryings); + // Used for load() + virtual std::vector> load(); + virtual angle::Result getResult(const gl::Context *context, gl::InfoLog &infoLog) = 0; - private: - angle::Result mResult; + // Used by the GL backend to query whether the driver is linking in parallel internally. + virtual bool isLinkingInternally(); }; -inline angle::Result LinkEventDone::wait(const gl::Context *context) -{ - return mResult; -} -inline bool LinkEventDone::isLinking() -{ - return false; -} - class ProgramImpl : angle::NonCopyable { public: @@ -75,17 +80,17 @@ class ProgramImpl : angle::NonCopyable virtual ~ProgramImpl() {} virtual void destroy(const gl::Context *context) {} - virtual std::unique_ptr load(const gl::Context *context, - gl::BinaryInputStream *stream) = 0; + virtual angle::Result load(const gl::Context *context, + gl::BinaryInputStream *stream, + std::shared_ptr *loadTaskOut) = 0; virtual void save(const gl::Context *context, gl::BinaryOutputStream *stream) = 0; virtual void setBinaryRetrievableHint(bool retrievable) = 0; virtual void setSeparable(bool separable) = 0; virtual void prepareForLink(const gl::ShaderMap &shaders) {} - virtual std::unique_ptr link(const gl::Context *context, - const gl::ProgramLinkedResources &resources, - gl::ProgramMergedVaryings &&mergedVaryings) = 0; - virtual GLboolean validate(const gl::Caps &caps) = 0; + virtual angle::Result link(const gl::Context *context, + std::shared_ptr *linkTaskOut) = 0; + virtual GLboolean validate(const gl::Caps &caps) = 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 fb698f726..f826d67f0 100644 --- a/src/libANGLE/renderer/d3d/ProgramD3D.cpp +++ b/src/libANGLE/renderer/d3d/ProgramD3D.cpp @@ -92,6 +92,86 @@ class HLSLBlockLayoutEncoderFactory : public gl::CustomBlockLayoutEncoderFactory return new sh::HLSLBlockEncoder(sh::HLSLBlockEncoder::ENCODE_PACKED, false); } }; + +// GetExecutableTask class +class GetExecutableTask : public LinkSubTask, public d3d::Context +{ + public: + GetExecutableTask(ProgramD3D *program, const SharedCompiledShaderStateD3D &shader) + : mProgram(program), mExecutable(program->getExecutable()), mShader(shader) + {} + ~GetExecutableTask() override = default; + + angle::Result getResult(const gl::Context *context, gl::InfoLog &infoLog) override + { + ASSERT((mResult == angle::Result::Continue) == (mStoredHR == S_OK)); + + ANGLE_TRY(checkTask(context, infoLog)); + + // Append debug info + if (mShader) + { + mShader->appendDebugInfo(mShaderExecutable->getDebugInfo()); + } + + return angle::Result::Continue; + } + + void handleResult(HRESULT hr, + const char *message, + const char *file, + const char *function, + unsigned int line) override + { + mStoredHR = hr; + mStoredMessage = message; + mStoredFile = file; + mStoredFunction = function; + mStoredLine = line; + } + + protected: + void popError(d3d::Context *context) + { + ASSERT(mStoredFile); + ASSERT(mStoredFunction); + context->handleResult(mStoredHR, mStoredMessage.c_str(), mStoredFile, mStoredFunction, + mStoredLine); + } + + angle::Result checkTask(const gl::Context *context, gl::InfoLog &infoLog) + { + // Forward any logs + if (!mInfoLog.empty()) + { + infoLog << mInfoLog.str(); + } + + // Forward any errors + if (mResult != angle::Result::Continue) + { + ContextD3D *contextD3D = GetImplAs(context); + popError(contextD3D); + return angle::Result::Stop; + } + + return angle::Result::Continue; + } + + ProgramD3D *mProgram = nullptr; + ProgramExecutableD3D *mExecutable = nullptr; + angle::Result mResult = angle::Result::Continue; + gl::InfoLog mInfoLog; + ShaderExecutableD3D *mShaderExecutable = nullptr; + SharedCompiledShaderStateD3D mShader; + + // Error handling + HRESULT mStoredHR = S_OK; + std::string mStoredMessage; + const char *mStoredFile = nullptr; + const char *mStoredFunction = nullptr; + unsigned int mStoredLine = 0; +}; } // anonymous namespace // ProgramD3DMetadata Implementation @@ -256,21 +336,117 @@ uint8_t ProgramD3DMetadata::getCullDistanceArraySize() const return shader ? shader->cullDistanceSize : 0; } -// ProgramD3D::GetExecutableTask class -class ProgramD3D::GetExecutableTask : public Closure, public d3d::Context +// ProgramD3D Implementation + +class ProgramD3D::GetVertexExecutableTask : public GetExecutableTask { public: - GetExecutableTask(ProgramD3D *program, ProgramExecutableD3D *executable) - : mProgram(program), mExecutable(executable) + GetVertexExecutableTask(ProgramD3D *program, const SharedCompiledShaderStateD3D &shader) + : GetExecutableTask(program, shader) {} + ~GetVertexExecutableTask() override = default; - virtual angle::Result run() = 0; + void operator()() override + { + ANGLE_TRACE_EVENT0("gpu.angle", "GetVertexExecutableTask::run"); - void operator()() override { mResult = run(); } + mExecutable->updateCachedImage2DBindLayoutFromShader(gl::ShaderType::Vertex); + mResult = mExecutable->getVertexExecutableForCachedInputLayout( + this, mProgram->mRenderer, &mShaderExecutable, &mInfoLog); + } +}; - angle::Result getResult() const { return mResult; } - const gl::InfoLog &getInfoLog() const { return mInfoLog; } - ShaderExecutableD3D *getExecutable() { return mShaderExecutable; } +class ProgramD3D::GetPixelExecutableTask : public GetExecutableTask +{ + public: + GetPixelExecutableTask(ProgramD3D *program, const SharedCompiledShaderStateD3D &shader) + : GetExecutableTask(program, shader) + {} + ~GetPixelExecutableTask() override = default; + + void operator()() override + { + ANGLE_TRACE_EVENT0("gpu.angle", "GetPixelExecutableTask::run"); + if (!mShader) + { + return; + } + + mExecutable->updateCachedOutputLayoutFromShader(); + mExecutable->updateCachedImage2DBindLayoutFromShader(gl::ShaderType::Fragment); + mResult = mExecutable->getPixelExecutableForCachedOutputLayout( + this, mProgram->mRenderer, &mShaderExecutable, &mInfoLog); + } +}; + +class ProgramD3D::GetGeometryExecutableTask : public GetExecutableTask +{ + public: + GetGeometryExecutableTask(ProgramD3D *program, + const SharedCompiledShaderStateD3D &shader, + const gl::Caps &caps, + gl::ProvokingVertexConvention provokingVertex) + : GetExecutableTask(program, shader), mCaps(caps), mProvokingVertex(provokingVertex) + {} + ~GetGeometryExecutableTask() override = default; + + void operator()() override + { + ANGLE_TRACE_EVENT0("gpu.angle", "GetGeometryExecutableTask::run"); + // Auto-generate the geometry shader here, if we expect to be using point rendering in + // D3D11. + if (mExecutable->usesGeometryShader(mProgram->mRenderer, mProvokingVertex, + gl::PrimitiveMode::Points)) + { + mResult = mExecutable->getGeometryExecutableForPrimitiveType( + this, mProgram->mRenderer, mCaps, mProvokingVertex, gl::PrimitiveMode::Points, + &mShaderExecutable, &mInfoLog); + } + } + + private: + const gl::Caps &mCaps; + gl::ProvokingVertexConvention mProvokingVertex; +}; + +class ProgramD3D::GetComputeExecutableTask : public GetExecutableTask +{ + public: + GetComputeExecutableTask(ProgramD3D *program, const SharedCompiledShaderStateD3D &shader) + : GetExecutableTask(program, shader) + {} + ~GetComputeExecutableTask() override = default; + + void operator()() override + { + ANGLE_TRACE_EVENT0("gpu.angle", "GetComputeExecutableTask::run"); + + mExecutable->updateCachedImage2DBindLayoutFromShader(gl::ShaderType::Compute); + mResult = mExecutable->getComputeExecutableForImage2DBindLayout( + this, mProgram->mRenderer, &mShaderExecutable, &mInfoLog); + } +}; + +class ProgramD3D::LinkLoadTaskD3D : public d3d::Context, public LinkTask +{ + public: + LinkLoadTaskD3D(ProgramD3D *program) : mProgram(program), mExecutable(program->getExecutable()) + { + ASSERT(mProgram); + } + ~LinkLoadTaskD3D() override = default; + + angle::Result getResult(const gl::Context *context, gl::InfoLog &infoLog) override + { + if (mStoredHR != S_OK) + { + GetImplAs(context)->handleResult(mStoredHR, mStoredMessage.c_str(), + mStoredFile, mStoredFunction, mStoredLine); + return angle::Result::Stop; + } + + return angle::Result::Continue; + } void handleResult(HRESULT hr, const char *message, @@ -285,28 +461,115 @@ class ProgramD3D::GetExecutableTask : public Closure, public d3d::Context mStoredLine = line; } - void popError(d3d::Context *context) - { - ASSERT(mStoredFile); - ASSERT(mStoredFunction); - context->handleResult(mStoredHR, mStoredMessage.c_str(), mStoredFile, mStoredFunction, - mStoredLine); - } - protected: - ProgramD3D *mProgram = nullptr; + // The front-end ensures that the program is not accessed while linking, so it is safe to + // direclty access the state from a potentially parallel job. + ProgramD3D *mProgram; ProgramExecutableD3D *mExecutable = nullptr; - angle::Result mResult = angle::Result::Continue; - gl::InfoLog mInfoLog; - ShaderExecutableD3D *mShaderExecutable = nullptr; - HRESULT mStoredHR = S_OK; + + // Error handling + HRESULT mStoredHR = S_OK; std::string mStoredMessage; const char *mStoredFile = nullptr; const char *mStoredFunction = nullptr; unsigned int mStoredLine = 0; }; -// ProgramD3D Implementation +class ProgramD3D::LinkTaskD3D final : public LinkLoadTaskD3D +{ + public: + LinkTaskD3D(const gl::Version &clientVersion, + const gl::Caps &caps, + EGLenum clientType, + ProgramD3D *program, + gl::ProvokingVertexConvention provokingVertex) + : LinkLoadTaskD3D(program), + mClientVersion(clientVersion), + mCaps(caps), + mClientType(clientType), + mProvokingVertex(provokingVertex) + {} + ~LinkTaskD3D() override = default; + + std::vector> link( + const gl::ProgramLinkedResources &resources, + const gl::ProgramMergedVaryings &mergedVaryings) override; + + private: + const gl::Version mClientVersion; + const gl::Caps &mCaps; + const EGLenum mClientType; + const gl::ProvokingVertexConvention mProvokingVertex; +}; + +std::vector> ProgramD3D::LinkTaskD3D::link( + const gl::ProgramLinkedResources &resources, + const gl::ProgramMergedVaryings &mergedVaryings) +{ + ANGLE_TRACE_EVENT0("gpu.angle", "LinkTaskD3D::link"); + + angle::Result result = + mProgram->linkJobImpl(this, mCaps, mClientVersion, mClientType, resources, mergedVaryings); + ASSERT((result == angle::Result::Continue) == (mStoredHR == S_OK)); + + if (result != angle::Result::Continue) + { + return {}; + } + + // Create the subtasks + std::vector> subTasks; + + if (mExecutable->hasShaderStage(gl::ShaderType::Compute)) + { + subTasks.push_back(std::make_shared( + mProgram, mProgram->getAttachedShader(gl::ShaderType::Compute))); + } + else + { + // 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. + subTasks.push_back(std::make_shared( + mProgram, mProgram->getAttachedShader(gl::ShaderType::Vertex))); + subTasks.push_back(std::make_shared( + mProgram, mProgram->getAttachedShader(gl::ShaderType::Fragment))); + subTasks.push_back(std::make_shared( + mProgram, mProgram->getAttachedShader(gl::ShaderType::Vertex), mCaps, + mProvokingVertex)); + } + + return subTasks; +} + +class ProgramD3D::LoadTaskD3D final : public LinkLoadTaskD3D +{ + public: + LoadTaskD3D(ProgramD3D *program, angle::MemoryBuffer &&streamData) + : LinkLoadTaskD3D(program), mStreamData(std::move(streamData)) + {} + ~LoadTaskD3D() override = default; + + std::vector> load() override + { + ANGLE_TRACE_EVENT0("gpu.angle", "LoadTaskD3D::load"); + + gl::BinaryInputStream stream(mStreamData.data(), mStreamData.size()); + mResult = mExecutable->loadBinaryShaderExecutables(this, mProgram->mRenderer, &stream); + + return {}; + } + + angle::Result getResult(const gl::Context *context, gl::InfoLog &infoLog) override + { + ANGLE_TRY(LinkLoadTaskD3D::getResult(context, infoLog)); + return mResult; + } + + private: + angle::MemoryBuffer mStreamData; + angle::Result mResult = angle::Result::Continue; +}; ProgramD3D::ProgramD3D(const gl::ProgramState &state, RendererD3D *renderer) : ProgramImpl(state), mRenderer(renderer) @@ -316,96 +579,35 @@ ProgramD3D::~ProgramD3D() = default; void ProgramD3D::destroy(const gl::Context *context) { - reset(); + getExecutable()->reset(); } -class ProgramD3D::LoadBinaryTask : public ProgramD3D::GetExecutableTask -{ - public: - LoadBinaryTask(ProgramD3D *program, - ProgramExecutableD3D *executable, - gl::BinaryInputStream *stream, - gl::InfoLog &infoLog) - : ProgramD3D::GetExecutableTask(program, executable) - { - ASSERT(mProgram); - ASSERT(mExecutable); - ASSERT(stream); - - // Copy the remaining data from the stream locally so that the client can't modify it when - // loading off thread. - size_t dataSize = stream->remainingSize(); - mDataCopySucceeded = mStreamData.resize(dataSize); - if (mDataCopySucceeded) - { - memcpy(mStreamData.data(), stream->data() + stream->offset(), dataSize); - } - } - - angle::Result run() override - { - ANGLE_TRACE_EVENT0("gpu.angle", "ProgramD3D::LoadBinaryTask::run"); - if (!mDataCopySucceeded) - { - mInfoLog << "Failed to copy program binary data to local buffer."; - return angle::Result::Incomplete; - } - - gl::BinaryInputStream stream(mStreamData.data(), mStreamData.size()); - return mExecutable->loadBinaryShaderExecutables(this, mProgram->mRenderer, &stream, - mInfoLog); - } - - private: - bool mDataCopySucceeded; - angle::MemoryBuffer mStreamData; -}; - -class ProgramD3D::LoadBinaryLinkEvent final : public LinkEvent -{ - public: - LoadBinaryLinkEvent(std::shared_ptr workerPool, - ProgramD3D *program, - ProgramExecutableD3D *executable, - gl::BinaryInputStream *stream, - gl::InfoLog &infoLog) - : mTask(std::make_shared(program, executable, stream, infoLog)), - mWaitableEvent(workerPool->postWorkerTask(mTask)) - {} - - angle::Result wait(const gl::Context *context) override - { - mWaitableEvent->wait(); - - // Continue and Incomplete are not errors. For Stop, pass the error to the ContextD3D. - if (mTask->getResult() != angle::Result::Stop) - { - return angle::Result::Continue; - } - - ContextD3D *contextD3D = GetImplAs(context); - mTask->popError(contextD3D); - return angle::Result::Stop; - } - - bool isLinking() override { return !mWaitableEvent->isReady(); } - - private: - std::shared_ptr mTask; - std::shared_ptr mWaitableEvent; -}; - -std::unique_ptr ProgramD3D::load(const gl::Context *context, - gl::BinaryInputStream *stream) +angle::Result ProgramD3D::load(const gl::Context *context, + gl::BinaryInputStream *stream, + std::shared_ptr *loadTaskOut) { if (!getExecutable()->load(context, mRenderer, stream)) { - return std::make_unique(angle::Result::Stop); + mState.getExecutable().getInfoLog() + << "Invalid program binary, device configuration has changed."; + return angle::Result::Incomplete; } - return std::make_unique(context->getShaderCompileThreadPool(), this, - getExecutable(), stream, - mState.getExecutable().getInfoLog()); + // Copy the remaining data from the stream locally so that the client can't modify it when + // loading off thread. + angle::MemoryBuffer streamData; + const size_t dataSize = stream->remainingSize(); + if (!streamData.resize(dataSize)) + { + mState.getExecutable().getInfoLog() + << "Failed to copy program binary data to local buffer."; + return angle::Result::Stop; + } + memcpy(streamData.data(), stream->data() + stream->offset(), dataSize); + + // Note: pretty much all the above can also be moved to the task + *loadTaskOut = std::shared_ptr(new LoadTaskD3D(this, std::move(streamData))); + return angle::Result::Continue; } void ProgramD3D::save(const gl::Context *context, gl::BinaryOutputStream *stream) @@ -417,277 +619,6 @@ void ProgramD3D::setBinaryRetrievableHint(bool /* retrievable */) {} void ProgramD3D::setSeparable(bool /* separable */) {} -class ProgramD3D::GetVertexExecutableTask : public ProgramD3D::GetExecutableTask -{ - public: - GetVertexExecutableTask(ProgramD3D *program, ProgramExecutableD3D *executable) - : GetExecutableTask(program, executable) - {} - angle::Result run() override - { - ANGLE_TRACE_EVENT0("gpu.angle", "ProgramD3D::GetVertexExecutableTask::run"); - - mExecutable->updateCachedImage2DBindLayoutFromShader(gl::ShaderType::Vertex); - ANGLE_TRY(mExecutable->getVertexExecutableForCachedInputLayout( - this, mProgram->mRenderer, &mShaderExecutable, &mInfoLog)); - - return angle::Result::Continue; - } -}; - -class ProgramD3D::GetPixelExecutableTask : public ProgramD3D::GetExecutableTask -{ - public: - GetPixelExecutableTask(ProgramD3D *program, ProgramExecutableD3D *executable) - : GetExecutableTask(program, executable) - {} - angle::Result run() override - { - ANGLE_TRACE_EVENT0("gpu.angle", "ProgramD3D::GetPixelExecutableTask::run"); - if (!mProgram->mState.getAttachedShader(gl::ShaderType::Fragment)) - { - return angle::Result::Continue; - } - - mExecutable->updateCachedOutputLayoutFromShader(); - mExecutable->updateCachedImage2DBindLayoutFromShader(gl::ShaderType::Fragment); - ANGLE_TRY(mExecutable->getPixelExecutableForCachedOutputLayout( - this, mProgram->mRenderer, &mShaderExecutable, &mInfoLog)); - - return angle::Result::Continue; - } -}; - -class ProgramD3D::GetGeometryExecutableTask : public ProgramD3D::GetExecutableTask -{ - public: - GetGeometryExecutableTask(ProgramD3D *program, - ProgramExecutableD3D *executable, - const gl::Caps &caps, - gl::ProvokingVertexConvention provokingVertex) - : GetExecutableTask(program, executable), mCaps(caps), mProvokingVertex(provokingVertex) - {} - - angle::Result run() override - { - ANGLE_TRACE_EVENT0("gpu.angle", "ProgramD3D::GetGeometryExecutableTask::run"); - // Auto-generate the geometry shader here, if we expect to be using point rendering in - // D3D11. - if (mExecutable->usesGeometryShader(mProgram->mRenderer, mProvokingVertex, - gl::PrimitiveMode::Points)) - { - ANGLE_TRY(mExecutable->getGeometryExecutableForPrimitiveType( - this, mProgram->mRenderer, mCaps, mProvokingVertex, gl::PrimitiveMode::Points, - &mShaderExecutable, &mInfoLog)); - } - - return angle::Result::Continue; - } - - private: - const gl::Caps &mCaps; - gl::ProvokingVertexConvention mProvokingVertex; -}; - -class ProgramD3D::GetComputeExecutableTask : public ProgramD3D::GetExecutableTask -{ - public: - GetComputeExecutableTask(ProgramD3D *program, ProgramExecutableD3D *executable) - : GetExecutableTask(program, executable) - {} - angle::Result run() override - { - ANGLE_TRACE_EVENT0("gpu.angle", "ProgramD3D::GetComputeExecutableTask::run"); - mExecutable->updateCachedImage2DBindLayoutFromShader(gl::ShaderType::Compute); - ShaderExecutableD3D *computeExecutable = nullptr; - ANGLE_TRY(mExecutable->getComputeExecutableForImage2DBindLayout( - this, mProgram->mRenderer, &computeExecutable, &mInfoLog)); - - return computeExecutable ? angle::Result::Continue : angle::Result::Incomplete; - } -}; - -// 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 workerPool, - std::shared_ptr vertexTask, - std::shared_ptr pixelTask, - std::shared_ptr geometryTask, - bool useGS, - const SharedCompiledShaderStateD3D &vertexShader, - const SharedCompiledShaderStateD3D &fragmentShader) - : mInfoLog(infoLog), - mVertexTask(vertexTask), - mPixelTask(pixelTask), - mGeometryTask(geometryTask), - mWaitEvents( - {{std::shared_ptr(workerPool->postWorkerTask(mVertexTask)), - std::shared_ptr(workerPool->postWorkerTask(mPixelTask)), - std::shared_ptr(workerPool->postWorkerTask(mGeometryTask))}}), - mUseGS(useGS), - mVertexShader(vertexShader), - mFragmentShader(fragmentShader) - {} - - angle::Result wait(const gl::Context *context) override - { - ANGLE_TRACE_EVENT0("gpu.angle", "ProgramD3D::GraphicsProgramLinkEvent::wait"); - WaitableEvent::WaitMany(&mWaitEvents); - - ANGLE_TRY(checkTask(context, mVertexTask.get())); - ANGLE_TRY(checkTask(context, mPixelTask.get())); - ANGLE_TRY(checkTask(context, mGeometryTask.get())); - - if (mVertexTask->getResult() == angle::Result::Incomplete || - mPixelTask->getResult() == angle::Result::Incomplete || - mGeometryTask->getResult() == angle::Result::Incomplete) - { - return angle::Result::Incomplete; - } - - 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 ? angle::Result::Continue : angle::Result::Incomplete; - } - - bool isLinking() override { return !WaitableEvent::AllReady(&mWaitEvents); } - - private: - angle::Result checkTask(const gl::Context *context, ProgramD3D::GetExecutableTask *task) - { - if (!task->getInfoLog().empty()) - { - mInfoLog << task->getInfoLog().str(); - } - - // Continue and Incomplete are not errors. For Stop, pass the error to the ContextD3D. - if (task->getResult() != angle::Result::Stop) - { - return angle::Result::Continue; - } - - ContextD3D *contextD3D = GetImplAs(context); - task->popError(contextD3D); - return angle::Result::Stop; - } - - gl::InfoLog &mInfoLog; - std::shared_ptr mVertexTask; - std::shared_ptr mPixelTask; - std::shared_ptr mGeometryTask; - std::array, 3> mWaitEvents; - bool mUseGS; - SharedCompiledShaderStateD3D mVertexShader; - SharedCompiledShaderStateD3D mFragmentShader; -}; - -// The LinkEvent implementation for linking a computing program. -class ProgramD3D::ComputeProgramLinkEvent final : public LinkEvent -{ - public: - ComputeProgramLinkEvent(gl::InfoLog &infoLog, - std::shared_ptr computeTask, - std::shared_ptr event) - : mInfoLog(infoLog), mComputeTask(computeTask), mWaitEvent(event) - {} - - bool isLinking() override { return !mWaitEvent->isReady(); } - - angle::Result wait(const gl::Context *context) override - { - ANGLE_TRACE_EVENT0("gpu.angle", "ProgramD3D::ComputeProgramLinkEvent::wait"); - mWaitEvent->wait(); - - angle::Result result = mComputeTask->getResult(); - if (result != angle::Result::Continue) - { - mInfoLog << "Failed to create D3D compute shader."; - } - return result; - } - - private: - gl::InfoLog &mInfoLog; - std::shared_ptr mComputeTask; - std::shared_ptr mWaitEvent; -}; - -std::unique_ptr ProgramD3D::compileProgramExecutables(const gl::Context *context) -{ - ANGLE_TRACE_EVENT0("gpu.angle", "ProgramD3D::compileProgramExecutables"); - - ProgramExecutableD3D *executableD3D = getExecutable(); - - auto vertexTask = std::make_shared(this, executableD3D); - auto pixelTask = std::make_shared(this, executableD3D); - auto geometryTask = std::make_shared( - this, executableD3D, context->getCaps(), context->getState().getProvokingVertex()); - bool useGS = executableD3D->usesGeometryShader( - mRenderer, context->getState().getProvokingVertex(), gl::PrimitiveMode::Points); - const SharedCompiledShaderStateD3D &vertexShaderD3D = - executableD3D->mAttachedShaders[gl::ShaderType::Vertex]; - const SharedCompiledShaderStateD3D &fragmentShaderD3D = - executableD3D->mAttachedShaders[gl::ShaderType::Fragment]; - - return std::make_unique( - mState.getExecutable().getInfoLog(), context->getShaderCompileThreadPool(), vertexTask, - pixelTask, geometryTask, useGS, vertexShaderD3D, fragmentShaderD3D); -} - -std::unique_ptr ProgramD3D::compileComputeExecutable(const gl::Context *context) -{ - ANGLE_TRACE_EVENT0("gpu.angle", "ProgramD3D::compileComputeExecutable"); - auto computeTask = std::make_shared(this, getExecutable()); - - std::shared_ptr waitableEvent; - - // TODO(jie.a.chen@intel.com): Fix the flaky bug. - // http://anglebug.com/3349 - bool compileInParallel = false; - if (!compileInParallel) - { - (*computeTask)(); - waitableEvent = std::make_shared(); - } - else - { - waitableEvent = context->getShaderCompileThreadPool()->postWorkerTask(computeTask); - } - - return std::make_unique(mState.getExecutable().getInfoLog(), - computeTask, waitableEvent); -} - void ProgramD3D::prepareForLink(const gl::ShaderMap &shaders) { ProgramExecutableD3D *executableD3D = getExecutable(); @@ -704,9 +635,7 @@ void ProgramD3D::prepareForLink(const gl::ShaderMap &shaders) } } -std::unique_ptr ProgramD3D::link(const gl::Context *context, - const gl::ProgramLinkedResources &resources, - gl::ProgramMergedVaryings && /*mergedVaryings*/) +angle::Result ProgramD3D::link(const gl::Context *context, std::shared_ptr *linkTaskOut) { ANGLE_TRACE_EVENT0("gpu.angle", "ProgramD3D::link"); const gl::Version &clientVersion = context->getClientVersion(); @@ -714,16 +643,10 @@ std::unique_ptr ProgramD3D::link(const gl::Context *context, EGLenum clientType = context->getClientType(); // Ensure the compiler is initialized to avoid race conditions. - angle::Result result = mRenderer->ensureHLSLCompilerInitialized(GetImplAs(context)); - if (result != angle::Result::Continue) - { - return std::make_unique(result); - } + ANGLE_TRY(mRenderer->ensureHLSLCompilerInitialized(GetImplAs(context))); ProgramExecutableD3D *executableD3D = getExecutable(); - reset(); - const gl::SharedCompiledShaderState &computeShader = mState.getAttachedShader(gl::ShaderType::Compute); if (!computeShader) @@ -758,6 +681,23 @@ std::unique_ptr ProgramD3D::link(const gl::Context *context, } } + *linkTaskOut = std::shared_ptr(new LinkTaskD3D( + clientVersion, caps, clientType, this, context->getState().getProvokingVertex())); + + return angle::Result::Continue; +} + +angle::Result ProgramD3D::linkJobImpl(d3d::Context *context, + const gl::Caps &caps, + const gl::Version &clientVersion, + EGLenum clientType, + const gl::ProgramLinkedResources &resources, + const gl::ProgramMergedVaryings &mergedVaryings) +{ + ProgramExecutableD3D *executableD3D = getExecutable(); + + const gl::SharedCompiledShaderState &computeShader = + mState.getAttachedShader(gl::ShaderType::Compute); if (computeShader) { const gl::SharedCompiledShaderState &shader = @@ -783,100 +723,98 @@ std::unique_ptr ProgramD3D::link(const gl::Context *context, executableD3D->defineUniformsAndAssignRegisters(mRenderer, mState.getAttachedShaders()); - return compileComputeExecutable(context); + return angle::Result::Continue; } - else + + for (gl::ShaderType shaderType : gl::kAllGraphicsShaderTypes) { - for (gl::ShaderType shaderType : gl::kAllGraphicsShaderTypes) + const gl::SharedCompiledShaderState &shader = mState.getAttachedShader(shaderType); + if (shader) { - const gl::SharedCompiledShaderState &shader = mState.getAttachedShader(shaderType); - if (shader) + executableD3D->mAttachedShaders[shaderType]->generateWorkarounds( + &executableD3D->mShaderWorkarounds[shaderType]); + executableD3D->mShaderSamplers[shaderType].resize( + caps.maxShaderTextureImageUnits[shaderType]); + executableD3D->mImages[shaderType].resize(caps.maxImageUnits); + executableD3D->mReadonlyImages[shaderType].resize(caps.maxImageUnits); + + executableD3D->mShaderUniformsDirty.set(shaderType); + + for (const sh::ShaderVariable &uniform : shader->uniforms) { - executableD3D->mAttachedShaders[shaderType]->generateWorkarounds( - &executableD3D->mShaderWorkarounds[shaderType]); - executableD3D->mShaderSamplers[shaderType].resize( - caps.maxShaderTextureImageUnits[shaderType]); - executableD3D->mImages[shaderType].resize(caps.maxImageUnits); - executableD3D->mReadonlyImages[shaderType].resize(caps.maxImageUnits); - - executableD3D->mShaderUniformsDirty.set(shaderType); - - for (const sh::ShaderVariable &uniform : shader->uniforms) + if (gl::IsImageType(uniform.type) && gl::IsImage2DType(uniform.type)) { - if (gl::IsImageType(uniform.type) && gl::IsImage2DType(uniform.type)) - { - executableD3D->mImage2DUniforms[shaderType].push_back(uniform); - } + executableD3D->mImage2DUniforms[shaderType].push_back(uniform); } } } - - if (mRenderer->getNativeLimitations().noFrontFacingSupport) - { - const SharedCompiledShaderStateD3D &fragmentShader = - executableD3D->mAttachedShaders[gl::ShaderType::Fragment]; - if (fragmentShader && fragmentShader->usesFrontFacing) - { - mState.getExecutable().getInfoLog() - << "The current renderer doesn't support gl_FrontFacing"; - return std::make_unique(angle::Result::Incomplete); - } - } - - const gl::VaryingPacking &varyingPacking = - resources.varyingPacking.getOutputPacking(gl::ShaderType::Vertex); - - ProgramD3DMetadata metadata( - mRenderer, mState.getAttachedShader(gl::ShaderType::Fragment), - executableD3D->mAttachedShaders, clientType, - mState.getAttachedShader(gl::ShaderType::Vertex)->shaderVersion); - BuiltinVaryingsD3D builtins(metadata, varyingPacking); - - DynamicHLSL::GenerateShaderLinkHLSL(mRenderer, caps, mState.getAttachedShaders(), - executableD3D->mAttachedShaders, metadata, - varyingPacking, builtins, &executableD3D->mShaderHLSL); - - executableD3D->mUsesPointSize = - executableD3D->mAttachedShaders[gl::ShaderType::Vertex] && - executableD3D->mAttachedShaders[gl::ShaderType::Vertex]->usesPointSize; - DynamicHLSL::GetPixelShaderOutputKey(mRenderer, caps, clientVersion, mState, metadata, - &executableD3D->mPixelShaderKey); - executableD3D->mFragDepthUsage = metadata.getFragDepthUsage(); - executableD3D->mUsesSampleMask = metadata.usesSampleMask(); - executableD3D->mUsesVertexID = metadata.usesVertexID(); - executableD3D->mUsesViewID = metadata.usesViewID(); - executableD3D->mHasMultiviewEnabled = metadata.hasMultiviewEnabled(); - - // Cache if we use flat shading - executableD3D->mUsesFlatInterpolation = - FindFlatInterpolationVarying(mState.getAttachedShaders()); - - if (mRenderer->getMajorShaderModel() >= 4) - { - executableD3D->mGeometryShaderPreamble = DynamicHLSL::GenerateGeometryShaderPreamble( - mRenderer, varyingPacking, builtins, executableD3D->mHasMultiviewEnabled, - metadata.canSelectViewInVertexShader()); - } - - executableD3D->initAttribLocationsToD3DSemantic( - mState.getAttachedShader(gl::ShaderType::Vertex)); - - executableD3D->defineUniformsAndAssignRegisters(mRenderer, mState.getAttachedShaders()); - - executableD3D->gatherTransformFeedbackVaryings(mRenderer, varyingPacking, - mState.getTransformFeedbackVaryingNames(), - builtins[gl::ShaderType::Vertex]); - - linkResources(resources); - - if (mState.getAttachedShader(gl::ShaderType::Vertex)) - { - executableD3D->updateCachedInputLayoutFromShader( - mRenderer, mState.getAttachedShader(gl::ShaderType::Vertex)); - } - - return compileProgramExecutables(context); } + + if (mRenderer->getNativeLimitations().noFrontFacingSupport) + { + const SharedCompiledShaderStateD3D &fragmentShader = + executableD3D->mAttachedShaders[gl::ShaderType::Fragment]; + if (fragmentShader && fragmentShader->usesFrontFacing) + { + mState.getExecutable().getInfoLog() + << "The current renderer doesn't support gl_FrontFacing"; + // Fail compilation + ANGLE_CHECK_HR(context, false, "gl_FrontFacing not supported", E_NOTIMPL); + } + } + + const gl::VaryingPacking &varyingPacking = + resources.varyingPacking.getOutputPacking(gl::ShaderType::Vertex); + + ProgramD3DMetadata metadata(mRenderer, mState.getAttachedShader(gl::ShaderType::Fragment), + executableD3D->mAttachedShaders, clientType, + mState.getAttachedShader(gl::ShaderType::Vertex)->shaderVersion); + BuiltinVaryingsD3D builtins(metadata, varyingPacking); + + DynamicHLSL::GenerateShaderLinkHLSL(mRenderer, caps, mState.getAttachedShaders(), + executableD3D->mAttachedShaders, metadata, varyingPacking, + builtins, &executableD3D->mShaderHLSL); + + executableD3D->mUsesPointSize = + executableD3D->mAttachedShaders[gl::ShaderType::Vertex] && + executableD3D->mAttachedShaders[gl::ShaderType::Vertex]->usesPointSize; + DynamicHLSL::GetPixelShaderOutputKey(mRenderer, caps, clientVersion, mState, metadata, + &executableD3D->mPixelShaderKey); + executableD3D->mFragDepthUsage = metadata.getFragDepthUsage(); + executableD3D->mUsesSampleMask = metadata.usesSampleMask(); + executableD3D->mUsesVertexID = metadata.usesVertexID(); + executableD3D->mUsesViewID = metadata.usesViewID(); + executableD3D->mHasMultiviewEnabled = metadata.hasMultiviewEnabled(); + + // Cache if we use flat shading + executableD3D->mUsesFlatInterpolation = + FindFlatInterpolationVarying(mState.getAttachedShaders()); + + if (mRenderer->getMajorShaderModel() >= 4) + { + executableD3D->mGeometryShaderPreamble = DynamicHLSL::GenerateGeometryShaderPreamble( + mRenderer, varyingPacking, builtins, executableD3D->mHasMultiviewEnabled, + metadata.canSelectViewInVertexShader()); + } + + executableD3D->initAttribLocationsToD3DSemantic( + mState.getAttachedShader(gl::ShaderType::Vertex)); + + executableD3D->defineUniformsAndAssignRegisters(mRenderer, mState.getAttachedShaders()); + + executableD3D->gatherTransformFeedbackVaryings(mRenderer, varyingPacking, + mState.getTransformFeedbackVaryingNames(), + builtins[gl::ShaderType::Vertex]); + + linkResources(resources); + + if (mState.getAttachedShader(gl::ShaderType::Vertex)) + { + executableD3D->updateCachedInputLayoutFromShader( + mRenderer, mState.getAttachedShader(gl::ShaderType::Vertex)); + } + + return angle::Result::Continue; } GLboolean ProgramD3D::validate(const gl::Caps & /*caps*/) @@ -1124,11 +1062,6 @@ void ProgramD3D::setUniformMatrixfvInternal(GLint location, } } -void ProgramD3D::reset() -{ - getExecutable()->reset(); -} - template void ProgramD3D::getUniformInternal(GLint location, DestT *dataOut) const { diff --git a/src/libANGLE/renderer/d3d/ProgramD3D.h b/src/libANGLE/renderer/d3d/ProgramD3D.h index 8b2616e9c..33ec9e237 100644 --- a/src/libANGLE/renderer/d3d/ProgramD3D.h +++ b/src/libANGLE/renderer/d3d/ProgramD3D.h @@ -78,16 +78,15 @@ class ProgramD3D : public ProgramImpl void destroy(const gl::Context *context) override; - std::unique_ptr load(const gl::Context *context, - gl::BinaryInputStream *stream) override; + angle::Result load(const gl::Context *context, + gl::BinaryInputStream *stream, + std::shared_ptr *loadTaskOut) override; void save(const gl::Context *context, gl::BinaryOutputStream *stream) override; void setBinaryRetrievableHint(bool retrievable) override; void setSeparable(bool separable) override; void prepareForLink(const gl::ShaderMap &shaders) override; - std::unique_ptr link(const gl::Context *context, - const gl::ProgramLinkedResources &resources, - gl::ProgramMergedVaryings &&mergedVaryings) override; + angle::Result link(const gl::Context *context, std::shared_ptr *linkTaskOut) override; GLboolean validate(const gl::Caps &caps) override; void setUniform1fv(GLint location, GLsizei count, const GLfloat *v) override; @@ -155,17 +154,27 @@ class ProgramD3D : public ProgramImpl } private: - // These forward-declared tasks are used for multi-thread shader compiles. - class GetExecutableTask; class GetVertexExecutableTask; class GetPixelExecutableTask; class GetGeometryExecutableTask; class GetComputeExecutableTask; - class GraphicsProgramLinkEvent; - class ComputeProgramLinkEvent; + class LinkLoadTaskD3D; + class LinkTaskD3D; + class LoadTaskD3D; - class LoadBinaryTask; - class LoadBinaryLinkEvent; + friend class LinkTaskD3D; + friend class LoadTaskD3D; + + angle::Result linkJobImpl(d3d::Context *context, + const gl::Caps &caps, + const gl::Version &clientVersion, + EGLenum clientType, + const gl::ProgramLinkedResources &resources, + const gl::ProgramMergedVaryings &mergedVaryings); + const SharedCompiledShaderStateD3D &getAttachedShader(gl::ShaderType shaderType) + { + return getExecutable()->mAttachedShaders[shaderType]; + } template void getUniformInternal(GLint location, DestT *dataOut) const; @@ -187,11 +196,6 @@ class ProgramD3D : public ProgramImpl GLboolean transpose, const GLfloat *value); - std::unique_ptr compileProgramExecutables(const gl::Context *context); - std::unique_ptr compileComputeExecutable(const gl::Context *context); - - void reset(); - void linkResources(const gl::ProgramLinkedResources &resources); RendererD3D *mRenderer; diff --git a/src/libANGLE/renderer/d3d/ProgramExecutableD3D.cpp b/src/libANGLE/renderer/d3d/ProgramExecutableD3D.cpp index 6de83f998..f4319a3ba 100644 --- a/src/libANGLE/renderer/d3d/ProgramExecutableD3D.cpp +++ b/src/libANGLE/renderer/d3d/ProgramExecutableD3D.cpp @@ -416,7 +416,9 @@ ProgramExecutableD3D::ProgramExecutableD3D(const gl::ProgramExecutable *executab mUsedReadonlyImageRange({}), mUsedAtomicCounterRange({}), mSerial(issueSerial()) -{} +{ + reset(); +} ProgramExecutableD3D::~ProgramExecutableD3D() {} @@ -706,9 +708,9 @@ bool ProgramExecutableD3D::load(const gl::Context *context, angle::Result ProgramExecutableD3D::loadBinaryShaderExecutables(d3d::Context *contextD3D, RendererD3D *renderer, - gl::BinaryInputStream *stream, - gl::InfoLog &infoLog) + gl::BinaryInputStream *stream) { + gl::InfoLog &infoLog = mExecutable->getInfoLog(); const unsigned char *binary = reinterpret_cast(stream->data()); bool separateAttribs = mExecutable->getTransformFeedbackBufferMode() == GL_SEPARATE_ATTRIBS; diff --git a/src/libANGLE/renderer/d3d/ProgramExecutableD3D.h b/src/libANGLE/renderer/d3d/ProgramExecutableD3D.h index 2879d9eff..447fdb6ac 100644 --- a/src/libANGLE/renderer/d3d/ProgramExecutableD3D.h +++ b/src/libANGLE/renderer/d3d/ProgramExecutableD3D.h @@ -344,8 +344,7 @@ class ProgramExecutableD3D : public ProgramExecutableImpl angle::Result loadBinaryShaderExecutables(d3d::Context *contextD3D, RendererD3D *renderer, - gl::BinaryInputStream *stream, - gl::InfoLog &infoLog); + gl::BinaryInputStream *stream); unsigned int getSerial() const { return mSerial; } diff --git a/src/libANGLE/renderer/gl/ProgramGL.cpp b/src/libANGLE/renderer/gl/ProgramGL.cpp index 277509a65..edb8b87d5 100644 --- a/src/libANGLE/renderer/gl/ProgramGL.cpp +++ b/src/libANGLE/renderer/gl/ProgramGL.cpp @@ -91,6 +91,75 @@ std::string GetTransformFeedbackVaryingMappedName(const gl::SharedCompiledShader } // anonymous namespace +class ProgramGL::LinkTaskGL final : public LinkTask +{ + public: + LinkTaskGL(ProgramGL *program, + bool hasNativeParallelCompile, + const FunctionsGL *functions, + const gl::Extensions &extensions, + GLuint programID) + : mProgram(program), + mHasNativeParallelCompile(hasNativeParallelCompile), + mFunctions(functions), + mExtensions(extensions), + mProgramID(programID) + {} + ~LinkTaskGL() override = default; + + std::vector> link( + const gl::ProgramLinkedResources &resources, + const gl::ProgramMergedVaryings &mergedVaryings) override + { + mProgram->linkJobImpl(mExtensions); + + // If there is no native parallel compile, do the post-link right away. + if (!mHasNativeParallelCompile) + { + mResult = mProgram->postLinkJobImpl(resources); + } + + // See comment on mResources + mResources = &resources; + return {}; + } + + angle::Result getResult(const gl::Context *context, gl::InfoLog &infoLog) override + { + ANGLE_TRACE_EVENT0("gpu.angle", "LinkTaskGL::getResult"); + + if (mHasNativeParallelCompile) + { + mResult = mProgram->postLinkJobImpl(*mResources); + } + + return mResult; + } + + bool isLinkingInternally() override + { + GLint completionStatus = GL_TRUE; + if (mHasNativeParallelCompile) + { + mFunctions->getProgramiv(mProgramID, GL_COMPLETION_STATUS, &completionStatus); + } + return completionStatus == GL_FALSE; + } + + private: + ProgramGL *mProgram; + const bool mHasNativeParallelCompile; + const FunctionsGL *mFunctions; + const gl::Extensions &mExtensions; + const GLuint mProgramID; + + angle::Result mResult = angle::Result::Continue; + + // Note: resources are kept alive by the front-end for the entire duration of the link, + // including during resolve when getResult() and postLink() are called. + const gl::ProgramLinkedResources *mResources = nullptr; +}; + ProgramGL::ProgramGL(const gl::ProgramState &data, const FunctionsGL *functions, const angle::FeaturesGL &features, @@ -117,14 +186,13 @@ void ProgramGL::destroy(const gl::Context *context) mProgramID = 0; } -std::unique_ptr ProgramGL::load(const gl::Context *context, - gl::BinaryInputStream *stream) +angle::Result ProgramGL::load(const gl::Context *context, + gl::BinaryInputStream *stream, + std::shared_ptr *loadTaskOut) { ANGLE_TRACE_EVENT0("gpu.angle", "ProgramGL::load"); ProgramExecutableGL *executableGL = getExecutable(); - executableGL->reset(); - // Read the binary format, size and blob GLenum binaryFormat = stream->readInt(); GLint binaryLength = stream->readInt(); @@ -137,13 +205,15 @@ std::unique_ptr ProgramGL::load(const gl::Context *context, // Verify that the program linked if (!checkLinkStatus()) { - return std::make_unique(angle::Result::Stop); + return angle::Result::Incomplete; } executableGL->postLink(mFunctions, mFeatures, mProgramID); reapplyUBOBindingsIfNeeded(context); - return std::make_unique(angle::Result::Continue); + *loadTaskOut = {}; + + return angle::Result::Continue; } void ProgramGL::save(const gl::Context *context, gl::BinaryOutputStream *stream) @@ -192,44 +262,6 @@ void ProgramGL::setSeparable(bool separable) mFunctions->programParameteri(mProgramID, GL_PROGRAM_SEPARABLE, separable ? GL_TRUE : GL_FALSE); } -using PostLinkImplFunctor = std::function; - -// The event for a parallelized linking using the native driver extension. -class ProgramGL::LinkEventNativeParallel final : public LinkEvent -{ - public: - LinkEventNativeParallel(PostLinkImplFunctor &&functor, - const FunctionsGL *functions, - GLuint programID) - : mPostLinkImplFunctor(functor), mFunctions(functions), mProgramID(programID) - {} - - angle::Result wait(const gl::Context *context) override - { - ANGLE_TRACE_EVENT0("gpu.angle", "ProgramGL::LinkEventNativeParallel::wait"); - - GLint linkStatus = GL_FALSE; - mFunctions->getProgramiv(mProgramID, GL_LINK_STATUS, &linkStatus); - if (linkStatus == GL_TRUE) - { - return mPostLinkImplFunctor(); - } - return angle::Result::Incomplete; - } - - bool isLinking() override - { - GLint completionStatus = GL_FALSE; - mFunctions->getProgramiv(mProgramID, GL_COMPLETION_STATUS, &completionStatus); - return completionStatus == GL_FALSE; - } - - private: - PostLinkImplFunctor mPostLinkImplFunctor; - const FunctionsGL *mFunctions; - GLuint mProgramID; -}; - void ProgramGL::prepareForLink(const gl::ShaderMap &shaders) { for (gl::ShaderType shaderType : gl::AllShaderTypes()) @@ -244,14 +276,20 @@ void ProgramGL::prepareForLink(const gl::ShaderMap &shaders) } } -std::unique_ptr ProgramGL::link(const gl::Context *context, - const gl::ProgramLinkedResources &resources, - gl::ProgramMergedVaryings && /*mergedVaryings*/) +angle::Result ProgramGL::link(const gl::Context *context, std::shared_ptr *linkTaskOut) { ANGLE_TRACE_EVENT0("gpu.angle", "ProgramGL::link"); - ProgramExecutableGL *executableGL = getExecutable(); - executableGL->reset(); + *linkTaskOut = std::make_shared(this, mRenderer->hasNativeParallelCompile(), + mFunctions, context->getExtensions(), mProgramID); + + return angle::Result::Continue; +} + +void ProgramGL::linkJobImpl(const gl::Extensions &extensions) +{ + ANGLE_TRACE_EVENT0("gpu.angle", "ProgramGL::linkJobImpl"); + ProgramExecutableGL *executableGL = getExecutable(); if (mAttachedShaders[gl::ShaderType::Compute] != 0) { @@ -261,14 +299,15 @@ std::unique_ptr ProgramGL::link(const gl::Context *context, { // Set the transform feedback state std::vector transformFeedbackVaryingMappedNames; + const gl::ShaderType tfShaderType = + mState.getExecutable().hasLinkedShaderStage(gl::ShaderType::Geometry) + ? gl::ShaderType::Geometry + : gl::ShaderType::Vertex; + const gl::SharedCompiledShaderState &tfShaderState = mState.getAttachedShader(tfShaderType); for (const auto &tfVarying : mState.getTransformFeedbackVaryingNames()) { - gl::ShaderType tfShaderType = - mState.getExecutable().hasLinkedShaderStage(gl::ShaderType::Geometry) - ? gl::ShaderType::Geometry - : gl::ShaderType::Vertex; - std::string tfVaryingMappedName = GetTransformFeedbackVaryingMappedName( - mState.getAttachedShader(tfShaderType), tfVarying); + std::string tfVaryingMappedName = + GetTransformFeedbackVaryingMappedName(tfShaderState, tfVarying); transformFeedbackVaryingMappedNames.push_back(tfVaryingMappedName); } @@ -321,7 +360,7 @@ std::unique_ptr ProgramGL::link(const gl::Context *context, // Bind the secondary fragment color outputs defined in EXT_blend_func_extended. We only use // the API to bind fragment output locations in case EXT_blend_func_extended is enabled. // Otherwise shader-assigned locations will work. - if (context->getExtensions().blendFuncExtendedEXT) + if (extensions.blendFuncExtendedEXT) { const gl::SharedCompiledShaderState &fragmentShader = mState.getAttachedShader(gl::ShaderType::Fragment); @@ -423,49 +462,44 @@ std::unique_ptr ProgramGL::link(const gl::Context *context, } } } - auto workerPool = context->getShaderCompileThreadPool(); - - auto postLinkImplTask = [this, &resources]() { - if (mAttachedShaders[gl::ShaderType::Compute] != 0) - { - mFunctions->detachShader(mProgramID, mAttachedShaders[gl::ShaderType::Compute]); - } - else - { - for (const gl::ShaderType shaderType : gl::kAllGraphicsShaderTypes) - { - if (mAttachedShaders[shaderType] != 0) - { - mFunctions->detachShader(mProgramID, mAttachedShaders[shaderType]); - } - } - } - // Verify the link - if (!checkLinkStatus()) - { - return angle::Result::Incomplete; - } - - if (mFeatures.alwaysCallUseProgramAfterLink.enabled) - { - mStateManager->forceUseProgram(mProgramID); - } - - linkResources(resources); - getExecutable()->postLink(mFunctions, mFeatures, mProgramID); - - return angle::Result::Continue; - }; mFunctions->linkProgram(mProgramID); - if (mRenderer->hasNativeParallelCompile()) +} + +angle::Result ProgramGL::postLinkJobImpl(const gl::ProgramLinkedResources &resources) +{ + ANGLE_TRACE_EVENT0("gpu.angle", "ProgramGL::postLinkJobImpl"); + + if (mAttachedShaders[gl::ShaderType::Compute] != 0) { - return std::make_unique(postLinkImplTask, mFunctions, mProgramID); + mFunctions->detachShader(mProgramID, mAttachedShaders[gl::ShaderType::Compute]); } else { - return std::make_unique(postLinkImplTask()); + for (const gl::ShaderType shaderType : gl::kAllGraphicsShaderTypes) + { + if (mAttachedShaders[shaderType] != 0) + { + mFunctions->detachShader(mProgramID, mAttachedShaders[shaderType]); + } + } } + + // Verify the link + if (!checkLinkStatus()) + { + return angle::Result::Stop; + } + + if (mFeatures.alwaysCallUseProgramAfterLink.enabled) + { + mStateManager->forceUseProgram(mProgramID); + } + + linkResources(resources); + getExecutable()->postLink(mFunctions, mFeatures, mProgramID); + + return angle::Result::Continue; } GLboolean ProgramGL::validate(const gl::Caps & /*caps*/) diff --git a/src/libANGLE/renderer/gl/ProgramGL.h b/src/libANGLE/renderer/gl/ProgramGL.h index da4cca3a6..68ebfed2f 100644 --- a/src/libANGLE/renderer/gl/ProgramGL.h +++ b/src/libANGLE/renderer/gl/ProgramGL.h @@ -39,16 +39,16 @@ class ProgramGL : public ProgramImpl void destroy(const gl::Context *context) override; - std::unique_ptr load(const gl::Context *context, - gl::BinaryInputStream *stream) override; + angle::Result load(const gl::Context *context, + gl::BinaryInputStream *stream, + std::shared_ptr *loadTaskOut) override; void save(const gl::Context *context, gl::BinaryOutputStream *stream) override; void setBinaryRetrievableHint(bool retrievable) override; void setSeparable(bool separable) override; void prepareForLink(const gl::ShaderMap &shaders) override; - std::unique_ptr link(const gl::Context *contextImpl, - const gl::ProgramLinkedResources &resources, - gl::ProgramMergedVaryings &&mergedVaryings) override; + angle::Result link(const gl::Context *contextImpl, + std::shared_ptr *linkTaskOut) override; GLboolean validate(const gl::Caps &caps) override; void setUniform1fv(GLint location, GLsizei count, const GLfloat *v) override; @@ -123,9 +123,14 @@ class ProgramGL : public ProgramImpl } private: - class LinkTask; - class LinkEventNativeParallel; - class LinkEventGL; + class LinkTaskGL; + class PostLinkGL; + + friend class LinkTaskGL; + friend class PostLinkGL; + + void linkJobImpl(const gl::Extensions &extensions); + angle::Result postLinkJobImpl(const gl::ProgramLinkedResources &resources); bool checkLinkStatus(); diff --git a/src/libANGLE/renderer/gl/renderergl_utils.cpp b/src/libANGLE/renderer/gl/renderergl_utils.cpp index b83b09095..2f5df6b51 100644 --- a/src/libANGLE/renderer/gl/renderergl_utils.cpp +++ b/src/libANGLE/renderer/gl/renderergl_utils.cpp @@ -2608,6 +2608,15 @@ void InitializeFrontendFeatures(const FunctionsGL *functions, angle::FrontendFea // https://crbug.com/480992 // Disable shader program cache to workaround PowerVR Rogue issues. ANGLE_FEATURE_CONDITION(features, disableProgramBinary, IsPowerVrRogue(functions)); + + // The link job needs a context, and previous experiments showed setting up temp contexts in + // threads for the sake of program link triggers too many driver bugs. See + // https://chromium-review.googlesource.com/c/angle/angle/+/4774785 for context. + // + // As a result, the link job is done in the same thread as the link call. If the native driver + // supports parallel link, it's still done internally by the driver, and ANGLE supports delaying + // post-link operations until that is done. + ANGLE_FEATURE_CONDITION(features, linkJobIsNotThreadSafe, true); } void ReInitializeFeaturesAtGPUSwitch(const FunctionsGL *functions, angle::FeaturesGL *features) diff --git a/src/libANGLE/renderer/metal/DisplayMtl.h b/src/libANGLE/renderer/metal/DisplayMtl.h index 384b1965d..ce444a435 100644 --- a/src/libANGLE/renderer/metal/DisplayMtl.h +++ b/src/libANGLE/renderer/metal/DisplayMtl.h @@ -109,6 +109,8 @@ class DisplayMtl : public DisplayImpl egl::Surface *readSurface, gl::Context *context) override; + void initializeFrontendFeatures(angle::FrontendFeatures *features) const override; + void populateFeatureList(angle::FeatureList *features) override; bool isValidNativeWindow(EGLNativeWindowType window) const override; diff --git a/src/libANGLE/renderer/metal/DisplayMtl.mm b/src/libANGLE/renderer/metal/DisplayMtl.mm index 5f2a086ee..d1129a019 100644 --- a/src/libANGLE/renderer/metal/DisplayMtl.mm +++ b/src/libANGLE/renderer/metal/DisplayMtl.mm @@ -516,6 +516,15 @@ void DisplayMtl::generateCaps(egl::Caps *outCaps) const outCaps->textureNPOT = true; } +void DisplayMtl::initializeFrontendFeatures(angle::FrontendFeatures *features) const +{ + // The link job in this backend references gl::Context and ContextMtl, and thread-safety is not + // guaranteed. The link subtasks are safe however, they are still parallelized. + // + // Once the link jobs are made thread-safe and using mtl::Context, this feature can be removed. + ANGLE_FEATURE_CONDITION(features, linkJobIsNotThreadSafe, true); +} + void DisplayMtl::populateFeatureList(angle::FeatureList *features) { mFeatures.populateFeatureList(features); diff --git a/src/libANGLE/renderer/metal/ProgramExecutableMtl.mm b/src/libANGLE/renderer/metal/ProgramExecutableMtl.mm index 466f3fa11..690f78fcb 100644 --- a/src/libANGLE/renderer/metal/ProgramExecutableMtl.mm +++ b/src/libANGLE/renderer/metal/ProgramExecutableMtl.mm @@ -318,7 +318,15 @@ DefaultUniformBlockMtl::~DefaultUniformBlockMtl() = default; ProgramExecutableMtl::ProgramExecutableMtl(const gl::ProgramExecutable *executable) : ProgramExecutableImpl(executable), mProgramHasFlatAttributes(false), mShadowCompareModes{} -{} +{ + mCurrentShaderVariants.fill(nullptr); + + for (gl::ShaderType shaderType : gl::AllShaderTypes()) + { + mMslShaderTranslateInfo[shaderType].reset(); + } + mMslXfbOnlyVertexShaderInfo.reset(); +} ProgramExecutableMtl::~ProgramExecutableMtl() {} diff --git a/src/libANGLE/renderer/metal/ProgramMtl.h b/src/libANGLE/renderer/metal/ProgramMtl.h index a764c3745..552aa6235 100644 --- a/src/libANGLE/renderer/metal/ProgramMtl.h +++ b/src/libANGLE/renderer/metal/ProgramMtl.h @@ -35,16 +35,15 @@ class ProgramMtl : public ProgramImpl void destroy(const gl::Context *context) override; - std::unique_ptr load(const gl::Context *context, - gl::BinaryInputStream *stream) override; + angle::Result load(const gl::Context *context, + gl::BinaryInputStream *stream, + std::shared_ptr *loadTaskOut) override; void save(const gl::Context *context, gl::BinaryOutputStream *stream) override; void setBinaryRetrievableHint(bool retrievable) override; void setSeparable(bool separable) override; void prepareForLink(const gl::ShaderMap &shaders) override; - std::unique_ptr link(const gl::Context *context, - const gl::ProgramLinkedResources &resources, - gl::ProgramMergedVaryings &&mergedVaryings) override; + angle::Result link(const gl::Context *context, std::shared_ptr *linkTaskOut) override; GLboolean validate(const gl::Caps &caps) override; void setUniform1fv(GLint location, GLsizei count, const GLfloat *v) override; @@ -107,8 +106,14 @@ class ProgramMtl : public ProgramImpl ProgramExecutableMtl *getExecutable() { return mtl::GetImpl(&mState.getExecutable()); } private: - class ProgramLinkEvent; - class CompileMslTask; + class LinkTaskMtl; + class LoadTaskMtl; + + friend class LinkTaskMtl; + + angle::Result linkJobImpl(const gl::Context *context, + const gl::ProgramLinkedResources &resources, + std::vector> *subTasksOut); template void setUniformMatrixfv(GLint location, @@ -121,10 +126,9 @@ class ProgramMtl : public ProgramImpl template void setUniformImpl(GLint location, GLsizei count, const T *v, GLenum entryPointType); - void reset(ContextMtl *context); - void linkResources(const gl::ProgramLinkedResources &resources); - std::unique_ptr compileMslShaderLibs(const gl::Context *context); + angle::Result compileMslShaderLibs(const gl::Context *context, + std::vector> *subTasksOut); gl::ShaderMap mAttachedShaders; }; diff --git a/src/libANGLE/renderer/metal/ProgramMtl.mm b/src/libANGLE/renderer/metal/ProgramMtl.mm index 43008d18f..bb104872a 100644 --- a/src/libANGLE/renderer/metal/ProgramMtl.mm +++ b/src/libANGLE/renderer/metal/ProgramMtl.mm @@ -29,9 +29,6 @@ #include "libANGLE/renderer/renderer_utils.h" #include "libANGLE/trace.h" -#define ANGLE_PARALLEL_LINK_RETURN(X) return std::make_unique(X) -#define ANGLE_PARALLEL_LINK_TRY(EXPR) ANGLE_TRY_TEMPLATE(EXPR, ANGLE_PARALLEL_LINK_RETURN) - namespace rx { @@ -129,8 +126,94 @@ class Std140BlockLayoutEncoderFactory : public gl::CustomBlockLayoutEncoderFacto public: sh::BlockLayoutEncoder *makeEncoder() override { return new sh::Std140BlockEncoder(); } }; + +class CompileMslTask final : public LinkSubTask +{ + public: + CompileMslTask(ContextMtl *context, + mtl::TranslatedShaderInfo *translatedMslInfo, + const std::map &substitutionMacros) + : mContext(context), + mTranslatedMslInfo(translatedMslInfo), + mSubstitutionMacros(substitutionMacros) + {} + ~CompileMslTask() override = default; + + void operator()() override + { + mResult = CreateMslShaderLib(mContext, mInfoLog, mTranslatedMslInfo, mSubstitutionMacros); + } + + angle::Result getResult(const gl::Context *context, gl::InfoLog &infoLog) override + { + if (!mInfoLog.empty()) + { + infoLog << mInfoLog.str(); + } + + return mResult; + } + + private: + // TODO: remove this, inherit from mtl::Context and ensure thread-safety. + // http://anglebug.com/8297 + ContextMtl *mContext; + gl::InfoLog mInfoLog; + mtl::TranslatedShaderInfo *mTranslatedMslInfo; + std::map mSubstitutionMacros; + angle::Result mResult = angle::Result::Continue; +}; } // namespace +class ProgramMtl::LinkTaskMtl final : public LinkTask +{ + public: + LinkTaskMtl(const gl::Context *context, ProgramMtl *program) + : mContext(context), mProgram(program) + {} + ~LinkTaskMtl() override = default; + + std::vector> link( + const gl::ProgramLinkedResources &resources, + const gl::ProgramMergedVaryings &mergedVaryings) override + { + std::vector> subTasks; + mResult = mProgram->linkJobImpl(mContext, resources, &subTasks); + return subTasks; + } + + angle::Result getResult(const gl::Context *context, gl::InfoLog &infoLog) override + { + return mResult; + } + + private: + // TODO: remove this, inherit from mtl::Context and ensure thread-safety. + // http://anglebug.com/8297 + const gl::Context *mContext; + ProgramMtl *mProgram; + angle::Result mResult = angle::Result::Continue; +}; + +class ProgramMtl::LoadTaskMtl final : public LinkTask +{ + public: + LoadTaskMtl(std::vector> &&subTasks) + : mSubTasks(std::move(subTasks)) + {} + ~LoadTaskMtl() override = default; + + std::vector> load() override { return mSubTasks; } + + angle::Result getResult(const gl::Context *context, gl::InfoLog &infoLog) override + { + return angle::Result::Continue; + } + + private: + std::vector> mSubTasks; +}; + // ProgramArgumentBufferEncoderMtl implementation void ProgramArgumentBufferEncoderMtl::reset(ContextMtl *contextMtl) { @@ -155,26 +238,26 @@ ProgramMtl::~ProgramMtl() = default; void ProgramMtl::destroy(const gl::Context *context) { - reset(mtl::GetImpl(context)); + getExecutable()->reset(mtl::GetImpl(context)); } -void ProgramMtl::reset(ContextMtl *context) -{ - getExecutable()->reset(context); -} - -std::unique_ptr ProgramMtl::load(const gl::Context *context, - gl::BinaryInputStream *stream) +angle::Result ProgramMtl::load(const gl::Context *context, + gl::BinaryInputStream *stream, + std::shared_ptr *loadTaskOut) { ContextMtl *contextMtl = mtl::GetImpl(context); // NOTE(hqle): No transform feedbacks for now, since we only support ES 2.0 atm - reset(contextMtl); + ANGLE_TRY(getExecutable()->load(contextMtl, stream)); - ANGLE_PARALLEL_LINK_TRY(getExecutable()->load(contextMtl, stream)); + // TODO: parallelize the above too. http://anglebug.com/8297 + std::vector> subTasks; - return compileMslShaderLibs(context); + ANGLE_TRY(compileMslShaderLibs(context, &subTasks)); + + *loadTaskOut = std::shared_ptr(new LoadTaskMtl(std::move(subTasks))); + return angle::Result::Continue; } void ProgramMtl::save(const gl::Context *context, gl::BinaryOutputStream *stream) @@ -203,9 +286,15 @@ void ProgramMtl::prepareForLink(const gl::ShaderMap &shaders) } } -std::unique_ptr ProgramMtl::link(const gl::Context *context, - const gl::ProgramLinkedResources &resources, - gl::ProgramMergedVaryings &&mergedVaryings) +angle::Result ProgramMtl::link(const gl::Context *context, std::shared_ptr *linkTaskOut) +{ + *linkTaskOut = std::shared_ptr(new LinkTaskMtl(context, this)); + return angle::Result::Continue; +} + +angle::Result ProgramMtl::linkJobImpl(const gl::Context *context, + const gl::ProgramLinkedResources &resources, + std::vector> *subTasksOut) { ContextMtl *contextMtl = mtl::GetImpl(context); ProgramExecutableMtl *executableMtl = getExecutable(); @@ -214,95 +303,24 @@ std::unique_ptr ProgramMtl::link(const gl::Context *context, // assignment done in that function. linkResources(resources); - reset(contextMtl); - ANGLE_PARALLEL_LINK_TRY( - executableMtl->initDefaultUniformBlocks(contextMtl, mState.getAttachedShaders())); + ANGLE_TRY(executableMtl->initDefaultUniformBlocks(contextMtl, mState.getAttachedShaders())); executableMtl->linkUpdateHasFlatAttributes(mState.getAttachedShader(gl::ShaderType::Vertex)); gl::ShaderMap shaderSources; mtl::MSLGetShaderSource(mState, resources, &shaderSources); - ANGLE_PARALLEL_LINK_TRY(mtl::MTLGetMSL(contextMtl, mState.getExecutable(), - contextMtl->getCaps(), shaderSources, mAttachedShaders, - &executableMtl->mMslShaderTranslateInfo)); + ANGLE_TRY(mtl::MTLGetMSL(contextMtl, mState.getExecutable(), contextMtl->getCaps(), + shaderSources, mAttachedShaders, + &executableMtl->mMslShaderTranslateInfo)); executableMtl->mMslXfbOnlyVertexShaderInfo = executableMtl->mMslShaderTranslateInfo[gl::ShaderType::Vertex]; - return compileMslShaderLibs(context); + return compileMslShaderLibs(context, subTasksOut); } -class ProgramMtl::CompileMslTask final : public angle::Closure -{ - public: - CompileMslTask(ContextMtl *context, - mtl::TranslatedShaderInfo *translatedMslInfo, - const std::map &substitutionMacros) - : mContext(context), - mTranslatedMslInfo(translatedMslInfo), - mSubstitutionMacros(substitutionMacros) - {} - - void operator()() override - { - mResult = CreateMslShaderLib(mContext, mInfoLog, mTranslatedMslInfo, mSubstitutionMacros); - } - - angle::Result getResult(gl::InfoLog &infoLog) - { - if (!mInfoLog.empty()) - { - infoLog << mInfoLog.str(); - } - - return mResult; - } - - private: - ContextMtl *mContext; - gl::InfoLog mInfoLog; - mtl::TranslatedShaderInfo *mTranslatedMslInfo; - std::map mSubstitutionMacros; - angle::Result mResult = angle::Result::Continue; -}; - -// The LinkEvent implementation for linking a Metal program -class ProgramMtl::ProgramLinkEvent final : public LinkEvent -{ - public: - ProgramLinkEvent(gl::InfoLog &infoLog, - std::shared_ptr workerPool, - std::vector> &&compileTasks) - : mInfoLog(infoLog), mTasks(std::move(compileTasks)) - { - mWaitableEvents.reserve(mTasks.size()); - for (const auto &task : mTasks) - { - mWaitableEvents.push_back(workerPool->postWorkerTask(task)); - } - } - - bool isLinking() override { return !angle::WaitableEvent::AllReady(&mWaitableEvents); } - - angle::Result wait(const gl::Context *context) override - { - ANGLE_TRACE_EVENT0("gpu.angle", "ProgramMtl::ProgramLinkEvent::wait"); - angle::WaitableEvent::WaitMany(&mWaitableEvents); - - for (const auto &task : mTasks) - { - ANGLE_TRY(task->getResult(mInfoLog)); - } - - return angle::Result::Continue; - } - - private: - gl::InfoLog &mInfoLog; - std::vector> mTasks; - std::vector> mWaitableEvents; -}; - -std::unique_ptr ProgramMtl::compileMslShaderLibs(const gl::Context *context) +angle::Result ProgramMtl::compileMslShaderLibs( + const gl::Context *context, + std::vector> *subTasksOut) { ANGLE_TRACE_EVENT0("gpu.angle", "ProgramMtl::compileMslShaderLibs"); gl::InfoLog &infoLog = mState.getExecutable().getInfoLog(); @@ -313,7 +331,6 @@ std::unique_ptr ProgramMtl::compileMslShaderLibs(const gl::Context *c contextMtl->getDisplay()->getFeatures().enableParallelMtlLibraryCompilation.enabled; mtl::LibraryCache &libraryCache = contextMtl->getDisplay()->getLibraryCache(); - std::vector> asyncTasks; for (gl::ShaderType shaderType : gl::kAllGLES2ShaderTypes) { mtl::TranslatedShaderInfo *translateInfo = @@ -330,26 +347,16 @@ std::unique_ptr ProgramMtl::compileMslShaderLibs(const gl::Context *c { if (asyncCompile) { - auto task = - std::make_shared(contextMtl, translateInfo, macros); - asyncTasks.push_back(task); + subTasksOut->emplace_back(new CompileMslTask(contextMtl, translateInfo, macros)); } else { - ANGLE_PARALLEL_LINK_TRY( - CreateMslShaderLib(contextMtl, infoLog, translateInfo, macros)); + ANGLE_TRY(CreateMslShaderLib(contextMtl, infoLog, translateInfo, macros)); } } } - if (asyncTasks.empty()) - { - // All shaders were in the cache, no async work to do - return std::make_unique(angle::Result::Continue); - } - - return std::make_unique( - infoLog, context->getShaderCompileThreadPool(), std::move(asyncTasks)); + return angle::Result::Continue; } void ProgramMtl::linkResources(const gl::ProgramLinkedResources &resources) diff --git a/src/libANGLE/renderer/null/ProgramNULL.cpp b/src/libANGLE/renderer/null/ProgramNULL.cpp index b8e124eb3..78baedee0 100644 --- a/src/libANGLE/renderer/null/ProgramNULL.cpp +++ b/src/libANGLE/renderer/null/ProgramNULL.cpp @@ -13,15 +13,35 @@ namespace rx { +namespace +{ +class LinkTaskNULL : public LinkTask +{ + public: + ~LinkTaskNULL() override = default; + std::vector> link( + const gl::ProgramLinkedResources &resources, + const gl::ProgramMergedVaryings &mergedVaryings) override + { + return {}; + } + angle::Result getResult(const gl::Context *context, gl::InfoLog &infoLog) override + { + return angle::Result::Continue; + } +}; +} // anonymous namespace ProgramNULL::ProgramNULL(const gl::ProgramState &state) : ProgramImpl(state) {} ProgramNULL::~ProgramNULL() {} -std::unique_ptr ProgramNULL::load(const gl::Context *context, - gl::BinaryInputStream *stream) +angle::Result ProgramNULL::load(const gl::Context *context, + gl::BinaryInputStream *stream, + std::shared_ptr *loadTaskOut) { - return std::make_unique(angle::Result::Continue); + *loadTaskOut = {}; + return angle::Result::Continue; } void ProgramNULL::save(const gl::Context *context, gl::BinaryOutputStream *stream) {} @@ -30,11 +50,11 @@ void ProgramNULL::setBinaryRetrievableHint(bool retrievable) {} void ProgramNULL::setSeparable(bool separable) {} -std::unique_ptr ProgramNULL::link(const gl::Context *contextImpl, - const gl::ProgramLinkedResources &resources, - gl::ProgramMergedVaryings && /*mergedVaryings*/) +angle::Result ProgramNULL::link(const gl::Context *contextImpl, + std::shared_ptr *linkTaskOut) { - return std::make_unique(angle::Result::Continue); + *linkTaskOut = std::shared_ptr(new LinkTaskNULL); + return angle::Result::Continue; } GLboolean ProgramNULL::validate(const gl::Caps &caps) diff --git a/src/libANGLE/renderer/null/ProgramNULL.h b/src/libANGLE/renderer/null/ProgramNULL.h index b73710d15..7a6467651 100644 --- a/src/libANGLE/renderer/null/ProgramNULL.h +++ b/src/libANGLE/renderer/null/ProgramNULL.h @@ -21,15 +21,14 @@ class ProgramNULL : public ProgramImpl ProgramNULL(const gl::ProgramState &state); ~ProgramNULL() override; - std::unique_ptr load(const gl::Context *context, - gl::BinaryInputStream *stream) override; + angle::Result load(const gl::Context *context, + gl::BinaryInputStream *stream, + std::shared_ptr *loadTaskOut) override; void save(const gl::Context *context, gl::BinaryOutputStream *stream) override; void setBinaryRetrievableHint(bool retrievable) override; void setSeparable(bool separable) override; - std::unique_ptr link(const gl::Context *context, - const gl::ProgramLinkedResources &resources, - gl::ProgramMergedVaryings &&mergedVaryings) override; + angle::Result link(const gl::Context *context, std::shared_ptr *linkTaskOut) override; GLboolean validate(const gl::Caps &caps) override; void setUniform1fv(GLint location, GLsizei count, const GLfloat *v) override; diff --git a/src/libANGLE/renderer/vulkan/ProgramExecutableVk.cpp b/src/libANGLE/renderer/vulkan/ProgramExecutableVk.cpp index 0758fdf35..5a0377a19 100644 --- a/src/libANGLE/renderer/vulkan/ProgramExecutableVk.cpp +++ b/src/libANGLE/renderer/vulkan/ProgramExecutableVk.cpp @@ -362,6 +362,7 @@ ProgramExecutableVk::ProgramExecutableVk(const gl::ProgramExecutable *executable mUniformBufferDescriptorType(VK_DESCRIPTOR_TYPE_MAX_ENUM), mDynamicUniformDescriptorOffsets{} { + mDescriptorSets.fill(VK_NULL_HANDLE); for (std::shared_ptr &defaultBlock : mDefaultUniformBlocks) { defaultBlock = std::make_shared(); @@ -499,9 +500,9 @@ angle::Result ProgramExecutableVk::ensurePipelineCacheInitialized(vk::Context *c return angle::Result::Continue; } -std::unique_ptr ProgramExecutableVk::load(ContextVk *contextVk, - bool isSeparable, - gl::BinaryInputStream *stream) +angle::Result ProgramExecutableVk::load(ContextVk *contextVk, + bool isSeparable, + gl::BinaryInputStream *stream) { mVariableInfoMap.load(stream); mOriginalShaderInfo.load(stream); @@ -528,33 +529,19 @@ std::unique_ptr ProgramExecutableVk::load(ContextVk *contextVk, stream->readBool(&compressedData); stream->readBytes(compressedPipelineData.data(), compressedPipelineDataSize); // Initialize the pipeline cache based on cached data. - angle::Result status = - initializePipelineCache(contextVk, compressedData, compressedPipelineData); - if (status != angle::Result::Continue) - { - return std::make_unique(status); - } + ANGLE_TRY(initializePipelineCache(contextVk, compressedData, compressedPipelineData)); } } // Initialize and resize the mDefaultUniformBlocks' memory - angle::Result status = resizeUniformBlockMemory(contextVk, requiredBufferSize); - if (status != angle::Result::Continue) - { - return std::make_unique(status); - } + ANGLE_TRY(resizeUniformBlockMemory(contextVk, requiredBufferSize)); resetLayout(contextVk); - status = createPipelineLayout(contextVk, &contextVk->getPipelineLayoutCache(), - &contextVk->getDescriptorSetLayoutCache(), nullptr); - if (status != angle::Result::Continue) - { - return std::make_unique(status); - } + ANGLE_TRY(createPipelineLayout(contextVk, &contextVk->getPipelineLayoutCache(), + &contextVk->getDescriptorSetLayoutCache(), nullptr)); - status = initializeDescriptorPools(contextVk, &contextVk->getDescriptorSetLayoutCache(), - &contextVk->getMetaDescriptorPools()); - return std::make_unique(status); + return initializeDescriptorPools(contextVk, &contextVk->getDescriptorSetLayoutCache(), + &contextVk->getMetaDescriptorPools()); } void ProgramExecutableVk::save(ContextVk *contextVk, diff --git a/src/libANGLE/renderer/vulkan/ProgramExecutableVk.h b/src/libANGLE/renderer/vulkan/ProgramExecutableVk.h index 6cd769039..34f384aa0 100644 --- a/src/libANGLE/renderer/vulkan/ProgramExecutableVk.h +++ b/src/libANGLE/renderer/vulkan/ProgramExecutableVk.h @@ -121,9 +121,7 @@ class ProgramExecutableVk : public ProgramExecutableImpl void destroy(const gl::Context *context) override; void save(ContextVk *contextVk, bool isSeparable, gl::BinaryOutputStream *stream); - std::unique_ptr load(ContextVk *contextVk, - bool isSeparable, - gl::BinaryInputStream *stream); + angle::Result load(ContextVk *contextVk, bool isSeparable, gl::BinaryInputStream *stream); void clearVariableInfoMap(); diff --git a/src/libANGLE/renderer/vulkan/ProgramVk.cpp b/src/libANGLE/renderer/vulkan/ProgramVk.cpp index e3673605f..425e7a656 100644 --- a/src/libANGLE/renderer/vulkan/ProgramVk.cpp +++ b/src/libANGLE/renderer/vulkan/ProgramVk.cpp @@ -48,34 +48,35 @@ class Std140BlockLayoutEncoderFactory : public gl::CustomBlockLayoutEncoderFacto sh::BlockLayoutEncoder *makeEncoder() override { return new sh::Std140BlockEncoder(); } }; -class LinkTaskVk final : public vk::Context, public angle::Closure +class LinkTaskVk final : public vk::Context, public LinkTask { public: LinkTaskVk(RendererVk *renderer, PipelineLayoutCache &pipelineLayoutCache, DescriptorSetLayoutCache &descriptorSetLayoutCache, const gl::ProgramState &state, - gl::ProgramMergedVaryings &&mergedVaryings, - const gl::ProgramLinkedResources &resources, bool isGLES1, vk::PipelineRobustness pipelineRobustness, vk::PipelineProtectedAccess pipelineProtectedAccess) : vk::Context(renderer), mState(state), mExecutable(&mState.getExecutable()), - mMergedVaryings(std::move(mergedVaryings)), - mResources(resources), mIsGLES1(isGLES1), mPipelineRobustness(pipelineRobustness), mPipelineProtectedAccess(pipelineProtectedAccess), mPipelineLayoutCache(pipelineLayoutCache), mDescriptorSetLayoutCache(descriptorSetLayoutCache) {} + ~LinkTaskVk() override = default; - void operator()() override + std::vector> link( + const gl::ProgramLinkedResources &resources, + const gl::ProgramMergedVaryings &mergedVaryings) override { - angle::Result result = linkImpl(); + angle::Result result = linkImpl(resources, mergedVaryings); ASSERT((result == angle::Result::Continue) == (mErrorCode == VK_SUCCESS)); + + return {}; } void handleError(VkResult result, @@ -89,11 +90,12 @@ class LinkTaskVk final : public vk::Context, public angle::Closure mErrorLine = line; } - angle::Result getResult(ContextVk *contextVk) + angle::Result getResult(const gl::Context *context, gl::InfoLog &infoLog) override { + ContextVk *contextVk = vk::GetImpl(context); ProgramExecutableVk *executableVk = vk::GetImpl(mExecutable); - // Clean up garbage first, it's not no matter what may fail below. + // Clean up garbage first, it's done no matter what may fail below. mCompatibleRenderPass.destroy(contextVk->getDevice()); ANGLE_TRY(executableVk->initializeDescriptorPools(contextVk, @@ -136,9 +138,10 @@ class LinkTaskVk final : public vk::Context, public angle::Closure } private: - angle::Result linkImpl(); + angle::Result linkImpl(const gl::ProgramLinkedResources &resources, + const gl::ProgramMergedVaryings &mergedVaryings); - void linkResources(); + void linkResources(const gl::ProgramLinkedResources &resources); angle::Result initDefaultUniformBlocks(); void generateUniformLayoutMapping(gl::ShaderMap *layoutMapOut, gl::ShaderMap *requiredBufferSizeOut); @@ -148,8 +151,6 @@ class LinkTaskVk final : public vk::Context, public angle::Closure // direclty access the state from a potentially parallel job. const gl::ProgramState &mState; const gl::ProgramExecutable *mExecutable; - const gl::ProgramMergedVaryings mMergedVaryings; - const gl::ProgramLinkedResources &mResources; const bool mIsGLES1; const vk::PipelineRobustness mPipelineRobustness; const vk::PipelineProtectedAccess mPipelineProtectedAccess; @@ -168,19 +169,20 @@ class LinkTaskVk final : public vk::Context, public angle::Closure unsigned int mErrorLine = 0; }; -angle::Result LinkTaskVk::linkImpl() +angle::Result LinkTaskVk::linkImpl(const gl::ProgramLinkedResources &resources, + const gl::ProgramMergedVaryings &mergedVaryings) { - ANGLE_TRACE_EVENT0("gpu.angle", "ProgramVk::LinkTaskVk::run"); + ANGLE_TRACE_EVENT0("gpu.angle", "LinkTaskVk::linkImpl"); ProgramExecutableVk *executableVk = vk::GetImpl(mExecutable); // Link resources before calling GetShaderSource to make sure they are ready for the set/binding // assignment done in that function. - linkResources(); + linkResources(resources); executableVk->clearVariableInfoMap(); // Gather variable info and compiled SPIR-V binaries. - executableVk->assignAllSpvLocations(this, mState, mResources); + executableVk->assignAllSpvLocations(this, mState, resources); gl::ShaderMap spirvBlobs; SpvGetShaderSpirvCode(mState, &spirvBlobs); @@ -188,7 +190,7 @@ angle::Result LinkTaskVk::linkImpl() if (getFeatures().varyingsRequireMatchingPrecisionInSpirv.enabled && getFeatures().enablePrecisionQualifiers.enabled) { - executableVk->resolvePrecisionMismatch(mMergedVaryings); + executableVk->resolvePrecisionMismatch(mergedVaryings); } // Compile the shaders. @@ -219,12 +221,12 @@ angle::Result LinkTaskVk::linkImpl() return angle::Result::Continue; } -void LinkTaskVk::linkResources() +void LinkTaskVk::linkResources(const gl::ProgramLinkedResources &resources) { Std140BlockLayoutEncoderFactory std140EncoderFactory; gl::ProgramLinkedResourcesLinker linker(&std140EncoderFactory); - linker.linkResources(mState, mResources); + linker.linkResources(mState, resources); } angle::Result LinkTaskVk::initDefaultUniformBlocks() @@ -334,33 +336,6 @@ void LinkTaskVk::initDefaultUniformLayoutMapping(gl::ShaderMap workerPool, - std::shared_ptr linkTask) - : mLinkTask(linkTask), - mWaitableEvent( - std::shared_ptr(workerPool->postWorkerTask(mLinkTask))) - {} - - angle::Result wait(const gl::Context *context) override - { - ANGLE_TRACE_EVENT0("gpu.angle", "ProgramVK::LinkEvent::wait"); - - mWaitableEvent->wait(); - - return mLinkTask->getResult(vk::GetImpl(context)); - } - - bool isLinking() override { return !mWaitableEvent->isReady(); } - - private: - std::shared_ptr mLinkTask; - std::shared_ptr mWaitableEvent; -}; - template void UpdateDefaultUniformBlock(GLsizei count, uint32_t arrayIndex, @@ -430,20 +405,17 @@ ProgramVk::~ProgramVk() = default; void ProgramVk::destroy(const gl::Context *context) { ContextVk *contextVk = vk::GetImpl(context); - reset(contextVk); -} - -void ProgramVk::reset(ContextVk *contextVk) -{ getExecutable()->reset(contextVk); } -std::unique_ptr ProgramVk::load(const gl::Context *context, - gl::BinaryInputStream *stream) +angle::Result ProgramVk::load(const gl::Context *context, + gl::BinaryInputStream *stream, + std::shared_ptr *loadTaskOut) { ContextVk *contextVk = vk::GetImpl(context); - reset(contextVk); + // TODO: parallelize program load. http://anglebug.com/8297 + *loadTaskOut = {}; return getExecutable()->load(contextVk, mState.isSeparable(), stream); } @@ -464,21 +436,16 @@ void ProgramVk::setSeparable(bool separable) // Nothing to do here yet. } -std::unique_ptr ProgramVk::link(const gl::Context *context, - const gl::ProgramLinkedResources &resources, - gl::ProgramMergedVaryings &&mergedVaryings) +angle::Result ProgramVk::link(const gl::Context *context, std::shared_ptr *linkTaskOut) { - ANGLE_TRACE_EVENT0("gpu.angle", "ProgramVk::link"); - ContextVk *contextVk = vk::GetImpl(context); - reset(contextVk); - std::shared_ptr linkTask = std::make_shared( + *linkTaskOut = std::shared_ptr(new LinkTaskVk( contextVk->getRenderer(), contextVk->getPipelineLayoutCache(), - contextVk->getDescriptorSetLayoutCache(), mState, std::move(mergedVaryings), resources, - context->getState().isGLES1(), contextVk->pipelineRobustness(), - contextVk->pipelineProtectedAccess()); - return std::make_unique(context->getShaderCompileThreadPool(), linkTask); + contextVk->getDescriptorSetLayoutCache(), mState, context->getState().isGLES1(), + contextVk->pipelineRobustness(), contextVk->pipelineProtectedAccess())); + + return angle::Result::Continue; } GLboolean ProgramVk::validate(const gl::Caps &caps) diff --git a/src/libANGLE/renderer/vulkan/ProgramVk.h b/src/libANGLE/renderer/vulkan/ProgramVk.h index 649b86c88..80aa6ae4e 100644 --- a/src/libANGLE/renderer/vulkan/ProgramVk.h +++ b/src/libANGLE/renderer/vulkan/ProgramVk.h @@ -29,15 +29,14 @@ class ProgramVk : public ProgramImpl ~ProgramVk() override; void destroy(const gl::Context *context) override; - std::unique_ptr load(const gl::Context *context, - gl::BinaryInputStream *stream) override; + angle::Result load(const gl::Context *context, + gl::BinaryInputStream *stream, + std::shared_ptr *loadTaskOut) override; void save(const gl::Context *context, gl::BinaryOutputStream *stream) override; void setBinaryRetrievableHint(bool retrievable) override; void setSeparable(bool separable) override; - std::unique_ptr link(const gl::Context *context, - const gl::ProgramLinkedResources &resources, - gl::ProgramMergedVaryings &&mergedVaryings) override; + angle::Result link(const gl::Context *context, std::shared_ptr *linkTaskOut) override; GLboolean validate(const gl::Caps &caps) override; angle::Result syncState(const gl::Context *context, @@ -109,8 +108,6 @@ class ProgramVk : public ProgramImpl GLboolean transpose, const GLfloat *value); - void reset(ContextVk *contextVk); - template void getUniformImpl(GLint location, T *v, GLenum entryPointType) const; diff --git a/util/autogen/angle_features_autogen.cpp b/util/autogen/angle_features_autogen.cpp index f0f3d0c58..78be6dedf 100644 --- a/util/autogen/angle_features_autogen.cpp +++ b/util/autogen/angle_features_autogen.cpp @@ -195,6 +195,7 @@ constexpr PackedEnumMap kFeatureNames = {{ {Feature::LimitMaxMSAASamplesTo4, "limitMaxMSAASamplesTo4"}, {Feature::LimitSampleCountTo2, "limitSampleCountTo2"}, {Feature::LimitWebglMaxTextureSizeTo4096, "limitWebglMaxTextureSizeTo4096"}, + {Feature::LinkJobIsNotThreadSafe, "linkJobIsNotThreadSafe"}, {Feature::LoadMetalShadersFromBlobCache, "loadMetalShadersFromBlobCache"}, {Feature::LogMemoryReportCallbacks, "logMemoryReportCallbacks"}, {Feature::LogMemoryReportStats, "logMemoryReportStats"}, diff --git a/util/autogen/angle_features_autogen.h b/util/autogen/angle_features_autogen.h index becd7cfd1..e6337601a 100644 --- a/util/autogen/angle_features_autogen.h +++ b/util/autogen/angle_features_autogen.h @@ -195,6 +195,7 @@ enum class Feature LimitMaxMSAASamplesTo4, LimitSampleCountTo2, LimitWebglMaxTextureSizeTo4096, + LinkJobIsNotThreadSafe, LoadMetalShadersFromBlobCache, LogMemoryReportCallbacks, LogMemoryReportStats,