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 
42 namespace deqp
43 {
44 namespace gles3
45 {
46 namespace Functional
47 {
48 
49 using std::string;
50 using tcu::TestLog;
51 
52 namespace
53 {
54 
55 enum IndexExprType
56 {
57 	INDEX_EXPR_STATIC	= 0,
58 	INDEX_EXPR_UNIFORM,
59 	INDEX_EXPR_DYNAMIC,
60 
61 	INDEX_EXPR_TYPE_LAST
62 };
63 
isExtensionSupported(const glu::RenderContext & renderCtx,const std::string & extension)64 static bool isExtensionSupported (const glu::RenderContext& renderCtx, const std::string& extension)
65 {
66 	const glw::Functions&	gl		= renderCtx.getFunctions();
67 	int						numExts	= 0;
68 
69 	gl.getIntegerv(GL_NUM_EXTENSIONS, &numExts);
70 
71 	for (int ndx = 0; ndx < numExts; ndx++)
72 	{
73 		const char* curExt = (const char*)gl.getStringi(GL_EXTENSIONS, ndx);
74 
75 		if (extension == curExt)
76 			return true;
77 	}
78 
79 	return false;
80 }
81 
compareSingleColor(tcu::TestLog & log,const tcu::Surface & surface,tcu::RGBA expectedColor,tcu::RGBA threshold)82 static bool compareSingleColor (tcu::TestLog& log, const tcu::Surface& surface, tcu::RGBA expectedColor, tcu::RGBA threshold)
83 {
84 	const int	maxPrints			= 10;
85 	int			numFailedPixels		= 0;
86 
87 	log << TestLog::Message << "Expecting " << expectedColor << " with threshold " << threshold << TestLog::EndMessage;
88 
89 	for (int y = 0; y < surface.getHeight(); y++)
90 	{
91 		for (int x = 0; x < surface.getWidth(); x++)
92 		{
93 			const tcu::RGBA		resultColor		= surface.getPixel(x, y);
94 			const bool			isOk			= compareThreshold(resultColor, expectedColor, threshold);
95 
96 			if (!isOk)
97 			{
98 				if (numFailedPixels < maxPrints)
99 					log << TestLog::Message << "ERROR: Got " << resultColor << " at (" << x << ", " << y << ")!" << TestLog::EndMessage;
100 				else if (numFailedPixels == maxPrints)
101 					log << TestLog::Message << "..." << TestLog::EndMessage;
102 
103 				numFailedPixels += 1;
104 			}
105 		}
106 	}
107 
108 	if (numFailedPixels > 0)
109 	{
110 		log << TestLog::Message << "Found " << numFailedPixels << " invalid pixels, comparison FAILED!" << TestLog::EndMessage;
111 		log << TestLog::Image("ResultImage", "Result Image", surface);
112 		return false;
113 	}
114 	else
115 	{
116 		log << TestLog::Message << "Image comparison passed." << TestLog::EndMessage;
117 		return true;
118 	}
119 }
120 
121 class FragDataIndexingCase : public TestCase
122 {
123 public:
FragDataIndexingCase(Context & context,const char * name,const char * description,IndexExprType indexExprType)124 	FragDataIndexingCase (Context& context, const char* name, const char* description, IndexExprType indexExprType)
125 		: TestCase			(context, name, description)
126 		, m_indexExprType	(indexExprType)
127 	{
128 	}
129 
genSources(const IndexExprType indexExprType)130 	static glu::ProgramSources genSources (const IndexExprType indexExprType)
131 	{
132 		const char* const	fragIndexExpr	= indexExprType == INDEX_EXPR_STATIC	? "0"				:
133 											  indexExprType == INDEX_EXPR_UNIFORM	? "u_index"			:
134 											  indexExprType == INDEX_EXPR_DYNAMIC	? "int(v_index)"	: DE_NULL;
135 		glu::ProgramSources	sources;
136 
137 		DE_ASSERT(fragIndexExpr);
138 
139 		sources << glu::VertexSource(
140 			"attribute highp vec4 a_position;\n"
141 			"attribute highp float a_index;\n"
142 			"attribute highp vec4 a_color;\n"
143 			"varying mediump float v_index;\n"
144 			"varying mediump vec4 v_color;\n"
145 			"void main (void)\n"
146 			"{\n"
147 			"	gl_Position = a_position;\n"
148 			"	v_color = a_color;\n"
149 			"	v_index = a_index;\n"
150 			"}\n");
151 
152 		sources << glu::FragmentSource(string(
153 			"varying mediump vec4 v_color;\n"
154 			"varying mediump float v_index;\n"
155 			"uniform mediump int u_index;\n"
156 			"void main (void)\n"
157 			"{\n"
158 			"	gl_FragData[") + fragIndexExpr + "] = v_color;\n"
159 			"}\n");
160 
161 		return sources;
162 	}
163 
iterate(void)164 	IterateResult iterate (void)
165 	{
166 		const glu::RenderContext&		renderCtx		= m_context.getRenderContext();
167 		const glw::Functions&			gl				= renderCtx.getFunctions();
168 		const glu::ShaderProgram		program			(renderCtx, genSources(m_indexExprType));
169 		const int						viewportW		= de::min(renderCtx.getRenderTarget().getWidth(), 128);
170 		const int						viewportH		= de::min(renderCtx.getRenderTarget().getHeight(), 128);
171 
172 		const float positions[] =
173 		{
174 			-1.0f, -1.0f,
175 			+1.0f, -1.0f,
176 			-1.0f, +1.0f,
177 			+1.0f, +1.0f
178 		};
179 		const float colors[] =
180 		{
181 			0.0f, 1.0f, 0.0f, 1.0f,
182 			0.0f, 1.0f, 0.0f, 1.0f,
183 			0.0f, 1.0f, 0.0f, 1.0f,
184 			0.0f, 1.0f, 0.0f, 1.0f
185 		};
186 		const float		indexValues[]	= { 0.0f, 0.0f, 0.0f, 0.0f };
187 		const deUint8	indices[]		= { 0, 1, 2, 2, 1, 3 };
188 
189 		const glu::VertexArrayBinding vertexArrays[] =
190 		{
191 			glu::va::Float("a_position",	2, 4, 0, &positions[0]),
192 			glu::va::Float("a_color",		4, 4, 0, &colors[0]),
193 			glu::va::Float("a_index",		1, 4, 0, &indexValues[0])
194 		};
195 
196 		m_testCtx.getLog() << program;
197 
198 		if (!program.isOk())
199 		{
200 			if (m_indexExprType == INDEX_EXPR_STATIC)
201 				TCU_FAIL("Compile failed");
202 			else
203 				throw tcu::NotSupportedError("Dynamic indexing of gl_FragData[] not supported");
204 		}
205 
206 		gl.clearColor	(1.0f, 0.0f, 0.0f, 1.0f);
207 		gl.clear		(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT|GL_STENCIL_BUFFER_BIT);
208 
209 		gl.viewport		(0, 0, viewportW, viewportH);
210 		gl.useProgram	(program.getProgram());
211 		gl.uniform1i	(gl.getUniformLocation(program.getProgram(), "u_index"), 0);
212 
213 		glu::draw(renderCtx, program.getProgram(), DE_LENGTH_OF_ARRAY(vertexArrays), &vertexArrays[0],
214 				  glu::pr::Triangles(DE_LENGTH_OF_ARRAY(indices), &indices[0]));
215 		GLU_EXPECT_NO_ERROR(gl.getError(), "Rendering failed");
216 
217 		{
218 			tcu::Surface		result		(viewportW, viewportH);
219 			const tcu::RGBA		threshold	= renderCtx.getRenderTarget().getPixelFormat().getColorThreshold() + tcu::RGBA(1,1,1,1);
220 			bool				isOk;
221 
222 			glu::readPixels(renderCtx, 0, 0, result.getAccess());
223 			GLU_EXPECT_NO_ERROR(gl.getError(), "Reading pixels failed");
224 
225 			isOk = compareSingleColor(m_testCtx.getLog(), result, tcu::RGBA::green(), threshold);
226 
227 			m_testCtx.setTestResult(isOk ? QP_TEST_RESULT_PASS	: QP_TEST_RESULT_FAIL,
228 									isOk ? "Pass"				: "Image comparison failed");
229 		}
230 
231 		return STOP;
232 	}
233 
234 private:
235 	const IndexExprType m_indexExprType;
236 };
237 
238 class FragDataDrawBuffersCase : public TestCase
239 {
240 public:
FragDataDrawBuffersCase(Context & context)241 	FragDataDrawBuffersCase (Context& context)
242 		: TestCase(context, "draw_buffers", "gl_FragData[] and glDrawBuffers() interaction")
243 	{
244 	}
245 
iterate(void)246 	IterateResult iterate (void)
247 	{
248 		const glu::RenderContext&		renderCtx			= m_context.getRenderContext();
249 
250 		int								num_test_attachment	= 2;
251 		if(!isExtensionSupported(renderCtx, "GL_EXT_draw_buffers") && !isExtensionSupported(renderCtx, "GL_NV_draw_buffers"))
252 			num_test_attachment = 1;
253 
254 		const glu::ShaderProgram		program			(renderCtx, glu::ProgramSources()
255 															<< glu::VertexSource(
256 																"attribute highp vec4 a_position;\n"
257 																"attribute highp vec4 a_color;\n"
258 																"varying mediump vec4 v_color;\n"
259 																"void main (void)\n"
260 																"{\n"
261 																"	gl_Position = a_position;\n"
262 																"	v_color = a_color;\n"
263 																"}\n")
264 															<< glu::FragmentSource(
265 																"varying mediump vec4 v_color;\n"
266 																"uniform mediump int u_index;\n"
267 																"void main (void)\n"
268 																"{\n"
269 																"	gl_FragData[u_index] = v_color;\n"
270 																"}\n"));
271 		const glw::Functions&			gl				= renderCtx.getFunctions();
272 		const int						width			= 128;
273 		const int						height			= 128;
274 		const int						indexLoc		= program.isOk() ? gl.getUniformLocation(program.getProgram(), "u_index") : -1;
275 		const glu::Framebuffer			fbo				(renderCtx);
276 		const glu::Renderbuffer			colorBuf0		(renderCtx);
277 		const glu::Renderbuffer			colorBuf1		(renderCtx);
278 
279 		const float positions[] =
280 		{
281 			-1.0f, -1.0f,
282 			+1.0f, -1.0f,
283 			-1.0f, +1.0f,
284 			+1.0f, +1.0f
285 		};
286 		const float colors[] =
287 		{
288 			0.0f, 1.0f, 0.0f, 1.0f,
289 			0.0f, 1.0f, 0.0f, 1.0f,
290 			0.0f, 1.0f, 0.0f, 1.0f,
291 			0.0f, 1.0f, 0.0f, 1.0f
292 		};
293 		const deUint8	indices[]		= { 0, 1, 2, 2, 1, 3 };
294 
295 		const glu::VertexArrayBinding vertexArrays[] =
296 		{
297 			glu::va::Float("a_position",	2, 4, 0, &positions[0]),
298 			glu::va::Float("a_color",		4, 4, 0, &colors[0])
299 		};
300 
301 		m_testCtx.getLog() << program;
302 
303 		if (!program.isOk())
304 			throw tcu::NotSupportedError("Dynamic indexing of gl_FragData[] not supported");
305 
306 		gl.bindFramebuffer(GL_FRAMEBUFFER, *fbo);
307 		for (int ndx = 0; ndx < num_test_attachment; ndx++)
308 		{
309 			const deUint32	rbo	= ndx == 0 ? *colorBuf0 : *colorBuf1;
310 
311 			gl.bindRenderbuffer(GL_RENDERBUFFER, rbo);
312 			gl.renderbufferStorage(GL_RENDERBUFFER, GL_RGBA8, width, height);
313 			gl.framebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0+ndx, GL_RENDERBUFFER, rbo);
314 		}
315 		TCU_CHECK(gl.checkFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE);
316 
317 		{
318 			const deUint32 drawBuffers[] = { GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1 };
319 			gl.drawBuffers(DE_LENGTH_OF_ARRAY(drawBuffers), &drawBuffers[0]);
320 		}
321 
322 		gl.viewport		(0, 0, width, height);
323 		gl.useProgram	(program.getProgram());
324 
325 		GLU_EXPECT_NO_ERROR(gl.getError(), "Setup failed");
326 
327 		bool			allOk		= true;
328 		const tcu::RGBA threshold	= renderCtx.getRenderTarget().getPixelFormat().getColorThreshold() + tcu::RGBA(1,1,1,1);
329 
330 		for (int ndx = 0; ndx < num_test_attachment; ndx++)
331 		{
332 			gl.clearBufferfv(GL_COLOR, 0, tcu::RGBA::red().toVec().getPtr());
333 			gl.clearBufferfv(GL_COLOR, 1, tcu::RGBA::red().toVec().getPtr());
334 
335 			m_testCtx.getLog() << TestLog::Message << "Drawing to attachments " << ndx << TestLog::EndMessage;
336 
337 			gl.uniform1i(indexLoc, ndx);
338 			glu::draw(renderCtx, program.getProgram(), DE_LENGTH_OF_ARRAY(vertexArrays), &vertexArrays[0],
339 					  glu::pr::Triangles(DE_LENGTH_OF_ARRAY(indices), &indices[0]));
340 
341 			GLU_EXPECT_NO_ERROR(gl.getError(), "Rendering failed");
342 
343 			{
344 				tcu::Surface result(width, height);
345 
346 				m_testCtx.getLog() << TestLog::Message << "Verifying attachment " << ndx << "..." << TestLog::EndMessage;
347 
348 				gl.readBuffer(GL_COLOR_ATTACHMENT0+ndx);
349 				glu::readPixels(renderCtx, 0, 0, result.getAccess());
350 				GLU_EXPECT_NO_ERROR(gl.getError(), "Reading pixels failed");
351 
352 				if (!compareSingleColor(m_testCtx.getLog(), result, tcu::RGBA::green(), threshold))
353 					allOk = false;
354 			}
355 		}
356 
357 		m_testCtx.setTestResult(allOk ? QP_TEST_RESULT_PASS	: QP_TEST_RESULT_FAIL,
358 								allOk ? "Pass"				: "Image comparison failed");
359 
360 		return STOP;
361 	}
362 };
363 
364 } // anonymous
365 
ShaderFragDataTests(Context & context)366 ShaderFragDataTests::ShaderFragDataTests (Context& context)
367 	: TestCaseGroup(context, "fragdata", "gl_FragData[] Tests")
368 {
369 }
370 
~ShaderFragDataTests(void)371 ShaderFragDataTests::~ShaderFragDataTests (void)
372 {
373 }
374 
init(void)375 void ShaderFragDataTests::init (void)
376 {
377 	addChild(new FragDataIndexingCase		(m_context, "valid_static_index",	"Valid gl_FragData[] assignment using static index",	INDEX_EXPR_STATIC));
378 	addChild(new FragDataIndexingCase		(m_context, "valid_uniform_index",	"Valid gl_FragData[] assignment using uniform index",	INDEX_EXPR_UNIFORM));
379 	addChild(new FragDataIndexingCase		(m_context, "valid_dynamic_index",	"Valid gl_FragData[] assignment using dynamic index",	INDEX_EXPR_DYNAMIC));
380 	addChild(new FragDataDrawBuffersCase	(m_context));
381 
382 	// Negative cases.
383 	{
384 		gls::ShaderLibrary library(m_testCtx, m_context.getRenderContext(), m_context.getContextInfo());
385 		std::vector<tcu::TestNode*> negativeCases = library.loadShaderFile("shaders/fragdata.test");
386 
387 		for (std::vector<tcu::TestNode*>::iterator i = negativeCases.begin(); i != negativeCases.end(); i++)
388 			addChild(*i);
389 	}
390 }
391 
392 } // Functional
393 } // gles3
394 } // deqp
395