blob: 83058e94e8dea2f726d14203584ea8076ac0288b [file] [log] [blame]
Haixia Shie04ddfd2014-11-11 19:14:32 -08001#include <errno.h>
2#include <fcntl.h>
3#include <stdbool.h>
4#include <stdio.h>
5#include <string.h>
6#include <sys/types.h>
7#include <unistd.h>
8
9#define EGL_EGLEXT_PROTOTYPES
10#define GL_GLEXT_PROTOTYPES
11
12#include <gbm.h>
13#include <EGL/egl.h>
14#include <EGL/eglext.h>
15#include <drm_fourcc.h>
16#include <GLES2/gl2.h>
17#include <GLES2/gl2ext.h>
18#include <xf86drm.h>
19#include <xf86drmMode.h>
20
21const char * get_gl_error()
22{
23 switch (glGetError()) {
24 case GL_NO_ERROR:
25 return "No error has been recorded.";
26 case GL_INVALID_ENUM:
27 return "An unacceptable value is specified for an enumerated argument. The offending command is ignored and has no other side effect than to set the error flag.";
28 case GL_INVALID_VALUE:
29 return "A numeric argument is out of range. The offending command is ignored and has no other side effect than to set the error flag.";
30 case GL_INVALID_OPERATION:
31 return "The specified operation is not allowed in the current state. The offending command is ignored and has no other side effect than to set the error flag.";
32 case GL_INVALID_FRAMEBUFFER_OPERATION:
33 return "The command is trying to render to or read from the framebuffer while the currently bound framebuffer is not framebuffer complete (i.e. the return value from glCheckFramebufferStatus is not GL_FRAMEBUFFER_COMPLETE). The offending command is ignored and has no other side effect than to set the error flag.";
34 case GL_OUT_OF_MEMORY:
35 return "There is not enough memory left to execute the command. The state of the GL is undefined, except for the state of the error flags, after this error is recorded.";
36 default:
37 return "Unknown error";
38 }
39}
40
41const char * get_egl_error()
42{
43 switch (eglGetError()) {
44 case EGL_SUCCESS:
45 return "The last function succeeded without error.";
46 case EGL_NOT_INITIALIZED:
47 return "EGL is not initialized, or could not be initialized, for the specified EGL display connection.";
48 case EGL_BAD_ACCESS:
49 return "EGL cannot access a requested resource (for example a context is bound in another thread).";
50 case EGL_BAD_ALLOC:
51 return "EGL failed to allocate resources for the requested operation.";
52 case EGL_BAD_ATTRIBUTE:
53 return "An unrecognized attribute or attribute value was passed in the attribute list.";
54 case EGL_BAD_CONTEXT:
55 return "An EGLContext argument does not name a valid EGL rendering context.";
56 case EGL_BAD_CONFIG:
57 return "An EGLConfig argument does not name a valid EGL frame buffer configuration.";
58 case EGL_BAD_CURRENT_SURFACE:
59 return "The current surface of the calling thread is a window, pixel buffer or pixmap that is no longer valid.";
60 case EGL_BAD_DISPLAY:
61 return "An EGLDisplay argument does not name a valid EGL display connection.";
62 case EGL_BAD_SURFACE:
63 return "An EGLSurface argument does not name a valid surface (window, pixel buffer or pixmap) configured for GL rendering.";
64 case EGL_BAD_MATCH:
65 return "Arguments are inconsistent (for example, a valid context requires buffers not supplied by a valid surface).";
66 case EGL_BAD_PARAMETER:
67 return "One or more argument values are invalid.";
68 case EGL_BAD_NATIVE_PIXMAP:
69 return "A NativePixmapType argument does not refer to a valid native pixmap.";
70 case EGL_BAD_NATIVE_WINDOW:
71 return "A NativeWindowType argument does not refer to a valid native window.";
72 case EGL_CONTEXT_LOST:
73 return "A power management event has occurred. The application must destroy all contexts and reinitialise OpenGL ES state and objects to continue rendering.";
74 default:
75 return "Unknown error";
76 }
77}
78
79struct context {
80 int card_fd;
81 struct gbm_device * gbm_dev;
82 struct gbm_bo * gbm_buffer;
83
84 EGLDisplay egl_display;
85 EGLContext egl_ctx;
86 EGLImageKHR egl_image;
87
88 drmModeConnector * connector;
89 drmModeEncoder * encoder;
90 drmModeModeInfo * mode;
91 uint32_t drm_fb_id;
92
93 unsigned gl_fb;
94 unsigned gl_rb;
95
96};
97
98bool setup_drm(struct context * ctx)
99{
100 int fd = ctx->card_fd;
101 drmModeRes *resources = NULL;
102 drmModeConnector *connector = NULL;
103 drmModeEncoder *encoder = NULL;
104 int i, j;
105
106 resources = drmModeGetResources(fd);
107 if (!resources) {
108 fprintf(stderr, "drmModeGetResources failed\n");
109 return false;
110 }
111
112 for (i = 0; i < resources->count_connectors; i++) {
113 connector = drmModeGetConnector(fd, resources->connectors[i]);
114 if (connector == NULL)
115 continue;
116
117 if (connector->connection == DRM_MODE_CONNECTED &&
118 connector->count_modes > 0)
119 break;
120
121 drmModeFreeConnector(connector);
122 }
123
124 if (i == resources->count_connectors) {
125 fprintf(stderr, "no currently active connector found\n");
126 return false;
127 }
128
129 for (i = 0; i < resources->count_encoders; i++) {
130 encoder = drmModeGetEncoder(fd, resources->encoders[i]);
131
132 if (encoder == NULL)
133 continue;
134
135 for (j = 0; j < connector->count_encoders; j++) {
136 if (encoder->encoder_id == connector->encoders[j])
137 break;
138 }
139
140 if (j == connector->count_encoders) {
141 drmModeFreeEncoder(encoder);
142 continue;
143 }
144
145 break;
146 }
147
148 if (i == resources->count_encoders) {
149 fprintf(stderr, "no supported encoder found\n");
150 return false;
151 }
152
153 for (i = 0; i < resources->count_crtcs; i++) {
154 if (encoder->possible_crtcs & (1 << i)) {
155 encoder->crtc_id = resources->crtcs[i];
156 break;
157 }
158 }
159
160 if (i == resources->count_crtcs) {
161 fprintf(stderr, "no possible crtc found\n");
162 return false;
163 }
164
165 ctx->connector = connector;
166 ctx->encoder = encoder;
167 ctx->mode = &connector->modes[0];
168
169 return true;
170}
171
172float f(int i) {
173 int a = i % 40;
174 int b = (i / 40) % 6;
175 switch (b) {
176 case 0:
177 case 1:
178 return 0.0f;
179 case 3:
180 case 4:
181 return 1.0f;
182 case 2:
183 return (a / 40.0f);
184 case 5:
185 return 1.0f - (a / 40.0f);
186 default:
187 return 0.0f;
188 }
189}
190
191void draw(struct context * ctx)
192{
193 int i;
194 const GLchar *vertexShaderStr =
195 "attribute vec4 vPosition;\n"
196 "attribute vec4 vColor;\n"
197 "varying vec4 vFillColor;\n"
198 "void main() {\n"
199 " gl_Position = vPosition;\n"
200 " vFillColor = vColor;\n"
201 "}\n";
202 const GLchar *fragmentShaderStr =
203 "precision mediump float;\n"
204 "varying vec4 vFillColor;\n"
205 "void main() {\n"
206 " gl_FragColor = vFillColor;\n"
207 "}\n";
208 GLint vertexShader, fragmentShader, program, status;
209
210 vertexShader = glCreateShader(GL_VERTEX_SHADER);
211 if (!vertexShader) {
212 fprintf(stderr, "Failed to create vertex shader. Error=0x%x\n", glGetError());
213 return;
214 }
215 glShaderSource(vertexShader, 1, &vertexShaderStr, NULL);
216 glCompileShader(vertexShader);
217 glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &status);
218 if (!status) {
219 fprintf(stderr, "Failed to compile vertex shader. Error=0x%x\n", glGetError());
220 return;
221 }
222
223 fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
224 if (!fragmentShader) {
225 fprintf(stderr, "Failed to create fragment shader. Error=0x%x\n", glGetError());
226 return;
227 }
228 glShaderSource(fragmentShader, 1, &fragmentShaderStr, NULL);
229 glCompileShader(fragmentShader);
230 glGetShaderiv(fragmentShader, GL_COMPILE_STATUS, &status);
231 if (!status) {
232 fprintf(stderr, "Failed to compile fragment shader. Error=0x%x\n", glGetError());
233 return;
234 }
235
236
237 program = glCreateProgram();
238 if (!program) {
239 fprintf(stderr, "Failed to create program.\n");
240 return;
241 }
242 glAttachShader(program, vertexShader);
243 glAttachShader(program, fragmentShader);
244 glBindAttribLocation(program, 0, "vPosition");
245 glBindAttribLocation(program, 1, "vColor");
246 glLinkProgram(program);
247 glGetShaderiv(program, GL_LINK_STATUS, &status);
248 if (!status) {
249 fprintf(stderr, "Failed to link program.\n");
250 return;
251 }
252
253
254 glViewport(0, 0,(GLint) ctx->mode->hdisplay,
255 (GLint) ctx->mode->vdisplay);
256
257 for (i = 0; i <= 500; i++) {
258 GLfloat verts[] = {
259 0.0f, -0.5f, 0.0f,
260 -0.5f, 0.5f, 0.0f,
261 0.5f, 0.5f, 0.0f
262 };
263 GLfloat colors[] = {
264 1.0f, 0.0f, 0.0f, 1.0f,
265 0.0f, 1.0f, 0.0f, 1.0f,
266 0.0f, 0.0f, 1.0f, 1.0f
267 };
268
269 glClearColor(f(i), f(i + 80), f(i + 160), 0.0f);
270 glClear(GL_COLOR_BUFFER_BIT);
271
272 glUseProgram(program);
273 glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, verts);
274 glVertexAttribPointer(1, 4, GL_FLOAT, GL_FALSE, 0, colors);
275 glEnableVertexAttribArray(0);
276 glEnableVertexAttribArray(1);
277 glDrawArrays(GL_TRIANGLES, 0, 3);
278
279 usleep(1e6 / 120); /* 120 Hz */
280 glFinish();
281 drmModePageFlip(ctx->card_fd, ctx->encoder->crtc_id,
282 ctx->drm_fb_id, 0, NULL);
283 }
284
285 glDeleteProgram(program);
286}
287
288int main(int argc, char ** argv)
289{
290 int ret = 0;
291 struct context ctx;
292 EGLint egl_major, egl_minor;
293 const char * extensions;
294 uint32_t bo_handle;
295 uint32_t bo_stride;
296 int drm_prime_fd;
297 EGLint num_configs;
298 EGLConfig egl_config;
299 char* card_path = "/dev/dri/card1";
300
301 const EGLint config_attribs[] = {
302 EGL_RED_SIZE, 1,
303 EGL_GREEN_SIZE, 1,
304 EGL_BLUE_SIZE, 1,
305 EGL_DEPTH_SIZE, 1,
306 EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
307 EGL_NONE
308 };
309 const EGLint context_attribs[] = {
310 EGL_CONTEXT_CLIENT_VERSION, 2,
311 EGL_NONE
312 };
313
314 if (argc >= 2)
315 card_path = argv[1];
316
317 ctx.card_fd = open(card_path, O_RDWR);
318 if (ctx.card_fd < 0) {
319 fprintf(stderr, "failed to open %s\n", card_path);
320 ret = 1;
321 goto fail;
322 }
323
324 ctx.gbm_dev = gbm_create_device(ctx.card_fd);
325 if (!ctx.gbm_dev) {
326 fprintf(stderr, "failed to create gbm device\n");
327 ret = 1;
328 goto close_card;
329 }
330
331 ctx.egl_display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
332 if (ctx.egl_display == EGL_NO_DISPLAY) {
333 fprintf(stderr, "failed to get egl display\n");
334 ret = 1;
335 goto destroy_device;
336 }
337
338 if (!eglInitialize(ctx.egl_display, &egl_major, &egl_minor)) {
339 fprintf(stderr, "failed to initialize egl: %s\n",
340 get_egl_error());
341 ret = 1;
342 goto terminate_display;
343 }
344
345 fprintf(stderr, "EGL %d.%d\n", egl_major, egl_minor);
346 fprintf(stderr, "EGL %s\n",
347 eglQueryString(ctx.egl_display, EGL_VERSION));
348
349 extensions = eglQueryString(ctx.egl_display, EGL_EXTENSIONS);
350 fprintf(stderr, "EGL Extensions: %s\n", extensions);
351
352 if (!setup_drm(&ctx)) {
353 fprintf(stderr, "failed to setup drm resources\n");
354 ret = 1;
355 goto terminate_display;
356 }
357
358 fprintf(stderr, "display size: %dx%d\n",
359 ctx.mode->hdisplay, ctx.mode->vdisplay);
360
361 if (!eglChooseConfig(ctx.egl_display, config_attribs, NULL, 0,
362 &num_configs)) {
363 fprintf(stderr, "eglChooseConfig() failed with error: %x\n", eglGetError());
364 goto free_drm;
365 }
366 if (!eglChooseConfig(ctx.egl_display, config_attribs, &egl_config, 1,
367 &num_configs)) {
368 fprintf(stderr, "eglChooseConfig() failed with error: %x\n", eglGetError());
369 goto free_drm;
370 }
371
372 if (!eglBindAPI(EGL_OPENGL_ES_API)) {
373 fprintf(stderr, "failed to bind OpenGL ES: %s\n",
374 get_egl_error());
375 ret = 1;
376 goto free_drm;
377 }
378
379 ctx.egl_ctx = eglCreateContext(ctx.egl_display,
380 NULL /* No framebuffer */,
381 EGL_NO_CONTEXT /* No shared context */,
382 context_attribs);
383 if (ctx.egl_ctx == EGL_NO_CONTEXT) {
384 fprintf(stderr, "failed to create OpenGL ES Context: %s\n",
385 get_egl_error());
386 ret = 1;
387 goto free_drm;
388 }
389
390 if (!eglMakeCurrent(ctx.egl_display,
391 EGL_NO_SURFACE /* No default draw surface */,
392 EGL_NO_SURFACE /* No default draw read */,
393 ctx.egl_ctx)) {
394 fprintf(stderr, "failed to make the OpenGL ES Context current: %s\n",
395 get_egl_error());
396 ret = 1;
397 goto destroy_context;
398 }
399
400 fprintf(stderr, "GL extensions: %s\n", glGetString(GL_EXTENSIONS));
401
402 ctx.gbm_buffer = gbm_bo_create(ctx.gbm_dev,
403 ctx.mode->hdisplay, ctx.mode->vdisplay, GBM_BO_FORMAT_XRGB8888,
404 GBM_BO_USE_SCANOUT | GBM_BO_USE_RENDERING);
405
406 if (!ctx.gbm_buffer) {
407 fprintf(stderr, "failed to create buffer object\n");
408 ret = 1;
409 goto destroy_context;
410 }
411
412 bo_handle = gbm_bo_get_handle(ctx.gbm_buffer).u32;
413 bo_stride = gbm_bo_get_stride(ctx.gbm_buffer);
414
415 if (drmPrimeHandleToFD(ctx.card_fd, bo_handle, DRM_CLOEXEC,
416 &drm_prime_fd)) {
417 fprintf(stderr, "failed to turn handle into fd\n");
418 ret = 1;
419 goto destroy_buffer;
420 }
421
422 glGenFramebuffers(1, &ctx.gl_fb);
423 glBindFramebuffer(GL_FRAMEBUFFER, ctx.gl_fb);
424 glGenRenderbuffers(1, &ctx.gl_rb);
425 glBindRenderbuffer(GL_RENDERBUFFER, ctx.gl_rb);
426
427 const EGLint khr_image_attrs[] = {
428 EGL_WIDTH, ctx.mode->hdisplay,
429 EGL_HEIGHT, ctx.mode->vdisplay,
430 EGL_LINUX_DRM_FOURCC_EXT, DRM_FORMAT_XRGB8888,
431 EGL_DMA_BUF_PLANE0_FD_EXT, drm_prime_fd,
432 EGL_DMA_BUF_PLANE0_OFFSET_EXT, 0,
433 EGL_DMA_BUF_PLANE0_PITCH_EXT, bo_stride,
434 EGL_NONE
435 };
436
437 ctx.egl_image = eglCreateImageKHR(ctx.egl_display, EGL_NO_CONTEXT,
438 EGL_LINUX_DMA_BUF_EXT, NULL, khr_image_attrs);
439 if (ctx.egl_image == EGL_NO_IMAGE_KHR) {
440 fprintf(stderr, "failed to create egl image: %s\n",
441 get_egl_error());
442 ret = 1;
443 goto delete_gl_buffers;
444 }
445
446 glEGLImageTargetRenderbufferStorageOES(GL_RENDERBUFFER, ctx.egl_image);
447 glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
448 GL_RENDERBUFFER, ctx.gl_rb);
449
450 if (glCheckFramebufferStatus(GL_FRAMEBUFFER) !=
451 GL_FRAMEBUFFER_COMPLETE) {
452 fprintf(stderr, "failed to create framebuffer: %s\n", get_gl_error());
453 ret = 1;
454 goto destroy_image;
455 }
456
457 ret = drmModeAddFB(ctx.card_fd, ctx.mode->hdisplay, ctx.mode->vdisplay,
458 24, 32, bo_stride, bo_handle, &ctx.drm_fb_id);
459
460 if (ret) {
461 fprintf(stderr, "failed to add fb\n");
462 ret = 1;
463 goto destroy_image;
464 }
465
466 fprintf(stderr, "drm fb id: %d\n", ctx.drm_fb_id);
467 fprintf(stderr, "calling drmModeSetCrtc with crtc_id = %d\n", ctx.encoder->crtc_id);
468
469 if (drmModeSetCrtc(ctx.card_fd, ctx.encoder->crtc_id, ctx.drm_fb_id,
470 0, 0, &ctx.connector->connector_id, 1, ctx.mode)) {
471 fprintf(stderr, "failed to set CRTC\n");
472 ret = 1;
473 goto remove_fb;
474 }
475
476 draw(&ctx);
477
478remove_fb:
479 drmModeRmFB(ctx.card_fd, ctx.drm_fb_id);
480destroy_image:
481 eglDestroyImageKHR(ctx.egl_display, ctx.egl_image);
482delete_gl_buffers:
483 glBindRenderbuffer(GL_RENDERBUFFER, 0);
484 glBindFramebuffer(GL_FRAMEBUFFER, 0);
485 glDeleteFramebuffers(1, &ctx.gl_fb);
486 glDeleteRenderbuffers(1, &ctx.gl_rb);
487destroy_buffer:
488 gbm_bo_destroy(ctx.gbm_buffer);
489destroy_context:
490 eglDestroyContext(ctx.egl_display, ctx.egl_ctx);
491free_drm:
492 drmModeFreeConnector(ctx.connector);
493 drmModeFreeEncoder(ctx.encoder);
494terminate_display:
495 eglTerminate(ctx.egl_display);
496destroy_device:
497 gbm_device_destroy(ctx.gbm_dev);
498close_card:
499 close(ctx.card_fd);
500fail:
501 return ret;
502}