/*
 * Copyright © 2019 Advanced Micro Devices, Inc.
 *
 * 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.
 */

#include "piglit-util-gl.h"

PIGLIT_GL_TEST_CONFIG_BEGIN

	config.supports_gl_compat_version = 12;

	config.window_visual = PIGLIT_GL_VISUAL_RGBA |
		PIGLIT_GL_VISUAL_DOUBLE;
	config.khr_no_error_support = PIGLIT_HAS_ERRORS;

PIGLIT_GL_TEST_CONFIG_END

void
piglit_init(int argc, char **argv)
{
	piglit_require_extension("GL_EXT_direct_state_access");

}

static GLuint
init_texture(GLenum target, int* image_size, float** expected_pixels)
{
	GLuint tex;

	const int height = (target == GL_TEXTURE_1D) ? 1 : piglit_height;
	const int depth = (target == GL_TEXTURE_3D) ? 2 : 1;
	float* image = piglit_rgbw_image(GL_RGBA, piglit_width, height * depth,
					 false, GL_UNSIGNED_NORMALIZED);
	*image_size = piglit_width * height * depth * 4 * sizeof(float);

	glGenTextures(1, &tex);
	glTextureParameteriEXT(tex, target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
	glTextureParameteriEXT(tex, target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
	glTextureParameteriEXT(tex, target, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
	glTextureParameteriEXT(tex, target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
	glTextureParameteriEXT(tex, target, GL_TEXTURE_MIN_FILTER,GL_NEAREST);
	if (target == GL_TEXTURE_1D) {
		glTextureImage1DEXT(tex, target, 0, GL_RGBA,
				    piglit_width, 0,
				    GL_RGBA, GL_FLOAT, image);
	} else if (target == GL_TEXTURE_2D) {
		glTextureImage2DEXT(tex, target, 0, GL_RGBA,
				    piglit_width, height, 0,
				    GL_RGBA, GL_FLOAT, image);
	} else {
		/* 2 layers 3D image */
		glTextureImage3DEXT(tex, target, 0, GL_RGBA,
		    piglit_width, height, depth, 0,
		    GL_RGBA, GL_FLOAT, image);
	}

	if (expected_pixels != NULL) {
		*expected_pixels = image;
	} else {
		free(image);
	}

	return tex;
}

static GLenum
dimension_to_target(int n)
{
	assert(n == 1 || n == 2 || n == 3);
	switch (n) {
		case 1: return GL_TEXTURE_1D;
		case 2: return GL_TEXTURE_2D;
		case 3:
		default:
			return GL_TEXTURE_3D;
	}
}

static GLenum use_display_list = GL_NONE;
static GLuint list;

static enum piglit_result
test_TextureImageNDEXT(void* data)
{
	bool pass = true;
	const int n = (int)(intptr_t) data;
	const GLenum target = dimension_to_target(n);
	int image_size;
	float* expected_pixels, *got_pixels;
	GLuint tex;

	if (use_display_list != GL_NONE)
		glNewList(list, use_display_list);

	tex = init_texture(target, &image_size, &expected_pixels);

	if (use_display_list != GL_NONE)
		glEndList(list);

	if (use_display_list == GL_COMPILE) {
		/* Texture shouldn't have been initialized yet */
		pass = !glIsTexture(tex);
		glCallList(list);
	}

	got_pixels = (float*) malloc(image_size);
	glGetTextureImageEXT(tex, target, 0, GL_RGBA, GL_FLOAT, got_pixels);

	pass = memcmp(expected_pixels, got_pixels, image_size) == 0 && pass;

	free(expected_pixels);
	free(got_pixels);

	/* The GL_EXT_direct_state_access spec says:
	 *
	 *    INVALID_OPERATION is generated [...] if the target parameter does
	 *    not match the target type of the texture object named by the texture
	 *    parameter.
	 */
	if (n == 2) {
		glTextureImage2DEXT(tex, GL_TEXTURE_CUBE_MAP_POSITIVE_X, 0, GL_RGBA,
				    piglit_width, piglit_height, 0,
				    GL_RGBA, GL_FLOAT, got_pixels);
		pass = piglit_check_gl_error(GL_INVALID_OPERATION) && pass;
	}

	glDeleteTextures(1, &tex);

	return pass ? PIGLIT_PASS : PIGLIT_FAIL;
}

static enum piglit_result
test_TextureSubImageNDEXT(void* data)
{
	bool pass = true;
	int i;
	const int n = (int)(intptr_t) data;
	const GLenum target = dimension_to_target(n);
	int image_size;
	float* original_pixels, *modified_pixels, *got_pixels;
	GLuint tex = init_texture(target, &image_size, &original_pixels);
	int len = image_size / sizeof(float);

	if (use_display_list != GL_NONE)
		glNewList(list, use_display_list);

	/* Replace the whole texture using glTextureSubImageNDEXT, line by line */
	modified_pixels = (float*) malloc(image_size);
	for (i = 0; i < len; i++) {
		modified_pixels[i] = original_pixels[(i + 1) % len];
	}

	if (n == 1) {
		glTextureSubImage1DEXT(tex, target, 0,
				       0, piglit_width,
				       GL_RGBA, GL_FLOAT,
				       modified_pixels);
	} else {
		for (i = 0; i < piglit_height; i++) {
			if (n == 2) {
				glTextureSubImage2DEXT(tex,
					target, 0,
					0, i, piglit_width, 1,
					GL_RGBA, GL_FLOAT,
					&modified_pixels[piglit_width * 4 * i]);
			} else {
				/* Update the 1st layer of the 3D image */
				glTextureSubImage3DEXT(tex,
					target, 0,
					0, i, 0, piglit_width, 1, 1,
					GL_RGBA, GL_FLOAT,
					&modified_pixels[piglit_width * 4 * i]);
				/* And the 2nd layer */
				glTextureSubImage3DEXT(tex,
					target, 0,
					0, i, 1, piglit_width, 1, 1,
					GL_RGBA, GL_FLOAT,
					&modified_pixels[piglit_width * 4 * (i + piglit_height)]);
			}
		}
	}

	if (use_display_list != GL_NONE)
		glEndList(list);

	got_pixels = (float*) malloc(image_size);
	glGetTextureImageEXT(tex, target, 0, GL_RGBA, GL_FLOAT, got_pixels);

	if (use_display_list == GL_COMPILE) {
		/* Texture shouldn't have been modified yet */
		pass = memcmp(original_pixels, got_pixels, image_size) == 0 && pass;
		glCallList(list);
		/* Re-read */
		glGetTextureImageEXT(tex, target, 0, GL_RGBA, GL_FLOAT, got_pixels);
	}

	pass = memcmp(modified_pixels, got_pixels, image_size) == 0 && pass;

	/* Verify error added by the extension:
	     "INVALID_OPERATION is generated [...] if the target parameter does
	      not match the target type of the texture object named by the texture
	      parameter."
	 */
	if (n == 2) {
		glTextureSubImage2DEXT(tex,
			GL_TEXTURE_CUBE_MAP_POSITIVE_X, 0,
			0, 0, 4, 1,
			GL_RGBA, GL_FLOAT,
			modified_pixels);
		pass = piglit_check_gl_error(GL_INVALID_OPERATION) && pass;
	}

	glDeleteTextures(1, &tex);
	free(modified_pixels);
	free(original_pixels);

	return pass ? PIGLIT_PASS : PIGLIT_FAIL;
}

static enum piglit_result
test_CopyTextureImageNDEXT(void* data)
{
	bool pass = true;
	const int n = (int)(intptr_t) data;
	assert(n == 1 || n == 2);
	const GLenum target = dimension_to_target(n);
	int image_size;
	float* original_pixels;
	GLuint tex = init_texture(target, &image_size, &original_pixels);
	const int height = (target == GL_TEXTURE_1D) ? 1 : piglit_height;
	float* got_pixels;

	glClearColor(0.25, 0.5, 0.75, 1);
	glClear(GL_COLOR_BUFFER_BIT);

	if (use_display_list != GL_NONE)
		glNewList(list, use_display_list);

	switch (target) {
		case GL_TEXTURE_1D:
			glCopyTextureImage1DEXT(tex, target, 0, GL_RGBA, 0, 0, piglit_width, 0);
			break;
		case GL_TEXTURE_2D:
			glCopyTextureImage2DEXT(tex, target, 0, GL_RGBA, 0, 0, piglit_width, piglit_height, 0);
			break;
	}

	if (use_display_list != GL_NONE)
		glEndList(list);

	got_pixels = (float*) malloc(piglit_width * height * 4 * sizeof(float));

	/* Compare glGetTextureImageEXT and on screen pixels */
	glGetTextureImageEXT(tex, target, 0, GL_RGBA, GL_FLOAT, got_pixels);

	if (use_display_list == GL_COMPILE) {
		/* Texture shouldn't have been modified yet */
		pass = memcmp(got_pixels, original_pixels, image_size) == 0;
		glCallList(list);
		glGetTextureImageEXT(tex, target, 0, GL_RGBA, GL_FLOAT, got_pixels);
	}

	pass = piglit_probe_rect_rgba(0, 0, piglit_width, height, got_pixels) && pass;

	free(got_pixels);
	free(original_pixels);

	glDeleteTextures(1, &tex);

	return pass ? PIGLIT_PASS : PIGLIT_FAIL;
}

static enum piglit_result
test_CopyTextureSubImageNDEXT(void* data)
{
	bool pass = true;
	const int n = (int)(intptr_t) data;
	const GLenum target = dimension_to_target(n);
	int image_size;
	float* original_pixels;
	GLuint tex = init_texture(target, &image_size, &original_pixels);
	const int height = (target == GL_TEXTURE_1D) ? 1 : piglit_height;
	float* got_pixels;

	glClearColor(0.25, 0.5, 0.75, 1);
	glClear(GL_COLOR_BUFFER_BIT);

	if (use_display_list != GL_NONE)
		glNewList(list, use_display_list);

	switch (target) {
		case GL_TEXTURE_1D:
			glCopyTextureSubImage1DEXT(tex, target, 0, 0, 0, 0, piglit_width);
			break;
		case GL_TEXTURE_2D:
			glCopyTextureSubImage2DEXT(tex, target, 0, 0, 0, 0, 0, piglit_width, piglit_height);
			break;
		case GL_TEXTURE_3D:
			glCopyTextureSubImage3DEXT(tex, target, 0, 0, 0, 0, 0, 0, piglit_width, piglit_height);
			break;
	}

	if (use_display_list != GL_NONE)
		glEndList(list);

	got_pixels = (float*) malloc(image_size);

	/* Compare glGetTextureImageEXT and on screen pixels */
	glGetTextureImageEXT(tex, target, 0, GL_RGBA, GL_FLOAT, got_pixels);

	if (use_display_list == GL_COMPILE) {
		/* Texture shouldn't have been modified yet */
		pass = memcmp(got_pixels, original_pixels, image_size) == 0;
		glCallList(list);
		glGetTextureImageEXT(tex, target, 0, GL_RGBA, GL_FLOAT, got_pixels);
	}

	pass = piglit_probe_rect_rgba(0, 0, piglit_width, height, got_pixels) && pass;

	free(got_pixels);
	free(original_pixels);

	glDeleteTextures(1, &tex);

	return pass ? PIGLIT_PASS : PIGLIT_FAIL;
}

static enum piglit_result
test_TextureParameterEXT(void* data)
{
	GLuint tex[2];
	static const GLenum targets[] = {
		GL_TEXTURE_1D, GL_TEXTURE_2D, GL_TEXTURE_3D,
		GL_TEXTURE_CUBE_MAP,
	};
	int i, j, k, value;
	bool pass = true;

	void (*textureParameteri_variant) (GLuint, GLenum, GLenum, GLint*) = data;

	struct pname_value {
		GLenum pname;
		int value_count;
		int values[8];
	} tested[] = {
		{
			GL_TEXTURE_WRAP_S,
			5,
			{
				GL_CLAMP, GL_CLAMP_TO_EDGE, GL_REPEAT,
				GL_CLAMP_TO_BORDER, GL_MIRRORED_REPEAT
			}
		},
		{
			GL_TEXTURE_WRAP_R,
			5,
			{
				GL_CLAMP, GL_CLAMP_TO_EDGE, GL_REPEAT,
				GL_CLAMP_TO_BORDER, GL_MIRRORED_REPEAT
			}
		},
		{
			GL_TEXTURE_WRAP_T,
			5,
			{
				GL_CLAMP, GL_CLAMP_TO_EDGE, GL_REPEAT,
				GL_CLAMP_TO_BORDER, GL_MIRRORED_REPEAT
			}
		},
		{
			GL_TEXTURE_MIN_FILTER,
			6,
			{
				GL_NEAREST, GL_LINEAR,
				GL_NEAREST_MIPMAP_LINEAR, GL_NEAREST_MIPMAP_NEAREST,
				GL_LINEAR_MIPMAP_LINEAR, GL_LINEAR_MIPMAP_NEAREST
			}
		},
		{
			GL_TEXTURE_MAG_FILTER,
			2,
			{
				GL_NEAREST, GL_LINEAR
			}
		},
		{
			GL_TEXTURE_BASE_LEVEL,
			1, { rand() }
		},
		{
			GL_TEXTURE_MAX_LEVEL,
			1, { rand() }
		},
		{
			GL_DEPTH_TEXTURE_MODE,
			4,
			{
				GL_RED, GL_LUMINANCE, GL_INTENSITY, GL_ALPHA
			}
		},
		{
			GL_TEXTURE_COMPARE_MODE,
			2,
			{
				GL_NONE, GL_COMPARE_REF_TO_TEXTURE
			}
		},
		{
			GL_TEXTURE_COMPARE_FUNC,
			8,
			{
				GL_LEQUAL, GL_GEQUAL, GL_LESS, GL_GREATER,
				GL_EQUAL, GL_NOTEQUAL, GL_ALWAYS, GL_NEVER
			}
		},
		{
			GL_GENERATE_MIPMAP,
			2,
			{
				GL_TRUE, GL_FALSE
			}
		},
	};

	if (!textureParameteri_variant) {
		return PIGLIT_SKIP;
	}

	for (i = 0; i < ARRAY_SIZE(targets); i++) {
		GLenum target = targets[i];

		glGenTextures(ARRAY_SIZE(tex), tex);

		for (j = 0; j < ARRAY_SIZE(tested); j++) {
			for (k = 0; k < tested[j].value_count; k++) {
				if (tested[j].values[k] == GL_RED && !piglit_is_extension_supported("GL_ARB_texture_rg"))
					continue;

				int original_value;
				if (use_display_list != GL_NONE)
					glNewList(list, use_display_list);

				glGetTextureParameterivEXT(tex[1], target,
					tested[j].pname, &original_value);

				(*textureParameteri_variant)(
					tex[1], target,
					tested[j].pname,
					&tested[j].values[k]);

				if (use_display_list != GL_NONE)
					glEndList(list);

				if (use_display_list == GL_COMPILE) {
					int v;
					glGetTextureParameterivEXT(tex[1], target,
						tested[j].pname, &v);
					pass = (v == original_value) && pass;
					glCallList(list);
				}

				glGetTextureParameterivEXT(tex[1], target, tested[j].pname, &value);

				if (value != tested[j].values[k]) {
					piglit_loge("TextureParameter*EXT(%s, %s, ...) failed. Expected %d but got %d\n",
						piglit_get_gl_enum_name(target),
						piglit_get_gl_enum_name(tested[j].pname),
						tested[j].values[k],
						value);
					return PIGLIT_FAIL;
				}
				 if (!piglit_check_gl_error(GL_NO_ERROR)) {
					piglit_loge("TextureParameter*EXT(%s, %s, ...) failed.\n",
						piglit_get_gl_enum_name(target),
						piglit_get_gl_enum_name(tested[j].pname));
					return PIGLIT_FAIL;
				}
			}
		}
		glDeleteTextures(ARRAY_SIZE(tex), tex);
	}
	return pass ? PIGLIT_PASS : PIGLIT_FAIL;
}

static enum piglit_result
test_TextureParameterfEXT(void* data)
{
	GLuint tex[2];
	static const GLenum targets[] = {
		GL_TEXTURE_1D, GL_TEXTURE_2D, GL_TEXTURE_3D,
		GL_TEXTURE_1D_ARRAY, GL_TEXTURE_2D_ARRAY,
		GL_TEXTURE_CUBE_MAP,
	};
	int i, j, k, l;
	bool pass = true;
	float value;

	const struct pname_value {
		GLenum pname;
		int value_count;
		float values[8];
	} tested[] = {
		{
			GL_TEXTURE_PRIORITY,
			1, { (float) rand() / RAND_MAX }
		},
		{
			GL_TEXTURE_MIN_LOD,
			1, { (float)rand() }
		},
		{
			GL_TEXTURE_MAX_LOD,
			1, { (float)rand() }
		},
		{
			GL_TEXTURE_LOD_BIAS,
			1, { (float)rand() }
		}
	};

	for (i = 0; i < ARRAY_SIZE(targets); i++) {
		GLenum target = targets[i];

		if ((target == GL_TEXTURE_1D_ARRAY || target == GL_TEXTURE_2D_ARRAY) &&
			!piglit_is_extension_supported("GL_EXT_texture_array")) {
			continue;
		}

		glGenTextures(ARRAY_SIZE(tex), tex);

		for (j = 0; j < ARRAY_SIZE(tested); j++) {
			for (k = 0; k < tested[j].value_count; k++) {
				float original_values[2];

				if (use_display_list != GL_NONE)
					glNewList(list, use_display_list);

				for (l = 0; l < 2; l++) {
					glGetTextureParameterfvEXT(tex[l], target,
						tested[j].pname, &original_values[l]);
				}

				glTextureParameterfEXT(
					tex[0], target,
					tested[j].pname,
					tested[j].values[k]);

				glTextureParameterfvEXT(
					tex[1], target,
					tested[j].pname,
					&tested[j].values[k]);

				if (use_display_list != GL_NONE)
					glEndList(list);

				if (use_display_list == GL_COMPILE) {
					for (l = 0; l < 2; l++) {
						float v;
						glGetTextureParameterfvEXT(tex[l], target,
							tested[j].pname, &v);
						pass = v == original_values[l] && pass;
					}
					glCallList(list);
				}

				for (l = 0; l < 2; l++) {
					glGetTextureParameterfvEXT(tex[l], target, tested[j].pname, &value);

					if (value != tested[j].values[k]) {
						piglit_loge("%s(%s, %s, ...) failed. Expected %f but got %f\n",
							l == 0 ? "glTextureParameterfEXT" : "glTextureParameterfvEXT",
							piglit_get_gl_enum_name(target),
							piglit_get_gl_enum_name(tested[j].pname),
							tested[j].values[k],
							value);
						return PIGLIT_FAIL;
					}
					 if (!piglit_check_gl_error(GL_NO_ERROR)) {
						piglit_loge("%s(%s, %s, ...) failed.\n",
							l == 0 ? "glTextureParameterfEXT" : "glTextureParameterfvEXT",
							piglit_get_gl_enum_name(target),
							piglit_get_gl_enum_name(tested[j].pname));
						return PIGLIT_FAIL;
					}
				}
			}
		}
		glDeleteTextures(ARRAY_SIZE(tex), tex);
	}
	return pass ? PIGLIT_PASS : PIGLIT_FAIL;
}

enum piglit_result
test_EnableDisableEXT(void* data)
{
	/* The GL_EXT_direct_state_access spec says:
	 *
	 * The following commands (introduced by EXT_draw_buffers2):
	 *
	 * 	void EnableIndexedEXT(enum cap, uint index);
	 * 	void DisableIndexedEXT(enum cap, uint index);
	 *
	 * are equivalent (assuming no errors) to the following:
	 *
	 * 	ActiveTexture(TEXTURE0+index);
	 * 	XXX(cap);
	 *
	 * [...] when the cap parameter is one of the texture-related
	 * enable token depending on the active texture state, namely
	 * TEXTURE_1D, TEXTURE_2D, TEXTURE_3D, TEXTURE_CUBE_MAP,
	 * TEXTURE_RECTANGLE_ARB, TEXTURE_GEN_S, TEXTURE_GEN_T,
	 * TEXTURE_GEN_R, or TEXTURE_GEN_Q.
	 */
	static const GLenum caps[] = {
		GL_TEXTURE_1D, GL_TEXTURE_2D, GL_TEXTURE_3D, GL_TEXTURE_CUBE_MAP,
		GL_TEXTURE_GEN_S, GL_TEXTURE_GEN_T, GL_TEXTURE_GEN_R,
    		GL_TEXTURE_GEN_Q
	};

	int i, max_texture_units;
	glGetIntegerv(GL_MAX_TEXTURE_UNITS, &max_texture_units);

	for (i = 0; i < ARRAY_SIZE(caps); i++) {
		const GLenum cap = caps[i];

		const int index = rand() % max_texture_units;
		const bool value = rand() % 2;
		GLboolean got;
		int got_i;

		glActiveTexture(GL_TEXTURE0 + (index + 1) % max_texture_units);

		if (use_display_list != GL_NONE)
			glNewList(list, use_display_list);

		if (value) {
			glEnableIndexedEXT(cap, index);
		} else {
			glDisableIndexedEXT(cap, index);
		}

		if (use_display_list != GL_NONE)
			glEndList(list);

		if (use_display_list == GL_COMPILE)
			glCallList(list);

		/* Read back with glIsEnabledIndexedEXT */
		if (value != glIsEnabledIndexedEXT(cap, index)) {
			piglit_loge("gl%sIndexedEXT(%s, %d) / glIsEnabledIndexedEXT failed.\n",
				    value ? "Enable" : "Disable",
				    piglit_get_gl_enum_name(cap),
				    index);
			return PIGLIT_FAIL;
		}

		/* Read back with glGetBooleanIndexedvEXT */
		glGetBooleanIndexedvEXT(cap, index, &got);
		if (value != got) {
			piglit_loge("gl%sIndexedEXT(%s, %d) / glGetBooleanIndexedvEXT failed.\n",
				    value ? "Enable" : "Disable",
				    piglit_get_gl_enum_name(cap),
				    index);
			return PIGLIT_FAIL;
		}

		/* Read back with glGetBooleanIndexedvEXT */
		glGetIntegerIndexedvEXT(cap, index, &got_i);
		if (value != got_i) {
			piglit_loge("gl%sIndexedEXT(%s, %d) / glGetIntegerIndexedvEXT failed.\n",
				    value ? "Enable" : "Disable",
				    piglit_get_gl_enum_name(cap),
				    index);
			return PIGLIT_FAIL;
		}
	}
	return PIGLIT_PASS;
}


static enum piglit_result
test_TextureProxyTarget(void* data)
{
	bool pass = true;
	GLint width;
	GLfloat height;
	const int n = (int)(intptr_t) data;
	const GLenum target = dimension_to_target(n);

	GLenum proxy_target = (target == GL_TEXTURE_1D) ? GL_PROXY_TEXTURE_1D :
		(target == GL_TEXTURE_2D ? GL_PROXY_TEXTURE_2D : GL_PROXY_TEXTURE_3D);

	GLuint tex;
	glGenTextures(1, &tex);

	/*   The GL_EXT_direct_state_access says:
	 *
	 *    Proxy targets work with the glTex* (glTexImage*)
         *    and glGetTex* (glGetTexLevelParameter*) commands that support
         *    proxy textures BUT the texture name must be 0 to avoid a
         *    GL_INVALID_OPERATION error.
	 */
	if (n == 1) {
		glTextureImage1DEXT(tex, proxy_target, 0, GL_RGBA,
				    piglit_width, 0,
				    GL_RGBA, GL_FLOAT, NULL);
	} else if (n == 2) {
		glTextureImage2DEXT(tex, proxy_target, 0, GL_RGBA,
				    piglit_width, piglit_height, 0,
				    GL_RGBA, GL_FLOAT, NULL);
	} else if (n == 3) {
		glTextureImage3DEXT(tex, proxy_target, 0, GL_RGBA,
				    piglit_width, piglit_height, 1, 0,
				    GL_RGBA, GL_FLOAT, NULL);
	}
	pass = piglit_check_gl_error(GL_INVALID_OPERATION) && pass;

	if (n == 1) {
		glTextureImage1DEXT(0, proxy_target, 0, GL_RGBA,
				    piglit_width, 0,
				    GL_RGBA, GL_FLOAT, NULL);
	} else if (n == 2) {
		glTextureImage2DEXT(0, proxy_target, 0, GL_RGBA,
				    piglit_width, piglit_height, 0,
				    GL_RGBA, GL_FLOAT, NULL);
	} else if (n == 3) {
		glTextureImage3DEXT(0, proxy_target, 0, GL_RGBA,
				    piglit_width, piglit_height, 1, 0,
				    GL_RGBA, GL_FLOAT, NULL);
	}
        glGetTextureLevelParameterivEXT(0, proxy_target, 0,
                                        GL_TEXTURE_WIDTH, &width);
        glGetTextureLevelParameterfvEXT(0, proxy_target, 0,
                                        GL_TEXTURE_HEIGHT, &height);
        pass = width == piglit_width && pass;
        pass = (int)height == (n > 1 ? piglit_height : 1) && pass;

	glDeleteTextures(1, &tex);

	return pass && piglit_check_gl_error(GL_NO_ERROR) ? PIGLIT_PASS : PIGLIT_FAIL;
}

void TextureParameteriEXT_wrapper(GLuint texture, GLenum target, GLenum pname, GLint* value) {
	glTextureParameteriEXT(texture, target, pname, *value);
}

void TextureParameterIuivEXT_wrapper(GLuint texture, GLenum target, GLenum pname, GLint* value) {
	glTextureParameterIuivEXT(texture, target, pname, (GLuint*) value);
}

enum piglit_result
piglit_display(void)
{
	int i;
	const bool have_ext_tex_int = piglit_is_extension_supported("GL_EXT_texture_integer");
	struct piglit_subtest tests[] = {
		{
			"TextureParameteriEXT",
			NULL,
			test_TextureParameterEXT,
			TextureParameteriEXT_wrapper
		},
		{
			"TextureParameterivEXT",
			NULL,
			test_TextureParameterEXT,
			glTextureParameterivEXT
		},
		{
			"TextureParameterIivEXT",
			NULL,
			test_TextureParameterEXT,
			have_ext_tex_int ? glTextureParameterIivEXT : NULL
		},
		{
			"TextureParameterIuivEXT",
			NULL,
			test_TextureParameterEXT,
			have_ext_tex_int ? TextureParameterIuivEXT_wrapper : NULL
		},
		{
			"TextureParameterfEXT",
			NULL,
			test_TextureParameterfEXT,
			NULL
		},
		{
			"TextureImage1DEXT",
			NULL,
			test_TextureImageNDEXT,
			(void*) 1
		},
		{
			"TextureImage2DEXT",
			NULL,
			test_TextureImageNDEXT,
			(void*) 2
		},
		{
			"TextureImage3DEXT",
			NULL,
			test_TextureImageNDEXT,
			(void*) 3
		},
		{
			"TextureSubImage1DEXT",
			NULL,
			test_TextureSubImageNDEXT,
			(void*) 1
		},
		{
			"TextureSubImage2DEXT",
			NULL,
			test_TextureSubImageNDEXT,
			(void*) 2
		},
		{
			"TextureSubImage3DEXT",
			NULL,
			test_TextureSubImageNDEXT,
			(void*) 3
		},
		{
			"CopyTextureImage1DEXT",
			NULL,
			test_CopyTextureImageNDEXT,
			(void*) 1
		},
		{
			"CopyTextureImage2DEXT",
			NULL,
			test_CopyTextureImageNDEXT,
			(void*) 2
		},
		{
			"CopyTextureSubImage1DEXT",
			NULL,
			test_CopyTextureSubImageNDEXT,
			(void*) 1
		},
		{
			"CopyTextureSubImage2DEXT",
			NULL,
			test_CopyTextureSubImageNDEXT,
			(void*) 2
		},
		{
			"CopyTextureSubImage3DEXT",
			NULL,
			test_CopyTextureSubImageNDEXT,
			(void*) 3
		},
		{
			"EnableDisableEXT",
			NULL,
			test_EnableDisableEXT
		},
		{
			"GL_PROXY_TEXTURE_1D + glTex*",
			NULL,
			test_TextureProxyTarget,
			(void*) 1
		},
		{
			"GL_PROXY_TEXTURE_2D + glTex*",
			NULL,
			test_TextureProxyTarget,
			(void*) 2
		},
		{
			"GL_PROXY_TEXTURE_3D + glTex*",
			NULL,
			test_TextureProxyTarget,
			(void*) 3
		},
		{
			NULL
		}
	};

	enum piglit_result result = piglit_run_selected_subtests(tests, NULL, 0, PIGLIT_PASS);
	list = glGenLists(1);

	/* Re-run the same test but using display list GL_COMPILE */
	for (i = 0; tests[i].name; i++) {
		char* test_name_display_list;
		asprintf(&test_name_display_list, "%s + display list GL_COMPILE", tests[i].name);
		tests[i].name = test_name_display_list;
	}
	use_display_list = GL_COMPILE;
	result = piglit_run_selected_subtests(tests, NULL, 0, result);

	/* Re-run the same test but using display list GL_COMPILE_AND_EXECUTE */
	for (i = 0; tests[i].name; i++) {
		char* test_name_display_list;
		asprintf(&test_name_display_list, "%s_AND_EXECUTE", tests[i].name);
		tests[i].name = test_name_display_list;
	}
	use_display_list = GL_COMPILE_AND_EXECUTE;
	result = piglit_run_selected_subtests(tests, NULL, 0, result);

	glDeleteLists(list, 1);

	return result;
}
