diff --git a/src/libANGLE/Context.cpp b/src/libANGLE/Context.cpp index af019f25d..076b46edc 100644 --- a/src/libANGLE/Context.cpp +++ b/src/libANGLE/Context.cpp @@ -1125,7 +1125,7 @@ GLuint Context::createShaderProgramv(ShaderType type, GLsizei count, const GLcha // We must wait to mark the program separable until it's successfully compiled. programObject->setSeparable(true); - programObject->attachShader(shaderObject); + programObject->attachShader(this, shaderObject); if (programObject->link(this) != angle::Result::Continue) { @@ -6155,7 +6155,7 @@ void Context::attachShader(ShaderProgramID program, ShaderProgramID shader) Program *programObject = mState.mShaderProgramManager->getProgram(program); Shader *shaderObject = mState.mShaderProgramManager->getShader(shader); ASSERT(programObject && shaderObject); - programObject->attachShader(shaderObject); + programObject->attachShader(this, shaderObject); } void Context::copyBufferSubData(BufferBinding readTarget, diff --git a/src/libANGLE/GLES1Renderer.cpp b/src/libANGLE/GLES1Renderer.cpp index 3f46313ec..b51e0d5a5 100644 --- a/src/libANGLE/GLES1Renderer.cpp +++ b/src/libANGLE/GLES1Renderer.cpp @@ -672,8 +672,8 @@ angle::Result GLES1Renderer::linkProgram(Context *context, *programOut = program; - programObject->attachShader(getShader(vertexShader)); - programObject->attachShader(getShader(fragmentShader)); + programObject->attachShader(context, getShader(vertexShader)); + programObject->attachShader(context, getShader(fragmentShader)); for (auto it : attribLocs) { diff --git a/src/libANGLE/Program.cpp b/src/libANGLE/Program.cpp index 543aebeb9..cf34bb67b 100644 --- a/src/libANGLE/Program.cpp +++ b/src/libANGLE/Program.cpp @@ -1144,8 +1144,9 @@ const std::string &Program::getLabel() const return mState.mLabel; } -void Program::attachShader(Shader *shader) +void Program::attachShader(const Context *context, Shader *shader) { + resolveLink(context); ShaderType shaderType = shader->getType(); ASSERT(shaderType != ShaderType::InvalidEnum); diff --git a/src/libANGLE/Program.h b/src/libANGLE/Program.h index 66b3ed5b4..c2f647ac4 100644 --- a/src/libANGLE/Program.h +++ b/src/libANGLE/Program.h @@ -456,7 +456,7 @@ class Program final : public LabeledObject, public angle::Subject return mProgram; } - void attachShader(Shader *shader); + void attachShader(const Context *context, Shader *shader); void detachShader(const Context *context, Shader *shader); int getAttachedShadersCount() const; diff --git a/src/libANGLE/renderer/vulkan/ProgramExecutableVk.cpp b/src/libANGLE/renderer/vulkan/ProgramExecutableVk.cpp index 7e692d951..2f4eb76e0 100644 --- a/src/libANGLE/renderer/vulkan/ProgramExecutableVk.cpp +++ b/src/libANGLE/renderer/vulkan/ProgramExecutableVk.cpp @@ -1867,7 +1867,7 @@ void ProgramExecutableVk::onProgramBind(const gl::ProgramExecutable &glExecutabl } angle::Result ProgramExecutableVk::resizeUniformBlockMemory( - ContextVk *contextVk, + vk::Context *context, const gl::ProgramExecutable &glExecutable, const gl::ShaderMap &requiredBufferSize) { @@ -1878,7 +1878,7 @@ angle::Result ProgramExecutableVk::resizeUniformBlockMemory( if (!mDefaultUniformBlocks[shaderType]->uniformData.resize( requiredBufferSize[shaderType])) { - ANGLE_VK_CHECK(contextVk, false, VK_ERROR_OUT_OF_HOST_MEMORY); + ANGLE_VK_CHECK(context, false, VK_ERROR_OUT_OF_HOST_MEMORY); } // Initialize uniform buffer memory to zero by default. diff --git a/src/libANGLE/renderer/vulkan/ProgramExecutableVk.h b/src/libANGLE/renderer/vulkan/ProgramExecutableVk.h index ea610ea63..a907ad30b 100644 --- a/src/libANGLE/renderer/vulkan/ProgramExecutableVk.h +++ b/src/libANGLE/renderer/vulkan/ProgramExecutableVk.h @@ -277,6 +277,11 @@ class ProgramExecutableVk const gl::Program::DirtyBits &getDirtyBits() const { return mDirtyBits; } void resetUniformBufferDirtyBits() { mDirtyBits.reset(); } + // The following functions are for internal use of programs, including from a threaded link job: + angle::Result resizeUniformBlockMemory(vk::Context *context, + const gl::ProgramExecutable &glExecutable, + const gl::ShaderMap &requiredBufferSize); + private: friend class ProgramVk; friend class ProgramPipelineVk; @@ -368,10 +373,6 @@ class ProgramExecutableVk const vk::GraphicsPipelineDesc **descPtrOut, vk::PipelineHelper **pipelineOut); - angle::Result resizeUniformBlockMemory(ContextVk *contextVk, - const gl::ProgramExecutable &glExecutable, - const gl::ShaderMap &requiredBufferSize); - angle::Result getOrAllocateDescriptorSet(vk::Context *context, UpdateDescriptorSetsBuilder *updateBuilder, vk::CommandBufferHelperCommon *commandBufferHelper, diff --git a/src/libANGLE/renderer/vulkan/ProgramVk.cpp b/src/libANGLE/renderer/vulkan/ProgramVk.cpp index b84cc5852..f8931ce87 100644 --- a/src/libANGLE/renderer/vulkan/ProgramVk.cpp +++ b/src/libANGLE/renderer/vulkan/ProgramVk.cpp @@ -22,16 +22,36 @@ namespace rx namespace { -class LinkTask final : public vk::Context, public angle::Closure +// Identical to Std140 encoder in all aspects, except it ignores opaque uniform types. +class VulkanDefaultBlockEncoder : public sh::Std140BlockEncoder { public: - LinkTask(RendererVk *renderer, - const gl::ProgramState &state, - const gl::ProgramExecutable &glExecutable, - ProgramExecutableVk *executable, - bool isGLES1, - vk::PipelineRobustness pipelineRobustness, - vk::PipelineProtectedAccess pipelineProtectedAccess) + void advanceOffset(GLenum type, + const std::vector &arraySizes, + bool isRowMajorMatrix, + int arrayStride, + int matrixStride) override + { + if (gl::IsOpaqueType(type)) + { + return; + } + + sh::Std140BlockEncoder::advanceOffset(type, arraySizes, isRowMajorMatrix, arrayStride, + matrixStride); + } +}; + +class LinkTaskVk final : public vk::Context, public angle::Closure +{ + public: + LinkTaskVk(RendererVk *renderer, + const gl::ProgramState &state, + const gl::ProgramExecutable &glExecutable, + ProgramExecutableVk *executable, + bool isGLES1, + vk::PipelineRobustness pipelineRobustness, + vk::PipelineProtectedAccess pipelineProtectedAccess) : vk::Context(renderer), mState(state), mGlExecutable(glExecutable), @@ -41,7 +61,11 @@ class LinkTask final : public vk::Context, public angle::Closure mPipelineProtectedAccess(pipelineProtectedAccess) {} - void operator()() override; + void operator()() override + { + angle::Result result = linkImpl(); + ASSERT((result == angle::Result::Continue) == (mErrorCode == VK_SUCCESS)); + } void handleError(VkResult result, const char *file, @@ -78,6 +102,13 @@ class LinkTask final : public vk::Context, public angle::Closure } private: + angle::Result linkImpl(); + + angle::Result initDefaultUniformBlocks(); + void generateUniformLayoutMapping(gl::ShaderMap *layoutMapOut, + gl::ShaderMap *requiredBufferSizeOut); + void initDefaultUniformLayoutMapping(gl::ShaderMap *layoutMapOut); + // 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. const gl::ProgramState &mState; @@ -97,9 +128,11 @@ class LinkTask final : public vk::Context, public angle::Closure unsigned int mErrorLine = 0; }; -void LinkTask::operator()() +angle::Result LinkTaskVk::linkImpl() { - ANGLE_TRACE_EVENT0("gpu.angle", "ProgramVk::LinkTask::run"); + ANGLE_TRACE_EVENT0("gpu.angle", "ProgramVk::LinkTaskVk::run"); + + ANGLE_TRY(initDefaultUniformBlocks()); // Warm up the pipeline cache by creating a few placeholder pipelines. This is not done for // separable programs, and is deferred to when the program pipeline is finalized. @@ -113,60 +146,27 @@ void LinkTask::operator()() // - Individual GLES1 tests are long, and this adds a considerable overhead to those tests if (!mState.isSeparable() && !mIsGLES1) { - angle::Result result = - mExecutable->warmUpPipelineCache(this, mGlExecutable, mPipelineRobustness, - mPipelineProtectedAccess, &mCompatibleRenderPass); - - ASSERT((result == angle::Result::Continue) == (mErrorCode == VK_SUCCESS)); + ANGLE_TRY(mExecutable->warmUpPipelineCache(this, mGlExecutable, mPipelineRobustness, + mPipelineProtectedAccess, + &mCompatibleRenderPass)); } + + return angle::Result::Continue; } -// The event for parallelized/lockless link. -class LinkEventVulkan final : public LinkEvent +angle::Result LinkTaskVk::initDefaultUniformBlocks() { - public: - LinkEventVulkan(std::shared_ptr workerPool, - std::shared_ptr linkTask) - : mLinkTask(linkTask), - mWaitableEvent( - std::shared_ptr(workerPool->postWorkerTask(mLinkTask))) - {} + // Process vertex and fragment uniforms into std140 packing. + gl::ShaderMap layoutMap; + gl::ShaderMap requiredBufferSize; + requiredBufferSize.fill(0); - angle::Result wait(const gl::Context *context) override - { - ANGLE_TRACE_EVENT0("gpu.angle", "ProgramVK::LinkEvent::wait"); + generateUniformLayoutMapping(&layoutMap, &requiredBufferSize); + initDefaultUniformLayoutMapping(&layoutMap); - mWaitableEvent->wait(); - - return mLinkTask->getResult(vk::GetImpl(context)); - } - - bool isLinking() override { return !mWaitableEvent->isReady(); } - - private: - std::shared_ptr mLinkTask; - std::shared_ptr mWaitableEvent; -}; - -// Identical to Std140 encoder in all aspects, except it ignores opaque uniform types. -class VulkanDefaultBlockEncoder : public sh::Std140BlockEncoder -{ - public: - void advanceOffset(GLenum type, - const std::vector &arraySizes, - bool isRowMajorMatrix, - int arrayStride, - int matrixStride) override - { - if (gl::IsOpaqueType(type)) - { - return; - } - - sh::Std140BlockEncoder::advanceOffset(type, arraySizes, isRowMajorMatrix, arrayStride, - matrixStride); - } -}; + // All uniform initializations are complete, now resize the buffers accordingly and return + return mExecutable->resizeUniformBlockMemory(this, mGlExecutable, requiredBufferSize); +} void InitDefaultUniformBlock(const std::vector &uniforms, sh::BlockLayoutMap *blockLayoutMapOut, @@ -194,6 +194,97 @@ void InitDefaultUniformBlock(const std::vector &uniforms, return; } +void LinkTaskVk::generateUniformLayoutMapping(gl::ShaderMap *layoutMapOut, + gl::ShaderMap *requiredBufferSizeOut) +{ + for (const gl::ShaderType shaderType : mGlExecutable.getLinkedShaderStages()) + { + const gl::SharedCompiledShaderState &shader = mState.getAttachedShader(shaderType); + + if (shader) + { + const std::vector &uniforms = shader->uniforms; + InitDefaultUniformBlock(uniforms, &(*layoutMapOut)[shaderType], + &(*requiredBufferSizeOut)[shaderType]); + } + } +} + +void LinkTaskVk::initDefaultUniformLayoutMapping(gl::ShaderMap *layoutMapOut) +{ + // Init the default block layout info. + const auto &uniforms = mGlExecutable.getUniforms(); + + for (const gl::VariableLocation &location : mState.getUniformLocations()) + { + gl::ShaderMap layoutInfo; + + if (location.used() && !location.ignored) + { + const auto &uniform = uniforms[location.index]; + if (uniform.isInDefaultBlock() && !uniform.isSampler() && !uniform.isImage() && + !uniform.isFragmentInOut()) + { + std::string uniformName = mGlExecutable.getUniformNameByIndex(location.index); + if (uniform.isArray()) + { + // Gets the uniform name without the [0] at the end. + uniformName = gl::StripLastArrayIndex(uniformName); + ASSERT(uniformName.size() != + mGlExecutable.getUniformNameByIndex(location.index).size()); + } + + bool found = false; + + for (const gl::ShaderType shaderType : mGlExecutable.getLinkedShaderStages()) + { + auto it = (*layoutMapOut)[shaderType].find(uniformName); + if (it != (*layoutMapOut)[shaderType].end()) + { + found = true; + layoutInfo[shaderType] = it->second; + } + } + + ASSERT(found); + } + } + + for (const gl::ShaderType shaderType : mGlExecutable.getLinkedShaderStages()) + { + mExecutable->getSharedDefaultUniformBlock(shaderType) + ->uniformLayout.push_back(layoutInfo[shaderType]); + } + } +} + +// The event for parallelized/lockless link. +class LinkEventVulkan final : public LinkEvent +{ + public: + LinkEventVulkan(std::shared_ptr 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, @@ -343,21 +434,13 @@ std::unique_ptr ProgramVk::link(const gl::Context *context, return std::make_unique(status); } - status = initDefaultUniformBlocks(context); - if (status != angle::Result::Continue) - { - return std::make_unique(status); - } - - // TODO(jie.a.chen@intel.com): Parallelize linking. - // http://crbug.com/849576 status = mExecutable.createPipelineLayout(contextVk, programExecutable, nullptr); if (status != angle::Result::Continue) { return std::make_unique(status); } - std::shared_ptr linkTask = std::make_shared( + std::shared_ptr linkTask = std::make_shared( contextVk->getRenderer(), mState, programExecutable, &mExecutable, context->getState().isGLES1(), contextVk->pipelineRobustness(), contextVk->pipelineProtectedAccess()); @@ -372,90 +455,6 @@ void ProgramVk::linkResources(const gl::ProgramLinkedResources &resources) linker.linkResources(mState, resources); } -angle::Result ProgramVk::initDefaultUniformBlocks(const gl::Context *glContext) -{ - ContextVk *contextVk = vk::GetImpl(glContext); - - // Process vertex and fragment uniforms into std140 packing. - gl::ShaderMap layoutMap; - gl::ShaderMap requiredBufferSize; - requiredBufferSize.fill(0); - - generateUniformLayoutMapping(layoutMap, requiredBufferSize); - initDefaultUniformLayoutMapping(layoutMap); - - // All uniform initializations are complete, now resize the buffers accordingly and return - return mExecutable.resizeUniformBlockMemory(contextVk, mState.getExecutable(), - requiredBufferSize); -} - -void ProgramVk::generateUniformLayoutMapping(gl::ShaderMap &layoutMap, - gl::ShaderMap &requiredBufferSize) -{ - const gl::ProgramExecutable &glExecutable = mState.getExecutable(); - - for (const gl::ShaderType shaderType : glExecutable.getLinkedShaderStages()) - { - const gl::SharedCompiledShaderState &shader = mState.getAttachedShader(shaderType); - - if (shader) - { - const std::vector &uniforms = shader->uniforms; - InitDefaultUniformBlock(uniforms, &layoutMap[shaderType], - &requiredBufferSize[shaderType]); - } - } -} - -void ProgramVk::initDefaultUniformLayoutMapping(gl::ShaderMap &layoutMap) -{ - // Init the default block layout info. - const auto &uniforms = mState.getUniforms(); - const gl::ProgramExecutable &glExecutable = mState.getExecutable(); - - for (const gl::VariableLocation &location : mState.getUniformLocations()) - { - gl::ShaderMap layoutInfo; - - if (location.used() && !location.ignored) - { - const auto &uniform = uniforms[location.index]; - if (uniform.isInDefaultBlock() && !uniform.isSampler() && !uniform.isImage() && - !uniform.isFragmentInOut()) - { - std::string uniformName = glExecutable.getUniformNameByIndex(location.index); - if (uniform.isArray()) - { - // Gets the uniform name without the [0] at the end. - uniformName = gl::StripLastArrayIndex(uniformName); - ASSERT(uniformName.size() != - glExecutable.getUniformNameByIndex(location.index).size()); - } - - bool found = false; - - for (const gl::ShaderType shaderType : glExecutable.getLinkedShaderStages()) - { - auto it = layoutMap[shaderType].find(uniformName); - if (it != layoutMap[shaderType].end()) - { - found = true; - layoutInfo[shaderType] = it->second; - } - } - - ASSERT(found); - } - } - - for (const gl::ShaderType shaderType : glExecutable.getLinkedShaderStages()) - { - mExecutable.mDefaultUniformBlocks[shaderType]->uniformLayout.push_back( - layoutInfo[shaderType]); - } - } -} - GLboolean ProgramVk::validate(const gl::Caps &caps, gl::InfoLog *infoLog) { // No-op. The spec is very vague about the behavior of validation. diff --git a/src/libANGLE/renderer/vulkan/ProgramVk.h b/src/libANGLE/renderer/vulkan/ProgramVk.h index e17540df9..80fb1dccf 100644 --- a/src/libANGLE/renderer/vulkan/ProgramVk.h +++ b/src/libANGLE/renderer/vulkan/ProgramVk.h @@ -127,10 +127,6 @@ class ProgramVk : public ProgramImpl const GLfloat *value); void reset(ContextVk *contextVk); - angle::Result initDefaultUniformBlocks(const gl::Context *glContext); - void generateUniformLayoutMapping(gl::ShaderMap &layoutMap, - gl::ShaderMap &requiredBufferSize); - void initDefaultUniformLayoutMapping(gl::ShaderMap &layoutMap); template void getUniformImpl(GLint location, T *v, GLenum entryPointType) const;