1/*
2 * KHR_vg_parent_image extension test
3 *
4 * This test aims to validate KHR_vg_parent_image extension which provides
5 * a mechanism for creating EGLImage objects from OpenVG VGImage resources,
6 * and then bind EGLImage with texture which will be used by OpenGL API.
7 *
8 * VGImage->EGLImage->Texture
9 *
10 * Cooper Yuan <cooperyuan@gmail.com>
11 * 20 Aug 2011
12 */
13
14#include <math.h>
15#include <stdlib.h>
16#include <stdio.h>
17#include <string.h>
18#include <X11/Xlib.h>
19#include <X11/Xutil.h>
20#include <X11/keysym.h>
21#include <GL/gl.h>
22#include <VG/openvg.h>
23#include <GL/glu.h>
24#include <EGL/egl.h>
25
26#include <EGL/eglext.h>
27
28#define WINDOW_WIDTH   300
29#define WINDOW_HEIGHT  300
30
31static PFNEGLCREATEIMAGEKHRPROC    eglCreateImageKHR                    = NULL;
32static PFNEGLDESTROYIMAGEKHRPROC   eglDestroyImageKHR                   = NULL;
33static PFNGLEGLIMAGETARGETTEXTURE2DOESPROC glEGLImageTargetTexture2DOES = NULL;
34
35typedef struct _egl_manager_t
36{
37    EGLNativeDisplayType xdpy;
38    EGLNativeWindowType  xwin;
39
40    EGLDisplay  dpy;
41    EGLConfig   conf;
42
43    // Rendering contexts
44    EGLContext  vg_ctx;
45    EGLContext  es_ctx;
46
47    // Surfaces
48    EGLSurface  win_surface;
49    EGLSurface  pbuf_surface;
50
51    VGImage     vg_image;
52    EGLImageKHR egl_image;
53    GLuint      texture;
54
55    EGLint      major_ver, minor_ver;
56}EGLmanager;
57
58
59static EGLBoolean check_ext(EGLmanager *eglman)
60{
61    const char* egl_ext_str = NULL;
62    egl_ext_str = eglQueryString(eglman->dpy, EGL_EXTENSIONS);
63
64    // check extension KHR_vg_parent_image
65    if (eglCreateImageKHR == NULL)
66    {
67        if (!strstr(egl_ext_str, "EGL_KHR_image"))
68        {
69            return EGL_FALSE;
70        }
71        else
72        {
73            eglCreateImageKHR = (PFNEGLCREATEIMAGEKHRPROC)eglGetProcAddress("eglCreateImageKHR");
74            eglDestroyImageKHR = (PFNEGLDESTROYIMAGEKHRPROC)eglGetProcAddress("eglDestroyImageKHR");
75            if ((!eglCreateImageKHR) || (!eglDestroyImageKHR))
76            {
77                return EGL_FALSE;
78            }
79        }
80    }
81
82    // check extension GL_OES_EGL_image
83    if (glEGLImageTargetTexture2DOES == NULL)
84    {
85        glEGLImageTargetTexture2DOES = (PFNGLEGLIMAGETARGETTEXTURE2DOESPROC)eglGetProcAddress("glEGLImageTargetTexture2DOES");
86        if (!glEGLImageTargetTexture2DOES)
87        {
88            return EGL_FALSE;
89        }
90    }
91
92    return EGL_TRUE;
93}
94
95static EGLBoolean create_x_window(EGLmanager *eglman, const char *name)
96{
97    EGLint scrnum, num_conf, num_visuals;
98    Window root;
99    EGLint vid;
100    XVisualInfo *visInfo, visTemplate;
101    XSetWindowAttributes attr;
102    unsigned long mask;
103
104    EGLint config_attrib[] =
105    {
106        EGL_RED_SIZE,			1,
107        EGL_GREEN_SIZE, 		1,
108        EGL_BLUE_SIZE,			1,
109        EGL_DEPTH_SIZE,         1,
110        EGL_SURFACE_TYPE,       EGL_WINDOW_BIT|EGL_PBUFFER_BIT,
111        EGL_RENDERABLE_TYPE,	EGL_OPENVG_BIT | EGL_OPENGL_BIT,
112        EGL_NONE
113    };
114
115    scrnum = DefaultScreen(eglman->xdpy);
116    root = RootWindow(eglman->xdpy, scrnum);
117
118    if (!eglChooseConfig(eglman->dpy, config_attrib, &eglman->conf, 1, &num_conf) ||
119        num_conf == 0 ||
120        eglGetError() != EGL_SUCCESS)
121    {
122        printf("Error: couldn't get an EGL visual config\n");
123        return EGL_FALSE;
124    }
125
126    if (!eglGetConfigAttrib(eglman->dpy, eglman->conf, EGL_NATIVE_VISUAL_ID, &vid) ||
127        eglGetError() != EGL_SUCCESS)
128    {
129        printf("Error: eglGetConfigAttrib() failed\n");
130        return EGL_FALSE;
131    }
132
133    /* The X window visual must match the EGL config */
134    visTemplate.visualid = vid;
135    visInfo = XGetVisualInfo(eglman->xdpy, VisualIDMask, &visTemplate, &num_visuals);
136    if (!visInfo)
137    {
138        printf("Error: couldn't get X visual\n");
139        return EGL_FALSE;
140    }
141
142    /* window attributes */
143    attr.background_pixel = 0;
144    attr.border_pixel = 0;
145    attr.colormap = XCreateColormap(eglman->xdpy, root, visInfo->visual, AllocNone);
146    attr.event_mask = StructureNotifyMask | ExposureMask | KeyPressMask;
147    mask = CWBackPixel | CWBorderPixel | CWColormap | CWEventMask;
148
149    eglman->xwin = XCreateWindow(eglman->xdpy, root, 0, 0, WINDOW_WIDTH, WINDOW_HEIGHT,
150                                 0, visInfo->depth, InputOutput,
151                                 visInfo->visual, mask, &attr);
152
153    if (!eglman->xwin)
154    {
155        printf("Error: couldn't create X Window\n");
156        return EGL_FALSE;
157    }
158
159    /* set hints and properties */
160    {
161       XSizeHints sizehints;
162       sizehints.x = 0;
163       sizehints.y = 0;
164       sizehints.width  = WINDOW_WIDTH;
165       sizehints.height = WINDOW_HEIGHT;
166       sizehints.flags = USSize | USPosition;
167       XSetNormalHints(eglman->xdpy, eglman->xwin, &sizehints);
168       XSetStandardProperties(eglman->xdpy, eglman->xwin, name, name,
169                              None, (char **)NULL, 0, &sizehints);
170    }
171
172    XFree(visInfo);
173
174    return EGL_TRUE;
175}
176
177static EGLBoolean egl_init(EGLmanager *eglman)
178{
179    EGLint pbuffer_attrib[] =
180    {
181        EGL_WIDTH,  128,
182        EGL_HEIGHT, 128,
183        EGL_NONE
184    };
185
186    // Check extension support
187    if (check_ext(eglman) != EGL_TRUE)
188    {
189        return EGL_FALSE;
190    }
191
192    // Create GL context
193    eglBindAPI(EGL_OPENGL_ES_API);
194    eglman->es_ctx = eglCreateContext(eglman->dpy, eglman->conf, NULL, NULL);
195    if (eglman->es_ctx == EGL_NO_CONTEXT ||
196        eglGetError() != EGL_SUCCESS)
197    {
198        return EGL_FALSE;
199    }
200
201    // Create VG context
202    eglBindAPI(EGL_OPENVG_API);
203    eglman->vg_ctx = eglCreateContext(eglman->dpy, eglman->conf, NULL, NULL);
204    if (eglman->vg_ctx == EGL_NO_CONTEXT ||
205        eglGetError() != EGL_SUCCESS)
206    {
207        return EGL_FALSE;
208    }
209
210    // Create window surface
211    eglman->win_surface = eglCreateWindowSurface(eglman->dpy, eglman->conf, eglman->xwin, NULL);
212    if (eglman->win_surface == EGL_NO_SURFACE ||
213        eglGetError() != EGL_SUCCESS)
214    {
215        return EGL_FALSE;
216    }
217
218    // Create pbuffer surface
219    eglman->pbuf_surface = eglCreatePbufferSurface(eglman->dpy, eglman->conf, pbuffer_attrib);
220    if (eglman->pbuf_surface == EGL_NO_SURFACE ||
221        eglGetError() != EGL_SUCCESS)
222    {
223
224        return EGL_FALSE;
225    }
226
227    return EGL_TRUE;
228}
229
230static void egl_deinit(EGLmanager *eglman)
231{
232    eglBindAPI(EGL_OPENGL_ES_API);
233    eglMakeCurrent(eglman->dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
234
235    eglBindAPI(EGL_OPENVG_API);
236    eglMakeCurrent(eglman->dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
237
238    eglDestroySurface(eglman->dpy, eglman->win_surface);
239    eglDestroySurface(eglman->dpy, eglman->pbuf_surface);
240
241    eglDestroyContext(eglman->dpy, eglman->es_ctx);
242    eglDestroyContext(eglman->dpy, eglman->vg_ctx);
243}
244
245static EGLBoolean vg_es_init(EGLmanager *eglman)
246{
247    // Initialize OpenVG
248    eglBindAPI(EGL_OPENVG_API);
249    eglMakeCurrent(eglman->dpy, eglman->pbuf_surface, eglman->pbuf_surface, eglman->vg_ctx);
250
251    // Create VGImage
252    eglman->vg_image = vgCreateImage(VG_sRGBA_8888, WINDOW_WIDTH, WINDOW_HEIGHT, VG_IMAGE_QUALITY_BETTER);
253
254    // Create EGLImage from VGImage
255    eglman->egl_image = (EGLImageKHR)eglCreateImageKHR(eglman->dpy, eglman->vg_ctx, EGL_VG_PARENT_IMAGE_KHR, (EGLClientBuffer)eglman->vg_image, NULL);
256
257    // Initialize OpenGL ES
258    eglBindAPI(EGL_OPENGL_ES_API);
259    eglMakeCurrent(eglman->dpy, eglman->win_surface, eglman->win_surface, eglman->es_ctx);
260
261    // Create Texture Target from EGLImage
262    glGenTextures(1, &eglman->texture);
263    glBindTexture(GL_TEXTURE_2D, eglman->texture);
264    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
265    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
266    glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE );
267    glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE );
268    glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, (GLeglImageOES)eglman->egl_image);
269
270    return EGL_TRUE;
271}
272
273static void vg_es_deinit(EGLmanager *eglman)
274{
275    // Destroy VG
276    eglBindAPI(EGL_OPENVG_API);
277    eglMakeCurrent(eglman->dpy, eglman->pbuf_surface, eglman->pbuf_surface, eglman->vg_ctx);
278    eglDestroyImageKHR(eglman->dpy, eglman->egl_image);
279    vgDestroyImage(eglman->vg_image);
280
281    // Destroy GL
282    eglBindAPI(EGL_OPENGL_ES_API);
283    eglMakeCurrent(eglman->dpy, eglman->win_surface, eglman->win_surface, eglman->es_ctx);
284    glDeleteTextures(1, &eglman->texture);
285    glBindTexture(GL_TEXTURE_2D, 0);
286
287}
288
289static void draw(EGLmanager *eglman)
290{
291    static const GLfloat red[4] = {1.0, 0.2, 0.2, 1.0};
292    static const GLfloat blue[4] = {0.2, 0.2, 1.0, 1.0};
293
294    GLfloat pVertex[12] =
295    {
296        -1.f, -1.f, 0.f,
297        1.f, -1.f, 0.f,
298        -1.f, 1.f, 0.f,
299        1.f, 1.f, 0.f
300    };
301    GLshort pTexCoord[8] =
302    {
303        0, 0,
304        1, 0,
305        0, 1,
306        1, 1,
307    };
308
309    // Make current to VG content
310    eglBindAPI(EGL_OPENVG_API);
311    eglMakeCurrent(eglman->dpy, eglman->pbuf_surface, eglman->pbuf_surface, eglman->vg_ctx);
312
313    vgSetfv(VG_CLEAR_COLOR, 4, red);
314    vgClearImage(eglman->vg_image, 0, 0, WINDOW_WIDTH/2, WINDOW_HEIGHT/2);
315
316    vgSetfv(VG_CLEAR_COLOR, 4, blue);
317    vgClearImage(eglman->vg_image, WINDOW_WIDTH/2, WINDOW_HEIGHT/2, WINDOW_WIDTH/2, WINDOW_HEIGHT/2);
318
319    // Make current to GL content
320    eglBindAPI(EGL_OPENGL_ES_API);
321    eglMakeCurrent(eglman->dpy, eglman->win_surface, eglman->win_surface, eglman->es_ctx);
322
323    glViewport(WINDOW_WIDTH / 8, WINDOW_HEIGHT / 8, WINDOW_WIDTH * 3 / 4, WINDOW_HEIGHT * 3 / 4);
324
325    glClearColor(1.0, 1.0, 1.0, 1.0);
326    glClear(GL_COLOR_BUFFER_BIT);
327
328    glBindTexture(GL_TEXTURE_2D, eglman->texture);
329    glEnable(GL_TEXTURE_2D);
330    glEnableClientState( GL_VERTEX_ARRAY);
331    glVertexPointer(3, GL_FLOAT, 0, pVertex);
332    glEnableClientState(GL_TEXTURE_COORD_ARRAY);
333    glTexCoordPointer(2, GL_SHORT, 0, pTexCoord);
334
335    glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
336
337    // Swap buffer
338    eglSwapBuffers(eglman->dpy, eglman->win_surface);
339
340    return;
341}
342
343int main(int argc, char **argv)
344{
345    const char *s;
346
347    EGLmanager *eglman = calloc(1, sizeof(*eglman));
348
349    // Open X Display
350    Display *x_dpy = XOpenDisplay(NULL);
351    if (!x_dpy)
352    {
353        printf("error: can't open default display\n");
354        goto exit0;
355    }
356    eglman->xdpy = (EGLNativeDisplayType)x_dpy;
357
358    // Get EGL Display
359    eglman->dpy = eglGetDisplay(eglman->xdpy);
360    if (!eglman->dpy || eglGetError() != EGL_SUCCESS)
361    {
362        printf("error: can't get EGL display\n");
363        goto exit1;
364    }
365
366    // Initialize EGL
367    eglInitialize(eglman->dpy, &eglman->major_ver, &eglman->minor_ver);
368    if (eglGetError() != EGL_SUCCESS)
369    {
370        goto exit1;
371    }
372
373    // Query and print out information
374    s = eglQueryString(eglman->dpy, EGL_VERSION);
375    printf("EGL_VERSION = %s\n", s);
376
377    s = eglQueryString(eglman->dpy, EGL_VENDOR);
378    printf("EGL_VENDOR = %s\n", s);
379
380    s = eglQueryString(eglman->dpy, EGL_EXTENSIONS);
381    printf("EGL_EXTENSIONS = %s\n", s);
382
383    s = eglQueryString(eglman->dpy, EGL_CLIENT_APIS);
384    printf("EGL_CLIENT_APIS = %s\n", s);
385
386    // Create an RGB, double-buffered X window
387    if (create_x_window(eglman, "vgimage to texture") != EGL_TRUE)
388    {
389        goto exit2;
390    }
391
392    XMapWindow(eglman->xdpy, eglman->xwin);
393
394    // Initialize EGL
395    if (egl_init(eglman) != EGL_TRUE)
396    {
397        goto exit3;
398    }
399
400    // Initialize rendering API: OpenGL ES and OpenVG
401    if (vg_es_init(eglman) != EGL_TRUE)
402    {
403        goto exit3;
404    }
405
406    // Rendering
407    draw(eglman);
408
409    // Deinitialize rendering API
410    vg_es_deinit(eglman);
411
412    // Deinitialize EGL
413    egl_deinit(eglman);
414
415exit3:
416    XDestroyWindow(eglman->xdpy, eglman->xwin);
417exit2:
418    eglTerminate(eglman->dpy);
419exit1:
420    XCloseDisplay(eglman->xdpy);
421exit0:
422    free(eglman);
423
424    return 0;
425}
426