/* * Copyright © 2011 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 texelFetch.c * * Tests the GLSL 1.30+ texelFetch() built-in function. * * The "texelFetch" binary takes two arguments: shader stage and sampler type. * * For example: * ./bin/texelFetch fs sampler1DArray * ./bin/texelFetch usampler3D vs * * The test covers: * - All pipeline stages (VS, GS, FS) * - Integer and floating point texture formats * - Sampler dimensionality (1D, 2D, 3D, 1DArray, 2DArray) * - Mipmapping * - Non-power-of-two textures * * Draws a series of "rectangles" which display each miplevel and array slice, * at full size. They are layed out as follows: * * miplevel 3 + + + + + * * miplevel 2 +-+ +-+ +-+ +-+ +-+ * +-+ +-+ +-+ +-+ +-+ * * miplevel 1 +---+ +---+ +---+ +---+ +---+ * | | | | | | | | | | * +---+ +---+ +---+ +---+ +---+ * * +------+ +------+ +------+ +------+ +------+ * miplevel 0 | | | | | | | | | | * | | | | | | | | | | * +------+ +------+ +------+ +------+ +------+ * slice #0 slice #1 slice #2 slice #3 slice #4 * * Normally, we could draw each rectangle as a single quad (or two triangles), * interpolate the texture coordinates across the primitive, and let the * fragment shader look up the color values from the texture. * * However, this fails miserably for vertex shader texturing: a quad only has * four vertices, which means we could only fetch/display at most 4 texels. * If we used a simple RGBW checkerboard, as in other Piglit tests, this would * only tell us that we sampled somewhere in the right 1/4 of the texture. * * Instead, we take a clever approach: draw each "rectangle" via a series of * 1-pixel wide GL_POINT primitives. This gives us one vertex per pixel; by * drawing the texture at full size, each pixel and vertex also correspond to * exactly one texel. So every texel is sampled and verified for correctness. * * In other words: "One pixel, one texel, one vertex." * * For convenience, we take the same approach for fragment shader testing * as well, since it allows us to reuse the same VBO setup and drawing code. */ #include "common.h" void parse_args(int argc, char **argv); static enum shader_target test_stage = UNKNOWN; PIGLIT_GL_TEST_CONFIG_BEGIN config.window_width = 900; config.window_height = 600; config.window_visual = PIGLIT_GL_VISUAL_RGBA | PIGLIT_GL_VISUAL_DOUBLE; config.khr_no_error_support = PIGLIT_NO_ERRORS; piglit_gl_process_args(&argc, argv, &config); parse_args(argc, argv); if (test_stage == GS) { config.supports_gl_compat_version = 32; config.supports_gl_core_version = 32; } else { config.supports_gl_compat_version = 10; config.supports_gl_core_version = 31; } PIGLIT_GL_TEST_CONFIG_END /** Vertex shader attribute locations */ const int pos_loc = 0; const int texcoord_loc = 1; /** Whether we're testing texelFetchOffset() instead of texelFetch(). */ static bool test_offset = false; const int fetch_x_offset = 7; const int fetch_y_offset = -8; const int fetch_z_offset = 4; const char *fetch_1d_arg = ", int(7)"; const char *fetch_2d_arg = ", ivec2(7, -8)"; const char *fetch_3d_arg = ", ivec3(7, -8, 4)"; /** Uniform locations */ int divisor_loc; /** * Expected color data for each rectangle drawn, indexed by miplevel and slice. * For example, expected_colors[1][3] contains the data for miplevel 1 slice 3. */ float ***expected_colors; int prog; GLuint tex; GLuint pos_vbo, tc_vbo; /** * Return the divisors necessary to scale the unnormalized texture data to * a floating point color value in the range [0, 1]. */ static void compute_divisors(int lod, float *divisors) { divisors[0] = max2(level_size[lod][0] - 1, 1); divisors[1] = max2(level_size[lod][1] - 1, 1); divisors[2] = max2(level_size[lod][2] - 1, 1); divisors[3] = miplevels; if (sampler.data_type != GL_UNSIGNED_INT) divisors[0] = -divisors[0]; } static bool test_once() { int i, l, z, level_y = 0; bool pass = true; i = 0; for (l = 0; l < miplevels; ++l) { if (l && !has_samples()) level_y += 1 + MAX2(base_size[1] >> (l-1), 1); for (z = 0; z < level_size[l][2]; z++) { /* Draw the "rectangle" for this miplevel/slice. */ int points = level_size[l][0] * level_size[l][1]; float divisors[4]; compute_divisors(l, divisors); swizzle(divisors); glUniform4fv(divisor_loc, 1, divisors); glDrawArrays(GL_POINTS, i, points); i += points; /* Compare results against reference image. */ if (!piglit_probe_image_rgba(5 + (1 + base_size[0]) * z, has_samples() ? 5 + (1 + base_size[1]) * l : 5 + level_y, level_size[l][0], level_size[l][1], expected_colors[l][z])) { piglit_loge("Failed at level %d, slice %d\n", l, z); pass = false; } free(expected_colors[l][z]); } free(expected_colors[l]); } free(expected_colors); return pass; } /** * Generate two VBOs for our vertex attributes: * 1. Pixel position (in window coordinates). * 2. Texture coordinates. * * The VBOs contain the data for every rectangle being drawn (as opposed to * creating and binding a separate VBO per miplevel/slice.) */ void generate_VBOs() { int x, y, z, l; float *pos, *pos_data; int *tc, *tc_data; bool array_1D = sampler.target == GL_TEXTURE_1D_ARRAY; int num_texels = 0; int level_y = 0; /* Calculate the # of texels a.k.a. size of the VBOs */ for (l = 0; l < miplevels; l++) { num_texels += level_size[l][0] * level_size[l][1] * level_size[l][2]; } pos = pos_data = malloc(num_texels * 4 * sizeof(float)); tc = tc_data = malloc(num_texels * 4 * sizeof(int)); for (l = 0; l < miplevels; l++) { if (l && !has_samples()) level_y += 1 + MAX2(base_size[1] >> (l-1), 1); for (z = 0; z < level_size[l][2]; z++) { for (y = 0; y < level_size[l][1]; y++) { for (x = 0; x < level_size[l][0]; x++) { /* Assign pixel positions: */ pos[0] = 5.5 + (1 + base_size[0])*z + x; if (has_samples()) pos[1] = 5.5 + (1 + base_size[1])*l + y; else pos[1] = 5.5 + level_y + y; pos[2] = 0.0; pos[3] = 1.0; pos[0] = -1.0 + 2.0 * (pos[0] / piglit_width); pos[1] = -1.0 + 2.0 * (pos[1] / piglit_height); pos += 4; /* Assign texture coordinates: * 1D: x _ _ l * 2D: x y _ l * 3D: x y z l * 1DArray: x z _ l * 2DArray: x y z l */ tc[0] = x; tc[1] = array_1D ? z : y; tc[2] = z; tc[3] = l; if (test_offset) { tc[0] -= fetch_x_offset; if (sampler.target != GL_TEXTURE_1D_ARRAY) tc[1] -= fetch_y_offset; if (sampler.target != GL_TEXTURE_2D_ARRAY) tc[2] -= fetch_z_offset; } tc += 4; } } } } /* Create VBO for pixel positions in NDC: */ glGenBuffers(1, &pos_vbo); glBindBuffer(GL_ARRAY_BUFFER, pos_vbo); glBufferData(GL_ARRAY_BUFFER, num_texels * 4 * sizeof(float), pos_data, GL_STATIC_DRAW); glVertexAttribPointer(pos_loc, 4, GL_FLOAT, false, 0, 0); glEnableVertexAttribArray(pos_loc); /* Create VBO for texture coordinates: */ glGenBuffers(1, &tc_vbo); glBindBuffer(GL_ARRAY_BUFFER, tc_vbo); glBufferData(GL_ARRAY_BUFFER, num_texels * 4 * sizeof(int), tc_data, GL_STATIC_DRAW); glVertexAttribIPointer(texcoord_loc, 4, GL_INT, 0, 0); glEnableVertexAttribArray(texcoord_loc); free(pos_data); free(tc_data); } /* like piglit_draw_rect(), but works in a core context too. * pretty silly and wasteful (binds a throwaway VAO and BO) */ static void draw_rect_core(int x, int y, int w, int h) { float verts[4][4]; GLuint bo; verts[0][0] = x; verts[0][1] = y; verts[0][2] = 0.0; verts[0][3] = 1.0; verts[1][0] = x + w; verts[1][1] = y; verts[1][2] = 0.0; verts[1][3] = 1.0; verts[2][0] = x + w; verts[2][1] = y + h; verts[2][2] = 0.0; verts[2][3] = 1.0; verts[3][0] = x; verts[3][1] = y + h; verts[3][2] = 0.0; verts[3][3] = 1.0; glGenBuffers(1, &bo); glBindBuffer(GL_ARRAY_BUFFER, bo); glBufferData(GL_ARRAY_BUFFER, 4 * 4 * sizeof(float), verts, GL_STATIC_DRAW); glVertexAttribPointer(0, 4, GL_FLOAT, false, 0, 0); glEnableVertexAttribArray(0); glDrawArrays(GL_TRIANGLE_FAN, 0, 4); glDeleteBuffers(1, &bo); } /** * Generate the same test pattern as generate_texture() does, but do it in the shader. * This is a silly workaround for not being able to just upload directly. */ static void upload_multisample_data(GLuint tex, int width, int height, int layers, int samples) { GLuint oldFBO, FBO; int si; char *fs_code; int vs, fs; int staging_program = 0; int layer_loc = 0; int si_loc = 0; glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, (GLint *) &oldFBO); if (is_array_sampler()) glTexImage3DMultisample(sampler.target, samples, sampler.internal_format, width, height, layers, GL_TRUE); else glTexImage2DMultisample(sampler.target, samples, sampler.internal_format, width, height, GL_TRUE); if (!piglit_check_gl_error(GL_NO_ERROR)) { printf("Error creating multisample texture\n"); piglit_report_result(PIGLIT_FAIL); } vs = piglit_compile_shader_text(GL_VERTEX_SHADER, "#version 130\n" "#extension GL_ARB_explicit_attrib_location: require\n" "layout(location=0) in vec4 pos;\n" "void main() {\n" " gl_Position = pos;\n" "}\n"); (void)!asprintf(&fs_code, "#version 130\n" "#extension GL_ARB_explicit_attrib_location: require\n" "#extension GL_ARB_fragment_coord_conventions: require\n" "uniform int layer;\n" "uniform int si;\n" "layout(pixel_center_integer) in vec4 gl_FragCoord;\n" "layout(location=0) out %s o;\n" "void main() {\n" " o = %s(%sgl_FragCoord.x, gl_FragCoord.y, layer, si);\n" "}\n", sampler.return_type, sampler.return_type, sampler.data_type == GL_UNSIGNED_INT ? "" : "-"); fs = piglit_compile_shader_text(GL_FRAGMENT_SHADER, fs_code); free(fs_code); if (!fs) { printf("Failed to compile staging program.\n"); piglit_report_result(PIGLIT_FAIL); } staging_program = piglit_link_simple_program(vs,fs); if (!piglit_link_check_status(staging_program)) piglit_report_result(PIGLIT_FAIL); layer_loc = glGetUniformLocation(staging_program, "layer"); si_loc = glGetUniformLocation(staging_program, "si"); glUseProgram(staging_program); if (!piglit_check_gl_error(GL_NO_ERROR)) { printf("glUseProgram failed\n"); piglit_report_result(PIGLIT_FAIL); } glGenFramebuffers(1, &FBO); glBindFramebuffer(GL_DRAW_FRAMEBUFFER, FBO); glViewport(0, 0, width, height); glEnable(GL_SAMPLE_MASK); for (si=0; si < samples; si++) { glUniform1i(si_loc, si); glSampleMaski(0, 1< " "[sample_count] [swizzle] [piglit args...]\n"); piglit_report_result(PIGLIT_FAIL); } void parse_args(int argc, char **argv) { int i; bool sampler_found = false; bool dim_range_found = false; sample_count = 0; minx = maxx = 65; miny = maxy = 32; minz = maxz = 5; for (i = 1; i < argc; i++) { if (test_stage == UNKNOWN) { /* Maybe it's the shader stage? */ if (strcmp(argv[i], "vs") == 0) { test_stage = VS; continue; } else if (strcmp(argv[i], "gs") == 0) { test_stage = GS; continue; } else if (strcmp(argv[i], "fs") == 0) { test_stage = FS; continue; } } if (strcmp(argv[i], "140") == 0) { shader_version = 140; continue; } if (strcmp(argv[i], "offset") == 0) { test_offset = true; continue; } /* Maybe it's the sampler type? */ if (!sampler_found && (sampler_found = select_sampler(argv[i]))) continue; /* Maybe it's the sample count? */ if (sampler_found && has_samples() && !sample_count) { sample_count = atoi(argv[i]); continue; } if (!swizzling && (swizzling = parse_swizzle(argv[i]))) continue; if (!dim_range_found) { if (sscanf(argv[i], "%ux%ux%u-%ux%ux%u", &minx, &miny, &minz, &maxx, &maxy, &maxz) == 6) { dim_range_found = true; continue; } if (sscanf(argv[i], "%ux%u-%ux%u", &minx, &miny, &maxx, &maxy) == 4) { minz = maxz = 1; dim_range_found = true; continue; } if (sscanf(argv[i], "%u-%u", &minx, &maxx) == 2) { miny = maxy = minz = maxz = 1; dim_range_found = true; continue; } if (sscanf(argv[i], "%ux%ux%u", &minx, &miny, &minz) == 3) { maxx = minx; maxy = miny; maxz = minz; dim_range_found = true; continue; } if (sscanf(argv[i], "%ux%u", &minx, &miny) == 2) { maxx = minx; maxy = miny; minz = maxz = 1; dim_range_found = true; continue; } } fail_and_show_usage(); } if (test_stage == UNKNOWN || !sampler_found) fail_and_show_usage(); if (test_stage == GS && shader_version < 150) shader_version = 150; if (!has_height()) { miny = maxy = 1; } if (!has_slices()) { minz = maxz = 1; } if (minx < maxx || miny < maxy || minz < maxz) piglit_automatic = true; } enum piglit_result piglit_display() { int x, y, z; bool pass = true; glClearColor(0.1, 0.1, 0.1, 1.0); glPointSize(1.0); for (x = minx; x <= maxx; x++) { for (y = miny; y <= maxy; y++) { for (z = minz; z <= maxz; z++) { printf("%ix%ix%i\n", x, y, z); if (5 + (x+1) * z >= piglit_width) { printf("Width or depth is too big.\n"); piglit_report_result(PIGLIT_FAIL); } if ((has_samples() && 5 + (y+1) * miplevels >= piglit_height) || (!has_samples() && 5 + y*2 + miplevels-2 >= piglit_height)) { printf("Height is too big or too many samples.\n"); piglit_report_result(PIGLIT_FAIL); } base_size[0] = x; base_size[1] = y; base_size[2] = z; /* Create textures and set miplevel info */ compute_miplevel_info(); generate_texture(); generate_VBOs(); glViewport(0, 0, piglit_width, piglit_height); glClear(GL_COLOR_BUFFER_BIT); glUseProgram(prog); pass &= test_once(); glUseProgram(0); glDeleteTextures(1, &tex); glDeleteBuffers(1, &pos_vbo); glDeleteBuffers(1, &tc_vbo); free_miplevel_info(); } } } piglit_present_results(); return pass ? PIGLIT_PASS : PIGLIT_FAIL; } void piglit_init(int argc, char **argv) { int tex_location; if (!supported_sampler()) { printf("%s unsupported\n", sampler.name); piglit_report_result(PIGLIT_FAIL); } require_GL_features(test_stage); if (sample_count) { /* check it */ GLint max_samples; if (sampler.data_type == GL_INT || sampler.data_type == GL_UNSIGNED_INT) { glGetIntegerv(GL_MAX_INTEGER_SAMPLES, &max_samples); if (sample_count > max_samples) { printf("Sample count of %d not supported," " >MAX_INTEGER_SAMPLES\n", sample_count); piglit_report_result(PIGLIT_SKIP); } } else { glGetIntegerv(GL_MAX_SAMPLES, &max_samples); if (sample_count > max_samples) { printf("Sample count of %d not supported," " >MAX_SAMPLES\n", sample_count); piglit_report_result(PIGLIT_SKIP); } } } prog = generate_GLSL(test_stage); tex_location = glGetUniformLocation(prog, "tex"); divisor_loc = glGetUniformLocation(prog, "divisor"); glUseProgram(prog); glUniform1i(tex_location, 0); if (piglit_get_gl_version() >= 31) { GLuint vao; glGenVertexArrays(1, &vao); glBindVertexArray(vao); } }