blob: 8ce0460b0be1c6ac32b18625172f020d5b4ff8da [file] [log] [blame]
Paulo Warren7d6d0f82020-01-30 14:52:59 -05001/*
2 * Copyright 2019 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#define _GNU_SOURCE
8
9#include <EGL/egl.h>
10#include <EGL/eglext.h>
11#include <GLES2/gl2.h>
12#include <GLES2/gl2ext.h>
13#include <fcntl.h>
14#include <linux/dma-buf.h>
15#include <linux/udmabuf.h>
Paulo Warren447fbdb2020-04-08 16:27:20 -040016#include <math.h>
Paulo Warren7d6d0f82020-01-30 14:52:59 -050017#include <stdint.h>
18#include <stdlib.h>
19#include <sys/ioctl.h>
20#include <sys/mman.h>
21#include <time.h>
22
23#include "bs_drm.h"
24
25#define SEC_TO_NS 1000000000L
26#define NS_TO_MS 1 / 1000000L
27
28#define HANDLE_EINTR_AND_EAGAIN(x) \
29 ({ \
30 int result; \
31 do { \
32 result = (x); \
33 } while (result != -1 && (errno == EINTR || errno == EAGAIN)); \
34 result; \
35 })
36
37// The purpose of this test is to assess the impact on the performance of
38// compositing when using udmabuf to avoid copies. To accomplish this, we
39// compare two paths:
40//
41// 1) Drawing the square to a shared memory buffer with the CPU, converting that
42// to a dma-buf using udmabuf_create, and importing that dma-buf in GL to
43// composite on to a scanout buffer.
44//
45// 2) Drawing the square to a shared memory buffer with the CPU, uploading that
46// as a GL texture, and using that texture to composite onto a scanout buffer.
47//
48// For each path and for each frame, we time drawing the square with the CPU,
49// and we time how long it takes GL to finish rendering.
Paulo Warren7d6d0f82020-01-30 14:52:59 -050050
51// Duration to display frames for in seconds.
52static const int kTestCaseDurationSeconds = 20;
53// Name of memfd file created.
54static const char* kMemFDCreateName = "dmabuf_test";
Paulo Warren447fbdb2020-04-08 16:27:20 -040055// Critical value for the standard normal distribution corresponding to a 95% confidence level.
56static const double kZCriticalValue = 1.960;
Paulo Warren7d6d0f82020-01-30 14:52:59 -050057
58// Represents a buffer that can be composited into and will be scanned out from.
59struct Buffer {
60 struct gbm_bo* bo;
61 struct bs_egl_fb* gl_fb;
62 uint32_t fb_id;
63 EGLImageKHR egl_image;
64};
65
66// An implementation of double buffering: we composite into buffers[back_buffer] while
67// the other buffer is being scanned out.
68struct BufferQueue {
69 struct Buffer buffers[2];
70 size_t back_buffer;
71};
72
73// Position and velocity of the square.
74struct MotionContext {
75 int x;
76 int y;
77 int x_v;
78 int y_v;
79};
80
Paulo Warren447fbdb2020-04-08 16:27:20 -040081struct SharedMemoryBuffer {
Paulo Warren7d6d0f82020-01-30 14:52:59 -050082 int memfd;
83 uint32_t* mapped_rgba_data;
84};
85
Paulo Warren447fbdb2020-04-08 16:27:20 -040086// Represents a shared-memory buffer imported into GL.
87// |image_bo|, |image|, and |dmabuf_fd| are only used in the zero-copy path.
88struct ImportedBuffer {
89 GLuint image_texture;
90 struct gbm_bo* image_bo;
91 EGLImageKHR image;
92 int dmabuf_fd;
93};
94
Paulo Warren7d6d0f82020-01-30 14:52:59 -050095// Context required for a page flip and memory cleanup when finished.
96struct PageFlipContext {
97 struct BufferQueue queue;
98 struct MotionContext motion_context;
Paulo Warren447fbdb2020-04-08 16:27:20 -040099
100 struct SharedMemoryBuffer shm_buffer;
101 struct ImportedBuffer imported_buffer;
102
Paulo Warren7d6d0f82020-01-30 14:52:59 -0500103 struct bs_egl* egl;
Paulo Warren447fbdb2020-04-08 16:27:20 -0400104 GLuint vertex_attributes;
105 bool use_zero_copy;
106 int frames;
Paulo Warren7d6d0f82020-01-30 14:52:59 -0500107 uint32_t width;
108 uint32_t height;
109 uint32_t crtc_id;
Paulo Warren447fbdb2020-04-08 16:27:20 -0400110 double sum_of_times; // Sum of timings on each frame.
111 double sum_of_squared_times; // Sum of squared timings on each frame.
Paulo Warren7d6d0f82020-01-30 14:52:59 -0500112};
113
Paulo Warren447fbdb2020-04-08 16:27:20 -0400114double standard_error(double stddev, size_t n)
115{
116 return kZCriticalValue * (stddev / sqrt(n));
117}
118
119// Aligns num up to the nearest multiple of |multiple|.
120uint32_t align(uint32_t num, int multiple)
Paulo Warren7d6d0f82020-01-30 14:52:59 -0500121{
122 assert(multiple);
123 return ((num + multiple - 1) / multiple) * multiple;
124}
125
126/*
Paulo Warren447fbdb2020-04-08 16:27:20 -0400127 * Upload pixel data as a GL texture.
128 */
129void upload_texture(uint32_t* arr, size_t width, size_t height)
130{
131 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, arr);
132}
133
134/*
Paulo Warren7d6d0f82020-01-30 14:52:59 -0500135 * Draw a randomly colored square, moving from top left to bottom right
136 * behind a black background. Position of the square is set by |motion_context|.
137 * |arr| points to the RGBA pixel data.
138 */
Paulo Warren447fbdb2020-04-08 16:27:20 -0400139void draw_square(size_t width, size_t height, struct MotionContext* motion_context, uint32_t* arr)
Paulo Warren7d6d0f82020-01-30 14:52:59 -0500140{
141 size_t j_left_bound = motion_context->x;
142 size_t j_right_bound = j_left_bound + 50;
143 size_t i_top_bound = motion_context->y;
144 size_t i_bottom_bound = i_top_bound + 50;
145 uint32_t color = drand48() * 0xFFFFFFFF;
146
147 if (i_bottom_bound >= height)
148 motion_context->y_v = -16;
149
150 if (j_right_bound >= width)
151 motion_context->x_v = -16;
152
153 if (j_left_bound <= 1)
154 motion_context->x_v = 16;
155
156 if (i_top_bound <= 1)
157 motion_context->y_v = 16;
158
Paulo Warren7d6d0f82020-01-30 14:52:59 -0500159 uint32_t* dst = (uint32_t*)arr + i_top_bound * width;
160 for (size_t row = i_top_bound; (row < i_bottom_bound) && (row < height); row++) {
161 for (size_t col = j_left_bound; (col < j_right_bound) && (col < width); col++) {
162 dst[col] = color;
163 }
164 dst += width;
165 }
Paulo Warren7d6d0f82020-01-30 14:52:59 -0500166}
167
168int create_udmabuf(int fd, size_t length)
169{
170 int udmabuf_dev_fd = HANDLE_EINTR_AND_EAGAIN(open("/dev/udmabuf", O_RDWR));
171
172 struct udmabuf_create create;
173 create.memfd = fd;
174 create.flags = UDMABUF_FLAGS_CLOEXEC;
175 create.offset = 0;
176 create.size = length;
177
178 int dmabuf_fd = HANDLE_EINTR_AND_EAGAIN(ioctl(udmabuf_dev_fd, UDMABUF_CREATE, &create));
179 if (dmabuf_fd < 0) {
180 bs_debug_error("error creating udmabuf");
181 exit(EXIT_FAILURE);
182 }
183
184 close(udmabuf_dev_fd);
185 return dmabuf_fd;
186}
187
188/*
189 * Create a region of shared memory of size |length|.
190 * The region is sealed with F_SEAL_SHRINK.
191 */
192int create_memfd(size_t length)
193{
194 int fd = memfd_create(kMemFDCreateName, MFD_ALLOW_SEALING);
195 if (fd == -1) {
196 bs_debug_error("memfd_create() error: %s", strerror(errno));
197 exit(EXIT_FAILURE);
198 }
199
200 int res = HANDLE_EINTR_AND_EAGAIN(ftruncate(fd, length));
201 if (res == -1) {
202 bs_debug_error("ftruncate() error: %s", strerror(errno));
203 exit(EXIT_FAILURE);
204 }
205
206 // udmabuf_create requires that file descriptors be sealed with
207 // F_SEAL_SHRINK.
208 if (fcntl(fd, F_ADD_SEALS, F_SEAL_SHRINK) < 0) {
209 bs_debug_error("fcntl() error: %s", strerror(errno));
210 exit(EXIT_FAILURE);
211 }
212
213 return fd;
214}
215
216GLuint setup_shaders_and_geometry(int width, int height)
217{
218 const GLchar* vert =
219 "attribute vec2 pos;\n"
220 "varying vec2 tex_pos;\n"
221 "void main() {\n"
222 " gl_Position = vec4(pos, 0, 1);\n"
223 " tex_pos = vec2((pos.x + 1.0) / 2.0, (pos.y + 1.0) / 2.0);\n"
224 "}\n";
225
226 const GLchar* frag =
227 "precision mediump float;\n"
228 "uniform sampler2D tex;\n"
229 "varying vec2 tex_pos;\n"
230 "void main() {\n"
231 " gl_FragColor = texture2D(tex, tex_pos);\n"
232 "}\n";
233
234 const GLfloat verts[] = {
235 -1.0f, -1.0f, 1.0f, -1.0f, -1.0f, 1.0f, 1.0f, 1.0f,
236 };
237
238 struct bs_gl_program_create_binding bindings[] = {
239 { 0, "pos" },
240 { 0, NULL },
241 };
242
243 // Compile and link GL program.
244 GLuint program = bs_gl_program_create_vert_frag_bind(vert, frag, bindings);
245 if (!program) {
246 bs_debug_error("failed to compile shader program");
247 exit(EXIT_FAILURE);
248 }
249
250 glUseProgram(program);
251 glViewport(0, 0, width, height);
252
253 GLuint buffer = 0;
254 glGenBuffers(1, &buffer);
255 glBindBuffer(GL_ARRAY_BUFFER, buffer);
256 glBufferData(GL_ARRAY_BUFFER, sizeof(verts), verts, GL_STATIC_DRAW);
257
258 glUniform1i(glGetUniformLocation(program, "tex"), 0);
259 GLint pos_attrib_index = glGetAttribLocation(program, "pos");
260 glEnableVertexAttribArray(pos_attrib_index);
261 glVertexAttribPointer(pos_attrib_index, 2, GL_FLOAT, GL_FALSE, 0, 0);
262 glDeleteProgram(program);
263 return buffer;
264}
265
266struct bs_egl_fb* create_gl_framebuffer(struct bs_egl* egl, EGLImageKHR egl_image)
267{
268 struct bs_egl_fb* fb = bs_egl_fb_new(egl, egl_image);
269 if (!fb) {
270 bs_egl_image_destroy(egl, &egl_image);
271 bs_debug_error("failed to make rendering framebuffer for buffer object");
272 exit(EXIT_FAILURE);
273 }
274 return fb;
275}
276
277EGLImageKHR import_source_buffer(struct bs_egl* egl, struct gbm_bo* bo, GLuint image_texture)
278{
279 EGLImageKHR image = bs_egl_image_create_gbm(egl, bo);
280 if (image == EGL_NO_IMAGE_KHR) {
281 bs_debug_error("failed to make image from buffer object");
282 exit(EXIT_FAILURE);
283 }
284
285 glBindTexture(GL_TEXTURE_2D, image_texture);
286
287 if (!bs_egl_target_texture2D(egl, image)) {
288 bs_debug_error("failed to import egl color_image as a texture");
289 exit(EXIT_FAILURE);
290 }
291 return image;
292}
293
294/*
295 * Initialize GL pipeline.
296 * width: width of display
297 * height: height of display
298 *
299 */
Paulo Warren447fbdb2020-04-08 16:27:20 -0400300GLuint init_gl(struct PageFlipContext* context, uint32_t width, uint32_t height)
Paulo Warren7d6d0f82020-01-30 14:52:59 -0500301{
Paulo Warren447fbdb2020-04-08 16:27:20 -0400302 context->vertex_attributes = setup_shaders_and_geometry(width, height);
Paulo Warren7d6d0f82020-01-30 14:52:59 -0500303
304 GLuint image_texture = 0;
305 glGenTextures(1, &image_texture);
306 glBindTexture(GL_TEXTURE_2D, image_texture);
307 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
308 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
309 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
310 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
Paulo Warren447fbdb2020-04-08 16:27:20 -0400311 return image_texture;
Paulo Warren7d6d0f82020-01-30 14:52:59 -0500312}
313
314/*
315 * Call on each frame.
316 * This function is called with alternating fb's.
317 */
318void draw_gl(GLuint fb)
319{
320 // Bind the screen framebuffer to GL.
321 glBindFramebuffer(GL_FRAMEBUFFER, fb);
322 glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
323 // Block until rendering is complete.
324 // We can easily measure how long rendering takes if this function
325 // blocks.
326 glFinish();
327}
328
329/*
330 * Called at the end of each page flip.
331 * Schedules a new page flip alternating between
332 * the two buffers.
333 */
334static void draw_and_swap_frame(int display_fd, unsigned int frame, unsigned int sec,
335 unsigned int usec, void* data)
336{
337 struct PageFlipContext* context = data;
Paulo Warren447fbdb2020-04-08 16:27:20 -0400338 struct BufferQueue* queue = &context->queue;
339 struct Buffer buf = queue->buffers[queue->back_buffer];
Paulo Warren7d6d0f82020-01-30 14:52:59 -0500340 struct bs_egl* egl = context->egl;
Paulo Warren447fbdb2020-04-08 16:27:20 -0400341 struct SharedMemoryBuffer shm_buffer = context->shm_buffer;
Paulo Warren7d6d0f82020-01-30 14:52:59 -0500342 int crtc_id = context->crtc_id;
343 uint32_t width = context->width;
344 uint32_t height = context->height;
345 int err;
Paulo Warren7d6d0f82020-01-30 14:52:59 -0500346 struct timespec start, finish;
347 clock_gettime(CLOCK_MONOTONIC, &start);
Paulo Warren447fbdb2020-04-08 16:27:20 -0400348 if (context->use_zero_copy) {
349 int dmabuf_fd = context->imported_buffer.dmabuf_fd;
350 struct dma_buf_sync sync_start = { 0 };
351 sync_start.flags = DMA_BUF_SYNC_START | DMA_BUF_SYNC_RW;
352 int rv = HANDLE_EINTR_AND_EAGAIN(ioctl(dmabuf_fd, DMA_BUF_IOCTL_SYNC, &sync_start));
353 if (rv != 0) {
354 bs_debug_error("error with dma_buf start sync");
355 exit(EXIT_FAILURE);
356 }
357
358 draw_square(width, height, &context->motion_context, shm_buffer.mapped_rgba_data);
359
360 struct dma_buf_sync sync_end = { 0 };
361 sync_end.flags = DMA_BUF_SYNC_END | DMA_BUF_SYNC_RW;
362 rv = HANDLE_EINTR_AND_EAGAIN(ioctl(dmabuf_fd, DMA_BUF_IOCTL_SYNC, &sync_end));
363 if (rv != 0) {
364 bs_debug_error("error with dma_buf end sync");
365 exit(EXIT_FAILURE);
366 }
367 } else {
368 draw_square(width, height, &context->motion_context, shm_buffer.mapped_rgba_data);
369 // TODO(crbug.com/1069612): Experiment a third path which uses
370 // glTexSubImage2D instead of glTexImage2D() on each frame. It
371 // should be faster.
372 upload_texture(shm_buffer.mapped_rgba_data, width, height);
373 }
Paulo Warren7d6d0f82020-01-30 14:52:59 -0500374 draw_gl(bs_egl_fb_name(buf.gl_fb));
375 clock_gettime(CLOCK_MONOTONIC, &finish);
376
377 double ns_diff =
378 (SEC_TO_NS * (finish.tv_sec - start.tv_sec)) + finish.tv_nsec - start.tv_nsec;
379 double ms_to_draw_and_render = (ns_diff)*NS_TO_MS;
380
381 context->sum_of_times += ms_to_draw_and_render;
Paulo Warren447fbdb2020-04-08 16:27:20 -0400382 context->sum_of_squared_times += ms_to_draw_and_render * ms_to_draw_and_render;
Paulo Warren7d6d0f82020-01-30 14:52:59 -0500383
384 bs_egl_image_flush_external(egl, buf.egl_image);
385 err = drmModePageFlip(display_fd, crtc_id, buf.fb_id, DRM_MODE_PAGE_FLIP_EVENT, context);
386
387 if (err) {
388 bs_debug_error("failed page flip: %s", strerror(errno));
389 exit(EXIT_FAILURE);
390 }
391
Paulo Warren447fbdb2020-04-08 16:27:20 -0400392 queue->back_buffer = (queue->back_buffer + 1) % 2;
Paulo Warren7d6d0f82020-01-30 14:52:59 -0500393 context->motion_context.x += context->motion_context.x_v;
394 context->motion_context.y += context->motion_context.y_v;
Paulo Warren447fbdb2020-04-08 16:27:20 -0400395 context->frames++;
Paulo Warren7d6d0f82020-01-30 14:52:59 -0500396}
397
398struct BufferQueue init_buffers(struct gbm_device* gbm, struct bs_egl* egl, uint32_t width,
399 uint32_t height)
400{
401 struct BufferQueue queue;
402 memset(&queue, 0, sizeof(struct BufferQueue));
Paulo Warren7d6d0f82020-01-30 14:52:59 -0500403 for (size_t i = 0; i < 2; i++) {
404 struct gbm_bo* screen_bo = gbm_bo_create(gbm, width, height, GBM_FORMAT_ARGB8888,
405 GBM_BO_USE_RENDERING | GBM_BO_USE_SCANOUT);
406 if (!screen_bo) {
407 bs_debug_error("failed to create screen bo");
408 exit(EXIT_FAILURE);
409 }
410
411 EGLImageKHR egl_image = bs_egl_image_create_gbm(egl, screen_bo);
412 if (egl_image == EGL_NO_IMAGE_KHR) {
413 bs_debug_error("failed to make image from buffer object");
414 exit(EXIT_FAILURE);
415 }
416
417 uint32_t fb_id = bs_drm_fb_create_gbm(screen_bo);
418 if (!fb_id) {
419 bs_debug_error("failed to make drm fb from image");
420 exit(EXIT_FAILURE);
421 }
422
423 queue.buffers[i].egl_image = egl_image;
424 queue.buffers[i].bo = screen_bo;
425 queue.buffers[i].fb_id = fb_id;
426 queue.buffers[i].gl_fb = create_gl_framebuffer(egl, egl_image);
427 }
Paulo Warren7d6d0f82020-01-30 14:52:59 -0500428 queue.back_buffer = 1;
Paulo Warren7d6d0f82020-01-30 14:52:59 -0500429 return queue;
430}
431
432struct PageFlipContext init_page_flip_context(struct gbm_device* gbm, struct bs_egl* egl,
433 int display_fd)
434{
435 struct bs_drm_pipe pipe = { 0 };
436 if (!bs_drm_pipe_make(display_fd, &pipe)) {
437 bs_debug_error("failed to make pipe: %s", strerror(errno));
438 exit(EXIT_FAILURE);
439 }
440
441 drmModeConnector* connector = drmModeGetConnector(display_fd, pipe.connector_id);
442 drmModeModeInfo* mode = &connector->modes[0];
443
444 struct PageFlipContext context;
445 memset(&context, 0, sizeof(struct PageFlipContext));
446
447 context.crtc_id = pipe.crtc_id;
448 context.height = mode->vdisplay;
449 context.width = mode->hdisplay;
450 context.egl = egl;
451 context.motion_context = (struct MotionContext){ 1, 1, 16, 16 };
452 context.queue = init_buffers(gbm, egl, mode->hdisplay, mode->vdisplay);
453 context.sum_of_times = 0;
Paulo Warren447fbdb2020-04-08 16:27:20 -0400454 context.sum_of_squared_times = 0;
455 context.frames = 0;
Paulo Warren7d6d0f82020-01-30 14:52:59 -0500456
457 // Set display mode which also flips the page.
458 int ret_display =
459 drmModeSetCrtc(display_fd, pipe.crtc_id, context.queue.buffers[0].fb_id, 0 /* x */,
460 0 /* y */, &pipe.connector_id, 1 /* connector count */, mode);
461
462 if (ret_display) {
463 bs_debug_error("failed to set crtc: %s", strerror(errno));
464 exit(EXIT_FAILURE);
465 }
466 return context;
467}
468
469struct gbm_bo* import_dmabuf(struct gbm_device* gbm, int dmabuf_fd, uint32_t width, uint32_t height)
470{
471 // Import buffer object from shared dma_buf.
472 struct gbm_import_fd_modifier_data gbm_import_data;
473 gbm_import_data.width = width;
474 gbm_import_data.height = height;
475 gbm_import_data.format = GBM_FORMAT_ARGB8888;
476 gbm_import_data.num_fds = 1;
477 gbm_import_data.fds[0] = dmabuf_fd;
478 gbm_import_data.strides[0] = width * 4;
479 gbm_import_data.offsets[0] = 0;
480 gbm_import_data.modifier = 0;
481
482 struct gbm_bo* image_bo =
483 gbm_bo_import(gbm, GBM_BO_IMPORT_FD_MODIFIER, &gbm_import_data, GBM_BO_USE_RENDERING);
484
485 if (!image_bo) {
486 bs_debug_error("failed to make image bo");
487 exit(EXIT_FAILURE);
488 }
489 return image_bo;
490}
491
Paulo Warren447fbdb2020-04-08 16:27:20 -0400492void destroy_shm_buffer(struct SharedMemoryBuffer buf, uint32_t length)
Paulo Warren7d6d0f82020-01-30 14:52:59 -0500493{
494 munmap(buf.mapped_rgba_data, length);
Paulo Warren447fbdb2020-04-08 16:27:20 -0400495 close(buf.memfd);
496}
497
498void destroy_imported_buffer(struct ImportedBuffer buf, struct bs_egl* egl)
499{
Paulo Warren7d6d0f82020-01-30 14:52:59 -0500500 glDeleteTextures(1, &buf.image_texture);
Paulo Warren447fbdb2020-04-08 16:27:20 -0400501 if (buf.image != EGL_NO_IMAGE_KHR)
502 bs_egl_image_destroy(egl, &buf.image);
503 if (buf.image_bo)
504 gbm_bo_destroy(buf.image_bo);
505 if (buf.dmabuf_fd >= 0)
506 close(buf.dmabuf_fd);
Paulo Warren7d6d0f82020-01-30 14:52:59 -0500507}
508
509void destroy_buffers(struct BufferQueue queue, struct bs_egl* egl)
510{
511 for (size_t i = 0; i < 2; i++) {
512 bs_egl_image_destroy(egl, &queue.buffers[i].egl_image);
513 bs_egl_fb_destroy(&queue.buffers[i].gl_fb);
514 gbm_bo_destroy(queue.buffers[i].bo);
515 }
516}
517
Paulo Warren447fbdb2020-04-08 16:27:20 -0400518void print_results(double sum_of_squares, double sum, int frames, bool use_zero_copy)
519{
520 double avg = sum / frames;
521 double stddev = sqrt((sum_of_squares - (frames * (avg * avg))) / (frames - 1));
522 double std_err = standard_error(stddev, frames);
523
524 double begin_range = avg - std_err;
525 double end_range = avg + std_err;
526 if (use_zero_copy)
527 printf("Using udmabuf (zero-copy path):\n");
528 else
529 printf("Using glTexImage2D (one-copy path):\n");
530
531 printf(" n = %d frames\n", frames);
532 printf(" CI(t) = (%.2f ms, %.2f ms)\n", begin_range, end_range);
533 printf(" Sum(t) = %.2f ms\n", sum);
534}
535
Paulo Warren7d6d0f82020-01-30 14:52:59 -0500536int main(int argc, char** argv)
537{
538 struct timespec clock_resolution;
539 clock_getres(CLOCK_MONOTONIC, &clock_resolution);
540 // Make sure that the clock resolution is at least 1ms.
541 assert(clock_resolution.tv_sec == 0 && clock_resolution.tv_nsec <= 1000000);
542
543 int display_fd = bs_drm_open_main_display();
544 if (display_fd < 0) {
545 bs_debug_error("failed to open card for display");
546 exit(EXIT_FAILURE);
547 }
548 struct gbm_device* gbm = gbm_create_device(display_fd);
549 if (!gbm) {
550 bs_debug_error("failed to create gbm device");
551 exit(EXIT_FAILURE);
552 }
553 struct bs_egl* egl = bs_egl_new();
554 if (!bs_egl_setup(egl, NULL)) {
555 bs_debug_error("failed to setup egl context");
556 exit(EXIT_FAILURE);
557 }
Paulo Warren447fbdb2020-04-08 16:27:20 -0400558
Paulo Warren7d6d0f82020-01-30 14:52:59 -0500559 struct PageFlipContext context = init_page_flip_context(gbm, egl, display_fd);
Paulo Warren447fbdb2020-04-08 16:27:20 -0400560
Paulo Warren7d6d0f82020-01-30 14:52:59 -0500561 const uint32_t width = context.width;
562 const uint32_t height = context.height;
Paulo Warren447fbdb2020-04-08 16:27:20 -0400563 uint32_t length = align(width * height * 4, getpagesize());
Paulo Warren7d6d0f82020-01-30 14:52:59 -0500564 int memfd = create_memfd(length);
Paulo Warren7d6d0f82020-01-30 14:52:59 -0500565
Paulo Warren447fbdb2020-04-08 16:27:20 -0400566 context.imported_buffer.image_texture = init_gl(&context, width, height);
Paulo Warren7d6d0f82020-01-30 14:52:59 -0500567 context.shm_buffer.memfd = memfd;
Paulo Warren447fbdb2020-04-08 16:27:20 -0400568
Paulo Warren7d6d0f82020-01-30 14:52:59 -0500569 context.shm_buffer.mapped_rgba_data =
Paulo Warren447fbdb2020-04-08 16:27:20 -0400570 mmap(NULL, length, PROT_WRITE | PROT_READ, MAP_SHARED, context.shm_buffer.memfd, 0);
Paulo Warren7d6d0f82020-01-30 14:52:59 -0500571
572 draw_and_swap_frame(display_fd, 0, 0, 0, &context);
573
574 int ret;
575 fd_set fds;
576 time_t start, cur;
577 struct timeval v;
578 drmEventContext ev;
579
Paulo Warren447fbdb2020-04-08 16:27:20 -0400580 printf("n = Number of frames\n");
581 printf(
582 "CI(t) = 95%% Z confidence interval for the mean time to draw and composite a "
583 "frame\n");
584 printf("Sum(t) = Total drawing and compositing time\n\n");
Paulo Warren7d6d0f82020-01-30 14:52:59 -0500585
Paulo Warren447fbdb2020-04-08 16:27:20 -0400586 for (size_t i = 0; i < 2; i++) {
587 context.use_zero_copy = i;
588 context.frames = 0;
589 context.sum_of_times = 0;
590 context.sum_of_squared_times = 0;
Paulo Warren7d6d0f82020-01-30 14:52:59 -0500591
Paulo Warren447fbdb2020-04-08 16:27:20 -0400592 if (context.use_zero_copy) {
593 context.imported_buffer.dmabuf_fd = create_udmabuf(memfd, length);
594 context.imported_buffer.image_bo =
595 import_dmabuf(gbm, context.imported_buffer.dmabuf_fd, width, height);
596
597 context.imported_buffer.image =
598 import_source_buffer(context.egl, context.imported_buffer.image_bo,
599 context.imported_buffer.image_texture);
Paulo Warren7d6d0f82020-01-30 14:52:59 -0500600 }
Paulo Warren447fbdb2020-04-08 16:27:20 -0400601 srand(time(&start));
602 FD_ZERO(&fds);
603 memset(&v, 0, sizeof(v));
604 memset(&ev, 0, sizeof(ev));
605 ev.version = 2;
606 ev.page_flip_handler = draw_and_swap_frame;
607
608 // Display for kTestCaseDurationSeconds seconds.
609 while (time(&cur) < start + kTestCaseDurationSeconds) {
610 FD_SET(0, &fds);
611 FD_SET(display_fd, &fds);
612 v.tv_sec = start + kTestCaseDurationSeconds - cur;
613
614 ret = HANDLE_EINTR_AND_EAGAIN(select(display_fd + 1, &fds, NULL, NULL, &v));
615 if (ret < 0) {
616 bs_debug_error("select() failed on page flip: %s", strerror(errno));
617 exit(EXIT_FAILURE);
618 } else if (FD_ISSET(0, &fds)) {
619 fprintf(stderr, "exit due to user-input\n");
620 break;
621 } else if (FD_ISSET(display_fd, &fds)) {
622 drmHandleEvent(display_fd, &ev);
623 }
624 }
625
626 print_results(context.sum_of_squared_times, context.sum_of_times, context.frames,
627 context.use_zero_copy);
Paulo Warren7d6d0f82020-01-30 14:52:59 -0500628 }
629
Paulo Warren447fbdb2020-04-08 16:27:20 -0400630 destroy_imported_buffer(context.imported_buffer, egl);
631 destroy_shm_buffer(context.shm_buffer, length);
632 glDeleteBuffers(1, &context.vertex_attributes);
Paulo Warren7d6d0f82020-01-30 14:52:59 -0500633 destroy_buffers(context.queue, egl);
634 bs_egl_destroy(&egl);
635 gbm_device_destroy(gbm);
636 close(display_fd);
637}