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