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