blob: 5ab69963459c7b6490c84daeb1c33652363f5afa [file] [log] [blame]
Zach Reizner02832e02016-03-01 15:27:33 -08001/*
2 * Copyright 2016 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#include "bs_drm.h"
8
9static const useconds_t test_case_display_usec = 2000000;
10
11struct test_case {
12 bool expect_success;
Zach Reiznerb5935642017-02-01 14:00:39 -080013 uint32_t format; /* format for allocating buffer object from GBM*/
14 uint32_t fb_format; /* format used to create DRM framebuffer, 0 indicates same as format */
Zach Reizner02832e02016-03-01 15:27:33 -080015 enum gbm_bo_flags usage;
16};
17
18const char *format_to_string(uint32_t format)
19{
20 switch (format) {
21 case GBM_FORMAT_XRGB8888:
22 return "GBM_FORMAT_XRGB8888";
23 case GBM_FORMAT_ARGB8888:
24 return "GBM_FORMAT_ARGB8888";
25 default:
26 return "GBM_FORMAT_????????";
27 }
28}
29
30static void test_case_print(FILE *out, const struct test_case *tcase)
31{
32 fprintf(out, "expect_success=%s format=%s usage=", tcase->expect_success ? "true" : "false",
33 format_to_string(tcase->format));
34 bool first;
35 if (tcase->usage & GBM_BO_USE_SCANOUT) {
36 fprintf(out, "GBM_BO_USE_SCANOUT");
37 first = false;
38 }
39 if (tcase->usage & GBM_BO_USE_RENDERING) {
40 fprintf(out, "%sGBM_BO_USE_RENDERING", first ? "" : " | ");
41 first = false;
42 }
43 if (tcase->usage & GBM_BO_USE_LINEAR) {
44 fprintf(out, "%sGBM_BO_USE_LINEAR", first ? "" : " | ");
45 first = false;
46 }
Zach Reiznerb5935642017-02-01 14:00:39 -080047 if (tcase->fb_format)
48 fprintf(out, " fb_format=%s", format_to_string(tcase->fb_format));
Zach Reizner02832e02016-03-01 15:27:33 -080049}
50
51static void test_case_colors(const struct test_case *tcase,
52 float *colors /* sizeof(colors) / sizeof(colors[0]) == 9 */)
53{
54 colors[0] = tcase->expect_success ? 0.0f : 1.0f;
55 colors[1] = tcase->expect_success ? 1.0f : 0.0f;
56 colors[2] = 0.0f;
57
58 colors[3] = tcase->usage & GBM_BO_USE_SCANOUT ? 1.0f : 0.0f;
59 colors[4] = tcase->usage & GBM_BO_USE_RENDERING ? 0.66f : 0.0f;
60 colors[5] = tcase->usage & GBM_BO_USE_LINEAR ? 1.0f : 0.0f;
61
62 switch (tcase->format) {
63 case GBM_FORMAT_XRGB8888:
64 colors[6] = 1.0f;
65 colors[7] = 1.0f;
66 break;
67 case GBM_FORMAT_ARGB8888:
68 colors[7] = 1.0f;
69 colors[8] = 1.0f;
70 break;
71 default:
72 colors[6] = 0.33f;
73 colors[7] = 0.33f;
74 colors[8] = 0.33f;
75 break;
76 }
77}
78
79static void bo_lines(uint32_t height, float *lines /* sizeof(lines) / sizeof(lines[0]) == 9 */)
80{
81 /*
82 The screen is divided into sections using 3 lines as shown.
83 *----------*
84 |\ | / |
85 | \|/ |
86 | X |
87 | /|\ |
88 |/ | \ |
89 *----------*
90
91 Lines are evaluated as positive or negative in the linear equation:
92 Ax + By - C
93
94 Where the coffecicents A, B, and C appear in the array in the following order:
95 [ A, B, C ]
96 */
97 // negative left of the following lines' intersection
98 lines[0] = 1;
99 lines[1] = 0;
100 lines[2] = height / 2;
101
102 // negative on lower-right triangle section
103 lines[3] = 1;
104 lines[4] = -1;
105 lines[5] = 0;
106
107 // negative on upper-left triangle section
108 lines[6] = 1;
109 lines[7] = 1;
110 lines[8] = height;
111}
112
113static bool test_case_draw_gl(struct bs_egl *egl, const struct test_case *tcase, struct gbm_bo *bo)
114{
115 bool success = true;
116 uint32_t width = gbm_bo_get_width(bo);
117 uint32_t height = gbm_bo_get_height(bo);
118
119 EGLImageKHR image = bs_egl_image_create_gbm(egl, bo);
120 if (image == EGL_NO_IMAGE_KHR) {
121 success = false;
122 bs_debug_error("failed to make image from buffer object");
123 goto out;
124 }
125
126 struct bs_egl_fb *fb = bs_egl_fb_new(egl, image);
127 if (!fb) {
128 success = false;
129 bs_debug_error("failed to make rednering framebuffer for buffer object");
130 bs_egl_image_destroy(egl, &image);
131 goto image_destroy;
132 }
133
134 const GLchar *vert =
135 "attribute vec2 vPosition;\n"
136 "void main() {\n"
137 " gl_Position = vec4(vPosition, 0, 1);\n"
138 "}\n";
139
140 const GLchar *frag =
141 "precision mediump float;\n"
142 "uniform vec3 uColors[3];\n"
143 "uniform vec3 uLines[3];\n"
144 "void main() {\n"
145 " bool left = dot(uLines[0].xy, gl_FragCoord.xy) < uLines[0].z;\n"
146 " bool lower_right = dot(uLines[1].xy, gl_FragCoord.xy) < uLines[1].z;\n"
147 " bool upper_left = dot(uLines[2].xy, gl_FragCoord.xy) < uLines[2].z;\n"
148 " if (left && upper_left)\n"
149 " gl_FragColor = vec4(uColors[0], 1.0);\n"
150 " else if ((left && !upper_left) || (!left && lower_right))\n"
151 " gl_FragColor = vec4(uColors[1], 1.0);\n"
152 " else\n"
153 " gl_FragColor = vec4(uColors[2], 1.0);\n"
154 "}\n";
155
156 struct bs_gl_program_create_binding bindings[] = {
157 { 0, "vPosition" }, { 0, NULL },
158 };
159
160 GLuint program = bs_gl_program_create_vert_frag_bind(vert, frag, bindings);
161 if (!program) {
162 success = false;
163 bs_debug_error("failed to compile test case shader program");
164 goto fb_destroy;
165 }
166 GLint colors_location = glGetUniformLocation(program, "uColors");
167 GLint lines_location = glGetUniformLocation(program, "uLines");
168 if (colors_location == -1 || lines_location == -1) {
169 success = false;
170 bs_debug_error("failed to retrieve uniform location");
171 goto delete_program;
172 }
173
174 GLfloat colors[9];
175 test_case_colors(tcase, colors);
176
177 float lines[9];
178 bo_lines(height, lines);
179
180 // clang-format off
181 const GLfloat verts[] = {
182 -1.0f, -1.0f,
183 2.0f, -1.0f,
184 -1.0f, 2.0f,
185 2.0f, 2.0f,
186 };
187 // clang-format on
188
189 glBindFramebuffer(GL_FRAMEBUFFER, bs_egl_fb_name(fb));
190 glViewport(0, 0, (GLint)width, (GLint)height);
191
192 glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
193 glClear(GL_COLOR_BUFFER_BIT);
194
195 glUseProgram(program);
196 glUniform3fv(colors_location, 3, colors);
197 glUniform3fv(lines_location, 3, lines);
198
199 glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, verts);
200 glEnableVertexAttribArray(0);
201 glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
202
203 glFinish();
204
205 glUseProgram(0);
206 glBindFramebuffer(GL_FRAMEBUFFER, 0);
207
208delete_program:
209 glDeleteProgram(program);
210fb_destroy:
211 bs_egl_fb_destroy(&fb);
212image_destroy:
213 bs_egl_image_destroy(egl, &image);
214out:
215 return success;
216}
217
Dongseong Hwang2193f5c2016-04-22 17:25:58 +0300218static bool test_case_draw_dma_buf(const struct test_case *tcase, struct gbm_bo *bo)
Zach Reizner02832e02016-03-01 15:27:33 -0800219{
220 int bo_fd = gbm_bo_get_fd(bo);
221 if (bo_fd < 0) {
222 bs_debug_error("failed to get fd of bo");
223 return false;
224 }
Zach Reizner02832e02016-03-01 15:27:33 -0800225 uint32_t width = gbm_bo_get_width(bo);
226 uint32_t height = gbm_bo_get_height(bo);
227 uint32_t stride = gbm_bo_get_stride(bo);
Dongseong Hwang2193f5c2016-04-22 17:25:58 +0300228 uint8_t *ptr = bs_dma_buf_mmap(bo);
Zach Reizner02832e02016-03-01 15:27:33 -0800229 if (!ptr) {
Dongseong Hwang2193f5c2016-04-22 17:25:58 +0300230 bs_debug_error("failed to mmap gbm bo");
Zach Reizner02832e02016-03-01 15:27:33 -0800231 return false;
232 }
233
234 float colors_float[9];
235 test_case_colors(tcase, colors_float);
236 uint8_t colors[9];
237 for (size_t i = 0; i < 9; i++)
238 colors[i] = (uint8_t)(colors_float[i] * 255.0f);
239
240 float lines[9];
241 bo_lines(height, lines);
242
243 for (uint32_t y = 0; y < height; y++) {
244 uint8_t *row_ptr = &ptr[y * stride];
245 for (uint32_t x = 0; x < width; x++) {
246 bool left = lines[0] * (float)x + lines[1] * (float)y < lines[2];
247 bool lower_right = lines[3] * (float)x + lines[4] * (float)y < lines[5];
248 bool upper_left = lines[6] * (float)x + lines[7] * (float)y < lines[8];
249
250 int color_index = 0;
251 if (left && upper_left)
252 color_index = 0;
253 else if ((left && !upper_left) || (!left && lower_right))
254 color_index = 1;
255 else
256 color_index = 2;
257
258 row_ptr[x * 4 + 0] = colors[color_index * 3 + 2];
259 row_ptr[x * 4 + 1] = colors[color_index * 3 + 1];
260 row_ptr[x * 4 + 2] = colors[color_index * 3 + 0];
261 row_ptr[x * 4 + 3] = 0;
262 }
263 }
264
Dongseong Hwang2193f5c2016-04-22 17:25:58 +0300265 bs_dma_buf_unmmap(bo, ptr);
Zach Reizner02832e02016-03-01 15:27:33 -0800266
267 return true;
268}
269
270int main(int argc, char **argv)
271{
272 const struct test_case tcases[] = {
Zach Reiznerb5935642017-02-01 14:00:39 -0800273 { true, GBM_FORMAT_XRGB8888, 0, GBM_BO_USE_SCANOUT | GBM_BO_USE_RENDERING },
274 { true, GBM_FORMAT_XRGB8888, 0, GBM_BO_USE_SCANOUT | GBM_BO_USE_LINEAR },
275 { true, GBM_FORMAT_ARGB8888, GBM_FORMAT_XRGB8888,
276 GBM_BO_USE_SCANOUT | GBM_BO_USE_RENDERING },
277 { true, GBM_FORMAT_ARGB8888, GBM_FORMAT_XRGB8888,
278 GBM_BO_USE_SCANOUT | GBM_BO_USE_LINEAR },
279 { false, GBM_FORMAT_XRGB8888, 0,
Zach Reizner02832e02016-03-01 15:27:33 -0800280 GBM_BO_USE_SCANOUT | GBM_BO_USE_RENDERING | GBM_BO_USE_LINEAR },
Zach Reiznerb5935642017-02-01 14:00:39 -0800281 { false, GBM_FORMAT_ARGB8888, 0,
Zach Reizner02832e02016-03-01 15:27:33 -0800282 GBM_BO_USE_SCANOUT | GBM_BO_USE_RENDERING | GBM_BO_USE_LINEAR },
283 };
Gurchetan Singh0ba89ed2017-02-23 16:46:34 -0800284 const size_t tcase_count = BS_ARRAY_LEN(tcases);
Zach Reizner02832e02016-03-01 15:27:33 -0800285
286 int display_fd = bs_drm_open_main_display();
287 if (display_fd < 0) {
288 bs_debug_error("failed to open card for display");
289 return 1;
290 }
291
Zach Reizner02832e02016-03-01 15:27:33 -0800292 struct gbm_device *gbm = gbm_create_device(display_fd);
293 if (!gbm) {
294 bs_debug_error("failed to create gbm device");
295 return 1;
296 }
297
298 struct bs_drm_pipe pipe = { 0 };
299 if (!bs_drm_pipe_make(display_fd, &pipe)) {
300 bs_debug_error("failed to make pipe");
301 return 1;
302 }
303
304 drmModeConnector *connector = drmModeGetConnector(display_fd, pipe.connector_id);
305 drmModeModeInfo *mode = &connector->modes[0];
306 uint32_t width = mode->hdisplay;
307 uint32_t height = mode->vdisplay;
308
309 struct bs_egl *egl = bs_egl_new();
310 if (!bs_egl_setup(egl)) {
311 bs_debug_error("failed to setup egl context");
312 return 1;
313 }
314
Gurchetan Singh0ba89ed2017-02-23 16:46:34 -0800315 uint32_t fbs[BS_ARRAY_LEN(tcases)] = { 0 };
Zach Reizner02832e02016-03-01 15:27:33 -0800316 bool all_pass = true;
317 for (size_t tcase_index = 0; tcase_index < tcase_count; tcase_index++) {
318 const struct test_case *tcase = &tcases[tcase_index];
319
320 struct gbm_bo *bo = gbm_bo_create(gbm, width, height, tcase->format, tcase->usage);
321
322 bool bo_success = (bo != NULL);
323
324 if (bo_success != tcase->expect_success) {
325 all_pass = false;
326 printf("failed test case: ");
327 test_case_print(stdout, tcase);
328 printf("\n");
329 }
330
331 if (!bo_success)
332 continue;
333
Zach Reiznerb5935642017-02-01 14:00:39 -0800334 struct bs_drm_fb_builder *fb_builder = bs_drm_fb_builder_new();
335 bs_drm_fb_builder_gbm_bo(fb_builder, bo);
336 if (tcase->fb_format)
337 bs_drm_fb_builder_format(fb_builder, tcase->fb_format);
338 fbs[tcase_index] = bs_drm_fb_builder_create_fb(fb_builder);
339 bs_drm_fb_builder_destroy(&fb_builder);
Zach Reizner02832e02016-03-01 15:27:33 -0800340 if (!fbs[tcase_index]) {
341 bs_debug_error("failed to create framebuffer from buffer object");
342 return 1;
343 }
344
345 if (tcase->usage & GBM_BO_USE_LINEAR) {
Dongseong Hwang2193f5c2016-04-22 17:25:58 +0300346 if (!test_case_draw_dma_buf(tcase, bo)) {
Zach Reizner02832e02016-03-01 15:27:33 -0800347 bs_debug_error("failed to draw to buffer using vgem");
348 return 1;
349 }
Dongseong Hwang2193f5c2016-04-22 17:25:58 +0300350 } else if (tcase->usage & GBM_BO_USE_RENDERING) {
Zach Reizner02832e02016-03-01 15:27:33 -0800351 if (!test_case_draw_gl(egl, tcase, bo)) {
352 bs_debug_error("failed to draw to buffer using GL");
353 return 1;
354 }
355 }
356
357 // Reference held in kernel by the frame buffer.
358 gbm_bo_destroy(bo);
359 }
360
361 for (size_t tcase_index = 0; tcase_index < tcase_count; tcase_index++) {
362 const struct test_case *tcase = &tcases[tcase_index];
363 uint32_t fb_id = fbs[tcase_index];
364
365 if (fb_id == 0)
366 continue;
367
368 printf("displaying test case: ");
369 test_case_print(stdout, tcase);
370 printf("\n");
371
372 int ret = drmModeSetCrtc(display_fd, pipe.crtc_id, fb_id, 0 /* x */, 0 /* y */,
373 &pipe.connector_id, 1 /* connector count */, mode);
374 if (ret) {
375 bs_debug_error("failed to set crtc: %d", ret);
376 return 1;
377 }
378 usleep(test_case_display_usec);
379 }
380
381 for (size_t tcase_index = 0; tcase_index < tcase_count; tcase_index++)
382 if (fbs[tcase_index] != 0)
383 drmModeRmFB(display_fd, fbs[tcase_index]);
384
385 bs_egl_destroy(&egl);
386
387 return all_pass ? 0 : 2;
388}