• 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 
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