1 // Copyright (c) 2016 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "gpu/gles2_conform_support/egl/thread_state.h"
6
7 #include "base/at_exit.h"
8 #include "base/command_line.h"
9 #include "base/environment.h"
10 #include "base/lazy_instance.h"
11 #include "base/strings/string_split.h"
12 #include "base/strings/string_util.h"
13 #include "base/strings/utf_string_conversions.h"
14 #include "gpu/command_buffer/client/gles2_lib.h"
15 #include "gpu/command_buffer/common/thread_local.h"
16 #include "gpu/config/gpu_info_collector.h"
17 #include "gpu/config/gpu_preferences.h"
18 #include "gpu/config/gpu_util.h"
19 #include "gpu/gles2_conform_support/egl/context.h"
20 #include "gpu/gles2_conform_support/egl/display.h"
21 #include "gpu/gles2_conform_support/egl/surface.h"
22 #include "gpu/gles2_conform_support/egl/test_support.h"
23 #include "ui/gl/gl_context.h"
24 #include "ui/gl/gl_surface.h"
25 #include "ui/gl/gl_switches.h"
26 #include "ui/gl/init/gl_factory.h"
27
28 // Thread local key for ThreadState instance. Accessed when holding g_egl_lock
29 // only, since the initialization can not be Guaranteed otherwise. Not in
30 // anonymous namespace due to Mac OS X 10.6 linker. See gles2_lib.cc.
31 static gpu::ThreadLocalKey g_egl_thread_state_key;
32
33 namespace {
34 base::LazyInstance<base::Lock>::Leaky g_egl_lock;
35 int g_egl_active_thread_count;
36
37 egl::Display* g_egl_default_display;
38
39 #if defined(COMMAND_BUFFER_GLES_LIB_SUPPORT_ONLY)
40 // egl::Display is used for comformance tests and command_buffer_gles. We only
41 // need the exit manager for the command_buffer_gles library.
42 base::AtExitManager* g_exit_manager;
43 #endif
44 } // namespace
45
46 namespace egl {
47
Get()48 egl::ThreadState* ThreadState::Get() {
49 base::AutoLock lock(g_egl_lock.Get());
50 if (g_egl_active_thread_count == 0) {
51 #if defined(COMMAND_BUFFER_GLES_LIB_SUPPORT_ONLY)
52 #if defined(COMPONENT_BUILD)
53 if (!g_command_buffer_gles_has_atexit_manager)
54 g_exit_manager = new base::AtExitManager;
55 #else
56 g_exit_manager = new base::AtExitManager;
57 #endif
58 #endif
59 gles2::Initialize();
60
61 if (gl::GetGLImplementation() == gl::kGLImplementationNone) {
62 base::CommandLine::StringVector argv;
63 std::unique_ptr<base::Environment> env(base::Environment::Create());
64 std::string env_string;
65 env->GetVar("CHROME_COMMAND_BUFFER_GLES2_ARGS", &env_string);
66 #if defined(OS_WIN)
67 argv = base::SplitString(base::UTF8ToUTF16(env_string),
68 base::kWhitespaceUTF16, base::TRIM_WHITESPACE,
69 base::SPLIT_WANT_NONEMPTY);
70 argv.insert(argv.begin(), base::UTF8ToUTF16("dummy"));
71 #else
72 argv =
73 base::SplitString(env_string, base::kWhitespaceASCII,
74 base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
75 argv.insert(argv.begin(), "dummy");
76 #endif
77 base::CommandLine::Init(0, nullptr);
78 base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
79 // Need to call both Init and InitFromArgv, since Windows does not use
80 // argc, argv in CommandLine::Init(argc, argv).
81 command_line->InitFromArgv(argv);
82 gl::init::InitializeGLNoExtensionsOneOff(/*init_bindings*/ true);
83 gpu::GpuFeatureInfo gpu_feature_info;
84 if (!command_line->HasSwitch(switches::kDisableGpuDriverBugWorkarounds)) {
85 gpu::GPUInfo gpu_info;
86 gpu::CollectGraphicsInfoForTesting(&gpu_info);
87 gpu_feature_info = gpu::ComputeGpuFeatureInfo(
88 gpu_info, gpu::GpuPreferences(), command_line, nullptr);
89 Context::SetPlatformGpuFeatureInfo(gpu_feature_info);
90 }
91
92 gl::init::SetDisabledExtensionsPlatform(
93 gpu_feature_info.disabled_extensions);
94 gl::init::InitializeExtensionSettingsOneOffPlatform();
95 }
96
97 g_egl_default_display = new egl::Display();
98 g_egl_thread_state_key = gpu::ThreadLocalAlloc();
99 }
100 egl::ThreadState* thread_state = static_cast<egl::ThreadState*>(
101 gpu::ThreadLocalGetValue(g_egl_thread_state_key));
102 if (!thread_state) {
103 thread_state = new egl::ThreadState;
104 gpu::ThreadLocalSetValue(g_egl_thread_state_key, thread_state);
105 ++g_egl_active_thread_count;
106 }
107 return thread_state;
108 }
109
ReleaseThread()110 void ThreadState::ReleaseThread() {
111 base::AutoLock lock(g_egl_lock.Get());
112 if (g_egl_active_thread_count == 0)
113 return;
114
115 egl::ThreadState* thread_state = static_cast<egl::ThreadState*>(
116 gpu::ThreadLocalGetValue(g_egl_thread_state_key));
117 if (!thread_state)
118 return;
119
120 --g_egl_active_thread_count;
121 if (g_egl_active_thread_count > 0) {
122 g_egl_default_display->ReleaseCurrent(thread_state);
123 delete thread_state;
124 } else {
125 gpu::ThreadLocalFree(g_egl_thread_state_key);
126
127 // First delete the display object, so that it drops the possible refs to
128 // current context.
129 delete g_egl_default_display;
130 g_egl_default_display = nullptr;
131
132 // We can use Surface and Context without lock, since there's no threads
133 // left anymore. Destroy the current context explicitly, in an attempt to
134 // reduce the number of error messages abandoned context would produce.
135 if (thread_state->current_context()) {
136 Context::MakeCurrent(thread_state->current_context(),
137 thread_state->current_surface(), nullptr, nullptr);
138 }
139 delete thread_state;
140
141 gles2::Terminate();
142 #if defined(COMMAND_BUFFER_GLES_LIB_SUPPORT_ONLY)
143 #if defined(COMPONENT_BUILD)
144 if (g_command_buffer_gles_has_atexit_manager)
145 delete g_exit_manager;
146 #else
147 delete g_exit_manager;
148 #endif
149 g_exit_manager = nullptr;
150 #endif
151 }
152 }
153
ThreadState()154 ThreadState::ThreadState() : error_code_(EGL_SUCCESS) {}
155
156 ThreadState::~ThreadState() = default;
157
ConsumeErrorCode()158 EGLint ThreadState::ConsumeErrorCode() {
159 EGLint current_error_code = error_code_;
160 error_code_ = EGL_SUCCESS;
161 return current_error_code;
162 }
163
GetDisplay(EGLDisplay dpy)164 Display* ThreadState::GetDisplay(EGLDisplay dpy) {
165 if (dpy == g_egl_default_display)
166 return g_egl_default_display;
167 return nullptr;
168 }
169
GetDefaultDisplay()170 Display* ThreadState::GetDefaultDisplay() {
171 return g_egl_default_display;
172 }
173
SetCurrent(Surface * surface,Context * context)174 void ThreadState::SetCurrent(Surface* surface, Context* context) {
175 DCHECK((surface == nullptr) == (context == nullptr));
176 if (current_context_) {
177 current_context_->set_is_current_in_some_thread(false);
178 current_surface_->set_is_current_in_some_thread(false);
179 }
180 current_surface_ = surface;
181 current_context_ = context;
182 if (current_context_) {
183 current_context_->set_is_current_in_some_thread(true);
184 current_surface_->set_is_current_in_some_thread(true);
185 }
186 }
187
AutoCurrentContextRestore(ThreadState * thread_state)188 ThreadState::AutoCurrentContextRestore::AutoCurrentContextRestore(
189 ThreadState* thread_state)
190 : thread_state_(thread_state) {}
191
~AutoCurrentContextRestore()192 ThreadState::AutoCurrentContextRestore::~AutoCurrentContextRestore() {
193 if (Context* current_context = thread_state_->current_context()) {
194 current_context->ApplyCurrentContext(
195 thread_state_->current_surface()->gl_surface());
196 } else {
197 Context::ApplyContextReleased();
198 }
199 }
200
SetCurrent(Surface * surface,Context * context)201 void ThreadState::AutoCurrentContextRestore::SetCurrent(Surface* surface,
202 Context* context) {
203 thread_state_->SetCurrent(surface, context);
204 }
205
206 } // namespace egl
207