blob: 9b88cfb49e63e64ac81a0e9ddf389e66d85bf01b [file] [log] [blame]
Dongseong Hwang20c494c2016-04-27 11:26:19 +03001/*
2 * Copyright 2017 The Chromium OS Authors. All rights reserved.
3 * Use of this source code is governed by a BSD-style license that can be
4 * found in the LICENSE file.
5 */
6
7/*
8 The mapped_texture_test test consists of:
9 * Importing external buffers as EGL Images
10 * Drawing an ellipse using the CPU
11 * Binding CPU drawn buffer as a texture and sampling from it
12 * Using KMS to scanout the resultant framebuffer
13 */
14
15#include <getopt.h>
16
17#include "bs_drm.h"
18
19// double buffering
20#define NUM_BUFFERS 2
21
22struct offscreen_buffer {
23 struct gbm_bo *bo;
24 GLuint tex;
25 EGLImageKHR image;
26 const struct bs_draw_format *draw_format;
27};
28
29struct framebuffer {
30 struct gbm_bo *bo;
31 uint32_t fb_id;
32 EGLImageKHR image;
33 struct bs_egl_fb *egl_fb;
34};
35
36struct gl_resources {
37 GLuint program;
38 GLuint vbo;
39};
40
41// clang-format off
42static const GLfloat vertices[] = {
43 // x y u v
44 -0.25f, -0.25f, 0.0f, 0.0f, // Bottom left
45 -0.25f, 0.25f, 0.0f, 1.0f, // Top left
46 0.25f, 0.25f, 1.0f, 1.0f, // Top right
47 0.25f, -0.25f, 1.0f, 0.0f, // Bottom Right
48};
49
50static const int binding_xy = 0;
51static const int binding_uv = 1;
52
53static const GLubyte indices[] = {
54 0, 1, 2,
55 0, 2, 3
56};
57
58// clang-format on
59
60static const GLchar *vert =
61 "attribute vec2 xy;\n"
62 "attribute vec2 uv;\n"
63 "varying vec2 tex_coordinate;\n"
64 "void main() {\n"
65 " gl_Position = vec4(xy, 0, 1);\n"
66 " tex_coordinate = uv;\n"
67 "}\n";
68
69static const GLchar *frag =
70 "precision mediump float;\n"
71 "uniform sampler2D ellipse;\n"
72 "varying vec2 tex_coordinate;\n"
73 "void main() {\n"
74 " gl_FragColor = texture2D(ellipse, tex_coordinate);\n"
75 "}\n";
76
77static bool create_framebuffer(int display_fd, struct gbm_device *gbm, struct bs_egl *egl,
78 uint32_t width, uint32_t height, struct framebuffer *fb)
79{
80 fb->bo = gbm_bo_create(gbm, width, height, GBM_FORMAT_XRGB8888,
81 GBM_BO_USE_SCANOUT | GBM_BO_USE_RENDERING);
82 if (!fb->bo) {
83 bs_debug_error("failed to create a gbm buffer.");
84 goto delete_gl_fb;
85 }
86
87 fb->fb_id = bs_drm_fb_create_gbm(fb->bo);
88 if (!fb->fb_id) {
89 bs_debug_error("failed to create framebuffer from buffer object");
90 goto delete_gl_image;
91 }
92
93 fb->image = bs_egl_image_create_gbm(egl, fb->bo);
94 if (fb->image == EGL_NO_IMAGE_KHR) {
95 bs_debug_error("failed to make image from buffer object");
96 goto delete_fb;
97 }
98
99 fb->egl_fb = bs_egl_fb_new(egl, fb->image);
100 if (!fb->egl_fb) {
101 bs_debug_error("failed to make rednering framebuffer for buffer object");
102 goto delete_gbm_bo;
103 }
104
105 return true;
106
107delete_gl_fb:
108 bs_egl_fb_destroy(&fb->egl_fb);
109delete_gl_image:
110 bs_egl_image_destroy(egl, &fb->image);
111delete_fb:
112 drmModeRmFB(display_fd, fb->fb_id);
113delete_gbm_bo:
114 gbm_bo_destroy(fb->bo);
115 return false;
116}
117
118static bool add_offscreen_texture(struct gbm_device *gbm, struct bs_egl *egl,
119 struct offscreen_buffer *buffer, uint32_t width, uint32_t height,
120 uint32_t flags)
121{
122 buffer->bo =
123 gbm_bo_create(gbm, width, height, bs_get_pixel_format(buffer->draw_format), flags);
124 if (!buffer->bo) {
125 bs_debug_error("failed to allocate offscreen buffer object: format=%s \n",
126 bs_get_format_name(buffer->draw_format));
127 goto destroy_offscreen_buffer;
128 }
129
130 buffer->image = bs_egl_image_create_gbm(egl, buffer->bo);
131 if (buffer->image == EGL_NO_IMAGE_KHR) {
132 bs_debug_error("failed to create offscreen egl image");
133 goto destroy_offscreen_bo;
134 }
135
136 glActiveTexture(GL_TEXTURE1);
137 glGenTextures(1, &buffer->tex);
138 glBindTexture(GL_TEXTURE_2D, buffer->tex);
139
140 if (!bs_egl_target_texture2D(egl, buffer->image)) {
141 bs_debug_error("failed to import egl image as texture");
142 goto destroy_offscreen_image;
143 }
144
145 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
146 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
147 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
148 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
149
150 return true;
151
152destroy_offscreen_image:
153 glDeleteTextures(1, &buffer->tex);
154 bs_egl_image_destroy(egl, &buffer->image);
155destroy_offscreen_bo:
156 gbm_bo_destroy(buffer->bo);
157destroy_offscreen_buffer:
158 return false;
159}
160
161static bool init_gl(struct bs_egl_fb *fb, uint32_t width, uint32_t height,
162 struct gl_resources *resources)
163{
164 struct bs_gl_program_create_binding bindings[] = {
165 { binding_xy, "xy" }, { binding_uv, "uv" }, { 2, NULL },
166 };
167
168 resources->program = bs_gl_program_create_vert_frag_bind(vert, frag, bindings);
169 if (!resources->program) {
170 bs_debug_error("failed to compile shader program");
171 return false;
172 }
173
174 glGenBuffers(1, &resources->vbo);
175 glBindBuffer(GL_ARRAY_BUFFER, resources->vbo);
176 glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
177
178 glBindFramebuffer(GL_FRAMEBUFFER, bs_egl_fb_name(fb));
179 glViewport(0, 0, (GLint)width, (GLint)height);
180
Gurchetan Singh7018a3c2017-04-10 17:30:12 -0700181 glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
Dongseong Hwang20c494c2016-04-27 11:26:19 +0300182 glClear(GL_COLOR_BUFFER_BIT);
183
184 glUseProgram(resources->program);
185
186 glUniform1i(glGetUniformLocation(resources->program, "ellipse"), 1);
187 glEnableVertexAttribArray(binding_xy);
188 glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(GLfloat), 0);
189
190 glEnableVertexAttribArray(binding_uv);
191 glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(GLfloat),
192 (void *)(2 * sizeof(GLfloat)));
193 return true;
194}
195
Gurchetan Singh7018a3c2017-04-10 17:30:12 -0700196static void draw_textured_quad(GLuint tex)
Dongseong Hwang20c494c2016-04-27 11:26:19 +0300197{
198 glClear(GL_COLOR_BUFFER_BIT);
199 glActiveTexture(GL_TEXTURE1);
200 glBindTexture(GL_TEXTURE_2D, tex);
201 glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_BYTE, indices);
202}
203
204static const struct option longopts[] = {
205 { "help", no_argument, NULL, 'h' },
206 { "format", required_argument, NULL, 'f' },
207 { "dma-buf", no_argument, NULL, 'b' },
208 { "gem", no_argument, NULL, 'g' },
209 { "dumb", no_argument, NULL, 'd' },
210 { "tiled", no_argument, NULL, 't' },
211 { 0, 0, 0, 0 },
212};
213
214static void print_help(const char *argv0)
215{
216 printf("Usage: %s [OPTIONS]\n", argv0);
217 printf(" -h, --help Print help.\n");
218 printf(" -f, --format FOURCC format of texture (defaults to ARGB8888)\n");
Shirish S37b57452017-07-10 10:35:02 +0530219 printf(" -b, --dma-buf Use dma-buf mmap.\n");
220 printf(" -g, --gem Use GEM map(by default).\n");
Dongseong Hwang20c494c2016-04-27 11:26:19 +0300221 printf(" -d, --dumb Use dump map.\n");
222 printf(" -t, --tiled Use potentially tiled buffer.\n");
223}
224
225static void page_flip_handler(int fd, unsigned int frame, unsigned int sec, unsigned int usec,
226 void *data)
227{
228 int *waiting_for_flip = data;
229 *waiting_for_flip = 0;
230}
231
232void flush_egl(struct bs_egl *egl, EGLImageKHR image)
233{
234 bs_egl_image_flush_external(egl, image);
235 EGLSyncKHR sync = bs_egl_create_sync(egl, EGL_SYNC_FENCE_KHR, NULL);
236 bs_egl_wait_sync(egl, sync, 0, EGL_FOREVER_KHR);
237 bs_egl_destroy_sync(egl, sync);
238}
239
240int main(int argc, char **argv)
241{
242 int ret = 1;
243 int display_fd = bs_drm_open_main_display();
244 if (display_fd < 0) {
245 bs_debug_error("failed to open card for display");
246 goto out;
247 }
248
249 struct offscreen_buffer buffer;
250 buffer.draw_format = bs_get_draw_format_from_name("ARGB8888");
251 struct bs_mapper *mapper = NULL;
Dongseong Hwang03ba11f2017-06-13 20:08:28 -0700252 uint32_t flags = GBM_BO_USE_TEXTURING | GBM_BO_USE_LINEAR;
Dongseong Hwang20c494c2016-04-27 11:26:19 +0300253
254 int c;
255 while ((c = getopt_long(argc, argv, "f:bgdth", longopts, NULL)) != -1) {
256 switch (c) {
257 case 'f':
258 if (!bs_parse_draw_format(optarg, &buffer.draw_format)) {
259 printf("choose the default format ARGB8888\n");
260 }
261 printf("format=%s\n", bs_get_format_name(buffer.draw_format));
262 break;
263 case 'b':
264 mapper = bs_mapper_dma_buf_new();
265 printf("using dma-buf mmap\n");
266 break;
267 case 'g':
268 mapper = bs_mapper_gem_new();
269 printf("using GEM map\n");
270 break;
271 case 'd':
272 mapper = bs_mapper_dumb_new(display_fd);
273 printf("using dumb map\n");
274 break;
275 case 't':
Dongseong Hwang03ba11f2017-06-13 20:08:28 -0700276 flags = GBM_BO_USE_TEXTURING;
Dongseong Hwang20c494c2016-04-27 11:26:19 +0300277 break;
278 case 'h':
279 default:
280 print_help(argv[0]);
281 goto destroy_display_fd;
282 }
283 }
284
Shirish S37b57452017-07-10 10:35:02 +0530285 // Use gem map mapper by default, in case any arguments aren't selected.
Dongseong Hwang20c494c2016-04-27 11:26:19 +0300286 if (!mapper) {
Shirish S37b57452017-07-10 10:35:02 +0530287 mapper = bs_mapper_gem_new();
288 printf("using GEM map\n");
Dongseong Hwang20c494c2016-04-27 11:26:19 +0300289 }
290
291 if (!mapper) {
292 bs_debug_error("failed to create mapper object");
293 goto destroy_display_fd;
294 }
295
296 uint32_t width;
297 uint32_t height;
298
299 struct gbm_device *gbm = gbm_create_device(display_fd);
300 if (!gbm) {
301 bs_debug_error("failed to create gbm device");
302 goto destroy_mapper;
303 }
304
305 struct bs_drm_pipe pipe = { 0 };
306 if (!bs_drm_pipe_make(display_fd, &pipe)) {
307 bs_debug_error("failed to make pipe");
308 goto destroy_gbm_device;
309 }
310
311 drmModeConnector *connector = drmModeGetConnector(display_fd, pipe.connector_id);
312 drmModeModeInfo *mode = &connector->modes[0];
313 width = mode->hdisplay;
314 height = mode->vdisplay;
315
316 struct bs_egl *egl = bs_egl_new();
317 if (!bs_egl_setup(egl)) {
318 bs_debug_error("failed to setup egl context");
319 goto destroy_gbm_device;
320 }
321
322 struct framebuffer fbs[NUM_BUFFERS] = {};
323 uint32_t front_buffer = 0;
324 for (size_t i = 0; i < NUM_BUFFERS; i++) {
325 if (!create_framebuffer(display_fd, gbm, egl, width, height, &fbs[i])) {
326 bs_debug_error("failed to create framebuffer");
327 goto delete_framebuffers;
328 }
329 }
330
331 if (!add_offscreen_texture(gbm, egl, &buffer, width / 4, height / 4, flags)) {
332 bs_debug_error("failed to create offscreen texture");
333 goto destroy_offscreen_buffer;
334 }
335
336 const struct framebuffer *back_fb = &fbs[front_buffer ^ 1];
337 struct gl_resources resources;
338 if (!init_gl(back_fb->egl_fb, width, height, &resources)) {
339 bs_debug_error("failed to initialize GL resources.\n");
340 goto destroy_gl_resources;
341 }
342
343 flush_egl(egl, back_fb->image);
344
345 ret = drmModeSetCrtc(display_fd, pipe.crtc_id, fbs[front_buffer].fb_id, 0 /* x */,
346 0 /* y */, &pipe.connector_id, 1 /* connector count */, mode);
347 if (ret) {
348 bs_debug_error("failed to set crtc: %d", ret);
349 goto destroy_gl_resources;
350 }
351
352 drmEventContext evctx = {
353 .version = DRM_EVENT_CONTEXT_VERSION, .page_flip_handler = page_flip_handler,
354 };
355 fd_set fds;
356
357 // The test takes about 2 seconds to complete.
358 const size_t test_frames = 120;
359 for (size_t i = 0; i < test_frames; i++) {
360 int waiting_for_flip = 1;
361
362 const struct framebuffer *back_fb = &fbs[front_buffer ^ 1];
363 glBindFramebuffer(GL_FRAMEBUFFER, bs_egl_fb_name(back_fb->egl_fb));
364 if (!bs_draw_ellipse(mapper, buffer.bo, buffer.draw_format,
365 (float)i / test_frames)) {
366 bs_debug_error("failed to draw to buffer");
367 goto destroy_gl_resources;
368 }
369
Gurchetan Singh7018a3c2017-04-10 17:30:12 -0700370 draw_textured_quad(buffer.tex);
Dongseong Hwang20c494c2016-04-27 11:26:19 +0300371
372 flush_egl(egl, back_fb->image);
373
374 ret = drmModePageFlip(display_fd, pipe.crtc_id, back_fb->fb_id,
375 DRM_MODE_PAGE_FLIP_EVENT, &waiting_for_flip);
376 if (ret) {
377 bs_debug_error("failed to queue page flip");
378 goto destroy_gl_resources;
379 }
380
381 while (waiting_for_flip) {
382 FD_ZERO(&fds);
383 FD_SET(0, &fds);
384 FD_SET(display_fd, &fds);
385 int ret = select(display_fd + 1, &fds, NULL, NULL, NULL);
386 if (ret < 0) {
387 bs_debug_error("select err: %s", strerror(errno));
388 goto destroy_gl_resources;
389 } else if (FD_ISSET(0, &fds)) {
390 bs_debug_error("exit due to user-input");
391 goto destroy_gl_resources;
392 } else if (FD_ISSET(display_fd, &fds)) {
393 drmHandleEvent(display_fd, &evctx);
394 }
395 }
396
397 front_buffer ^= 1;
398 }
399
400destroy_gl_resources:
401 glBindBuffer(GL_ARRAY_BUFFER, 0);
402 glUseProgram(0);
403 glBindFramebuffer(GL_FRAMEBUFFER, 0);
404 glBindTexture(GL_TEXTURE_2D, 0);
405 glDeleteProgram(resources.program);
406 glDeleteBuffers(1, &resources.vbo);
407destroy_offscreen_buffer:
408 glDeleteTextures(1, &buffer.tex);
409 bs_egl_image_destroy(egl, &buffer.image);
410 gbm_bo_destroy(buffer.bo);
411delete_framebuffers:
412 for (size_t i = 0; i < NUM_BUFFERS; i++) {
413 bs_egl_fb_destroy(&fbs[i].egl_fb);
414 bs_egl_image_destroy(egl, &fbs[i].image);
415 drmModeRmFB(display_fd, fbs[i].fb_id);
416 gbm_bo_destroy(fbs[i].bo);
417 }
418 bs_egl_destroy(&egl);
419destroy_gbm_device:
420 gbm_device_destroy(gbm);
421destroy_mapper:
422 bs_mapper_destroy(mapper);
423destroy_display_fd:
424 close(display_fd);
425out:
426 return ret;
427}