/*
 * 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 tessellation.c
 *
 * Verify that transform feedback properly converts primitives of
 * types GL_LINE_LOOP, GL_LINE_STRIP, GL_TRIANGLE_STRIP,
 * GL_TRIANGLE_FAN, GL_QUADS, GL_QUAD_STRIP, and GL_POLYGON into
 * primitives of type GL_LINES or GL_TRIANGLES, as appropriate.
 *
 * According to the OpenGL 3.0 spec (section 2.15: Transform Feedback):
 *
 *     "When quads and polygons are provided to transform feedback
 *     with a primitive mode of TRIANGLES, they will be tessellated
 *     and recorded as triangles (the order of tessellation within a
 *     primitive is undefined). Individual lines or triangles of a
 *     strip or fan primitive will be extracted and recorded
 *     separately."
 *
 * Although it is not stated explicitly, it is clear from context that
 * individual lines of a LINE_LOOP primitive are also expected to be
 * extracted and recorded separately.  Also, the spec does not place
 * any requirement on the order in which vertices are output when
 * extracting individual lines or triangles of a strip, fan, or
 * LINE_LOOP primitive.
 *
 * Because the spec allows variability in how these primitives are
 * tessellated and extracted, we can't verify correct operation by
 * examining the vertices themselves.  However, we can check that if
 * the transform feedback output is fed back into the GL pipeline
 * (using GL_TRIANGLES or GL_LINES, as appropriate), the same image
 * will be rendered.
 *
 * This test operates by first rendering an image without transform
 * feedback, then rendering the same image with transform feedback,
 * then rendering the transform feedback output.  Then it checks that
 * the 3 generated images match exactly.
 *
 * In addition, the test verifies that the expected number of vertices
 * was output by transform feedback.
 *
 * The images are rendered using a fragment shader that attenuates the
 * color of back-facing primitives, so that the test will verify that
 * tessellation preserves winding order properly.
 *
 * The test can be run in four different coloring modes:
 *
 * - "monochrome", meaning that all vertices are assigned the same
 *   color.  A failure in this mode means that the tessellated image
 *   did not have the correct shape.
 *
 * - "wireframe", meaning that all vertices are assigned the same
 *   color, but the image is drawn using
 *   glPolygonMode(GL_FRONT_AND_BACK, GL_LINE).  This test only makes
 *   sense for shapes that would normally be filled (e.g. polygons).
 *   Since we don't expect a tessellated polygon to have the same
 *   appearance as the original image (since additional edges are
 *   added), in this mode we merely check that the correct number of
 *   vertices are output and that the image renders the same with
 *   transform feedback active as with transform feedback inactive.
 *
 * - "smooth", meaning that all vertices are assigned different
 *   colors, and the primitives are drawn with smooth interpolation.
 *   A failure in this mode means that the tessellation performed by
 *   transform feedback failed to match the tessellation performed by
 *   the GL pipeline under normal operation.
 *
 * - "flat_last" or "flat_first", meaning that all vertices are
 *   assigned different colors, and the primitives are flatshaded.  In
 *   the "flat_last" case, they are flatshaded using the GL standard
 *   "last vertex" convention to select the provoking vertex.  In the
 *   "flat_first" case, they are flatshaded using the alternative
 *   "first vertex" convention provided by GL_EXT_provoking_vertex or
 *   GL_ARB_provoking_vertex.  A failure in one of these modes means
 *   that within at least one of the tessellated primitives, transform
 *   feedback failed to output the vertices in the correct order for
 *   proper flatshading.
 *
 * Note: the test can also be run on primitive types "points",
 * "lines", and "triangles".  Although these primitive types are not
 * subject to tessellation, the test is still useful for verifying
 * that correct transform feedback output is generated.
 *
 * Note: some OpenGL implementations do not pass the "flat_first" and
 * "flat_last" tests when rendering quads or polygons.  That is, they
 * produce a tessellation which contains the correct vertices, but not
 * in the order required to preserve flat shaded colors.  This is
 * unlikely to cause problems for client programs, since client
 * programs that use new features like transform feedback are unlikely
 * to also use deprecated features like quads and polygons.  Also, it
 * is a matter of interpretation whether these tests are expected to
 * pass at all--after all, the spec does say that "the order of
 * tessellation within a primitive is undefined".  Accordingly, these
 * failures, should they occur, are flagged as warnings rather than
 * failures.
 */

#include "piglit-util-gl.h"

#define BUFFER_SIZE 20

PIGLIT_GL_TEST_CONFIG_BEGIN

	config.supports_gl_compat_version = 10;

	config.window_width = 256;
	config.window_height = 256;
	config.window_visual = PIGLIT_GL_VISUAL_DOUBLE | PIGLIT_GL_VISUAL_RGBA;
	config.khr_no_error_support = PIGLIT_NO_ERRORS;

PIGLIT_GL_TEST_CONFIG_END

/* Test parameters */
static GLenum draw_mode;
static GLenum xfb_mode;
static unsigned num_input_vertices;
static unsigned expected_num_output_vertices;
static unsigned expected_num_output_primitives;
static float (*vertex_positions)[2];
static GLboolean monochrome;
static GLboolean use_flat_color;
static GLboolean wireframe;
static GLboolean is_deprecated_draw_mode;

/* Other globals */
static GLuint normal_prog;
static GLuint xfb_prog;
static GLuint xfb_buf;
static GLuint xfb_generated_query;
static GLuint xfb_written_query;
static float vertex_colors[][4] = {
	{ 0.00, 0.00, 0.00, 0.00 },
	{ 1.00, 0.25, 0.25, 1.00 },
	{ 0.15, 0.37, 0.98, 1.00 },
	{ 0.50, 0.93, 0.07, 1.00 },
	{ 0.85, 0.02, 0.63, 1.00 },
	{ 0.0,  0.75, 0.75, 1.00 },
	{ 0.85, 0.63, 0.02, 1.00 },
	{ 0.5,  0.07, 0.93, 1.00 },
	{ 0.15, 0.98, 0.37, 1.00 }
};

static struct vertex_data {
	float vertex[2];
	float smooth_color[4];
	float flat_color[4];
} verts[BUFFER_SIZE];

/* Note: vertices are chosen to be on pixel centers to minimize the
 * risk that rounding errors change the image.
 */
static float points_vertices[][2] = {
	{  2.5, 62.5 },
	{ 62.5, 62.5 },
	{  2.5,  2.5 },
	{ 62.5,  2.5 }
};

/* Note: vertices are chosen to be on pixel centers to minimize the
 * risk that rounding errors change the image.
 */
static float lines_vertices[][2] = {
	{  2.5, 62.5 },
	{ 62.5, 62.5 },
	{  2.5,  2.5 },
	{ 62.5,  2.5 }
};

/* Note: vertices are chosen to be on pixel centers to minimize the
 * risk that rounding errors change the image.
 */
static float line_loop_vertices[][2] = {
	{  2.5,  2.5 },
	{  2.5, 62.5 },
	{ 62.5, 62.5 },
	{ 62.5,  2.5 }
};

/* Note: vertices are chosen to be on pixel centers to minimize the
 * risk that rounding errors change the image.
 */
static float line_strip_vertices[][2] = {
	{  2.5,  2.5 },
	{  2.5, 32.5 },
	{ 32.5, 32.5 },
	{ 32.5, 62.5 }
};

static float triangles_vertices[][2] = {
	{   2,  2 },
	{   2, 62 },
	{  42,  2 },
	{  62,  2 },
	{  62, 62 },
	{ 102,  2 }
};

static float triangle_strip_vertices[][2] = {
	{  2,  2 },
	{  2, 62 },
	{ 42,  2 },
	{ 42, 62 },
	{ 82,  2 }
};

static float triangle_fan_vertices[][2] = {
	{  2,  2 },
	{  2, 62 },
	{ 32, 47 },
	{ 52, 27 },
	{ 57, 12 }
};

static float quads_vertices[][2] = {
	{   2,  2 },
	{   2, 62 },
	{  62, 62 },
	{  62,  2 },
	{ 102,  2 },
	{ 102, 62 },
	{ 162, 62 },
	{ 162,  2 }
};

static float quad_strip_vertices[][2] = {
	{   2,  2 },
	{   2, 62 },
	{  62,  2 },
	{  62, 62 },
	{ 122,  2 },
	{ 122, 62 }
};

static float polygon_vertices[][2] = {
	{ 12,  2 },
	{  2, 42 },
	{ 32, 62 },
	{ 62, 42 },
	{ 52,  2 }
};

static const char *vstext =
	"#version 130\n"
	"uniform vec2 vertex_offset;\n"
	"in vec2 vertex;\n"
	"in vec4 smooth_color;\n"
	"in vec4 flat_color;\n"
	"out vec2 vertex_varying;\n"
	"out vec4 smooth_color_varying;\n"
	"flat out vec4 flat_color_varying;\n"
	"\n"
	"void main()\n"
	"{\n"
	"  gl_Position = vec4(vertex + vertex_offset, 0, 128.0);\n"
	"  vertex_varying = vertex;\n"
	"  smooth_color_varying = smooth_color;\n"
	"  flat_color_varying = flat_color;\n"
	"}\n";

static const char *fstext =
	"#version 130\n"
	"uniform bool use_flat_color;\n"
	"in vec4 smooth_color_varying;\n"
	"flat in vec4 flat_color_varying;\n"
	"\n"
	"void main()\n"
	"{\n"
	"  vec4 color = use_flat_color ? flat_color_varying\n"
	"                              : smooth_color_varying;\n"
	"  if (!gl_FrontFacing)\n"
	"    color *= 0.5;\n"
	"  gl_FragColor = color;\n"
	"}\n";

static const char *varyings[] = {
	"vertex_varying", "smooth_color_varying", "flat_color_varying"
};

static void
initialize_shader_and_xfb()
{
	GLuint vs, fs;

	piglit_require_GLSL_version(130);
	piglit_require_transform_feedback();
	vs = piglit_compile_shader_text(GL_VERTEX_SHADER, vstext);
	fs = piglit_compile_shader_text(GL_FRAGMENT_SHADER, fstext);
	normal_prog = glCreateProgram();
	glAttachShader(normal_prog, vs);
	glAttachShader(normal_prog, fs);
	glLinkProgram(normal_prog);
	if (!piglit_link_check_status(normal_prog)) {
		piglit_report_result(PIGLIT_FAIL);
	}
	xfb_prog = glCreateProgram();
	glAttachShader(xfb_prog, vs);
	glAttachShader(xfb_prog, fs);
	glTransformFeedbackVaryings(xfb_prog, 3, varyings,
				    GL_INTERLEAVED_ATTRIBS);
	glLinkProgram(xfb_prog);
	if (!piglit_link_check_status(xfb_prog)) {
		piglit_report_result(PIGLIT_FAIL);
	}
	glGenBuffers(1, &xfb_buf);
	glGenQueries(1, &xfb_generated_query);
	glGenQueries(1, &xfb_written_query);
	glFrontFace(GL_CW);
	if (!piglit_check_gl_error(0))
		piglit_report_result(PIGLIT_FAIL);
}

static void
setup_vertex_shader_inputs(GLuint prog)
{
	GLint vertex_index = glGetAttribLocation(prog, "vertex");
	GLint smooth_color_index = glGetAttribLocation(prog, "smooth_color");
	GLint flat_color_index = glGetAttribLocation(prog, "flat_color");

	glVertexAttribPointer(vertex_index, 2, GL_FLOAT, GL_FALSE,
			      sizeof(verts[0]), &verts[0].vertex);
	glVertexAttribPointer(smooth_color_index, 4, GL_FLOAT, GL_FALSE,
			      sizeof(verts[0]), &verts[0].smooth_color);
	glVertexAttribPointer(flat_color_index, 4, GL_FLOAT, GL_FALSE,
			      sizeof(verts[0]), &verts[0].flat_color);
	glEnableVertexAttribArray(vertex_index);
	glEnableVertexAttribArray(smooth_color_index);
	glEnableVertexAttribArray(flat_color_index);
	if (!piglit_check_gl_error(0))
		piglit_report_result(PIGLIT_FAIL);
}

static void
initialize_vertex_shader_inputs()
{
	unsigned i;

	if (monochrome) {
		for (i = 1; i < ARRAY_SIZE(vertex_colors); ++i) {
			vertex_colors[i][0] = 1.0;
			vertex_colors[i][1] = 1.0;
			vertex_colors[i][2] = 1.0;
			vertex_colors[i][3] = 1.0;
		}
	}

	for (i = 0; i < num_input_vertices; ++i) {
		memcpy(verts[i].vertex, vertex_positions[i],
		       sizeof(verts[i].vertex));
		memcpy(verts[i].smooth_color, vertex_colors[i+1],
		       sizeof(verts[i].smooth_color));
		memcpy(verts[i].flat_color, vertex_colors[i+1],
		       sizeof(verts[i].flat_color));
	}
}

/**
 * Determine how many vertices were output by transform feedback by
 * seeing which elements of the transform feedback buffer have been
 * changed from their zero-initialized value.
 */
static unsigned
count_output_vertices(struct vertex_data *vertices)
{
	struct vertex_data zero_initialized;
	unsigned i;

	memset(&zero_initialized, 0, sizeof(zero_initialized));

	for (i = 0; i < BUFFER_SIZE; ++i) {
		if (memcmp(&vertices[i], &zero_initialized,
			   sizeof(zero_initialized)) == 0)
			break;
	}
	return i;
}

/**
 * Check that two strips of the window match.  Strips are numbered
 * from the top from 0 to 3.
 */
static GLboolean
match_strips(int reference, int compare)
{
	GLfloat *reference_image =
		malloc(piglit_width * (piglit_height / 4) * 4 * sizeof(float));
	int reference_offset = (3 - reference) * (piglit_height / 4);
	int compare_offset = (3 - compare) * (piglit_height / 4);
	GLboolean result;
	glReadPixels(0, reference_offset, piglit_width, piglit_height / 4,
		     GL_RGBA, GL_FLOAT, reference_image);
	result = piglit_probe_image_rgba(0, compare_offset, piglit_width,
					 piglit_height / 4, reference_image);
	free(reference_image);
	return result;
}

static void
draw(GLuint prog, bool use_xfb, float y_offset, GLenum mode,
     unsigned num_vertices)
{
	float vertex_offset[2] = { -82.0, y_offset };
	struct vertex_data buffer[BUFFER_SIZE];

	glUseProgram(prog);
	setup_vertex_shader_inputs(prog);
	glUniform2fv(glGetUniformLocation(prog, "vertex_offset"),
			  1, vertex_offset);
	glUniform1i(glGetUniformLocation(prog, "use_flat_color"),
			 use_flat_color);
	glBindBuffer(GL_ARRAY_BUFFER, 0);
	if (use_xfb) {
		glBindBuffer(GL_TRANSFORM_FEEDBACK_BUFFER, xfb_buf);
		/* Initialize the buffer with 0 so that we will be
		 * able to identify membory that was not overwritten by
		 * the transform feedback.
		 */
		memset(buffer, 0, sizeof(buffer));
		glBufferData(GL_TRANSFORM_FEEDBACK_BUFFER, sizeof(buffer),
			     buffer, GL_STREAM_READ);
		glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, xfb_buf);
		glBeginTransformFeedback(xfb_mode);
		glBeginQuery(GL_PRIMITIVES_GENERATED, xfb_generated_query);
		glBeginQuery(GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN,
			     xfb_written_query);
	}
	glDrawArrays(mode, 0, num_vertices);
	if (use_xfb) {
		glEndTransformFeedback();
		glEndQuery(GL_PRIMITIVES_GENERATED);
		glEndQuery(GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN);
	}
	if (!piglit_check_gl_error(0))
		piglit_report_result(PIGLIT_FAIL);
}

static void
print_usage_and_exit(char *prog_name)
{
	printf("Usage: %s <draw_mode> <shade_mode>\n"
	       "  where <draw_mode> is one of:\n"
	       "    points\n"
	       "    lines\n"
	       "    line_loop\n"
	       "    line_strip\n"
	       "    triangles\n"
	       "    triangle_strip\n"
	       "    triangle_fan\n"
	       "    quads\n"
	       "    quad_strip\n"
	       "    polygon\n"
	       "  and <shade_mode> is one of:\n"
	       "    monochrome\n"
	       "    smooth\n"
	       "    flat_first\n"
	       "    flat_last\n", prog_name);
	exit(1);
}

void
piglit_init(int argc, char **argv)
{
	/* Interpret command line args */
	if (argc != 3)
		print_usage_and_exit(argv[0]);
	if (strcmp(argv[1], "points") == 0) {
		draw_mode = GL_POINTS;
                is_deprecated_draw_mode = GL_FALSE;
		xfb_mode = GL_POINTS;
		num_input_vertices = 4;
		expected_num_output_vertices = 4;
		expected_num_output_primitives = 4;
		vertex_positions = points_vertices;
	} else if (strcmp(argv[1], "lines") == 0) {
		draw_mode = GL_LINES;
                is_deprecated_draw_mode = GL_FALSE;
		xfb_mode = GL_LINES;
		num_input_vertices = 4;
		expected_num_output_vertices = 4;
		expected_num_output_primitives = 2;
		vertex_positions = lines_vertices;
	} else if (strcmp(argv[1], "line_loop") == 0) {
		draw_mode = GL_LINE_LOOP;
                is_deprecated_draw_mode = GL_FALSE;
		xfb_mode = GL_LINES;
		num_input_vertices = 4;
		expected_num_output_vertices = 8;
		expected_num_output_primitives = 4;
		vertex_positions = line_loop_vertices;
	} else if (strcmp(argv[1], "line_strip") == 0) {
		draw_mode = GL_LINE_STRIP;
                is_deprecated_draw_mode = GL_FALSE;
		xfb_mode = GL_LINES;
		num_input_vertices = 4;
		expected_num_output_vertices = 6;
		expected_num_output_primitives = 3;
		vertex_positions = line_strip_vertices;
	} else if (strcmp(argv[1], "triangles") == 0) {
		draw_mode = GL_TRIANGLES;
                is_deprecated_draw_mode = GL_FALSE;
		xfb_mode = GL_TRIANGLES;
		num_input_vertices = 6;
		expected_num_output_vertices = 6;
		expected_num_output_primitives = 2;
		vertex_positions = triangles_vertices;
	} else if (strcmp(argv[1], "triangle_strip") == 0) {
		draw_mode = GL_TRIANGLE_STRIP;
                is_deprecated_draw_mode = GL_FALSE;
		xfb_mode = GL_TRIANGLES;
		num_input_vertices = 5;
		expected_num_output_vertices = 9;
		expected_num_output_primitives = 3;
		vertex_positions = triangle_strip_vertices;
	} else if (strcmp(argv[1], "triangle_fan") == 0) {
		draw_mode = GL_TRIANGLE_FAN;
                is_deprecated_draw_mode = GL_FALSE;
		xfb_mode = GL_TRIANGLES;
		num_input_vertices = 5;
		expected_num_output_vertices = 9;
		expected_num_output_primitives = 3;
		vertex_positions = triangle_fan_vertices;
	} else if (strcmp(argv[1], "quads") == 0) {
		draw_mode = GL_QUADS;
                is_deprecated_draw_mode = GL_TRUE;
		xfb_mode = GL_TRIANGLES;
		num_input_vertices = 8;
		expected_num_output_vertices = 12;
		expected_num_output_primitives = 4;
		vertex_positions = quads_vertices;
	} else if (strcmp(argv[1], "quad_strip") == 0) {
		draw_mode = GL_QUAD_STRIP;
                is_deprecated_draw_mode = GL_TRUE;
		xfb_mode = GL_TRIANGLES;
		num_input_vertices = 6;
		expected_num_output_vertices = 12;
		expected_num_output_primitives = 4;
		vertex_positions = quad_strip_vertices;
	} else if (strcmp(argv[1], "polygon") == 0) {
		draw_mode = GL_POLYGON;
                is_deprecated_draw_mode = GL_TRUE;
		xfb_mode = GL_TRIANGLES;
		num_input_vertices = 5;
		expected_num_output_vertices = 9;
		expected_num_output_primitives = 3;
		vertex_positions = polygon_vertices;
	} else {
		print_usage_and_exit(argv[0]);
	}
	if (strcmp(argv[2], "monochrome") == 0) {
		monochrome = GL_TRUE;
		use_flat_color = GL_FALSE;
		wireframe = GL_FALSE;
	} else if (strcmp(argv[2], "smooth") == 0) {
		monochrome = GL_FALSE;
		use_flat_color = GL_FALSE;
		wireframe = GL_FALSE;
	} else if (strcmp(argv[2], "flat_last") == 0) {
		monochrome = GL_FALSE;
		use_flat_color = GL_TRUE;
		wireframe = GL_FALSE;
	} else if (strcmp(argv[2], "flat_first") == 0) {
		monochrome = GL_FALSE;
		use_flat_color = GL_TRUE;
		if (piglit_is_extension_supported("GL_EXT_provoking_vertex")) {
			glProvokingVertexEXT(GL_FIRST_VERTEX_CONVENTION);
		} else if (piglit_is_extension_supported("GL_ARB_provoking_vertex")) {
			glProvokingVertex(GL_FIRST_VERTEX_CONVENTION);
		} else {
			printf("Test requires GL_EXT_provoking_vertex "
			       "or GL_ARB_provoking_vertex\n");
			piglit_report_result(PIGLIT_SKIP);
		}
		wireframe = GL_FALSE;
	} else if (strcmp(argv[2], "wireframe") == 0) {
		monochrome = GL_TRUE;
		use_flat_color = GL_FALSE;
		wireframe = GL_TRUE;
	} else {
		print_usage_and_exit(argv[0]);
	}

	initialize_shader_and_xfb();
}

enum piglit_result piglit_display(void)
{
	struct vertex_data *readback;
	unsigned num_output_vertices;
	GLboolean pass = GL_TRUE;
	GLboolean warn = GL_FALSE;
	GLuint num_generated_primitives;
	GLuint num_written_primitives;

	initialize_vertex_shader_inputs();

	glClear(GL_COLOR_BUFFER_BIT);
	if (wireframe)
		glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
	draw(normal_prog, false, 64.0, draw_mode, num_input_vertices);
	draw(xfb_prog, true, 0.0, draw_mode, num_input_vertices);

	pass = match_strips(0, 1) && pass;

	readback = glMapBuffer(GL_TRANSFORM_FEEDBACK_BUFFER, GL_READ_ONLY);
	pass = piglit_check_gl_error(0) && pass;

	num_output_vertices = count_output_vertices(readback);
	if (num_output_vertices != expected_num_output_vertices) {
		printf("Expected %u output vertices, but got %u\n",
		       expected_num_output_vertices, num_output_vertices);
		pass = GL_FALSE;
	}
	glGetQueryObjectuiv(xfb_generated_query, GL_QUERY_RESULT,
			    &num_generated_primitives);
	if (num_generated_primitives != expected_num_output_primitives) {
		printf("Expected %u primitives generated, but got %u\n",
		       expected_num_output_primitives,
		       num_generated_primitives);
		pass = GL_FALSE;
	}
	glGetQueryObjectuiv(xfb_written_query, GL_QUERY_RESULT,
			    &num_written_primitives);
	if (num_written_primitives != expected_num_output_primitives) {
		printf("Expected %u primitives written, but got %u\n",
		       expected_num_output_primitives, num_written_primitives);
		pass = GL_FALSE;
	}

	memcpy(verts, readback, sizeof(verts));
	glUnmapBuffer(GL_TRANSFORM_FEEDBACK_BUFFER);

	draw(normal_prog, false, -64.0, xfb_mode, num_output_vertices);

	if (!wireframe) {
		if (use_flat_color && is_deprecated_draw_mode)
			warn = (!match_strips(0, 2)) || warn;
		else
			pass = match_strips(0, 2) && pass;
	}

	piglit_present_results();

	if (!pass)
		return PIGLIT_FAIL;
	else if (warn)
		return PIGLIT_WARN;
	else
		return PIGLIT_PASS;
}
