/* * 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 common.c * * Common utility functions for the ARB_shader_image_load_store tests. */ #include "common.h" bool set_uniform_int(GLuint prog, const char *name, int value) { int loc, last_prog; loc = glGetUniformLocation(prog, name); if (loc < 0) return true; glGetIntegerv(GL_CURRENT_PROGRAM, &last_prog); if (prog != last_prog) glUseProgram(prog); glUniform1i(loc, value); return piglit_check_gl_error(GL_NO_ERROR); } static GLuint *textures, *buffers; static unsigned num_textures, num_buffers; GLuint get_texture(unsigned unit) { if (unit >= num_textures) { const unsigned n = unit + 1; textures = realloc(textures, sizeof(GLuint) * n); memset(&textures[num_textures], 0, sizeof(GLuint) * (n - num_textures)); num_textures = n; } return textures[unit]; } GLuint get_buffer(unsigned unit) { if (unit >= num_buffers) { const unsigned n = unit + 1; buffers = realloc(buffers, sizeof(GLuint) * n); memset(&buffers[num_buffers], 0, sizeof(GLuint) * (n - num_buffers)); num_buffers = n; } return buffers[unit]; } static GLuint fb[2], cb[2], zb[2]; static GLfloat vp[2][4]; static bool generate_fb(const struct grid_info grid, unsigned idx) { if (!fb[idx]) { glGenFramebuffers(1, &fb[idx]); glGenRenderbuffers(1, &cb[idx]); glGenRenderbuffers(1, &zb[idx]); } glBindFramebuffer(GL_FRAMEBUFFER, fb[idx]); glBindRenderbuffer(GL_RENDERBUFFER, cb[idx]); glRenderbufferStorage(GL_RENDERBUFFER, grid.format->format, grid.size.x, grid.size.y); glFramebufferRenderbuffer(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, cb[idx]); glBindRenderbuffer(GL_RENDERBUFFER, zb[idx]); glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT32F, grid.size.x, grid.size.y); glFramebufferRenderbuffer(GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, zb[idx]); vp[idx][0] = 0; vp[idx][1] = 0; vp[idx][2] = grid.size.x; vp[idx][3] = grid.size.y; glViewportIndexedfv(0, vp[idx]); return piglit_check_gl_error(GL_NO_ERROR); } bool upload_image(const struct image_info img, unsigned unit, const uint32_t *pixels) { return upload_image_levels(img, 1, 0, unit, pixels); } bool upload_image_levels(const struct image_info img, unsigned num_levels, unsigned level, unsigned unit, const uint32_t *pixels) { const unsigned m = image_num_components(img.format); int i, l; if (get_texture(unit)) { glDeleteTextures(1, &textures[unit]); textures[unit] = 0; } if (get_buffer(unit)) { glDeleteBuffers(1, &buffers[unit]); buffers[unit] = 0; } glGenTextures(1, &textures[unit]); glBindTexture(img.target->target, textures[unit]); switch (img.target->target) { case GL_TEXTURE_1D: for (l = 0; l < num_levels; ++l) { const struct image_extent size = image_level_size(img, l); glTexImage1D(GL_TEXTURE_1D, l, img.format->format, size.x, 0, img.format->pixel_format, image_base_type(img.format), &pixels[m * image_level_offset(img, l)]); } break; case GL_TEXTURE_2D: for (l = 0; l < num_levels; ++l) { const struct image_extent size = image_level_size(img, l); glTexImage2D(GL_TEXTURE_2D, l, img.format->format, size.x, size.y, 0, img.format->pixel_format, image_base_type(img.format), &pixels[m * image_level_offset(img, l)]); } break; case GL_TEXTURE_3D: for (l = 0; l < num_levels; ++l) { const struct image_extent size = image_level_size(img, l); glTexImage3D(GL_TEXTURE_3D, l, img.format->format, size.x, size.y, size.z, 0, img.format->pixel_format, image_base_type(img.format), &pixels[m * image_level_offset(img, l)]); } break; case GL_TEXTURE_RECTANGLE: assert(num_levels == 1); glTexImage2D(GL_TEXTURE_RECTANGLE, 0, img.format->format, img.size.x, img.size.y, 0, img.format->pixel_format, image_base_type(img.format), pixels); break; case GL_TEXTURE_CUBE_MAP: for (l = 0; l < num_levels; ++l) { const unsigned offset = m * image_level_offset(img, l); const struct image_extent size = image_level_size(img, l); const unsigned face_sz = m * product(size) / 6; for (i = 0; i < 6; ++i) glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, l, img.format->format, size.x, size.y, 0, img.format->pixel_format, image_base_type(img.format), &pixels[offset + face_sz * i]); } break; case GL_TEXTURE_BUFFER: { /* * glTexImage*() isn't supposed to work with buffer * textures. We copy the unpacked pixels to a texture * with the desired internal format to let the GL pack * them for us. */ const struct image_extent grid = image_optimal_extent(img.size); GLuint packed_tex; assert(num_levels == 1); glGenBuffers(1, &buffers[unit]); glBindBuffer(GL_PIXEL_PACK_BUFFER, buffers[unit]); glBufferData(GL_PIXEL_PACK_BUFFER, img.size.x * image_pixel_size(img.format) / 8, NULL, GL_STATIC_DRAW); glGenTextures(1, &packed_tex); glBindTexture(GL_TEXTURE_2D, packed_tex); glTexImage2D(GL_TEXTURE_2D, 0, img.format->format, grid.x, grid.y, 0, img.format->pixel_format, image_base_type(img.format), pixels); glGetTexImage(GL_TEXTURE_2D, 0, img.format->pixel_format, img.format->pixel_type, NULL); glDeleteTextures(1, &packed_tex); glBindBuffer(GL_PIXEL_PACK_BUFFER, 0); glTexBuffer(GL_TEXTURE_BUFFER, image_compat_format(img.format), buffers[unit]); break; } case GL_TEXTURE_1D_ARRAY: for (l = 0; l < num_levels; ++l) { const struct image_extent size = image_level_size(img, l); glTexImage2D(GL_TEXTURE_1D_ARRAY, l, img.format->format, size.x, size.y, 0, img.format->pixel_format, image_base_type(img.format), &pixels[m * image_level_offset(img, l)]); } break; case GL_TEXTURE_2D_ARRAY: for (l = 0; l < num_levels; ++l) { const struct image_extent size = image_level_size(img, l); glTexImage3D(GL_TEXTURE_2D_ARRAY, l, img.format->format, size.x, size.y, size.z, 0, img.format->pixel_format, image_base_type(img.format), &pixels[m * image_level_offset(img, l)]); } break; case GL_TEXTURE_CUBE_MAP_ARRAY: for (l = 0; l < num_levels; ++l) { const struct image_extent size = image_level_size(img, l); glTexImage3D(GL_TEXTURE_CUBE_MAP_ARRAY, l, img.format->format, size.x, size.y, size.z, 0, img.format->pixel_format, image_base_type(img.format), &pixels[m * image_level_offset(img, l)]); } break; case GL_TEXTURE_2D_MULTISAMPLE: case GL_TEXTURE_2D_MULTISAMPLE_ARRAY: { /* * GL doesn't seem to provide any direct way to * initialize a multisample texture, so we use * imageStore() to render to it from the fragment * shader copying the contents of a larger * single-sample 2D texture. */ const struct grid_info grid = { get_image_stage(GL_FRAGMENT_SHADER)->bit, img.format, image_optimal_extent(img.size) }; GLuint prog = generate_program( grid, GL_FRAGMENT_SHADER, concat(image_hunk(image_info_for_grid(grid), "SRC_"), image_hunk(img, "DST_"), hunk("readonly SRC_IMAGE_UNIFORM_T src_img;\n" "writeonly DST_IMAGE_UNIFORM_T dst_img;\n" "\n" "GRID_T op(ivec2 idx, GRID_T x) {\n" " imageStore(dst_img, DST_IMAGE_ADDR(idx),\n" " imageLoad(src_img, SRC_IMAGE_ADDR(idx)));\n" " return x;\n" "}\n"), NULL)); bool ret = prog && generate_fb(grid, 1); GLuint tmp_tex; assert(num_levels == 1); glGenTextures(1, &tmp_tex); glBindTexture(GL_TEXTURE_2D, tmp_tex); if (img.target->target == GL_TEXTURE_2D_MULTISAMPLE_ARRAY) { glTexImage3DMultisample(GL_TEXTURE_2D_MULTISAMPLE_ARRAY, img.size.x, img.format->format, img.size.y, img.size.z, img.size.w, GL_FALSE); } else { glTexImage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE, img.size.x, img.format->format, img.size.y, img.size.z, GL_FALSE); } glTexImage2D(GL_TEXTURE_2D, 0, img.format->format, grid.size.x, grid.size.y, 0, img.format->pixel_format, image_base_type(img.format), pixels); glBindImageTexture(unit, textures[unit], 0, GL_TRUE, 0, GL_WRITE_ONLY, img.format->format); glBindImageTexture(6, tmp_tex, 0, GL_TRUE, 0, GL_READ_ONLY, img.format->format); ret &= set_uniform_int(prog, "src_img", 6) && set_uniform_int(prog, "dst_img", unit) && draw_grid(grid, prog); glDeleteProgram(prog); glDeleteTextures(1, &tmp_tex); glBindFramebuffer(GL_FRAMEBUFFER, fb[0]); glViewportIndexedfv(0, vp[0]); glMemoryBarrier(GL_SHADER_IMAGE_ACCESS_BARRIER_BIT); if (!ret) return false; break; } default: abort(); } glBindImageTexture(unit, textures[unit], level, GL_TRUE, 0, GL_READ_WRITE, img.format->format); return piglit_check_gl_error(GL_NO_ERROR); } bool download_image(const struct image_info img, unsigned unit, uint32_t *r_pixels) { return download_image_levels(img, 1, unit, r_pixels); } bool download_image_levels(const struct image_info img, unsigned num_levels, unsigned unit, uint32_t *r_pixels) { const unsigned m = image_num_components(img.format); int i, l; glMemoryBarrier(GL_TEXTURE_UPDATE_BARRIER_BIT | GL_BUFFER_UPDATE_BARRIER_BIT | GL_PIXEL_BUFFER_BARRIER_BIT | GL_SHADER_IMAGE_ACCESS_BARRIER_BIT); glBindTexture(img.target->target, textures[unit]); switch (img.target->target) { case GL_TEXTURE_1D: case GL_TEXTURE_2D: case GL_TEXTURE_3D: case GL_TEXTURE_RECTANGLE: case GL_TEXTURE_1D_ARRAY: case GL_TEXTURE_2D_ARRAY: case GL_TEXTURE_CUBE_MAP_ARRAY: assert(img.target->target != GL_TEXTURE_RECTANGLE || num_levels == 1); for (l = 0; l < num_levels; ++l) glGetTexImage(img.target->target, l, img.format->pixel_format, image_base_type(img.format), &r_pixels[m * image_level_offset(img, l)]); break; case GL_TEXTURE_CUBE_MAP: for (l = 0; l < num_levels; ++l) { const unsigned offset = m * image_level_offset(img, l); const unsigned face_sz = m * product(image_level_size(img, l)) / 6; for (i = 0; i < 6; ++i) glGetTexImage(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, l, img.format->pixel_format, image_base_type(img.format), &r_pixels[offset + face_sz * i]); } break; case GL_TEXTURE_BUFFER: { /* * glGetTexImage() isn't supposed to work with buffer * textures. We copy the packed pixels to a texture * with the same internal format as the image to let * the GL unpack it for us. */ const struct image_extent grid = image_optimal_extent(img.size); GLuint packed_tex; assert(num_levels == 1); glGenTextures(1, &packed_tex); glBindTexture(GL_TEXTURE_2D, packed_tex); glBindBuffer(GL_PIXEL_UNPACK_BUFFER, buffers[unit]); glTexImage2D(GL_TEXTURE_2D, 0, img.format->format, grid.x, grid.y, 0, img.format->pixel_format, img.format->pixel_type, NULL); glGetTexImage(GL_TEXTURE_2D, 0, img.format->pixel_format, image_base_type(img.format), r_pixels); glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); glDeleteTextures(1, &packed_tex); break; } case GL_TEXTURE_2D_MULTISAMPLE: case GL_TEXTURE_2D_MULTISAMPLE_ARRAY: { /* * GL doesn't seem to provide any direct way to read * back a multisample texture, so we use imageLoad() * to copy its contents to a larger single-sample 2D * texture from the fragment shader. */ const struct grid_info grid = { get_image_stage(GL_FRAGMENT_SHADER)->bit, img.format, image_optimal_extent(img.size) }; GLuint prog = generate_program( grid, GL_FRAGMENT_SHADER, concat(image_hunk(img, "SRC_"), image_hunk(image_info_for_grid(grid), "DST_"), hunk("readonly SRC_IMAGE_UNIFORM_T src_img;\n" "writeonly DST_IMAGE_UNIFORM_T dst_img;\n" "\n" "GRID_T op(ivec2 idx, GRID_T x) {\n" " imageStore(dst_img, DST_IMAGE_ADDR(idx),\n" " imageLoad(src_img, SRC_IMAGE_ADDR(idx)));\n" " return x;\n" "}\n"), NULL)); bool ret = prog && generate_fb(grid, 1); GLuint tmp_tex; assert(num_levels == 1); glGenTextures(1, &tmp_tex); glBindTexture(GL_TEXTURE_2D, tmp_tex); glTexImage2D(GL_TEXTURE_2D, 0, img.format->format, grid.size.x, grid.size.y, 0, img.format->pixel_format, image_base_type(img.format), NULL); glBindImageTexture(unit, textures[unit], 0, GL_TRUE, 0, GL_READ_ONLY, img.format->format); glBindImageTexture(6, tmp_tex, 0, GL_TRUE, 0, GL_WRITE_ONLY, img.format->format); ret &= set_uniform_int(prog, "src_img", unit) && set_uniform_int(prog, "dst_img", 6) && draw_grid(grid, prog); glMemoryBarrier(GL_TEXTURE_UPDATE_BARRIER_BIT); glGetTexImage(GL_TEXTURE_2D, 0, img.format->pixel_format, image_base_type(img.format), r_pixels); glDeleteProgram(prog); glDeleteTextures(1, &tmp_tex); glBindFramebuffer(GL_FRAMEBUFFER, fb[0]); glViewportIndexedfv(0, vp[0]); if (!ret) return false; break; } default: abort(); } return piglit_check_gl_error(GL_NO_ERROR); } bool init_pixels(const struct image_info img, uint32_t *r_pixels, double r, double g, double b, double a) { const double init[4] = { r, g, b, a }; const unsigned m = image_num_components(img.format); unsigned i, j; for (i = 0; i < product(img.size); ++i) { for (j = 0; j < m; ++j) { r_pixels[i * m + j] = encode(img.format, init[j]); } } return true; } static bool check_pixels_vs(const struct image_info img, unsigned stride, const uint32_t *pixels, const uint32_t *expect) { const unsigned m = image_num_components(img.format); unsigned i, j; for (i = 0; i < product(img.size); ++i) { const uint32_t *v = &pixels[m * i]; const uint32_t *u = &expect[stride * m * i]; for (j = 0; j < m; ++j) { if ((fabs(decode(img.format, v[j]) - decode(img.format, u[j])) > get_idx(img.epsilon, j)) && isfinite(decode(img.format, u[j]))) { printf("Probe value at (%u, %u, %u, %u)\n", i % img.size.x, i / img.size.x % img.size.y, i / img.size.x / img.size.y % img.size.z, i / img.size.x / img.size.y / img.size.z); printf(" Expected:"); for (j = 0; j < m; ++j) printf(" %f", decode(img.format, u[j])); printf("\n Observed:"); for (j = 0; j < m; ++j) printf(" %f", decode(img.format, v[j])); printf("\n"); return false; } } } return true; } bool check_pixels(const struct image_info img, const uint32_t *pixels, double r, double g, double b, double a) { const uint32_t expect[4] = { encode(img.format, r), encode(img.format, g), encode(img.format, b), encode(img.format, a) }; return check_pixels_vs(img, 0, pixels, expect); } bool check_pixels_v(const struct image_info img, const uint32_t *pixels, const uint32_t *expect) { return check_pixels_vs(img, 1, pixels, expect); } bool init_fb(const struct grid_info grid) { bool ret = true; if (grid.stages & GL_COMPUTE_SHADER_BIT) { const struct image_info img = image_info_for_grid(grid); const unsigned n = product(grid.size) * image_num_components(grid.format); uint32_t *pixels = malloc(n * sizeof(*pixels)); ret = init_pixels(img, pixels, 0.5, 0.5, 0.5, 0.5) && upload_image(img, max_image_units(), pixels); free(pixels); } else { ret = generate_fb(grid, 0); glClearColor(0.5, 0.5, 0.5, 0.5); glClear(GL_COLOR_BUFFER_BIT); glClearDepth(0.5); glClear(GL_DEPTH_BUFFER_BIT); } return ret; } bool download_result(const struct grid_info grid, uint32_t *r_pixels) { if (grid.stages & GL_COMPUTE_SHADER_BIT) { /* No actual framebuffer. Results are returned into * an image. */ return download_image(image_info_for_grid(grid), max_image_units(), r_pixels); } else { glReadPixels(0, 0, grid.size.x, grid.size.y, grid.format->pixel_format, image_base_type(grid.format), r_pixels); return piglit_check_gl_error(GL_NO_ERROR); } }