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 "GLTestContext_angle.h" 9 10#include <EGL/egl.h> 11#include <EGL/eglext.h> 12 13#include "gl/GrGLDefines.h" 14#include "gl/GrGLUtil.h" 15 16#include "gl/GrGLInterface.h" 17#include "gl/GrGLAssembleInterface.h" 18#include "../ports/SkOSLibrary.h" 19 20#include <EGL/egl.h> 21 22#define EGL_PLATFORM_ANGLE_ANGLE 0x3202 23#define EGL_PLATFORM_ANGLE_TYPE_ANGLE 0x3203 24#define EGL_PLATFORM_ANGLE_TYPE_D3D9_ANGLE 0x3207 25#define EGL_PLATFORM_ANGLE_TYPE_D3D11_ANGLE 0x3208 26#define EGL_PLATFORM_ANGLE_TYPE_OPENGL_ANGLE 0x320D 27 28using sk_gpu_test::ANGLEBackend; 29using sk_gpu_test::ANGLEContextVersion; 30 31namespace { 32struct Libs { 33 void* fGLLib; 34 void* fEGLLib; 35}; 36 37static GrGLFuncPtr angle_get_gl_proc(void* ctx, const char name[]) { 38 const Libs* libs = reinterpret_cast<const Libs*>(ctx); 39 GrGLFuncPtr proc = (GrGLFuncPtr) GetProcedureAddress(libs->fGLLib, name); 40 if (proc) { 41 return proc; 42 } 43 proc = (GrGLFuncPtr) GetProcedureAddress(libs->fEGLLib, name); 44 if (proc) { 45 return proc; 46 } 47 return eglGetProcAddress(name); 48} 49 50void* get_angle_egl_display(void* nativeDisplay, ANGLEBackend type) { 51 PFNEGLGETPLATFORMDISPLAYEXTPROC eglGetPlatformDisplayEXT; 52 eglGetPlatformDisplayEXT = 53 (PFNEGLGETPLATFORMDISPLAYEXTPROC)eglGetProcAddress("eglGetPlatformDisplayEXT"); 54 55 // We expect ANGLE to support this extension 56 if (!eglGetPlatformDisplayEXT) { 57 return EGL_NO_DISPLAY; 58 } 59 60 EGLint typeNum = 0; 61 switch (type) { 62 case ANGLEBackend::kD3D9: 63 typeNum = EGL_PLATFORM_ANGLE_TYPE_D3D9_ANGLE; 64 break; 65 case ANGLEBackend::kD3D11: 66 typeNum = EGL_PLATFORM_ANGLE_TYPE_D3D11_ANGLE; 67 break; 68 case ANGLEBackend::kOpenGL: 69 typeNum = EGL_PLATFORM_ANGLE_TYPE_OPENGL_ANGLE; 70 break; 71 } 72 const EGLint attribs[] = { EGL_PLATFORM_ANGLE_TYPE_ANGLE, typeNum, EGL_NONE }; 73 return eglGetPlatformDisplayEXT(EGL_PLATFORM_ANGLE_ANGLE, nativeDisplay, attribs); 74} 75 76class ANGLEGLContext : public sk_gpu_test::GLTestContext { 77public: 78 ANGLEGLContext(ANGLEBackend, ANGLEContextVersion, ANGLEGLContext* shareContext, void* display); 79 ~ANGLEGLContext() override; 80 81 GrEGLImage texture2DToEGLImage(GrGLuint texID) const override; 82 void destroyEGLImage(GrEGLImage) const override; 83 GrGLuint eglImageToExternalTexture(GrEGLImage) const override; 84 std::unique_ptr<sk_gpu_test::GLTestContext> makeNew() const override; 85 86private: 87 void destroyGLContext(); 88 89 void onPlatformMakeCurrent() const override; 90 void onPlatformSwapBuffers() const override; 91 GrGLFuncPtr onPlatformGetProcAddress(const char* name) const override; 92 93 void* fContext; 94 void* fDisplay; 95 void* fSurface; 96 ANGLEBackend fType; 97 ANGLEContextVersion fVersion; 98 99#ifdef SK_BUILD_FOR_WIN 100 HWND fWindow; 101 HDC fDeviceContext; 102 static ATOM gWC; 103#endif 104}; 105 106#ifdef SK_BUILD_FOR_WIN 107ATOM ANGLEGLContext::gWC = 0; 108#endif 109 110ANGLEGLContext::ANGLEGLContext(ANGLEBackend type, ANGLEContextVersion version, 111 ANGLEGLContext* shareContext, void* display) 112 : fContext(EGL_NO_CONTEXT) 113 , fDisplay(display) 114 , fSurface(EGL_NO_SURFACE) 115 , fType(type) 116 , fVersion(version) { 117#ifdef SK_BUILD_FOR_WIN 118 fWindow = nullptr; 119 fDeviceContext = nullptr; 120 121 if (EGL_NO_DISPLAY == fDisplay) { 122 HINSTANCE hInstance = (HINSTANCE)GetModuleHandle(nullptr); 123 124 if (!gWC) { 125 WNDCLASS wc; 126 wc.cbClsExtra = 0; 127 wc.cbWndExtra = 0; 128 wc.hbrBackground = nullptr; 129 wc.hCursor = LoadCursor(nullptr, IDC_ARROW); 130 wc.hIcon = LoadIcon(nullptr, IDI_APPLICATION); 131 wc.hInstance = hInstance; 132 wc.lpfnWndProc = (WNDPROC) DefWindowProc; 133 wc.lpszClassName = TEXT("ANGLE-win"); 134 wc.lpszMenuName = nullptr; 135 wc.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC; 136 137 gWC = RegisterClass(&wc); 138 if (!gWC) { 139 SkDebugf("Could not register window class.\n"); 140 return; 141 } 142 } 143 if (!(fWindow = CreateWindow(TEXT("ANGLE-win"), 144 TEXT("The Invisible Man"), 145 WS_OVERLAPPEDWINDOW, 146 0, 0, 1, 1, 147 nullptr, nullptr, 148 hInstance, nullptr))) { 149 SkDebugf("Could not create window.\n"); 150 return; 151 } 152 153 if (!(fDeviceContext = GetDC(fWindow))) { 154 SkDebugf("Could not get device context.\n"); 155 this->destroyGLContext(); 156 return; 157 } 158 159 fDisplay = get_angle_egl_display(fDeviceContext, type); 160 } 161#else 162 SkASSERT(EGL_NO_DISPLAY == fDisplay); 163 fDisplay = get_angle_egl_display(EGL_DEFAULT_DISPLAY, type); 164#endif 165 if (EGL_NO_DISPLAY == fDisplay) { 166 SkDebugf("Could not create EGL display!"); 167 return; 168 } 169 170 EGLint majorVersion; 171 EGLint minorVersion; 172 if (!eglInitialize(fDisplay, &majorVersion, &minorVersion)) { 173 SkDebugf("Could not initialize display!"); 174 this->destroyGLContext(); 175 return; 176 } 177 178 EGLint numConfigs; 179 static const EGLint configAttribs[] = { 180 EGL_SURFACE_TYPE, EGL_PBUFFER_BIT, 181 EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, 182 EGL_RED_SIZE, 8, 183 EGL_GREEN_SIZE, 8, 184 EGL_BLUE_SIZE, 8, 185 EGL_ALPHA_SIZE, 8, 186 EGL_NONE 187 }; 188 189 EGLConfig surfaceConfig; 190 if (!eglChooseConfig(fDisplay, configAttribs, &surfaceConfig, 1, &numConfigs)) { 191 SkDebugf("Could not create choose config!"); 192 this->destroyGLContext(); 193 return; 194 } 195 196 int versionNum = ANGLEContextVersion::kES2 == version ? 2 : 3; 197 const EGLint contextAttribs[] = { 198 EGL_CONTEXT_CLIENT_VERSION, versionNum, 199 EGL_NONE 200 }; 201 EGLContext eglShareContext = shareContext ? shareContext->fContext : nullptr; 202 fContext = eglCreateContext(fDisplay, surfaceConfig, eglShareContext, contextAttribs); 203 if (EGL_NO_CONTEXT == fContext) { 204 SkDebugf("Could not create context!"); 205 this->destroyGLContext(); 206 return; 207 } 208 209 static const EGLint surfaceAttribs[] = { 210 EGL_WIDTH, 1, 211 EGL_HEIGHT, 1, 212 EGL_NONE 213 }; 214 215 fSurface = eglCreatePbufferSurface(fDisplay, surfaceConfig, surfaceAttribs); 216 217 if (!eglMakeCurrent(fDisplay, fSurface, fSurface, fContext)) { 218 SkDebugf("Could not set the context."); 219 this->destroyGLContext(); 220 return; 221 } 222 223 sk_sp<const GrGLInterface> gl(sk_gpu_test::CreateANGLEGLInterface()); 224 if (nullptr == gl.get()) { 225 SkDebugf("Could not create ANGLE GL interface!\n"); 226 this->destroyGLContext(); 227 return; 228 } 229 if (!gl->validate()) { 230 SkDebugf("Could not validate ANGLE GL interface!\n"); 231 this->destroyGLContext(); 232 return; 233 } 234 235#ifdef SK_DEBUG 236 // Verify that the interface we requested was actually returned to us 237 const GrGLubyte* rendererUByte; 238 GR_GL_CALL_RET(gl.get(), rendererUByte, GetString(GR_GL_RENDERER)); 239 const char* renderer = reinterpret_cast<const char*>(rendererUByte); 240 switch (type) { 241 case ANGLEBackend::kD3D9: 242 SkASSERT(strstr(renderer, "Direct3D9")); 243 break; 244 case ANGLEBackend::kD3D11: 245 SkASSERT(strstr(renderer, "Direct3D11")); 246 break; 247 case ANGLEBackend::kOpenGL: 248 SkASSERT(strstr(renderer, "OpenGL")); 249 break; 250 } 251#endif 252 253 this->init(gl.release()); 254} 255 256ANGLEGLContext::~ANGLEGLContext() { 257 this->teardown(); 258 this->destroyGLContext(); 259} 260 261GrEGLImage ANGLEGLContext::texture2DToEGLImage(GrGLuint texID) const { 262 if (!this->gl()->hasExtension("EGL_KHR_gl_texture_2D_image")) { 263 return GR_EGL_NO_IMAGE; 264 } 265 GrEGLImage img; 266 GrEGLint attribs[] = { GR_EGL_GL_TEXTURE_LEVEL, 0, 267 GR_EGL_IMAGE_PRESERVED, GR_EGL_TRUE, 268 GR_EGL_NONE }; 269 // 64 bit cast is to shut Visual C++ up about casting 32 bit value to a pointer. 270 GrEGLClientBuffer clientBuffer = reinterpret_cast<GrEGLClientBuffer>((uint64_t)texID); 271 GR_GL_CALL_RET(this->gl(), img, 272 EGLCreateImage(fDisplay, fContext, GR_EGL_GL_TEXTURE_2D, clientBuffer, 273 attribs)); 274 return img; 275} 276 277void ANGLEGLContext::destroyEGLImage(GrEGLImage image) const { 278 GR_GL_CALL(this->gl(), EGLDestroyImage(fDisplay, image)); 279} 280 281GrGLuint ANGLEGLContext::eglImageToExternalTexture(GrEGLImage image) const { 282 GrGLClearErr(this->gl()); 283 if (!this->gl()->hasExtension("GL_OES_EGL_image_external")) { 284 return 0; 285 } 286 typedef GrGLvoid (EGLAPIENTRY *EGLImageTargetTexture2DProc)(GrGLenum, GrGLeglImage); 287 EGLImageTargetTexture2DProc glEGLImageTargetTexture2D = 288 (EGLImageTargetTexture2DProc)eglGetProcAddress("glEGLImageTargetTexture2DOES"); 289 if (!glEGLImageTargetTexture2D) { 290 return 0; 291 } 292 GrGLuint texID; 293 GR_GL_CALL(this->gl(), GenTextures(1, &texID)); 294 if (!texID) { 295 return 0; 296 } 297 GR_GL_CALL(this->gl(), BindTexture(GR_GL_TEXTURE_EXTERNAL, texID)); 298 if (GR_GL_GET_ERROR(this->gl()) != GR_GL_NO_ERROR) { 299 GR_GL_CALL(this->gl(), DeleteTextures(1, &texID)); 300 return 0; 301 } 302 glEGLImageTargetTexture2D(GR_GL_TEXTURE_EXTERNAL, image); 303 if (GR_GL_GET_ERROR(this->gl()) != GR_GL_NO_ERROR) { 304 GR_GL_CALL(this->gl(), DeleteTextures(1, &texID)); 305 return 0; 306 } 307 return texID; 308} 309 310std::unique_ptr<sk_gpu_test::GLTestContext> ANGLEGLContext::makeNew() const { 311 // For EGLImage sharing between contexts to work in ANGLE the two contexts 312 // need to share the same display 313 std::unique_ptr<sk_gpu_test::GLTestContext> ctx = 314 sk_gpu_test::MakeANGLETestContext(fType, fVersion, nullptr, fDisplay); 315 if (ctx) { 316 ctx->makeCurrent(); 317 } 318 return ctx; 319} 320 321void ANGLEGLContext::destroyGLContext() { 322 if (EGL_NO_DISPLAY != fDisplay) { 323 eglMakeCurrent(fDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); 324 325 if (EGL_NO_CONTEXT != fContext) { 326 eglDestroyContext(fDisplay, fContext); 327 fContext = EGL_NO_CONTEXT; 328 } 329 330 if (EGL_NO_SURFACE != fSurface) { 331 eglDestroySurface(fDisplay, fSurface); 332 fSurface = EGL_NO_SURFACE; 333 } 334 335 eglTerminate(fDisplay); 336 fDisplay = EGL_NO_DISPLAY; 337 } 338 339#ifdef SK_BUILD_FOR_WIN 340 if (fWindow) { 341 if (fDeviceContext) { 342 ReleaseDC(fWindow, fDeviceContext); 343 fDeviceContext = 0; 344 } 345 346 DestroyWindow(fWindow); 347 fWindow = 0; 348 } 349#endif 350} 351 352void ANGLEGLContext::onPlatformMakeCurrent() const { 353 if (!eglMakeCurrent(fDisplay, fSurface, fSurface, fContext)) { 354 SkDebugf("Could not set the context 0x%x.\n", eglGetError()); 355 } 356} 357 358void ANGLEGLContext::onPlatformSwapBuffers() const { 359 if (!eglSwapBuffers(fDisplay, fSurface)) { 360 SkDebugf("Could not complete eglSwapBuffers.\n"); 361 } 362} 363 364GrGLFuncPtr ANGLEGLContext::onPlatformGetProcAddress(const char* name) const { 365 return eglGetProcAddress(name); 366} 367} // anonymous namespace 368 369namespace sk_gpu_test { 370const GrGLInterface* CreateANGLEGLInterface() { 371 static Libs gLibs = { nullptr, nullptr }; 372 373 if (nullptr == gLibs.fGLLib) { 374 // We load the ANGLE library and never let it go 375#if defined _WIN32 376 gLibs.fGLLib = DynamicLoadLibrary("libGLESv2.dll"); 377 gLibs.fEGLLib = DynamicLoadLibrary("libEGL.dll"); 378#elif defined SK_BUILD_FOR_MAC 379 gLibs.fGLLib = DynamicLoadLibrary("libGLESv2.dylib"); 380 gLibs.fEGLLib = DynamicLoadLibrary("libEGL.dylib"); 381#else 382 gLibs.fGLLib = DynamicLoadLibrary("libGLESv2.so"); 383 gLibs.fEGLLib = DynamicLoadLibrary("libEGL.so"); 384#endif 385 } 386 387 if (nullptr == gLibs.fGLLib || nullptr == gLibs.fEGLLib) { 388 // We can't setup the interface correctly w/o the so 389 return nullptr; 390 } 391 392 return GrGLAssembleGLESInterface(&gLibs, angle_get_gl_proc); 393} 394 395std::unique_ptr<GLTestContext> MakeANGLETestContext(ANGLEBackend type, ANGLEContextVersion version, 396 GLTestContext* shareContext, void* display){ 397 ANGLEGLContext* angleShareContext = reinterpret_cast<ANGLEGLContext*>(shareContext); 398 std::unique_ptr<GLTestContext> ctx(new ANGLEGLContext(type, version, 399 angleShareContext, display)); 400 if (!ctx->isValid()) { 401 return nullptr; 402 } 403 return ctx; 404} 405} // namespace sk_gpu_test 406