1 /**************************************************************************
2  *
3  * Copyright 2012 VMware, Inc.
4  * All Rights Reserved.
5  *
6  * Permission is hereby granted, free of charge, to any person obtaining a
7  * copy of this software and associated documentation files (the
8  * "Software"), to deal in the Software without restriction, including
9  * without limitation the rights to use, copy, modify, merge, publish,
10  * distribute, sub license, and/or sell copies of the Software, and to
11  * permit persons to whom the Software is furnished to do so, subject to
12  * the following conditions:
13  *
14  * The above copyright notice and this permission notice (including the
15  * next paragraph) shall be included in all copies or substantial portions
16  * of the Software.
17  *
18  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
19  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
21  * IN NO EVENT SHALL VMWARE AND/OR ITS SUPPLIERS BE LIABLE FOR
22  * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
23  * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
24  * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
25  *
26  **************************************************************************/
27 
28 /**
29  * Triangle Rasterization Overdraw Test
30  *
31  * Draws a triangle fan to fill the screen and ensures every pixel was drawn only once
32  * Based on idea from Brian Paul
33  *
34  *
35  * Contains two methods of drawing to cover both clipped and unclipped triangles:
36  *
37  *   1. No-Clip: Picks a random point in the window and walks the perimeter of the window
38  *               adding vertices to the triangle fan at non integer steps.
39  *
40  *   2. Clip:    Picks a random point in the window and adds vertices to the triangle fan
41  *               around a circle that contains the entire window, thus going off screen.
42  */
43 
44 #include "piglit-util-gl.h"
45 #include "mersenne.hpp"
46 
47 #include <time.h>
48 #include <vector>
49 #include <algorithm>
50 
51 /* Data structures */
52 struct Vector
53 {
VectorVector54 	Vector()
55 		: x(0), y(0)
56 	{
57 	}
58 
VectorVector59 	Vector(float x, float y)
60 		: x(x), y(y)
61 	{
62 	}
63 
64 	float x, y;
65 };
66 
67 /* Command line arguments */
68 bool rect = false;
69 bool clips = false;
70 bool break_on_fail = false;
71 int random_test_count = 10;
72 
73 /* Piglit variables */
74 
75 PIGLIT_GL_TEST_CONFIG_BEGIN
76 
77 	config.supports_gl_compat_version = 10;
78 
79 	config.window_width = 1000;
80 	config.window_height = 1000;
81 	config.window_visual = PIGLIT_GL_VISUAL_RGB | PIGLIT_GL_VISUAL_DOUBLE;
82 	config.khr_no_error_support = PIGLIT_NO_ERRORS;
83 
84 PIGLIT_GL_TEST_CONFIG_END
85 
86 /* Globals */
87 int test_id = 0;
88 Mersenne mersenne;
89 
90 
91 /* Random floating point number between 0 and 1. */
92 static inline float
random_float(void)93 random_float(void)
94 {
95 	const int float_range = 1 << 23;
96 	return (mersenne.value() % float_range) * (1.0 / float_range);
97 }
98 
99 /* Random float from [a to b) */
100 static inline float
random_float(float a,float b)101 random_float(float a, float b)
102 {
103 	return a + (b - a - 1) * random_float();
104 }
105 
106 
107 struct TestCase
108 {
109 	Vector mid;
110 	std::vector<Vector> triangle_fan;
111 
112 	struct {
113 		int x;
114 		int y;
115 		int w;
116 		int h;
117 	} probe_rect;
118 
119 	void generate(void);
120 	bool run(void) const;
121 };
122 
123 
124 /* Generates a random triangle fan with a random origin, and contouring a rectangle or circle */
generate(void)125 void TestCase::generate(void)
126 {
127 	/* Random center point */
128 	if (clips) {
129 		mid.x = random_float(-0.5*piglit_width, 1.5*piglit_width);
130 		mid.y = random_float(-0.5*piglit_height, 1.5*piglit_height);
131 	} else {
132 		mid.x = random_float(0, piglit_width);
133 		mid.y = random_float(0, piglit_height);
134 	}
135 
136 	if (clips) {
137 		probe_rect.x = 0;
138 		probe_rect.y = 0;
139 		probe_rect.w = piglit_width;
140 		probe_rect.h = piglit_height;
141 	} else {
142 		probe_rect.x = piglit_width/4;
143 		probe_rect.y = piglit_height/4;
144 		probe_rect.w = piglit_width/2;
145 		probe_rect.h = piglit_height/2;
146 	}
147 
148 	triangle_fan.clear();
149 	triangle_fan.push_back(mid);
150 
151 	if (rect) {
152 		/* Step around the window perimeter adding triangles */
153 		double perimeter = probe_rect.w*2 + probe_rect.h*2;
154 		double pos = 0.0;
155 
156 		while (pos < perimeter) {
157 			Vector vertex;
158 
159 			if (pos < probe_rect.w) {
160 				/* bottom */
161 				vertex.x = probe_rect.x + pos;
162 				vertex.y = probe_rect.y + 0.0f;
163 			} else if (pos < probe_rect.w + probe_rect.h) {
164 				/* right */
165 				vertex.x = probe_rect.x + probe_rect.w;
166 				vertex.y = probe_rect.y + pos - probe_rect.w;
167 			} else if(pos < probe_rect.w*2 + probe_rect.h) {
168 				/* top */
169 				vertex.x = probe_rect.x + probe_rect.w - (pos - (probe_rect.w + probe_rect.h));
170 				vertex.y = probe_rect.y + probe_rect.h;
171 			} else {
172 				/* left */
173 				vertex.x = probe_rect.x + 0.0f;
174 				vertex.y = probe_rect.y + probe_rect.h - (pos - (probe_rect.w*2 + probe_rect.h));
175 			}
176 
177 			triangle_fan.push_back(vertex);
178 			pos += random_float();
179 		}
180 	} else {
181 		/* Step around a circle that contains the window */
182 		double radius = (sqrt((double)(probe_rect.w*probe_rect.w + probe_rect.h*probe_rect.h)) / 2.0) + 5.0;
183 		double perimeter = 2.0 * M_PI * radius;
184 		double pos = 0.0;
185 
186 		while (pos < perimeter) {
187 			double theta = pos / radius;
188 
189 			float x = probe_rect.x + 0.5 * probe_rect.w + cos(theta) * radius;
190 			float y = probe_rect.y + 0.5 * probe_rect.h + sin(theta) * radius;
191 
192 			triangle_fan.push_back(Vector(x, y));
193 			pos += random_float();
194 		}
195 	}
196 
197 	/* Complete the fan! */
198 	triangle_fan.push_back(triangle_fan.at(1));
199 	++test_id;
200 }
201 
202 /* Tests a triangle fan */
run(void) const203 bool TestCase::run(void) const
204 {
205 	glViewport(0, 0, piglit_width, piglit_height);
206 	piglit_ortho_projection(piglit_width, piglit_height, GL_FALSE);
207 
208 	/* Set render state */
209 	float colour[4];
210 	glEnable(GL_BLEND);
211 	glBlendEquation(GL_FUNC_ADD);
212 	if (rect) {
213 		glBlendFunc(GL_ONE, GL_ONE);
214 		colour[0] = colour[1] = colour[2] = 127.0f / 255.0f;
215 	} else {
216 		/* Invert.
217 		 *
218 		 * When contouring a circle with very small steps, some overdraw occurs
219 		 * naturally, but it should cancel itself out, i.e., there should be an
220 		 * odd number of overdraw inside the shape, and an even number outside.
221 		 * */
222 		glBlendFunc(GL_ONE_MINUS_DST_COLOR, GL_ZERO);
223 		colour[0] = colour[1] = colour[2] = 1.0f;
224 	}
225 
226 	colour[3] = 1.0f;
227 	glColor4fv(colour);
228 
229 	glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
230 	glClear(GL_COLOR_BUFFER_BIT);
231 
232 	/* Draw triangle fan */
233 	glEnableClientState(GL_VERTEX_ARRAY);
234 	glVertexPointer(2, GL_FLOAT, 0, &triangle_fan.front());
235 	glDrawArrays(GL_TRIANGLE_FAN, 0, triangle_fan.size());
236 	glDisableClientState(GL_VERTEX_ARRAY);
237 
238 	/* Reset draw state */
239 	glDisable(GL_BLEND);
240 
241 	if (!piglit_probe_rect_rgb(probe_rect.x, probe_rect.y, probe_rect.w, probe_rect.y, colour)) {
242 		printf("%d. Triangle Fan with %d triangles around (%f, %f)\n",
243 		       test_id, (int)triangle_fan.size(), mid.x, mid.y);
244 
245 		fflush(stdout);
246 		return false;
247 	}
248 
249 	return true;
250 }
251 
252 /* Render */
253 enum piglit_result
piglit_display(void)254 piglit_display(void)
255 {
256 	/* Perform test */
257 	GLboolean pass = GL_TRUE;
258 
259 	TestCase test_case;
260 
261 	if (piglit_automatic) {
262 		int fail_count = 0;
263 
264 		printf("Running %d random tests\n", random_test_count);
265 		fflush(stdout);
266 
267 		for (int i = 0; i < random_test_count && !(fail_count && break_on_fail); ++i) {
268 			test_case.generate();
269 			if (!test_case.run())
270 				fail_count++;
271 		}
272 
273 		printf("Failed %d random tests\n", fail_count);
274 		fflush(stdout);
275 
276 		if (fail_count)
277 			pass = GL_FALSE;
278 	} else {
279 		test_case.generate();
280 		pass = pass && test_case.run();
281 
282 		piglit_present_results();
283 	}
284 
285 	if (!piglit_check_gl_error(GL_NO_ERROR))
286 		piglit_report_result(PIGLIT_FAIL);
287 	return pass ? PIGLIT_PASS : PIGLIT_FAIL;
288 }
289 
290 /* Read command line arguments! */
291 void
piglit_init(int argc,char ** argv)292 piglit_init(int argc, char **argv)
293 {
294 	uint32_t seed = 0xfacebeef ^ time(NULL);
295 
296 	for (int i = 1; i < argc; ++i) {
297 		if (strcmp(argv[i], "-break_on_fail") == 0){
298 			break_on_fail = true;
299 			printf("Execution will stop on first fail\n");
300 		} else if (strcmp(argv[i], "-rect") == 0){
301 			rect = true;
302 		} else if (strcmp(argv[i], "-max_size") == 0){
303 			glGetIntegerv(GL_MAX_TEXTURE_SIZE, &piglit_width);
304 			piglit_height = piglit_width;
305 		} else if (strcmp(argv[i], "-clip") == 0){
306 			clips = true;
307 			printf("Clipped triangles are being tested\n");
308 		} else if (i + 1 < argc) {
309 			if (strcmp(argv[i], "-count") == 0) {
310 				random_test_count = strtoul(argv[++i], NULL, 0);
311 			} else if (strcmp(argv[i], "-seed") == 0) {
312 				seed = strtoul(argv[++i], NULL, 0);
313 			}
314 		}
315 	}
316 
317 	printf("Random seed: 0x%08X\n", seed);
318 	mersenne.init(seed);
319 }
320