blob: 13c5f89d7d2d0111c365200b3aec8d889aa54d0a [file] [log] [blame]
// Copyright (c) 2016 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 "gpu/gles2_conform_support/egl/thread_state.h"
#include "base/at_exit.h"
#include "base/command_line.h"
#include "base/environment.h"
#include "base/lazy_instance.h"
#include "base/strings/string_split.h"
#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
#include "gpu/command_buffer/client/gles2_lib.h"
#include "gpu/command_buffer/common/thread_local.h"
#include "gpu/command_buffer/service/gpu_switches.h"
#include "gpu/config/gpu_info_collector.h"
#include "gpu/config/gpu_util.h"
#include "gpu/gles2_conform_support/egl/context.h"
#include "gpu/gles2_conform_support/egl/display.h"
#include "gpu/gles2_conform_support/egl/surface.h"
#include "gpu/gles2_conform_support/egl/test_support.h"
#include "ui/gl/gl_context.h"
#include "ui/gl/gl_surface.h"
#include "ui/gl/init/gl_factory.h"
// Thread local key for ThreadState instance. Accessed when holding g_egl_lock
// only, since the initialization can not be Guaranteed otherwise. Not in
// anonymous namespace due to Mac OS X 10.6 linker. See gles2_lib.cc.
static gpu::ThreadLocalKey g_egl_thread_state_key;
namespace {
base::LazyInstance<base::Lock>::Leaky g_egl_lock;
int g_egl_active_thread_count;
egl::Display* g_egl_default_display;
#if defined(COMMAND_BUFFER_GLES_LIB_SUPPORT_ONLY)
// egl::Display is used for comformance tests and command_buffer_gles. We only
// need the exit manager for the command_buffer_gles library.
base::AtExitManager* g_exit_manager;
#endif
} // namespace
namespace egl {
egl::ThreadState* ThreadState::Get() {
base::AutoLock lock(g_egl_lock.Get());
if (g_egl_active_thread_count == 0) {
#if defined(COMMAND_BUFFER_GLES_LIB_SUPPORT_ONLY)
#if defined(COMPONENT_BUILD)
if (!g_command_buffer_gles_has_atexit_manager)
g_exit_manager = new base::AtExitManager;
#else
g_exit_manager = new base::AtExitManager;
#endif
#endif
gles2::Initialize();
if (gfx::GetGLImplementation() == gfx::kGLImplementationNone) {
base::CommandLine::StringVector argv;
std::unique_ptr<base::Environment> env(base::Environment::Create());
std::string env_string;
env->GetVar("CHROME_COMMAND_BUFFER_GLES2_ARGS", &env_string);
#if defined(OS_WIN)
argv = base::SplitString(base::UTF8ToUTF16(env_string),
base::kWhitespaceUTF16, base::TRIM_WHITESPACE,
base::SPLIT_WANT_NONEMPTY);
argv.insert(argv.begin(), base::UTF8ToUTF16("dummy"));
#else
argv =
base::SplitString(env_string, base::kWhitespaceASCII,
base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
argv.insert(argv.begin(), "dummy");
#endif
base::CommandLine::Init(0, nullptr);
base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
// Need to call both Init and InitFromArgv, since Windows does not use
// argc, argv in CommandLine::Init(argc, argv).
command_line->InitFromArgv(argv);
if (!command_line->HasSwitch(switches::kDisableGpuDriverBugWorkarounds)) {
gpu::GPUInfo gpu_info;
gpu::CollectBasicGraphicsInfo(&gpu_info);
gpu::ApplyGpuDriverBugWorkarounds(gpu_info, command_line);
}
gl::init::InitializeGLOneOff();
}
g_egl_default_display = new egl::Display();
g_egl_thread_state_key = gpu::ThreadLocalAlloc();
}
egl::ThreadState* thread_state = static_cast<egl::ThreadState*>(
gpu::ThreadLocalGetValue(g_egl_thread_state_key));
if (!thread_state) {
thread_state = new egl::ThreadState;
gpu::ThreadLocalSetValue(g_egl_thread_state_key, thread_state);
++g_egl_active_thread_count;
}
return thread_state;
}
void ThreadState::ReleaseThread() {
base::AutoLock lock(g_egl_lock.Get());
if (g_egl_active_thread_count == 0)
return;
egl::ThreadState* thread_state = static_cast<egl::ThreadState*>(
gpu::ThreadLocalGetValue(g_egl_thread_state_key));
if (!thread_state)
return;
--g_egl_active_thread_count;
if (g_egl_active_thread_count > 0) {
g_egl_default_display->ReleaseCurrent(thread_state);
delete thread_state;
} else {
gpu::ThreadLocalFree(g_egl_thread_state_key);
// First delete the display object, so that it drops the possible refs to
// current context.
delete g_egl_default_display;
g_egl_default_display = nullptr;
// We can use Surface and Context without lock, since there's no threads
// left anymore. Destroy the current context explicitly, in an attempt to
// reduce the number of error messages abandoned context would produce.
if (thread_state->current_context()) {
Context::MakeCurrent(thread_state->current_context(),
thread_state->current_surface(), nullptr, nullptr);
}
delete thread_state;
gles2::Terminate();
#if defined(COMMAND_BUFFER_GLES_LIB_SUPPORT_ONLY)
#if defined(COMPONENT_BUILD)
if (g_command_buffer_gles_has_atexit_manager)
delete g_exit_manager;
#else
delete g_exit_manager;
#endif
g_exit_manager = nullptr;
#endif
}
}
ThreadState::ThreadState() : error_code_(EGL_SUCCESS) {}
ThreadState::~ThreadState() {}
EGLint ThreadState::ConsumeErrorCode() {
EGLint current_error_code = error_code_;
error_code_ = EGL_SUCCESS;
return current_error_code;
}
Display* ThreadState::GetDisplay(EGLDisplay dpy) {
if (dpy == g_egl_default_display)
return g_egl_default_display;
return nullptr;
}
Display* ThreadState::GetDefaultDisplay() {
return g_egl_default_display;
}
void ThreadState::SetCurrent(Surface* surface, Context* context) {
DCHECK((surface == nullptr) == (context == nullptr));
if (current_context_) {
current_context_->set_is_current_in_some_thread(false);
current_surface_->set_is_current_in_some_thread(false);
}
current_surface_ = surface;
current_context_ = context;
if (current_context_) {
current_context_->set_is_current_in_some_thread(true);
current_surface_->set_is_current_in_some_thread(true);
}
}
ThreadState::AutoCurrentContextRestore::AutoCurrentContextRestore(
ThreadState* thread_state)
: thread_state_(thread_state) {}
ThreadState::AutoCurrentContextRestore::~AutoCurrentContextRestore() {
if (Context* current_context = thread_state_->current_context()) {
current_context->ApplyCurrentContext(
thread_state_->current_surface()->gl_surface());
} else {
Context::ApplyContextReleased();
}
}
void ThreadState::AutoCurrentContextRestore::SetCurrent(Surface* surface,
Context* context) {
thread_state_->SetCurrent(surface, context);
}
} // namespace egl