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