/* * Copyright (C) 2014 Intel Corporation * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice (including the next * paragraph) shall be included in all copies or substantial portions of the * Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ /** @file invalid.c * * The ARB_shader_image_load_store extension defines an image access * to be invalid when certain conditions are met, in which case image * stores and atomics are defined to have no effect and image loads * and atomics give zero as result. This test causes such invalid * accesses and checks that the result is as expected and that no data * is accidentally overwritten. * * The spec describes other conditions that cause an image access to * have undefined results. In those cases we simply check that the * undefined access didn't lead to program termination. */ #include "common.h" /** Window width. */ #define W 16 /** Window height. */ #define H 96 /** Total number of pixels in the window and image. */ #define N (W * H) PIGLIT_GL_TEST_CONFIG_BEGIN config.supports_gl_core_version = 32; config.window_width = W; config.window_height = H; config.window_visual = PIGLIT_GL_VISUAL_DOUBLE | PIGLIT_GL_VISUAL_RGBA; config.khr_no_error_support = PIGLIT_NO_ERRORS; PIGLIT_GL_TEST_CONFIG_END struct image_op_info { /** Image built-in name. */ const char *name; /** Allowed image formats. */ const struct image_format_info *formats; /** GLSL statement that invokes this image built-in. */ const char *hunk; }; static const struct image_op_info image_ops[] = { { "imageLoad", image_formats_load_store, "GRID_T op(ivec2 idx, GRID_T x) {\n" " return imageLoad(imgs[u], off + IMAGE_ADDR(idx));\n" "}\n" }, { "imageStore", image_formats_load_store, "GRID_T op(ivec2 idx, GRID_T x) {\n" " imageStore(imgs[u], off + IMAGE_ADDR(idx), DATA_T(33));\n" " return GRID_T(0, 0, 0, SCALE.w == 0 ? 1 : 0);" "}\n" }, { "imageAtomicAdd", image_formats_atomic, "GRID_T op(ivec2 idx, GRID_T x) {\n" " return GRID_T(imageAtomicAdd(imgs[u]," " off + IMAGE_ADDR(idx)," " BASE_T(33))," " 0, 0, 1);\n" "}\n" }, { "imageAtomicMin", image_formats_atomic, "GRID_T op(ivec2 idx, GRID_T x) {\n" " return GRID_T(imageAtomicMin(imgs[u]," " off + IMAGE_ADDR(idx)," " BASE_T(33))," " 0, 0, 1);\n" "}\n" }, { "imageAtomicMax", image_formats_atomic, "GRID_T op(ivec2 idx, GRID_T x) {\n" " return GRID_T(imageAtomicMax(imgs[u]," " off + IMAGE_ADDR(idx)," " BASE_T(33))," " 0, 0, 1);\n" "}\n" }, { "imageAtomicAnd", image_formats_atomic, "GRID_T op(ivec2 idx, GRID_T x) {\n" " return GRID_T(imageAtomicAnd(imgs[u]," " off + IMAGE_ADDR(idx)," " BASE_T(33))," " 0, 0, 1);\n" "}\n" }, { "imageAtomicOr", image_formats_atomic, "GRID_T op(ivec2 idx, GRID_T x) {\n" " return GRID_T(imageAtomicOr(imgs[u]," " off + IMAGE_ADDR(idx)," " BASE_T(33))," " 0, 0, 1);\n" "}\n" }, { "imageAtomicXor", image_formats_atomic, "GRID_T op(ivec2 idx, GRID_T x) {\n" " return GRID_T(imageAtomicXor(imgs[u]," " off + IMAGE_ADDR(idx)," " BASE_T(33))," " 0, 0, 1);\n" "}\n" }, { "imageAtomicExchange", image_formats_atomic, "GRID_T op(ivec2 idx, GRID_T x) {\n" " return GRID_T(imageAtomicExchange(imgs[u]," " off + IMAGE_ADDR(idx)," " BASE_T(33))," " 0, 0, 1);\n" "}\n" }, { "imageAtomicCompSwap", image_formats_atomic, "GRID_T op(ivec2 idx, GRID_T x) {\n" " return GRID_T(imageAtomicCompSwap(imgs[u]," " off + IMAGE_ADDR(idx)," " BASE_T(0), BASE_T(33))," " 0, 0, 1);\n" "}\n" }, { 0 } }; static bool init_image(const struct image_info img, GLuint prog) { uint32_t pixels[4 * N]; return init_pixels(img, pixels, 1, 1, 1, 1) && upload_image(img, 0, pixels) && set_uniform_int(prog, "imgs[0]", 0); } static bool init_level(const struct image_info img, unsigned level, GLenum format, unsigned w, unsigned h) { uint32_t pixels[4 * N]; init_pixels(img, pixels, 1, 1, 1, 1); glBindTexture(GL_TEXTURE_2D, get_texture(0)); glTexImage2D(GL_TEXTURE_2D, level, format, w, h, 0, img.format->pixel_format, image_base_type(img.format), pixels); return piglit_check_gl_error(GL_NO_ERROR); } static bool check(const struct grid_info grid, const struct image_info img) { uint32_t pixels_fb[4 * N], pixels_img[4 * N]; if (!download_result(grid, pixels_fb) || !download_image(img, 0, pixels_img)) return false; /* Check that the built-in return value is zero (nonexisting texel). */ if (!check_pixels(image_info_for_grid(grid), pixels_fb, 0, 0, 0, (image_num_components(img.format) < 4 ? 1 : 0))) { printf(" Source: framebuffer\n"); return false; } /* Check that the image wasn't modified. */ if (!check_pixels(img, pixels_img, 1, 1, 1, 1)) { printf(" Source: image\n"); return false; } return true; } static bool invalidate_unbound(const struct image_info img, GLuint prog) { glBindImageTexture(0, 0, 0, GL_FALSE, 0, GL_READ_ONLY, img.format->format); return piglit_check_gl_error(GL_NO_ERROR); } static bool invalidate_incomplete(const struct image_info img, GLuint prog) { /* Bind a mipmap level with incorrect dimensions so the * texture becomes incomplete. */ bool ret = init_level(img, 1, img.format->format, W, H); glBindImageTexture(0, get_texture(0), 1, GL_TRUE, 0, GL_READ_WRITE, img.format->format); return ret && piglit_check_gl_error(GL_NO_ERROR); } static bool invalidate_level_bounds(const struct image_info img, GLuint prog) { const int level = 1; const struct image_extent size = image_level_size(img, level); /* Create a second mipmap level */ bool ret = init_level(img, level, img.format->format, size.x, size.y); /* and set it as base level, */ glTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, &level); /* but keep the the first level bound. */ glBindImageTexture(0, get_texture(0), 0, GL_TRUE, 0, GL_READ_WRITE, img.format->format); return ret && piglit_check_gl_error(GL_NO_ERROR); } static bool invalidate_invalid_format(const struct image_info img, GLuint prog) { const GLenum base_format = image_base_internal_format(img.format); /* Pick an invalid texture format with a compatible base * type. */ bool ret = init_level(img, 0, (base_format == GL_RGBA32F ? GL_RGB5_A1 : GL_RGB8UI), W, H); glBindImageTexture(0, get_texture(0), 0, GL_TRUE, 0, GL_READ_WRITE, img.format->format); return ret && piglit_check_gl_error(GL_NO_ERROR); } static bool invalidate_incompatible_format(const struct image_info img, GLuint prog) { GLenum base_format = image_base_internal_format(img.format); /* Pick an incompatible texture format with a compatible base * type. */ glBindImageTexture(0, get_texture(0), 0, GL_TRUE, 0, GL_READ_WRITE, (base_format == GL_RGBA32F ? GL_RGBA8 : GL_RG32UI)); return piglit_check_gl_error(GL_NO_ERROR); } static bool invalidate_layer_bounds(const struct image_info img, GLuint prog) { glBindImageTexture(0, get_texture(0), 0, GL_FALSE, N, GL_READ_WRITE, img.format->format); return piglit_check_gl_error(GL_NO_ERROR); } static bool invalidate_address_bounds(const struct image_info img, GLuint prog) { return set_uniform_int(prog, "off", N); } static bool invalidate_index_bounds(const struct image_info img, GLuint prog) { return set_uniform_int(prog, "u", 0xdeadcafe); } static bool invalidate_nop(const struct image_info img, GLuint prog) { return true; } 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) { const struct grid_info grid = grid_info(GL_FRAGMENT_SHADER, image_base_internal_format(real_img.format), W, H); GLuint prog = generate_program( grid, GL_FRAGMENT_SHADER, concat(image_hunk(prog_img, ""), hunk("IMAGE_UNIFORM_T imgs[1];\n" "uniform int u;\n" "uniform int off;\n"), hunk(op->hunk), NULL)); bool ret = prog && init_fb(grid) && init_image(real_img, prog) && invalidate(real_img, prog) && draw_grid(grid, prog) && (check(grid, real_img) || control_test); glDeleteProgram(prog); return ret; } void piglit_init(int argc, char **argv) { enum piglit_result status = PIGLIT_PASS; const struct image_op_info *op; const struct image_format_info *format; const struct image_target_info *target; piglit_require_extension("GL_ARB_shader_image_load_store"); for (op = image_ops; op->name; ++op) { const struct image_info def_img = image_info( GL_TEXTURE_2D, op->formats[0].format, W, H); const struct image_info def_img_buffer = image_info( GL_TEXTURE_BUFFER, op->formats[0].format, W, H); /* * According to the spec, an access is considered * invalid in the following cases, in which image * stores and atomics should have no effect, and image * loads should return zero: * * " * no texture is bound to the selected image unit; * [...]" */ subtest(&status, true, run_test(op, def_img, def_img, invalidate_unbound, false), "%s/unbound image test", op->name); /* * " * the texture bound to the selected image unit is * incomplete; [...]" */ subtest(&status, true, run_test(op, def_img, def_img, invalidate_incomplete, false), "%s/incomplete image test", op->name); /* * " * the texture level bound to the image unit is * less than the base level or greater than the * maximum level of the texture; [...]" */ subtest(&status, true, run_test(op, def_img, def_img, invalidate_level_bounds, false), "%s/level bounds test", op->name); /* * " * the internal format of the texture bound to the * image unit is not found in Table X.2; [...]" */ subtest(&status, true, run_test(op, def_img, def_img, invalidate_invalid_format, false), "%s/invalid format test", op->name); /* * " * the internal format of the texture bound to the * image unit is incompatible with the specified * according to Table X.3; [...]" */ subtest(&status, true, run_test(op, def_img, def_img, invalidate_incompatible_format, false), "%s/incompatible format test", op->name); /* Test for the regression which happened when * GL_TEXTURE_BUFFER was allowed to have incompatible format. */ subtest(&status, true, run_test(op, def_img_buffer, def_img_buffer, invalidate_incompatible_format, false), "%s/incompatible format test/image%s", op->name, def_img_buffer.target->name); /* * " * the texture bound to the image unit has layers, * and the selected layer or cube map face doesn't * exist; [...]" */ subtest(&status, true, run_test(op, image_info(GL_TEXTURE_2D_ARRAY, op->formats[0].format, W, H), def_img, invalidate_layer_bounds, false), "%s/layer bounds test", op->name); /* * " * the selected texel tau_i, tau_i_j, or tau_i_j_k * doesn't exist; [...]" */ for (target = image_targets(); target->name; ++target) { const struct image_info img = image_info( target->target, op->formats[0].format, W, H); subtest(&status, true, run_test(op, img, img, invalidate_address_bounds, false), "%s/address bounds test/image%s/%s", op->name, img.target->name, img.format->name); } for (format = &op->formats[1]; format->name; ++format) { const struct image_info img = image_info( GL_TEXTURE_2D, format->format, W, H); subtest(&status, true, run_test(op, img, img, invalidate_address_bounds, false), "%s/address bounds test/image%s/%s", op->name, img.target->name, img.format->name); } /* * The following cases have undefined results, but may * not lead to program termination: * * "If the index used to select an individual [array] * element is negative or greater than or equal to * the size of the array [...]" */ subtest(&status, true, run_test(op, def_img, def_img, invalidate_index_bounds, true), "%s/index bounds test", op->name); /* * "the type of image variable used to access the * image unit does not match the target of a texture * bound to the image unit [...]" */ subtest(&status, true, run_test(op, def_img, image_info(GL_TEXTURE_3D, op->formats[0].format, W, H), invalidate_nop, true), "%s/target mismatch test", op->name); /* * "the format layout qualifier for an image variable * used for an image load or atomic operation does * not match the format of the image unit [...]" */ subtest(&status, true, run_test(op, image_info(GL_TEXTURE_2D, GL_R11F_G11F_B10F, W, H), def_img, invalidate_nop, true), "%s/format mismatch test", op->name); } piglit_report_result(status); } enum piglit_result piglit_display(void) { return PIGLIT_FAIL; }