1/*-------------------------------------------------------------------------
2 * drawElements Quality Program OpenGL ES 3.0 Module
3 * -------------------------------------------------
4 *
5 * Copyright 2014 The Android Open Source Project
6 *
7 * Licensed under the Apache License, Version 2.0 (the "License");
8 * you may not use this file except in compliance with the License.
9 * You may obtain a copy of the License at
10 *
11 *      http://www.apache.org/licenses/LICENSE-2.0
12 *
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS,
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
18 *
19 *//*!
20 * \file
21 * \brief GLSL ES 1.0 gl_FragData[] tests.
22 *//*--------------------------------------------------------------------*/
23
24#include "es3fShaderFragDataTests.hpp"
25
26#include "glsShaderLibrary.hpp"
27
28#include "gluRenderContext.hpp"
29#include "gluShaderProgram.hpp"
30#include "gluDrawUtil.hpp"
31#include "gluPixelTransfer.hpp"
32#include "gluObjectWrapper.hpp"
33
34#include "tcuRenderTarget.hpp"
35#include "tcuStringTemplate.hpp"
36#include "tcuTestLog.hpp"
37#include "tcuSurface.hpp"
38
39#include "glwFunctions.hpp"
40#include "glwEnums.hpp"
41
42namespace deqp
43{
44namespace gles3
45{
46namespace Functional
47{
48
49using std::string;
50using tcu::TestLog;
51
52namespace
53{
54
55enum IndexExprType
56{
57	INDEX_EXPR_STATIC	= 0,
58	INDEX_EXPR_UNIFORM,
59	INDEX_EXPR_DYNAMIC,
60
61	INDEX_EXPR_TYPE_LAST
62};
63
64static bool compareSingleColor (tcu::TestLog& log, const tcu::Surface& surface, tcu::RGBA expectedColor, tcu::RGBA threshold)
65{
66	const int	maxPrints			= 10;
67	int			numFailedPixels		= 0;
68
69	log << TestLog::Message << "Expecting " << expectedColor << " with threshold " << threshold << TestLog::EndMessage;
70
71	for (int y = 0; y < surface.getHeight(); y++)
72	{
73		for (int x = 0; x < surface.getWidth(); x++)
74		{
75			const tcu::RGBA		resultColor		= surface.getPixel(x, y);
76			const bool			isOk			= compareThreshold(resultColor, expectedColor, threshold);
77
78			if (!isOk)
79			{
80				if (numFailedPixels < maxPrints)
81					log << TestLog::Message << "ERROR: Got " << resultColor << " at (" << x << ", " << y << ")!" << TestLog::EndMessage;
82				else if (numFailedPixels == maxPrints)
83					log << TestLog::Message << "..." << TestLog::EndMessage;
84
85				numFailedPixels += 1;
86			}
87		}
88	}
89
90	if (numFailedPixels > 0)
91	{
92		log << TestLog::Message << "Found " << numFailedPixels << " invalid pixels, comparison FAILED!" << TestLog::EndMessage;
93		log << TestLog::Image("ResultImage", "Result Image", surface);
94		return false;
95	}
96	else
97	{
98		log << TestLog::Message << "Image comparison passed." << TestLog::EndMessage;
99		return true;
100	}
101}
102
103class FragDataIndexingCase : public TestCase
104{
105public:
106	FragDataIndexingCase (Context& context, const char* name, const char* description, IndexExprType indexExprType)
107		: TestCase			(context, name, description)
108		, m_indexExprType	(indexExprType)
109	{
110	}
111
112	static glu::ProgramSources genSources (const IndexExprType indexExprType)
113	{
114		const char* const	fragIndexExpr	= indexExprType == INDEX_EXPR_STATIC	? "0"				:
115											  indexExprType == INDEX_EXPR_UNIFORM	? "u_index"			:
116											  indexExprType == INDEX_EXPR_DYNAMIC	? "int(v_index)"	: DE_NULL;
117		glu::ProgramSources	sources;
118
119		DE_ASSERT(fragIndexExpr);
120
121		sources << glu::VertexSource(
122			"attribute highp vec4 a_position;\n"
123			"attribute highp float a_index;\n"
124			"attribute highp vec4 a_color;\n"
125			"varying mediump float v_index;\n"
126			"varying mediump vec4 v_color;\n"
127			"void main (void)\n"
128			"{\n"
129			"	gl_Position = a_position;\n"
130			"	v_color = a_color;\n"
131			"	v_index = a_index;\n"
132			"}\n");
133
134		sources << glu::FragmentSource(string(
135			"varying mediump vec4 v_color;\n"
136			"varying mediump float v_index;\n"
137			"uniform mediump int u_index;\n"
138			"void main (void)\n"
139			"{\n"
140			"	gl_FragData[") + fragIndexExpr + "] = v_color;\n"
141			"}\n");
142
143		return sources;
144	}
145
146	IterateResult iterate (void)
147	{
148		const glu::RenderContext&		renderCtx		= m_context.getRenderContext();
149		const glw::Functions&			gl				= renderCtx.getFunctions();
150		const glu::ShaderProgram		program			(renderCtx, genSources(m_indexExprType));
151		const int						viewportW		= de::min(renderCtx.getRenderTarget().getWidth(), 128);
152		const int						viewportH		= de::min(renderCtx.getRenderTarget().getHeight(), 128);
153
154		const float positions[] =
155		{
156			-1.0f, -1.0f,
157			+1.0f, -1.0f,
158			-1.0f, +1.0f,
159			+1.0f, +1.0f
160		};
161		const float colors[] =
162		{
163			0.0f, 1.0f, 0.0f, 1.0f,
164			0.0f, 1.0f, 0.0f, 1.0f,
165			0.0f, 1.0f, 0.0f, 1.0f,
166			0.0f, 1.0f, 0.0f, 1.0f
167		};
168		const float		indexValues[]	= { 0.0f, 0.0f, 0.0f, 0.0f };
169		const deUint8	indices[]		= { 0, 1, 2, 2, 1, 3 };
170
171		const glu::VertexArrayBinding vertexArrays[] =
172		{
173			glu::va::Float("a_position",	2, 4, 0, &positions[0]),
174			glu::va::Float("a_color",		4, 4, 0, &colors[0]),
175			glu::va::Float("a_index",		1, 4, 0, &indexValues[0])
176		};
177
178		m_testCtx.getLog() << program;
179
180		if (!program.isOk())
181		{
182			if (m_indexExprType == INDEX_EXPR_STATIC)
183				TCU_FAIL("Compile failed");
184			else
185				throw tcu::NotSupportedError("Dynamic indexing of gl_FragData[] not supported");
186		}
187
188		gl.clearColor	(1.0f, 0.0f, 0.0f, 1.0f);
189		gl.clear		(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT|GL_STENCIL_BUFFER_BIT);
190
191		gl.viewport		(0, 0, viewportW, viewportH);
192		gl.useProgram	(program.getProgram());
193		gl.uniform1i	(gl.getUniformLocation(program.getProgram(), "u_index"), 0);
194
195		glu::draw(renderCtx, program.getProgram(), DE_LENGTH_OF_ARRAY(vertexArrays), &vertexArrays[0],
196				  glu::pr::Triangles(DE_LENGTH_OF_ARRAY(indices), &indices[0]));
197		GLU_EXPECT_NO_ERROR(gl.getError(), "Rendering failed");
198
199		{
200			tcu::Surface		result		(viewportW, viewportH);
201			const tcu::RGBA		threshold	= renderCtx.getRenderTarget().getPixelFormat().getColorThreshold() + tcu::RGBA(1,1,1,1);
202			bool				isOk;
203
204			glu::readPixels(renderCtx, 0, 0, result.getAccess());
205			GLU_EXPECT_NO_ERROR(gl.getError(), "Reading pixels failed");
206
207			isOk = compareSingleColor(m_testCtx.getLog(), result, tcu::RGBA::green(), threshold);
208
209			m_testCtx.setTestResult(isOk ? QP_TEST_RESULT_PASS	: QP_TEST_RESULT_FAIL,
210									isOk ? "Pass"				: "Image comparison failed");
211		}
212
213		return STOP;
214	}
215
216private:
217	const IndexExprType m_indexExprType;
218};
219
220class FragDataDrawBuffersCase : public TestCase
221{
222public:
223	FragDataDrawBuffersCase (Context& context)
224		: TestCase(context, "draw_buffers", "gl_FragData[] and glDrawBuffers() interaction")
225	{
226	}
227
228	IterateResult iterate (void)
229	{
230		const glu::RenderContext&		renderCtx		= m_context.getRenderContext();
231		const glu::ShaderProgram		program			(renderCtx, glu::ProgramSources()
232															<< glu::VertexSource(
233																"attribute highp vec4 a_position;\n"
234																"attribute highp vec4 a_color;\n"
235																"varying mediump vec4 v_color;\n"
236																"void main (void)\n"
237																"{\n"
238																"	gl_Position = a_position;\n"
239																"	v_color = a_color;\n"
240																"}\n")
241															<< glu::FragmentSource(
242																"varying mediump vec4 v_color;\n"
243																"uniform mediump int u_index;\n"
244																"void main (void)\n"
245																"{\n"
246																"	gl_FragData[u_index] = v_color;\n"
247																"}\n"));
248		const glw::Functions&			gl				= renderCtx.getFunctions();
249		const int						width			= 128;
250		const int						height			= 128;
251		const int						indexLoc		= program.isOk() ? gl.getUniformLocation(program.getProgram(), "u_index") : -1;
252		const glu::Framebuffer			fbo				(renderCtx);
253		const glu::Renderbuffer			colorBuf0		(renderCtx);
254		const glu::Renderbuffer			colorBuf1		(renderCtx);
255
256		const float positions[] =
257		{
258			-1.0f, -1.0f,
259			+1.0f, -1.0f,
260			-1.0f, +1.0f,
261			+1.0f, +1.0f
262		};
263		const float colors[] =
264		{
265			0.0f, 1.0f, 0.0f, 1.0f,
266			0.0f, 1.0f, 0.0f, 1.0f,
267			0.0f, 1.0f, 0.0f, 1.0f,
268			0.0f, 1.0f, 0.0f, 1.0f
269		};
270		const deUint8	indices[]		= { 0, 1, 2, 2, 1, 3 };
271
272		const glu::VertexArrayBinding vertexArrays[] =
273		{
274			glu::va::Float("a_position",	2, 4, 0, &positions[0]),
275			glu::va::Float("a_color",		4, 4, 0, &colors[0])
276		};
277
278		m_testCtx.getLog() << program;
279
280		if (!program.isOk())
281			throw tcu::NotSupportedError("Dynamic indexing of gl_FragData[] not supported");
282
283		gl.bindFramebuffer(GL_FRAMEBUFFER, *fbo);
284		for (int ndx = 0; ndx < 2; ndx++)
285		{
286			const deUint32	rbo	= ndx == 0 ? *colorBuf0 : *colorBuf1;
287
288			gl.bindRenderbuffer(GL_RENDERBUFFER, rbo);
289			gl.renderbufferStorage(GL_RENDERBUFFER, GL_RGBA8, width, height);
290			gl.framebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0+ndx, GL_RENDERBUFFER, rbo);
291		}
292		TCU_CHECK(gl.checkFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE);
293
294		{
295			const deUint32 drawBuffers[] = { GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1 };
296			gl.drawBuffers(DE_LENGTH_OF_ARRAY(drawBuffers), &drawBuffers[0]);
297		}
298
299		gl.clearBufferfv(GL_COLOR, 0, tcu::RGBA::red().toVec().getPtr());
300		gl.clearBufferfv(GL_COLOR, 1, tcu::RGBA::red().toVec().getPtr());
301
302		gl.viewport		(0, 0, width, height);
303		gl.useProgram	(program.getProgram());
304
305		GLU_EXPECT_NO_ERROR(gl.getError(), "Setup failed");
306
307		m_testCtx.getLog() << TestLog::Message << "Drawing to attachments 0 and 1, expecting only attachment 0 to change." << TestLog::EndMessage;
308
309		for (int ndx = 0; ndx < 2; ndx++)
310		{
311			gl.uniform1i(indexLoc, ndx);
312			glu::draw(renderCtx, program.getProgram(), DE_LENGTH_OF_ARRAY(vertexArrays), &vertexArrays[0],
313					  glu::pr::Triangles(DE_LENGTH_OF_ARRAY(indices), &indices[0]));
314		}
315		GLU_EXPECT_NO_ERROR(gl.getError(), "Rendering failed");
316
317		{
318			tcu::Surface		result		(width, height);
319			const tcu::RGBA		threshold	= renderCtx.getRenderTarget().getPixelFormat().getColorThreshold() + tcu::RGBA(1,1,1,1);
320			bool				allOk		= true;
321
322			for (int ndx = 0; ndx < 2; ndx++)
323			{
324				m_testCtx.getLog() << TestLog::Message << "Verifying attachment " << ndx << "..." << TestLog::EndMessage;
325
326				gl.readBuffer(GL_COLOR_ATTACHMENT0+ndx);
327				glu::readPixels(renderCtx, 0, 0, result.getAccess());
328				GLU_EXPECT_NO_ERROR(gl.getError(), "Reading pixels failed");
329
330				if (!compareSingleColor(m_testCtx.getLog(), result, ndx == 0 ? tcu::RGBA::green() : tcu::RGBA::red(), threshold))
331					allOk = false;
332			}
333
334			m_testCtx.setTestResult(allOk ? QP_TEST_RESULT_PASS	: QP_TEST_RESULT_FAIL,
335									allOk ? "Pass"				: "Image comparison failed");
336		}
337
338		return STOP;
339	}
340};
341
342} // anonymous
343
344ShaderFragDataTests::ShaderFragDataTests (Context& context)
345	: TestCaseGroup(context, "fragdata", "gl_FragData[] Tests")
346{
347}
348
349ShaderFragDataTests::~ShaderFragDataTests (void)
350{
351}
352
353void ShaderFragDataTests::init (void)
354{
355	addChild(new FragDataIndexingCase		(m_context, "valid_static_index",	"Valid gl_FragData[] assignment using static index",	INDEX_EXPR_STATIC));
356	addChild(new FragDataIndexingCase		(m_context, "valid_uniform_index",	"Valid gl_FragData[] assignment using uniform index",	INDEX_EXPR_UNIFORM));
357	addChild(new FragDataIndexingCase		(m_context, "valid_dynamic_index",	"Valid gl_FragData[] assignment using dynamic index",	INDEX_EXPR_DYNAMIC));
358	addChild(new FragDataDrawBuffersCase	(m_context));
359
360	// Negative cases.
361	{
362		gls::ShaderLibrary library(m_testCtx, m_context.getRenderContext(), m_context.getContextInfo());
363		std::vector<tcu::TestNode*> negativeCases = library.loadShaderFile("shaders/fragdata.test");
364
365		for (std::vector<tcu::TestNode*>::iterator i = negativeCases.begin(); i != negativeCases.end(); i++)
366			addChild(*i);
367	}
368}
369
370} // Functional
371} // gles3
372} // deqp
373