/*
 * Copyright (c) 2016 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.
 */

/**
 * \file gs-stream-location-aliasing.c
 *
 * Tests two vertex streams produced by a geometry shader, with location
 * aliasing between the outputs of the different streams.
 *
 * Loosely based on transform_feedback_layout_qualifiers.c
 */

#include "piglit-util-gl.h"

PIGLIT_GL_TEST_CONFIG_BEGIN

	config.supports_gl_compat_version = 32;
	config.supports_gl_core_version = 32;

PIGLIT_GL_TEST_CONFIG_END

static const char vs_text[] =
	"#version 150\n"
	"out int vertexid;\n"
	"void main() {\n"
	"  vertexid = gl_VertexID;\n"
	"}\n";

static const char gs_text[] =
	"#version 150\n"
	"#extension GL_ARB_gpu_shader5: require\n"
	"#extension GL_ARB_separate_shader_objects: require\n"
	"#extension GL_ARB_enhanced_layouts: require\n"
	"\n"
	"layout(points, invocations = 2) in;\n"
	"layout(points, max_vertices = 2) out;\n"
	"\n"
	"in int vertexid[];\n"
	"\n"
	"layout(xfb_offset = 0, xfb_buffer = 0, location = 0, component = 0, stream = 0) out float x1_out;\n"
	"layout(xfb_offset = 0, xfb_buffer = 1, location = 0, component = 1, stream = 1) out float x2_out;\n"
	"\n"
	"void main() {\n"
	"  x1_out = 100 + 10 * vertexid[0] + gl_InvocationID;\n"
	"  EmitStreamVertex(0);\n"
	"  EndStreamPrimitive(0);\n"
	"  x2_out = 200 + 10 * vertexid[0] + gl_InvocationID;\n"
	"  EmitStreamVertex(1);\n"
	"  EndStreamPrimitive(1);\n"
	"}";

static const int num_in_vertices = 2;
static const int num_xfb_results_per_stream = 2 * 2;

static void
build_and_use_program()
{
	GLuint prog;

	prog = piglit_build_simple_program_multiple_shaders(
			GL_VERTEX_SHADER, vs_text,
			GL_GEOMETRY_SHADER, gs_text, 0);

	glLinkProgram(prog);
	if (!piglit_link_check_status(prog))
		piglit_report_result(PIGLIT_FAIL);
	if (!piglit_check_gl_error(GL_NO_ERROR))
		piglit_report_result(PIGLIT_FAIL);

	glUseProgram(prog);
}

static bool
probe_buffers(const GLuint *xfb)
{
	bool pass = true;
	float *first;
	float *second;

	first = malloc(num_xfb_results_per_stream * sizeof(float));
	second = malloc(num_xfb_results_per_stream * sizeof(float));

	unsigned i = 0;
	for (unsigned vertex = 0; vertex < 2; ++vertex) {
		for (unsigned invocation = 0; invocation < 2; ++invocation) {
			first[i] = 100 + 10 * vertex + invocation;
			second[i] = 200 + 10 * vertex + invocation;
			i++;
		}
	}
	assert(i == num_xfb_results_per_stream);

	pass = piglit_probe_buffer(xfb[0], GL_TRANSFORM_FEEDBACK_BUFFER,
			"first", 1, num_xfb_results_per_stream, first) && pass;
	pass = piglit_probe_buffer(xfb[1], GL_TRANSFORM_FEEDBACK_BUFFER,
			"second", 1, num_xfb_results_per_stream, second) &&
			pass;

	free(first);
	free(second);

	return pass;
}

void
piglit_init(int argc, char **argv)
{
	bool pass = true;
	GLuint xfb[2];
	GLuint vao;

	piglit_require_GLSL_version(150);
	piglit_require_extension("GL_ARB_transform_feedback3");
	piglit_require_extension("GL_ARB_enhanced_layouts");

	build_and_use_program();
	glEnable(GL_RASTERIZER_DISCARD);

	/* Set up the transform feedback buffers. */
	glGenBuffers(2, xfb);
	glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, xfb[0]);
	glBufferData(GL_TRANSFORM_FEEDBACK_BUFFER,
		     num_xfb_results_per_stream * 4, NULL, GL_STREAM_READ);
	glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 1, xfb[1]);
	glBufferData(GL_TRANSFORM_FEEDBACK_BUFFER,
		     num_xfb_results_per_stream * 4, NULL, GL_STREAM_READ);

	/* Test is run under desktop OpenGL 3.2 -> use of VAOs is required */
	glGenVertexArrays(1, &vao);
	glBindVertexArray(vao);

	/* Draw and record */
	glBeginTransformFeedback(GL_POINTS);
	glDrawArrays(GL_POINTS, 0, num_in_vertices);
	glEndTransformFeedback();
	glDeleteVertexArrays(1, &vao);

	if (!piglit_check_gl_error(GL_NO_ERROR))
		piglit_report_result(PIGLIT_FAIL);

	pass = probe_buffers(xfb);

	glDeleteBuffers(2, xfb);

	piglit_report_result(pass ? PIGLIT_PASS : PIGLIT_FAIL);
}

enum piglit_result
piglit_display(void)
{
	/* Should never be reached */
	return PIGLIT_FAIL;
}
