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 
20 namespace egl
21 {
22 namespace
23 {
24 ANGLE_REQUIRE_CONSTANT_INIT std::atomic<angle::GlobalMutex *> g_Mutex(nullptr);
25 static_assert(std::is_trivially_destructible<decltype(g_Mutex)>::value,
26               "global mutex is not trivially destructible");
27 
28 ANGLE_REQUIRE_CONSTANT_INIT gl::Context *g_LastContext(nullptr);
29 static_assert(std::is_trivially_destructible<decltype(g_LastContext)>::value,
30               "global last context is not trivially destructible");
31 
SetContextToAndroidOpenGLTLSSlot(gl::Context * value)32 void SetContextToAndroidOpenGLTLSSlot(gl::Context *value)
33 {
34 #if defined(ANGLE_PLATFORM_ANDROID)
35     if (angle::gUseAndroidOpenGLTlsSlot)
36     {
37         ANGLE_ANDROID_GET_GL_TLS()[angle::kAndroidOpenGLTlsSlot] = static_cast<void *>(value);
38     }
39 #endif
40 }
41 
AllocateCurrentThread()42 Thread *AllocateCurrentThread()
43 {
44     {
45         // Global thread intentionally leaked
46         ANGLE_SCOPED_DISABLE_LSAN();
47         gCurrentThread = new Thread();
48     }
49 
50     // Initialize fast TLS slot
51     SetContextToAndroidOpenGLTLSSlot(nullptr);
52     gl::gCurrentValidContext = nullptr;
53 
54     return gCurrentThread;
55 }
56 
AllocateMutex()57 void AllocateMutex()
58 {
59     if (g_Mutex == nullptr)
60     {
61         std::unique_ptr<angle::GlobalMutex> newMutex(new angle::GlobalMutex());
62         angle::GlobalMutex *expected = nullptr;
63         if (g_Mutex.compare_exchange_strong(expected, newMutex.get()))
64         {
65             newMutex.release();
66         }
67     }
68 }
69 
70 }  // anonymous namespace
71 
72 thread_local Thread *gCurrentThread = nullptr;
73 
GetGlobalMutex()74 angle::GlobalMutex &GetGlobalMutex()
75 {
76     AllocateMutex();
77     return *g_Mutex;
78 }
79 
GetGlobalLastContext()80 gl::Context *GetGlobalLastContext()
81 {
82     return g_LastContext;
83 }
84 
SetGlobalLastContext(gl::Context * context)85 void SetGlobalLastContext(gl::Context *context)
86 {
87     g_LastContext = context;
88 }
89 
90 // This function causes an MSAN false positive, which is muted. See https://crbug.com/1211047
91 // It also causes a flaky false positive in TSAN. http://crbug.com/1223970
GetCurrentThread()92 ANGLE_NO_SANITIZE_MEMORY ANGLE_NO_SANITIZE_THREAD Thread *GetCurrentThread()
93 {
94     Thread *current = gCurrentThread;
95     return (current ? current : AllocateCurrentThread());
96 }
97 
SetContextCurrent(Thread * thread,gl::Context * context)98 void SetContextCurrent(Thread *thread, gl::Context *context)
99 {
100     ASSERT(gCurrentThread == thread);
101     SetContextToAndroidOpenGLTLSSlot(context);
102     gl::gCurrentValidContext = context;
103 #if defined(ANGLE_FORCE_CONTEXT_CHECK_EVERY_CALL)
104     DirtyContextIfNeeded(context);
105 #endif
106 }
107 
ScopedSyncCurrentContextFromThread(egl::Thread * thread)108 ScopedSyncCurrentContextFromThread::ScopedSyncCurrentContextFromThread(egl::Thread *thread)
109     : mThread(thread)
110 {
111     ASSERT(mThread);
112 }
113 
~ScopedSyncCurrentContextFromThread()114 ScopedSyncCurrentContextFromThread::~ScopedSyncCurrentContextFromThread()
115 {
116     SetContextCurrent(mThread, mThread->getContext());
117 }
118 
119 }  // namespace egl
120 
121 namespace gl
122 {
GenerateContextLostErrorOnContext(Context * context)123 void GenerateContextLostErrorOnContext(Context *context)
124 {
125     if (context && context->isContextLost())
126     {
127         context->validationError(GL_CONTEXT_LOST, err::kContextLost);
128     }
129 }
130 
GenerateContextLostErrorOnCurrentGlobalContext()131 void GenerateContextLostErrorOnCurrentGlobalContext()
132 {
133     GenerateContextLostErrorOnContext(GetGlobalContext());
134 }
135 }  // namespace gl
136 
137 #if defined(ANGLE_PLATFORM_WINDOWS) && !defined(ANGLE_STATIC)
138 namespace egl
139 {
140 
141 namespace
142 {
DeallocateCurrentThread()143 void DeallocateCurrentThread()
144 {
145     SafeDelete(gCurrentThread);
146 }
147 
DeallocateMutex()148 void DeallocateMutex()
149 {
150     angle::GlobalMutex *mutex = g_Mutex.exchange(nullptr);
151     {
152         // Wait for the mutex to become released by other threads before deleting.
153         std::lock_guard<angle::GlobalMutex> lock(*mutex);
154     }
155     SafeDelete(mutex);
156 }
157 
InitializeProcess()158 bool InitializeProcess()
159 {
160     EnsureDebugAllocated();
161     AllocateMutex();
162     return AllocateCurrentThread() != nullptr;
163 }
164 
TerminateProcess()165 void TerminateProcess()
166 {
167     DeallocateDebug();
168     DeallocateMutex();
169     DeallocateCurrentThread();
170 }
171 
172 }  // anonymous namespace
173 
174 }  // namespace egl
175 
176 namespace
177 {
178 // The following WaitForDebugger code is based on SwiftShader. See:
179 // https://cs.chromium.org/chromium/src/third_party/swiftshader/src/Vulkan/main.cpp
180 #    if defined(ANGLE_ENABLE_ASSERTS) && !defined(ANGLE_ENABLE_WINDOWS_UWP)
DebuggerWaitDialogProc(HWND hwnd,UINT uMsg,WPARAM wParam,LPARAM lParam)181 INT_PTR CALLBACK DebuggerWaitDialogProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
182 {
183     RECT rect;
184 
185     switch (uMsg)
186     {
187         case WM_INITDIALOG:
188             ::GetWindowRect(GetDesktopWindow(), &rect);
189             ::SetWindowPos(hwnd, HWND_TOP, rect.right / 2, rect.bottom / 2, 0, 0, SWP_NOSIZE);
190             ::SetTimer(hwnd, 1, 100, NULL);
191             return TRUE;
192         case WM_COMMAND:
193             if (LOWORD(wParam) == IDCANCEL)
194             {
195                 ::EndDialog(hwnd, 0);
196             }
197             break;
198         case WM_TIMER:
199             if (angle::IsDebuggerAttached())
200             {
201                 ::EndDialog(hwnd, 0);
202             }
203     }
204 
205     return FALSE;
206 }
207 
WaitForDebugger(HINSTANCE instance)208 void WaitForDebugger(HINSTANCE instance)
209 {
210     if (angle::IsDebuggerAttached())
211         return;
212 
213     HRSRC dialog = ::FindResourceA(instance, MAKEINTRESOURCEA(IDD_DIALOG1), MAKEINTRESOURCEA(5));
214     if (!dialog)
215     {
216         printf("Error finding wait for debugger dialog. Error %lu.\n", ::GetLastError());
217         return;
218     }
219 
220     DLGTEMPLATE *dialogTemplate = reinterpret_cast<DLGTEMPLATE *>(::LoadResource(instance, dialog));
221     ::DialogBoxIndirectA(instance, dialogTemplate, NULL, DebuggerWaitDialogProc);
222 }
223 #    else
224 void WaitForDebugger(HINSTANCE instance) {}
225 #    endif  // defined(ANGLE_ENABLE_ASSERTS) && !defined(ANGLE_ENABLE_WINDOWS_UWP)
226 }  // namespace
227 
DllMain(HINSTANCE instance,DWORD reason,LPVOID)228 extern "C" BOOL WINAPI DllMain(HINSTANCE instance, DWORD reason, LPVOID)
229 {
230     switch (reason)
231     {
232         case DLL_PROCESS_ATTACH:
233             if (angle::GetEnvironmentVar("ANGLE_WAIT_FOR_DEBUGGER") == "1")
234             {
235                 WaitForDebugger(instance);
236             }
237             return static_cast<BOOL>(egl::InitializeProcess());
238 
239         case DLL_THREAD_ATTACH:
240             return static_cast<BOOL>(egl::AllocateCurrentThread() != nullptr);
241 
242         case DLL_THREAD_DETACH:
243             egl::DeallocateCurrentThread();
244             break;
245 
246         case DLL_PROCESS_DETACH:
247             egl::TerminateProcess();
248             break;
249     }
250 
251     return TRUE;
252 }
253 #endif  // defined(ANGLE_PLATFORM_WINDOWS) && !defined(ANGLE_STATIC)
254