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