1 /*
2 * Copyright (c) 2012 Arvin Schnell <arvin.schnell@gmail.com>
3 * Copyright (c) 2012 Rob Clark <rob@ti.com>
4 * Copyright (c) 2013 Anand Balagopalakrishnan <anandb@ti.com>
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a
7 * copy of this software and associated documentation files (the "Software"),
8 * to deal in the Software without restriction, including without limitation
9 * the rights to use, copy, modify, merge, publish, distribute, sub license,
10 * and/or sell copies of the Software, and to permit persons to whom the
11 * Software is furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice (including the
14 * next paragraph) shall be included in all copies or substantial portions
15 * of the Software.
16 *
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
20 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
22 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
23 * DEALINGS IN THE SOFTWARE.
24 */
26 /* Based on a egl cube test app originally written by Arvin Schnell */
28 #include <sys/types.h>
29 #include <sys/stat.h>
30 #include <fcntl.h>
31 #include <string.h>
32 #include <stdbool.h>
33 #include <stdio.h>
34 #include <stdlib.h>
35 #include <unistd.h>
36 #include <errno.h>
37 #include <signal.h>
38 #include <pthread.h>
40 #include <xf86drm.h>
41 #include <xf86drmMode.h>
42 #include <gbm.h>
43 #include <time.h>
44 #include <semaphore.h>
46 #include "esUtil.h"
48 #define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
50 #define printc(fmt, ...) do { struct timespec tp; clock_gettime(CLOCK_MONOTONIC, &tp); printf("%d.%ld"fmt, tp.tv_sec, tp.tv_nsec, ##__VA_ARGS__); } while(0)
51 #define printc(fmt, ...)
52 #define MAX_DISPLAYS (4)
53 #define MAX_PLANES (4)
54 #define CONFIG_WIDTH (320)
55 #define CONFIG_HEIGHT (240)
56 uint8_t DISP_ID = 0;
57 uint8_t all_display = 0;
58 int8_t connector_id = -1;
60 struct gl {
61 EGLDisplay display;
62 EGLConfig config;
63 EGLContext context;
64 EGLSurface surface;
65 GLuint program;
66 GLint modelviewmatrix, modelviewprojectionmatrix, normalmatrix;
67 GLuint vbo;
68 GLuint positionsoffset, colorsoffset, normalsoffset;
69 GLuint vertex_shader, fragment_shader;
71 };
73 struct gl_params {
74 EGLNativeDisplayType display;
75 EGLNativeWindowType window;
76 uint32_t width;
77 uint32_t height;
78 int32_t frame_count;
79 bool running;
80 sem_t render;
81 sem_t disp;
82 };
84 static struct {
85 struct gbm_device *dev;
86 struct gbm_surface *surface[8];
87 struct gbm_bo *flip_bo;
88 uint32_t count;
89 } gbm;
91 static struct {
92 int fd;
93 uint32_t ndisp;
94 uint32_t crtc_id[MAX_DISPLAYS];
95 uint32_t plane_id[MAX_PLANES];
96 uint32_t num_planes;
97 uint32_t connector_id[MAX_DISPLAYS];
98 uint32_t resource_id;
99 uint32_t encoder[MAX_DISPLAYS];
100 drmModeModeInfo *mode[MAX_DISPLAYS];
101 drmModeConnector *connectors[MAX_DISPLAYS];
102 } drm;
104 struct drm_fb {
105 struct gbm_bo *bo;
106 uint32_t fb_id;
107 };
109 static void exit_gl(void *arg)
110 {
111 struct gl *gl = arg;
113 glDeleteProgram(gl->program);
114 glDeleteBuffers(1, &gl->vbo);
115 glDeleteShader(gl->fragment_shader);
116 glDeleteShader(gl->vertex_shader);
117 eglDestroySurface(gl->display, gl->surface);
118 eglDestroyContext(gl->display, gl->context);
119 eglTerminate(gl->display);
120 return;
121 }
123 static void *gl_thread (void *arg)
124 {
125 EGLint major, minor, n;
126 GLint ret;
127 struct gl *gl;
128 struct gl_params *params = arg;
129 uint32_t frame_count = params->frame_count;
131 static const GLfloat vVertices[] = {
132 // front
133 -1.0f, -1.0f, +1.0f, // point blue
134 +1.0f, -1.0f, +1.0f, // point magenta
135 -1.0f, +1.0f, +1.0f, // point cyan
136 +1.0f, +1.0f, +1.0f, // point white
137 // back
138 +1.0f, -1.0f, -1.0f, // point red
139 -1.0f, -1.0f, -1.0f, // point black
140 +1.0f, +1.0f, -1.0f, // point yellow
141 -1.0f, +1.0f, -1.0f, // point green
142 // right
143 +1.0f, -1.0f, +1.0f, // point magenta
144 +1.0f, -1.0f, -1.0f, // point red
145 +1.0f, +1.0f, +1.0f, // point white
146 +1.0f, +1.0f, -1.0f, // point yellow
147 // left
148 -1.0f, -1.0f, -1.0f, // point black
149 -1.0f, -1.0f, +1.0f, // point blue
150 -1.0f, +1.0f, -1.0f, // point green
151 -1.0f, +1.0f, +1.0f, // point cyan
152 // top
153 -1.0f, +1.0f, +1.0f, // point cyan
154 +1.0f, +1.0f, +1.0f, // point white
155 -1.0f, +1.0f, -1.0f, // point green
156 +1.0f, +1.0f, -1.0f, // point yellow
157 // bottom
158 -1.0f, -1.0f, -1.0f, // point black
159 +1.0f, -1.0f, -1.0f, // point red
160 -1.0f, -1.0f, +1.0f, // point blue
161 +1.0f, -1.0f, +1.0f // point magenta
162 };
164 static const GLfloat vColors[] = {
165 // front
166 0.0f, 0.0f, 1.0f, // blue
167 1.0f, 0.0f, 1.0f, // magenta
168 0.0f, 1.0f, 1.0f, // cyan
169 1.0f, 1.0f, 1.0f, // white
170 // back
171 1.0f, 0.0f, 0.0f, // red
172 0.0f, 0.0f, 0.0f, // black
173 1.0f, 1.0f, 0.0f, // yellow
174 0.0f, 1.0f, 0.0f, // green
175 // right
176 1.0f, 0.0f, 1.0f, // magenta
177 1.0f, 0.0f, 0.0f, // red
178 1.0f, 1.0f, 1.0f, // white
179 1.0f, 1.0f, 0.0f, // yellow
180 // left
181 0.0f, 0.0f, 0.0f, // black
182 0.0f, 0.0f, 1.0f, // blue
183 0.0f, 1.0f, 0.0f, // green
184 0.0f, 1.0f, 1.0f, // cyan
185 // top
186 0.0f, 1.0f, 1.0f, // cyan
187 1.0f, 1.0f, 1.0f, // white
188 0.0f, 1.0f, 0.0f, // green
189 1.0f, 1.0f, 0.0f, // yellow
190 // bottom
191 0.0f, 0.0f, 0.0f, // black
192 1.0f, 0.0f, 0.0f, // red
193 0.0f, 0.0f, 1.0f, // blue
194 1.0f, 0.0f, 1.0f // magenta
195 };
197 static const GLfloat vNormals[] = {
198 // front
199 +0.0f, +0.0f, +1.0f, // forward
200 +0.0f, +0.0f, +1.0f, // forward
201 +0.0f, +0.0f, +1.0f, // forward
202 +0.0f, +0.0f, +1.0f, // forward
203 // back
204 +0.0f, +0.0f, -1.0f, // backbard
205 +0.0f, +0.0f, -1.0f, // backbard
206 +0.0f, +0.0f, -1.0f, // backbard
207 +0.0f, +0.0f, -1.0f, // backbard
208 // right
209 +1.0f, +0.0f, +0.0f, // right
210 +1.0f, +0.0f, +0.0f, // right
211 +1.0f, +0.0f, +0.0f, // right
212 +1.0f, +0.0f, +0.0f, // right
213 // left
214 -1.0f, +0.0f, +0.0f, // left
215 -1.0f, +0.0f, +0.0f, // left
216 -1.0f, +0.0f, +0.0f, // left
217 -1.0f, +0.0f, +0.0f, // left
218 // top
219 +0.0f, +1.0f, +0.0f, // up
220 +0.0f, +1.0f, +0.0f, // up
221 +0.0f, +1.0f, +0.0f, // up
222 +0.0f, +1.0f, +0.0f, // up
223 // bottom
224 +0.0f, -1.0f, +0.0f, // down
225 +0.0f, -1.0f, +0.0f, // down
226 +0.0f, -1.0f, +0.0f, // down
227 +0.0f, -1.0f, +0.0f // down
228 };
230 static const EGLint context_attribs[] = {
231 EGL_CONTEXT_CLIENT_VERSION, 2,
232 EGL_NONE
233 };
235 static const EGLint config_attribs[] = {
236 EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
237 EGL_RED_SIZE, 1,
238 EGL_GREEN_SIZE, 1,
239 EGL_BLUE_SIZE, 1,
240 EGL_ALPHA_SIZE, 0,
241 EGL_DEPTH_SIZE, 8,
242 EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
243 EGL_NONE
244 };
246 static const char *vertex_shader_source =
247 "uniform mat4 modelviewMatrix; \n"
248 "uniform mat4 modelviewprojectionMatrix;\n"
249 "uniform mat3 normalMatrix; \n"
250 " \n"
251 "attribute vec4 in_position; \n"
252 "attribute vec3 in_normal; \n"
253 "attribute vec4 in_color; \n"
254 "\n"
255 "vec4 lightSource = vec4(2.0, 2.0, 20.0, 0.0);\n"
256 " \n"
257 "varying vec4 vVaryingColor; \n"
258 " \n"
259 "void main() \n"
260 "{ \n"
261 " gl_Position = modelviewprojectionMatrix * in_position;\n"
262 " vec3 vEyeNormal = normalMatrix * in_normal;\n"
263 " vec4 vPosition4 = modelviewMatrix * in_position;\n"
264 " vec3 vPosition3 = vPosition4.xyz / vPosition4.w;\n"
265 " vec3 vLightDir = normalize(lightSource.xyz - vPosition3);\n"
266 " float diff = max(0.0, dot(vEyeNormal, vLightDir));\n"
267 " vVaryingColor = vec4(diff * in_color.rgb, 1.0);\n"
268 "} \n";
270 static const char *fragment_shader_source =
271 "precision mediump float; \n"
272 " \n"
273 "varying vec4 vVaryingColor; \n"
274 " \n"
275 "void main() \n"
276 "{ \n"
277 " gl_FragColor = vVaryingColor; \n"
278 "} \n";
279 pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);
280 gl = calloc(1, sizeof(struct gl));
281 if(!gl) {
282 printf("GL memory allocation failed\n");
283 return NULL;
284 }
286 pthread_cleanup_push(exit_gl, gl);
287 pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
289 gl->display = eglGetDisplay(params->display);
291 if (!eglInitialize(gl->display, &major, &minor)) {
292 printf("failed to initialize\n");
293 return NULL;
294 }
296 if (!eglBindAPI(EGL_OPENGL_ES_API)) {
297 printf("failed to bind api EGL_OPENGL_ES_API\n");
298 return NULL;
299 }
301 if (!eglChooseConfig(gl->display, config_attribs, &gl->config, 1, &n) || n != 1) {
302 printf("failed to choose config: %d\n", n);
303 return NULL;
304 }
306 gl->context = eglCreateContext(gl->display, gl->config,
307 EGL_NO_CONTEXT, context_attribs);
308 if (gl->context == NULL) {
309 printf("failed to create context\n");
310 return NULL;
311 }
313 gl->surface = eglCreateWindowSurface(gl->display, gl->config, params->window, NULL);
314 if (gl->surface == EGL_NO_SURFACE) {
315 printf("failed to create egl surface\n");
316 return NULL;
317 }
319 /* connect the context to the surface */
320 eglMakeCurrent(gl->display, gl->surface, gl->surface, gl->context);
323 gl->vertex_shader = glCreateShader(GL_VERTEX_SHADER);
325 glShaderSource(gl->vertex_shader, 1, &vertex_shader_source, NULL);
326 glCompileShader(gl->vertex_shader);
328 glGetShaderiv(gl->vertex_shader, GL_COMPILE_STATUS, &ret);
329 if (!ret) {
330 char *log;
332 printf("vertex shader compilation failed!:\n");
333 glGetShaderiv(gl->vertex_shader, GL_INFO_LOG_LENGTH, &ret);
334 if (ret > 1) {
335 log = malloc(ret);
336 glGetShaderInfoLog(gl->vertex_shader, ret, NULL, log);
337 printf("%s", log);
338 }
340 return NULL;
341 }
343 gl->fragment_shader = glCreateShader(GL_FRAGMENT_SHADER);
345 glShaderSource(gl->fragment_shader, 1, &fragment_shader_source, NULL);
346 glCompileShader(gl->fragment_shader);
348 glGetShaderiv(gl->fragment_shader, GL_COMPILE_STATUS, &ret);
349 if (!ret) {
350 char *log;
352 printf("fragment shader compilation failed!:\n");
353 glGetShaderiv(gl->fragment_shader, GL_INFO_LOG_LENGTH, &ret);
355 if (ret > 1) {
356 log = malloc(ret);
357 glGetShaderInfoLog(gl->fragment_shader, ret, NULL, log);
358 printf("%s", log);
359 }
361 return NULL;
362 }
364 gl->program = glCreateProgram();
366 glAttachShader(gl->program, gl->vertex_shader);
367 glAttachShader(gl->program, gl->fragment_shader);
369 glBindAttribLocation(gl->program, 0, "in_position");
370 glBindAttribLocation(gl->program, 1, "in_normal");
371 glBindAttribLocation(gl->program, 2, "in_color");
373 glLinkProgram(gl->program);
375 glGetProgramiv(gl->program, GL_LINK_STATUS, &ret);
376 if (!ret) {
377 char *log;
379 printf("program linking failed!:\n");
380 glGetProgramiv(gl->program, GL_INFO_LOG_LENGTH, &ret);
382 if (ret > 1) {
383 log = malloc(ret);
384 glGetProgramInfoLog(gl->program, ret, NULL, log);
385 printf("%s", log);
386 }
388 return NULL;
389 }
391 glUseProgram(gl->program);
393 gl->modelviewmatrix = glGetUniformLocation(gl->program, "modelviewMatrix");
394 gl->modelviewprojectionmatrix = glGetUniformLocation(gl->program, "modelviewprojectionMatrix");
395 gl->normalmatrix = glGetUniformLocation(gl->program, "normalMatrix");
397 glViewport(0, 0, params->width, params->height);
398 glEnable(GL_CULL_FACE);
400 gl->positionsoffset = 0;
401 gl->colorsoffset = sizeof(vVertices);
402 gl->normalsoffset = sizeof(vVertices) + sizeof(vColors);
403 glGenBuffers(1, &gl->vbo);
404 glBindBuffer(GL_ARRAY_BUFFER, gl->vbo);
405 glBufferData(GL_ARRAY_BUFFER, sizeof(vVertices) + sizeof(vColors) + sizeof(vNormals), 0, GL_STATIC_DRAW);
406 glBufferSubData(GL_ARRAY_BUFFER, gl->positionsoffset, sizeof(vVertices), &vVertices[0]);
407 glBufferSubData(GL_ARRAY_BUFFER, gl->colorsoffset, sizeof(vColors), &vColors[0]);
408 glBufferSubData(GL_ARRAY_BUFFER, gl->normalsoffset, sizeof(vNormals), &vNormals[0]);
409 glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, (const GLvoid*)gl->positionsoffset);
410 glEnableVertexAttribArray(0);
411 glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 0, (const GLvoid*)gl->normalsoffset);
412 glEnableVertexAttribArray(1);
413 glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, 0, (const GLvoid*)gl->colorsoffset);
414 glEnableVertexAttribArray(2);
416 int i = 0;
417 while (frame_count != 0 && params->running == true) {
418 ESMatrix modelview;
420 /* clear the color buffer */
422 glClearColor(0.5, 0.5, 0.5, 1.0);
423 glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
425 esMatrixLoadIdentity(&modelview);
426 esTranslate(&modelview, 0.0f, 0.0f, -8.0f);
427 esRotate(&modelview, 45.0f + (0.25f * i), 1.0f, 0.0f, 0.0f);
428 esRotate(&modelview, 45.0f - (0.5f * i), 0.0f, 1.0f, 0.0f);
429 esRotate(&modelview, 10.0f + (0.15f * i), 0.0f, 0.0f, 1.0f);
431 GLfloat aspect = (GLfloat)(drm.mode[DISP_ID]->vdisplay) / (GLfloat)(drm.mode[DISP_ID]->hdisplay);
433 ESMatrix projection;
434 esMatrixLoadIdentity(&projection);
435 esFrustum(&projection, -2.8f, +2.8f, -2.8f * aspect, +2.8f * aspect, 6.0f, 10.0f);
437 ESMatrix modelviewprojection;
438 esMatrixLoadIdentity(&modelviewprojection);
439 esMatrixMultiply(&modelviewprojection, &modelview, &projection);
441 float normal[9];
442 normal[0] = modelview.m[0][0];
443 normal[1] = modelview.m[0][1];
444 normal[2] = modelview.m[0][2];
445 normal[3] = modelview.m[1][0];
446 normal[4] = modelview.m[1][1];
447 normal[5] = modelview.m[1][2];
448 normal[6] = modelview.m[2][0];
449 normal[7] = modelview.m[2][1];
450 normal[8] = modelview.m[2][2];
452 glUniformMatrix4fv(gl->modelviewmatrix, 1, GL_FALSE, &modelview.m[0][0]);
453 glUniformMatrix4fv(gl->modelviewprojectionmatrix, 1, GL_FALSE, &modelviewprojection.m[0][0]);
454 glUniformMatrix3fv(gl->normalmatrix, 1, GL_FALSE, normal);
456 glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
457 glDrawArrays(GL_TRIANGLE_STRIP, 4, 4);
458 glDrawArrays(GL_TRIANGLE_STRIP, 8, 4);
459 glDrawArrays(GL_TRIANGLE_STRIP, 12, 4);
460 glDrawArrays(GL_TRIANGLE_STRIP, 16, 4);
461 glDrawArrays(GL_TRIANGLE_STRIP, 20, 4);
463 eglSwapBuffers(gl->display, gl->surface);
464 printc("#### swap buffers\n");
466 sem_post(¶ms->render);
468 i++;
469 frame_count--;
471 sem_wait(¶ms->disp);
473 }
474 pthread_cleanup_pop(1);
476 }
478 #define THR_CNT (3)
479 pthread_t draw_thread[MAX_PLANES];
480 uint32_t num_threads;
482 static int init_drm(void)
483 {
484 static const char *modules[] = {
485 "omapdrm", "i915", "radeon", "nouveau", "vmwgfx", "exynos"
486 };
487 drmModeRes *resources;
488 drmModePlaneResPtr planeRes;
489 drmModeConnector *connector = NULL;
490 drmModeEncoder *encoder = NULL;
491 int i, j;
492 uint32_t maxRes, curRes;
494 for (i = 0; i < ARRAY_SIZE(modules); i++) {
495 printf("trying to load module %s...", modules[i]);
496 drm.fd = drmOpen(modules[i], NULL);
497 if (drm.fd < 0) {
498 printf("failed.\n");
499 } else {
500 printf("success.\n");
501 break;
502 }
503 }
505 if (drm.fd < 0) {
506 printf("could not open drm device\n");
507 return -1;
508 }
510 resources = drmModeGetResources(drm.fd);
511 if (!resources) {
512 printf("drmModeGetResources failed: %s\n", strerror(errno));
513 return -1;
514 }
515 drm.resource_id = (uint32_t) resources;
517 /* find a connected connector: */
518 for (i = 0; i < resources->count_connectors; i++) {
519 connector = drmModeGetConnector(drm.fd, resources->connectors[i]);
520 if (connector->connection == DRM_MODE_CONNECTED) {
521 /* choose the first supported mode */
522 drm.mode[drm.ndisp] = &connector->modes[0];
523 drm.connector_id[drm.ndisp] = connector->connector_id;
525 for (j=0; j<resources->count_encoders; j++) {
526 encoder = drmModeGetEncoder(drm.fd, resources->encoders[j]);
527 if (encoder->encoder_id == connector->encoder_id)
528 break;
530 drmModeFreeEncoder(encoder);
531 encoder = NULL;
532 }
534 if (!encoder) {
535 printf("no encoder!\n");
536 return -1;
537 }
539 drm.encoder[drm.ndisp] = (uint32_t) encoder;
540 drm.crtc_id[drm.ndisp] = encoder->crtc_id;
541 drm.connectors[drm.ndisp] = connector;
543 printf("### Display [%d]: CRTC = %d, Connector = %d\n", drm.ndisp, drm.crtc_id[drm.ndisp], drm.connector_id[drm.ndisp]);
544 printf("\tMode chosen [%s] : Clock => %d, Vertical refresh => %d, Type => %d\n", drm.mode[drm.ndisp]->name, drm.mode[drm.ndisp]->clock, drm.mode[drm.ndisp]->vrefresh, drm.mode[drm.ndisp]->type);
545 printf("\tHorizontal => %d, %d, %d, %d, %d\n", drm.mode[drm.ndisp]->hdisplay, drm.mode[drm.ndisp]->hsync_start, drm.mode[drm.ndisp]->hsync_end, drm.mode[drm.ndisp]->htotal, drm.mode[drm.ndisp]->hskew);
546 printf("\tVertical => %d, %d, %d, %d, %d\n", drm.mode[drm.ndisp]->vdisplay, drm.mode[drm.ndisp]->vsync_start, drm.mode[drm.ndisp]->vsync_end, drm.mode[drm.ndisp]->vtotal, drm.mode[drm.ndisp]->vscan);
548 /* If a connector_id is specified, use the corresponding display */
549 if ((connector_id != -1) && (connector_id == drm.connector_id[drm.ndisp]))
550 DISP_ID = drm.ndisp;
552 /* If all displays are enabled, choose the connector with maximum
553 * resolution as the primary display */
554 if (all_display) {
555 maxRes = drm.mode[DISP_ID]->vdisplay * drm.mode[DISP_ID]->hdisplay;
556 curRes = drm.mode[drm.ndisp]->vdisplay * drm.mode[drm.ndisp]->hdisplay;
558 if (curRes > maxRes)
559 DISP_ID = drm.ndisp;
560 }
562 drm.ndisp++;
563 } else {
564 drmModeFreeConnector(connector);
565 }
566 }
570 if (drm.ndisp == 0) {
571 /* we could be fancy and listen for hotplug events and wait for
572 * a connector..
573 */
574 printf("no connected connector!\n");
575 return -1;
576 }
578 planeRes = drmModeGetPlaneResources (drm.fd);
579 for (i = 0; i < planeRes->count_planes; i++)
580 drm.plane_id[drm.num_planes++] = planeRes->planes[i];
582 return 0;
583 }
585 static int init_gbm(uint32_t surf_count, uint32_t width, uint32_t height)
586 {
587 int count;
589 gbm.dev = gbm_create_device(drm.fd);
590 gbm.flip_bo = gbm_bo_create(gbm.dev,
591 drm.mode[DISP_ID]->hdisplay,
592 drm.mode[DISP_ID]->vdisplay,
593 GBM_FORMAT_XRGB8888,
594 GBM_BO_USE_SCANOUT | GBM_BO_USE_RENDERING);
596 for(count = 0; count < surf_count; count++) {
597 gbm.surface[count] = gbm_surface_create(gbm.dev,
598 width, height,
599 GBM_FORMAT_XRGB8888,
600 GBM_BO_USE_SCANOUT | GBM_BO_USE_RENDERING);
601 if (!gbm.surface[count]) {
602 printf("failed to create gbm surface\n");
603 return -1;
604 }
605 gbm.count++;
606 }
608 return 0;
609 }
612 static void exit_gbm(void)
613 {
614 while(gbm.count--)
615 gbm_surface_destroy(gbm.surface[gbm.count]);
616 gbm_bo_destroy(gbm.flip_bo);
617 gbm_device_destroy(gbm.dev);
618 return;
619 }
621 static void exit_drm(void)
622 {
624 drmModeRes *resources;
625 int i;
627 resources = (drmModeRes *)drm.resource_id;
628 for (i = 0; i < resources->count_connectors; i++) {
629 drmModeFreeEncoder(drm.encoder[i]);
630 drmModeFreeConnector(drm.connectors[i]);
631 }
632 drmModeFreeResources(drm.resource_id);
633 drmClose(drm.fd);
634 return;
635 }
637 void cleanup_kmscube(void)
638 {
639 while(num_threads--) {
640 pthread_cancel(draw_thread[num_threads]);
641 pthread_join(draw_thread[num_threads], NULL);
642 }
643 exit_gbm();
644 exit_drm();
645 printf("Cleanup of GL, GBM and DRM completed\n");
646 return;
647 }
649 static void
650 drm_fb_destroy_callback(struct gbm_bo *bo, void *data)
651 {
652 struct drm_fb *fb = data;
653 struct gbm_device *gbm = gbm_bo_get_device(bo);
655 if (fb->fb_id)
656 drmModeRmFB(drm.fd, fb->fb_id);
658 free(fb);
659 }
661 static struct drm_fb * drm_fb_get_from_bo(struct gbm_bo *bo)
662 {
663 struct drm_fb *fb = gbm_bo_get_user_data(bo);
664 uint32_t width, height, stride, handle;
665 int ret;
667 if (fb)
668 return fb;
670 fb = calloc(1, sizeof *fb);
671 fb->bo = bo;
673 width = gbm_bo_get_width(bo);
674 height = gbm_bo_get_height(bo);
675 stride = gbm_bo_get_stride(bo);
676 handle = gbm_bo_get_handle(bo).u32;
678 ret = drmModeAddFB(drm.fd, width, height, 24, 32, stride, handle, &fb->fb_id);
679 if (ret) {
680 printf("failed to create fb: %s\n", strerror(errno));
681 free(fb);
682 return NULL;
683 }
685 gbm_bo_set_user_data(bo, fb, drm_fb_destroy_callback);
687 return fb;
688 }
690 static void page_flip_handler(int fd, unsigned int frame,
691 unsigned int sec, unsigned int usec, void *data)
692 {
693 int *waiting_for_flip = data;
694 *waiting_for_flip = 0;
695 }
697 void print_usage()
698 {
699 printf("Usage : kmscube <options>\n");
700 printf("\t-h : Help\n");
701 printf("\t-a : Enable all displays\n");
702 printf("\t-c <id> : Display using connector_id [if not specified, use the first connected connector]\n");
703 printf("\t-n <number> (optional): Number of frames to render\n");
704 }
706 int kms_signalhandler(int signum)
707 {
708 switch(signum) {
709 case SIGINT:
710 case SIGTERM:
711 /* Allow the pending page flip requests to be completed before
712 * the teardown sequence */
713 sleep(1);
714 printf("Handling signal number = %d\n", signum);
715 cleanup_kmscube();
716 break;
717 default:
718 printf("Unknown signal\n");
719 break;
720 }
721 exit(1);
722 }
724 int main(int argc, char *argv[])
725 {
726 fd_set fds;
727 drmEventContext evctx = {
728 .version = DRM_EVENT_CONTEXT_VERSION,
729 .page_flip_handler = page_flip_handler,
730 };
731 struct gbm_bo *bo = NULL;
732 struct drm_fb *flip_fb;
733 struct drm_fb *fb[THR_CNT];
734 uint32_t i = 0;
735 int ret;
736 int opt;
737 int frame_count = -1;
738 int count;
740 signal(SIGINT, kms_signalhandler);
741 signal(SIGTERM, kms_signalhandler);
743 while ((opt = getopt(argc, argv, "ahc:n:")) != -1) {
744 switch(opt) {
745 case 'a':
746 all_display = 1;
747 break;
749 case 'h':
750 print_usage();
751 return 0;
753 case 'c':
754 connector_id = atoi(optarg);
755 break;
756 case 'n':
757 frame_count = atoi(optarg);
758 break;
761 default:
762 printf("Undefined option %s\n", argv[optind]);
763 print_usage();
764 return -1;
765 }
766 }
768 if (all_display) {
769 printf("### Enabling all displays\n");
770 connector_id = -1;
771 }
773 ret = init_drm();
774 if (ret) {
775 printf("failed to initialize DRM\n");
776 return ret;
777 }
778 printf("### Primary display => ConnectorId = %d, Resolution = %dx%d\n",
779 drm.connector_id[DISP_ID], drm.mode[DISP_ID]->hdisplay,
780 drm.mode[DISP_ID]->vdisplay);
782 FD_ZERO(&fds);
783 FD_SET(drm.fd, &fds);
785 ret = init_gbm(THR_CNT, CONFIG_WIDTH, CONFIG_HEIGHT);
786 if (ret) {
787 printf("failed to initialize GBM\n");
788 return ret;
789 }
791 struct gl_params p[THR_CNT];
792 for(count = 0; count < THR_CNT; count++) {
793 p[count].display = gbm.dev;
794 p[count].window = gbm.surface[count];
795 p[count].frame_count = frame_count;
796 p[count].running = true;
797 p[count].width = CONFIG_WIDTH;
798 p[count].height = CONFIG_HEIGHT;
799 sem_init(&p[count].render, 0, 0);
800 sem_init(&p[count].disp, 0, 0);
801 ret = pthread_create(&draw_thread[num_threads++], NULL, gl_thread, &p[count]);
802 if (ret) {
803 printf("failed to initialize EGL\n");
804 return ret;
805 }
806 }
809 /* set mode: */
810 flip_fb = drm_fb_get_from_bo(gbm.flip_bo);
811 if (all_display) {
812 for (i=0; i<drm.ndisp; i++) {
813 ret = drmModeSetCrtc(drm.fd, drm.crtc_id[i], flip_fb->fb_id, 0, 0,
814 &drm.connector_id[i], 1, drm.mode[i]);
815 if (ret) {
816 printf("display %d failed to set mode: %s\n", i, strerror(errno));
817 return ret;
818 }
819 }
820 } else {
821 ret = drmModeSetCrtc(drm.fd, drm.crtc_id[DISP_ID], flip_fb->fb_id,
822 0, 0, &drm.connector_id[DISP_ID], 1, drm.mode[DISP_ID]);
823 if (ret) {
824 printf("display %d failed to set mode: %s\n", DISP_ID, strerror(errno));
825 return ret;
826 }
827 }
829 while (frame_count != 0) {
830 struct gbm_bo *next_bo[THR_CNT];
831 int waiting_for_flip = 1;
833 for(count = 0; count < THR_CNT; count++) {
834 sem_wait(&p[count].render);
835 next_bo[count] = gbm_surface_lock_front_buffer(gbm.surface[count]);
836 fb[count] = drm_fb_get_from_bo(next_bo[count]);
838 printc("#### locked %d buffer\n", count);
839 if(count < drm.num_planes) {
840 ret = drmModeSetPlane(drm.fd, drm.plane_id[count], drm.crtc_id[DISP_ID], fb[count]->fb_id, 0, count*CONFIG_WIDTH + count*30, 0, CONFIG_WIDTH, CONFIG_HEIGHT, 0, 0, CONFIG_WIDTH << 16, CONFIG_HEIGHT << 16);
841 if (ret) {
842 printf("failed to set plane: %s\n", strerror(errno));
843 return -1;
844 }
845 }
846 }
848 printc("#### set plane completed\n");
849 ret = drmModePageFlip(drm.fd, drm.crtc_id[DISP_ID], flip_fb->fb_id,
850 DRM_MODE_PAGE_FLIP_EVENT, &waiting_for_flip);
851 if (ret) {
852 printf("failed to queue page flip: %s\n", strerror(errno));
853 return -1;
854 }
856 printc("#### page flip completed\n");
858 while (waiting_for_flip) {
859 ret = select(drm.fd + 1, &fds, NULL, NULL, NULL);
860 if (ret < 0) {
861 printf("select err: %s\n", strerror(errno));
862 return ret;
863 } else if (ret == 0) {
864 printf("select timeout!\n");
865 return -1;
866 } else if (FD_ISSET(0, &fds)) {
867 continue;
868 }
869 drmHandleEvent(drm.fd, &evctx);
870 }
872 printc("#### page flip callback completed\n");
874 for (count = 0; count < THR_CNT; count++) {
875 gbm_surface_release_buffer(gbm.surface[count], next_bo[count]);
876 sem_post(&p[count].disp);
877 printc("#### released %d buffer\n", count);
878 }
880 if(frame_count >= 0)
881 frame_count--;
882 }
885 for(count = 0; count < THR_CNT; count++) {
886 p[count].running = false;
887 pthread_join(draw_thread[count], NULL);
888 }
891 cleanup_kmscube();
892 printf("\n Exiting kmscube \n");
894 return ret;
895 }