/*
 * BEGIN_COPYRIGHT -*- glean -*-
 *
 * Copyright (C) 2001  Allen Akin   All Rights Reserved.
 * Copyright (C) 2014  Intel Corporation   All Rights Reserved.
 *
 * 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 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.
 *
 * END_COPYRIGHT
 */

/** @file polygon-offset.c
 *
 * Implementation of polygon offset tests.
 *
 * This test verifies glPolygonOffset.  It is run on every OpenGL-capable
 * drawing surface configuration that supports creation of a window, has a
 * depth buffer, and is RGB.
 *
 * The first subtest verifies that the OpenGL implementation is using a
 * plausible value for the "minimum resolvable difference" (MRD).  This is the
 * offset in window coordinates that is sufficient to provide separation in
 * depth (Z) for any two parallel surfaces.  The subtest searches for the MRD
 * by drawing two surfaces at a distance from each other and checking the
 * resulting image to see if they were cleanly separated.  The distance is
 * then modified (using a binary search) until a minimum value is found.  This
 * is the so-called "ideal" MRD.  Then two surfaces are drawn using
 * glPolygonOffset to produce a separation that should equal one MRD.  The
 * depth values at corresponding points on each surface are subtracted to form
 * the "actual" MRD.  The subtest performs these checks twice, once close to
 * the viewpoint and once far away from it, and passes if the largest of the
 * ideal MRDs and the largest of the actual MRDs are nearly the same.
 *
 * The second subtest verifies that the OpenGL implementation is producing
 * plausible values for slope-dependent offsets.  The OpenGL spec requires
 * that the depth slope of a surface be computed by an approximation that is
 * at least as large as max(abs(dz/dx),abs(dz/dy)) and no larger than
 * sqrt((dz/dx)**2+(dz/dy)**2).  The subtest draws a quad rotated by various
 * angles along various axes, samples three points on the quad's surface, and
 * computes dz/dx and dz/dy.  Then it draws two additional quads offset by one
 * and two times the depth slope, respectively.  The base quad and the two new
 * quads are sampled and their actual depths read from the depth buffer.  The
 * subtest passes if the quads are offset by amounts that are within one and
 * two times the allowable range, respectively.
 *
 * Derived in part from tests written by Angus Dorbie <dorbie@sgi.com> in
 * September 2000 and Rickard E. (Rik) Faith <faith@valinux.com> in October
 * 2000.
 *
 * Ported to Piglit by Laura Ekstrand.
 */

#include "piglit-util-gl.h"
#include <math.h>

PIGLIT_GL_TEST_CONFIG_BEGIN

	config.supports_gl_compat_version = 11;

	config.window_visual = PIGLIT_GL_VISUAL_RGBA |
		PIGLIT_GL_VISUAL_DOUBLE | PIGLIT_GL_VISUAL_DEPTH;

PIGLIT_GL_TEST_CONFIG_END


struct angle_axis {
	GLfloat angle;
	GLfloat axis[3];
};

static void
multiply_matrix_with_vector(const double *matrix, double *vector)
{
	double tmp[4];
	unsigned i;

	/* The matrix is stored in the natural OpenGL order: column-major.  In
	 * GLSL, this would be:
	 *
	 *     vp.x = m[0] * v.xxxx;
	 *     vp.y = m[1] * v.yyyy;
	 *     vp.z = m[2] * v.zzzz;
	 *     vp.w = m[3] * v.wwww;
	 *     v = vp;
	 */
	for (i = 0; i < 4; i++)
		tmp[i] = matrix[i] * vector[0];

	for (i = 0; i < 4; i++)
		tmp[i] += matrix[i + 4] * vector[1];

	for (i = 0; i < 4; i++)
		tmp[i] += matrix[i + 8] * vector[2];

	for (i = 0; i < 4; i++)
		tmp[i] += matrix[i + 12] * vector[3];

	memcpy(vector, tmp, sizeof(tmp));
}

static void
project(double x, double y, double z, const double *modelview,
	const double *projection, const int *viewport, double *result)
{
	double vp[4] = { x, y, z, 1.0 };

	/* Calculate v' as P * M * v. */
	multiply_matrix_with_vector(modelview, vp);
	multiply_matrix_with_vector(projection, vp);

	if (vp[3] == 0.0) {
		fprintf(stderr, "Cannot perspective divide by zero.\n");
		piglit_report_result(PIGLIT_FAIL);
	}

	vp[0] /= vp[3];
	vp[1] /= vp[3];
	vp[2] /= vp[3];

	/* Calculate the screen position using the same formula a gluProject. */
	result[0] = viewport[0] + (viewport[2] * ((vp[0] + 1.0) / 2.0));
	result[1] = viewport[1] + (viewport[3] * ((vp[1] + 1.0) / 2.0));
	result[2] =                               (vp[2] + 1.0) / 2.0;
}

static void
draw_quad_at_distance(GLdouble dist)
{
	glBegin(GL_QUADS);
		glVertex3d(-dist, -dist, -dist);
		glVertex3d( dist, -dist, -dist);
		glVertex3d( dist,  dist, -dist);
		glVertex3d(-dist,  dist, -dist);
	glEnd();
}

static GLdouble
window_coord_depth(GLdouble dist)
{
	/* Assumes we're using the "far at infinity" projection matrix and
	 * simple viewport transformation.
	 */
	return 0.5 * (dist - 2.0) / dist + 0.5;
}

static bool
red_quad_was_drawn(void)
{
	static const float expected[] = {1.0f, 0.0f, 0.0f};
	return piglit_probe_rect_rgb_silent(0, 0, piglit_width, piglit_height,
					    expected);
}

void
piglit_init(int argc, char **argv)
{
	glEnable(GL_DEPTH_TEST);
	glDisable(GL_DITHER);
	glEnable(GL_CULL_FACE);
	glShadeModel(GL_FLAT);
}

static void
find_ideal_mrd(GLdouble *ideal_mrd_near, GLdouble *ideal_mrd_far,
	       GLdouble *next_to_near, GLdouble *next_to_far)
{
	/* MRD stands for Minimum Resolvable Difference, the smallest distance
	 * in depth that suffices to separate any two polygons (or a polygon
	 * and the near or far clipping planes).
	 *
	 * This function tries to determine the "ideal" MRD for the current
	 * rendering context.  It's expressed in window coordinates, because
	 * the value in model or clipping coordinates depends on the scale
	 * factors in the modelview and projection matrices and on the
	 * distances to the near and far clipping planes.
	 *
	 * For simple unsigned-integer depth buffers that aren't too deep (so
	 * that precision isn't an issue during coordinate transformations),
	 * it should be about one least-significant bit.  For deep or
	 * floating-point or compressed depth buffers the situation may be
	 * more complicated, so we don't pass or fail an implementation solely
	 * on the basis of its ideal MRD.
	 *
	 * There are two subtle parts of this function.  The first is the
	 * projection matrix we use for rendering.  This matrix places the far
	 * clip plane at infinity (so that we don't run into arbitrary limits
	 * during our search process).  The second is the method used for
	 * drawing the polygon.  We scale the x and y coords of the polygon
	 * vertices by the polygon's depth, so that it always occupies the
	 * full view frustum.  This makes it easier to verify that the polygon
	 * was resolved completely -- we just read back the entire window and
	 * see if any background pixels appear.
	 *
	 * To insure that we get reasonable results on machines with unusual
	 * depth buffers (floating-point, or compressed), we determine the MRD
	 * twice, once close to the near clipping plane and once as far away
	 * from the eye as possible.  On a simple integer depth buffer these
	 * two values should be essentially the same.  For other depth-buffer
	 * formats, the ideal MRD is simply the largest of the two.
	 */

	GLdouble near_dist, far_dist, half_dist;
	int i;

	/* First, find a distance that is as far away as possible, yet a quad
	 * at that distance can be distinguished from the background.  Start
	 * by pushing quads away from the eye until we find an interval where
	 * the closer quad can be resolved, but the farther quad cannot.  Then
	 * binary-search to find the threshold.
	 */

	glDepthFunc(GL_LESS);
	glClearDepth(1.0);
	glColor3f(1.0, 0.0, 0.0); /* red */
	near_dist = 1.0;
	far_dist = 2.0;
	for (;;) {
		glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
		draw_quad_at_distance(far_dist);
		if (!red_quad_was_drawn())
			break;
		piglit_present_results();
		near_dist = far_dist;
		far_dist *= 2.0;
	}
	for (i = 0; i < 64; ++i) {
		half_dist = 0.5 * (near_dist + far_dist);
		glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
		draw_quad_at_distance(half_dist);
		if (red_quad_was_drawn())
			near_dist = half_dist;
		else
			far_dist = half_dist;
		piglit_present_results();
	}
	*next_to_far = near_dist;

	/* We can derive a resolvable difference from the value next_to_far,
	 * but it's not necessarily the one we want.  Consider mapping the
	 * object coordinate range [0,1] onto the integer window coordinate
	 * range [0,2].  A natural way to do this is with a linear function,
	 * windowCoord = 2*objectCoord.  With rounding, this maps [0,0.25) to
	 * 0, [0.25,0.75) to 1, and [0.75,1] to 2.  Note that the intervals at
	 * either end are 0.25 wide, but the one in the middle is 0.5 wide.
	 * The difference we can derive from next_to_far is related to the
	 * width of the final interval.  We want to back up just a bit so that
	 * we can get a (possibly much larger) difference that will work for
	 * the larger interval.  To do this we need to find a difference that
	 * allows us to distinguish two quads when the more distant one is at
	 * distance next_to_far.
	 */

	near_dist = 1.0;
	far_dist = *next_to_far;
	for (i = 0; i < 64; ++i) {
		half_dist = 0.5 * (near_dist + far_dist);
		glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

		glColor3f(0.0, 0.0, 0.0); /* black */
		glDepthFunc(GL_ALWAYS);
		draw_quad_at_distance(*next_to_far);

		glColor3f(1.0, 0.0, 0.0); /* red */
		glDepthFunc(GL_LESS);
		draw_quad_at_distance(half_dist);

		if (red_quad_was_drawn())
			near_dist = half_dist;
		else
			far_dist = half_dist;
		piglit_present_results();
	}

	*ideal_mrd_far = window_coord_depth(*next_to_far)
		- window_coord_depth(near_dist);

	/* Now we apply a similar strategy at the near end of the depth range,
	 * but swapping the senses of various comparisons so that we approach
	 * the near clipping plane rather than the far.
	 */

	glClearDepth(0.0);
	glDepthFunc(GL_GREATER);
	glColor3f(1.0, 0.0, 0.0); /* red */
	near_dist = 1.0;
	far_dist = *next_to_far;
	for (i = 0; i < 64; ++i) {
		half_dist = 0.5 * (near_dist + far_dist);
		glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
		draw_quad_at_distance(half_dist);
		if (red_quad_was_drawn())
			far_dist = half_dist;
		else
			near_dist = half_dist;
		piglit_present_results();
	}
	*next_to_near = far_dist;

	near_dist = *next_to_near;
	far_dist = *next_to_far;
	for (i = 0; i < 64; ++i) {
		half_dist = 0.5 * (near_dist + far_dist);
		glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

		glColor3f(0.0, 0.0, 0.0); /* black */
		glDepthFunc(GL_ALWAYS);
		draw_quad_at_distance(*next_to_near);

		glColor3f(1.0, 0.0, 0.0); /* red */
		glDepthFunc(GL_GREATER);
		draw_quad_at_distance(half_dist);

		if (red_quad_was_drawn())
			far_dist = half_dist;
		else
			near_dist = half_dist;
		piglit_present_results();
	}

	*ideal_mrd_near = window_coord_depth(far_dist)
		- window_coord_depth(*next_to_near);
}

static double
read_depth(int x, int y)
{
	GLuint depth;
	glReadPixels(x, y, 1, 1, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, &depth);

	/* This normalization of "depth" is correct even on 64-bit
	 * machines because GL types have machine-independent ranges.
	 */
	return ((double) depth) / 4294967295.0;
}

static void
find_actual_mrd(GLdouble *next_to_near, GLdouble *next_to_far,
		GLdouble *actual_mrd_near, GLdouble *actual_mrd_far)
{
	/* Here we use polygon offset to determine the implementation's actual
	 * MRD.
	 */

	double base_depth;

	glDepthFunc(GL_ALWAYS);

	/* Draw a quad far away from the eye and read the depth at its
	 * center:
	 */
	glDisable(GL_POLYGON_OFFSET_FILL);
	draw_quad_at_distance(*next_to_far);
	base_depth = read_depth(piglit_width/2, piglit_height/2);

	/* Now draw a quad that's one MRD closer to the eye: */
	glEnable(GL_POLYGON_OFFSET_FILL);
	glPolygonOffset(0.0, -1.0);
	draw_quad_at_distance(*next_to_far);

	/* The difference between the depths of the two quads is the value the
	 * implementation is actually using for one MRD:
	 */
	*actual_mrd_far = base_depth
		- read_depth(piglit_width/2, piglit_height/2);

	/* Repeat the process for a quad close to the eye: */
	glDisable(GL_POLYGON_OFFSET_FILL);
	draw_quad_at_distance(*next_to_near);
	base_depth = read_depth(piglit_width / 2, piglit_height / 2);
	glEnable(GL_POLYGON_OFFSET_FILL);
	glPolygonOffset(0.0, 1.0);	/* 1 MRD further away */
	draw_quad_at_distance(*next_to_near);
	*actual_mrd_near = read_depth(piglit_width / 2, piglit_height / 2)
		- base_depth;
}

static bool
check_slope_offset(const struct angle_axis *aa, GLdouble *ideal_mrd_near)
{
	/* This function checks for correct slope-based offsets for a quad
	 * rotated to a given angle around a given axis.
	 *
	 * The basic strategy is to:
	 *	Draw the quad.  (Note: the quad's size and position
	 *		are chosen so that it won't ever be clipped.)
	 *	Sample three points in the quad's interior.
	 *	Compute dz/dx and dz/dy based on those samples.
	 *	Compute the range of allowable offsets; must be between
	 *		max(abs(dz/dx), abs(dz/dy)) and
	 *		sqrt((dz/dx)**2, (dz/dy)**2)
	 *	Sample the depth of the quad at its center.
	 *	Use PolygonOffset to produce an offset equal to one
	 *		times the depth slope of the base quad.
	 *	Draw another quad with the same orientation as the first.
	 *	Sample the second quad at its center.
	 *	Compute the difference in depths between the first quad
	 *		and the second.
	 *	Verify that the difference is within the allowable range.
	 *	Repeat for a third quad at twice the offset from the first.
	 *		(This verifies that the implementation is scaling
	 *		the depth offset correctly.)
	 */
	const GLfloat quad_dist = 2.5;	/* must be > 1+sqrt(2) to avoid */
					/* clipping by the near plane */
	GLdouble modelview_mat[16];
	GLdouble projection_mat[16];
	GLint viewport[4];
	GLdouble centerw[3];
	GLdouble base_depth;
	GLdouble p0[3];
	GLdouble p1[3];
	GLdouble p2[3];
	double det, dzdx, dzdy, mmax, mmin;
	GLdouble offset_depth, offset;


	glClearDepth(1.0);
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
	glEnable(GL_DEPTH_TEST);
	glDepthFunc(GL_LESS);

	glColor3f(1.0, 0.0, 0.0); /* red */

	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();
	glTranslatef(0.0, 0.0, -quad_dist);
	glRotatef(aa->angle, aa->axis[0], aa->axis[1], aa->axis[2]);

	glGetDoublev(GL_MODELVIEW_MATRIX, modelview_mat);
	glGetDoublev(GL_PROJECTION_MATRIX, projection_mat);
	glGetIntegerv(GL_VIEWPORT, viewport);

	glDisable(GL_POLYGON_OFFSET_FILL);

	piglit_draw_rect(-1.0, -1.0, 2.0, 2.0);

	project(0.0, 0.0, 0.0, modelview_mat, projection_mat, viewport,
		centerw);
	base_depth = read_depth(centerw[0], centerw[1]);

	project(-0.9, -0.9, 0.0, modelview_mat, projection_mat, viewport, p0);
	p0[2] = read_depth(p0[0], p0[1]);

	project( 0.9, -0.9, 0.0, modelview_mat, projection_mat, viewport, p1);
	p1[2] = read_depth(p1[0], p1[1]);

	project( 0.9,  0.9, 0.0, modelview_mat, projection_mat, viewport, p2);
	p2[2] = read_depth(p2[0], p2[1]);

	det = (p0[0] - p1[0]) * (p0[1] - p2[1])
		- (p0[0] - p2[0]) * (p0[1] - p1[1]);
	if (fabs(det) < 0.001)
		return false;	/* too close to colinear to evaluate */

	dzdx = ((p0[2] - p1[2]) * (p0[1] - p2[1])
		- (p0[2] - p2[2]) * (p0[1] - p1[1])) / det;
	dzdy = ((p0[0] - p1[0]) * (p0[2] - p2[2])
		- (p0[0] - p2[0]) * (p0[2] - p1[2])) / det;

	mmax = 1.1 * sqrt(dzdx * dzdx + dzdy * dzdy) + (*ideal_mrd_near);
		/* (adding ideal_mrd_near is a fudge for roundoff error */
		/* when the slope is extremely close to zero) */
	mmin = 0.9 * fmax(fabs(dzdx), fabs(dzdy));

	glEnable(GL_POLYGON_OFFSET_FILL);
	glPolygonOffset(-1.0, 0.0);
	piglit_present_results();
	piglit_draw_rect(-1.0, -1.0, 2.0, 2.0);
	offset_depth = read_depth(centerw[0], centerw[1]);
	offset = fmax(base_depth - offset_depth, 0.0);
	if (offset < mmin || offset > mmax) {
		if (offset < mmin)
			printf("Depth-slope related offset was too small");
		else
			printf("Depth-slope related offset was too large");
		printf("; first failure at:\n");
		printf("\tAngle = %f degrees, axis = (%f, %f, %f)\n",
			aa->angle, aa->axis[0], aa->axis[1], aa->axis[2]);
		printf("\tFailing offset was %.16f\n", offset);
		printf("\tAllowable range is (%f, %f)\n", mmin, mmax);
		printf("\tglPolygonOffset(-1.0, 0.0);\n");
		printf("\tglReadPixels returned %f\n", offset_depth);
		return false;
	}

	glPolygonOffset(-2.0, 0.0);
	piglit_present_results();
	piglit_draw_rect(-1.0, -1.0, 2.0, 2.0);
	offset_depth = read_depth(centerw[0], centerw[1]);
	offset = fmax(base_depth - offset_depth, 0.0);
	if (offset < 2.0 * mmin || offset > 2.0 * mmax) {
		if (offset < 2.0 * mmin)
			printf("Depth-slope related offset was too small");
		else
			printf("Depth-slope related offset was too large");
		printf("; first failure at:\n");
		printf("\tAngle = %f degrees, axis = (%f, %f, %f)\n",
			aa->angle, aa->axis[0], aa->axis[1], aa->axis[2]);
		printf("\tFailing offset was %.16f\n", offset);
		printf("\tAllowable range is (%f, %f)\n", 2.0 * mmin,
			2.0 * mmax);
		printf("\tglPolygonOffset(-2.0, 0.0);\n");
		printf("\tglReadPixels returned %f\n", offset_depth);
		return false;
	}

	return true;
}

static bool
check_slope_offsets(GLdouble* ideal_mrd_near)
{
	/* This function checks that the implementation is offsetting
	 * primitives correctly according to their depth slopes.  (Note that
	 * it uses some values computed by find_ideal_mrd, so that function
	 * must be run first.)
	 */
	unsigned i;

	/* Rotation angles (degrees) and axes for which offset will be checked
	 */
	static const struct angle_axis aa[] = {
		{ 0,	{1, 0, 0}},
		{30,	{1, 0, 0}},
		{45,	{1, 0, 0}},
		{60,	{1, 0, 0}},
		{80,	{1, 0, 0}},
		{ 0,	{0, 1, 0}},
		{30,	{0, 1, 0}},
		{45,	{0, 1, 0}},
		{60,	{0, 1, 0}},
		{80,	{0, 1, 0}},
		{ 0,	{1, 1, 0}},
		{30,	{1, 1, 0}},
		{45,	{1, 1, 0}},
		{60,	{1, 1, 0}},
		{80,	{1, 1, 0}},
		{ 0,	{2, 1, 0}},
		{30,	{2, 1, 0}},
		{45,	{2, 1, 0}},
		{60,	{2, 1, 0}},
		{80,	{2, 1, 0}}
	};

	for (i = 0; i < ARRAY_SIZE(aa); ++i)
		if (!check_slope_offset(aa + i, ideal_mrd_near))
			return false;

	return true;
} /* check_slope_offsets */

static void
log_mrd(const char *pre, double mrd, GLint dbits)
{
	int bits;
	bits = (int)(0.5 + (pow(2.0, dbits) - 1.0) * mrd);
	printf("%s %e (nominally %i %s)\n", pre, mrd, bits,
		(bits == 1) ? "bit": "bits");
} /* log_mrd */

enum piglit_result
piglit_display(void)
{
	bool pass = true;
	double ideal_mrd, actual_mrd;
	GLdouble ideal_mrd_near, ideal_mrd_far, next_to_near, next_to_far;
	GLdouble actual_mrd_near, actual_mrd_far;
	bool big_enough_mrd, small_enough_mrd;
	GLint dbits;

	/* The following projection matrix places the near clipping plane at
	 * distance 1.0, and the far clipping plane at infinity.  This allows
	 * us to stress depth-buffer resolution as far away from the eye as
	 * possible, without introducing code that depends on the size or
	 * format of the depth buffer.
	 *
	 * To derive this matrix, start with the matrix generated by glFrustum
	 * with near-plane distance equal to 1.0, and take the limit of the
	 * matrix elements as the far-plane distance goes to infinity.
	 */
	static const GLfloat near_1_far_infinity[] = {
		1.0,  0.0,  0.0,  0.0,
		0.0,  1.0,  0.0,  0.0,
		0.0,  0.0, -1.0, -1.0,
		0.0,  0.0, -2.0,  0.0
	};

	glViewport(0, 0, piglit_width, piglit_height);

	glMatrixMode(GL_PROJECTION);
	glLoadMatrixf(near_1_far_infinity);

	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();

	glDepthFunc(GL_LESS);
	glDisable(GL_POLYGON_OFFSET_FILL);
	glClearDepth(1.0);

	find_ideal_mrd(&ideal_mrd_near, &ideal_mrd_far,
		&next_to_near, &next_to_far);
	find_actual_mrd(&next_to_near, &next_to_far,
		&actual_mrd_near, &actual_mrd_far);
	ideal_mrd = fmax(ideal_mrd_near, ideal_mrd_far);
	actual_mrd = fmax(actual_mrd_near, actual_mrd_far);
	big_enough_mrd = (actual_mrd >= 0.99 * ideal_mrd);
	small_enough_mrd = (actual_mrd <= 2.0 * ideal_mrd);

	pass = big_enough_mrd && small_enough_mrd && pass;
	pass = check_slope_offsets(&ideal_mrd_near) && pass;

	/* Print the results */
	if (!big_enough_mrd) {
		printf("Actual MRD is too small ");
		printf("(may cause incorrect results)\n");
	}

	if (!small_enough_mrd) {
		printf("Actual MRD is too large ");
		printf("(may waste depth-buffer range)\n\n");
	}

	glGetIntegerv(GL_DEPTH_BITS, &dbits);
	printf("GL_DEPTH_BITS = %d\n", dbits);
	log_mrd("Ideal  MRD at near plane is", ideal_mrd_near, dbits);
	log_mrd("Actual MRD at near plane is", actual_mrd_near, dbits);
	log_mrd("Ideal  MRD at infinity is", ideal_mrd_far, dbits);
	log_mrd("Actual MRD at infinity is", actual_mrd_far, dbits);

	return pass ? PIGLIT_PASS : PIGLIT_FAIL;
}
