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
angle_get_gl_proc(void * ctx,const char name[])37 static 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
get_angle_egl_display(void * nativeDisplay,ANGLEBackend type)50 void* 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
76 class ANGLEGLContext : public sk_gpu_test::GLTestContext {
77 public:
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
86 private:
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
107 ATOM ANGLEGLContext::gWC = 0;
108 #endif
109
ANGLEGLContext(ANGLEBackend type,ANGLEContextVersion version,ANGLEGLContext * shareContext,void * display)110 ANGLEGLContext::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
~ANGLEGLContext()256 ANGLEGLContext::~ANGLEGLContext() {
257 this->teardown();
258 this->destroyGLContext();
259 }
260
texture2DToEGLImage(GrGLuint texID) const261 GrEGLImage 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
destroyEGLImage(GrEGLImage image) const277 void ANGLEGLContext::destroyEGLImage(GrEGLImage image) const {
278 GR_GL_CALL(this->gl(), EGLDestroyImage(fDisplay, image));
279 }
280
eglImageToExternalTexture(GrEGLImage image) const281 GrGLuint 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
makeNew() const310 std::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
destroyGLContext()321 void 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
onPlatformMakeCurrent() const352 void ANGLEGLContext::onPlatformMakeCurrent() const {
353 if (!eglMakeCurrent(fDisplay, fSurface, fSurface, fContext)) {
354 SkDebugf("Could not set the context 0x%x.\n", eglGetError());
355 }
356 }
357
onPlatformSwapBuffers() const358 void ANGLEGLContext::onPlatformSwapBuffers() const {
359 if (!eglSwapBuffers(fDisplay, fSurface)) {
360 SkDebugf("Could not complete eglSwapBuffers.\n");
361 }
362 }
363
onPlatformGetProcAddress(const char * name) const364 GrGLFuncPtr ANGLEGLContext::onPlatformGetProcAddress(const char* name) const {
365 return eglGetProcAddress(name);
366 }
367 } // anonymous namespace
368
369 namespace sk_gpu_test {
CreateANGLEGLInterface()370 const 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
MakeANGLETestContext(ANGLEBackend type,ANGLEContextVersion version,GLTestContext * shareContext,void * display)395 std::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