// // 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. // // RendererGL.cpp: Implements the class methods for RendererGL. #include "libANGLE/renderer/gl/RendererGL.h" #include #include "common/debug.h" #include "libANGLE/AttributeMap.h" #include "libANGLE/Context.h" #include "libANGLE/Display.h" #include "libANGLE/State.h" #include "libANGLE/Surface.h" #include "libANGLE/renderer/gl/BlitGL.h" #include "libANGLE/renderer/gl/BufferGL.h" #include "libANGLE/renderer/gl/ClearMultiviewGL.h" #include "libANGLE/renderer/gl/CompilerGL.h" #include "libANGLE/renderer/gl/ContextGL.h" #include "libANGLE/renderer/gl/DisplayGL.h" #include "libANGLE/renderer/gl/FenceNVGL.h" #include "libANGLE/renderer/gl/FramebufferGL.h" #include "libANGLE/renderer/gl/FunctionsGL.h" #include "libANGLE/renderer/gl/ProgramGL.h" #include "libANGLE/renderer/gl/QueryGL.h" #include "libANGLE/renderer/gl/RenderbufferGL.h" #include "libANGLE/renderer/gl/SamplerGL.h" #include "libANGLE/renderer/gl/ShaderGL.h" #include "libANGLE/renderer/gl/StateManagerGL.h" #include "libANGLE/renderer/gl/SurfaceGL.h" #include "libANGLE/renderer/gl/SyncGL.h" #include "libANGLE/renderer/gl/TextureGL.h" #include "libANGLE/renderer/gl/TransformFeedbackGL.h" #include "libANGLE/renderer/gl/VertexArrayGL.h" #include "libANGLE/renderer/gl/renderergl_utils.h" #include "libANGLE/renderer/renderer_utils.h" namespace { void SetMaxShaderCompilerThreads(const rx::FunctionsGL *functions, GLuint count) { if (functions->maxShaderCompilerThreadsKHR != nullptr) { functions->maxShaderCompilerThreadsKHR(count); } else { ASSERT(functions->maxShaderCompilerThreadsARB != nullptr); functions->maxShaderCompilerThreadsARB(count); } } #if defined(ANGLE_PLATFORM_ANDROID) const char *kIgnoredErrors[] = { // Wrong error message on Android Q Pixel 2. http://anglebug.com/3491 "FreeAllocationOnTimestamp - Reference to buffer created from " "different context without a share list. Application failed to pass " "share_context to eglCreateContext. Results are undefined.", }; #endif // defined(ANGLE_PLATFORM_ANDROID) const char *kIgnoredWarnings[] = { // We always request GL_ARB_gpu_shader5 and GL_EXT_gpu_shader5 when compiling shaders but some // drivers warn when it is not present. This ends up spamming the console on every shader // compile. "extension `GL_ARB_gpu_shader5' unsupported in", "extension `GL_EXT_gpu_shader5' unsupported in", }; } // namespace static void INTERNAL_GL_APIENTRY LogGLDebugMessage(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar *message, const void *userParam) { std::string sourceText = gl::GetDebugMessageSourceString(source); std::string typeText = gl::GetDebugMessageTypeString(type); std::string severityText = gl::GetDebugMessageSeverityString(severity); #if defined(ANGLE_PLATFORM_ANDROID) if (type == GL_DEBUG_TYPE_ERROR) { for (const char *&err : kIgnoredErrors) { if (strncmp(err, message, length) == 0) { // There is only one ignored message right now and it is quite spammy, around 3MB // for a complete end2end tests run, so don't print it even as a warning. return; } } } #endif // defined(ANGLE_PLATFORM_ANDROID) if (type == GL_DEBUG_TYPE_ERROR) { ERR() << std::endl << "\tSource: " << sourceText << std::endl << "\tType: " << typeText << std::endl << "\tID: " << gl::FmtHex(id) << std::endl << "\tSeverity: " << severityText << std::endl << "\tMessage: " << message; } else if (type != GL_DEBUG_TYPE_PERFORMANCE) { // Don't print performance warnings. They tend to be very spammy in the dEQP test suite and // there is very little we can do about them. for (const char *&warn : kIgnoredWarnings) { if (strstr(message, warn) != nullptr) { return; } } // TODO(ynovikov): filter into WARN and INFO if INFO is ever implemented WARN() << std::endl << "\tSource: " << sourceText << std::endl << "\tType: " << typeText << std::endl << "\tID: " << gl::FmtHex(id) << std::endl << "\tSeverity: " << severityText << std::endl << "\tMessage: " << message; } } namespace rx { RendererGL::RendererGL(std::unique_ptr functions, const egl::AttributeMap &attribMap, DisplayGL *display) : mMaxSupportedESVersion(0, 0), mFunctions(std::move(functions)), mStateManager(nullptr), mBlitter(nullptr), mMultiviewClearer(nullptr), mUseDebugOutput(false), mCapsInitialized(false), mMultiviewImplementationType(MultiviewImplementationTypeGL::UNSPECIFIED), mNativeParallelCompileEnabled(false), mNeedsFlushBeforeDeleteTextures(false) { ASSERT(mFunctions); if (!display->getState().featuresAllDisabled) { nativegl_gl::InitializeFeatures(mFunctions.get(), &mFeatures); } ApplyFeatureOverrides(&mFeatures, display->getState()); mStateManager = new StateManagerGL(mFunctions.get(), getNativeCaps(), getNativeExtensions(), mFeatures); mBlitter = new BlitGL(mFunctions.get(), mFeatures, mStateManager); mMultiviewClearer = new ClearMultiviewGL(mFunctions.get(), mStateManager); bool hasDebugOutput = mFunctions->isAtLeastGL(gl::Version(4, 3)) || mFunctions->hasGLExtension("GL_KHR_debug") || mFunctions->isAtLeastGLES(gl::Version(3, 2)) || mFunctions->hasGLESExtension("GL_KHR_debug"); mUseDebugOutput = hasDebugOutput && ShouldUseDebugLayers(attribMap); if (mUseDebugOutput) { mFunctions->enable(GL_DEBUG_OUTPUT); mFunctions->enable(GL_DEBUG_OUTPUT_SYNCHRONOUS); mFunctions->debugMessageControl(GL_DONT_CARE, GL_DONT_CARE, GL_DEBUG_SEVERITY_HIGH, 0, nullptr, GL_TRUE); mFunctions->debugMessageControl(GL_DONT_CARE, GL_DONT_CARE, GL_DEBUG_SEVERITY_MEDIUM, 0, nullptr, GL_TRUE); mFunctions->debugMessageControl(GL_DONT_CARE, GL_DONT_CARE, GL_DEBUG_SEVERITY_LOW, 0, nullptr, GL_FALSE); mFunctions->debugMessageControl(GL_DONT_CARE, GL_DONT_CARE, GL_DEBUG_SEVERITY_NOTIFICATION, 0, nullptr, GL_FALSE); mFunctions->debugMessageCallback(&LogGLDebugMessage, nullptr); } if (mFeatures.initializeCurrentVertexAttributes.enabled) { GLint maxVertexAttribs = 0; mFunctions->getIntegerv(GL_MAX_VERTEX_ATTRIBS, &maxVertexAttribs); for (GLint i = 0; i < maxVertexAttribs; ++i) { mFunctions->vertexAttrib4f(i, 0.0f, 0.0f, 0.0f, 1.0f); } } if (hasNativeParallelCompile() && !mNativeParallelCompileEnabled) { SetMaxShaderCompilerThreads(mFunctions.get(), 0xffffffff); mNativeParallelCompileEnabled = true; } } RendererGL::~RendererGL() { SafeDelete(mBlitter); SafeDelete(mMultiviewClearer); SafeDelete(mStateManager); std::lock_guard lock(mWorkerMutex); ASSERT(mCurrentWorkerContexts.empty()); mWorkerContextPool.clear(); } angle::Result RendererGL::flush() { if (!mWorkDoneSinceLastFlush && !mNeedsFlushBeforeDeleteTextures) { return angle::Result::Continue; } mFunctions->flush(); mNeedsFlushBeforeDeleteTextures = false; mWorkDoneSinceLastFlush = false; return angle::Result::Continue; } angle::Result RendererGL::finish() { if (mFeatures.finishDoesNotCauseQueriesToBeAvailable.enabled && mUseDebugOutput) { mFunctions->enable(GL_DEBUG_OUTPUT_SYNCHRONOUS); } mFunctions->finish(); mNeedsFlushBeforeDeleteTextures = false; mWorkDoneSinceLastFlush = false; if (mFeatures.finishDoesNotCauseQueriesToBeAvailable.enabled && mUseDebugOutput) { mFunctions->disable(GL_DEBUG_OUTPUT_SYNCHRONOUS); } return angle::Result::Continue; } gl::GraphicsResetStatus RendererGL::getResetStatus() { return gl::FromGLenum(mFunctions->getGraphicsResetStatus()); } void RendererGL::insertEventMarker(GLsizei length, const char *marker) {} void RendererGL::pushGroupMarker(GLsizei length, const char *marker) {} void RendererGL::popGroupMarker() {} void RendererGL::pushDebugGroup(GLenum source, GLuint id, const std::string &message) {} void RendererGL::popDebugGroup() {} const gl::Version &RendererGL::getMaxSupportedESVersion() const { // Force generation of caps getNativeCaps(); return mMaxSupportedESVersion; } void RendererGL::generateCaps(gl::Caps *outCaps, gl::TextureCapsMap *outTextureCaps, gl::Extensions *outExtensions, gl::Limitations *outLimitations) const { nativegl_gl::GenerateCaps(mFunctions.get(), mFeatures, outCaps, outTextureCaps, outExtensions, outLimitations, &mMaxSupportedESVersion, &mMultiviewImplementationType); } GLint RendererGL::getGPUDisjoint() { // TODO(ewell): On GLES backends we should find a way to reliably query disjoint events return 0; } GLint64 RendererGL::getTimestamp() { GLint64 result = 0; mFunctions->getInteger64v(GL_TIMESTAMP, &result); return result; } void RendererGL::ensureCapsInitialized() const { if (!mCapsInitialized) { generateCaps(&mNativeCaps, &mNativeTextureCaps, &mNativeExtensions, &mNativeLimitations); mCapsInitialized = true; } } const gl::Caps &RendererGL::getNativeCaps() const { ensureCapsInitialized(); return mNativeCaps; } const gl::TextureCapsMap &RendererGL::getNativeTextureCaps() const { ensureCapsInitialized(); return mNativeTextureCaps; } const gl::Extensions &RendererGL::getNativeExtensions() const { ensureCapsInitialized(); return mNativeExtensions; } const gl::Limitations &RendererGL::getNativeLimitations() const { ensureCapsInitialized(); return mNativeLimitations; } MultiviewImplementationTypeGL RendererGL::getMultiviewImplementationType() const { ensureCapsInitialized(); return mMultiviewImplementationType; } void RendererGL::initializeFrontendFeatures(angle::FrontendFeatures *features) const { ensureCapsInitialized(); nativegl_gl::InitializeFrontendFeatures(mFunctions.get(), features); } angle::Result RendererGL::dispatchCompute(const gl::Context *context, GLuint numGroupsX, GLuint numGroupsY, GLuint numGroupsZ) { mFunctions->dispatchCompute(numGroupsX, numGroupsY, numGroupsZ); mWorkDoneSinceLastFlush = true; return angle::Result::Continue; } angle::Result RendererGL::dispatchComputeIndirect(const gl::Context *context, GLintptr indirect) { mFunctions->dispatchComputeIndirect(indirect); mWorkDoneSinceLastFlush = true; return angle::Result::Continue; } angle::Result RendererGL::memoryBarrier(GLbitfield barriers) { mFunctions->memoryBarrier(barriers); mWorkDoneSinceLastFlush = true; return angle::Result::Continue; } angle::Result RendererGL::memoryBarrierByRegion(GLbitfield barriers) { mFunctions->memoryBarrierByRegion(barriers); mWorkDoneSinceLastFlush = true; return angle::Result::Continue; } bool RendererGL::bindWorkerContext(std::string *infoLog) { if (mFeatures.disableWorkerContexts.enabled) { return false; } std::thread::id threadID = std::this_thread::get_id(); std::lock_guard lock(mWorkerMutex); std::unique_ptr workerContext; if (!mWorkerContextPool.empty()) { auto it = mWorkerContextPool.begin(); workerContext = std::move(*it); mWorkerContextPool.erase(it); } else { WorkerContext *newContext = createWorkerContext(infoLog); if (newContext == nullptr) { return false; } workerContext.reset(newContext); } if (!workerContext->makeCurrent()) { mWorkerContextPool.push_back(std::move(workerContext)); return false; } mCurrentWorkerContexts[threadID] = std::move(workerContext); return true; } void RendererGL::unbindWorkerContext() { std::thread::id threadID = std::this_thread::get_id(); std::lock_guard lock(mWorkerMutex); auto it = mCurrentWorkerContexts.find(threadID); ASSERT(it != mCurrentWorkerContexts.end()); (*it).second->unmakeCurrent(); mWorkerContextPool.push_back(std::move((*it).second)); mCurrentWorkerContexts.erase(it); } unsigned int RendererGL::getMaxWorkerContexts() { // No more than 16 worker contexts. return std::min(16u, std::thread::hardware_concurrency()); } bool RendererGL::hasNativeParallelCompile() { if (mFeatures.disableNativeParallelCompile.enabled) { return false; } return mFunctions->maxShaderCompilerThreadsKHR != nullptr || mFunctions->maxShaderCompilerThreadsARB != nullptr; } void RendererGL::setMaxShaderCompilerThreads(GLuint count) { if (hasNativeParallelCompile()) { SetMaxShaderCompilerThreads(mFunctions.get(), count); } } void RendererGL::setNeedsFlushBeforeDeleteTextures() { mNeedsFlushBeforeDeleteTextures = true; } void RendererGL::markWorkSubmitted() { mWorkDoneSinceLastFlush = true; } void RendererGL::flushIfNecessaryBeforeDeleteTextures() { if (mNeedsFlushBeforeDeleteTextures) { (void)flush(); } } ScopedWorkerContextGL::ScopedWorkerContextGL(RendererGL *renderer, std::string *infoLog) : mRenderer(renderer) { mValid = mRenderer->bindWorkerContext(infoLog); } ScopedWorkerContextGL::~ScopedWorkerContextGL() { if (mValid) { mRenderer->unbindWorkerContext(); } } bool ScopedWorkerContextGL::operator()() const { return mValid; } void RendererGL::handleGPUSwitch() { nativegl_gl::ReInitializeFeaturesAtGPUSwitch(mFunctions.get(), &mFeatures); } } // namespace rx