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