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