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