/* * Copyright © 2017 Fabian Bieler * * 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 built-in-uniform-state.c: Access uniform state in GLSL * * Set uniform state with the OpenGL API and access it in GLSL shaders. */ #include "piglit-util-gl.h" PIGLIT_GL_TEST_CONFIG_BEGIN config.supports_gl_compat_version = 20; config.window_visual = PIGLIT_GL_VISUAL_RGB; config.khr_no_error_support = PIGLIT_NO_ERRORS; PIGLIT_GL_TEST_CONFIG_END #ifdef _WIN32 #define SRAND(x) srand(x) #define DRAND() ((float)rand() / RAND_MAX) #else #define SRAND(x) srand48(x) #define DRAND() drand48() #endif static const char *vs_text = "void main() {\n" " gl_Position = gl_Vertex;\n" "}\n"; static const char *fs_float = "void main() {\n" " float epsilon = 1.0 / 256.0;\n" " vec4 green = vec4(0.0, 1.0, 0.0, 1.0);\n" " vec4 red = vec4(1.0, 0.0, 0.0, 1.0);\n" " float a = %s;\n" " float b = %f;\n" " gl_FragColor = abs(a - b) < epsilon ? green : red;\n" "}\n"; static const char *fs_vec3 = "void main() {\n" " vec3 epsilon = vec3(1.0 / 256.0);\n" " vec4 green = vec4(0.0, 1.0, 0.0, 1.0);\n" " vec4 red = vec4(1.0, 0.0, 0.0, 1.0);\n" " vec3 a = %s;\n" " vec3 b = vec3(%f, %f, %f);\n" " gl_FragColor = all(lessThan(abs(a - b), epsilon)) ? green : red;\n" "}\n"; static const char *fs_vec4 = "void main() {\n" " vec4 epsilon = vec4(1.0 / 256.0);\n" " vec4 green = vec4(0.0, 1.0, 0.0, 1.0);\n" " vec4 red = vec4(1.0, 0.0, 0.0, 1.0);\n" " vec4 a = %s;\n" " vec4 b = vec4(%f, %f, %f, %f);\n" " gl_FragColor = all(lessThan(abs(a - b), epsilon)) ? green : red;\n" "}\n"; /** * Check that the built-in shader uniform \name of type \type is equal to \val * * Since we also test for derived state involving floating point computation * don't test for strict equality but rather only check if the uniform's * components are within and espilon of their expected values. */ static bool check_shader_builtin_(const GLenum type, const float *val, const char *name) { char *fs_text; const float green[3] = {0.0, 1.0, 0.0}; switch (type) { case GL_FLOAT: asprintf(&fs_text, fs_float, name, *val); break; case GL_FLOAT_VEC3: asprintf(&fs_text, fs_vec3, name, val[0], val[1], val[2]); break; case GL_FLOAT_VEC4: asprintf(&fs_text, fs_vec4, name, val[0], val[1], val[2], val[3]); break; default: assert(0); } const GLuint program = piglit_build_simple_program(vs_text, fs_text); free(fs_text); glUseProgram(program); glDeleteProgram(program); glClear(GL_COLOR_BUFFER_BIT); piglit_draw_rect(-1, -1, 2, 2); if (piglit_probe_pixel_rgb_silent(piglit_width / 2, piglit_height / 2, green, NULL)) return true; printf("Failed uniform: '%s'.\n", name); return false; } /** * printf-like version of function above. */ static bool check_shader_builtin(const GLenum type, const float *val, const char *format, ...) PRINTFLIKE(3, 4); static bool check_shader_builtin(const GLenum type, const float *val, const char *format, ...) { char *name; va_list ap; va_start(ap, format); vasprintf(&name, format, ap); va_end(ap); const bool r = check_shader_builtin_(type, val, name); free(name); return r; } static void normalize(float *v) { const float norm = sqrtf(v[0] * v[0] + v[1] * v[1] + v[2] * v[2]); for (int i = 0; i < 3; ++i) v[i] /= norm; } static void random_vec4(float *v) { for (int i = 0; i < 4; ++i) v[i] = DRAND(); } /** * Get name fragment used in GLSL for GLenum \pname. */ static const char * enum2glsl(const GLenum pname) { switch (pname) { case GL_EMISSION: return "emission"; case GL_AMBIENT: return "ambient"; case GL_DIFFUSE: return "diffuse"; case GL_SPECULAR: return "specular"; case GL_POSITION: return "position"; case GL_SPOT_EXPONENT: return "spotExponent"; case GL_SPOT_CUTOFF: return "spotCutoff"; case GL_CONSTANT_ATTENUATION: return "constantAttenuation"; case GL_LINEAR_ATTENUATION: return "linearAttenuation"; case GL_QUADRATIC_ATTENUATION: return "quadraticAttenuation"; case GL_S: return "S"; case GL_T: return "T"; case GL_R: return "R"; case GL_Q: return "Q"; default: assert(!"unexpected pname"); return ""; } } enum piglit_result piglit_display(void) { bool pass = true; float val[4]; /* Depth range */ val[0] = DRAND(); val[1] = DRAND(); const float diff = val[1] - val[0]; glDepthRangef(val[0], val[1]); pass = check_shader_builtin(GL_FLOAT, val, "gl_DepthRange.near") && pass; pass = check_shader_builtin(GL_FLOAT, &val[1], "gl_DepthRange.far") && pass; pass = check_shader_builtin(GL_FLOAT, &diff, "gl_DepthRange.diff") && pass; /* Clip planes */ int max_clip_planes; glGetIntegerv(GL_MAX_CLIP_PLANES, &max_clip_planes); for (int cp = 0; cp < max_clip_planes; ++cp) { double vald[4]; for (int c = 0; c < 4; ++c) vald[c] = val[c] = DRAND(); glClipPlane(GL_CLIP_PLANE0 + cp, vald); pass = check_shader_builtin(GL_FLOAT_VEC4, val, "gl_ClipPlane[%d]", cp) && pass; } /* Point Size */ val[0] = DRAND(); glPointSize(val[0]); pass = check_shader_builtin(GL_FLOAT, val, "gl_Point.size") && pass; val[0] = DRAND(); glPointParameterf(GL_POINT_SIZE_MIN, val[0]); pass = check_shader_builtin(GL_FLOAT, val, "gl_Point.sizeMin") && pass; val[0] = DRAND(); glPointParameterf(GL_POINT_SIZE_MAX, val[0]); pass = check_shader_builtin(GL_FLOAT, val, "gl_Point.sizeMax") && pass; val[0] = DRAND(); glPointParameterf(GL_POINT_FADE_THRESHOLD_SIZE, val[0]); pass = check_shader_builtin(GL_FLOAT, val, "gl_Point.fadeThresholdSize") && pass; val[0] = DRAND(); val[1] = DRAND(); val[2] = DRAND(); glPointParameterfv(GL_POINT_DISTANCE_ATTENUATION, val); pass = check_shader_builtin(GL_FLOAT, &val[0], "gl_Point.distanceConstantAttenuation") && pass; pass = check_shader_builtin(GL_FLOAT, &val[1], "gl_Point.distanceLinearAttenuation") && pass; pass = check_shader_builtin( GL_FLOAT, &val[2], "gl_Point.distanceQuadraticAttenuation") && pass; /* Material State */ for (int s = 0; s < 2; ++s) { for (int p = 0; p < 4; ++p) { const GLenum pname[] = {GL_EMISSION, GL_AMBIENT, GL_DIFFUSE, GL_SPECULAR}; random_vec4(val); glMaterialfv(GL_FRONT + s, pname[p], val); pass = check_shader_builtin(GL_FLOAT_VEC4, val, "gl_%sMaterial.%s", s ? "Back" : "Front", enum2glsl(pname[p])) && pass; } val[0] = DRAND(); glMaterialf(GL_FRONT + s, GL_SHININESS, val[0]); pass = check_shader_builtin(GL_FLOAT, val, "gl_%sMaterial.shininess", s ? "Back" : "Front") && pass; } /* Light State */ int max_lights; glGetIntegerv(GL_MAX_LIGHTS, &max_lights); for (int l = 0; l < max_lights; ++l) { for (int p = 0; p < 4; ++p) { const GLenum pname[] = {GL_AMBIENT, GL_DIFFUSE, GL_SPECULAR, GL_POSITION}; random_vec4(val); glLightfv(GL_LIGHT0 + l, pname[p], val); pass = check_shader_builtin(GL_FLOAT_VEC4, val, "gl_LightSource[%d].%s", l, enum2glsl(pname[p])) && pass; } random_vec4(val); glLightfv(GL_LIGHT0 + l, GL_SPOT_DIRECTION, val); pass = check_shader_builtin( GL_FLOAT_VEC3, val, "gl_LightSource[%d].spotDirection", l) && pass; for (int p = 0; p < 5; ++p) { const GLenum pname[] = {GL_SPOT_EXPONENT, GL_SPOT_CUTOFF, GL_CONSTANT_ATTENUATION, GL_LINEAR_ATTENUATION, GL_QUADRATIC_ATTENUATION}; val[0] = DRAND(); glLightf(GL_LIGHT0 + l, pname[p], val[0]); pass = check_shader_builtin(GL_FLOAT, val, "gl_LightSource[%d].%s", l, enum2glsl(pname[p])) && pass; } val[0] = DRAND() * 90; glLightf(GL_LIGHT0 + l, GL_SPOT_CUTOFF, val[0]); const float cos_cutoff = cosf(val[0] / 180 * M_PI); pass = check_shader_builtin( GL_FLOAT, &cos_cutoff, "gl_LightSource[%d].spotCosCutoff", l) && pass; for (int c = 0; c < 3; ++c) val[c] = DRAND(); val[3] = 1; glLightfv(GL_LIGHT0 + l, GL_POSITION, val); normalize(val); val[2] += 1; normalize(val); pass = check_shader_builtin(GL_FLOAT_VEC4, val, "gl_LightSource[%d].halfVector", l) && pass; } random_vec4(val); glLightModelfv(GL_LIGHT_MODEL_AMBIENT, val); pass = check_shader_builtin(GL_FLOAT_VEC4, val, "gl_LightModel.ambient") && pass; /* Derived state from products of light and material. */ for (int s = 0; s < 2; ++s) { float scene_color[4]; for (int c = 0; c < 4; ++c) scene_color[c] = val[c] = DRAND(); glMaterialfv(GL_FRONT + s, GL_AMBIENT, val); for (int c = 0; c < 4; ++c) scene_color[c] *= val[c] = DRAND(); glLightModelfv(GL_LIGHT_MODEL_AMBIENT, val); for (int c = 0; c < 4; ++c) scene_color[c] += val[c] = DRAND(); glMaterialfv(GL_FRONT + s, GL_EMISSION, val); /* Page 63 (77 of the PDF) of the OpenGL 2.0 spec says: * * "The value of A produced by lighting is the alpha * value associated with d_{cm}." * * I'm not sure if this applies to the scene color, but both * Mesa and the NVIDIA driver do this. */ random_vec4(val); glMaterialfv(GL_FRONT + s, GL_DIFFUSE, val); scene_color[3] = val[3]; pass = check_shader_builtin( GL_FLOAT_VEC4, scene_color, s ? "gl_BackLightModelProduct.sceneColor" : "gl_FrontLightModelProduct.sceneColor") && pass; } for (int s = 0; s < 2; ++s) { for (int l = 0; l < max_lights; ++l) { const GLenum pname[] = {GL_AMBIENT, GL_DIFFUSE, GL_SPECULAR}; for (int p = 0; p < 3; ++p) { float light_product[4]; for (int c = 0; c < 4; ++c) light_product[c] = val[c] = DRAND(); glLightfv(GL_LIGHT0 + l, pname[p], val); for (int c = 0; c < 4; ++c) light_product[c] *= val[c] = DRAND(); glMaterialfv(GL_FRONT + s, pname[p], val); /* XXX: I have no Idea where the spec says the * alpha value of the light product is the * material's alpha value, but both Mesa and * the NVIDIA driver do this. */ light_product[3] = val[3]; pass = check_shader_builtin( GL_FLOAT_VEC4, light_product, "gl_%sLightProduct[%d].%s", s ? "Back" : "Front", l, enum2glsl(pname[p])) && pass; } } } /* Texture Environment and Generation */ int max_tu; glGetIntegerv(GL_MAX_TEXTURE_UNITS, &max_tu); int max_tiu; glGetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS, &max_tiu); const int max_textures = MIN2(max_tu, max_tiu); for (int t = 0; t < max_textures; ++t) { glActiveTexture(GL_TEXTURE0 + t); random_vec4(val); glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, val); pass = check_shader_builtin(GL_FLOAT_VEC4, val, "gl_TextureEnvColor[%d]", t) && pass; } int max_texture_coords; glGetIntegerv(GL_MAX_TEXTURE_COORDS, &max_texture_coords); for (int t = 0; t < max_texture_coords; ++t) { const GLenum coord[] = {GL_S, GL_T, GL_R, GL_Q}; glActiveTexture(GL_TEXTURE0 + t); for (int co = 0; co < 4; ++co) { const GLenum plane[] = {GL_EYE_PLANE, GL_OBJECT_PLANE}; const char *plane_name[] = {"Eye", "Object"}; for (int pl = 0; pl < 2; ++pl) { random_vec4(val); glTexGenfv(coord[co], plane[pl], val); pass = check_shader_builtin( GL_FLOAT_VEC4, val, "gl_%sPlane%s[%d]", plane_name[pl], enum2glsl(coord[co]), t) && pass; } } } /* Fog */ random_vec4(val); glFogfv(GL_FOG_COLOR, val); pass = check_shader_builtin(GL_FLOAT_VEC4, val, "gl_Fog.color") && pass; val[0] = DRAND(); glFogf(GL_FOG_DENSITY, val[0]); pass = check_shader_builtin(GL_FLOAT, val, "gl_Fog.density") && pass; val[1] = DRAND(); glFogf(GL_FOG_START, val[1]); pass = check_shader_builtin(GL_FLOAT, &val[1], "gl_Fog.start") && pass; val[2] = DRAND(); glFogf(GL_FOG_END, val[2]); pass = check_shader_builtin(GL_FLOAT, &val[2], "gl_Fog.end") && pass; const float fog_scale = 1 / (val[2] - val[1]); pass = check_shader_builtin(GL_FLOAT, &fog_scale, "gl_Fog.scale") && pass; return pass ? PIGLIT_PASS : PIGLIT_FAIL; } void piglit_init(int argc, char **argv) { SRAND(17); }