/* * 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 state.c * * Test image unit binding by creating a number of textures and * binding them as images with different parameters (including * incorrect arguments that are supposed to generate GL errors), * delete and unbind a few images and check using the state query API * that the implementation is keeping track of the image unit state * correctly. * * A second test checks that glUniform*() work as specified when used * to assign image units to shader image uniforms. */ #include "common.h" PIGLIT_GL_TEST_CONFIG_BEGIN /** Image width. */ #define W 16 /** Image height. */ #define H 96 /** Total number of pixels in the image. */ #define N (W * H) /** Maximum number of mipmap levels. */ #define M 11 config.supports_gl_core_version = 32; config.window_width = 1; config.window_height = 1; config.window_visual = PIGLIT_GL_VISUAL_DOUBLE | PIGLIT_GL_VISUAL_RGBA; config.khr_no_error_support = PIGLIT_HAS_ERRORS; PIGLIT_GL_TEST_CONFIG_END struct image_unit_action { enum { /** End of action list. */ END = 0, /** Create a new texture object of type \a obj and * bind it to the specified image unit. */ BIND_NEW, /** Bind the same texture object that was previously * bound to image unit \a obj to the specified image * unit. */ BIND_IDX, /** Bind texture object \a obj to the specified image * unit. */ BIND_OBJ, /** Delete the texture object that was previously * bound to image unit \a obj. */ DELETE_IDX } action; /** Image unit this action has an effect on. */ unsigned idx; /** Object of this action. */ unsigned obj; /** Texture mipmap level that should be bound. */ int level; /** If true the whole texture level is bound rather than a * single layer. */ bool layered; /** If \a layered is false, the index of the individual layer * to bind. */ int layer; /** GL_READ_ONLY, GL_WRITE_ONLY or GL_READ_WRITE. */ GLenum access; /** Image format used to interpret the texture data. */ GLenum format; /** GL error code that should be expected after the completion * of this action. */ GLenum expect_status; }; static const struct image_unit_action actions[] = { { BIND_NEW, 0, GL_TEXTURE_2D, 2, GL_FALSE, 0, GL_READ_WRITE, GL_RGBA16UI, GL_NO_ERROR }, { BIND_NEW, 1, GL_TEXTURE_2D, 1, GL_FALSE, 0, GL_READ_WRITE, GL_RGBA16F, GL_NO_ERROR }, { BIND_NEW, 2, GL_TEXTURE_BUFFER, 0, GL_FALSE, 0, GL_WRITE_ONLY, GL_RGBA16F, GL_NO_ERROR }, { BIND_NEW, 3, GL_TEXTURE_2D, -1, GL_FALSE, 0, GL_WRITE_ONLY, GL_RGBA16_SNORM, GL_INVALID_VALUE }, { BIND_NEW, 3, GL_TEXTURE_2D, 0, GL_FALSE, 0, GL_WRITE_ONLY, GL_RGB565, GL_INVALID_VALUE }, { BIND_NEW, 3, GL_TEXTURE_2D_ARRAY, 0, GL_FALSE, -1, GL_WRITE_ONLY, GL_RGBA16_SNORM, GL_INVALID_VALUE }, { BIND_OBJ, 3, 0xdeadcafe, 0, GL_FALSE, 0, GL_WRITE_ONLY, GL_RGBA8, GL_INVALID_VALUE }, { BIND_NEW, 3, GL_TEXTURE_2D_ARRAY, 0, GL_FALSE, 2, GL_WRITE_ONLY, GL_RGBA16, GL_NO_ERROR }, { BIND_NEW, 4, GL_TEXTURE_2D_ARRAY, 0, GL_TRUE, 0, GL_READ_ONLY, GL_RGBA16, GL_NO_ERROR }, { BIND_OBJ, 2, 0, 0, GL_FALSE, 0, GL_READ_ONLY, GL_R8, GL_NO_ERROR }, { BIND_IDX, ~0, 1, 0, GL_FALSE, 0, GL_WRITE_ONLY, GL_RGBA16_SNORM, GL_INVALID_VALUE }, { BIND_NEW, 5, GL_TEXTURE_2D, 0, GL_FALSE, 0, GL_READ_ONLY, GL_RGBA16F, GL_NO_ERROR }, { BIND_NEW, 6, GL_TEXTURE_3D, 0, GL_FALSE, 3, GL_WRITE_ONLY, GL_RGBA16F, GL_NO_ERROR }, { DELETE_IDX, 5, 5, 0, GL_FALSE, 0, GL_READ_ONLY, GL_R8, GL_NO_ERROR }, { END } }; /** * Get the maximum number of image units supported by the * implementation. */ static unsigned first_invalid_image_unit(void) { int n; glGetIntegerv(GL_MAX_IMAGE_UNITS, &n); return n; } /** * Get the last action that modified the state of image unit \a idx. */ static struct image_unit_action get_last_unit_action(unsigned idx) { /* The initial image unit state is equivalent to this * action. */ const struct image_unit_action def_action = { BIND_OBJ, idx, 0, 0, GL_FALSE, 0, GL_READ_ONLY, GL_R8, GL_NO_ERROR }; const struct image_unit_action *a, *la = &def_action; for (a = actions; a->action; ++a) { if (a->idx == idx) la = a; } return *la; } /** * Execute the given action. */ static bool exec_action(const struct image_unit_action a) { if (a.action == BIND_NEW) { const GLenum format = (get_image_format(a.format) ? a.format : GL_RGBA32F); const struct image_info img = image_info(a.obj, format, W, H); const unsigned num_levels = image_num_levels(img); uint32_t pixels[4 * N * M] = { 0 }; if (!upload_image_levels(img, num_levels, 0, a.idx, pixels)) return false; glBindImageTexture(a.idx, get_texture(a.idx), a.level, a.layered, a.layer, a.access, a.format); } else if (a.action == BIND_IDX) { const unsigned idx = MIN2(a.idx, first_invalid_image_unit()); glBindImageTexture(idx, get_texture(a.obj), a.level, a.layered, a.layer, a.access, a.format); } else if (a.action == BIND_OBJ) { glBindImageTexture(a.idx, a.obj, a.level, a.layered, a.layer, a.access, a.format); } else if (a.action == DELETE_IDX) { GLuint tex = get_texture(a.idx); glDeleteTextures(1, &tex); } else { abort(); } return piglit_check_gl_error(a.expect_status); } static bool check_integer(GLenum name, unsigned idx, int expect) { int v = 0xdeadcafe; glGetIntegeri_v(name, idx, &v); if (v != expect) { fprintf(stderr, "Invalid value for integer %s index %d\n" " Expected: %d\n" " Observed: %d\n", piglit_get_gl_enum_name(name), idx, expect, v); return false; } return true; } static bool check_tex_parameter(GLenum target, GLuint obj, GLenum name, int expect) { int v = 0xdeadcafe; glBindTexture(target, obj); glGetTexParameteriv(target, name, &v); if (v != expect) { fprintf(stderr, "Invalid value for tex parameter %s\n" " Expected: %d\n" " Observed: %d\n", piglit_get_gl_enum_name(name), expect, v); return false; } return true; } /** * Check that the image unit state matches the result of the specified * action. */ static bool check_action(const struct image_unit_action a) { if ((a.action == BIND_NEW || a.action == BIND_OBJ || a.action == BIND_IDX) && a.expect_status == GL_NO_ERROR) { const GLuint obj = (a.action == BIND_NEW ? get_texture(a.idx) : a.action == BIND_IDX ? get_texture(a.obj) : a.obj); if (a.action == BIND_NEW && !check_tex_parameter(a.obj, obj, GL_IMAGE_FORMAT_COMPATIBILITY_TYPE, GL_IMAGE_FORMAT_COMPATIBILITY_BY_SIZE)) return false; return check_integer(GL_IMAGE_BINDING_NAME, a.idx, obj) && check_integer(GL_IMAGE_BINDING_LEVEL, a.idx, a.level) && check_integer(GL_IMAGE_BINDING_LAYERED, a.idx, a.layered) && check_integer(GL_IMAGE_BINDING_LAYER, a.idx, a.layer) && check_integer(GL_IMAGE_BINDING_ACCESS, a.idx, a.access) && check_integer(GL_IMAGE_BINDING_FORMAT, a.idx, a.format); } else { return check_integer(GL_IMAGE_BINDING_NAME, a.idx, 0); } } /** * Bind a number of texture objects to different image units and check * that the image unit state was updated correctly. */ static bool run_test_binding(void) { const struct image_unit_action *action; bool ret = true; int i; for (action = actions; action->action; ++action) ret &= exec_action(*action); for (i = 0; i < max_image_units(); ++i) ret &= check_action(get_last_unit_action(i)); return ret; } static bool check_uniform_int(GLuint prog, int loc, int expect) { int v = 0xdeadcafe; glGetUniformiv(prog, loc, &v); if (v != expect) { fprintf(stderr, "Invalid value for uniform %d\n" " Expected: %d\n" " Observed: %d\n", loc, expect, v); return false; } return piglit_check_gl_error(GL_NO_ERROR); } #define CHECK_INVAL_1(prefix, suffix, args, ret) do { \ prefix##suffix args; \ ret &= piglit_check_gl_error(GL_INVALID_OPERATION); \ } while (0) #define CHECK_INVAL_2(prefix, suffix0, suffix1, args, ret) do { \ CHECK_INVAL_1(prefix, suffix0, args, ret); \ CHECK_INVAL_1(prefix, suffix1, args, ret); \ } while (0) #define CHECK_INVAL_3(prefix, suffix0, suffix1, suffix2, args, ret) do { \ CHECK_INVAL_2(prefix, suffix0, suffix1, args, ret); \ CHECK_INVAL_1(prefix, suffix2, args, ret); \ } while (0) /** * Test binding image uniforms to image units for a simple shader * program. */ static bool run_test_uniform(void) { const struct grid_info grid = grid_info(GL_FRAGMENT_SHADER, GL_RGBA32F, W, H); GLuint prog = generate_program( grid, GL_FRAGMENT_SHADER, concat(image_hunk(image_info_for_grid(grid), ""), hunk("IMAGE_UNIFORM_T imgs[2];\n" "\n" "GRID_T op(ivec2 idx, GRID_T x) {\n" " imageStore(imgs[0], IMAGE_ADDR(idx), x);\n" " imageStore(imgs[1], IMAGE_ADDR(idx), x);\n" " return x;\n" "}\n"), NULL)); const int loc = glGetUniformLocation(prog, "imgs"); bool ret = prog && check_uniform_int(prog, loc, 0) && check_uniform_int(prog, loc + 1, 0); int v[2]; glUseProgram(prog); /* * Image uniforms are bound to image units using * glUniform1i{v}. */ glUniform1i(loc, 3); ret &= check_uniform_int(prog, loc, 3) && check_uniform_int(prog, loc + 1, 0); glUniform1i(loc + 1, 3); ret &= check_uniform_int(prog, loc, 3) && check_uniform_int(prog, loc + 1, 3); v[0] = 4; v[1] = 5; glUniform1iv(loc, 2, v); ret &= check_uniform_int(prog, loc, 4) && check_uniform_int(prog, loc + 1, 5); /* * GL_INVALID_VALUE is generated if the value specified is * greater than or equal to the value of GL_MAX_IMAGE_UNITS. */ glUniform1i(loc, first_invalid_image_unit()); ret &= piglit_check_gl_error(GL_INVALID_VALUE); v[0] = 3; v[1] = first_invalid_image_unit() + 1; glUniform1iv(loc, 2, v); ret &= piglit_check_gl_error(GL_INVALID_VALUE); /* * GL_INVALID_VALUE is generated if the value specified is * less than zero. */ glUniform1i(loc, -1); ret &= piglit_check_gl_error(GL_INVALID_VALUE); v[0] = 3; v[1] = -4; glUniform1iv(loc, 2, v); ret &= piglit_check_gl_error(GL_INVALID_VALUE); /* * GL_INVALID_OPERATION is generated by Uniform* functions * other than Uniform1i{v}. */ CHECK_INVAL_2(glUniform, 1f, 1ui, (loc, 0), ret); CHECK_INVAL_3(glUniform, 2i, 2f, 2ui, (loc, 0, 0), ret); CHECK_INVAL_3(glUniform, 3i, 3f, 3ui, (loc, 0, 0, 0), ret); CHECK_INVAL_3(glUniform, 4i, 4f, 4ui, (loc, 0, 0, 0, 0), ret); CHECK_INVAL_2(glUniform, 1fv, 1uiv, (loc, 1, (void *)v), ret); CHECK_INVAL_3(glUniform, 2iv, 2fv, 2uiv, (loc, 1, (void *)v), ret); CHECK_INVAL_3(glUniform, 3iv, 3fv, 3uiv, (loc, 1, (void *)v), ret); CHECK_INVAL_3(glUniform, 4iv, 4fv, 4uiv, (loc, 1, (void *)v), ret); CHECK_INVAL_3(glUniformMatrix, 2fv, 3fv, 4fv, (loc, 1, GL_FALSE, (float *)v), ret); CHECK_INVAL_3(glUniformMatrix, 2x3fv, 3x2fv, 2x4fv, (loc, 1, GL_FALSE, (float *)v), ret); CHECK_INVAL_3(glUniformMatrix, 4x2fv, 3x4fv, 4x3fv, (loc, 1, GL_FALSE, (float *)v), ret); if (piglit_is_extension_supported("GL_ARB_gpu_shader_fp64")) { CHECK_INVAL_1(glUniform, 1d, (loc, 0), ret); CHECK_INVAL_1(glUniform, 2d, (loc, 0, 0), ret); CHECK_INVAL_1(glUniform, 3d, (loc, 0, 0, 0), ret); CHECK_INVAL_1(glUniform, 4d, (loc, 0, 0, 0, 0), ret); CHECK_INVAL_2(glUniform, 1dv, 2dv, (loc, 1, (double *)v), ret); CHECK_INVAL_2(glUniform, 3dv, 4dv, (loc, 1, (double *)v), ret); CHECK_INVAL_3(glUniformMatrix, 2dv, 3dv, 4dv, (loc, 1, GL_FALSE, (double *)v), ret); CHECK_INVAL_3(glUniformMatrix, 2x3dv, 3x2dv, 2x4dv, (loc, 1, GL_FALSE, (double *)v), ret); CHECK_INVAL_3(glUniformMatrix, 4x2dv, 3x4dv, 4x3dv, (loc, 1, GL_FALSE, (double *)v), ret); } glDeleteProgram(prog); return ret; } void piglit_init(int argc, char **argv) { enum piglit_result status = PIGLIT_PASS; piglit_require_extension("GL_ARB_shader_image_load_store"); subtest(&status, true, run_test_binding(), "binding state test"); subtest(&status, true, run_test_uniform(), "uniform state test"); piglit_report_result(status); } enum piglit_result piglit_display(void) { return PIGLIT_FAIL; }