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