1 /**************************************************************************
2 *
3 * Copyright 2014 Advanced Micro Devices, Inc.
4 * All Rights Reserved.
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a
7 * copy of this software and associated documentation files (the
8 * "Software"), to deal in the Software without restriction, including
9 * without limitation the rights to use, copy, modify, merge, publish,
10 * distribute, sub license, and/or sell copies of the Software, and to
11 * permit persons to whom the Software is furnished to do so, subject to
12 * the following conditions:
13 *
14 * The above copyright notice and this permission notice (including the
15 * next paragraph) shall be included in all copies or substantial portions
16 * of the Software.
17 *
18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
19 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
21 * IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR
22 * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
23 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
24 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
25 *
26 **************************************************************************/
27
28 #include "util/u_tests.h"
29
30 #include "util/u_draw_quad.h"
31 #include "util/format/u_format.h"
32 #include "util/u_inlines.h"
33 #include "util/u_memory.h"
34 #include "util/u_simple_shaders.h"
35 #include "util/u_surface.h"
36 #include "util/u_string.h"
37 #include "util/u_tile.h"
38 #include "tgsi/tgsi_strings.h"
39 #include "tgsi/tgsi_text.h"
40 #include "cso_cache/cso_context.h"
41 #include "frontend/winsys_handle.h"
42 #include <stdio.h>
43
44 #define TOLERANCE 0.01
45
46 static struct pipe_resource *
util_create_texture2d(struct pipe_screen * screen,unsigned width,unsigned height,enum pipe_format format,unsigned num_samples)47 util_create_texture2d(struct pipe_screen *screen, unsigned width,
48 unsigned height, enum pipe_format format,
49 unsigned num_samples)
50 {
51 struct pipe_resource templ = {0};
52
53 templ.target = PIPE_TEXTURE_2D;
54 templ.width0 = width;
55 templ.height0 = height;
56 templ.depth0 = 1;
57 templ.array_size = 1;
58 templ.nr_samples = num_samples;
59 templ.nr_storage_samples = num_samples;
60 templ.format = format;
61 templ.usage = PIPE_USAGE_DEFAULT;
62 templ.bind = PIPE_BIND_SAMPLER_VIEW |
63 (util_format_is_depth_or_stencil(format) ?
64 PIPE_BIND_DEPTH_STENCIL : PIPE_BIND_RENDER_TARGET);
65
66 return screen->resource_create(screen, &templ);
67 }
68
69 static void
util_set_framebuffer_cb0(struct cso_context * cso,struct pipe_context * ctx,struct pipe_resource * tex)70 util_set_framebuffer_cb0(struct cso_context *cso, struct pipe_context *ctx,
71 struct pipe_resource *tex)
72 {
73 struct pipe_surface templ = {{0}}, *surf;
74 struct pipe_framebuffer_state fb = {0};
75
76 templ.format = tex->format;
77 surf = ctx->create_surface(ctx, tex, &templ);
78
79 fb.width = tex->width0;
80 fb.height = tex->height0;
81 fb.cbufs[0] = surf;
82 fb.nr_cbufs = 1;
83
84 cso_set_framebuffer(cso, &fb);
85 pipe_surface_reference(&surf, NULL);
86 }
87
88 static void
util_set_blend_normal(struct cso_context * cso)89 util_set_blend_normal(struct cso_context *cso)
90 {
91 struct pipe_blend_state blend = {0};
92
93 blend.rt[0].colormask = PIPE_MASK_RGBA;
94 cso_set_blend(cso, &blend);
95 }
96
97 static void
util_set_dsa_disable(struct cso_context * cso)98 util_set_dsa_disable(struct cso_context *cso)
99 {
100 struct pipe_depth_stencil_alpha_state dsa = {{{0}}};
101
102 cso_set_depth_stencil_alpha(cso, &dsa);
103 }
104
105 static void
util_set_rasterizer_normal(struct cso_context * cso)106 util_set_rasterizer_normal(struct cso_context *cso)
107 {
108 struct pipe_rasterizer_state rs = {0};
109
110 rs.half_pixel_center = 1;
111 rs.bottom_edge_rule = 1;
112 rs.depth_clip_near = 1;
113 rs.depth_clip_far = 1;
114
115 cso_set_rasterizer(cso, &rs);
116 }
117
118 static void
util_set_max_viewport(struct cso_context * cso,struct pipe_resource * tex)119 util_set_max_viewport(struct cso_context *cso, struct pipe_resource *tex)
120 {
121 struct pipe_viewport_state viewport;
122
123 viewport.scale[0] = 0.5f * tex->width0;
124 viewport.scale[1] = 0.5f * tex->height0;
125 viewport.scale[2] = 1.0f;
126 viewport.translate[0] = 0.5f * tex->width0;
127 viewport.translate[1] = 0.5f * tex->height0;
128 viewport.translate[2] = 0.0f;
129 viewport.swizzle_x = PIPE_VIEWPORT_SWIZZLE_POSITIVE_X;
130 viewport.swizzle_y = PIPE_VIEWPORT_SWIZZLE_POSITIVE_Y;
131 viewport.swizzle_z = PIPE_VIEWPORT_SWIZZLE_POSITIVE_Z;
132 viewport.swizzle_w = PIPE_VIEWPORT_SWIZZLE_POSITIVE_W;
133
134 cso_set_viewport(cso, &viewport);
135 }
136
137 static struct cso_velems_state
util_get_interleaved_vertex_elements(struct cso_context * cso,unsigned num_elements)138 util_get_interleaved_vertex_elements(struct cso_context *cso,
139 unsigned num_elements)
140 {
141 struct cso_velems_state velem;
142 unsigned i;
143
144 memset(&velem, 0, sizeof(velem));
145 velem.count = num_elements;
146 for (i = 0; i < num_elements; i++) {
147 velem.velems[i].src_format = PIPE_FORMAT_R32G32B32A32_FLOAT;
148 velem.velems[i].src_offset = i * 16;
149 velem.velems[i].src_stride = num_elements * 4 * sizeof(float);
150 }
151
152 return velem;
153 }
154
155 static void *
util_set_passthrough_vertex_shader(struct cso_context * cso,struct pipe_context * ctx,bool window_space)156 util_set_passthrough_vertex_shader(struct cso_context *cso,
157 struct pipe_context *ctx,
158 bool window_space)
159 {
160 static const enum tgsi_semantic vs_attribs[] = {
161 TGSI_SEMANTIC_POSITION,
162 TGSI_SEMANTIC_GENERIC
163 };
164 static const unsigned vs_indices[] = {0, 0};
165 void *vs;
166
167 vs = util_make_vertex_passthrough_shader(ctx, 2, vs_attribs, vs_indices,
168 window_space);
169 cso_set_vertex_shader_handle(cso, vs);
170 return vs;
171 }
172
173 static void
util_set_common_states_and_clear(struct cso_context * cso,struct pipe_context * ctx,struct pipe_resource * cb)174 util_set_common_states_and_clear(struct cso_context *cso, struct pipe_context *ctx,
175 struct pipe_resource *cb)
176 {
177 static const float clear_color[] = {0.1, 0.1, 0.1, 0.1};
178
179 util_set_framebuffer_cb0(cso, ctx, cb);
180 util_set_blend_normal(cso);
181 util_set_dsa_disable(cso);
182 util_set_rasterizer_normal(cso);
183 util_set_max_viewport(cso, cb);
184
185 ctx->clear(ctx, PIPE_CLEAR_COLOR0, NULL, (void*)clear_color, 0, 0);
186 }
187
188 static void
util_draw_fullscreen_quad(struct cso_context * cso)189 util_draw_fullscreen_quad(struct cso_context *cso)
190 {
191 static float vertices[] = {
192 -1, -1, 0, 1, 0, 0, 0, 0,
193 -1, 1, 0, 1, 0, 1, 0, 0,
194 1, 1, 0, 1, 1, 1, 0, 0,
195 1, -1, 0, 1, 1, 0, 0, 0
196 };
197 struct cso_velems_state ve = util_get_interleaved_vertex_elements(cso, 2);
198
199 util_draw_user_vertices(cso, &ve, vertices, MESA_PRIM_QUADS, 4);
200 }
201
202 static void
util_draw_fullscreen_quad_fill(struct cso_context * cso,float r,float g,float b,float a)203 util_draw_fullscreen_quad_fill(struct cso_context *cso,
204 float r, float g, float b, float a)
205 {
206 float vertices[] = {
207 -1, -1, 0, 1, r, g, b, a,
208 -1, 1, 0, 1, r, g, b, a,
209 1, 1, 0, 1, r, g, b, a,
210 1, -1, 0, 1, r, g, b, a,
211 };
212 struct cso_velems_state ve = util_get_interleaved_vertex_elements(cso, 2);
213
214 util_draw_user_vertices(cso, &ve, vertices, MESA_PRIM_QUADS, 4);
215 }
216
217 /**
218 * Probe and test if the rectangle contains the expected color.
219 *
220 * If "num_expected_colors" > 1, at least one expected color must match
221 * the probed color. "expected" should be an array of 4*num_expected_colors
222 * floats.
223 */
224 static bool
util_probe_rect_rgba_multi(struct pipe_context * ctx,struct pipe_resource * tex,unsigned offx,unsigned offy,unsigned w,unsigned h,const float * expected,unsigned num_expected_colors)225 util_probe_rect_rgba_multi(struct pipe_context *ctx, struct pipe_resource *tex,
226 unsigned offx, unsigned offy, unsigned w,
227 unsigned h,
228 const float *expected,
229 unsigned num_expected_colors)
230 {
231 struct pipe_transfer *transfer;
232 void *map;
233 float *pixels = malloc(w * h * 4 * sizeof(float));
234 unsigned x,y,e,c;
235 bool pass = true;
236
237 map = pipe_texture_map(ctx, tex, 0, 0, PIPE_MAP_READ,
238 offx, offy, w, h, &transfer);
239 pipe_get_tile_rgba(transfer, map, 0, 0, w, h, tex->format, pixels);
240 pipe_texture_unmap(ctx, transfer);
241
242 for (e = 0; e < num_expected_colors; e++) {
243 for (y = 0; y < h; y++) {
244 for (x = 0; x < w; x++) {
245 float *probe = &pixels[(y*w + x)*4];
246
247 for (c = 0; c < 4; c++) {
248 if (fabs(probe[c] - expected[e*4+c]) >= TOLERANCE) {
249 if (e < num_expected_colors-1)
250 goto next_color; /* test the next expected color */
251
252 printf("Probe color at (%i,%i), ", offx+x, offy+y);
253 printf("Expected: %.3f, %.3f, %.3f, %.3f, ",
254 expected[e*4], expected[e*4+1],
255 expected[e*4+2], expected[e*4+3]);
256 printf("Got: %.3f, %.3f, %.3f, %.3f\n",
257 probe[0], probe[1], probe[2], probe[3]);
258 pass = false;
259 goto done;
260 }
261 }
262 }
263 }
264 break; /* this color was successful */
265
266 next_color:;
267 }
268 done:
269
270 free(pixels);
271 return pass;
272 }
273
274 static bool
util_probe_rect_rgba(struct pipe_context * ctx,struct pipe_resource * tex,unsigned offx,unsigned offy,unsigned w,unsigned h,const float * expected)275 util_probe_rect_rgba(struct pipe_context *ctx, struct pipe_resource *tex,
276 unsigned offx, unsigned offy, unsigned w, unsigned h,
277 const float *expected)
278 {
279 return util_probe_rect_rgba_multi(ctx, tex, offx, offy, w, h, expected, 1);
280 }
281
282 enum {
283 SKIP = -1,
284 FAIL = 0, /* also "false" */
285 PASS = 1 /* also "true" */
286 };
287
288 static void
util_report_result_helper(int status,const char * name,...)289 util_report_result_helper(int status, const char *name, ...)
290 {
291 char buf[256];
292 va_list ap;
293
294 va_start(ap, name);
295 vsnprintf(buf, sizeof(buf), name, ap);
296 va_end(ap);
297
298 printf("Test(%s) = %s\n", buf,
299 status == SKIP ? "skip" :
300 status == PASS ? "pass" : "fail");
301 }
302
303 #define util_report_result(status) util_report_result_helper(status, __func__)
304
305 /**
306 * Test TGSI_PROPERTY_VS_WINDOW_SPACE_POSITION.
307 *
308 * The viewport state is set as usual, but it should have no effect.
309 * Clipping should also be disabled.
310 *
311 * POSITION.xyz should already be multiplied by 1/w and POSITION.w should
312 * contain 1/w. By setting w=0, we can test that POSITION.xyz isn't
313 * multiplied by 1/w (otherwise nothing would be rendered).
314 *
315 * TODO: Whether the value of POSITION.w is correctly interpreted as 1/w
316 * during perspective interpolation is not tested.
317 */
318 static void
tgsi_vs_window_space_position(struct pipe_context * ctx)319 tgsi_vs_window_space_position(struct pipe_context *ctx)
320 {
321 struct cso_context *cso;
322 struct pipe_resource *cb;
323 void *fs, *vs;
324 bool pass = true;
325 static const float red[] = {1, 0, 0, 1};
326
327 if (!ctx->screen->get_param(ctx->screen,
328 PIPE_CAP_VS_WINDOW_SPACE_POSITION)) {
329 util_report_result(SKIP);
330 return;
331 }
332
333 cso = cso_create_context(ctx, 0);
334 cb = util_create_texture2d(ctx->screen, 256, 256,
335 PIPE_FORMAT_R8G8B8A8_UNORM, 0);
336 util_set_common_states_and_clear(cso, ctx, cb);
337
338 /* Fragment shader. */
339 fs = util_make_fragment_passthrough_shader(ctx, TGSI_SEMANTIC_GENERIC,
340 TGSI_INTERPOLATE_LINEAR, true);
341 cso_set_fragment_shader_handle(cso, fs);
342
343 /* Vertex shader. */
344 vs = util_set_passthrough_vertex_shader(cso, ctx, true);
345
346 /* Draw. */
347 {
348 static float vertices[] = {
349 0, 0, 0, 0, 1, 0, 0, 1,
350 0, 256, 0, 0, 1, 0, 0, 1,
351 256, 256, 0, 0, 1, 0, 0, 1,
352 256, 0, 0, 0, 1, 0, 0, 1,
353 };
354 struct cso_velems_state ve = util_get_interleaved_vertex_elements(cso, 2);
355
356 util_draw_user_vertices(cso, &ve, vertices, MESA_PRIM_QUADS, 4);
357 }
358
359 /* Probe pixels. */
360 pass = pass && util_probe_rect_rgba(ctx, cb, 0, 0,
361 cb->width0, cb->height0, red);
362
363 /* Cleanup. */
364 cso_destroy_context(cso);
365 ctx->delete_vs_state(ctx, vs);
366 ctx->delete_fs_state(ctx, fs);
367 pipe_resource_reference(&cb, NULL);
368
369 util_report_result(pass);
370 }
371
372 static void
null_sampler_view(struct pipe_context * ctx,unsigned tgsi_tex_target)373 null_sampler_view(struct pipe_context *ctx, unsigned tgsi_tex_target)
374 {
375 struct cso_context *cso;
376 struct pipe_resource *cb;
377 void *fs, *vs;
378 bool pass = true;
379 /* 2 expected colors: */
380 static const float expected_tex[] = {0, 0, 0, 1,
381 0, 0, 0, 0};
382 static const float expected_buf[] = {0, 0, 0, 0};
383 const float *expected = tgsi_tex_target == TGSI_TEXTURE_BUFFER ?
384 expected_buf : expected_tex;
385 unsigned num_expected = tgsi_tex_target == TGSI_TEXTURE_BUFFER ? 1 : 2;
386
387 if (tgsi_tex_target == TGSI_TEXTURE_BUFFER &&
388 !ctx->screen->get_param(ctx->screen, PIPE_CAP_TEXTURE_BUFFER_OBJECTS)) {
389 util_report_result_helper(SKIP, "%s: %s", __func__,
390 tgsi_texture_names[tgsi_tex_target]);
391 return;
392 }
393
394 cso = cso_create_context(ctx, 0);
395 cb = util_create_texture2d(ctx->screen, 256, 256,
396 PIPE_FORMAT_R8G8B8A8_UNORM, 0);
397 util_set_common_states_and_clear(cso, ctx, cb);
398
399 ctx->set_sampler_views(ctx, PIPE_SHADER_FRAGMENT, 0, 0, 1, false, NULL);
400
401 /* Fragment shader. */
402 fs = util_make_fragment_tex_shader(ctx, tgsi_tex_target,
403 TGSI_RETURN_TYPE_FLOAT,
404 TGSI_RETURN_TYPE_FLOAT, false, false);
405 cso_set_fragment_shader_handle(cso, fs);
406
407 /* Vertex shader. */
408 vs = util_set_passthrough_vertex_shader(cso, ctx, false);
409 util_draw_fullscreen_quad(cso);
410
411 /* Probe pixels. */
412 pass = pass && util_probe_rect_rgba_multi(ctx, cb, 0, 0,
413 cb->width0, cb->height0, expected,
414 num_expected);
415
416 /* Cleanup. */
417 cso_destroy_context(cso);
418 ctx->delete_vs_state(ctx, vs);
419 ctx->delete_fs_state(ctx, fs);
420 pipe_resource_reference(&cb, NULL);
421
422 util_report_result_helper(pass, "%s: %s", __func__,
423 tgsi_texture_names[tgsi_tex_target]);
424 }
425
426 void
util_test_constant_buffer(struct pipe_context * ctx,struct pipe_resource * constbuf)427 util_test_constant_buffer(struct pipe_context *ctx,
428 struct pipe_resource *constbuf)
429 {
430 struct cso_context *cso;
431 struct pipe_resource *cb;
432 void *fs, *vs;
433 bool pass = true;
434 static const float zero[] = {0, 0, 0, 0};
435
436 cso = cso_create_context(ctx, 0);
437 cb = util_create_texture2d(ctx->screen, 256, 256,
438 PIPE_FORMAT_R8G8B8A8_UNORM, 0);
439 util_set_common_states_and_clear(cso, ctx, cb);
440
441 pipe_set_constant_buffer(ctx, PIPE_SHADER_FRAGMENT, 0, constbuf);
442
443 /* Fragment shader. */
444 {
445 static const char *text = /* I don't like ureg... */
446 "FRAG\n"
447 "DCL CONST[0][0]\n"
448 "DCL OUT[0], COLOR\n"
449
450 "MOV OUT[0], CONST[0][0]\n"
451 "END\n";
452 struct tgsi_token tokens[1000];
453 struct pipe_shader_state state = {0};
454
455 if (!tgsi_text_translate(text, tokens, ARRAY_SIZE(tokens))) {
456 puts("Can't compile a fragment shader.");
457 util_report_result(FAIL);
458 return;
459 }
460 pipe_shader_state_from_tgsi(&state, tokens);
461 fs = ctx->create_fs_state(ctx, &state);
462 cso_set_fragment_shader_handle(cso, fs);
463 }
464
465 /* Vertex shader. */
466 vs = util_set_passthrough_vertex_shader(cso, ctx, false);
467 util_draw_fullscreen_quad(cso);
468
469 /* Probe pixels. */
470 pass = pass && util_probe_rect_rgba(ctx, cb, 0, 0, cb->width0,
471 cb->height0, zero);
472
473 /* Cleanup. */
474 cso_destroy_context(cso);
475 ctx->delete_vs_state(ctx, vs);
476 ctx->delete_fs_state(ctx, fs);
477 pipe_resource_reference(&cb, NULL);
478
479 util_report_result(pass);
480 }
481
482 static void
disabled_fragment_shader(struct pipe_context * ctx)483 disabled_fragment_shader(struct pipe_context *ctx)
484 {
485 struct cso_context *cso;
486 struct pipe_resource *cb;
487 void *vs;
488 struct pipe_rasterizer_state rs = {0};
489 struct pipe_query *query;
490 union pipe_query_result qresult;
491
492 cso = cso_create_context(ctx, 0);
493 cb = util_create_texture2d(ctx->screen, 256, 256,
494 PIPE_FORMAT_R8G8B8A8_UNORM, 0);
495 util_set_common_states_and_clear(cso, ctx, cb);
496
497 /* No rasterization. */
498 rs.rasterizer_discard = 1;
499 cso_set_rasterizer(cso, &rs);
500
501 vs = util_set_passthrough_vertex_shader(cso, ctx, false);
502
503 void *fs = util_make_empty_fragment_shader(ctx);
504 cso_set_fragment_shader_handle(cso, fs);
505
506 query = ctx->create_query(ctx, PIPE_QUERY_PRIMITIVES_GENERATED, 0);
507 ctx->begin_query(ctx, query);
508 util_draw_fullscreen_quad(cso);
509 ctx->end_query(ctx, query);
510 ctx->get_query_result(ctx, query, true, &qresult);
511
512 /* Cleanup. */
513 cso_destroy_context(cso);
514 ctx->delete_vs_state(ctx, vs);
515 ctx->delete_fs_state(ctx, fs);
516 ctx->destroy_query(ctx, query);
517 pipe_resource_reference(&cb, NULL);
518
519 /* Check PRIMITIVES_GENERATED. */
520 util_report_result(qresult.u64 == 2);
521 }
522
523 #if DETECT_OS_LINUX && defined(HAVE_LIBDRM)
524 #include <libsync.h>
525 #else
526 #define sync_merge(str, fd1, fd2) (-1)
527 #define sync_wait(fd, timeout) (-1)
528 #endif
529
530 static void
test_sync_file_fences(struct pipe_context * ctx)531 test_sync_file_fences(struct pipe_context *ctx)
532 {
533 struct pipe_screen *screen = ctx->screen;
534 bool pass = true;
535 enum pipe_fd_type fd_type = PIPE_FD_TYPE_NATIVE_SYNC;
536
537 if (!screen->get_param(screen, PIPE_CAP_NATIVE_FENCE_FD))
538 return;
539
540 struct cso_context *cso = cso_create_context(ctx, 0);
541 struct pipe_resource *buf =
542 pipe_buffer_create(screen, 0, PIPE_USAGE_DEFAULT, 1024 * 1024);
543 struct pipe_resource *tex =
544 util_create_texture2d(screen, 4096, 1024, PIPE_FORMAT_R8_UNORM, 0);
545 struct pipe_fence_handle *buf_fence = NULL, *tex_fence = NULL;
546
547 /* Run 2 clears, get fencess. */
548 uint32_t value = 0;
549 ctx->clear_buffer(ctx, buf, 0, buf->width0, &value, sizeof(value));
550 ctx->flush(ctx, &buf_fence, PIPE_FLUSH_FENCE_FD);
551
552 struct pipe_box box;
553 u_box_2d(0, 0, tex->width0, tex->height0, &box);
554 ctx->clear_texture(ctx, tex, 0, &box, &value);
555 ctx->flush(ctx, &tex_fence, PIPE_FLUSH_FENCE_FD);
556 pass = pass && buf_fence && tex_fence;
557
558 /* Export fences. */
559 int buf_fd = screen->fence_get_fd(screen, buf_fence);
560 int tex_fd = screen->fence_get_fd(screen, tex_fence);
561 pass = pass && buf_fd >= 0 && tex_fd >= 0;
562
563 /* Merge fences. */
564 int merged_fd = sync_merge("test", buf_fd, tex_fd);
565 pass = pass && merged_fd >= 0;
566
567 /* (Re)import all fences. */
568 struct pipe_fence_handle *re_buf_fence = NULL, *re_tex_fence = NULL;
569 struct pipe_fence_handle *merged_fence = NULL;
570 ctx->create_fence_fd(ctx, &re_buf_fence, buf_fd, fd_type);
571 ctx->create_fence_fd(ctx, &re_tex_fence, tex_fd, fd_type);
572 ctx->create_fence_fd(ctx, &merged_fence, merged_fd, fd_type);
573 pass = pass && re_buf_fence && re_tex_fence && merged_fence;
574
575 /* Run another clear after waiting for everything. */
576 struct pipe_fence_handle *final_fence = NULL;
577 ctx->fence_server_sync(ctx, merged_fence);
578 value = 0xff;
579 ctx->clear_buffer(ctx, buf, 0, buf->width0, &value, sizeof(value));
580 ctx->flush(ctx, &final_fence, PIPE_FLUSH_FENCE_FD);
581 pass = pass && final_fence;
582
583 /* Wait for the last fence. */
584 int final_fd = screen->fence_get_fd(screen, final_fence);
585 pass = pass && final_fd >= 0;
586 pass = pass && sync_wait(final_fd, -1) == 0;
587
588 /* Check that all fences are signalled. */
589 pass = pass && sync_wait(buf_fd, 0) == 0;
590 pass = pass && sync_wait(tex_fd, 0) == 0;
591 pass = pass && sync_wait(merged_fd, 0) == 0;
592
593 pass = pass && screen->fence_finish(screen, NULL, buf_fence, 0);
594 pass = pass && screen->fence_finish(screen, NULL, tex_fence, 0);
595 pass = pass && screen->fence_finish(screen, NULL, re_buf_fence, 0);
596 pass = pass && screen->fence_finish(screen, NULL, re_tex_fence, 0);
597 pass = pass && screen->fence_finish(screen, NULL, merged_fence, 0);
598 pass = pass && screen->fence_finish(screen, NULL, final_fence, 0);
599
600 /* Cleanup. */
601 #if !DETECT_OS_WINDOWS
602 if (buf_fd >= 0)
603 close(buf_fd);
604 if (tex_fd >= 0)
605 close(tex_fd);
606 if (merged_fd >= 0)
607 close(merged_fd);
608 if (final_fd >= 0)
609 close(final_fd);
610 #endif
611
612 screen->fence_reference(screen, &buf_fence, NULL);
613 screen->fence_reference(screen, &tex_fence, NULL);
614 screen->fence_reference(screen, &re_buf_fence, NULL);
615 screen->fence_reference(screen, &re_tex_fence, NULL);
616 screen->fence_reference(screen, &merged_fence, NULL);
617 screen->fence_reference(screen, &final_fence, NULL);
618
619 cso_destroy_context(cso);
620 pipe_resource_reference(&buf, NULL);
621 pipe_resource_reference(&tex, NULL);
622
623 util_report_result(pass);
624 }
625
626 static void
test_texture_barrier(struct pipe_context * ctx,bool use_fbfetch,unsigned num_samples)627 test_texture_barrier(struct pipe_context *ctx, bool use_fbfetch,
628 unsigned num_samples)
629 {
630 struct cso_context *cso;
631 struct pipe_resource *cb;
632 struct pipe_sampler_view *view = NULL;
633 char name[256];
634 const char *text;
635
636 assert(num_samples >= 1 && num_samples <= 8);
637
638 snprintf(name, sizeof(name), "%s: %s, %u samples", __func__,
639 use_fbfetch ? "FBFETCH" : "sampler", MAX2(num_samples, 1));
640
641 if (!ctx->screen->get_param(ctx->screen, PIPE_CAP_TEXTURE_BARRIER)) {
642 util_report_result_helper(SKIP, name);
643 return;
644 }
645 if (use_fbfetch &&
646 !ctx->screen->get_param(ctx->screen, PIPE_CAP_FBFETCH)) {
647 util_report_result_helper(SKIP, name);
648 return;
649 }
650
651 cso = cso_create_context(ctx, 0);
652 cb = util_create_texture2d(ctx->screen, 256, 256,
653 PIPE_FORMAT_R8G8B8A8_UNORM, num_samples);
654 util_set_common_states_and_clear(cso, ctx, cb);
655
656 /* Clear each sample to a different value. */
657 if (num_samples > 1) {
658 void *fs =
659 util_make_fragment_passthrough_shader(ctx, TGSI_SEMANTIC_GENERIC,
660 TGSI_INTERPOLATE_LINEAR, true);
661 cso_set_fragment_shader_handle(cso, fs);
662
663 /* Vertex shader. */
664 void *vs = util_set_passthrough_vertex_shader(cso, ctx, false);
665
666 for (unsigned i = 0; i < num_samples / 2; i++) {
667 float value;
668
669 /* 2 consecutive samples should have the same color to test MSAA
670 * compression properly.
671 */
672 if (num_samples == 2) {
673 value = 0.1;
674 } else {
675 /* The average value must be 0.1 */
676 static const float values[] = {
677 0.0, 0.2, 0.05, 0.15
678 };
679 value = values[i];
680 }
681
682 ctx->set_sample_mask(ctx, 0x3 << (i * 2));
683 util_draw_fullscreen_quad_fill(cso, value, value, value, value);
684 }
685 ctx->set_sample_mask(ctx, ~0);
686
687 cso_set_vertex_shader_handle(cso, NULL);
688 cso_set_fragment_shader_handle(cso, NULL);
689 ctx->delete_vs_state(ctx, vs);
690 ctx->delete_fs_state(ctx, fs);
691 }
692
693 if (use_fbfetch) {
694 /* Fragment shader. */
695 text = "FRAG\n"
696 "DCL OUT[0], COLOR[0]\n"
697 "DCL TEMP[0]\n"
698 "IMM[0] FLT32 { 0.1, 0.2, 0.3, 0.4}\n"
699
700 "FBFETCH TEMP[0], OUT[0]\n"
701 "ADD OUT[0], TEMP[0], IMM[0]\n"
702 "END\n";
703 } else {
704 struct pipe_sampler_view templ = {0};
705 templ.format = cb->format;
706 templ.target = cb->target;
707 templ.swizzle_r = PIPE_SWIZZLE_X;
708 templ.swizzle_g = PIPE_SWIZZLE_Y;
709 templ.swizzle_b = PIPE_SWIZZLE_Z;
710 templ.swizzle_a = PIPE_SWIZZLE_W;
711 view = ctx->create_sampler_view(ctx, cb, &templ);
712 ctx->set_sampler_views(ctx, PIPE_SHADER_FRAGMENT, 0, 1, 0, false, &view);
713
714 /* Fragment shader. */
715 if (num_samples > 1) {
716 text = "FRAG\n"
717 "DCL SV[0], POSITION\n"
718 "DCL SV[1], SAMPLEID\n"
719 "DCL SAMP[0]\n"
720 "DCL SVIEW[0], 2D_MSAA, FLOAT\n"
721 "DCL OUT[0], COLOR[0]\n"
722 "DCL TEMP[0]\n"
723 "IMM[0] FLT32 { 0.1, 0.2, 0.3, 0.4}\n"
724
725 "F2I TEMP[0].xy, SV[0].xyyy\n"
726 "MOV TEMP[0].w, SV[1].xxxx\n"
727 "TXF TEMP[0], TEMP[0], SAMP[0], 2D_MSAA\n"
728 "ADD OUT[0], TEMP[0], IMM[0]\n"
729 "END\n";
730 } else {
731 text = "FRAG\n"
732 "DCL SV[0], POSITION\n"
733 "DCL SAMP[0]\n"
734 "DCL SVIEW[0], 2D, FLOAT\n"
735 "DCL OUT[0], COLOR[0]\n"
736 "DCL TEMP[0]\n"
737 "IMM[0] FLT32 { 0.1, 0.2, 0.3, 0.4}\n"
738 "IMM[1] INT32 { 0, 0, 0, 0}\n"
739
740 "F2I TEMP[0].xy, SV[0].xyyy\n"
741 "MOV TEMP[0].zw, IMM[1]\n"
742 "TXF TEMP[0], TEMP[0], SAMP[0], 2D\n"
743 "ADD OUT[0], TEMP[0], IMM[0]\n"
744 "END\n";
745 }
746 }
747
748 struct tgsi_token tokens[1000];
749 struct pipe_shader_state state = {0};
750
751 if (!tgsi_text_translate(text, tokens, ARRAY_SIZE(tokens))) {
752 assert(0);
753 util_report_result_helper(FAIL, name);
754 return;
755 }
756 pipe_shader_state_from_tgsi(&state, tokens);
757
758 void *fs = ctx->create_fs_state(ctx, &state);
759 cso_set_fragment_shader_handle(cso, fs);
760
761 /* Vertex shader. */
762 void *vs = util_set_passthrough_vertex_shader(cso, ctx, false);
763
764 if (num_samples > 1 && !use_fbfetch)
765 ctx->set_min_samples(ctx, num_samples);
766
767 for (int i = 0; i < 2; i++) {
768 ctx->texture_barrier(ctx,
769 use_fbfetch ? PIPE_TEXTURE_BARRIER_FRAMEBUFFER :
770 PIPE_TEXTURE_BARRIER_SAMPLER);
771 util_draw_fullscreen_quad(cso);
772 }
773 if (num_samples > 1 && !use_fbfetch)
774 ctx->set_min_samples(ctx, 1);
775
776 /* Probe pixels.
777 *
778 * For single sample:
779 * result = 0.1 (clear) + (0.1, 0.2, 0.3, 0.4) * 2 = (0.3, 0.5, 0.7, 0.9)
780 *
781 * For MSAA 4x:
782 * sample0 = 0.0 (clear) + (0.1, 0.2, 0.3, 0.4) * 2 = (0.2, 0.4, 0.6, 0.8)
783 * sample1 = sample0
784 * sample2 = 0.2 (clear) + (0.1, 0.2, 0.3, 0.4) * 2 = (0.4, 0.6, 0.8, 1.0)
785 * sample3 = sample2
786 * resolved = sum(sample[0:3]) / 4 = (0.3, 0.5, 0.7, 0.9)
787 */
788 static const float expected[] = {0.3, 0.5, 0.7, 0.9};
789 bool pass = util_probe_rect_rgba(ctx, cb, 0, 0,
790 cb->width0, cb->height0, expected);
791
792 /* Cleanup. */
793 cso_destroy_context(cso);
794 ctx->delete_vs_state(ctx, vs);
795 ctx->delete_fs_state(ctx, fs);
796 pipe_sampler_view_reference(&view, NULL);
797 pipe_resource_reference(&cb, NULL);
798
799 util_report_result_helper(pass, name);
800 }
801
802 static void
test_compute_clear_image_shader(struct pipe_context * ctx)803 test_compute_clear_image_shader(struct pipe_context *ctx)
804 {
805 struct pipe_resource *cb;
806 const char *text;
807
808 cb = util_create_texture2d(ctx->screen, 256, 256,
809 PIPE_FORMAT_R8G8B8A8_UNORM, 1);
810
811 /* Compute shader. */
812 text = "COMP\n"
813 "PROPERTY CS_FIXED_BLOCK_WIDTH 8\n"
814 "PROPERTY CS_FIXED_BLOCK_HEIGHT 8\n"
815 "PROPERTY CS_FIXED_BLOCK_DEPTH 1\n"
816 "DCL SV[0], THREAD_ID\n"
817 "DCL SV[1], BLOCK_ID\n"
818 "DCL IMAGE[0], 2D, PIPE_FORMAT_R8G8B8A8_UNORM, WR\n"
819 "DCL TEMP[0]\n"
820 "IMM[0] UINT32 { 8, 8, 0, 0}\n"
821 "IMM[1] FLT32 { 1, 0, 0, 0}\n"
822
823 /* TEMP[0].xy = SV[1] * IMM[0] + SV[0]; */
824 "UMAD TEMP[0].xy, SV[1], IMM[0], SV[0]\n"
825 "STORE IMAGE[0], TEMP[0], IMM[1], 2D, PIPE_FORMAT_R8G8B8A8_UNORM\n"
826 "END\n";
827
828 struct tgsi_token tokens[1000];
829 if (!tgsi_text_translate(text, tokens, ARRAY_SIZE(tokens))) {
830 assert(0);
831 util_report_result(FAIL);
832 return;
833 }
834
835 struct pipe_compute_state state = {0};
836 state.ir_type = PIPE_SHADER_IR_TGSI;
837 state.prog = tokens;
838
839 void *compute_shader = ctx->create_compute_state(ctx, &state);
840 ctx->bind_compute_state(ctx, compute_shader);
841
842 /* Bind the image. */
843 struct pipe_image_view image = {0};
844 image.resource = cb;
845 image.shader_access = image.access = PIPE_IMAGE_ACCESS_READ_WRITE;
846 image.format = cb->format;
847
848 ctx->set_shader_images(ctx, PIPE_SHADER_COMPUTE, 0, 1, 0, &image);
849
850 /* Dispatch compute. */
851 struct pipe_grid_info info = {0};
852 info.block[0] = 8;
853 info.block[1] = 8;
854 info.block[2] = 1;
855 info.grid[0] = cb->width0 / 8;
856 info.grid[1] = cb->height0 / 8;
857 info.grid[2] = 1;
858
859 ctx->launch_grid(ctx, &info);
860
861 /* Check pixels. */
862 static const float expected[] = {1.0, 0.0, 0.0, 0.0};
863 bool pass = util_probe_rect_rgba(ctx, cb, 0, 0,
864 cb->width0, cb->height0, expected);
865
866 /* Cleanup. */
867 ctx->delete_compute_state(ctx, compute_shader);
868 pipe_resource_reference(&cb, NULL);
869
870 util_report_result(pass);
871 }
872
873 static void
test_compute_clear_texture(struct pipe_context * ctx)874 test_compute_clear_texture(struct pipe_context *ctx)
875 {
876 struct pipe_resource *tex;
877
878 tex = util_create_texture2d(ctx->screen, 256, 256,
879 PIPE_FORMAT_R8G8B8A8_UNORM, 1);
880 srand(time(NULL));
881 uint8_t data[] = {rand() % 256, rand() % 256, rand() % 256, rand() % 256};
882 float expected[] = {
883 ubyte_to_float(data[0]),
884 ubyte_to_float(data[1]),
885 ubyte_to_float(data[2]),
886 ubyte_to_float(data[3]),
887 };
888
889 struct pipe_box box;
890 u_box_2d(0, 0, tex->width0, tex->height0, &box);
891 ctx->clear_texture(ctx, tex, 0, &box, &data);
892
893 /* Check pixels. */
894 bool pass = util_probe_rect_rgba(ctx, tex, 0, 0,
895 tex->width0, tex->height0, expected);
896
897 /* Cleanup. */
898 pipe_resource_reference(&tex, NULL);
899
900 util_report_result(pass);
901 }
902
903 static void
test_compute_resource_copy_region(struct pipe_context * ctx)904 test_compute_resource_copy_region(struct pipe_context *ctx)
905 {
906 struct pipe_resource *src, *dst;
907
908 src = util_create_texture2d(ctx->screen, 256, 256,
909 PIPE_FORMAT_R8G8B8A8_UNORM, 1);
910 dst = util_create_texture2d(ctx->screen, 256, 256,
911 PIPE_FORMAT_R8G8B8A8_UNORM, 1);
912 srand(time(NULL));
913 uint8_t data[] = {rand() % 256, rand() % 256, rand() % 256, rand() % 256};
914 float expected[] = {
915 ubyte_to_float(data[0]),
916 ubyte_to_float(data[1]),
917 ubyte_to_float(data[2]),
918 ubyte_to_float(data[3]),
919 };
920
921 struct pipe_box box;
922 u_box_2d(0, 0, src->width0, src->height0, &box);
923 ctx->clear_texture(ctx, src, 0, &box, &data);
924 ctx->resource_copy_region(ctx, dst, 0, 0, 0, 0, src, 0, &box);
925
926 /* Check pixels. */
927 bool pass = util_probe_rect_rgba(ctx, dst, 0, 0,
928 dst->width0, dst->height0, expected);
929
930 /* Cleanup. */
931 pipe_resource_reference(&src, NULL);
932 pipe_resource_reference(&dst, NULL);
933
934 util_report_result(pass);
935 }
936
937 #define NV12_WIDTH 2560
938 #define NV12_HEIGHT 1440
939
940 static bool
nv12_validate_resource_fields(struct pipe_resource * tex)941 nv12_validate_resource_fields(struct pipe_resource *tex)
942 {
943 return tex->format == util_format_get_plane_format(PIPE_FORMAT_NV12, 0) &&
944 tex->width0 == NV12_WIDTH &&
945 tex->height0 == NV12_HEIGHT &&
946 tex->last_level == 0 &&
947 tex->usage == PIPE_USAGE_DEFAULT &&
948 tex->next &&
949 tex->next->format == util_format_get_plane_format(PIPE_FORMAT_NV12, 1) &&
950 tex->next->width0 == tex->width0 / 2 &&
951 tex->next->height0 == tex->height0 / 2 &&
952 tex->next->usage == tex->usage;
953 }
954
955 /* This test enforces the behavior of NV12 allocation and exports. */
956 static void
test_nv12(struct pipe_screen * screen)957 test_nv12(struct pipe_screen *screen)
958 {
959 struct pipe_resource *tex = util_create_texture2d(screen, NV12_WIDTH, NV12_HEIGHT,
960 PIPE_FORMAT_NV12, 1);
961
962 if (!tex) {
963 printf("resource_create failed\n");
964 util_report_result(false);
965 return;
966 }
967
968 if (!nv12_validate_resource_fields(tex)) {
969 printf("incorrect pipe_resource fields\n");
970 util_report_result(false);
971 return;
972 }
973
974 /* resource_get_param */
975 if (screen->resource_get_param) {
976 struct {
977 uint64_t handle, dmabuf, offset, stride, planes;
978 } handle[3];
979
980 /* Export */
981 for (unsigned i = 0; i < 3; i++) {
982 struct pipe_resource *res = i == 2 ? tex->next : tex;
983 unsigned plane = i == 2 ? 0 : i;
984
985 if (!screen->resource_get_param(screen, NULL, res, plane, 0, 0,
986 PIPE_RESOURCE_PARAM_HANDLE_TYPE_KMS,
987 0, &handle[i].handle)) {
988 printf("resource_get_param failed\n");
989 util_report_result(false);
990 goto cleanup;
991 }
992
993 if (!screen->resource_get_param(screen, NULL, res, plane, 0, 0,
994 PIPE_RESOURCE_PARAM_HANDLE_TYPE_FD,
995 0, &handle[i].dmabuf)) {
996 printf("resource_get_param failed\n");
997 util_report_result(false);
998 goto cleanup;
999 }
1000
1001 if (!screen->resource_get_param(screen, NULL, res, plane, 0, 0,
1002 PIPE_RESOURCE_PARAM_OFFSET,
1003 0, &handle[i].offset)) {
1004 printf("resource_get_param failed\n");
1005 util_report_result(false);
1006 goto cleanup;
1007 }
1008
1009 if (!screen->resource_get_param(screen, NULL, res, plane, 0, 0,
1010 PIPE_RESOURCE_PARAM_STRIDE,
1011 0, &handle[i].stride)) {
1012 printf("resource_get_param failed\n");
1013 util_report_result(false);
1014 goto cleanup;
1015 }
1016
1017 if (!screen->resource_get_param(screen, NULL, res, plane, 0, 0,
1018 PIPE_RESOURCE_PARAM_NPLANES,
1019 0, &handle[i].planes)) {
1020 printf("resource_get_param failed\n");
1021 util_report_result(false);
1022 goto cleanup;
1023 }
1024 }
1025
1026 /* Validate export. */
1027 bool get_param_pass = /* Sanity checking */
1028 handle[0].handle && handle[1].handle && handle[2].handle &&
1029 handle[0].dmabuf && handle[1].dmabuf && handle[2].dmabuf &&
1030 handle[0].stride && handle[1].stride && handle[2].stride &&
1031 handle[0].planes == 2 &&
1032 handle[1].planes == 2 &&
1033 handle[2].planes == 2 &&
1034 /* Different planes */
1035 handle[0].handle == handle[1].handle &&
1036 handle[0].offset != handle[1].offset &&
1037 /* Same planes. */
1038 handle[1].handle == handle[2].handle &&
1039 handle[1].stride == handle[2].stride &&
1040 handle[1].offset == handle[2].offset;
1041
1042 if (!get_param_pass) {
1043 printf("resource_get_param returned incorrect values\n");
1044 util_report_result(false);
1045 goto cleanup;
1046 }
1047 }
1048
1049 /* resource_get_handle */
1050 struct winsys_handle handle[4] = {{0}};
1051
1052 /* Export */
1053 for (unsigned i = 0; i < 4; i++) {
1054 handle[i].type = i < 2 ? WINSYS_HANDLE_TYPE_KMS : WINSYS_HANDLE_TYPE_FD;
1055 handle[i].plane = i % 2;
1056
1057 if (!screen->resource_get_handle(screen, NULL, tex, &handle[i], 0)) {
1058 printf("resource_get_handle failed\n");
1059 util_report_result(false);
1060 goto cleanup;
1061 }
1062 }
1063
1064 /* Validate export. */
1065 bool get_handle_pass = /* Sanity checking */
1066 handle[0].handle && handle[1].handle &&
1067 handle[0].stride && handle[1].stride &&
1068 handle[2].handle && handle[3].handle &&
1069 handle[2].stride && handle[3].stride &&
1070 /* KMS - different planes */
1071 handle[0].handle == handle[1].handle &&
1072 handle[0].offset != handle[1].offset &&
1073 /* DMABUF - different planes */
1074 handle[2].offset != handle[3].offset &&
1075 /* KMS and DMABUF equivalence */
1076 handle[0].offset == handle[2].offset &&
1077 handle[1].offset == handle[3].offset &&
1078 handle[0].stride == handle[2].stride &&
1079 handle[1].stride == handle[3].stride;
1080
1081 if (!get_handle_pass) {
1082 printf("resource_get_handle returned incorrect values\n");
1083 util_report_result(false);
1084 goto cleanup;
1085 }
1086
1087 util_report_result(true);
1088
1089 cleanup:
1090 pipe_resource_reference(&tex, NULL);
1091 }
1092
1093 /**
1094 * Run all tests. This should be run with a clean context after
1095 * context_create.
1096 */
1097 void
util_run_tests(struct pipe_screen * screen)1098 util_run_tests(struct pipe_screen *screen)
1099 {
1100 struct pipe_context *ctx = screen->context_create(screen, NULL, 0);
1101
1102 disabled_fragment_shader(ctx);
1103 tgsi_vs_window_space_position(ctx);
1104 null_sampler_view(ctx, TGSI_TEXTURE_2D);
1105 null_sampler_view(ctx, TGSI_TEXTURE_BUFFER);
1106 util_test_constant_buffer(ctx, NULL);
1107 test_sync_file_fences(ctx);
1108
1109 for (int i = 1; i <= 8; i = i * 2)
1110 test_texture_barrier(ctx, false, i);
1111 for (int i = 1; i <= 8; i = i * 2)
1112 test_texture_barrier(ctx, true, i);
1113 ctx->destroy(ctx);
1114
1115 ctx = screen->context_create(screen, NULL, PIPE_CONTEXT_COMPUTE_ONLY);
1116 test_compute_clear_image_shader(ctx);
1117 test_compute_clear_texture(ctx);
1118 test_compute_resource_copy_region(ctx);
1119 ctx->destroy(ctx);
1120
1121 test_nv12(screen);
1122
1123 puts("Done. Exiting..");
1124 exit(0);
1125 }
1126