gallium: add pipe cap for scissored clears and pass scissor state to clear() hook
[mesa.git] / src / gallium / auxiliary / util / u_tests.c
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 "state_tracker/winsys_handle.h"
42 #include <stdio.h>
43
44 #define TOLERANCE 0.01
45
46 static struct pipe_resource *
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
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
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
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
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
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
130 cso_set_viewport(cso, &viewport);
131 }
132
133 static void
134 util_set_interleaved_vertex_elements(struct cso_context *cso,
135 unsigned num_elements)
136 {
137 struct cso_velems_state velem;
138 unsigned i;
139
140 memset(&velem, 0, sizeof(velem));
141 velem.count = num_elements;
142 for (i = 0; i < num_elements; i++) {
143 velem.velems[i].src_format = PIPE_FORMAT_R32G32B32A32_FLOAT;
144 velem.velems[i].src_offset = i * 16;
145 }
146
147 cso_set_vertex_elements(cso, &velem);
148 }
149
150 static void *
151 util_set_passthrough_vertex_shader(struct cso_context *cso,
152 struct pipe_context *ctx,
153 bool window_space)
154 {
155 static const enum tgsi_semantic vs_attribs[] = {
156 TGSI_SEMANTIC_POSITION,
157 TGSI_SEMANTIC_GENERIC
158 };
159 static const uint vs_indices[] = {0, 0};
160 void *vs;
161
162 vs = util_make_vertex_passthrough_shader(ctx, 2, vs_attribs, vs_indices,
163 window_space);
164 cso_set_vertex_shader_handle(cso, vs);
165 return vs;
166 }
167
168 static void
169 util_set_common_states_and_clear(struct cso_context *cso, struct pipe_context *ctx,
170 struct pipe_resource *cb)
171 {
172 static const float clear_color[] = {0.1, 0.1, 0.1, 0.1};
173
174 util_set_framebuffer_cb0(cso, ctx, cb);
175 util_set_blend_normal(cso);
176 util_set_dsa_disable(cso);
177 util_set_rasterizer_normal(cso);
178 util_set_max_viewport(cso, cb);
179
180 ctx->clear(ctx, PIPE_CLEAR_COLOR0, NULL, (void*)clear_color, 0, 0);
181 }
182
183 static void
184 util_draw_fullscreen_quad(struct cso_context *cso)
185 {
186 static float vertices[] = {
187 -1, -1, 0, 1, 0, 0, 0, 0,
188 -1, 1, 0, 1, 0, 1, 0, 0,
189 1, 1, 0, 1, 1, 1, 0, 0,
190 1, -1, 0, 1, 1, 0, 0, 0
191 };
192 util_set_interleaved_vertex_elements(cso, 2);
193 util_draw_user_vertex_buffer(cso, vertices, PIPE_PRIM_QUADS, 4, 2);
194 }
195
196 static void
197 util_draw_fullscreen_quad_fill(struct cso_context *cso,
198 float r, float g, float b, float a)
199 {
200 float vertices[] = {
201 -1, -1, 0, 1, r, g, b, a,
202 -1, 1, 0, 1, r, g, b, a,
203 1, 1, 0, 1, r, g, b, a,
204 1, -1, 0, 1, r, g, b, a,
205 };
206 util_set_interleaved_vertex_elements(cso, 2);
207 util_draw_user_vertex_buffer(cso, vertices, PIPE_PRIM_QUADS, 4, 2);
208 }
209
210 /**
211 * Probe and test if the rectangle contains the expected color.
212 *
213 * If "num_expected_colors" > 1, at least one expected color must match
214 * the probed color. "expected" should be an array of 4*num_expected_colors
215 * floats.
216 */
217 static bool
218 util_probe_rect_rgba_multi(struct pipe_context *ctx, struct pipe_resource *tex,
219 unsigned offx, unsigned offy, unsigned w,
220 unsigned h,
221 const float *expected,
222 unsigned num_expected_colors)
223 {
224 struct pipe_transfer *transfer;
225 void *map;
226 float *pixels = malloc(w * h * 4 * sizeof(float));
227 unsigned x,y,e,c;
228 bool pass = true;
229
230 map = pipe_transfer_map(ctx, tex, 0, 0, PIPE_TRANSFER_READ,
231 offx, offy, w, h, &transfer);
232 pipe_get_tile_rgba(transfer, map, 0, 0, w, h, tex->format, pixels);
233 pipe_transfer_unmap(ctx, transfer);
234
235 for (e = 0; e < num_expected_colors; e++) {
236 for (y = 0; y < h; y++) {
237 for (x = 0; x < w; x++) {
238 float *probe = &pixels[(y*w + x)*4];
239
240 for (c = 0; c < 4; c++) {
241 if (fabs(probe[c] - expected[e*4+c]) >= TOLERANCE) {
242 if (e < num_expected_colors-1)
243 goto next_color; /* test the next expected color */
244
245 printf("Probe color at (%i,%i), ", offx+x, offy+y);
246 printf("Expected: %.3f, %.3f, %.3f, %.3f, ",
247 expected[e*4], expected[e*4+1],
248 expected[e*4+2], expected[e*4+3]);
249 printf("Got: %.3f, %.3f, %.3f, %.3f\n",
250 probe[0], probe[1], probe[2], probe[3]);
251 pass = false;
252 goto done;
253 }
254 }
255 }
256 }
257 break; /* this color was successful */
258
259 next_color:;
260 }
261 done:
262
263 free(pixels);
264 return pass;
265 }
266
267 static bool
268 util_probe_rect_rgba(struct pipe_context *ctx, struct pipe_resource *tex,
269 unsigned offx, unsigned offy, unsigned w, unsigned h,
270 const float *expected)
271 {
272 return util_probe_rect_rgba_multi(ctx, tex, offx, offy, w, h, expected, 1);
273 }
274
275 enum {
276 SKIP = -1,
277 FAIL = 0, /* also "false" */
278 PASS = 1 /* also "true" */
279 };
280
281 static void
282 util_report_result_helper(int status, const char *name, ...)
283 {
284 char buf[256];
285 va_list ap;
286
287 va_start(ap, name);
288 vsnprintf(buf, sizeof(buf), name, ap);
289 va_end(ap);
290
291 printf("Test(%s) = %s\n", buf,
292 status == SKIP ? "skip" :
293 status == PASS ? "pass" : "fail");
294 }
295
296 #define util_report_result(status) util_report_result_helper(status, __func__)
297
298 /**
299 * Test TGSI_PROPERTY_VS_WINDOW_SPACE_POSITION.
300 *
301 * The viewport state is set as usual, but it should have no effect.
302 * Clipping should also be disabled.
303 *
304 * POSITION.xyz should already be multiplied by 1/w and POSITION.w should
305 * contain 1/w. By setting w=0, we can test that POSITION.xyz isn't
306 * multiplied by 1/w (otherwise nothing would be rendered).
307 *
308 * TODO: Whether the value of POSITION.w is correctly interpreted as 1/w
309 * during perspective interpolation is not tested.
310 */
311 static void
312 tgsi_vs_window_space_position(struct pipe_context *ctx)
313 {
314 struct cso_context *cso;
315 struct pipe_resource *cb;
316 void *fs, *vs;
317 bool pass = true;
318 static const float red[] = {1, 0, 0, 1};
319
320 if (!ctx->screen->get_param(ctx->screen,
321 PIPE_CAP_TGSI_VS_WINDOW_SPACE_POSITION)) {
322 util_report_result(SKIP);
323 return;
324 }
325
326 cso = cso_create_context(ctx, 0);
327 cb = util_create_texture2d(ctx->screen, 256, 256,
328 PIPE_FORMAT_R8G8B8A8_UNORM, 0);
329 util_set_common_states_and_clear(cso, ctx, cb);
330
331 /* Fragment shader. */
332 fs = util_make_fragment_passthrough_shader(ctx, TGSI_SEMANTIC_GENERIC,
333 TGSI_INTERPOLATE_LINEAR, TRUE);
334 cso_set_fragment_shader_handle(cso, fs);
335
336 /* Vertex shader. */
337 vs = util_set_passthrough_vertex_shader(cso, ctx, true);
338
339 /* Draw. */
340 {
341 static float vertices[] = {
342 0, 0, 0, 0, 1, 0, 0, 1,
343 0, 256, 0, 0, 1, 0, 0, 1,
344 256, 256, 0, 0, 1, 0, 0, 1,
345 256, 0, 0, 0, 1, 0, 0, 1,
346 };
347 util_set_interleaved_vertex_elements(cso, 2);
348 util_draw_user_vertex_buffer(cso, vertices, PIPE_PRIM_QUADS, 4, 2);
349 }
350
351 /* Probe pixels. */
352 pass = pass && util_probe_rect_rgba(ctx, cb, 0, 0,
353 cb->width0, cb->height0, red);
354
355 /* Cleanup. */
356 cso_destroy_context(cso);
357 ctx->delete_vs_state(ctx, vs);
358 ctx->delete_fs_state(ctx, fs);
359 pipe_resource_reference(&cb, NULL);
360
361 util_report_result(pass);
362 }
363
364 static void
365 null_sampler_view(struct pipe_context *ctx, unsigned tgsi_tex_target)
366 {
367 struct cso_context *cso;
368 struct pipe_resource *cb;
369 void *fs, *vs;
370 bool pass = true;
371 /* 2 expected colors: */
372 static const float expected_tex[] = {0, 0, 0, 1,
373 0, 0, 0, 0};
374 static const float expected_buf[] = {0, 0, 0, 0};
375 const float *expected = tgsi_tex_target == TGSI_TEXTURE_BUFFER ?
376 expected_buf : expected_tex;
377 unsigned num_expected = tgsi_tex_target == TGSI_TEXTURE_BUFFER ? 1 : 2;
378
379 if (tgsi_tex_target == TGSI_TEXTURE_BUFFER &&
380 !ctx->screen->get_param(ctx->screen, PIPE_CAP_TEXTURE_BUFFER_OBJECTS)) {
381 util_report_result_helper(SKIP, "%s: %s", __func__,
382 tgsi_texture_names[tgsi_tex_target]);
383 return;
384 }
385
386 cso = cso_create_context(ctx, 0);
387 cb = util_create_texture2d(ctx->screen, 256, 256,
388 PIPE_FORMAT_R8G8B8A8_UNORM, 0);
389 util_set_common_states_and_clear(cso, ctx, cb);
390
391 ctx->set_sampler_views(ctx, PIPE_SHADER_FRAGMENT, 0, 1, NULL);
392
393 /* Fragment shader. */
394 fs = util_make_fragment_tex_shader(ctx, tgsi_tex_target,
395 TGSI_INTERPOLATE_LINEAR,
396 TGSI_RETURN_TYPE_FLOAT,
397 TGSI_RETURN_TYPE_FLOAT, false, false);
398 cso_set_fragment_shader_handle(cso, fs);
399
400 /* Vertex shader. */
401 vs = util_set_passthrough_vertex_shader(cso, ctx, false);
402 util_draw_fullscreen_quad(cso);
403
404 /* Probe pixels. */
405 pass = pass && util_probe_rect_rgba_multi(ctx, cb, 0, 0,
406 cb->width0, cb->height0, expected,
407 num_expected);
408
409 /* Cleanup. */
410 cso_destroy_context(cso);
411 ctx->delete_vs_state(ctx, vs);
412 ctx->delete_fs_state(ctx, fs);
413 pipe_resource_reference(&cb, NULL);
414
415 util_report_result_helper(pass, "%s: %s", __func__,
416 tgsi_texture_names[tgsi_tex_target]);
417 }
418
419 void
420 util_test_constant_buffer(struct pipe_context *ctx,
421 struct pipe_resource *constbuf)
422 {
423 struct cso_context *cso;
424 struct pipe_resource *cb;
425 void *fs, *vs;
426 bool pass = true;
427 static const float zero[] = {0, 0, 0, 0};
428
429 cso = cso_create_context(ctx, 0);
430 cb = util_create_texture2d(ctx->screen, 256, 256,
431 PIPE_FORMAT_R8G8B8A8_UNORM, 0);
432 util_set_common_states_and_clear(cso, ctx, cb);
433
434 pipe_set_constant_buffer(ctx, PIPE_SHADER_FRAGMENT, 0, constbuf);
435
436 /* Fragment shader. */
437 {
438 static const char *text = /* I don't like ureg... */
439 "FRAG\n"
440 "DCL CONST[0][0]\n"
441 "DCL OUT[0], COLOR\n"
442
443 "MOV OUT[0], CONST[0][0]\n"
444 "END\n";
445 struct tgsi_token tokens[1000];
446 struct pipe_shader_state state;
447
448 if (!tgsi_text_translate(text, tokens, ARRAY_SIZE(tokens))) {
449 puts("Can't compile a fragment shader.");
450 util_report_result(FAIL);
451 return;
452 }
453 pipe_shader_state_from_tgsi(&state, tokens);
454 fs = ctx->create_fs_state(ctx, &state);
455 cso_set_fragment_shader_handle(cso, fs);
456 }
457
458 /* Vertex shader. */
459 vs = util_set_passthrough_vertex_shader(cso, ctx, false);
460 util_draw_fullscreen_quad(cso);
461
462 /* Probe pixels. */
463 pass = pass && util_probe_rect_rgba(ctx, cb, 0, 0, cb->width0,
464 cb->height0, zero);
465
466 /* Cleanup. */
467 cso_destroy_context(cso);
468 ctx->delete_vs_state(ctx, vs);
469 ctx->delete_fs_state(ctx, fs);
470 pipe_resource_reference(&cb, NULL);
471
472 util_report_result(pass);
473 }
474
475 static void
476 null_fragment_shader(struct pipe_context *ctx)
477 {
478 struct cso_context *cso;
479 struct pipe_resource *cb;
480 void *vs;
481 struct pipe_rasterizer_state rs = {0};
482 struct pipe_query *query;
483 union pipe_query_result qresult;
484
485 cso = cso_create_context(ctx, 0);
486 cb = util_create_texture2d(ctx->screen, 256, 256,
487 PIPE_FORMAT_R8G8B8A8_UNORM, 0);
488 util_set_common_states_and_clear(cso, ctx, cb);
489
490 /* No rasterization. */
491 rs.rasterizer_discard = 1;
492 cso_set_rasterizer(cso, &rs);
493
494 vs = util_set_passthrough_vertex_shader(cso, ctx, false);
495
496 query = ctx->create_query(ctx, PIPE_QUERY_PRIMITIVES_GENERATED, 0);
497 ctx->begin_query(ctx, query);
498 util_draw_fullscreen_quad(cso);
499 ctx->end_query(ctx, query);
500 ctx->get_query_result(ctx, query, true, &qresult);
501
502 /* Cleanup. */
503 cso_destroy_context(cso);
504 ctx->delete_vs_state(ctx, vs);
505 ctx->destroy_query(ctx, query);
506 pipe_resource_reference(&cb, NULL);
507
508 /* Check PRIMITIVES_GENERATED. */
509 util_report_result(qresult.u64 == 2);
510 }
511
512 #if defined(PIPE_OS_LINUX) && defined(HAVE_LIBDRM)
513 #include <libsync.h>
514 #else
515 #define sync_merge(str, fd1, fd2) (-1)
516 #define sync_wait(fd, timeout) (-1)
517 #endif
518
519 static void
520 test_sync_file_fences(struct pipe_context *ctx)
521 {
522 struct pipe_screen *screen = ctx->screen;
523 bool pass = true;
524 enum pipe_fd_type fd_type = PIPE_FD_TYPE_NATIVE_SYNC;
525
526 if (!screen->get_param(screen, PIPE_CAP_NATIVE_FENCE_FD))
527 return;
528
529 struct cso_context *cso = cso_create_context(ctx, 0);
530 struct pipe_resource *buf =
531 pipe_buffer_create(screen, 0, PIPE_USAGE_DEFAULT, 1024 * 1024);
532 struct pipe_resource *tex =
533 util_create_texture2d(screen, 4096, 1024, PIPE_FORMAT_R8_UNORM, 0);
534 struct pipe_fence_handle *buf_fence = NULL, *tex_fence = NULL;
535
536 /* Run 2 clears, get fencess. */
537 uint32_t value = 0;
538 ctx->clear_buffer(ctx, buf, 0, buf->width0, &value, sizeof(value));
539 ctx->flush(ctx, &buf_fence, PIPE_FLUSH_FENCE_FD);
540
541 struct pipe_box box;
542 u_box_2d(0, 0, tex->width0, tex->height0, &box);
543 ctx->clear_texture(ctx, tex, 0, &box, &value);
544 ctx->flush(ctx, &tex_fence, PIPE_FLUSH_FENCE_FD);
545 pass = pass && buf_fence && tex_fence;
546
547 /* Export fences. */
548 int buf_fd = screen->fence_get_fd(screen, buf_fence);
549 int tex_fd = screen->fence_get_fd(screen, tex_fence);
550 pass = pass && buf_fd >= 0 && tex_fd >= 0;
551
552 /* Merge fences. */
553 int merged_fd = sync_merge("test", buf_fd, tex_fd);
554 pass = pass && merged_fd >= 0;
555
556 /* (Re)import all fences. */
557 struct pipe_fence_handle *re_buf_fence = NULL, *re_tex_fence = NULL;
558 struct pipe_fence_handle *merged_fence = NULL;
559 ctx->create_fence_fd(ctx, &re_buf_fence, buf_fd, fd_type);
560 ctx->create_fence_fd(ctx, &re_tex_fence, tex_fd, fd_type);
561 ctx->create_fence_fd(ctx, &merged_fence, merged_fd, fd_type);
562 pass = pass && re_buf_fence && re_tex_fence && merged_fence;
563
564 /* Run another clear after waiting for everything. */
565 struct pipe_fence_handle *final_fence = NULL;
566 ctx->fence_server_sync(ctx, merged_fence);
567 value = 0xff;
568 ctx->clear_buffer(ctx, buf, 0, buf->width0, &value, sizeof(value));
569 ctx->flush(ctx, &final_fence, PIPE_FLUSH_FENCE_FD);
570 pass = pass && final_fence;
571
572 /* Wait for the last fence. */
573 int final_fd = screen->fence_get_fd(screen, final_fence);
574 pass = pass && final_fd >= 0;
575 pass = pass && sync_wait(final_fd, -1) == 0;
576
577 /* Check that all fences are signalled. */
578 pass = pass && sync_wait(buf_fd, 0) == 0;
579 pass = pass && sync_wait(tex_fd, 0) == 0;
580 pass = pass && sync_wait(merged_fd, 0) == 0;
581
582 pass = pass && screen->fence_finish(screen, NULL, buf_fence, 0);
583 pass = pass && screen->fence_finish(screen, NULL, tex_fence, 0);
584 pass = pass && screen->fence_finish(screen, NULL, re_buf_fence, 0);
585 pass = pass && screen->fence_finish(screen, NULL, re_tex_fence, 0);
586 pass = pass && screen->fence_finish(screen, NULL, merged_fence, 0);
587 pass = pass && screen->fence_finish(screen, NULL, final_fence, 0);
588
589 /* Cleanup. */
590 #ifndef PIPE_OS_WINDOWS
591 if (buf_fd >= 0)
592 close(buf_fd);
593 if (tex_fd >= 0)
594 close(tex_fd);
595 if (merged_fd >= 0)
596 close(merged_fd);
597 if (final_fd >= 0)
598 close(final_fd);
599 #endif
600
601 screen->fence_reference(screen, &buf_fence, NULL);
602 screen->fence_reference(screen, &tex_fence, NULL);
603 screen->fence_reference(screen, &re_buf_fence, NULL);
604 screen->fence_reference(screen, &re_tex_fence, NULL);
605 screen->fence_reference(screen, &merged_fence, NULL);
606 screen->fence_reference(screen, &final_fence, NULL);
607
608 cso_destroy_context(cso);
609 pipe_resource_reference(&buf, NULL);
610 pipe_resource_reference(&tex, NULL);
611
612 util_report_result(pass);
613 }
614
615 static void
616 test_texture_barrier(struct pipe_context *ctx, bool use_fbfetch,
617 unsigned num_samples)
618 {
619 struct cso_context *cso;
620 struct pipe_resource *cb;
621 struct pipe_sampler_view *view = NULL;
622 char name[256];
623 const char *text;
624
625 assert(num_samples >= 1 && num_samples <= 8);
626
627 snprintf(name, sizeof(name), "%s: %s, %u samples", __func__,
628 use_fbfetch ? "FBFETCH" : "sampler", MAX2(num_samples, 1));
629
630 if (!ctx->screen->get_param(ctx->screen, PIPE_CAP_TEXTURE_BARRIER)) {
631 util_report_result_helper(SKIP, name);
632 return;
633 }
634 if (use_fbfetch &&
635 !ctx->screen->get_param(ctx->screen, PIPE_CAP_FBFETCH)) {
636 util_report_result_helper(SKIP, name);
637 return;
638 }
639
640 cso = cso_create_context(ctx, 0);
641 cb = util_create_texture2d(ctx->screen, 256, 256,
642 PIPE_FORMAT_R8G8B8A8_UNORM, num_samples);
643 util_set_common_states_and_clear(cso, ctx, cb);
644
645 /* Clear each sample to a different value. */
646 if (num_samples > 1) {
647 void *fs =
648 util_make_fragment_passthrough_shader(ctx, TGSI_SEMANTIC_GENERIC,
649 TGSI_INTERPOLATE_LINEAR, TRUE);
650 cso_set_fragment_shader_handle(cso, fs);
651
652 /* Vertex shader. */
653 void *vs = util_set_passthrough_vertex_shader(cso, ctx, false);
654
655 for (unsigned i = 0; i < num_samples / 2; i++) {
656 float value;
657
658 /* 2 consecutive samples should have the same color to test MSAA
659 * compression properly.
660 */
661 if (num_samples == 2) {
662 value = 0.1;
663 } else {
664 /* The average value must be 0.1 */
665 static const float values[] = {
666 0.0, 0.2, 0.05, 0.15
667 };
668 value = values[i];
669 }
670
671 ctx->set_sample_mask(ctx, 0x3 << (i * 2));
672 util_draw_fullscreen_quad_fill(cso, value, value, value, value);
673 }
674 ctx->set_sample_mask(ctx, ~0);
675
676 cso_set_vertex_shader_handle(cso, NULL);
677 cso_set_fragment_shader_handle(cso, NULL);
678 ctx->delete_vs_state(ctx, vs);
679 ctx->delete_fs_state(ctx, fs);
680 }
681
682 if (use_fbfetch) {
683 /* Fragment shader. */
684 text = "FRAG\n"
685 "DCL OUT[0], COLOR[0]\n"
686 "DCL TEMP[0]\n"
687 "IMM[0] FLT32 { 0.1, 0.2, 0.3, 0.4}\n"
688
689 "FBFETCH TEMP[0], OUT[0]\n"
690 "ADD OUT[0], TEMP[0], IMM[0]\n"
691 "END\n";
692 } else {
693 struct pipe_sampler_view templ = {{0}};
694 templ.format = cb->format;
695 templ.target = cb->target;
696 templ.swizzle_r = PIPE_SWIZZLE_X;
697 templ.swizzle_g = PIPE_SWIZZLE_Y;
698 templ.swizzle_b = PIPE_SWIZZLE_Z;
699 templ.swizzle_a = PIPE_SWIZZLE_W;
700 view = ctx->create_sampler_view(ctx, cb, &templ);
701 ctx->set_sampler_views(ctx, PIPE_SHADER_FRAGMENT, 0, 1, &view);
702
703 /* Fragment shader. */
704 if (num_samples > 1) {
705 text = "FRAG\n"
706 "DCL SV[0], POSITION\n"
707 "DCL SV[1], SAMPLEID\n"
708 "DCL SAMP[0]\n"
709 "DCL SVIEW[0], 2D_MSAA, FLOAT\n"
710 "DCL OUT[0], COLOR[0]\n"
711 "DCL TEMP[0]\n"
712 "IMM[0] FLT32 { 0.1, 0.2, 0.3, 0.4}\n"
713
714 "F2I TEMP[0].xy, SV[0].xyyy\n"
715 "MOV TEMP[0].w, SV[1].xxxx\n"
716 "TXF TEMP[0], TEMP[0], SAMP[0], 2D_MSAA\n"
717 "ADD OUT[0], TEMP[0], IMM[0]\n"
718 "END\n";
719 } else {
720 text = "FRAG\n"
721 "DCL SV[0], POSITION\n"
722 "DCL SAMP[0]\n"
723 "DCL SVIEW[0], 2D, FLOAT\n"
724 "DCL OUT[0], COLOR[0]\n"
725 "DCL TEMP[0]\n"
726 "IMM[0] FLT32 { 0.1, 0.2, 0.3, 0.4}\n"
727 "IMM[1] INT32 { 0, 0, 0, 0}\n"
728
729 "F2I TEMP[0].xy, SV[0].xyyy\n"
730 "MOV TEMP[0].zw, IMM[1]\n"
731 "TXF TEMP[0], TEMP[0], SAMP[0], 2D\n"
732 "ADD OUT[0], TEMP[0], IMM[0]\n"
733 "END\n";
734 }
735 }
736
737 struct tgsi_token tokens[1000];
738 struct pipe_shader_state state;
739
740 if (!tgsi_text_translate(text, tokens, ARRAY_SIZE(tokens))) {
741 assert(0);
742 util_report_result_helper(FAIL, name);
743 return;
744 }
745 pipe_shader_state_from_tgsi(&state, tokens);
746
747 void *fs = ctx->create_fs_state(ctx, &state);
748 cso_set_fragment_shader_handle(cso, fs);
749
750 /* Vertex shader. */
751 void *vs = util_set_passthrough_vertex_shader(cso, ctx, false);
752
753 if (num_samples > 1 && !use_fbfetch)
754 ctx->set_min_samples(ctx, num_samples);
755
756 for (int i = 0; i < 2; i++) {
757 ctx->texture_barrier(ctx,
758 use_fbfetch ? PIPE_TEXTURE_BARRIER_FRAMEBUFFER :
759 PIPE_TEXTURE_BARRIER_SAMPLER);
760 util_draw_fullscreen_quad(cso);
761 }
762 if (num_samples > 1 && !use_fbfetch)
763 ctx->set_min_samples(ctx, 1);
764
765 /* Probe pixels.
766 *
767 * For single sample:
768 * result = 0.1 (clear) + (0.1, 0.2, 0.3, 0.4) * 2 = (0.3, 0.5, 0.7, 0.9)
769 *
770 * For MSAA 4x:
771 * sample0 = 0.0 (clear) + (0.1, 0.2, 0.3, 0.4) * 2 = (0.2, 0.4, 0.6, 0.8)
772 * sample1 = sample0
773 * sample2 = 0.2 (clear) + (0.1, 0.2, 0.3, 0.4) * 2 = (0.4, 0.6, 0.8, 1.0)
774 * sample3 = sample2
775 * resolved = sum(sample[0:3]) / 4 = (0.3, 0.5, 0.7, 0.9)
776 */
777 static const float expected[] = {0.3, 0.5, 0.7, 0.9};
778 bool pass = util_probe_rect_rgba(ctx, cb, 0, 0,
779 cb->width0, cb->height0, expected);
780
781 /* Cleanup. */
782 cso_destroy_context(cso);
783 ctx->delete_vs_state(ctx, vs);
784 ctx->delete_fs_state(ctx, fs);
785 pipe_sampler_view_reference(&view, NULL);
786 pipe_resource_reference(&cb, NULL);
787
788 util_report_result_helper(pass, name);
789 }
790
791 static void
792 test_compute_clear_image(struct pipe_context *ctx)
793 {
794 struct pipe_resource *cb;
795 const char *text;
796
797 cb = util_create_texture2d(ctx->screen, 256, 256,
798 PIPE_FORMAT_R8G8B8A8_UNORM, 1);
799
800 /* Compute shader. */
801 text = "COMP\n"
802 "PROPERTY CS_FIXED_BLOCK_WIDTH 8\n"
803 "PROPERTY CS_FIXED_BLOCK_HEIGHT 8\n"
804 "PROPERTY CS_FIXED_BLOCK_DEPTH 1\n"
805 "DCL SV[0], THREAD_ID\n"
806 "DCL SV[1], BLOCK_ID\n"
807 "DCL IMAGE[0], 2D, PIPE_FORMAT_R8G8B8A8_UNORM, WR\n"
808 "DCL TEMP[0]\n"
809 "IMM[0] UINT32 { 8, 8, 0, 0}\n"
810 "IMM[1] FLT32 { 1, 0, 0, 0}\n"
811
812 /* TEMP[0].xy = SV[1] * IMM[0] + SV[0]; */
813 "UMAD TEMP[0].xy, SV[1], IMM[0], SV[0]\n"
814 "STORE IMAGE[0], TEMP[0], IMM[1], 2D, PIPE_FORMAT_R8G8B8A8_UNORM\n"
815 "END\n";
816
817 struct tgsi_token tokens[1000];
818 if (!tgsi_text_translate(text, tokens, ARRAY_SIZE(tokens))) {
819 assert(0);
820 util_report_result(FAIL);
821 return;
822 }
823
824 struct pipe_compute_state state = {0};
825 state.ir_type = PIPE_SHADER_IR_TGSI;
826 state.prog = tokens;
827
828 void *compute_shader = ctx->create_compute_state(ctx, &state);
829 ctx->bind_compute_state(ctx, compute_shader);
830
831 /* Bind the image. */
832 struct pipe_image_view image = {0};
833 image.resource = cb;
834 image.shader_access = image.access = PIPE_IMAGE_ACCESS_READ_WRITE;
835 image.format = cb->format;
836
837 ctx->set_shader_images(ctx, PIPE_SHADER_COMPUTE, 0, 1, &image);
838
839 /* Dispatch compute. */
840 struct pipe_grid_info info = {0};
841 info.block[0] = 8;
842 info.block[1] = 8;
843 info.block[2] = 1;
844 info.grid[0] = cb->width0 / 8;
845 info.grid[1] = cb->height0 / 8;
846 info.grid[2] = 1;
847
848 ctx->launch_grid(ctx, &info);
849
850 /* Check pixels. */
851 static const float expected[] = {1.0, 0.0, 0.0, 0.0};
852 bool pass = util_probe_rect_rgba(ctx, cb, 0, 0,
853 cb->width0, cb->height0, expected);
854
855 /* Cleanup. */
856 ctx->delete_compute_state(ctx, compute_shader);
857 pipe_resource_reference(&cb, NULL);
858
859 util_report_result(pass);
860 }
861
862 #define NV12_WIDTH 2560
863 #define NV12_HEIGHT 1440
864
865 static bool
866 nv12_validate_resource_fields(struct pipe_resource *tex)
867 {
868 return tex->format == util_format_get_plane_format(PIPE_FORMAT_NV12, 0) &&
869 tex->width0 == NV12_WIDTH &&
870 tex->height0 == NV12_HEIGHT &&
871 tex->last_level == 0 &&
872 tex->usage == PIPE_USAGE_DEFAULT &&
873 tex->next &&
874 tex->next->format == util_format_get_plane_format(PIPE_FORMAT_NV12, 1) &&
875 tex->next->width0 == tex->width0 / 2 &&
876 tex->next->height0 == tex->height0 / 2 &&
877 tex->next->usage == tex->usage;
878 }
879
880 /* This test enforces the behavior of NV12 allocation and exports. */
881 static void
882 test_nv12(struct pipe_screen *screen)
883 {
884 struct pipe_resource *tex = util_create_texture2d(screen, NV12_WIDTH, NV12_HEIGHT,
885 PIPE_FORMAT_NV12, 1);
886
887 if (!tex) {
888 printf("resource_create failed\n");
889 util_report_result(false);
890 return;
891 }
892
893 if (!nv12_validate_resource_fields(tex)) {
894 printf("incorrect pipe_resource fields\n");
895 util_report_result(false);
896 return;
897 }
898
899 /* resource_get_param */
900 if (screen->resource_get_param) {
901 struct {
902 uint64_t handle, dmabuf, offset, stride, planes;
903 } handle[3];
904
905 /* Export */
906 for (unsigned i = 0; i < 3; i++) {
907 struct pipe_resource *res = i == 2 ? tex->next : tex;
908 unsigned plane = i == 2 ? 0 : i;
909
910 if (!screen->resource_get_param(screen, NULL, res, plane, 0,
911 PIPE_RESOURCE_PARAM_HANDLE_TYPE_KMS,
912 0, &handle[i].handle)) {
913 printf("resource_get_param failed\n");
914 util_report_result(false);
915 goto cleanup;
916 }
917
918 if (!screen->resource_get_param(screen, NULL, res, plane, 0,
919 PIPE_RESOURCE_PARAM_HANDLE_TYPE_FD,
920 0, &handle[i].dmabuf)) {
921 printf("resource_get_param failed\n");
922 util_report_result(false);
923 goto cleanup;
924 }
925
926 if (!screen->resource_get_param(screen, NULL, res, plane, 0,
927 PIPE_RESOURCE_PARAM_OFFSET,
928 0, &handle[i].offset)) {
929 printf("resource_get_param failed\n");
930 util_report_result(false);
931 goto cleanup;
932 }
933
934 if (!screen->resource_get_param(screen, NULL, res, plane, 0,
935 PIPE_RESOURCE_PARAM_STRIDE,
936 0, &handle[i].stride)) {
937 printf("resource_get_param failed\n");
938 util_report_result(false);
939 goto cleanup;
940 }
941
942 if (!screen->resource_get_param(screen, NULL, res, plane, 0,
943 PIPE_RESOURCE_PARAM_NPLANES,
944 0, &handle[i].planes)) {
945 printf("resource_get_param failed\n");
946 util_report_result(false);
947 goto cleanup;
948 }
949 }
950
951 /* Validate export. */
952 bool get_param_pass = /* Sanity checking */
953 handle[0].handle && handle[1].handle && handle[2].handle &&
954 handle[0].dmabuf && handle[1].dmabuf && handle[2].dmabuf &&
955 handle[0].stride && handle[1].stride && handle[2].stride &&
956 handle[0].planes == 2 &&
957 handle[1].planes == 2 &&
958 handle[2].planes == 2 &&
959 /* Different planes */
960 handle[0].handle == handle[1].handle &&
961 handle[0].offset != handle[1].offset &&
962 /* Same planes. */
963 handle[1].handle == handle[2].handle &&
964 handle[1].stride == handle[2].stride &&
965 handle[1].offset == handle[2].offset;
966
967 if (!get_param_pass) {
968 printf("resource_get_param returned incorrect values\n");
969 util_report_result(false);
970 goto cleanup;
971 }
972 }
973
974 /* resource_get_handle */
975 struct winsys_handle handle[4] = {{0}};
976
977 /* Export */
978 for (unsigned i = 0; i < 4; i++) {
979 handle[i].type = i < 2 ? WINSYS_HANDLE_TYPE_KMS : WINSYS_HANDLE_TYPE_FD;
980 handle[i].plane = i % 2;
981
982 if (!screen->resource_get_handle(screen, NULL, tex, &handle[i], 0)) {
983 printf("resource_get_handle failed\n");
984 util_report_result(false);
985 goto cleanup;
986 }
987 }
988
989 /* Validate export. */
990 bool get_handle_pass = /* Sanity checking */
991 handle[0].handle && handle[1].handle &&
992 handle[0].stride && handle[1].stride &&
993 handle[2].handle && handle[3].handle &&
994 handle[2].stride && handle[3].stride &&
995 /* KMS - different planes */
996 handle[0].handle == handle[1].handle &&
997 handle[0].offset != handle[1].offset &&
998 /* DMABUF - different planes */
999 handle[2].offset != handle[3].offset &&
1000 /* KMS and DMABUF equivalence */
1001 handle[0].offset == handle[2].offset &&
1002 handle[1].offset == handle[3].offset &&
1003 handle[0].stride == handle[2].stride &&
1004 handle[1].stride == handle[3].stride;
1005
1006 if (!get_handle_pass) {
1007 printf("resource_get_handle returned incorrect values\n");
1008 util_report_result(false);
1009 goto cleanup;
1010 }
1011
1012 util_report_result(true);
1013
1014 cleanup:
1015 pipe_resource_reference(&tex, NULL);
1016 }
1017
1018 /**
1019 * Run all tests. This should be run with a clean context after
1020 * context_create.
1021 */
1022 void
1023 util_run_tests(struct pipe_screen *screen)
1024 {
1025 struct pipe_context *ctx = screen->context_create(screen, NULL, 0);
1026
1027 null_fragment_shader(ctx);
1028 tgsi_vs_window_space_position(ctx);
1029 null_sampler_view(ctx, TGSI_TEXTURE_2D);
1030 null_sampler_view(ctx, TGSI_TEXTURE_BUFFER);
1031 util_test_constant_buffer(ctx, NULL);
1032 test_sync_file_fences(ctx);
1033
1034 for (int i = 1; i <= 8; i = i * 2)
1035 test_texture_barrier(ctx, false, i);
1036 for (int i = 1; i <= 8; i = i * 2)
1037 test_texture_barrier(ctx, true, i);
1038 ctx->destroy(ctx);
1039
1040 ctx = screen->context_create(screen, NULL, PIPE_CONTEXT_COMPUTE_ONLY);
1041 test_compute_clear_image(ctx);
1042 ctx->destroy(ctx);
1043
1044 test_nv12(screen);
1045
1046 puts("Done. Exiting..");
1047 exit(0);
1048 }