1 //
2 // Copyright 2014 The ANGLE Project Authors. All rights reserved.
3 // Use of this source code is governed by a BSD-style license that can be
4 // found in the LICENSE file.
5 //
6
7 // global_state.cpp : Implements functions for querying the thread-local GL and EGL state.
8
9 #include "libGLESv2/global_state.h"
10
11 #include "common/debug.h"
12 #include "common/platform.h"
13 #include "common/system_utils.h"
14 #include "libANGLE/ErrorStrings.h"
15 #include "libANGLE/Thread.h"
16 #include "libGLESv2/resource.h"
17
18 #include <atomic>
19 #if defined(ANGLE_PLATFORM_APPLE)
20 # include <dispatch/dispatch.h>
21 #endif
22 namespace egl
23 {
24 namespace
25 {
26 ANGLE_REQUIRE_CONSTANT_INIT std::atomic<angle::GlobalMutex *> g_Mutex(nullptr);
27 static_assert(std::is_trivially_destructible<decltype(g_Mutex)>::value,
28 "global mutex is not trivially destructible");
29
30 ANGLE_REQUIRE_CONSTANT_INIT gl::Context *g_LastContext(nullptr);
31 static_assert(std::is_trivially_destructible<decltype(g_LastContext)>::value,
32 "global last context is not trivially destructible");
33
SetContextToAndroidOpenGLTLSSlot(gl::Context * value)34 void SetContextToAndroidOpenGLTLSSlot(gl::Context *value)
35 {
36 #if defined(ANGLE_PLATFORM_ANDROID)
37 if (angle::gUseAndroidOpenGLTlsSlot)
38 {
39 ANGLE_ANDROID_GET_GL_TLS()[angle::kAndroidOpenGLTlsSlot] = static_cast<void *>(value);
40 }
41 #endif
42 }
43
AllocateCurrentThread()44 Thread *AllocateCurrentThread()
45 {
46 Thread *thread;
47 {
48 // Global thread intentionally leaked
49 ANGLE_SCOPED_DISABLE_LSAN();
50 thread = new Thread();
51 #if defined(ANGLE_PLATFORM_APPLE)
52 SetCurrentThreadTLS(thread);
53 #else
54 gCurrentThread = thread;
55 #endif
56 }
57
58 // Initialize fast TLS slot
59 SetContextToAndroidOpenGLTLSSlot(nullptr);
60
61 #if defined(ANGLE_PLATFORM_APPLE)
62 gl::SetCurrentValidContextTLS(nullptr);
63 #else
64 gl::gCurrentValidContext = nullptr;
65 #endif
66
67 #if defined(ANGLE_PLATFORM_ANDROID)
68 static pthread_once_t keyOnce = PTHREAD_ONCE_INIT;
69 static TLSIndex gProcessCleanupTLSIndex = TLS_INVALID_INDEX;
70
71 // Create process cleanup TLS slot
72 auto CreateProcessCleanupTLSIndex = []() {
73 gProcessCleanupTLSIndex = CreateTLSIndex(angle::ProcessCleanupCallback);
74 };
75 pthread_once(&keyOnce, CreateProcessCleanupTLSIndex);
76 ASSERT(gProcessCleanupTLSIndex != TLS_INVALID_INDEX);
77
78 // Initialize process cleanup TLS slot
79 angle::gProcessCleanupRefCount++;
80 SetTLSValue(gProcessCleanupTLSIndex, thread);
81 #endif // ANGLE_PLATFORM_ANDROID
82
83 ASSERT(thread);
84 return thread;
85 }
86
AllocateMutex()87 void AllocateMutex()
88 {
89 if (g_Mutex == nullptr)
90 {
91 std::unique_ptr<angle::GlobalMutex> newMutex(new angle::GlobalMutex());
92 angle::GlobalMutex *expected = nullptr;
93 if (g_Mutex.compare_exchange_strong(expected, newMutex.get()))
94 {
95 newMutex.release();
96 }
97 }
98 }
99
100 } // anonymous namespace
101
102 #if defined(ANGLE_PLATFORM_APPLE)
103 // TODO(angleproject:6479): Due to a bug in Apple's dyld loader, `thread_local` will cause
104 // excessive memory use. Temporarily avoid it by using pthread's thread
105 // local storage instead.
106 // https://bugs.webkit.org/show_bug.cgi?id=228240
107
GetCurrentThreadTLSIndex()108 static TLSIndex GetCurrentThreadTLSIndex()
109 {
110 static TLSIndex CurrentThreadIndex = TLS_INVALID_INDEX;
111 static dispatch_once_t once;
112 dispatch_once(&once, ^{
113 ASSERT(CurrentThreadIndex == TLS_INVALID_INDEX);
114 CurrentThreadIndex = CreateTLSIndex(nullptr);
115 });
116 return CurrentThreadIndex;
117 }
GetCurrentThreadTLS()118 Thread *GetCurrentThreadTLS()
119 {
120 TLSIndex CurrentThreadIndex = GetCurrentThreadTLSIndex();
121 ASSERT(CurrentThreadIndex != TLS_INVALID_INDEX);
122 return static_cast<Thread *>(GetTLSValue(CurrentThreadIndex));
123 }
SetCurrentThreadTLS(Thread * thread)124 void SetCurrentThreadTLS(Thread *thread)
125 {
126 TLSIndex CurrentThreadIndex = GetCurrentThreadTLSIndex();
127 ASSERT(CurrentThreadIndex != TLS_INVALID_INDEX);
128 SetTLSValue(CurrentThreadIndex, thread);
129 }
130 #else
131 thread_local Thread *gCurrentThread = nullptr;
132 #endif
133
GetGlobalMutex()134 angle::GlobalMutex &GetGlobalMutex()
135 {
136 AllocateMutex();
137 return *g_Mutex;
138 }
139
GetGlobalLastContext()140 gl::Context *GetGlobalLastContext()
141 {
142 return g_LastContext;
143 }
144
SetGlobalLastContext(gl::Context * context)145 void SetGlobalLastContext(gl::Context *context)
146 {
147 g_LastContext = context;
148 }
149
150 // This function causes an MSAN false positive, which is muted. See https://crbug.com/1211047
151 // It also causes a flaky false positive in TSAN. http://crbug.com/1223970
GetCurrentThread()152 ANGLE_NO_SANITIZE_MEMORY ANGLE_NO_SANITIZE_THREAD Thread *GetCurrentThread()
153 {
154 #if defined(ANGLE_PLATFORM_APPLE)
155 Thread *current = GetCurrentThreadTLS();
156 #else
157 Thread *current = gCurrentThread;
158 #endif
159 return (current ? current : AllocateCurrentThread());
160 }
161
SetContextCurrent(Thread * thread,gl::Context * context)162 void SetContextCurrent(Thread *thread, gl::Context *context)
163 {
164 #if defined(ANGLE_PLATFORM_APPLE)
165 Thread *currentThread = GetCurrentThreadTLS();
166 #else
167 Thread *currentThread = gCurrentThread;
168 #endif
169 ASSERT(currentThread);
170 currentThread->setCurrent(context);
171 SetContextToAndroidOpenGLTLSSlot(context);
172
173 #if defined(ANGLE_PLATFORM_APPLE)
174 gl::SetCurrentValidContextTLS(context);
175 #else
176 gl::gCurrentValidContext = context;
177 #endif
178
179 #if defined(ANGLE_FORCE_CONTEXT_CHECK_EVERY_CALL)
180 DirtyContextIfNeeded(context);
181 #endif
182 }
183
ScopedSyncCurrentContextFromThread(egl::Thread * thread)184 ScopedSyncCurrentContextFromThread::ScopedSyncCurrentContextFromThread(egl::Thread *thread)
185 : mThread(thread)
186 {
187 ASSERT(mThread);
188 }
189
~ScopedSyncCurrentContextFromThread()190 ScopedSyncCurrentContextFromThread::~ScopedSyncCurrentContextFromThread()
191 {
192 SetContextCurrent(mThread, mThread->getContext());
193 }
194
195 } // namespace egl
196
197 namespace gl
198 {
GenerateContextLostErrorOnContext(Context * context)199 void GenerateContextLostErrorOnContext(Context *context)
200 {
201 if (context && context->isContextLost())
202 {
203 context->validationError(angle::EntryPoint::GLInvalid, GL_CONTEXT_LOST, err::kContextLost);
204 }
205 }
206
GenerateContextLostErrorOnCurrentGlobalContext()207 void GenerateContextLostErrorOnCurrentGlobalContext()
208 {
209 GenerateContextLostErrorOnContext(GetGlobalContext());
210 }
211 } // namespace gl
212
213 #if defined(ANGLE_PLATFORM_WINDOWS) && !defined(ANGLE_STATIC)
214 namespace egl
215 {
216
217 namespace
218 {
DeallocateCurrentThread()219 void DeallocateCurrentThread()
220 {
221 SafeDelete(gCurrentThread);
222 }
223
DeallocateMutex()224 void DeallocateMutex()
225 {
226 angle::GlobalMutex *mutex = g_Mutex.exchange(nullptr);
227 {
228 // Wait for the mutex to become released by other threads before deleting.
229 std::lock_guard<angle::GlobalMutex> lock(*mutex);
230 }
231 SafeDelete(mutex);
232 }
233
InitializeProcess()234 bool InitializeProcess()
235 {
236 EnsureDebugAllocated();
237 AllocateMutex();
238 return AllocateCurrentThread() != nullptr;
239 }
240
TerminateProcess()241 void TerminateProcess()
242 {
243 DeallocateDebug();
244 DeallocateMutex();
245 DeallocateCurrentThread();
246 }
247
248 } // anonymous namespace
249
250 } // namespace egl
251
252 namespace
253 {
254 // The following WaitForDebugger code is based on SwiftShader. See:
255 // https://cs.chromium.org/chromium/src/third_party/swiftshader/src/Vulkan/main.cpp
256 # if defined(ANGLE_ENABLE_ASSERTS) && !defined(ANGLE_ENABLE_WINDOWS_UWP)
DebuggerWaitDialogProc(HWND hwnd,UINT uMsg,WPARAM wParam,LPARAM lParam)257 INT_PTR CALLBACK DebuggerWaitDialogProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
258 {
259 RECT rect;
260
261 switch (uMsg)
262 {
263 case WM_INITDIALOG:
264 ::GetWindowRect(GetDesktopWindow(), &rect);
265 ::SetWindowPos(hwnd, HWND_TOP, rect.right / 2, rect.bottom / 2, 0, 0, SWP_NOSIZE);
266 ::SetTimer(hwnd, 1, 100, NULL);
267 return TRUE;
268 case WM_COMMAND:
269 if (LOWORD(wParam) == IDCANCEL)
270 {
271 ::EndDialog(hwnd, 0);
272 }
273 break;
274 case WM_TIMER:
275 if (angle::IsDebuggerAttached())
276 {
277 ::EndDialog(hwnd, 0);
278 }
279 }
280
281 return FALSE;
282 }
283
WaitForDebugger(HINSTANCE instance)284 void WaitForDebugger(HINSTANCE instance)
285 {
286 if (angle::IsDebuggerAttached())
287 return;
288
289 HRSRC dialog = ::FindResourceA(instance, MAKEINTRESOURCEA(IDD_DIALOG1), MAKEINTRESOURCEA(5));
290 if (!dialog)
291 {
292 printf("Error finding wait for debugger dialog. Error %lu.\n", ::GetLastError());
293 return;
294 }
295
296 DLGTEMPLATE *dialogTemplate = reinterpret_cast<DLGTEMPLATE *>(::LoadResource(instance, dialog));
297 ::DialogBoxIndirectA(instance, dialogTemplate, NULL, DebuggerWaitDialogProc);
298 }
299 # else
300 void WaitForDebugger(HINSTANCE instance) {}
301 # endif // defined(ANGLE_ENABLE_ASSERTS) && !defined(ANGLE_ENABLE_WINDOWS_UWP)
302 } // namespace
303
DllMain(HINSTANCE instance,DWORD reason,LPVOID)304 extern "C" BOOL WINAPI DllMain(HINSTANCE instance, DWORD reason, LPVOID)
305 {
306 switch (reason)
307 {
308 case DLL_PROCESS_ATTACH:
309 if (angle::GetEnvironmentVar("ANGLE_WAIT_FOR_DEBUGGER") == "1")
310 {
311 WaitForDebugger(instance);
312 }
313 return static_cast<BOOL>(egl::InitializeProcess());
314
315 case DLL_THREAD_ATTACH:
316 return static_cast<BOOL>(egl::AllocateCurrentThread() != nullptr);
317
318 case DLL_THREAD_DETACH:
319 egl::DeallocateCurrentThread();
320 break;
321
322 case DLL_PROCESS_DETACH:
323 egl::TerminateProcess();
324 break;
325 }
326
327 return TRUE;
328 }
329 #endif // defined(ANGLE_PLATFORM_WINDOWS) && !defined(ANGLE_STATIC)
330