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