1 // Copyright 2016 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "ui/gl/init/gl_initializer.h"
6 
7 #include <dwmapi.h>
8 
9 #include "base/at_exit.h"
10 #include "base/base_paths.h"
11 #include "base/bind.h"
12 #include "base/files/file_path.h"
13 #include "base/logging.h"
14 #include "base/native_library.h"
15 #include "base/path_service.h"
16 #include "base/strings/stringprintf.h"
17 #include "base/threading/thread_restrictions.h"
18 #include "base/trace_event/trace_event.h"
19 #include "base/win/windows_version.h"
20 #include "ui/gl/buildflags.h"
21 #include "ui/gl/gl_bindings.h"
22 #include "ui/gl/gl_egl_api_implementation.h"
23 #include "ui/gl/gl_gl_api_implementation.h"
24 #include "ui/gl/gl_surface_egl.h"
25 #include "ui/gl/gl_surface_wgl.h"
26 #include "ui/gl/gl_wgl_api_implementation.h"
27 #include "ui/gl/vsync_provider_win.h"
28 
29 namespace gl {
30 namespace init {
31 
32 namespace {
33 
34 const wchar_t kD3DCompiler[] = L"D3DCompiler_47.dll";
35 
36 #if defined(NDEBUG) || !defined(TOOLKIT_QT)
37 const wchar_t kGLESv2Library[] = L"libglesv2.dll";
38 const wchar_t kEGLLibrary[] = L"libegl.dll";
39 #else
40 const wchar_t kGLESv2Library[] = L"libglesv2d.dll";
41 const wchar_t kEGLLibrary[] = L"libegld.dll";
42 #endif
43 
LoadD3DXLibrary(const base::FilePath & module_path,const base::FilePath::StringType & name)44 bool LoadD3DXLibrary(const base::FilePath& module_path,
45                      const base::FilePath::StringType& name) {
46   base::NativeLibrary library =
47       base::LoadNativeLibrary(base::FilePath(name), nullptr);
48   if (!library) {
49     library = base::LoadNativeLibrary(module_path.Append(name), nullptr);
50     if (!library) {
51       DVLOG(1) << name << " not found.";
52       return false;
53     }
54   }
55   return true;
56 }
57 
InitializeStaticEGLInternal(GLImplementation implementation)58 bool InitializeStaticEGLInternal(GLImplementation implementation) {
59   base::FilePath module_path;
60   if (!base::PathService::Get(base::DIR_MODULE, &module_path))
61     return false;
62 
63   // Attempt to load the D3DX shader compiler using the default search path
64   // and if that fails, using an absolute path. This is to ensure these DLLs
65   // are loaded before ANGLE is loaded in case they are not in the default
66   // search path.
67   LoadD3DXLibrary(module_path, kD3DCompiler);
68 
69   base::FilePath gles_path;
70   if (implementation == kGLImplementationSwiftShaderGL) {
71 #if BUILDFLAG(ENABLE_SWIFTSHADER)
72     gles_path = module_path.Append(L"swiftshader/");
73     // Preload library
74     LoadLibrary(L"ddraw.dll");
75 #else
76     return false;
77 #endif
78   } else {
79     gles_path = module_path;
80   }
81 
82   // Load libglesv2.dll before libegl.dll because the latter is dependent on
83   // the former and if there is another version of libglesv2.dll in the dll
84   // search path, it will get loaded instead.
85   base::NativeLibrary gles_library =
86       base::LoadNativeLibrary(gles_path.Append(kGLESv2Library), nullptr);
87   if (!gles_library) {
88     DVLOG(1) << kGLESv2Library << "not found";
89     return false;
90   }
91 
92   // When using EGL, first try eglGetProcAddress and then Windows
93   // GetProcAddress on both the EGL and GLES2 DLLs.
94   base::NativeLibrary egl_library =
95       base::LoadNativeLibrary(gles_path.Append(kEGLLibrary), nullptr);
96   if (!egl_library) {
97     DVLOG(1) << kEGLLibrary << "not found.";
98     base::UnloadNativeLibrary(gles_library);
99     return false;
100   }
101 
102   GLGetProcAddressProc get_proc_address =
103       reinterpret_cast<GLGetProcAddressProc>(
104           base::GetFunctionPointerFromNativeLibrary(egl_library,
105                                                     "eglGetProcAddress"));
106   if (!get_proc_address) {
107     LOG(ERROR) << "eglGetProcAddress not found.";
108     base::UnloadNativeLibrary(egl_library);
109     base::UnloadNativeLibrary(gles_library);
110     return false;
111   }
112 
113   SetGLGetProcAddressProc(get_proc_address);
114   AddGLNativeLibrary(egl_library);
115   AddGLNativeLibrary(gles_library);
116   SetGLImplementation(implementation);
117 
118   InitializeStaticGLBindingsGL();
119   InitializeStaticGLBindingsEGL();
120 
121   return true;
122 }
123 
InitializeStaticWGLInternal()124 bool InitializeStaticWGLInternal() {
125 #ifdef TOOLKIT_QT
126   const wchar_t *libraryName = L"opengl32.dll";
127   if (usingSoftwareDynamicGL())
128       libraryName = L"opengl32sw.dll";
129 
130   base::NativeLibrary library =
131       base::LoadNativeLibrary(base::FilePath(libraryName), nullptr);
132   if (!library) {
133     DVLOG(1) << libraryName << " not found";
134     return false;
135   }
136 #else
137   base::NativeLibrary library =
138       base::LoadNativeLibrary(base::FilePath(L"opengl32.dll"), nullptr);
139   if (!library) {
140     DVLOG(1) << "opengl32.dll not found";
141     return false;
142   }
143 #endif
144 
145   GLGetProcAddressProc get_proc_address =
146       reinterpret_cast<GLGetProcAddressProc>(
147           base::GetFunctionPointerFromNativeLibrary(library,
148                                                     "wglGetProcAddress"));
149   if (!get_proc_address) {
150     LOG(ERROR) << "wglGetProcAddress not found.";
151     base::UnloadNativeLibrary(library);
152     return false;
153   }
154 
155   SetGLGetProcAddressProc(get_proc_address);
156   AddGLNativeLibrary(library);
157   SetGLImplementation(kGLImplementationDesktopGL);
158 
159   // Initialize GL surface and get some functions needed for the context
160   // creation below.
161   if (!GLSurfaceWGL::InitializeOneOff()) {
162     LOG(ERROR) << "GLSurfaceWGL::InitializeOneOff failed.";
163     return false;
164   }
165   wglCreateContextProc wglCreateContextFn =
166       reinterpret_cast<wglCreateContextProc>(
167           GetGLProcAddress("wglCreateContext"));
168   wglDeleteContextProc wglDeleteContextFn =
169       reinterpret_cast<wglDeleteContextProc>(
170           GetGLProcAddress("wglDeleteContext"));
171   wglMakeCurrentProc wglMakeCurrentFn =
172       reinterpret_cast<wglMakeCurrentProc>(GetGLProcAddress("wglMakeCurrent"));
173 
174   // Create a temporary GL context to bind to entry points. This is needed
175   // because wglGetProcAddress is specified to return nullptr for all queries
176   // if a context is not current in MSDN documentation, and the static
177   // bindings may contain functions that need to be queried with
178   // wglGetProcAddress. OpenGL wiki further warns that other error values
179   // than nullptr could also be returned from wglGetProcAddress on some
180   // implementations, so we need to clear the WGL bindings and reinitialize
181   // them after the context creation.
182   HGLRC gl_context = wglCreateContextFn(GLSurfaceWGL::GetDisplayDC());
183   if (!gl_context) {
184     LOG(ERROR) << "Failed to create temporary context.";
185     return false;
186   }
187   if (!wglMakeCurrentFn(GLSurfaceWGL::GetDisplayDC(), gl_context)) {
188     LOG(ERROR) << "Failed to make temporary GL context current.";
189     wglDeleteContextFn(gl_context);
190     return false;
191   }
192 
193   InitializeStaticGLBindingsGL();
194   InitializeStaticGLBindingsWGL();
195 
196   wglMakeCurrent(nullptr, nullptr);
197   wglDeleteContext(gl_context);
198 
199   return true;
200 }
201 
202 }  // namespace
203 
204 #if !defined(TOOLKIT_QT)
InitializeGLOneOffPlatform()205 bool InitializeGLOneOffPlatform() {
206   VSyncProviderWin::InitializeOneOff();
207 
208   switch (GetGLImplementation()) {
209     case kGLImplementationDesktopGL:
210       if (!GLSurfaceWGL::InitializeOneOff()) {
211         LOG(ERROR) << "GLSurfaceWGL::InitializeOneOff failed.";
212         return false;
213       }
214       break;
215     case kGLImplementationSwiftShaderGL:
216     case kGLImplementationEGLANGLE:
217       if (!GLSurfaceEGL::InitializeOneOff(EGLDisplayPlatform(GetDC(nullptr)))) {
218         LOG(ERROR) << "GLSurfaceEGL::InitializeOneOff failed.";
219         return false;
220       }
221       break;
222     case kGLImplementationMockGL:
223     case kGLImplementationStubGL:
224       break;
225     default:
226       NOTREACHED();
227   }
228   return true;
229 }
230 #endif
231 
InitializeStaticGLBindings(GLImplementation implementation)232 bool InitializeStaticGLBindings(GLImplementation implementation) {
233   // Prevent reinitialization with a different implementation. Once the gpu
234   // unit tests have initialized with kGLImplementationMock, we don't want to
235   // later switch to another GL implementation.
236   DCHECK_EQ(kGLImplementationNone, GetGLImplementation());
237 
238   // Allow the main thread or another to initialize these bindings
239   // after instituting restrictions on I/O. Going forward they will
240   // likely be used in the browser process on most platforms. The
241   // one-time initialization cost is small, between 2 and 5 ms.
242   base::ThreadRestrictions::ScopedAllowIO allow_io;
243 
244   switch (implementation) {
245     case kGLImplementationSwiftShaderGL:
246     case kGLImplementationEGLANGLE:
247       return InitializeStaticEGLInternal(implementation);
248     case kGLImplementationDesktopGL:
249       return InitializeStaticWGLInternal();
250     case kGLImplementationMockGL:
251     case kGLImplementationStubGL:
252       SetGLImplementation(implementation);
253       InitializeStaticGLBindingsGL();
254       return true;
255     default:
256       NOTREACHED();
257   }
258 
259   return false;
260 }
261 
ShutdownGLPlatform()262 void ShutdownGLPlatform() {
263   GLSurfaceEGL::ShutdownOneOff();
264   ClearBindingsEGL();
265   ClearBindingsGL();
266   ClearBindingsWGL();
267 }
268 
269 }  // namespace init
270 }  // namespace gl
271