1 /*
2 * Copyright © 2011 Intel Corporation
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software"),
6 * to deal in the Software without restriction, including without limitation
7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 * and/or sell copies of the Software, and to permit persons to whom the
9 * Software is furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice (including the next
12 * paragraph) shall be included in all copies or substantial portions of the
13 * Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
18 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
21 * DEALINGS IN THE SOFTWARE.
22 */
23
24 /**
25 * \file clipping-transforms.c
26 *
27 * This test verifies that clip planes are transformed using the
28 * correct matrices, at the correct times.
29 *
30 * The transformations affecting clipping in fixed functionality mode
31 * (with no vertex shader) are described in the OpenGL 2.1 spec,
32 * section 2.12 ("Clipping"):
33 *
34 * "A client-defined clip plane is specified with
35 *
36 * void ClipPlane( enum p, double eqn[4] );
37 *
38 * ... eqn is an array of four double-precision floating-point
39 * values. These are the coefficients of a plane equation in
40 * object coordinates: p1 , p2 , p3 , and p4 (in that order). The
41 * inverse of the current model-view matrix is applied to these
42 * coefficients, at the time they are specified, yielding
43 *
44 * (p1' p2' p3' p4') = (p1 p2 p3 p4) M^-1
45 *
46 * (where M is the current model-view matrix; the resulting plane
47 * equation is undefined if M is singular and may be inaccurate if
48 * M is poorly-conditioned) to obtain the plane equation
49 * coefficients in eye coordinates. All points with eye
50 * coordinates (xe ye ze we)^T that satisfy
51 *
52 * (p1' p2' p3' p4') (xe ye ze we)^T >= 0
53 *
54 * lie in the half-space defined by the plane; points that do not
55 * satisfy this condition do not lie in the half-space."
56 *
57 * Thus, the clip planes should be modified by the value of the
58 * model-view matrix at the time clip planes are specified; the value
59 * of the model-view transformation at drawing time should have no
60 * effect on which part of the scene is clipped.
61 *
62 * The projection matrix, on the other hand, should be the opposite:
63 * its value at the time clip planes are specified should have no
64 * effect, but its value at drawing time should determine where on the
65 * screen clipping takes place (since clipping is performed on eye
66 * coordinates, before the perspective matrix is applied).
67 *
68 * The transformations affecting clipping when a vertex shader is
69 * present can be inferred from the text that follows:
70 *
71 * "When a vertex shader is active, the vector (xe ye ze we)^T is
72 * no longer computed. Instead, the value of the gl_ClipVertex
73 * built-in variable is used in its place."
74 *
75 * So, as before, the model-view matrix affects clip planes at the
76 * time they are specified, but not at draw time. However, the
77 * projection matrix no longer necessarily has an effect; instead, the
78 * place on the screen where clipping takes place is determined by the
79 * relationship between the values of gl_Position and gl_ClipVertex
80 * that are output by the vertex shader.
81 *
82 * It's also possible that the vertex shader might not store a value
83 * in gl_ClipVertex at all; what happens in this case is less clear.
84 * According to the GL 2.1 spec (from the same section):
85 *
86 * "If gl ClipVertex is not written by the vertex shader, its
87 * value is undefined, which implies that the results of clipping
88 * to any client-defined clip planes are also undefined."
89 *
90 * The GL 3.0 spec says the same thing, and the GLSL 1.10 and 1.20
91 * specs have compatible language (from section 7.1: Vertex Shader
92 * Special Variables):
93 *
94 * "If gl_PointSize or gl_ClipVertex are not written to, their
95 * values are undefined."
96 *
97 * However, the GLSL 1.30 spec says:
98 *
99 * "If a linked set of shaders forming the vertex stage contains
100 * no static write to gl_ClipVertex or gl_ClipDistance, but the
101 * application has requested clipping against user clip planes
102 * through the API, then the coordinate written to gl_Position is
103 * used for comparison against the user clip planes."
104 *
105 * So, if GLSL 1.30 is to be believed, if the vertex shader does not
106 * write to gl_Position, then the place on the screen where clipping
107 * takes place is determined exclusively by the plane equation (p1'
108 * p2' p3' p4'). No further transformation is applied.
109 *
110 * Note that strictly speaking, this doesn't contradict any of the
111 * other specs, since a conformant implementation may do anything it
112 * desires when behavior is "undefined", including clipping based on
113 * gl_Position. Since this behavior is only specified in GLSL 1.30,
114 * we include a "#version 130" directive in the shader when testing
115 * it.
116 *
117 *
118 * The test operates by constructing four clip plane equations which
119 * are only satisfied by points within a small square region near (1,
120 * 0). Setting all matrices to the identity matrix, and setting
121 * gl_Position == gl_ClipVertex == gl_Vertex, it draws a large square,
122 * large enough to cover the entire window, and then probes the
123 * resulting image to determine where pixels were actually drawn; due
124 * to clipping, they should be drawn only near (1, 0).
125 *
126 * Then it performs a 20 degree rotation in each of the following ways
127 * in turn, leaving all other transformations as the identity
128 * transformation:
129 * - Using the model-view matrix at the time clip planes are specified
130 * - Using the projection matrix at the time clip planes are specified
131 * - Using the model-view matrix at the time of drawing
132 * - Using the projection matrix at the time of drawing
133 * - Using the vertex shader to rotate gl_Position with respect to gl_Vertex
134 * - Using the vertex shader to rotate gl_ClipVertex with respect to gl_Vertex
135 *
136 * In each case it probes the resulting image to determine where
137 * pixels were actually drawn, and compares the result to the expected
138 * behavior from the spec.
139 *
140 *
141 * The test may be run in one of four modes, chosen with a single
142 * command line argument:
143 * - "fixed": test using fixed functionality (no vertex shader)
144 * - "arb": test using GL_ARB_vertex_program extension (see below)
145 * - "pos": test using a vertex shader that sets gl_Position only
146 * - "pos_clipvert": test using a vertex shader that sets gl_Position first,
147 * then gl_ClipVertex
148 * - "clipvert_pos": test using a vertex shader that sets gl_ClipVertex first,
149 * then gl_Position
150 *
151 * The reason for distinguishing between "pos_clipvert" and
152 * "clipvert_pos" is that in the present Mesa implementation, the
153 * variables gl_Position and gl_ClipVertex are aliases of each other,
154 * so the order in which values are stored into these two variables
155 * may affect shader behavior.
156 *
157 * Note: "arb" mode tests using an ARB vertex program, as defined in
158 * the GL_ARB_vertex_program extension. From the extension spec:
159 *
160 * "User-defined clipping is not supported in standard vertex
161 * program mode. User-defined clipping support will be provided
162 * for programs that use the "position invariant" option, where
163 * all vertex transformation operations are performed by the
164 * fixed-function pipeline."
165 *
166 * The strong implication seems to be that for ARB vertex programs
167 * that use the "position invariant" option, clipping should behave as
168 * it does in fixed function mode.
169 */
170
171 #include "piglit-util-gl.h"
172
173 PIGLIT_GL_TEST_CONFIG_BEGIN
174
175 config.supports_gl_compat_version = 10;
176
177 config.window_visual = PIGLIT_GL_VISUAL_RGB | PIGLIT_GL_VISUAL_DOUBLE;
178
179 PIGLIT_GL_TEST_CONFIG_END
180
181 GLint position_angle_loc;
182 GLint clipVertex_angle_loc;
183 bool use_ff = false;
184 bool use_arb = false;
185 bool use_glsl = false;
186 bool use_clip_vertex = false;
187 bool use_glsl_130 = false;
188
189 /**
190 * GLSL code used to set gl_Position and/or gl_ClipVertex in the
191 * vertex shader.
192 */
193 char *setters;
194
195 void
setup_glsl_programs()196 setup_glsl_programs()
197 {
198 GLuint prog;
199
200 char vert[4096];
201 char frag[4096];
202 char *version_directive;
203
204 if (use_glsl_130) {
205 version_directive = "#version 130";
206 } else {
207 version_directive = "";
208 }
209
210 sprintf(vert,
211 "%s\n"
212 "uniform float position_angle;\n"
213 "uniform float clipVertex_angle;\n"
214 "mat4 rotate(float angle)\n"
215 "{\n"
216 " angle = radians(angle);\n"
217 " return mat4( cos(angle), sin(angle), 0.0, 0.0,\n"
218 " -sin(angle), cos(angle), 0.0, 0.0,\n"
219 " 0.0, 0.0, 1.0, 0.0,\n"
220 " 0.0, 0.0, 0.0, 1.0);\n"
221 "}\n"
222 "void main()\n"
223 "{\n"
224 "%s\n"
225 "}",
226 version_directive, setters);
227 sprintf(frag,
228 "%s\n"
229 "void main()\n"
230 "{\n"
231 " gl_FragColor = vec4(1.0);\n"
232 "}",
233 version_directive);
234
235 prog = piglit_build_simple_program(vert, frag);
236 glUseProgram(prog);
237 position_angle_loc = glGetUniformLocation(prog, "position_angle");
238 if (use_clip_vertex) {
239 clipVertex_angle_loc =
240 glGetUniformLocation(prog, "clipVertex_angle");
241 }
242 }
243
244 void
setup_arb_program()245 setup_arb_program()
246 {
247 char vert[] =
248 "!!ARBvp1.0\n"
249 "OPTION ARB_position_invariant;\n"
250 "MOV result.color, { 1.0, 1.0, 1.0, 1.0 };"
251 "END";
252 GLuint vert_prog;
253
254 glGenProgramsARB(1, &vert_prog);
255 glBindProgramARB(GL_VERTEX_PROGRAM_ARB, vert_prog);
256 glProgramStringARB(GL_VERTEX_PROGRAM_ARB, GL_PROGRAM_FORMAT_ASCII_ARB,
257 strlen(vert), vert);
258 glEnable(GL_VERTEX_PROGRAM_ARB);
259 }
260
261 void
print_usage_and_exit(char * prog_name)262 print_usage_and_exit(char *prog_name)
263 {
264 printf("Usage: %s <mode>\n"
265 " where <mode> is one of:\n"
266 " fixed\n"
267 " arb\n"
268 " pos\n"
269 " pos_clipvert\n"
270 " clipvert_pos\n", prog_name);
271 exit(1);
272 }
273
274 void
piglit_init(int argc,char ** argv)275 piglit_init(int argc, char **argv)
276 {
277 if (argc != 2)
278 print_usage_and_exit(argv[0]);
279 if (strcmp(argv[1], "fixed") == 0) {
280 use_ff = true;
281 } else if (strcmp(argv[1], "arb") == 0) {
282 use_arb = true;
283 } else if (strcmp(argv[1], "pos") == 0) {
284 use_glsl = true;
285 setters = " gl_Position = rotate(position_angle) * gl_Vertex;\n";
286 use_glsl_130 = true;
287 } else if (strcmp(argv[1], "pos_clipvert") == 0) {
288 use_glsl = true;
289 setters =
290 " gl_Position = rotate(position_angle) * gl_Vertex;\n"
291 " gl_ClipVertex = rotate(clipVertex_angle) * gl_Vertex;\n";
292 use_clip_vertex = true;
293 } else if (strcmp(argv[1], "clipvert_pos") == 0) {
294 use_glsl = true;
295 setters =
296 " gl_ClipVertex = rotate(clipVertex_angle) * gl_Vertex;\n"
297 " gl_Position = rotate(position_angle) * gl_Vertex;\n";
298 use_clip_vertex = true;
299 } else {
300 print_usage_and_exit(argv[0]);
301 }
302
303 if (use_arb) {
304 piglit_require_extension("GL_ARB_vertex_program");
305 setup_arb_program();
306 } else if (use_glsl) {
307 piglit_require_GLSL();
308 piglit_require_GLSL_version(use_glsl_130 ? 130 : 110);
309 setup_glsl_programs();
310 }
311 }
312
313 void
setup_clip_plane(int plane,float p1,float p2,float p3,float p4)314 setup_clip_plane(int plane, float p1, float p2, float p3, float p4)
315 {
316 double eqn[4] = { p1, p2, p3, p4 };
317 glClipPlane(GL_CLIP_PLANE0 + plane, eqn);
318 }
319
320 bool
measure_effects(char * desc,int mc,int pc,int md,int pd,int expected)321 measure_effects(char *desc, int mc, int pc, int md, int pd, int expected)
322 {
323 float size = 0.1;
324 float dist = 1.0 - size/2;
325
326 int angle;
327
328 printf("Measuring %s: ", desc);
329
330 glClear(GL_COLOR_BUFFER_BIT);
331
332 glMatrixMode(GL_MODELVIEW);
333 glLoadIdentity();
334 glRotatef(mc, 0, 0, 1);
335
336 glMatrixMode(GL_PROJECTION);
337 glLoadIdentity();
338 glRotatef(pc, 0, 0, 1);
339
340 setup_clip_plane(0, 1.0, 0.0, 0.0, size-1.0); /* x > 1.0-size */
341 setup_clip_plane(1, -1.0, 0.0, 0.0, 1.0); /* x < 1.0 */
342 setup_clip_plane(2, 0.0, 1.0, 0.0, size/2); /* y > -size/2 */
343 setup_clip_plane(3, 0.0, -1.0, 0.0, size/2); /* y < size/2 */
344 glEnable(GL_CLIP_PLANE0);
345 glEnable(GL_CLIP_PLANE1);
346 glEnable(GL_CLIP_PLANE2);
347 glEnable(GL_CLIP_PLANE3);
348
349 glMatrixMode(GL_MODELVIEW);
350 glLoadIdentity();
351 glRotatef(md, 0, 0, 1);
352
353 glMatrixMode(GL_PROJECTION);
354 glLoadIdentity();
355 glRotatef(pd, 0, 0, 1);
356
357 piglit_draw_rect(-2, -2, 4, 4);
358
359 for (angle = -180; angle < 180; angle += 10) {
360 float angle_rad = angle * M_PI / 180.0;
361 float xf = dist * cos(angle_rad);
362 float yf = dist * sin(angle_rad);
363 int x = (int) (0.5 + piglit_width * (xf + 1.0)/2.0);
364 int y = (int) (0.5 + piglit_width * (yf + 1.0)/2.0);
365 float found_color[4];
366 glReadPixels(x, y, 1, 1, GL_RGBA, GL_FLOAT, found_color);
367 if (found_color[0] > 0.5) {
368 if (angle == expected) {
369 printf("OK (angle=%d)\n", angle);
370 return true;
371 } else {
372 printf("FAIL (angle=%d, expected=%d)\n", angle,
373 expected);
374 return false;
375 }
376 }
377 }
378 printf("FAIL (test rect not found, expected=%d)\n", expected);
379 return false;
380 }
381
382 enum piglit_result
piglit_display()383 piglit_display()
384 {
385 bool pass = true;
386
387 if (use_glsl) {
388 glUniform1f(position_angle_loc, 0.0);
389 glUniform1f(clipVertex_angle_loc, 0.0);
390 }
391
392 /* Base behavior: no rotations, so the clipping planes should
393 * show up on screen at the coordinates where they were
394 * defined
395 */
396 pass = measure_effects("base behavior", 0, 0, 0, 0, 0) && pass;
397
398 /* A 20 degree rotation in the model-view matrix at the time
399 * clip planes are specified should result in a 20 degree
400 * rotation of where clipping takes effect.
401 */
402 pass = measure_effects(
403 "effect of 20deg ModelView rotation while setting clip plane",
404 20, 0, 0, 0, 20) && pass;
405
406 /* A 20 degree rotation in the projection matrix at the time
407 * clip planes are specified should have no effect.
408 */
409 pass = measure_effects(
410 "effect of 20deg Projection rotation while setting clip plane",
411 0, 20, 0, 0, 0) && pass;
412
413 /* A 20 degree rotation in the model-view matrix at the time
414 * of drawing should have no effect.
415 */
416 pass = measure_effects(
417 "effect of 20deg ModelView rotation while drawing",
418 0, 0, 20, 0, 0) && pass;
419
420 /* When using fixed functionality or an ARB position invariant
421 * program, a 20 degree rotation in the projection matrix at
422 * the time of drawing should result in a 20 degree rotation
423 * of where clipping takes effect when using fixed
424 * functionality. When using a vertex shader, it should have
425 * no effect.
426 */
427 pass = measure_effects(
428 "effect of 20deg Projection rotation while drawing",
429 0, 0, 0, 20, use_ff || use_arb ? 20 : 0) && pass;
430
431 if (use_glsl) {
432 /* When a vertex shader sets gl_Position to be 20
433 * degrees rotated compared to gl_Vertex, and sets
434 * gl_ClipVertex to be equal to gl_Vertex, this should
435 * result in a 20 degree rotation of where clipping
436 * takes effect, because it causes gl_Position to be
437 * rotated 20 degrees with respect to gl_ClipVertex.
438 * However, when a vertex shader sets gl_Position and
439 * does not set gl_ClipVertex, there should be no
440 * effect, because the shader should behave as though
441 * it set gl_ClipVertex equal to gl_Position.
442 */
443 glUniform1f(position_angle_loc, 20.0);
444 pass = measure_effects(
445 "effect of 20deg rotation on gl_Position",
446 0, 0, 0, 0, use_clip_vertex ? 20 : 0) && pass;
447 glUniform1f(position_angle_loc, 0.0);
448 }
449
450 if (use_clip_vertex) {
451 /* When a vertex shader sets gl_Position to be equal
452 * to gl_Vertex, and sets gl_ClipVertex to be 20
453 * degrees rotated compared to gl_Vertex, this should
454 * result in a negative 20 degree rotation of where
455 * clipping takes effect, because it causes
456 * gl_Position to be rotated negative 20 degrees with
457 * respect to gl_ClipVertex.
458 */
459 glUniform1f(clipVertex_angle_loc, 20.0);
460 pass = measure_effects(
461 "effect of 20deg rotation on gl_ClipVertex",
462 0, 0, 0, 0, -20) && pass;
463 glUniform1f(clipVertex_angle_loc, 0.0);
464 }
465
466 piglit_present_results();
467
468 return pass ? PIGLIT_PASS : PIGLIT_FAIL;
469 }
470