blob: c9e9619cb787fdd90c6a85614774e6f291c308ed [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
215static bool test_case_draw_vgem(int vgem_fd, const struct test_case *tcase, struct gbm_bo *bo)
216{
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 }
222 uint32_t vgem_handle;
223 int ret = drmPrimeFDToHandle(vgem_fd, bo_fd, &vgem_handle);
224 if (ret) {
225 bs_debug_error("failed to convert prime fd to vgem handle: %d", ret);
226 return false;
227 }
228 uint32_t width = gbm_bo_get_width(bo);
229 uint32_t height = gbm_bo_get_height(bo);
230 uint32_t stride = gbm_bo_get_stride(bo);
231 uint32_t bo_size = height * stride;
232 uint8_t *ptr = bs_dumb_mmap(vgem_fd, vgem_handle, bo_size);
233 if (!ptr) {
234 bs_debug_error("failed to mmap vgem handle");
235 struct drm_gem_close gem_close = { vgem_handle, 0 };
236 drmIoctl(vgem_fd, DRM_IOCTL_GEM_CLOSE, &gem_close);
237 return false;
238 }
239
240 float colors_float[9];
241 test_case_colors(tcase, colors_float);
242 uint8_t colors[9];
243 for (size_t i = 0; i < 9; i++)
244 colors[i] = (uint8_t)(colors_float[i] * 255.0f);
245
246 float lines[9];
247 bo_lines(height, lines);
248
249 for (uint32_t y = 0; y < height; y++) {
250 uint8_t *row_ptr = &ptr[y * stride];
251 for (uint32_t x = 0; x < width; x++) {
252 bool left = lines[0] * (float)x + lines[1] * (float)y < lines[2];
253 bool lower_right = lines[3] * (float)x + lines[4] * (float)y < lines[5];
254 bool upper_left = lines[6] * (float)x + lines[7] * (float)y < lines[8];
255
256 int color_index = 0;
257 if (left && upper_left)
258 color_index = 0;
259 else if ((left && !upper_left) || (!left && lower_right))
260 color_index = 1;
261 else
262 color_index = 2;
263
264 row_ptr[x * 4 + 0] = colors[color_index * 3 + 2];
265 row_ptr[x * 4 + 1] = colors[color_index * 3 + 1];
266 row_ptr[x * 4 + 2] = colors[color_index * 3 + 0];
267 row_ptr[x * 4 + 3] = 0;
268 }
269 }
270
271 munmap(ptr, bo_size);
272 struct drm_gem_close gem_close = { vgem_handle, 0 };
273 drmIoctl(vgem_fd, DRM_IOCTL_GEM_CLOSE, &gem_close);
274
275 return true;
276}
277
278int main(int argc, char **argv)
279{
280 const struct test_case tcases[] = {
281 { true, GBM_FORMAT_XRGB8888, GBM_BO_USE_SCANOUT | GBM_BO_USE_RENDERING },
282 { true, GBM_FORMAT_XRGB8888, GBM_BO_USE_SCANOUT | GBM_BO_USE_LINEAR },
283 { true, GBM_FORMAT_ARGB8888, GBM_BO_USE_SCANOUT | GBM_BO_USE_RENDERING },
284 { true, GBM_FORMAT_ARGB8888, GBM_BO_USE_SCANOUT | GBM_BO_USE_LINEAR },
285 { false, GBM_FORMAT_XRGB8888,
286 GBM_BO_USE_SCANOUT | GBM_BO_USE_RENDERING | GBM_BO_USE_LINEAR },
287 { false, GBM_FORMAT_ARGB8888,
288 GBM_BO_USE_SCANOUT | GBM_BO_USE_RENDERING | GBM_BO_USE_LINEAR },
289 };
290 const size_t tcase_count = sizeof(tcases) / sizeof(tcases[0]);
291
292 int display_fd = bs_drm_open_main_display();
293 if (display_fd < 0) {
294 bs_debug_error("failed to open card for display");
295 return 1;
296 }
297
298 int vgem_fd = bs_drm_open_vgem();
299 if (vgem_fd < 0) {
300 bs_debug_error("failed to open vgem card");
301 return 1;
302 }
303
304 struct gbm_device *gbm = gbm_create_device(display_fd);
305 if (!gbm) {
306 bs_debug_error("failed to create gbm device");
307 return 1;
308 }
309
310 struct bs_drm_pipe pipe = { 0 };
311 if (!bs_drm_pipe_make(display_fd, &pipe)) {
312 bs_debug_error("failed to make pipe");
313 return 1;
314 }
315
316 drmModeConnector *connector = drmModeGetConnector(display_fd, pipe.connector_id);
317 drmModeModeInfo *mode = &connector->modes[0];
318 uint32_t width = mode->hdisplay;
319 uint32_t height = mode->vdisplay;
320
321 struct bs_egl *egl = bs_egl_new();
322 if (!bs_egl_setup(egl)) {
323 bs_debug_error("failed to setup egl context");
324 return 1;
325 }
326
327 uint32_t fbs[sizeof(tcases) / sizeof(tcases[0])] = { 0 };
328 bool all_pass = true;
329 for (size_t tcase_index = 0; tcase_index < tcase_count; tcase_index++) {
330 const struct test_case *tcase = &tcases[tcase_index];
331
332 struct gbm_bo *bo = gbm_bo_create(gbm, width, height, tcase->format, tcase->usage);
333
334 bool bo_success = (bo != NULL);
335
336 if (bo_success != tcase->expect_success) {
337 all_pass = false;
338 printf("failed test case: ");
339 test_case_print(stdout, tcase);
340 printf("\n");
341 }
342
343 if (!bo_success)
344 continue;
345
346 fbs[tcase_index] = bs_drm_fb_create_gbm(bo);
347 if (!fbs[tcase_index]) {
348 bs_debug_error("failed to create framebuffer from buffer object");
349 return 1;
350 }
351
352 if (tcase->usage & GBM_BO_USE_LINEAR) {
353 if (!test_case_draw_vgem(vgem_fd, tcase, bo)) {
354 bs_debug_error("failed to draw to buffer using vgem");
355 return 1;
356 }
357 }
358 else if (tcase->usage & GBM_BO_USE_RENDERING) {
359 if (!test_case_draw_gl(egl, tcase, bo)) {
360 bs_debug_error("failed to draw to buffer using GL");
361 return 1;
362 }
363 }
364
365 // Reference held in kernel by the frame buffer.
366 gbm_bo_destroy(bo);
367 }
368
369 for (size_t tcase_index = 0; tcase_index < tcase_count; tcase_index++) {
370 const struct test_case *tcase = &tcases[tcase_index];
371 uint32_t fb_id = fbs[tcase_index];
372
373 if (fb_id == 0)
374 continue;
375
376 printf("displaying test case: ");
377 test_case_print(stdout, tcase);
378 printf("\n");
379
380 int ret = drmModeSetCrtc(display_fd, pipe.crtc_id, fb_id, 0 /* x */, 0 /* y */,
381 &pipe.connector_id, 1 /* connector count */, mode);
382 if (ret) {
383 bs_debug_error("failed to set crtc: %d", ret);
384 return 1;
385 }
386 usleep(test_case_display_usec);
387 }
388
389 for (size_t tcase_index = 0; tcase_index < tcase_count; tcase_index++)
390 if (fbs[tcase_index] != 0)
391 drmModeRmFB(display_fd, fbs[tcase_index]);
392
393 bs_egl_destroy(&egl);
394
395 return all_pass ? 0 : 2;
396}