// // Copyright 2015 The ANGLE Project Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. // // ShaderGL.cpp: Implements the class methods for ShaderGL. #include "libANGLE/renderer/gl/ShaderGL.h" #include "common/debug.h" #include "libANGLE/Compiler.h" #include "libANGLE/Context.h" #include "libANGLE/renderer/gl/FunctionsGL.h" #include "libANGLE/renderer/gl/RendererGL.h" #include "libANGLE/trace.h" #include "platform/FeaturesGL.h" #include namespace rx { using CompileAndCheckShaderInWorkerFunctor = std::function; class TranslateTaskGL : public angle::Closure { public: TranslateTaskGL(ShHandle handle, ShCompileOptions options, const std::string &source, CompileAndCheckShaderInWorkerFunctor &&compileAndCheckShaderInWorkerFunctor) : mHandle(handle), mOptions(options), mSource(source), mCompileAndCheckShaderInWorkerFunctor(std::move(compileAndCheckShaderInWorkerFunctor)), mResult(false), mWorkerAvailable(true) {} void operator()() override { ANGLE_TRACE_EVENT1("gpu.angle", "TranslateTaskGL::run", "source", mSource); const char *source = mSource.c_str(); mResult = sh::Compile(mHandle, &source, 1, mOptions); if (mResult) { mWorkerAvailable = mCompileAndCheckShaderInWorkerFunctor(sh::GetObjectCode(mHandle).c_str()); } } bool getResult() { return mResult; } bool workerAvailable() { return mWorkerAvailable; } ShHandle getHandle() { return mHandle; } private: ShHandle mHandle; ShCompileOptions mOptions; std::string mSource; CompileAndCheckShaderInWorkerFunctor mCompileAndCheckShaderInWorkerFunctor; bool mResult; bool mWorkerAvailable; }; using PostTranslateFunctor = std::function; using CompileAndCheckShaderFunctor = std::function; class WaitableCompileEventWorkerContext final : public WaitableCompileEvent { public: WaitableCompileEventWorkerContext(std::shared_ptr waitableEvent, CompileAndCheckShaderFunctor &&compileAndCheckShaderFunctor, PostTranslateFunctor &&postTranslateFunctor, std::shared_ptr translateTask) : WaitableCompileEvent(waitableEvent), mCompileAndCheckShaderFunctor(std::move(compileAndCheckShaderFunctor)), mPostTranslateFunctor(std::move(postTranslateFunctor)), mTranslateTask(translateTask) {} bool getResult() override { return mTranslateTask->getResult(); } bool postTranslate(std::string *infoLog) override { if (!mTranslateTask->workerAvailable()) { ShHandle handle = mTranslateTask->getHandle(); mCompileAndCheckShaderFunctor(sh::GetObjectCode(handle).c_str()); } return mPostTranslateFunctor(infoLog); } private: CompileAndCheckShaderFunctor mCompileAndCheckShaderFunctor; PostTranslateFunctor mPostTranslateFunctor; std::shared_ptr mTranslateTask; }; using PeekCompletionFunctor = std::function; using CheckShaderFunctor = std::function; class WaitableCompileEventNativeParallel final : public WaitableCompileEvent { public: WaitableCompileEventNativeParallel(PostTranslateFunctor &&postTranslateFunctor, bool result, CheckShaderFunctor &&checkShaderFunctor, PeekCompletionFunctor &&peekCompletionFunctor) : WaitableCompileEvent(std::shared_ptr()), mPostTranslateFunctor(std::move(postTranslateFunctor)), mResult(result), mCheckShaderFunctor(std::move(checkShaderFunctor)), mPeekCompletionFunctor(std::move(peekCompletionFunctor)) {} void wait() override { mCheckShaderFunctor(); } bool isReady() override { return mPeekCompletionFunctor(); } bool getResult() override { return mResult; } bool postTranslate(std::string *infoLog) override { return mPostTranslateFunctor(infoLog); } private: PostTranslateFunctor mPostTranslateFunctor; bool mResult; CheckShaderFunctor mCheckShaderFunctor; PeekCompletionFunctor mPeekCompletionFunctor; }; class WaitableCompileEventDone final : public WaitableCompileEvent { public: WaitableCompileEventDone(PostTranslateFunctor &&postTranslateFunctor, bool result) : WaitableCompileEvent(std::make_shared()), mPostTranslateFunctor(std::move(postTranslateFunctor)), mResult(result) {} bool getResult() override { return mResult; } bool postTranslate(std::string *infoLog) override { return mPostTranslateFunctor(infoLog); } private: PostTranslateFunctor mPostTranslateFunctor; bool mResult; }; ShaderGL::ShaderGL(const gl::ShaderState &data, GLuint shaderID, MultiviewImplementationTypeGL multiviewImplementationType, const std::shared_ptr &renderer) : ShaderImpl(data), mShaderID(shaderID), mMultiviewImplementationType(multiviewImplementationType), mRenderer(renderer), mCompileStatus(GL_FALSE) {} ShaderGL::~ShaderGL() { ASSERT(mShaderID == 0); } void ShaderGL::destroy() { mRenderer->getFunctions()->deleteShader(mShaderID); mShaderID = 0; } void ShaderGL::compileAndCheckShader(const char *source) { compileShader(source); checkShader(); } void ShaderGL::compileShader(const char *source) { const FunctionsGL *functions = mRenderer->getFunctions(); functions->shaderSource(mShaderID, 1, &source, nullptr); functions->compileShader(mShaderID); } void ShaderGL::checkShader() { const FunctionsGL *functions = mRenderer->getFunctions(); // Check for compile errors from the native driver mCompileStatus = GL_FALSE; functions->getShaderiv(mShaderID, GL_COMPILE_STATUS, &mCompileStatus); if (mCompileStatus == GL_FALSE) { // Compilation failed, put the error into the info log GLint infoLogLength = 0; functions->getShaderiv(mShaderID, GL_INFO_LOG_LENGTH, &infoLogLength); // Info log length includes the null terminator, so 1 means that the info log is an empty // string. if (infoLogLength > 1) { std::vector buf(infoLogLength); functions->getShaderInfoLog(mShaderID, infoLogLength, nullptr, &buf[0]); mInfoLog += &buf[0]; WARN() << std::endl << mInfoLog; } else { WARN() << std::endl << "Shader compilation failed with no info log."; } } } bool ShaderGL::peekCompletion() { const FunctionsGL *functions = mRenderer->getFunctions(); GLint status = GL_FALSE; functions->getShaderiv(mShaderID, GL_COMPLETION_STATUS, &status); return status == GL_TRUE; } bool ShaderGL::compileAndCheckShaderInWorker(const char *source) { std::string workerInfoLog; ScopedWorkerContextGL worker(mRenderer.get(), &workerInfoLog); if (worker()) { compileAndCheckShader(source); return true; } else { #if !defined(NDEBUG) mInfoLog += "bindWorkerContext failed.\n" + workerInfoLog; #endif return false; } } std::shared_ptr ShaderGL::compile(const gl::Context *context, gl::ShCompilerInstance *compilerInstance, ShCompileOptions options) { mInfoLog.clear(); ShCompileOptions additionalOptions = SH_INIT_GL_POSITION; bool isWebGL = context->getExtensions().webglCompatibility; if (isWebGL && mState.getShaderType() != gl::ShaderType::Compute) { additionalOptions |= SH_INIT_OUTPUT_VARIABLES; } if (isWebGL && !context->getState().getEnableFeature(GL_TEXTURE_RECTANGLE_ANGLE)) { additionalOptions |= SH_DISABLE_ARB_TEXTURE_RECTANGLE; } const angle::FeaturesGL &features = GetFeaturesGL(context); if (features.initFragmentOutputVariables.enabled) { additionalOptions |= SH_INIT_FRAGMENT_OUTPUT_VARIABLES; } if (features.doWhileGLSLCausesGPUHang.enabled) { additionalOptions |= SH_REWRITE_DO_WHILE_LOOPS; } if (features.emulateAbsIntFunction.enabled) { additionalOptions |= SH_EMULATE_ABS_INT_FUNCTION; } if (features.addAndTrueToLoopCondition.enabled) { additionalOptions |= SH_ADD_AND_TRUE_TO_LOOP_CONDITION; } if (features.emulateIsnanFloat.enabled) { additionalOptions |= SH_EMULATE_ISNAN_FLOAT_FUNCTION; } if (features.emulateAtan2Float.enabled) { additionalOptions |= SH_EMULATE_ATAN2_FLOAT_FUNCTION; } if (features.useUnusedBlocksWithStandardOrSharedLayout.enabled) { additionalOptions |= SH_USE_UNUSED_STANDARD_SHARED_BLOCKS; } if (features.removeInvariantAndCentroidForESSL3.enabled) { additionalOptions |= SH_REMOVE_INVARIANT_AND_CENTROID_FOR_ESSL3; } if (features.rewriteFloatUnaryMinusOperator.enabled) { additionalOptions |= SH_REWRITE_FLOAT_UNARY_MINUS_OPERATOR; } if (!features.dontInitializeUninitializedLocals.enabled) { additionalOptions |= SH_INITIALIZE_UNINITIALIZED_LOCALS; } if (features.clampPointSize.enabled) { additionalOptions |= SH_CLAMP_POINT_SIZE; } if (features.rewriteVectorScalarArithmetic.enabled) { additionalOptions |= SH_REWRITE_VECTOR_SCALAR_ARITHMETIC; } if (features.dontUseLoopsToInitializeVariables.enabled) { additionalOptions |= SH_DONT_USE_LOOPS_TO_INITIALIZE_VARIABLES; } if (features.clampFragDepth.enabled) { additionalOptions |= SH_CLAMP_FRAG_DEPTH; } if (features.rewriteRepeatedAssignToSwizzled.enabled) { additionalOptions |= SH_REWRITE_REPEATED_ASSIGN_TO_SWIZZLED; } if (mMultiviewImplementationType == MultiviewImplementationTypeGL::NV_VIEWPORT_ARRAY2) { additionalOptions |= SH_INITIALIZE_BUILTINS_FOR_INSTANCED_MULTIVIEW; additionalOptions |= SH_SELECT_VIEW_IN_NV_GLSL_VERTEX_SHADER; } if (features.clampArrayAccess.enabled || isWebGL) { additionalOptions |= SH_CLAMP_INDIRECT_ARRAY_BOUNDS; } if (features.addBaseVertexToVertexID.enabled) { additionalOptions |= SH_ADD_BASE_VERTEX_TO_VERTEX_ID; } if (features.unfoldShortCircuits.enabled) { additionalOptions |= SH_UNFOLD_SHORT_CIRCUIT; } if (features.removeDynamicIndexingOfSwizzledVector.enabled) { additionalOptions |= SH_REMOVE_DYNAMIC_INDEXING_OF_SWIZZLED_VECTOR; } if (features.preAddTexelFetchOffsets.enabled) { additionalOptions |= SH_REWRITE_TEXELFETCHOFFSET_TO_TEXELFETCH; } if (features.regenerateStructNames.enabled) { additionalOptions |= SH_REGENERATE_STRUCT_NAMES; } if (features.rewriteRowMajorMatrices.enabled) { additionalOptions |= SH_REWRITE_ROW_MAJOR_MATRICES; } options |= additionalOptions; auto workerThreadPool = context->getWorkerThreadPool(); const std::string &source = mState.getSource(); auto postTranslateFunctor = [this](std::string *infoLog) { if (mCompileStatus == GL_FALSE) { *infoLog = mInfoLog; return false; } return true; }; if (mRenderer->hasNativeParallelCompile()) { ShHandle handle = compilerInstance->getHandle(); const char *str = source.c_str(); bool result = sh::Compile(handle, &str, 1, options); if (result) { compileShader(sh::GetObjectCode(handle).c_str()); auto checkShaderFunctor = [this]() { checkShader(); }; auto peekCompletionFunctor = [this]() { return peekCompletion(); }; return std::make_shared( std::move(postTranslateFunctor), result, std::move(checkShaderFunctor), std::move(peekCompletionFunctor)); } else { return std::make_shared([](std::string *) { return true; }, result); } } else if (workerThreadPool->isAsync()) { auto compileAndCheckShaderInWorkerFunctor = [this](const char *source) { return compileAndCheckShaderInWorker(source); }; auto translateTask = std::make_shared(compilerInstance->getHandle(), options, source, std::move(compileAndCheckShaderInWorkerFunctor)); auto compileAndCheckShaderFunctor = [this](const char *source) { compileAndCheckShader(source); }; return std::make_shared( angle::WorkerThreadPool::PostWorkerTask(workerThreadPool, translateTask), std::move(compileAndCheckShaderFunctor), std::move(postTranslateFunctor), translateTask); } else { ShHandle handle = compilerInstance->getHandle(); const char *str = source.c_str(); bool result = sh::Compile(handle, &str, 1, options); if (result) { compileAndCheckShader(sh::GetObjectCode(handle).c_str()); } return std::make_shared(std::move(postTranslateFunctor), result); } } std::string ShaderGL::getDebugInfo() const { return mState.getTranslatedSource(); } GLuint ShaderGL::getShaderID() const { return mShaderID; } } // namespace rx