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