#include "piglit-util-gl.h"

PIGLIT_GL_TEST_CONFIG_BEGIN

	config.supports_gl_compat_version = 11;
	config.supports_gl_core_version = 31;

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

PIGLIT_GL_TEST_CONFIG_END

enum { NOSTAGE, VS, FS } stage = NOSTAGE;
enum { NONE = -1, RED, GREEN, BLUE, ALPHA, ZERO, ONE } swizzle = NONE;
enum { UNORM_T, FLOAT_T, INT_T, UINT_T, SHADOW_T, NUM_COMPTYPES } comptype = UNORM_T;
enum { SAMPLER_2D, SAMPLER_2DARRAY, SAMPLER_CUBE, SAMPLER_CUBEARRAY, SAMPLER_2DRECT } sampler = SAMPLER_2D;
bool use_offset = false;
bool use_nonconst = false;
bool use_offsets = false;
int components = 0;
int comp_select = -1;

int min_offset = 0;
int max_offset = 0;

int texture_width = 32;
int texture_height = 32;
int max_components;

GLenum internalformat_for_components[][4] = {
	{ GL_R16, GL_RG16, GL_RGB16, GL_RGBA16, },
	{ GL_R32F, GL_RG32F, GL_RGB32F, GL_RGBA32F, },
	{ GL_R16I, GL_RG16I, GL_RGB16I, GL_RGBA16I },
	{ GL_R16UI, GL_RG16UI, GL_RGB16UI, GL_RGBA16UI },
	{ GL_DEPTH_COMPONENT, 0, 0, 0 },
};
GLenum format_for_components[][4] = {
	{ GL_RED, GL_RG, GL_RGB, GL_RGBA },
	{ GL_RED, GL_RG, GL_RGB, GL_RGBA },
	{ GL_RED_INTEGER, GL_RG_INTEGER, GL_RGB_INTEGER,  GL_RGBA_INTEGER },
	{ GL_RED_INTEGER, GL_RG_INTEGER, GL_RGB_INTEGER,  GL_RGBA_INTEGER },
	{ GL_DEPTH_COMPONENT, 0, 0, 0 },
};
GLenum swizzles[] = { GL_RED, GL_GREEN, GL_BLUE, GL_ALPHA, GL_ZERO, GL_ONE };
int slices_for_sampler[] = { 1, 3, 6, 12, 1 };
GLenum target_for_sampler[] = { GL_TEXTURE_2D, GL_TEXTURE_2D_ARRAY, GL_TEXTURE_CUBE_MAP, GL_TEXTURE_CUBE_MAP_ARRAY, GL_TEXTURE_RECTANGLE };
GLenum address_mode = GL_REPEAT;

unsigned char *pixels;
float *expected;

enum piglit_result
piglit_display(void)
{
	bool pass = true;

	glViewport(0, 0, texture_width, texture_height);
	glClearColor(0.4, 0.4, 0.4, 0.4);
	glClear(GL_COLOR_BUFFER_BIT);

	if (swizzle >= 0) {
		GLint sw[] = { GL_ZERO, GL_ZERO, GL_ZERO, GL_ZERO };
		if (comp_select != -1)
			sw[comp_select] = swizzles[swizzle];
		else
			sw[0] = swizzles[swizzle];
		glTexParameteriv(target_for_sampler[sampler], GL_TEXTURE_SWIZZLE_RGBA, sw);
	}

	if (stage == FS)
		glDrawArrays(GL_TRIANGLES, 0, 6);
	else
		glDrawArrays(GL_POINTS, 0, texture_width * texture_height);

	pass = piglit_probe_rect_rgba_varying(1, 1,
					      texture_width - 2,
					      texture_height - 2,
					      expected + (texture_width + 1) * 4,
					      texture_width * 4 * sizeof(float));

	piglit_present_results();

	return pass ? PIGLIT_PASS : PIGLIT_FAIL;
}

/* TODO:
 * Test other sampler types: gsampler2D|gsampler2DArray|gsamplerCube|gsamplerCubeArray
 * Test GS texturing too -- Paul?
 */

static unsigned char
pixel_value(int i, int j, int offset_sel)
{
	if (swizzle == ZERO)
		return 0;
	if (swizzle == ONE)
		return 255;

	if (use_offset) {
		/* apply texel offset */
		i += min_offset;
		j += max_offset;
	} else if (use_offsets) {
		switch (offset_sel) {
		case 0:
			i += min_offset;
			j += max_offset;
			break;
		case 1:
			i += max_offset;
			j += min_offset;
			break;
		case 2:
			i += 3;
			j += 3;
			break;
		case 3:
			i += -3;
			j += -3;
			break;
		}
	}

	if (address_mode == GL_REPEAT) {
		/* WRAP at border */
		i += texture_width;
		j += texture_height;
		i %= texture_width;
		j %= texture_height;
	}
	else if (address_mode == GL_CLAMP_TO_EDGE) {
		if (i < 0) i = 0;
		if (j < 0) j = 0;
		if (i > texture_width - 1) i = texture_width - 1;
		if (j > texture_height - 1) j = texture_height - 1;
	}
	else if (address_mode == GL_MIRRORED_REPEAT) {
		bool isOdd;
		if (i < 0)
			i = -i - 1;
		isOdd = (i / texture_width) & 1;
		i = i % texture_width;
		if (isOdd)
			i = texture_width - i - 1;
		if (j < 0)
			j = -j - 1;
		isOdd = (j / texture_height) & 1;
		j = j % texture_height;
		if (isOdd)
			j = texture_height - j - 1;
	}
	else if (address_mode == GL_MIRROR_CLAMP_TO_EDGE) {
		if (i < 0)
			i = -i - 1;
		if (i > texture_width - 1)
			i = texture_width - 1;
		if (j < 0)
			j = -j - 1;
		if (j > texture_height - 1)
			j = texture_height - 1;
	}

	return i + j * texture_width;
}

static float
norm_value(int x)
{
	return (float)x / 255.0f;
}

static float
normalized_value(int component, int x)
{
	if (swizzle >= 0 && max_components < 4 && component > 0)
		return 0;
	else
		return (float)x / 255.0f;
}

static void
make_image(int num_channels, int use_channel)
{
	unsigned char *pp = pixels;
	int i, j, ch;

	for (j = 0; j < texture_height; j++)
		for (i = 0; i < texture_width; i++)
			for (ch = 0; ch < num_channels; ch++)
				*pp++ = (ch == use_channel) ? (i+j*texture_width) : 128;
}

static float shadow_compare(int component, float x)
{
	if (swizzle >= 0 && max_components < 4 && component > 0)
		return 0;
        else
		return x > 0.5f ? 1.0f : 0.0f;
}

static void
make_expected(void)
{
	float *pe = expected;
	int i, j;

	for (j = 0; j < texture_height; j++)
		for (i = 0; i < texture_width; i++) {
			if (comptype == SHADOW_T) {
				if (use_offsets) {
					*pe++ = shadow_compare(0, norm_value(pixel_value(i, j, 0)));
					*pe++ = shadow_compare(1, norm_value(pixel_value(i, j, 1)));
					*pe++ = shadow_compare(2, norm_value(pixel_value(i, j, 2)));
					*pe++ = shadow_compare(3, norm_value(pixel_value(i, j, 3)));
				} else {
					*pe++ = shadow_compare(0, norm_value(pixel_value(i, j + 1, 0)));
					*pe++ = shadow_compare(1, norm_value(pixel_value(i + 1, j + 1, 0)));
					*pe++ = shadow_compare(2, norm_value(pixel_value(i + 1, j, 0)));
					*pe++ = shadow_compare(3, norm_value(pixel_value(i, j, 0)));
				}
			}
			else {
				if (use_offsets) {
					*pe++ = normalized_value(0, pixel_value(i, j, 0));
					*pe++ = normalized_value(1, pixel_value(i, j, 1));
					*pe++ = normalized_value(2, pixel_value(i, j, 2));
					*pe++ = normalized_value(3, pixel_value(i, j, 3));
				} else {
					*pe++ = normalized_value(0, pixel_value(i, j + 1, 0));
					*pe++ = normalized_value(1, pixel_value(i + 1, j + 1, 0));
					*pe++ = normalized_value(2, pixel_value(i + 1, j, 0));
					*pe++ = normalized_value(3, pixel_value(i, j, 0));
				}
			}
		}
}

static void
upload_verts(void)
{
	if (stage == VS) {
		size_t size = 4 * texture_width * texture_height * sizeof(float);
		float *v = (float *)malloc(size);
		float *pv = v;
		int i, j;
		for (j = 0; j < texture_height; j++)
			for (i = 0; i < texture_width; i++) {
				*pv++ = (i + 0.5f) * 2 / texture_width - 1;
				*pv++ = (j + 0.5f) * 2 / texture_height - 1;
				*pv++ = 0;
				*pv++ = 1;
			}
		glBufferData(GL_ARRAY_BUFFER, size, v, GL_STATIC_DRAW);
		free(v);
	}
	else {
		static const float verts[] = {
			-1, -1, 0, 1,
			-1, 1, 0, 1,
			1, 1, 0, 1,
			-1, -1, 0, 1,
			1, 1, 0, 1,
			1, -1, 0, 1,
		};
		glBufferData(GL_ARRAY_BUFFER, sizeof(verts), verts, GL_STATIC_DRAW);
	}
}

void
do_requires(void)
{
	piglit_require_GLSL_version(130);
	piglit_require_extension("GL_ARB_texture_gather");

	/* check whether component count will actually work */
	glGetIntegerv(GL_MAX_PROGRAM_TEXTURE_GATHER_COMPONENTS_ARB, &max_components);
	if (components > max_components) {
		printf("Test requires gather from texture with %d components;"
		       "This implementation only supports %d\n",
		       components, max_components);
		piglit_report_result(PIGLIT_SKIP);
	}

        /* get the offset limits */
        glGetIntegerv(GL_MIN_PROGRAM_TEXTURE_GATHER_OFFSET_ARB, &min_offset);
        glGetIntegerv(GL_MAX_PROGRAM_TEXTURE_GATHER_OFFSET_ARB, &max_offset);

        /* increase width/height if necessary */
        if (use_offset || use_offsets) {
           texture_width = MAX2(texture_width, (max_offset + 1) * 2);
           texture_height = MAX2(texture_height, (max_offset + 1) * 2);
        }

	/* if we are trying to swizzle, check that we can! */
	if (swizzle != -1)
		piglit_require_extension("GL_EXT_texture_swizzle");

	/* check the sampler type we want actually exists */
	if (sampler == SAMPLER_CUBEARRAY)
		piglit_require_extension("GL_ARB_texture_cube_map_array");

	if ((use_offsets || use_offset) && (sampler == SAMPLER_CUBE || sampler == SAMPLER_CUBEARRAY)) {
		printf("Offset is not supported with cube or cube array samplers.\n");
		piglit_report_result(PIGLIT_SKIP);
	}

	if (comptype == SHADOW_T && components > 1) {
		printf("Shadow supported with single-component textures only\n");
		piglit_report_result(PIGLIT_SKIP);
	}

	if (comptype == SHADOW_T && comp_select != -1) {
		printf("Shadow not supported with component select parameter\n");
		piglit_report_result(PIGLIT_SKIP);
	}

	/* if we are trying to specify the component from the shader,
	 * or use non-constant offsets, or use shadow comparitor, or
	 * use gsampler2DRect, check that we have ARB_gpu_shader5
	 */
	if (comp_select != -1 || use_offsets || use_nonconst || comptype == SHADOW_T || sampler == SAMPLER_2DRECT)
		piglit_require_extension("GL_ARB_gpu_shader5");

	/* if rect sampler, repeat is not available */
	if (sampler == SAMPLER_2DRECT && address_mode == GL_REPEAT) {
		printf("GL_REPEAT not supported with rectangle textures\n");
		piglit_report_result(PIGLIT_SKIP);
	}
}

static void
upload_2d(GLenum target, void *pixels)
{
	glTexImage2D(target, 0,
		     internalformat_for_components[comptype][components - 1],
		     texture_width, texture_height,
		     0, format_for_components[comptype][components-1],
		     GL_UNSIGNED_BYTE, pixels);
}

static void
upload_array_slice(GLenum target, int slice, void *pixels)
{
	glTexSubImage3D(target, 0, 0, 0, slice, texture_width, texture_height, 1,
			format_for_components[comptype][components-1],
			GL_UNSIGNED_BYTE, pixels);
}

static void
upload_3d(GLenum target, void *pixels)
{
	glTexImage3D(target, 0,
		     internalformat_for_components[comptype][components - 1],
		     texture_width, texture_height,
		     slices_for_sampler[sampler], 0,
		     format_for_components[comptype][components-1],
		     GL_UNSIGNED_BYTE, pixels);
}

static int
channel_to_fill(void) {
	if (swizzle != NONE)
		return swizzle;
	if (comp_select != NONE)
		return comp_select;
	return 0;
}

static void
do_texture_setup(void)
{
	GLuint tex;
	GLenum target = target_for_sampler[sampler];
	pixels = malloc(components * sizeof(unsigned char) * texture_width * texture_height);
	expected = malloc(4 * sizeof(float) * texture_width * texture_height);

	glGenTextures(1, &tex);
	glBindTexture(target, tex);

	make_image(components, channel_to_fill());
	make_expected();

	switch(sampler) {
	case SAMPLER_2D:
	case SAMPLER_2DRECT:
		upload_2d(target, pixels);
		break;
	case SAMPLER_2DARRAY:
		upload_3d(target, NULL);
		upload_array_slice(target, 1, pixels);
		break;
	case SAMPLER_CUBE:
		/* legacy cubes are weird. the only sane way to specify the whole
		 * thing at once is using glTexStorage, and we'd rather not rely on
		 * ARB_texture_storage just for that. */
		upload_2d(GL_TEXTURE_CUBE_MAP_NEGATIVE_X, NULL);
		upload_2d(GL_TEXTURE_CUBE_MAP_POSITIVE_X, NULL);
		upload_2d(GL_TEXTURE_CUBE_MAP_NEGATIVE_Y, NULL);
		upload_2d(GL_TEXTURE_CUBE_MAP_POSITIVE_Y, NULL);
		upload_2d(GL_TEXTURE_CUBE_MAP_NEGATIVE_Z, NULL);
		upload_2d(GL_TEXTURE_CUBE_MAP_POSITIVE_Z, pixels);
		break;
	case SAMPLER_CUBEARRAY:
		upload_3d(target, NULL);
		upload_array_slice(target, 10, pixels);
		break;
	}

	glTexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
	glTexParameteri(target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);

	if (comptype == SHADOW_T) {
		glTexParameteri(target_for_sampler[sampler], GL_TEXTURE_COMPARE_MODE,
				GL_COMPARE_R_TO_TEXTURE);
		glTexParameteri(target_for_sampler[sampler], GL_TEXTURE_COMPARE_FUNC,
				GL_LESS);
	}

	glTexParameteri(target_for_sampler[sampler], GL_TEXTURE_WRAP_S, address_mode);
	glTexParameteri(target_for_sampler[sampler], GL_TEXTURE_WRAP_T, address_mode);
	glTexParameteri(target_for_sampler[sampler], GL_TEXTURE_WRAP_R, address_mode);
}

static void
do_shader_setup(void)
{
	GLint prog;
	GLint sampler_loc, offset_loc;
	char *vs_code = NULL, *fs_code = NULL;
	char *offset_coords;
	char *prefix[] = { "" /* unorm */, "" /* float */, "i" /* int */, "u" /* uint */, "" /* shadow */ };
	char *scale[] = {
		"vec4(1)",		/* unorm + GL_ONE swizzle */
		"vec4(1)",		/* float */
		"vec4(1.0/255.0)",	/* int */
		"vec4(1.0/255.0)",	/* uint */
		"vec4(1)",		/* shadow */
	};
	char *samplersuffix[] = { "2D", "2DArray", "Cube", "CubeArray", "2DRect" };
	char *vs_tc_expr[] = {
		"0.5 * pos.xy + vec2(0.5)",
		"vec3(0.5 * pos.xy + vec2(0.5), 1)",
		"vec3(pos.x, -pos.y, 1)",		/* cube */
		"vec4(pos.x, -pos.y, 1, 1)",		/* cube array */
		"textureSize(s).xy * (0.5 * pos.xy + vec2(0.5))",		/* 2drect */
	};
	char *fs_tc_expr[] = {
		"gl_FragCoord.xy / textureSize(s, 0).xy",
		"vec3(gl_FragCoord.xy / textureSize(s, 0).xy, 1)",
		"vec3(vec2(2, -2) * (gl_FragCoord.xy / textureSize(s, 0).xy - vec2(0.5)), 1)",	/* cube */
		"vec4(vec2(2, -2) * (gl_FragCoord.xy / textureSize(s, 0).xy - vec2(0.5)), 1, 1)",	/* cube array */
		"gl_FragCoord.xy",		/* 2drect */
	};
	char *comp_expr[] = {"", ", 0", ", 1", ", 2", ", 3"};
	bool need_shader5 = (comp_select != -1) || use_offsets || use_nonconst || (comptype == SHADOW_T) || sampler == SAMPLER_2DRECT;

	if (use_offsets)
		(void)!asprintf(&offset_coords, "const ivec2 osets[4] = ivec2[4](ivec2(%d, %d), ivec2(%d, %d), ivec2(3, 3), ivec2(-3, -3));\n",
			 min_offset, max_offset, max_offset, min_offset);
	else if (use_offset)
		(void)!asprintf(&offset_coords, ", ivec2(%d,%d)", min_offset, max_offset);

	if (stage == VS) {
		(void)!asprintf(&vs_code, "#version %s\n"
				"#extension GL_ARB_explicit_attrib_location: require\n"
				"#extension GL_ARB_texture_gather: require\n"
				"%s"
				"%s"
				"\n"
				"layout(location=0) in vec4 pos;\n"
				"uniform %ssampler%s%s s;\n"
				"%s"
				"out vec4 c;\n"
				"\n"
				"void main() {\n"
				"	gl_Position = pos;\n"
				"	c = %s * textureGather%s(s, %s %s %s %s);\n"
				"}\n",
				need_shader5 ? "150" : "130",
				sampler == SAMPLER_CUBEARRAY ? "#extension GL_ARB_texture_cube_map_array: require\n" : "",
				need_shader5 ? "#extension GL_ARB_gpu_shader5: require\n" : "",
				prefix[comptype],
				samplersuffix[sampler],
				comptype == SHADOW_T ? "Shadow" : "",
				use_offsets ? offset_coords :use_nonconst ? "uniform ivec2 o1,o2;\n" : "",
				swizzle == ONE ? scale[0] : scale[comptype],
				use_offsets ? "Offsets" : (use_offset ? "Offset" : ""),
				vs_tc_expr[sampler],
				comptype == SHADOW_T ? ", 0.5" : "",
				use_offsets ? ", osets" : use_nonconst ? ", o1+o2" : use_offset ? offset_coords :  "",
				comp_expr[1 + comp_select]);
		(void)!asprintf(&fs_code,
				"#version %s\n"
				"\n"
				"in vec4 c;\n"
				"\n"
				"out vec4 fragColor;\n"
				"void main() {\n"
				"	fragColor = c;\n"
				"}\n",
				need_shader5 ? "150" : "130");
	}
	else {
		(void)!asprintf(&vs_code,
				"#version %s\n"
				"#extension GL_ARB_explicit_attrib_location: require\n"
				"layout(location=0) in vec4 pos;\n"
				"\n"
				"void main() {\n"
				"	gl_Position = pos;\n"
				"}\n",
				need_shader5 ? "150" : "130");
		(void)!asprintf(&fs_code,
				"#version %s\n"
				"#extension GL_ARB_texture_gather: require\n"
				"%s"
				"%s"
				"\n"
				"uniform %ssampler%s%s s;\n"
				"%s"
				"\n"
				"out vec4 fragColor;\n"
				"void main() {\n"
				"	fragColor = %s * textureGather%s(s, %s %s %s %s);\n"
				"}\n",
				need_shader5 ? "150" : "130",
				sampler == SAMPLER_CUBEARRAY ? "#extension GL_ARB_texture_cube_map_array: require\n" : "",
				need_shader5 ? "#extension GL_ARB_gpu_shader5: require\n" : "",
				prefix[comptype],
				samplersuffix[sampler],
				comptype == SHADOW_T ? "Shadow" : "",
				use_offsets ? offset_coords :use_nonconst ? "uniform ivec2 o1,o2;\n" : "",
				swizzle == ONE ? scale[0] : scale[comptype],
				use_offsets ? "Offsets" : (use_offset ? "Offset" : ""),
				fs_tc_expr[sampler],
				comptype == SHADOW_T ? ", 0.5" : "",
				use_offsets ? ", osets" : use_nonconst ? ", o1+o2" : use_offset ? offset_coords :  "",
				comp_expr[1 + comp_select]);
	}

	prog = piglit_build_simple_program(vs_code, fs_code);

	glUseProgram(prog);
	sampler_loc = glGetUniformLocation(prog, "s");
	glUniform1i(sampler_loc, 0);

	if (use_nonconst) {
		offset_loc = glGetUniformLocation(prog, "o1");
		glUniform2i(offset_loc, min_offset, 0);
		offset_loc = glGetUniformLocation(prog, "o2");
		glUniform2i(offset_loc, 0, max_offset);
	}
	free(vs_code);
	free(fs_code);
}

static void
do_geometry_setup(void)
{
	GLuint vbo;
	if (piglit_get_gl_version() >= 31) {
		GLuint vao;
		glGenVertexArrays(1, &vao);
		glBindVertexArray(vao);
	}

	glGenBuffers(1, &vbo);
	glBindBuffer(GL_ARRAY_BUFFER, vbo);
	upload_verts();
	glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 0, 0);
	glEnableVertexAttribArray(0);
}

void
fail_with_usage(void)
{
	printf("Usage: textureGather <stage> [offset] [nonconst] [offsets] <components> <swizzle> <comptype> <sampler> <compselect> <addressmode>\n"
	       "	stage = vs|fs\n"
	       "	components = r|rg|rgb|rgba\n"
	       "	swizzle = red|green|blue|alpha|zero|one\n"
	       "	comptype = unorm|float|uint|int|shadow\n"
	       "	sampler = 2D|2DArray|Cube|CubeArray|2DRect\n"
	       "	compselect = 0|1|2|3\n"
	       "	addressmode = repeat|clamp|mirror_repeat|mirror_clamp\n");
	piglit_report_result(PIGLIT_SKIP);
}

void
piglit_init(int argc, char **argv)
{
	int i;
	for (i = 1; i < argc; i++) {
		char *opt = argv[i];
		if (!strcmp(opt, "vs")) stage = VS;
		else if (!strcmp(opt, "fs")) stage = FS;
		else if (!strcmp(opt, "offset")) use_offset = true;
		else if (!strcmp(opt, "nonconst")) use_nonconst = true;
		else if (!strcmp(opt, "offsets")) use_offsets = true;
		else if (!strcmp(opt, "r")) components = 1;
		else if (!strcmp(opt, "rg")) components = 2;
		else if (!strcmp(opt, "rgb")) components = 3;
		else if (!strcmp(opt, "rgba")) components = 4;
		else if (!strcmp(opt, "red")) swizzle = 0;
		else if (!strcmp(opt, "green")) swizzle = 1;
		else if (!strcmp(opt, "blue")) swizzle = 2;
		else if (!strcmp(opt, "alpha")) swizzle = 3;
		else if (!strcmp(opt, "zero")) swizzle = 4;
		else if (!strcmp(opt, "one")) swizzle = 5;
		else if (!strcmp(opt, "unorm")) comptype = UNORM_T;
		else if (!strcmp(opt, "float")) comptype = FLOAT_T;
		else if (!strcmp(opt, "int")) comptype = INT_T;
		else if (!strcmp(opt, "uint")) comptype = UINT_T;
		else if (!strcmp(opt, "shadow")) comptype = SHADOW_T;
		else if (!strcmp(opt, "2D")) sampler = SAMPLER_2D;
		else if (!strcmp(opt, "2DArray")) sampler = SAMPLER_2DARRAY;
		else if (!strcmp(opt, "Cube")) sampler = SAMPLER_CUBE;
		else if (!strcmp(opt, "CubeArray")) sampler = SAMPLER_CUBEARRAY;
		else if (!strcmp(opt, "2DRect")) sampler = SAMPLER_2DRECT;
		else if (!strcmp(opt, "0")) comp_select = 0;
		else if (!strcmp(opt, "1")) comp_select = 1;
		else if (!strcmp(opt, "2")) comp_select = 2;
		else if (!strcmp(opt, "3")) comp_select = 3;
		else if (!strcmp(opt, "repeat")) address_mode = GL_REPEAT;
		else if (!strcmp(opt, "clamp")) address_mode = GL_CLAMP_TO_EDGE;
		else if (!strcmp(opt, "mirror_repeat")) address_mode = GL_MIRRORED_REPEAT;
		else if (!strcmp(opt, "mirror_clamp")) address_mode = GL_MIRROR_CLAMP_TO_EDGE;
	}

	if (stage == NOSTAGE) fail_with_usage();
	if (components == 0) fail_with_usage();

	if (use_nonconst) use_offset = true;

	do_requires();
	do_texture_setup();

	if (!piglit_check_gl_error(GL_NO_ERROR)) {
		printf("Error in texture setup\n");
		piglit_report_result(PIGLIT_FAIL);
	}

	do_shader_setup();

	if (!piglit_check_gl_error(GL_NO_ERROR)) {
		printf("Error in shader setup\n");
		piglit_report_result(PIGLIT_FAIL);
	}

	do_geometry_setup();

	if (!piglit_check_gl_error(GL_NO_ERROR)) {
		printf("Error in geometry setup\n");
		piglit_report_result(PIGLIT_FAIL);
	}
}
