1 /*
2 * Copyright (c) 2012 Arvin Schnell <arvin.schnell@gmail.com>
3 * Copyright (c) 2012 Rob Clark <rob@ti.com>
4 *
5 * Permission is hereby granted, free of charge, to any person obtaining a
6 * copy of this software and associated documentation files (the "Software"),
7 * to deal in the Software without restriction, including without limitation
8 * the rights to use, copy, modify, merge, publish, distribute, sub license,
9 * and/or sell copies of the Software, and to permit persons to whom the
10 * Software is furnished to do so, subject to the following conditions:
11 *
12 * The above copyright notice and this permission notice (including the
13 * next paragraph) shall be included in all copies or substantial portions
14 * of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
19 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
22 * DEALINGS IN THE SOFTWARE.
23 */
25 /* Based on a egl cube test app originally written by Arvin Schnell */
27 #include <sys/types.h>
28 #include <sys/stat.h>
29 #include <fcntl.h>
30 #include <string.h>
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <errno.h>
35 #include <xf86drm.h>
36 #include <xf86drmMode.h>
37 #include <gbm.h>
39 #include "esUtil.h"
42 #define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
45 static struct {
46 EGLDisplay display;
47 EGLConfig config;
48 EGLContext context;
49 EGLSurface surface;
50 GLuint program;
51 GLint modelviewmatrix, modelviewprojectionmatrix, normalmatrix;
52 GLuint vbo;
53 GLuint positionsoffset, colorsoffset, normalsoffset;
54 } gl;
56 static struct {
57 struct gbm_device *dev;
58 struct gbm_surface *surface;
59 } gbm;
61 static struct {
62 int fd;
63 drmModeModeInfo *mode;
64 uint32_t crtc_id;
65 uint32_t connector_id;
66 } drm;
68 struct drm_fb {
69 struct gbm_bo *bo;
70 uint32_t fb_id;
71 };
73 static int init_drm(void)
74 {
75 static const char *modules[] = {
76 "i915", "radeon", "nouveau", "vmwgfx", "omapdrm", "exynos"
77 };
78 drmModeRes *resources;
79 drmModeConnector *connector = NULL;
80 drmModeEncoder *encoder = NULL;
81 int i, area;
83 for (i = 0; i < ARRAY_SIZE(modules); i++) {
84 printf("trying to load module %s...", modules[i]);
85 drm.fd = drmOpen(modules[i], NULL);
86 if (drm.fd < 0) {
87 printf("failed.\n");
88 } else {
89 printf("success.\n");
90 break;
91 }
92 }
94 if (drm.fd < 0) {
95 printf("could not open drm device\n");
96 return -1;
97 }
99 resources = drmModeGetResources(drm.fd);
100 if (!resources) {
101 printf("drmModeGetResources failed: %s\n", strerror(errno));
102 return -1;
103 }
105 /* find a connected connector: */
106 for (i = 0; i < resources->count_connectors; i++) {
107 connector = drmModeGetConnector(drm.fd, resources->connectors[i]);
108 if (connector->connection == DRM_MODE_CONNECTED) {
109 /* it's connected, let's use this! */
110 break;
111 }
112 drmModeFreeConnector(connector);
113 connector = NULL;
114 }
116 if (!connector) {
117 /* we could be fancy and listen for hotplug events and wait for
118 * a connector..
119 */
120 printf("no connected connector!\n");
121 return -1;
122 }
124 /* find highest resolution mode: */
125 for (i = 0, area = 0; i < connector->count_modes; i++) {
126 drmModeModeInfo *current_mode = &connector->modes[i];
127 int current_area = current_mode->hdisplay * current_mode->vdisplay;
128 if (current_area > area) {
129 drm.mode = current_mode;
130 area = current_area;
131 }
132 }
134 if (!drm.mode) {
135 printf("could not find mode!\n");
136 return -1;
137 }
139 /* find encoder: */
140 for (i = 0; i < resources->count_encoders; i++) {
141 encoder = drmModeGetEncoder(drm.fd, resources->encoders[i]);
142 if (encoder->encoder_id == connector->encoder_id)
143 break;
144 drmModeFreeEncoder(encoder);
145 encoder = NULL;
146 }
148 if (!encoder) {
149 printf("no encoder!\n");
150 return -1;
151 }
153 drm.crtc_id = encoder->crtc_id;
154 drm.connector_id = connector->connector_id;
156 return 0;
157 }
159 static int init_gbm(void)
160 {
161 gbm.dev = gbm_create_device(drm.fd);
163 gbm.surface = gbm_surface_create(gbm.dev,
164 drm.mode->hdisplay, drm.mode->vdisplay,
165 GBM_FORMAT_XRGB8888,
166 GBM_BO_USE_SCANOUT | GBM_BO_USE_RENDERING);
167 if (!gbm.surface) {
168 printf("failed to create gbm surface\n");
169 return -1;
170 }
172 return 0;
173 }
175 static int init_gl(void)
176 {
177 EGLint major, minor, n;
178 GLuint vertex_shader, fragment_shader;
179 GLint ret;
181 static const GLfloat vVertices[] = {
182 // front
183 -1.0f, -1.0f, +1.0f, // point blue
184 +1.0f, -1.0f, +1.0f, // point magenta
185 -1.0f, +1.0f, +1.0f, // point cyan
186 +1.0f, +1.0f, +1.0f, // point white
187 // back
188 +1.0f, -1.0f, -1.0f, // point red
189 -1.0f, -1.0f, -1.0f, // point black
190 +1.0f, +1.0f, -1.0f, // point yellow
191 -1.0f, +1.0f, -1.0f, // point green
192 // right
193 +1.0f, -1.0f, +1.0f, // point magenta
194 +1.0f, -1.0f, -1.0f, // point red
195 +1.0f, +1.0f, +1.0f, // point white
196 +1.0f, +1.0f, -1.0f, // point yellow
197 // left
198 -1.0f, -1.0f, -1.0f, // point black
199 -1.0f, -1.0f, +1.0f, // point blue
200 -1.0f, +1.0f, -1.0f, // point green
201 -1.0f, +1.0f, +1.0f, // point cyan
202 // top
203 -1.0f, +1.0f, +1.0f, // point cyan
204 +1.0f, +1.0f, +1.0f, // point white
205 -1.0f, +1.0f, -1.0f, // point green
206 +1.0f, +1.0f, -1.0f, // point yellow
207 // bottom
208 -1.0f, -1.0f, -1.0f, // point black
209 +1.0f, -1.0f, -1.0f, // point red
210 -1.0f, -1.0f, +1.0f, // point blue
211 +1.0f, -1.0f, +1.0f // point magenta
212 };
214 static const GLfloat vColors[] = {
215 // front
216 0.0f, 0.0f, 1.0f, // blue
217 1.0f, 0.0f, 1.0f, // magenta
218 0.0f, 1.0f, 1.0f, // cyan
219 1.0f, 1.0f, 1.0f, // white
220 // back
221 1.0f, 0.0f, 0.0f, // red
222 0.0f, 0.0f, 0.0f, // black
223 1.0f, 1.0f, 0.0f, // yellow
224 0.0f, 1.0f, 0.0f, // green
225 // right
226 1.0f, 0.0f, 1.0f, // magenta
227 1.0f, 0.0f, 0.0f, // red
228 1.0f, 1.0f, 1.0f, // white
229 1.0f, 1.0f, 0.0f, // yellow
230 // left
231 0.0f, 0.0f, 0.0f, // black
232 0.0f, 0.0f, 1.0f, // blue
233 0.0f, 1.0f, 0.0f, // green
234 0.0f, 1.0f, 1.0f, // cyan
235 // top
236 0.0f, 1.0f, 1.0f, // cyan
237 1.0f, 1.0f, 1.0f, // white
238 0.0f, 1.0f, 0.0f, // green
239 1.0f, 1.0f, 0.0f, // yellow
240 // bottom
241 0.0f, 0.0f, 0.0f, // black
242 1.0f, 0.0f, 0.0f, // red
243 0.0f, 0.0f, 1.0f, // blue
244 1.0f, 0.0f, 1.0f // magenta
245 };
247 static const GLfloat vNormals[] = {
248 // front
249 +0.0f, +0.0f, +1.0f, // forward
250 +0.0f, +0.0f, +1.0f, // forward
251 +0.0f, +0.0f, +1.0f, // forward
252 +0.0f, +0.0f, +1.0f, // forward
253 // back
254 +0.0f, +0.0f, -1.0f, // backbard
255 +0.0f, +0.0f, -1.0f, // backbard
256 +0.0f, +0.0f, -1.0f, // backbard
257 +0.0f, +0.0f, -1.0f, // backbard
258 // right
259 +1.0f, +0.0f, +0.0f, // right
260 +1.0f, +0.0f, +0.0f, // right
261 +1.0f, +0.0f, +0.0f, // right
262 +1.0f, +0.0f, +0.0f, // right
263 // left
264 -1.0f, +0.0f, +0.0f, // left
265 -1.0f, +0.0f, +0.0f, // left
266 -1.0f, +0.0f, +0.0f, // left
267 -1.0f, +0.0f, +0.0f, // left
268 // top
269 +0.0f, +1.0f, +0.0f, // up
270 +0.0f, +1.0f, +0.0f, // up
271 +0.0f, +1.0f, +0.0f, // up
272 +0.0f, +1.0f, +0.0f, // up
273 // bottom
274 +0.0f, -1.0f, +0.0f, // down
275 +0.0f, -1.0f, +0.0f, // down
276 +0.0f, -1.0f, +0.0f, // down
277 +0.0f, -1.0f, +0.0f // down
278 };
280 static const EGLint context_attribs[] = {
281 EGL_CONTEXT_CLIENT_VERSION, 2,
282 EGL_NONE
283 };
285 static const EGLint config_attribs[] = {
286 EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
287 EGL_RED_SIZE, 1,
288 EGL_GREEN_SIZE, 1,
289 EGL_BLUE_SIZE, 1,
290 EGL_ALPHA_SIZE, 0,
291 EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
292 EGL_NONE
293 };
295 static const char *vertex_shader_source =
296 "uniform mat4 modelviewMatrix; \n"
297 "uniform mat4 modelviewprojectionMatrix;\n"
298 "uniform mat3 normalMatrix; \n"
299 " \n"
300 "attribute vec4 in_position; \n"
301 "attribute vec3 in_normal; \n"
302 "attribute vec4 in_color; \n"
303 "\n"
304 "vec4 lightSource = vec4(2.0, 2.0, 20.0, 0.0);\n"
305 " \n"
306 "varying vec4 vVaryingColor; \n"
307 " \n"
308 "void main() \n"
309 "{ \n"
310 " gl_Position = modelviewprojectionMatrix * in_position;\n"
311 " vec3 vEyeNormal = normalMatrix * in_normal;\n"
312 " vec4 vPosition4 = modelviewMatrix * in_position;\n"
313 " vec3 vPosition3 = vPosition4.xyz / vPosition4.w;\n"
314 " vec3 vLightDir = normalize(lightSource.xyz - vPosition3);\n"
315 " float diff = max(0.0, dot(vEyeNormal, vLightDir));\n"
316 " vVaryingColor = vec4(diff * in_color.rgb, 1.0);\n"
317 "} \n";
319 static const char *fragment_shader_source =
320 "precision mediump float; \n"
321 " \n"
322 "varying vec4 vVaryingColor; \n"
323 " \n"
324 "void main() \n"
325 "{ \n"
326 " gl_FragColor = vVaryingColor; \n"
327 "} \n";
329 gl.display = eglGetDisplay(gbm.dev);
331 if (!eglInitialize(gl.display, &major, &minor)) {
332 printf("failed to initialize\n");
333 return -1;
334 }
336 printf("Using display %p with EGL version %d.%d\n",
337 gl.display, major, minor);
339 printf("EGL Version \"%s\"\n", eglQueryString(gl.display, EGL_VERSION));
340 printf("EGL Vendor \"%s\"\n", eglQueryString(gl.display, EGL_VENDOR));
341 printf("EGL Extensions \"%s\"\n", eglQueryString(gl.display, EGL_EXTENSIONS));
343 if (!eglBindAPI(EGL_OPENGL_ES_API)) {
344 printf("failed to bind api EGL_OPENGL_ES_API\n");
345 return -1;
346 }
348 if (!eglChooseConfig(gl.display, config_attribs, &gl.config, 1, &n) || n != 1) {
349 printf("failed to choose config: %d\n", n);
350 return -1;
351 }
353 gl.context = eglCreateContext(gl.display, gl.config,
354 EGL_NO_CONTEXT, context_attribs);
355 if (gl.context == NULL) {
356 printf("failed to create context\n");
357 return -1;
358 }
360 gl.surface = eglCreateWindowSurface(gl.display, gl.config, gbm.surface, NULL);
361 if (gl.surface == EGL_NO_SURFACE) {
362 printf("failed to create egl surface\n");
363 return -1;
364 }
366 /* connect the context to the surface */
367 eglMakeCurrent(gl.display, gl.surface, gl.surface, gl.context);
370 vertex_shader = glCreateShader(GL_VERTEX_SHADER);
372 glShaderSource(vertex_shader, 1, &vertex_shader_source, NULL);
373 glCompileShader(vertex_shader);
375 glGetShaderiv(vertex_shader, GL_COMPILE_STATUS, &ret);
376 if (!ret) {
377 char *log;
379 printf("vertex shader compilation failed!:\n");
380 glGetShaderiv(vertex_shader, GL_INFO_LOG_LENGTH, &ret);
381 if (ret > 1) {
382 log = malloc(ret);
383 glGetShaderInfoLog(vertex_shader, ret, NULL, log);
384 printf("%s", log);
385 }
387 return -1;
388 }
390 fragment_shader = glCreateShader(GL_FRAGMENT_SHADER);
392 glShaderSource(fragment_shader, 1, &fragment_shader_source, NULL);
393 glCompileShader(fragment_shader);
395 glGetShaderiv(fragment_shader, GL_COMPILE_STATUS, &ret);
396 if (!ret) {
397 char *log;
399 printf("fragment shader compilation failed!:\n");
400 glGetShaderiv(fragment_shader, GL_INFO_LOG_LENGTH, &ret);
402 if (ret > 1) {
403 log = malloc(ret);
404 glGetShaderInfoLog(fragment_shader, ret, NULL, log);
405 printf("%s", log);
406 }
408 return -1;
409 }
411 gl.program = glCreateProgram();
413 glAttachShader(gl.program, vertex_shader);
414 glAttachShader(gl.program, fragment_shader);
416 glBindAttribLocation(gl.program, 0, "in_position");
417 glBindAttribLocation(gl.program, 1, "in_normal");
418 glBindAttribLocation(gl.program, 2, "in_color");
420 glLinkProgram(gl.program);
422 glGetProgramiv(gl.program, GL_LINK_STATUS, &ret);
423 if (!ret) {
424 char *log;
426 printf("program linking failed!:\n");
427 glGetProgramiv(gl.program, GL_INFO_LOG_LENGTH, &ret);
429 if (ret > 1) {
430 log = malloc(ret);
431 glGetProgramInfoLog(gl.program, ret, NULL, log);
432 printf("%s", log);
433 }
435 return -1;
436 }
438 glUseProgram(gl.program);
440 gl.modelviewmatrix = glGetUniformLocation(gl.program, "modelviewMatrix");
441 gl.modelviewprojectionmatrix = glGetUniformLocation(gl.program, "modelviewprojectionMatrix");
442 gl.normalmatrix = glGetUniformLocation(gl.program, "normalMatrix");
444 glViewport(0, 0, drm.mode->hdisplay, drm.mode->vdisplay);
445 glEnable(GL_CULL_FACE);
447 gl.positionsoffset = 0;
448 gl.colorsoffset = sizeof(vVertices);
449 gl.normalsoffset = sizeof(vVertices) + sizeof(vColors);
450 glGenBuffers(1, &gl.vbo);
451 glBindBuffer(GL_ARRAY_BUFFER, gl.vbo);
452 glBufferData(GL_ARRAY_BUFFER, sizeof(vVertices) + sizeof(vColors) + sizeof(vNormals), 0, GL_STATIC_DRAW);
453 glBufferSubData(GL_ARRAY_BUFFER, gl.positionsoffset, sizeof(vVertices), &vVertices[0]);
454 glBufferSubData(GL_ARRAY_BUFFER, gl.colorsoffset, sizeof(vColors), &vColors[0]);
455 glBufferSubData(GL_ARRAY_BUFFER, gl.normalsoffset, sizeof(vNormals), &vNormals[0]);
456 glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, (const GLvoid*)gl.positionsoffset);
457 glEnableVertexAttribArray(0);
458 glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 0, (const GLvoid*)gl.normalsoffset);
459 glEnableVertexAttribArray(1);
460 glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, 0, (const GLvoid*)gl.colorsoffset);
461 glEnableVertexAttribArray(2);
463 return 0;
464 }
466 static void draw(uint32_t i)
467 {
468 ESMatrix modelview;
470 /* clear the color buffer */
471 glClearColor(0.5, 0.5, 0.5, 1.0);
472 glClear(GL_COLOR_BUFFER_BIT);
474 esMatrixLoadIdentity(&modelview);
475 esTranslate(&modelview, 0.0f, 0.0f, -8.0f);
476 esRotate(&modelview, 45.0f + (0.25f * i), 1.0f, 0.0f, 0.0f);
477 esRotate(&modelview, 45.0f - (0.5f * i), 0.0f, 1.0f, 0.0f);
478 esRotate(&modelview, 10.0f + (0.15f * i), 0.0f, 0.0f, 1.0f);
480 GLfloat aspect = (GLfloat)(drm.mode->vdisplay) / (GLfloat)(drm.mode->hdisplay);
482 ESMatrix projection;
483 esMatrixLoadIdentity(&projection);
484 esFrustum(&projection, -2.8f, +2.8f, -2.8f * aspect, +2.8f * aspect, 6.0f, 10.0f);
486 ESMatrix modelviewprojection;
487 esMatrixLoadIdentity(&modelviewprojection);
488 esMatrixMultiply(&modelviewprojection, &modelview, &projection);
490 float normal[9];
491 normal[0] = modelview.m[0][0];
492 normal[1] = modelview.m[0][1];
493 normal[2] = modelview.m[0][2];
494 normal[3] = modelview.m[1][0];
495 normal[4] = modelview.m[1][1];
496 normal[5] = modelview.m[1][2];
497 normal[6] = modelview.m[2][0];
498 normal[7] = modelview.m[2][1];
499 normal[8] = modelview.m[2][2];
501 glUniformMatrix4fv(gl.modelviewmatrix, 1, GL_FALSE, &modelview.m[0][0]);
502 glUniformMatrix4fv(gl.modelviewprojectionmatrix, 1, GL_FALSE, &modelviewprojection.m[0][0]);
503 glUniformMatrix3fv(gl.normalmatrix, 1, GL_FALSE, normal);
505 glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
506 glDrawArrays(GL_TRIANGLE_STRIP, 4, 4);
507 glDrawArrays(GL_TRIANGLE_STRIP, 8, 4);
508 glDrawArrays(GL_TRIANGLE_STRIP, 12, 4);
509 glDrawArrays(GL_TRIANGLE_STRIP, 16, 4);
510 glDrawArrays(GL_TRIANGLE_STRIP, 20, 4);
511 }
513 static void
514 drm_fb_destroy_callback(struct gbm_bo *bo, void *data)
515 {
516 struct drm_fb *fb = data;
517 struct gbm_device *gbm = gbm_bo_get_device(bo);
519 if (fb->fb_id)
520 drmModeRmFB(drm.fd, fb->fb_id);
522 free(fb);
523 }
525 static struct drm_fb * drm_fb_get_from_bo(struct gbm_bo *bo)
526 {
527 struct drm_fb *fb = gbm_bo_get_user_data(bo);
528 uint32_t width, height, stride, handle;
529 int ret;
531 if (fb)
532 return fb;
534 fb = calloc(1, sizeof *fb);
535 fb->bo = bo;
537 width = gbm_bo_get_width(bo);
538 height = gbm_bo_get_height(bo);
539 stride = gbm_bo_get_stride(bo);
540 handle = gbm_bo_get_handle(bo).u32;
542 ret = drmModeAddFB(drm.fd, width, height, 24, 32, stride, handle, &fb->fb_id);
543 if (ret) {
544 printf("failed to create fb: %s\n", strerror(errno));
545 free(fb);
546 return NULL;
547 }
549 gbm_bo_set_user_data(bo, fb, drm_fb_destroy_callback);
551 return fb;
552 }
554 static void page_flip_handler(int fd, unsigned int frame,
555 unsigned int sec, unsigned int usec, void *data)
556 {
557 int *waiting_for_flip = data;
558 *waiting_for_flip = 0;
559 }
561 int main(int argc, char *argv[])
562 {
563 fd_set fds;
564 drmEventContext evctx = {
565 .version = DRM_EVENT_CONTEXT_VERSION,
566 .page_flip_handler = page_flip_handler,
567 };
568 struct gbm_bo *bo;
569 struct drm_fb *fb;
570 uint32_t i = 0;
571 int ret;
573 ret = init_drm();
574 if (ret) {
575 printf("failed to initialize DRM\n");
576 return ret;
577 }
579 FD_ZERO(&fds);
580 FD_SET(0, &fds);
581 FD_SET(drm.fd, &fds);
583 ret = init_gbm();
584 if (ret) {
585 printf("failed to initialize GBM\n");
586 return ret;
587 }
589 ret = init_gl();
590 if (ret) {
591 printf("failed to initialize EGL\n");
592 return ret;
593 }
595 /* clear the color buffer */
596 glClearColor(0.5, 0.5, 0.5, 1.0);
597 glClear(GL_COLOR_BUFFER_BIT);
598 eglSwapBuffers(gl.display, gl.surface);
599 bo = gbm_surface_lock_front_buffer(gbm.surface);
600 fb = drm_fb_get_from_bo(bo);
602 /* set mode: */
603 ret = drmModeSetCrtc(drm.fd, drm.crtc_id, fb->fb_id, 0, 0,
604 &drm.connector_id, 1, drm.mode);
605 if (ret) {
606 printf("failed to set mode: %s\n", strerror(errno));
607 return ret;
608 }
610 while (1) {
611 struct gbm_bo *next_bo;
612 int waiting_for_flip = 1;
614 draw(i++);
616 eglSwapBuffers(gl.display, gl.surface);
617 next_bo = gbm_surface_lock_front_buffer(gbm.surface);
618 fb = drm_fb_get_from_bo(next_bo);
620 /*
621 * Here you could also update drm plane layers if you want
622 * hw composition
623 */
625 ret = drmModePageFlip(drm.fd, drm.crtc_id, fb->fb_id,
626 DRM_MODE_PAGE_FLIP_EVENT, &waiting_for_flip);
627 if (ret) {
628 printf("failed to queue page flip: %s\n", strerror(errno));
629 return -1;
630 }
632 while (waiting_for_flip) {
633 ret = select(drm.fd + 1, &fds, NULL, NULL, NULL);
634 if (ret < 0) {
635 printf("select err: %s\n", strerror(errno));
636 return ret;
637 } else if (ret == 0) {
638 printf("select timeout!\n");
639 return -1;
640 } else if (FD_ISSET(0, &fds)) {
641 printf("user interrupted!\n");
642 break;
643 }
644 drmHandleEvent(drm.fd, &evctx);
645 }
647 /* release last buffer to render on again: */
648 gbm_surface_release_buffer(gbm.surface, bo);
649 bo = next_bo;
650 }
652 return ret;
653 }