// Copyright (c) 2012 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "ui/gl/gl_context.h" #include #include "base/bind.h" #include "base/cancelable_callback.h" #include "base/command_line.h" #include "base/lazy_instance.h" #include "base/logging.h" #include "base/memory/ptr_util.h" #include "base/strings/string_util.h" #include "base/threading/thread_local.h" #include "ui/gl/gl_bindings.h" #include "ui/gl/gl_gl_api_implementation.h" #include "ui/gl/gl_implementation.h" #include "ui/gl/gl_surface.h" #include "ui/gl/gl_switches.h" #include "ui/gl/gl_version_info.h" #include "ui/gl/gpu_timing.h" namespace gl { namespace { base::LazyInstance>::Leaky current_context_ = LAZY_INSTANCE_INITIALIZER; base::LazyInstance>::Leaky current_real_context_ = LAZY_INSTANCE_INITIALIZER; } // namespace // static base::subtle::Atomic32 GLContext::total_gl_contexts_ = 0; // static bool GLContext::switchable_gpus_supported_ = false; // static GpuPreference GLContext::forced_gpu_preference_ = GpuPreferenceNone; GLContext::ScopedReleaseCurrent::ScopedReleaseCurrent() : canceled_(false) {} GLContext::ScopedReleaseCurrent::~ScopedReleaseCurrent() { if (!canceled_ && GetCurrent()) { GetCurrent()->ReleaseCurrent(nullptr); } } void GLContext::ScopedReleaseCurrent::Cancel() { canceled_ = true; } GLContext::GLContext(GLShareGroup* share_group) : share_group_(share_group) { if (!share_group_.get()) share_group_ = new gl::GLShareGroup(); share_group_->AddContext(this); base::subtle::NoBarrier_AtomicIncrement(&total_gl_contexts_, 1); } GLContext::~GLContext() { share_group_->RemoveContext(this); if (GetCurrent() == this) { SetCurrent(nullptr); SetCurrentGL(nullptr); } base::subtle::Atomic32 after_value = base::subtle::NoBarrier_AtomicIncrement(&total_gl_contexts_, -1); DCHECK(after_value >= 0); } // static int32_t GLContext::TotalGLContexts() { return static_cast( base::subtle::NoBarrier_Load(&total_gl_contexts_)); } // static bool GLContext::SwitchableGPUsSupported() { return switchable_gpus_supported_; } // static void GLContext::SetSwitchableGPUsSupported() { DCHECK(!switchable_gpus_supported_); switchable_gpus_supported_ = true; } // static void GLContext::SetForcedGpuPreference(GpuPreference gpu_preference) { DCHECK_EQ(GpuPreferenceNone, forced_gpu_preference_); forced_gpu_preference_ = gpu_preference; } // static GpuPreference GLContext::AdjustGpuPreference(GpuPreference gpu_preference) { switch (forced_gpu_preference_) { case GpuPreferenceNone: return gpu_preference; case PreferIntegratedGpu: case PreferDiscreteGpu: return forced_gpu_preference_; default: NOTREACHED(); return GpuPreferenceNone; } } GLApi* GLContext::CreateGLApi(DriverGL* driver) { real_gl_api_ = new RealGLApi; real_gl_api_->set_gl_workarounds(gl_workarounds_); real_gl_api_->SetDisabledExtensions(disabled_gl_extensions_); real_gl_api_->Initialize(driver); return real_gl_api_; } void GLContext::SetSafeToForceGpuSwitch() { } bool GLContext::ForceGpuSwitchIfNeeded() { return true; } void GLContext::SetUnbindFboOnMakeCurrent() { NOTIMPLEMENTED(); } std::string GLContext::GetGLVersion() { DCHECK(IsCurrent(nullptr)); DCHECK(gl_api_ != nullptr); const char* version = reinterpret_cast(gl_api_->glGetStringFn(GL_VERSION)); return std::string(version ? version : ""); } std::string GLContext::GetGLRenderer() { DCHECK(IsCurrent(nullptr)); DCHECK(gl_api_ != nullptr); const char* renderer = reinterpret_cast(gl_api_->glGetStringFn(GL_RENDERER)); return std::string(renderer ? renderer : ""); } YUVToRGBConverter* GLContext::GetYUVToRGBConverter( const gfx::ColorSpace& color_space) { return nullptr; } CurrentGL* GLContext::GetCurrentGL() { if (!static_bindings_initialized_) { driver_gl_.reset(new DriverGL); driver_gl_->InitializeStaticBindings(); gl_api_.reset(CreateGLApi(driver_gl_.get())); GLApi* final_api = gl_api_.get(); if (base::CommandLine::ForCurrentProcess()->HasSwitch( switches::kEnableGPUServiceTracing)) { trace_gl_api_.reset(new TraceGLApi(final_api)); final_api = trace_gl_api_.get(); } if (GetDebugGLBindingsInitializedGL()) { debug_gl_api_.reset(new DebugGLApi(final_api)); final_api = debug_gl_api_.get(); } current_gl_.reset(new CurrentGL); current_gl_->Driver = driver_gl_.get(); current_gl_->Api = final_api; current_gl_->Version = version_info_.get(); static_bindings_initialized_ = true; } return current_gl_.get(); } void GLContext::ReinitializeDynamicBindings() { DCHECK(IsCurrent(nullptr)); dynamic_bindings_initialized_ = false; ResetExtensions(); InitializeDynamicBindings(); } void GLContext::ForceReleaseVirtuallyCurrent() { NOTREACHED(); } bool GLContext::HasExtension(const char* name) { return gl::HasExtension(GetExtensions(), name); } const GLVersionInfo* GLContext::GetVersionInfo() { if (!version_info_) { version_info_ = GenerateGLVersionInfo(); // current_gl_ may be null for virtual contexts if (current_gl_) { current_gl_->Version = version_info_.get(); } } return version_info_.get(); } GLShareGroup* GLContext::share_group() { return share_group_.get(); } bool GLContext::LosesAllContextsOnContextLost() { switch (GetGLImplementation()) { case kGLImplementationDesktopGL: return false; case kGLImplementationEGLGLES2: case kGLImplementationSwiftShaderGL: return true; case kGLImplementationOSMesaGL: case kGLImplementationAppleGL: return false; case kGLImplementationMockGL: case kGLImplementationStubGL: return false; default: NOTREACHED(); return true; } } GLContext* GLContext::GetCurrent() { return current_context_.Pointer()->Get(); } GLContext* GLContext::GetRealCurrent() { return current_real_context_.Pointer()->Get(); } std::unique_ptr GLContext::GenerateGLVersionInfo() { return base::MakeUnique( GetGLVersion().c_str(), GetGLRenderer().c_str(), GetExtensions()); } void GLContext::SetCurrent(GLSurface* surface) { current_context_.Pointer()->Set(surface ? this : nullptr); GLSurface::SetCurrent(surface); // Leave the real GL api current so that unit tests work correctly. // TODO(sievers): Remove this, but needs all gpu_unittest classes // to create and make current a context. if (!surface && GetGLImplementation() != kGLImplementationMockGL && GetGLImplementation() != kGLImplementationStubGL) { SetCurrentGL(nullptr); } } void GLContext::SetGLWorkarounds(const GLWorkarounds& workarounds) { DCHECK(!real_gl_api_); gl_workarounds_ = workarounds; } void GLContext::SetDisabledGLExtensions( const std::string& disabled_extensions) { DCHECK(!real_gl_api_); disabled_gl_extensions_ = disabled_extensions; } GLStateRestorer* GLContext::GetGLStateRestorer() { return state_restorer_.get(); } void GLContext::SetGLStateRestorer(GLStateRestorer* state_restorer) { state_restorer_ = base::WrapUnique(state_restorer); } void GLContext::SetSwapInterval(int interval) { if (swap_interval_ == interval) return; swap_interval_ = interval; OnSetSwapInterval(force_swap_interval_zero_ ? 0 : swap_interval_); } void GLContext::ForceSwapIntervalZero(bool force) { if (force_swap_interval_zero_ == force) return; force_swap_interval_zero_ = force; OnSetSwapInterval(force_swap_interval_zero_ ? 0 : swap_interval_); } bool GLContext::WasAllocatedUsingRobustnessExtension() { return false; } void GLContext::InitializeDynamicBindings() { DCHECK(IsCurrent(nullptr)); DCHECK(static_bindings_initialized_); if (!dynamic_bindings_initialized_) { if (real_gl_api_) { // This is called everytime DoRequestExtensionCHROMIUM() is called in // passthrough command buffer. So the underlying ANGLE driver will have // different GL extensions, therefore we need to clear the cache and // recompute on demand later. real_gl_api_->ClearCachedGLExtensions(); real_gl_api_->set_version(GenerateGLVersionInfo()); } driver_gl_->InitializeDynamicBindings(GetVersionInfo(), GetExtensions()); dynamic_bindings_initialized_ = true; } } bool GLContext::MakeVirtuallyCurrent( GLContext* virtual_context, GLSurface* surface) { if (!ForceGpuSwitchIfNeeded()) return false; bool switched_real_contexts = GLContext::GetRealCurrent() != this; GLSurface* current_surface = GLSurface::GetCurrent(); if (switched_real_contexts || surface != current_surface) { // MakeCurrent 'lite' path that avoids potentially expensive MakeCurrent() // calls if the GLSurface uses the same underlying surface or renders to // an FBO. if (switched_real_contexts || !current_surface || !virtual_context->IsCurrent(surface)) { if (!MakeCurrent(surface)) { return false; } } } DCHECK_EQ(this, GLContext::GetRealCurrent()); DCHECK(IsCurrent(NULL)); DCHECK(virtual_context->IsCurrent(surface)); if (switched_real_contexts || virtual_context != current_virtual_context_) { #if DCHECK_IS_ON() GLenum error = glGetError(); // Accepting a context loss error here enables using debug mode to work on // context loss handling in virtual context mode. // There should be no other errors from the previous context leaking into // the new context. DCHECK(error == GL_NO_ERROR || error == GL_CONTEXT_LOST_KHR) << "GL error was: " << error; #endif // Set all state that is different from the real state if (virtual_context->GetGLStateRestorer()->IsInitialized()) { GLStateRestorer* virtual_state = virtual_context->GetGLStateRestorer(); GLStateRestorer* current_state = current_virtual_context_ ? current_virtual_context_->GetGLStateRestorer() : nullptr; if (current_state) current_state->PauseQueries(); virtual_state->ResumeQueries(); virtual_state->RestoreState( (current_state && !switched_real_contexts) ? current_state : NULL); } current_virtual_context_ = virtual_context; } virtual_context->SetCurrent(surface); if (!surface->OnMakeCurrent(virtual_context)) { LOG(ERROR) << "Could not make GLSurface current."; return false; } return true; } void GLContext::OnReleaseVirtuallyCurrent(GLContext* virtual_context) { if (current_virtual_context_ == virtual_context) current_virtual_context_ = nullptr; } void GLContext::BindGLApi() { SetCurrentGL(GetCurrentGL()); } GLContextReal::GLContextReal(GLShareGroup* share_group) : GLContext(share_group) {} scoped_refptr GLContextReal::CreateGPUTimingClient() { if (!gpu_timing_) { gpu_timing_.reset(GPUTiming::CreateGPUTiming(this)); } return gpu_timing_->CreateGPUTimingClient(); } const ExtensionSet& GLContextReal::GetExtensions() { DCHECK(IsCurrent(nullptr)); if (!extensions_initialized_) { SetExtensionsFromString(GetGLExtensionsFromCurrentContext(gl_api())); } return extensions_; } GLContextReal::~GLContextReal() { if (GetRealCurrent() == this) current_real_context_.Pointer()->Set(nullptr); } void GLContextReal::SetCurrent(GLSurface* surface) { GLContext::SetCurrent(surface); current_real_context_.Pointer()->Set(surface ? this : nullptr); } scoped_refptr InitializeGLContext(scoped_refptr context, GLSurface* compatible_surface, const GLContextAttribs& attribs) { if (!context->Initialize(compatible_surface, attribs)) return nullptr; return context; } void GLContextReal::SetExtensionsFromString(std::string extensions) { extensions_string_ = std::move(extensions); extensions_ = MakeExtensionSet(extensions_string_); extensions_initialized_ = true; } void GLContextReal::ResetExtensions() { extensions_.clear(); extensions_string_.clear(); extensions_initialized_ = false; } } // namespace gl