1 /*
2 * Copyright 2012 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8 #include "tools/gpu/gl/angle/GLTestContext_angle.h"
9
10 #define EGL_EGL_PROTOTYPES 1
11
12 #include <EGL/egl.h>
13 #include <EGL/eglext.h>
14
15 #include "src/gpu/gl/GrGLDefines.h"
16 #include "src/gpu/gl/GrGLUtil.h"
17
18 #include "include/core/SkTime.h"
19 #include "include/gpu/gl/GrGLAssembleInterface.h"
20 #include "include/gpu/gl/GrGLInterface.h"
21 #include "src/core/SkTraceEvent.h"
22 #include "src/ports/SkOSLibrary.h"
23 #include "third_party/externals/angle2/include/platform/Platform.h"
24
25 #include <EGL/egl.h>
26
27 #define EGL_PLATFORM_ANGLE_ANGLE 0x3202
28 #define EGL_PLATFORM_ANGLE_TYPE_ANGLE 0x3203
29 #define EGL_PLATFORM_ANGLE_TYPE_D3D9_ANGLE 0x3207
30 #define EGL_PLATFORM_ANGLE_TYPE_D3D11_ANGLE 0x3208
31 #define EGL_PLATFORM_ANGLE_TYPE_OPENGL_ANGLE 0x320D
32
33 using sk_gpu_test::ANGLEBackend;
34 using sk_gpu_test::ANGLEContextVersion;
35
36 namespace {
37 struct Libs {
38 void* fGLLib;
39 void* fEGLLib;
40 };
41
context_restorer()42 std::function<void()> context_restorer() {
43 auto display = eglGetCurrentDisplay();
44 auto dsurface = eglGetCurrentSurface(EGL_DRAW);
45 auto rsurface = eglGetCurrentSurface(EGL_READ);
46 auto context = eglGetCurrentContext();
47 return [display, dsurface, rsurface, context] {
48 eglMakeCurrent(display, dsurface, rsurface, context);
49 };
50 }
51
angle_get_gl_proc(void * ctx,const char name[])52 static GrGLFuncPtr angle_get_gl_proc(void* ctx, const char name[]) {
53 const Libs* libs = reinterpret_cast<const Libs*>(ctx);
54 GrGLFuncPtr proc = (GrGLFuncPtr) GetProcedureAddress(libs->fGLLib, name);
55 if (proc) {
56 return proc;
57 }
58 proc = (GrGLFuncPtr) GetProcedureAddress(libs->fEGLLib, name);
59 if (proc) {
60 return proc;
61 }
62 return eglGetProcAddress(name);
63 }
64
get_angle_egl_display(void * nativeDisplay,ANGLEBackend type)65 void* get_angle_egl_display(void* nativeDisplay, ANGLEBackend type) {
66 PFNEGLGETPLATFORMDISPLAYEXTPROC eglGetPlatformDisplayEXT;
67 eglGetPlatformDisplayEXT =
68 (PFNEGLGETPLATFORMDISPLAYEXTPROC)eglGetProcAddress("eglGetPlatformDisplayEXT");
69
70 // We expect ANGLE to support this extension
71 if (!eglGetPlatformDisplayEXT) {
72 return EGL_NO_DISPLAY;
73 }
74
75 EGLint typeNum = 0;
76 switch (type) {
77 case ANGLEBackend::kD3D9:
78 typeNum = EGL_PLATFORM_ANGLE_TYPE_D3D9_ANGLE;
79 break;
80 case ANGLEBackend::kD3D11:
81 typeNum = EGL_PLATFORM_ANGLE_TYPE_D3D11_ANGLE;
82 break;
83 case ANGLEBackend::kOpenGL:
84 typeNum = EGL_PLATFORM_ANGLE_TYPE_OPENGL_ANGLE;
85 break;
86 }
87 const EGLint attribs[] = { EGL_PLATFORM_ANGLE_TYPE_ANGLE, typeNum, EGL_NONE };
88 return eglGetPlatformDisplayEXT(EGL_PLATFORM_ANGLE_ANGLE, nativeDisplay, attribs);
89 }
90
91 class ANGLEGLContext : public sk_gpu_test::GLTestContext {
92 public:
93 ANGLEGLContext(ANGLEBackend, ANGLEContextVersion, ANGLEGLContext* shareContext, void* display);
94 ~ANGLEGLContext() override;
95
96 GrEGLImage texture2DToEGLImage(GrGLuint texID) const override;
97 void destroyEGLImage(GrEGLImage) const override;
98 GrGLuint eglImageToExternalTexture(GrEGLImage) const override;
99 std::unique_ptr<sk_gpu_test::GLTestContext> makeNew() const override;
100
101 private:
102 void destroyGLContext();
103
104 void onPlatformMakeCurrent() const override;
105 std::function<void()> onPlatformGetAutoContextRestore() const override;
106 void onPlatformSwapBuffers() const override;
107 GrGLFuncPtr onPlatformGetProcAddress(const char* name) const override;
108
109 void* fContext;
110 void* fDisplay;
111 void* fSurface;
112 ANGLEBackend fType;
113 ANGLEContextVersion fVersion;
114
115 angle::ResetDisplayPlatformFunc fResetPlatform = nullptr;
116
117 PFNEGLCREATEIMAGEKHRPROC fCreateImage = nullptr;
118 PFNEGLDESTROYIMAGEKHRPROC fDestroyImage = nullptr;
119
120 #ifdef SK_BUILD_FOR_WIN
121 HWND fWindow;
122 HDC fDeviceContext;
123 static ATOM gWC;
124 #endif
125 };
126
127 #ifdef SK_BUILD_FOR_WIN
128 ATOM ANGLEGLContext::gWC = 0;
129
130 enum class IsWine { kUnknown, kNo, kYes };
131
is_wine()132 static IsWine is_wine() {
133 HMODULE ntdll = GetModuleHandle("ntdll.dll");
134 if (!ntdll) {
135 SkDebugf("No ntdll.dll on Windows?!\n");
136 return IsWine::kUnknown;
137 }
138 return GetProcAddress(ntdll, "wine_get_version") == nullptr ? IsWine::kNo : IsWine::kYes;
139 }
140
141 #endif
142
ANGLE_getTraceCategoryEnabledFlag(angle::PlatformMethods * platform,const char * category_group)143 static const unsigned char* ANGLE_getTraceCategoryEnabledFlag(angle::PlatformMethods* platform,
144 const char* category_group) {
145 return SkEventTracer::GetInstance()->getCategoryGroupEnabled(category_group);
146 }
147
ANGLE_addTraceEvent(angle::PlatformMethods * platform,char phase,const unsigned char * category_group_enabled,const char * name,unsigned long long id,double timestamp,int num_args,const char ** arg_names,const unsigned char * arg_types,const unsigned long long * arg_values,unsigned char flags)148 static angle::TraceEventHandle ANGLE_addTraceEvent(angle::PlatformMethods* platform,
149 char phase,
150 const unsigned char* category_group_enabled,
151 const char* name,
152 unsigned long long id,
153 double timestamp,
154 int num_args,
155 const char** arg_names,
156 const unsigned char* arg_types,
157 const unsigned long long* arg_values,
158 unsigned char flags) {
159 static_assert(sizeof(unsigned long long) == sizeof(uint64_t), "Non-64-bit trace event args!");
160 return SkEventTracer::GetInstance()->addTraceEvent(
161 phase, category_group_enabled, name, id, num_args, arg_names, arg_types,
162 reinterpret_cast<const uint64_t*>(arg_values), flags);
163 }
164
ANGLE_updateTraceEventDuration(angle::PlatformMethods * platform,const unsigned char * category_group_enabled,const char * name,angle::TraceEventHandle handle)165 static void ANGLE_updateTraceEventDuration(angle::PlatformMethods* platform,
166 const unsigned char* category_group_enabled,
167 const char* name,
168 angle::TraceEventHandle handle) {
169 SkEventTracer::GetInstance()->updateTraceEventDuration(category_group_enabled, name, handle);
170 }
171
ANGLE_monotonicallyIncreasingTime(angle::PlatformMethods * platform)172 static double ANGLE_monotonicallyIncreasingTime(angle::PlatformMethods* platform) {
173 return SkTime::GetSecs();
174 }
175
ANGLEGLContext(ANGLEBackend type,ANGLEContextVersion version,ANGLEGLContext * shareContext,void * display)176 ANGLEGLContext::ANGLEGLContext(ANGLEBackend type, ANGLEContextVersion version,
177 ANGLEGLContext* shareContext, void* display)
178 : fContext(EGL_NO_CONTEXT)
179 , fDisplay(display)
180 , fSurface(EGL_NO_SURFACE)
181 , fType(type)
182 , fVersion(version) {
183 #ifdef SK_BUILD_FOR_WIN
184 fWindow = nullptr;
185 fDeviceContext = nullptr;
186
187 static IsWine gIsWine = is_wine();
188 if (gIsWine == IsWine::kYes && type != ANGLEBackend::kOpenGL) {
189 // D3D backends of ANGLE don't really work well under Wine with our tests and are likely to
190 // crash. This makes it easier to test using the GL ANGLE backend under Wine on Linux
191 // without lots of spurious Wine debug spew and crashes.
192 return;
193 }
194
195 if (EGL_NO_DISPLAY == fDisplay) {
196 HINSTANCE hInstance = (HINSTANCE)GetModuleHandle(nullptr);
197
198 if (!gWC) {
199 WNDCLASS wc;
200 wc.cbClsExtra = 0;
201 wc.cbWndExtra = 0;
202 wc.hbrBackground = nullptr;
203 wc.hCursor = LoadCursor(nullptr, IDC_ARROW);
204 wc.hIcon = LoadIcon(nullptr, IDI_APPLICATION);
205 wc.hInstance = hInstance;
206 wc.lpfnWndProc = (WNDPROC) DefWindowProc;
207 wc.lpszClassName = TEXT("ANGLE-win");
208 wc.lpszMenuName = nullptr;
209 wc.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
210
211 gWC = RegisterClass(&wc);
212 if (!gWC) {
213 SkDebugf("Could not register window class.\n");
214 return;
215 }
216 }
217 if (!(fWindow = CreateWindow(TEXT("ANGLE-win"),
218 TEXT("The Invisible Man"),
219 WS_OVERLAPPEDWINDOW,
220 0, 0, 1, 1,
221 nullptr, nullptr,
222 hInstance, nullptr))) {
223 SkDebugf("Could not create window.\n");
224 return;
225 }
226
227 if (!(fDeviceContext = GetDC(fWindow))) {
228 SkDebugf("Could not get device context.\n");
229 this->destroyGLContext();
230 return;
231 }
232
233 fDisplay = get_angle_egl_display(fDeviceContext, type);
234 }
235 #else
236 SkASSERT(EGL_NO_DISPLAY == fDisplay);
237 fDisplay = get_angle_egl_display(EGL_DEFAULT_DISPLAY, type);
238 #endif
239 if (EGL_NO_DISPLAY == fDisplay) {
240 SkDebugf("Could not create EGL display!");
241 return;
242 }
243
244 // Add ANGLE platform hooks to connect to Skia's tracing implementation
245 angle::GetDisplayPlatformFunc getPlatform = reinterpret_cast<angle::GetDisplayPlatformFunc>(
246 eglGetProcAddress("ANGLEGetDisplayPlatform"));
247 if (getPlatform) {
248 fResetPlatform = reinterpret_cast<angle::ResetDisplayPlatformFunc>(
249 eglGetProcAddress("ANGLEResetDisplayPlatform"));
250 SkASSERT(fResetPlatform);
251
252 angle::PlatformMethods* platformMethods = nullptr;
253 if (getPlatform(fDisplay, angle::g_PlatformMethodNames, angle::g_NumPlatformMethods,
254 nullptr, &platformMethods)) {
255 platformMethods->addTraceEvent = ANGLE_addTraceEvent;
256 platformMethods->getTraceCategoryEnabledFlag = ANGLE_getTraceCategoryEnabledFlag;
257 platformMethods->updateTraceEventDuration = ANGLE_updateTraceEventDuration;
258 platformMethods->monotonicallyIncreasingTime = ANGLE_monotonicallyIncreasingTime;
259 }
260 }
261
262 EGLint majorVersion;
263 EGLint minorVersion;
264 if (!eglInitialize(fDisplay, &majorVersion, &minorVersion)) {
265 SkDebugf("Could not initialize display!");
266 this->destroyGLContext();
267 return;
268 }
269
270 EGLint numConfigs;
271 static const EGLint configAttribs[] = {
272 EGL_SURFACE_TYPE, EGL_PBUFFER_BIT,
273 EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
274 EGL_RED_SIZE, 8,
275 EGL_GREEN_SIZE, 8,
276 EGL_BLUE_SIZE, 8,
277 EGL_ALPHA_SIZE, 8,
278 EGL_NONE
279 };
280
281 EGLConfig surfaceConfig;
282 if (!eglChooseConfig(fDisplay, configAttribs, &surfaceConfig, 1, &numConfigs)) {
283 SkDebugf("Could not create choose config!");
284 this->destroyGLContext();
285 return;
286 }
287
288 int versionNum = ANGLEContextVersion::kES2 == version ? 2 : 3;
289 const EGLint contextAttribs[] = {
290 EGL_CONTEXT_CLIENT_VERSION, versionNum,
291 EGL_NONE
292 };
293 EGLContext eglShareContext = shareContext ? shareContext->fContext : nullptr;
294 fContext = eglCreateContext(fDisplay, surfaceConfig, eglShareContext, contextAttribs);
295 if (EGL_NO_CONTEXT == fContext) {
296 SkDebugf("Could not create context!");
297 this->destroyGLContext();
298 return;
299 }
300
301 static const EGLint surfaceAttribs[] = {
302 EGL_WIDTH, 1,
303 EGL_HEIGHT, 1,
304 EGL_NONE
305 };
306
307 fSurface = eglCreatePbufferSurface(fDisplay, surfaceConfig, surfaceAttribs);
308
309 SkScopeExit restorer(context_restorer());
310 if (!eglMakeCurrent(fDisplay, fSurface, fSurface, fContext)) {
311 SkDebugf("Could not set the context.");
312 this->destroyGLContext();
313 return;
314 }
315
316 sk_sp<const GrGLInterface> gl = sk_gpu_test::CreateANGLEGLInterface();
317 if (nullptr == gl.get()) {
318 SkDebugf("Could not create ANGLE GL interface!\n");
319 this->destroyGLContext();
320 return;
321 }
322 if (!gl->validate()) {
323 SkDebugf("Could not validate ANGLE GL interface!\n");
324 this->destroyGLContext();
325 return;
326 }
327
328 #ifdef SK_DEBUG
329 // Verify that the interface we requested was actually returned to us
330 const GrGLubyte* rendererUByte;
331 GR_GL_CALL_RET(gl.get(), rendererUByte, GetString(GR_GL_RENDERER));
332 const char* renderer = reinterpret_cast<const char*>(rendererUByte);
333 switch (type) {
334 case ANGLEBackend::kD3D9:
335 SkASSERT(strstr(renderer, "Direct3D9"));
336 break;
337 case ANGLEBackend::kD3D11:
338 SkASSERT(strstr(renderer, "Direct3D11"));
339 break;
340 case ANGLEBackend::kOpenGL:
341 SkASSERT(strstr(renderer, "OpenGL"));
342 break;
343 }
344 #endif
345 const char* extensions = eglQueryString(fDisplay, EGL_EXTENSIONS);
346 if (strstr(extensions, "EGL_KHR_image")) {
347 fCreateImage = (PFNEGLCREATEIMAGEKHRPROC)eglGetProcAddress("eglCreateImageKHR");
348 fDestroyImage = (PFNEGLDESTROYIMAGEKHRPROC)eglGetProcAddress("eglDestroyImageKHR");
349 }
350
351 this->init(std::move(gl));
352 }
353
~ANGLEGLContext()354 ANGLEGLContext::~ANGLEGLContext() {
355 this->teardown();
356 this->destroyGLContext();
357 }
358
texture2DToEGLImage(GrGLuint texID) const359 GrEGLImage ANGLEGLContext::texture2DToEGLImage(GrGLuint texID) const {
360 if (!this->gl()->hasExtension("EGL_KHR_gl_texture_2D_image")) {
361 return GR_EGL_NO_IMAGE;
362 }
363 EGLint attribs[] = { GR_EGL_GL_TEXTURE_LEVEL, 0,
364 GR_EGL_IMAGE_PRESERVED, GR_EGL_TRUE,
365 GR_EGL_NONE };
366 // 64 bit cast is to shut Visual C++ up about casting 32 bit value to a pointer.
367 GrEGLClientBuffer clientBuffer = reinterpret_cast<GrEGLClientBuffer>((uint64_t)texID);
368 return fCreateImage(fDisplay, fContext, GR_EGL_GL_TEXTURE_2D, clientBuffer, attribs);
369 }
370
destroyEGLImage(GrEGLImage image) const371 void ANGLEGLContext::destroyEGLImage(GrEGLImage image) const { fDestroyImage(fDisplay, image); }
372
eglImageToExternalTexture(GrEGLImage image) const373 GrGLuint ANGLEGLContext::eglImageToExternalTexture(GrEGLImage image) const {
374 GrGLClearErr(this->gl());
375 if (!this->gl()->hasExtension("GL_OES_EGL_image_external")) {
376 return 0;
377 }
378 typedef GrGLvoid (EGLAPIENTRY *EGLImageTargetTexture2DProc)(GrGLenum, GrGLeglImage);
379 EGLImageTargetTexture2DProc glEGLImageTargetTexture2D =
380 (EGLImageTargetTexture2DProc)eglGetProcAddress("glEGLImageTargetTexture2DOES");
381 if (!glEGLImageTargetTexture2D) {
382 return 0;
383 }
384 GrGLuint texID;
385 GR_GL_CALL(this->gl(), GenTextures(1, &texID));
386 if (!texID) {
387 return 0;
388 }
389 GR_GL_CALL(this->gl(), BindTexture(GR_GL_TEXTURE_EXTERNAL, texID));
390 if (GR_GL_GET_ERROR(this->gl()) != GR_GL_NO_ERROR) {
391 GR_GL_CALL(this->gl(), DeleteTextures(1, &texID));
392 return 0;
393 }
394 glEGLImageTargetTexture2D(GR_GL_TEXTURE_EXTERNAL, image);
395 if (GR_GL_GET_ERROR(this->gl()) != GR_GL_NO_ERROR) {
396 GR_GL_CALL(this->gl(), DeleteTextures(1, &texID));
397 return 0;
398 }
399 return texID;
400 }
401
makeNew() const402 std::unique_ptr<sk_gpu_test::GLTestContext> ANGLEGLContext::makeNew() const {
403 // For EGLImage sharing between contexts to work in ANGLE the two contexts
404 // need to share the same display
405 std::unique_ptr<sk_gpu_test::GLTestContext> ctx =
406 sk_gpu_test::MakeANGLETestContext(fType, fVersion, nullptr, fDisplay);
407 if (ctx) {
408 ctx->makeCurrent();
409 }
410 return ctx;
411 }
412
destroyGLContext()413 void ANGLEGLContext::destroyGLContext() {
414 if (EGL_NO_DISPLAY != fDisplay) {
415 if (eglGetCurrentContext() == fContext) {
416 // This will ensure that the context is immediately deleted.
417 eglMakeCurrent(fDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
418 }
419
420 if (EGL_NO_CONTEXT != fContext) {
421 eglDestroyContext(fDisplay, fContext);
422 fContext = EGL_NO_CONTEXT;
423 }
424
425 if (EGL_NO_SURFACE != fSurface) {
426 eglDestroySurface(fDisplay, fSurface);
427 fSurface = EGL_NO_SURFACE;
428 }
429
430 if (fResetPlatform) {
431 fResetPlatform(fDisplay);
432 }
433
434 eglTerminate(fDisplay);
435 fDisplay = EGL_NO_DISPLAY;
436 }
437
438 #ifdef SK_BUILD_FOR_WIN
439 if (fWindow) {
440 if (fDeviceContext) {
441 ReleaseDC(fWindow, fDeviceContext);
442 fDeviceContext = 0;
443 }
444
445 DestroyWindow(fWindow);
446 fWindow = 0;
447 }
448 #endif
449 }
450
onPlatformMakeCurrent() const451 void ANGLEGLContext::onPlatformMakeCurrent() const {
452 if (!eglMakeCurrent(fDisplay, fSurface, fSurface, fContext)) {
453 SkDebugf("Could not set the context 0x%x.\n", eglGetError());
454 }
455 }
456
onPlatformGetAutoContextRestore() const457 std::function<void()> ANGLEGLContext::onPlatformGetAutoContextRestore() const {
458 if (eglGetCurrentContext() == fContext) {
459 return nullptr;
460 }
461 return context_restorer();
462 }
463
onPlatformSwapBuffers() const464 void ANGLEGLContext::onPlatformSwapBuffers() const {
465 if (!eglSwapBuffers(fDisplay, fSurface)) {
466 SkDebugf("Could not complete eglSwapBuffers.\n");
467 }
468 }
469
onPlatformGetProcAddress(const char * name) const470 GrGLFuncPtr ANGLEGLContext::onPlatformGetProcAddress(const char* name) const {
471 return eglGetProcAddress(name);
472 }
473 } // anonymous namespace
474
475 namespace sk_gpu_test {
CreateANGLEGLInterface()476 sk_sp<const GrGLInterface> CreateANGLEGLInterface() {
477 static Libs gLibs = { nullptr, nullptr };
478
479 if (nullptr == gLibs.fGLLib) {
480 // We load the ANGLE library and never let it go
481 #if defined _WIN32
482 gLibs.fGLLib = DynamicLoadLibrary("libGLESv2.dll");
483 gLibs.fEGLLib = DynamicLoadLibrary("libEGL.dll");
484 #elif defined SK_BUILD_FOR_MAC
485 gLibs.fGLLib = DynamicLoadLibrary("libGLESv2.dylib");
486 gLibs.fEGLLib = DynamicLoadLibrary("libEGL.dylib");
487 #else
488 gLibs.fGLLib = DynamicLoadLibrary("libGLESv2.so");
489 gLibs.fEGLLib = DynamicLoadLibrary("libEGL.so");
490 #endif
491 }
492
493 if (nullptr == gLibs.fGLLib || nullptr == gLibs.fEGLLib) {
494 // We can't setup the interface correctly w/o the so
495 return nullptr;
496 }
497
498 return GrGLMakeAssembledGLESInterface(&gLibs, angle_get_gl_proc);
499 }
500
MakeANGLETestContext(ANGLEBackend type,ANGLEContextVersion version,GLTestContext * shareContext,void * display)501 std::unique_ptr<GLTestContext> MakeANGLETestContext(ANGLEBackend type, ANGLEContextVersion version,
502 GLTestContext* shareContext, void* display){
503 #if defined(SK_BUILD_FOR_WIN) && defined(_M_ARM64)
504 // Windows-on-ARM only has D3D11. This will fail correctly, but it produces huge amounts of
505 // debug output for every unit test from both ANGLE and our context factory.
506 if (ANGLEBackend::kD3D11 != type) {
507 return nullptr;
508 }
509 #endif
510
511 ANGLEGLContext* angleShareContext = reinterpret_cast<ANGLEGLContext*>(shareContext);
512 std::unique_ptr<GLTestContext> ctx(new ANGLEGLContext(type, version,
513 angleShareContext, display));
514 if (!ctx->isValid()) {
515 return nullptr;
516 }
517 return ctx;
518 }
519 } // namespace sk_gpu_test
520