+static void
+test_compute_clear_image(struct pipe_context *ctx)
+{
+ struct pipe_resource *cb;
+ const char *text;
+
+ cb = util_create_texture2d(ctx->screen, 256, 256,
+ PIPE_FORMAT_R8G8B8A8_UNORM, 1);
+
+ /* Compute shader. */
+ text = "COMP\n"
+ "PROPERTY CS_FIXED_BLOCK_WIDTH 8\n"
+ "PROPERTY CS_FIXED_BLOCK_HEIGHT 8\n"
+ "PROPERTY CS_FIXED_BLOCK_DEPTH 1\n"
+ "DCL SV[0], THREAD_ID\n"
+ "DCL SV[1], BLOCK_ID\n"
+ "DCL IMAGE[0], 2D, PIPE_FORMAT_R8G8B8A8_UNORM, WR\n"
+ "DCL TEMP[0]\n"
+ "IMM[0] UINT32 { 8, 8, 0, 0}\n"
+ "IMM[1] FLT32 { 1, 0, 0, 0}\n"
+
+ /* TEMP[0].xy = SV[1] * IMM[0] + SV[0]; */
+ "UMAD TEMP[0].xy, SV[1], IMM[0], SV[0]\n"
+ "STORE IMAGE[0], TEMP[0], IMM[1], 2D, PIPE_FORMAT_R8G8B8A8_UNORM\n"
+ "END\n";
+
+ struct tgsi_token tokens[1000];
+ if (!tgsi_text_translate(text, tokens, ARRAY_SIZE(tokens))) {
+ assert(0);
+ util_report_result(FAIL);
+ return;
+ }
+
+ struct pipe_compute_state state = {0};
+ state.ir_type = PIPE_SHADER_IR_TGSI;
+ state.prog = tokens;
+
+ void *compute_shader = ctx->create_compute_state(ctx, &state);
+ ctx->bind_compute_state(ctx, compute_shader);
+
+ /* Bind the image. */
+ struct pipe_image_view image = {0};
+ image.resource = cb;
+ image.shader_access = image.access = PIPE_IMAGE_ACCESS_READ_WRITE;
+ image.format = cb->format;
+
+ ctx->set_shader_images(ctx, PIPE_SHADER_COMPUTE, 0, 1, &image);
+
+ /* Dispatch compute. */
+ struct pipe_grid_info info = {0};
+ info.block[0] = 8;
+ info.block[1] = 8;
+ info.block[2] = 1;
+ info.grid[0] = cb->width0 / 8;
+ info.grid[1] = cb->height0 / 8;
+ info.grid[2] = 1;
+
+ ctx->launch_grid(ctx, &info);
+
+ /* Check pixels. */
+ static const float expected[] = {1.0, 0.0, 0.0, 0.0};
+ bool pass = util_probe_rect_rgba(ctx, cb, 0, 0,
+ cb->width0, cb->height0, expected);
+
+ /* Cleanup. */
+ ctx->delete_compute_state(ctx, compute_shader);
+ pipe_resource_reference(&cb, NULL);
+
+ util_report_result(pass);
+}
+
+#define NV12_WIDTH 2560
+#define NV12_HEIGHT 1440
+
+static bool
+nv12_validate_resource_fields(struct pipe_resource *tex)
+{
+ return tex->format == util_format_get_plane_format(PIPE_FORMAT_NV12, 0) &&
+ tex->width0 == NV12_WIDTH &&
+ tex->height0 == NV12_HEIGHT &&
+ tex->last_level == 0 &&
+ tex->usage == PIPE_USAGE_DEFAULT &&
+ tex->next &&
+ tex->next->format == util_format_get_plane_format(PIPE_FORMAT_NV12, 1) &&
+ tex->next->width0 == tex->width0 / 2 &&
+ tex->next->height0 == tex->height0 / 2 &&
+ tex->next->usage == tex->usage;
+}
+
+/* This test enforces the behavior of NV12 allocation and exports. */
+static void
+test_nv12(struct pipe_screen *screen)
+{
+ struct pipe_resource *tex = util_create_texture2d(screen, NV12_WIDTH, NV12_HEIGHT,
+ PIPE_FORMAT_NV12, 1);
+
+ if (!tex) {
+ printf("resource_create failed\n");
+ util_report_result(false);
+ return;
+ }
+
+ if (!nv12_validate_resource_fields(tex)) {
+ printf("incorrect pipe_resource fields\n");
+ util_report_result(false);
+ return;
+ }
+
+ /* resource_get_param */
+ if (screen->resource_get_param) {
+ struct {
+ uint64_t handle, dmabuf, offset, stride, planes;
+ } handle[3];
+
+ /* Export */
+ for (unsigned i = 0; i < 3; i++) {
+ struct pipe_resource *res = i == 2 ? tex->next : tex;
+ unsigned plane = i == 2 ? 0 : i;
+
+ if (!screen->resource_get_param(screen, NULL, res, plane, 0,
+ PIPE_RESOURCE_PARAM_HANDLE_TYPE_KMS,
+ 0, &handle[i].handle)) {
+ printf("resource_get_param failed\n");
+ util_report_result(false);
+ goto cleanup;
+ }
+
+ if (!screen->resource_get_param(screen, NULL, res, plane, 0,
+ PIPE_RESOURCE_PARAM_HANDLE_TYPE_FD,
+ 0, &handle[i].dmabuf)) {
+ printf("resource_get_param failed\n");
+ util_report_result(false);
+ goto cleanup;
+ }
+
+ if (!screen->resource_get_param(screen, NULL, res, plane, 0,
+ PIPE_RESOURCE_PARAM_OFFSET,
+ 0, &handle[i].offset)) {
+ printf("resource_get_param failed\n");
+ util_report_result(false);
+ goto cleanup;
+ }
+
+ if (!screen->resource_get_param(screen, NULL, res, plane, 0,
+ PIPE_RESOURCE_PARAM_STRIDE,
+ 0, &handle[i].stride)) {
+ printf("resource_get_param failed\n");
+ util_report_result(false);
+ goto cleanup;
+ }
+
+ if (!screen->resource_get_param(screen, NULL, res, plane, 0,
+ PIPE_RESOURCE_PARAM_NPLANES,
+ 0, &handle[i].planes)) {
+ printf("resource_get_param failed\n");
+ util_report_result(false);
+ goto cleanup;
+ }
+ }
+
+ /* Validate export. */
+ bool get_param_pass = /* Sanity checking */
+ handle[0].handle && handle[1].handle && handle[2].handle &&
+ handle[0].dmabuf && handle[1].dmabuf && handle[2].dmabuf &&
+ handle[0].stride && handle[1].stride && handle[2].stride &&
+ handle[0].planes == 2 &&
+ handle[1].planes == 2 &&
+ handle[2].planes == 2 &&
+ /* Different planes */
+ handle[0].handle == handle[1].handle &&
+ handle[0].offset != handle[1].offset &&
+ /* Same planes. */
+ handle[1].handle == handle[2].handle &&
+ handle[1].stride == handle[2].stride &&
+ handle[1].offset == handle[2].offset;
+
+ if (!get_param_pass) {
+ printf("resource_get_param returned incorrect values\n");
+ util_report_result(false);
+ goto cleanup;
+ }
+ }
+
+ /* resource_get_handle */
+ struct winsys_handle handle[4] = {{0}};
+
+ /* Export */
+ for (unsigned i = 0; i < 4; i++) {
+ handle[i].type = i < 2 ? WINSYS_HANDLE_TYPE_KMS : WINSYS_HANDLE_TYPE_FD;
+ handle[i].plane = i % 2;
+
+ if (!screen->resource_get_handle(screen, NULL, tex, &handle[i], 0)) {
+ printf("resource_get_handle failed\n");
+ util_report_result(false);
+ goto cleanup;
+ }
+ }
+
+ /* Validate export. */
+ bool get_handle_pass = /* Sanity checking */
+ handle[0].handle && handle[1].handle &&
+ handle[0].stride && handle[1].stride &&
+ handle[2].handle && handle[3].handle &&
+ handle[2].stride && handle[3].stride &&
+ /* KMS - different planes */
+ handle[0].handle == handle[1].handle &&
+ handle[0].offset != handle[1].offset &&
+ /* DMABUF - different planes */
+ handle[2].offset != handle[3].offset &&
+ /* KMS and DMABUF equivalence */
+ handle[0].offset == handle[2].offset &&
+ handle[1].offset == handle[3].offset &&
+ handle[0].stride == handle[2].stride &&
+ handle[1].stride == handle[3].stride;
+
+ if (!get_handle_pass) {
+ printf("resource_get_handle returned incorrect values\n");
+ util_report_result(false);
+ goto cleanup;
+ }
+
+ util_report_result(true);
+
+cleanup:
+ pipe_resource_reference(&tex, NULL);
+}
+