blob: 8a9ab665450b85798cf2eaf6e2864dbef7224867 [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 {
Zach Reiznerb5935642017-02-01 14:00:39 -080012 uint32_t format; /* format for allocating buffer object from GBM*/
13 uint32_t fb_format; /* format used to create DRM framebuffer, 0 indicates same as format */
Zach Reizner02832e02016-03-01 15:27:33 -080014 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{
Gurchetan Singhb2d9f442017-02-23 16:58:05 -080031 fprintf(out, "format=%s usage=", format_to_string(tcase->format));
32 bool first = true;
Zach Reizner02832e02016-03-01 15:27:33 -080033 if (tcase->usage & GBM_BO_USE_SCANOUT) {
34 fprintf(out, "GBM_BO_USE_SCANOUT");
35 first = false;
36 }
37 if (tcase->usage & GBM_BO_USE_RENDERING) {
38 fprintf(out, "%sGBM_BO_USE_RENDERING", first ? "" : " | ");
39 first = false;
40 }
41 if (tcase->usage & GBM_BO_USE_LINEAR) {
42 fprintf(out, "%sGBM_BO_USE_LINEAR", first ? "" : " | ");
43 first = false;
44 }
Zach Reiznerb5935642017-02-01 14:00:39 -080045 if (tcase->fb_format)
46 fprintf(out, " fb_format=%s", format_to_string(tcase->fb_format));
Zach Reizner02832e02016-03-01 15:27:33 -080047}
48
49static void test_case_colors(const struct test_case *tcase,
50 float *colors /* sizeof(colors) / sizeof(colors[0]) == 9 */)
51{
Gurchetan Singhb2d9f442017-02-23 16:58:05 -080052 colors[0] = 0.0f;
53 colors[1] = 1.0f;
Zach Reizner02832e02016-03-01 15:27:33 -080054 colors[2] = 0.0f;
55
56 colors[3] = tcase->usage & GBM_BO_USE_SCANOUT ? 1.0f : 0.0f;
57 colors[4] = tcase->usage & GBM_BO_USE_RENDERING ? 0.66f : 0.0f;
58 colors[5] = tcase->usage & GBM_BO_USE_LINEAR ? 1.0f : 0.0f;
59
60 switch (tcase->format) {
61 case GBM_FORMAT_XRGB8888:
62 colors[6] = 1.0f;
63 colors[7] = 1.0f;
64 break;
65 case GBM_FORMAT_ARGB8888:
66 colors[7] = 1.0f;
67 colors[8] = 1.0f;
68 break;
69 default:
70 colors[6] = 0.33f;
71 colors[7] = 0.33f;
72 colors[8] = 0.33f;
73 break;
74 }
75}
76
77static void bo_lines(uint32_t height, float *lines /* sizeof(lines) / sizeof(lines[0]) == 9 */)
78{
79 /*
80 The screen is divided into sections using 3 lines as shown.
81 *----------*
82 |\ | / |
83 | \|/ |
84 | X |
85 | /|\ |
86 |/ | \ |
87 *----------*
88
89 Lines are evaluated as positive or negative in the linear equation:
90 Ax + By - C
91
92 Where the coffecicents A, B, and C appear in the array in the following order:
93 [ A, B, C ]
94 */
95 // negative left of the following lines' intersection
96 lines[0] = 1;
97 lines[1] = 0;
98 lines[2] = height / 2;
99
100 // negative on lower-right triangle section
101 lines[3] = 1;
102 lines[4] = -1;
103 lines[5] = 0;
104
105 // negative on upper-left triangle section
106 lines[6] = 1;
107 lines[7] = 1;
108 lines[8] = height;
109}
110
111static bool test_case_draw_gl(struct bs_egl *egl, const struct test_case *tcase, struct gbm_bo *bo)
112{
113 bool success = true;
114 uint32_t width = gbm_bo_get_width(bo);
115 uint32_t height = gbm_bo_get_height(bo);
116
117 EGLImageKHR image = bs_egl_image_create_gbm(egl, bo);
118 if (image == EGL_NO_IMAGE_KHR) {
119 success = false;
120 bs_debug_error("failed to make image from buffer object");
121 goto out;
122 }
123
124 struct bs_egl_fb *fb = bs_egl_fb_new(egl, image);
125 if (!fb) {
126 success = false;
127 bs_debug_error("failed to make rednering framebuffer for buffer object");
128 bs_egl_image_destroy(egl, &image);
129 goto image_destroy;
130 }
131
132 const GLchar *vert =
133 "attribute vec2 vPosition;\n"
134 "void main() {\n"
135 " gl_Position = vec4(vPosition, 0, 1);\n"
136 "}\n";
137
138 const GLchar *frag =
139 "precision mediump float;\n"
140 "uniform vec3 uColors[3];\n"
141 "uniform vec3 uLines[3];\n"
142 "void main() {\n"
143 " bool left = dot(uLines[0].xy, gl_FragCoord.xy) < uLines[0].z;\n"
144 " bool lower_right = dot(uLines[1].xy, gl_FragCoord.xy) < uLines[1].z;\n"
145 " bool upper_left = dot(uLines[2].xy, gl_FragCoord.xy) < uLines[2].z;\n"
146 " if (left && upper_left)\n"
147 " gl_FragColor = vec4(uColors[0], 1.0);\n"
148 " else if ((left && !upper_left) || (!left && lower_right))\n"
149 " gl_FragColor = vec4(uColors[1], 1.0);\n"
150 " else\n"
151 " gl_FragColor = vec4(uColors[2], 1.0);\n"
152 "}\n";
153
154 struct bs_gl_program_create_binding bindings[] = {
155 { 0, "vPosition" }, { 0, NULL },
156 };
157
158 GLuint program = bs_gl_program_create_vert_frag_bind(vert, frag, bindings);
159 if (!program) {
160 success = false;
161 bs_debug_error("failed to compile test case shader program");
162 goto fb_destroy;
163 }
164 GLint colors_location = glGetUniformLocation(program, "uColors");
165 GLint lines_location = glGetUniformLocation(program, "uLines");
166 if (colors_location == -1 || lines_location == -1) {
167 success = false;
168 bs_debug_error("failed to retrieve uniform location");
169 goto delete_program;
170 }
171
172 GLfloat colors[9];
173 test_case_colors(tcase, colors);
174
175 float lines[9];
176 bo_lines(height, lines);
177
178 // clang-format off
179 const GLfloat verts[] = {
180 -1.0f, -1.0f,
181 2.0f, -1.0f,
182 -1.0f, 2.0f,
183 2.0f, 2.0f,
184 };
185 // clang-format on
186
187 glBindFramebuffer(GL_FRAMEBUFFER, bs_egl_fb_name(fb));
188 glViewport(0, 0, (GLint)width, (GLint)height);
189
190 glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
191 glClear(GL_COLOR_BUFFER_BIT);
192
193 glUseProgram(program);
194 glUniform3fv(colors_location, 3, colors);
195 glUniform3fv(lines_location, 3, lines);
196
197 glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, verts);
198 glEnableVertexAttribArray(0);
199 glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
200
201 glFinish();
202
203 glUseProgram(0);
204 glBindFramebuffer(GL_FRAMEBUFFER, 0);
205
206delete_program:
207 glDeleteProgram(program);
208fb_destroy:
209 bs_egl_fb_destroy(&fb);
210image_destroy:
211 bs_egl_image_destroy(egl, &image);
212out:
213 return success;
214}
215
Dongseong Hwang9093afe2017-03-20 19:16:28 -0700216static bool test_case_draw_dma_buf(const struct test_case *tcase, struct bs_mapper *mapper,
217 struct gbm_bo *bo)
Zach Reizner02832e02016-03-01 15:27:33 -0800218{
219 int bo_fd = gbm_bo_get_fd(bo);
220 if (bo_fd < 0) {
221 bs_debug_error("failed to get fd of bo");
222 return false;
223 }
Zach Reizner02832e02016-03-01 15:27:33 -0800224 uint32_t width = gbm_bo_get_width(bo);
225 uint32_t height = gbm_bo_get_height(bo);
226 uint32_t stride = gbm_bo_get_stride(bo);
Dongseong Hwang9093afe2017-03-20 19:16:28 -0700227 void *map_data;
228 uint8_t *ptr = bs_mapper_map(mapper, bo, 0, &map_data);
229 if (ptr == MAP_FAILED) {
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 Hwang9093afe2017-03-20 19:16:28 -0700265 bs_mapper_unmap(mapper, bo, map_data);
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[] = {
Gurchetan Singhb2d9f442017-02-23 16:58:05 -0800273 { GBM_FORMAT_XRGB8888, 0, GBM_BO_USE_SCANOUT | GBM_BO_USE_RENDERING },
274 { GBM_FORMAT_XRGB8888, 0, GBM_BO_USE_SCANOUT | GBM_BO_USE_LINEAR },
275 { GBM_FORMAT_ARGB8888, GBM_FORMAT_XRGB8888,
Zach Reiznerb5935642017-02-01 14:00:39 -0800276 GBM_BO_USE_SCANOUT | GBM_BO_USE_RENDERING },
Gurchetan Singhb2d9f442017-02-23 16:58:05 -0800277 { GBM_FORMAT_ARGB8888, GBM_FORMAT_XRGB8888,
Zach Reiznerb5935642017-02-01 14:00:39 -0800278 GBM_BO_USE_SCANOUT | GBM_BO_USE_LINEAR },
Zach Reizner02832e02016-03-01 15:27:33 -0800279 };
Gurchetan Singh0ba89ed2017-02-23 16:46:34 -0800280 const size_t tcase_count = BS_ARRAY_LEN(tcases);
Zach Reizner02832e02016-03-01 15:27:33 -0800281
282 int display_fd = bs_drm_open_main_display();
283 if (display_fd < 0) {
284 bs_debug_error("failed to open card for display");
285 return 1;
286 }
287
Zach Reizner02832e02016-03-01 15:27:33 -0800288 struct gbm_device *gbm = gbm_create_device(display_fd);
289 if (!gbm) {
290 bs_debug_error("failed to create gbm device");
291 return 1;
292 }
293
294 struct bs_drm_pipe pipe = { 0 };
295 if (!bs_drm_pipe_make(display_fd, &pipe)) {
296 bs_debug_error("failed to make pipe");
297 return 1;
298 }
299
300 drmModeConnector *connector = drmModeGetConnector(display_fd, pipe.connector_id);
301 drmModeModeInfo *mode = &connector->modes[0];
302 uint32_t width = mode->hdisplay;
303 uint32_t height = mode->vdisplay;
304
305 struct bs_egl *egl = bs_egl_new();
306 if (!bs_egl_setup(egl)) {
307 bs_debug_error("failed to setup egl context");
308 return 1;
309 }
310
Gurchetan Singh0ba89ed2017-02-23 16:46:34 -0800311 uint32_t fbs[BS_ARRAY_LEN(tcases)] = { 0 };
Shirish S99c916e2017-07-07 15:57:57 +0530312 struct bs_mapper *mapper = bs_mapper_gem_new();
Dongseong Hwang9093afe2017-03-20 19:16:28 -0700313 if (mapper == NULL) {
314 bs_debug_error("failed to create mapper object");
315 return 1;
316 }
317
Zach Reizner02832e02016-03-01 15:27:33 -0800318 bool all_pass = true;
319 for (size_t tcase_index = 0; tcase_index < tcase_count; tcase_index++) {
320 const struct test_case *tcase = &tcases[tcase_index];
321
322 struct gbm_bo *bo = gbm_bo_create(gbm, width, height, tcase->format, tcase->usage);
323
Gurchetan Singhb2d9f442017-02-23 16:58:05 -0800324 if (!bo) {
Zach Reizner02832e02016-03-01 15:27:33 -0800325 all_pass = false;
326 printf("failed test case: ");
327 test_case_print(stdout, tcase);
328 printf("\n");
Zach Reizner02832e02016-03-01 15:27:33 -0800329 continue;
Gurchetan Singhb2d9f442017-02-23 16:58:05 -0800330 }
Zach Reizner02832e02016-03-01 15:27:33 -0800331
Zach Reiznerb5935642017-02-01 14:00:39 -0800332 struct bs_drm_fb_builder *fb_builder = bs_drm_fb_builder_new();
333 bs_drm_fb_builder_gbm_bo(fb_builder, bo);
334 if (tcase->fb_format)
335 bs_drm_fb_builder_format(fb_builder, tcase->fb_format);
336 fbs[tcase_index] = bs_drm_fb_builder_create_fb(fb_builder);
337 bs_drm_fb_builder_destroy(&fb_builder);
Zach Reizner02832e02016-03-01 15:27:33 -0800338 if (!fbs[tcase_index]) {
339 bs_debug_error("failed to create framebuffer from buffer object");
340 return 1;
341 }
342
343 if (tcase->usage & GBM_BO_USE_LINEAR) {
Dongseong Hwang9093afe2017-03-20 19:16:28 -0700344 if (!test_case_draw_dma_buf(tcase, mapper, bo)) {
Zach Reizner02832e02016-03-01 15:27:33 -0800345 bs_debug_error("failed to draw to buffer using vgem");
346 return 1;
347 }
Dongseong Hwang2193f5c2016-04-22 17:25:58 +0300348 } else if (tcase->usage & GBM_BO_USE_RENDERING) {
Zach Reizner02832e02016-03-01 15:27:33 -0800349 if (!test_case_draw_gl(egl, tcase, bo)) {
350 bs_debug_error("failed to draw to buffer using GL");
351 return 1;
352 }
353 }
354
355 // Reference held in kernel by the frame buffer.
356 gbm_bo_destroy(bo);
357 }
358
Dongseong Hwang9093afe2017-03-20 19:16:28 -0700359 bs_mapper_destroy(mapper);
360
Zach Reizner02832e02016-03-01 15:27:33 -0800361 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}