1 /*
2  * Copyright (C) 2014 Intel Corporation
3  *
4  * Permission is hereby granted, free of charge, to any person obtaining a
5  * copy of this software and associated documentation files (the "Software"),
6  * to deal in the Software without restriction, including without limitation
7  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8  * and/or sell copies of the Software, and to permit persons to whom the
9  * Software is furnished to do so, subject to the following conditions:
10  *
11  * The above copyright notice and this permission notice (including the next
12  * paragraph) shall be included in all copies or substantial portions of the
13  * Software.
14  *
15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
18  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
21  * IN THE SOFTWARE.
22  */
23 
24 /** @file invalid.c
25  *
26  * The ARB_shader_image_load_store extension defines an image access
27  * to be invalid when certain conditions are met, in which case image
28  * stores and atomics are defined to have no effect and image loads
29  * and atomics give zero as result.  This test causes such invalid
30  * accesses and checks that the result is as expected and that no data
31  * is accidentally overwritten.
32  *
33  * The spec describes other conditions that cause an image access to
34  * have undefined results.  In those cases we simply check that the
35  * undefined access didn't lead to program termination.
36  */
37 
38 #include "common.h"
39 
40 /** Window width. */
41 #define W 16
42 
43 /** Window height. */
44 #define H 96
45 
46 /** Total number of pixels in the window and image. */
47 #define N (W * H)
48 
49 PIGLIT_GL_TEST_CONFIG_BEGIN
50 
51 config.supports_gl_core_version = 32;
52 
53 config.window_width = W;
54 config.window_height = H;
55 config.window_visual = PIGLIT_GL_VISUAL_DOUBLE | PIGLIT_GL_VISUAL_RGBA;
56 config.khr_no_error_support = PIGLIT_NO_ERRORS;
57 
58 PIGLIT_GL_TEST_CONFIG_END
59 
60 struct image_op_info {
61         /** Image built-in name. */
62         const char *name;
63 
64         /** Allowed image formats. */
65         const struct image_format_info *formats;
66 
67         /** GLSL statement that invokes this image built-in. */
68         const char *hunk;
69 };
70 
71 static const struct image_op_info image_ops[] = {
72         {
73                 "imageLoad", image_formats_load_store,
74                 "GRID_T op(ivec2 idx, GRID_T x) {\n"
75                 "        return imageLoad(imgs[u], off + IMAGE_ADDR(idx));\n"
76                 "}\n"
77         },
78         {
79                 "imageStore", image_formats_load_store,
80                 "GRID_T op(ivec2 idx, GRID_T x) {\n"
81                 "        imageStore(imgs[u], off + IMAGE_ADDR(idx), DATA_T(33));\n"
82                 "        return GRID_T(0, 0, 0, SCALE.w == 0 ? 1 : 0);"
83                 "}\n"
84         },
85         {
86                 "imageAtomicAdd", image_formats_atomic,
87                 "GRID_T op(ivec2 idx, GRID_T x) {\n"
88                 "        return GRID_T(imageAtomicAdd(imgs[u],"
89                 "                                    off + IMAGE_ADDR(idx),"
90                 "                                    BASE_T(33)),"
91                 "                      0, 0, 1);\n"
92                 "}\n"
93         },
94         {
95                 "imageAtomicMin", image_formats_atomic,
96                 "GRID_T op(ivec2 idx, GRID_T x) {\n"
97                 "        return GRID_T(imageAtomicMin(imgs[u],"
98                 "                                     off + IMAGE_ADDR(idx),"
99                 "                                     BASE_T(33)),"
100                 "                      0, 0, 1);\n"
101                 "}\n"
102         },
103         {
104                 "imageAtomicMax", image_formats_atomic,
105                 "GRID_T op(ivec2 idx, GRID_T x) {\n"
106                 "        return GRID_T(imageAtomicMax(imgs[u],"
107                 "                                     off + IMAGE_ADDR(idx),"
108                 "                                     BASE_T(33)),"
109                 "                      0, 0, 1);\n"
110                 "}\n"
111         },
112         {
113                 "imageAtomicAnd", image_formats_atomic,
114                 "GRID_T op(ivec2 idx, GRID_T x) {\n"
115                 "        return GRID_T(imageAtomicAnd(imgs[u],"
116                 "                                     off + IMAGE_ADDR(idx),"
117                 "                                     BASE_T(33)),"
118                 "                      0, 0, 1);\n"
119                 "}\n"
120         },
121         {
122                 "imageAtomicOr", image_formats_atomic,
123                 "GRID_T op(ivec2 idx, GRID_T x) {\n"
124                 "        return GRID_T(imageAtomicOr(imgs[u],"
125                 "                                    off + IMAGE_ADDR(idx),"
126                 "                                    BASE_T(33)),"
127                 "                      0, 0, 1);\n"
128                 "}\n"
129         },
130         {
131                 "imageAtomicXor", image_formats_atomic,
132                 "GRID_T op(ivec2 idx, GRID_T x) {\n"
133                 "        return GRID_T(imageAtomicXor(imgs[u],"
134                 "                                     off + IMAGE_ADDR(idx),"
135                 "                                     BASE_T(33)),"
136                 "                      0, 0, 1);\n"
137                 "}\n"
138         },
139         {
140                 "imageAtomicExchange", image_formats_atomic,
141                 "GRID_T op(ivec2 idx, GRID_T x) {\n"
142                 "        return GRID_T(imageAtomicExchange(imgs[u],"
143                 "                                          off + IMAGE_ADDR(idx),"
144                 "                                          BASE_T(33)),"
145                 "                      0, 0, 1);\n"
146                 "}\n"
147         },
148         {
149                 "imageAtomicCompSwap", image_formats_atomic,
150                 "GRID_T op(ivec2 idx, GRID_T x) {\n"
151                 "        return GRID_T(imageAtomicCompSwap(imgs[u],"
152                 "                                          off + IMAGE_ADDR(idx),"
153                 "                                          BASE_T(0), BASE_T(33)),"
154                 "                      0, 0, 1);\n"
155                 "}\n"
156         },
157         { 0 }
158 };
159 
160 static bool
init_image(const struct image_info img,GLuint prog)161 init_image(const struct image_info img, GLuint prog)
162 {
163         uint32_t pixels[4 * N];
164 
165         return init_pixels(img, pixels, 1, 1, 1, 1) &&
166                 upload_image(img, 0, pixels) &&
167                 set_uniform_int(prog, "imgs[0]", 0);
168 }
169 
170 static bool
init_level(const struct image_info img,unsigned level,GLenum format,unsigned w,unsigned h)171 init_level(const struct image_info img, unsigned level,
172            GLenum format, unsigned w, unsigned h)
173 {
174         uint32_t pixels[4 * N];
175 
176         init_pixels(img, pixels, 1, 1, 1, 1);
177         glBindTexture(GL_TEXTURE_2D, get_texture(0));
178         glTexImage2D(GL_TEXTURE_2D, level, format,
179                      w, h, 0, img.format->pixel_format,
180                      image_base_type(img.format), pixels);
181 
182         return piglit_check_gl_error(GL_NO_ERROR);
183 }
184 
185 static bool
check(const struct grid_info grid,const struct image_info img)186 check(const struct grid_info grid, const struct image_info img)
187 {
188         uint32_t pixels_fb[4 * N], pixels_img[4 * N];
189 
190         if (!download_result(grid, pixels_fb) ||
191             !download_image(img, 0, pixels_img))
192                 return false;
193 
194         /* Check that the built-in return value is zero (nonexisting texel). */
195         if (!check_pixels(image_info_for_grid(grid), pixels_fb, 0, 0, 0,
196                           (image_num_components(img.format) < 4 ? 1 : 0))) {
197                 printf("  Source: framebuffer\n");
198                 return false;
199         }
200 
201         /* Check that the image wasn't modified. */
202         if (!check_pixels(img, pixels_img, 1, 1, 1, 1)) {
203                 printf("  Source: image\n");
204                 return false;
205         }
206 
207         return true;
208 }
209 
210 static bool
invalidate_unbound(const struct image_info img,GLuint prog)211 invalidate_unbound(const struct image_info img, GLuint prog)
212 {
213         glBindImageTexture(0, 0, 0, GL_FALSE, 0, GL_READ_ONLY,
214                            img.format->format);
215 
216         return piglit_check_gl_error(GL_NO_ERROR);
217 }
218 
219 static bool
invalidate_incomplete(const struct image_info img,GLuint prog)220 invalidate_incomplete(const struct image_info img, GLuint prog)
221 {
222         /* Bind a mipmap level with incorrect dimensions so the
223          * texture becomes incomplete. */
224         bool ret = init_level(img, 1, img.format->format, W, H);
225 
226         glBindImageTexture(0, get_texture(0), 1, GL_TRUE, 0, GL_READ_WRITE,
227                            img.format->format);
228 
229         return ret && piglit_check_gl_error(GL_NO_ERROR);
230 }
231 
232 static bool
invalidate_level_bounds(const struct image_info img,GLuint prog)233 invalidate_level_bounds(const struct image_info img, GLuint prog)
234 {
235         const int level = 1;
236         const struct image_extent size = image_level_size(img, level);
237         /* Create a second mipmap level */
238         bool ret = init_level(img, level, img.format->format, size.x, size.y);
239 
240         /* and set it as base level, */
241         glTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, &level);
242 
243         /* but keep the the first level bound. */
244         glBindImageTexture(0, get_texture(0), 0, GL_TRUE, 0, GL_READ_WRITE,
245                            img.format->format);
246 
247         return ret && piglit_check_gl_error(GL_NO_ERROR);
248 }
249 
250 static bool
invalidate_invalid_format(const struct image_info img,GLuint prog)251 invalidate_invalid_format(const struct image_info img, GLuint prog)
252 {
253         const GLenum base_format = image_base_internal_format(img.format);
254         /* Pick an invalid texture format with a compatible base
255          * type. */
256         bool ret = init_level(img, 0, (base_format == GL_RGBA32F ?
257                                        GL_RGB5_A1 : GL_RGB8UI), W, H);
258 
259         glBindImageTexture(0, get_texture(0), 0, GL_TRUE, 0,
260                            GL_READ_WRITE, img.format->format);
261 
262         return ret && piglit_check_gl_error(GL_NO_ERROR);
263 }
264 
265 static bool
invalidate_incompatible_format(const struct image_info img,GLuint prog)266 invalidate_incompatible_format(const struct image_info img, GLuint prog)
267 {
268         GLenum base_format = image_base_internal_format(img.format);
269         /* Pick an incompatible texture format with a compatible base
270          * type. */
271         glBindImageTexture(0, get_texture(0), 0, GL_TRUE, 0,
272                            GL_READ_WRITE, (base_format == GL_RGBA32F ?
273                                            GL_RGBA8 : GL_RG32UI));
274 
275         return piglit_check_gl_error(GL_NO_ERROR);
276 }
277 
278 static bool
invalidate_layer_bounds(const struct image_info img,GLuint prog)279 invalidate_layer_bounds(const struct image_info img, GLuint prog)
280 {
281         glBindImageTexture(0, get_texture(0), 0, GL_FALSE, N,
282                            GL_READ_WRITE, img.format->format);
283 
284         return piglit_check_gl_error(GL_NO_ERROR);
285 }
286 
287 static bool
invalidate_address_bounds(const struct image_info img,GLuint prog)288 invalidate_address_bounds(const struct image_info img, GLuint prog)
289 {
290         return set_uniform_int(prog, "off", N);
291 }
292 
293 static bool
invalidate_index_bounds(const struct image_info img,GLuint prog)294 invalidate_index_bounds(const struct image_info img, GLuint prog)
295 {
296         return set_uniform_int(prog, "u", 0xdeadcafe);
297 }
298 
299 static bool
invalidate_nop(const struct image_info img,GLuint prog)300 invalidate_nop(const struct image_info img, GLuint prog)
301 {
302         return true;
303 }
304 
305 static bool
run_test(const struct image_op_info * op,const struct image_info real_img,const struct image_info prog_img,bool (* invalidate)(const struct image_info,GLuint),bool control_test)306 run_test(const struct image_op_info *op,
307          const struct image_info real_img,
308          const struct image_info prog_img,
309          bool (*invalidate)(const struct image_info, GLuint),
310          bool control_test)
311 {
312         const struct grid_info grid =
313                 grid_info(GL_FRAGMENT_SHADER,
314                           image_base_internal_format(real_img.format), W, H);
315         GLuint prog = generate_program(
316                 grid, GL_FRAGMENT_SHADER,
317                 concat(image_hunk(prog_img, ""),
318                        hunk("IMAGE_UNIFORM_T imgs[1];\n"
319                             "uniform int u;\n"
320                             "uniform int off;\n"),
321                        hunk(op->hunk),
322                        NULL));
323         bool ret = prog &&
324                 init_fb(grid) &&
325                 init_image(real_img, prog) &&
326                 invalidate(real_img, prog) &&
327                 draw_grid(grid, prog) &&
328                 (check(grid, real_img) || control_test);
329 
330         glDeleteProgram(prog);
331         return ret;
332 }
333 
334 void
piglit_init(int argc,char ** argv)335 piglit_init(int argc, char **argv)
336 {
337         enum piglit_result status = PIGLIT_PASS;
338         const struct image_op_info *op;
339         const struct image_format_info *format;
340         const struct image_target_info *target;
341 
342         piglit_require_extension("GL_ARB_shader_image_load_store");
343 
344         for (op = image_ops; op->name; ++op) {
345                 const struct image_info def_img = image_info(
346                         GL_TEXTURE_2D, op->formats[0].format, W, H);
347                 const struct image_info def_img_buffer = image_info(
348                         GL_TEXTURE_BUFFER, op->formats[0].format, W, H);
349 
350                 /*
351                  * According to the spec, an access is considered
352                  * invalid in the following cases, in which image
353                  * stores and atomics should have no effect, and image
354                  * loads should return zero:
355                  *
356                  * " * no texture is bound to the selected image unit;
357                  *     [...]"
358                  */
359                 subtest(&status, true,
360                         run_test(op, def_img, def_img,
361                                  invalidate_unbound, false),
362                         "%s/unbound image test", op->name);
363 
364                 /*
365                  * " * the texture bound to the selected image unit is
366                  *     incomplete; [...]"
367                  */
368                 subtest(&status, true,
369                         run_test(op, def_img, def_img,
370                                  invalidate_incomplete, false),
371                         "%s/incomplete image test", op->name);
372 
373                 /*
374                  * " * the texture level bound to the image unit is
375                  *     less than the base level or greater than the
376                  *     maximum level of the texture; [...]"
377                  */
378                 subtest(&status, true,
379                         run_test(op, def_img, def_img,
380                                  invalidate_level_bounds, false),
381                         "%s/level bounds test", op->name);
382 
383                 /*
384                  * " * the internal format of the texture bound to the
385                  *     image unit is not found in Table X.2; [...]"
386                  */
387                 subtest(&status, true,
388                         run_test(op, def_img, def_img,
389                                  invalidate_invalid_format, false),
390                         "%s/invalid format test", op->name);
391 
392                 /*
393                  * " * the internal format of the texture bound to the
394                  *     image unit is incompatible with the specified
395                  *     <format> according to Table X.3; [...]"
396                  */
397                 subtest(&status, true,
398                         run_test(op, def_img, def_img,
399                                  invalidate_incompatible_format, false),
400                         "%s/incompatible format test", op->name);
401 
402                 /* Test for the regression which happened when
403                  * GL_TEXTURE_BUFFER was allowed to have incompatible format.
404                  */
405                 subtest(&status, true,
406                         run_test(op, def_img_buffer, def_img_buffer,
407                                  invalidate_incompatible_format, false),
408                         "%s/incompatible format test/image%s",
409                         op->name, def_img_buffer.target->name);
410 
411                 /*
412                  * " * the texture bound to the image unit has layers,
413                  *     and the selected layer or cube map face doesn't
414                  *     exist; [...]"
415                  */
416                 subtest(&status, true,
417                         run_test(op,
418                                  image_info(GL_TEXTURE_2D_ARRAY,
419                                             op->formats[0].format, W, H),
420                                  def_img, invalidate_layer_bounds, false),
421                         "%s/layer bounds test", op->name);
422 
423                 /*
424                  * " * the selected texel tau_i, tau_i_j, or tau_i_j_k
425                  *     doesn't exist; [...]"
426                  */
427                 for (target = image_targets(); target->name; ++target) {
428                         const struct image_info img = image_info(
429                                 target->target, op->formats[0].format, W, H);
430 
431                         subtest(&status, true,
432                                 run_test(op, img, img,
433                                          invalidate_address_bounds, false),
434                                 "%s/address bounds test/image%s/%s",
435                                 op->name, img.target->name, img.format->name);
436                 }
437 
438                 for (format = &op->formats[1]; format->name; ++format) {
439                         const struct image_info img = image_info(
440                                 GL_TEXTURE_2D, format->format, W, H);
441 
442                         subtest(&status, true,
443                                 run_test(op, img, img,
444                                          invalidate_address_bounds, false),
445                                 "%s/address bounds test/image%s/%s",
446                                 op->name, img.target->name, img.format->name);
447                 }
448 
449                 /*
450                  * The following cases have undefined results, but may
451                  * not lead to program termination:
452                  *
453                  * "If the index used to select an individual [array]
454                  *  element is negative or greater than or equal to
455                  *  the size of the array [...]"
456                  */
457                 subtest(&status, true,
458                         run_test(op, def_img, def_img,
459                                  invalidate_index_bounds, true),
460                         "%s/index bounds test", op->name);
461 
462                 /*
463                  * "the type of image variable used to access the
464                  *  image unit does not match the target of a texture
465                  *  bound to the image unit [...]"
466                  */
467                 subtest(&status, true,
468                         run_test(op, def_img,
469                                  image_info(GL_TEXTURE_3D,
470                                             op->formats[0].format, W, H),
471                                  invalidate_nop, true),
472                         "%s/target mismatch test", op->name);
473 
474                 /*
475                  * "the format layout qualifier for an image variable
476                  *  used for an image load or atomic operation does
477                  *  not match the format of the image unit [...]"
478                  */
479                 subtest(&status, true,
480                         run_test(op,
481                                  image_info(GL_TEXTURE_2D,
482                                             GL_R11F_G11F_B10F, W, H),
483                                  def_img, invalidate_nop, true),
484                         "%s/format mismatch test", op->name);
485 
486         }
487 
488         piglit_report_result(status);
489 }
490 
491 enum piglit_result
piglit_display(void)492 piglit_display(void)
493 {
494         return PIGLIT_FAIL;
495 }
496