1 /*-------------------------------------------------------------------------
2  * drawElements Quality Program OpenGL ES 3.0 Module
3  * -------------------------------------------------
4  *
5  * Copyright 2017 Hugues Evrard, Imperial College London
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 Shader metamorphic tests.
22  *//*--------------------------------------------------------------------*/
23 
24 #include "es3fShaderMetamorphicTests.hpp"
25 #include "glsShaderRenderCase.hpp"
26 #include "deUniquePtr.hpp"
27 #include "deFilePath.hpp"
28 #include "tcuTestContext.hpp"
29 #include "tcuTestLog.hpp"
30 #include "tcuRenderTarget.hpp"
31 #include "tcuImageCompare.hpp"
32 #include "tcuVectorUtil.hpp"
33 #include "tcuResource.hpp"
34 #include "gluPixelTransfer.hpp"
35 #include "gluDrawUtil.hpp"
36 
37 #include "glwFunctions.hpp"
38 
39 using std::vector;
40 using tcu::TestLog;
41 
42 namespace deqp
43 {
44 namespace gles3
45 {
46 namespace Functional
47 {
48 
49 static const int MAX_RENDER_WIDTH	= 256;
50 static const int MAX_RENDER_HEIGHT	= 256;
51 
52 typedef bool (*SanityCheckFunc)(const tcu::ConstPixelBufferAccess&);
53 
54 /*--------------------------------------------------------------------*//*!
55  * \brief ShaderMetamorphicVariant
56  *
57  * ShaderMetamorphicVariant aims at rendering a recipient shader and a
58  * variant shader, and compare whether the resulting images are the
59  * approximately the same. It also checks non-deterministic renderings,
60  * by rendering each fragment shader a couple of times.
61  *//*--------------------------------------------------------------------*/
62 class ShaderMetamorphicVariant : public TestCase
63 {
64 public:
65 					ShaderMetamorphicVariant	(Context& context, const char* name, const std::string& vertexFilename, const std::string& recipientFilename, const std::string& variantFilename, SanityCheckFunc sanityCheck);
66 					~ShaderMetamorphicVariant	(void);
67 	IterateResult	iterate						(void);
68 
69 private:
70 	const std::string	m_vertexFilename;
71 	const std::string	m_recipientFilename;
72 	const std::string	m_variantFilename;
73 	SanityCheckFunc		m_sanityCheck;
74 
75 	std::string			fileContents	(const std::string& filename);
76 	void				render			(const tcu::PixelBufferAccess& img, const std::string& vertexSrc, const std::string& fragmentSrc);
77 	void				checkNondet		(const tcu::Surface& refImg, const std::string& vertexSrc, const std::string& fragmentSrc);
78 };
79 
ShaderMetamorphicVariant(Context & context,const char * name,const std::string & vertexFilename,const std::string & recipientFilename,const std::string & variantFilename,SanityCheckFunc sanityCheck)80 ShaderMetamorphicVariant::ShaderMetamorphicVariant (Context& context, const char* name, const std::string& vertexFilename, const std::string& recipientFilename, const std::string& variantFilename, SanityCheckFunc sanityCheck)
81 	: TestCase				(context, name, "Test a given variant")
82 	, m_vertexFilename		(vertexFilename)
83 	, m_recipientFilename	(recipientFilename)
84 	, m_variantFilename		(variantFilename)
85 	, m_sanityCheck			(sanityCheck)
86 {
87 }
88 
~ShaderMetamorphicVariant(void)89 ShaderMetamorphicVariant::~ShaderMetamorphicVariant (void)
90 {
91 }
92 
fileContents(const std::string & filename)93 std::string ShaderMetamorphicVariant::fileContents (const std::string& filename)
94 {
95 	de::UniquePtr<tcu::Resource>	resource		(m_testCtx.getArchive().getResource(filename.c_str()));
96 	int								size			= resource->getSize();
97 	std::vector<deUint8>			data;
98 
99 	data.resize(size + 1);
100 	resource->read(&data[0], size);
101 	data[size] = '\0';
102 	std::string contents = std::string((const char*)(&data[0]));
103 	return contents;
104 }
105 
render(const tcu::PixelBufferAccess & img,const std::string & vertexSrc,const std::string & fragmentSrc)106 void ShaderMetamorphicVariant::render (const tcu::PixelBufferAccess& img, const std::string& vertexSrc, const std::string& fragmentSrc)
107 {
108 	TestLog&				log		= m_testCtx.getLog();
109 	const glw::Functions&	gl		= m_context.getRenderContext().getFunctions();
110 
111 	// Positions, shared between shaders
112 	const float positions[] =
113 	{
114 		-1.0f,  1.0f,	// top-left
115 		-1.0f, -1.0f,	// bottom-left
116 		 1.0f, -1.0f,	// bottom-right
117 		 1.0f,  1.0f,	// top-right
118 	};
119 
120 	const deUint16 indices[] =
121 	{
122 		0, 1, 2,	// bottom-left triangle
123 		0, 3, 2,	// top-right triangle
124 	};
125 
126 	glu::VertexArrayBinding posBinding = glu::va::Float("coord2d", 2, 6, 0, &positions[0]);
127 
128 	const glu::ShaderProgram program(m_context.getRenderContext(), glu::makeVtxFragSources(vertexSrc, fragmentSrc));
129 	log << program;
130 
131 	if (!program.isOk())
132 		throw tcu::TestError("Compile failed");
133 
134 	// Set uniforms expected in GraphicsFuzz generated programs
135 	gl.useProgram(program.getProgram());
136 	// Uniform: injectionSwitch
137 	int uniformLoc = gl.getUniformLocation(program.getProgram(), "injectionSwitch");
138 	if (uniformLoc != -1)
139 		gl.uniform2f(uniformLoc, 0.0f, 1.0f);
140 	// Uniform: resolution
141 	uniformLoc = gl.getUniformLocation(program.getProgram(), "resolution");
142 	if (uniformLoc != -1)
143 		gl.uniform2f(uniformLoc, glw::GLfloat(img.getWidth()), glw::GLfloat(img.getHeight()));
144 	// Uniform: mouse
145 	uniformLoc = gl.getUniformLocation(program.getProgram(), "mouse");
146 	if (uniformLoc != -1)
147 		gl.uniform2f(uniformLoc, 0.0f, 0.0f);
148 	// Uniform: time
149 	uniformLoc = gl.getUniformLocation(program.getProgram(), "time");
150 	if (uniformLoc != -1)
151 		gl.uniform1f(uniformLoc, 0.0f);
152 
153 	// Render two times to check nondeterministic renderings
154 	glu::draw(m_context.getRenderContext(), program.getProgram(), 1, &posBinding, glu::pr::Triangles(DE_LENGTH_OF_ARRAY(indices), &indices[0]));
155 	glu::readPixels(m_context.getRenderContext(), 0, 0, img);
156 	GLU_EXPECT_NO_ERROR(gl.getError(), "Draw");
157 }
158 
checkNondet(const tcu::Surface & refImg,const std::string & vertexSrc,const std::string & fragmentSrc)159 void ShaderMetamorphicVariant::checkNondet (const tcu::Surface& refImg, const std::string& vertexSrc, const std::string& fragmentSrc)
160 {
161 	TestLog&		log	= m_testCtx.getLog();
162 	tcu::Surface	img	= tcu::Surface(refImg.getWidth(), refImg.getHeight());
163 
164 	render(img.getAccess(), vertexSrc, fragmentSrc);
165 	bool same = tcu::pixelThresholdCompare(log, "Result", "Image comparison result", img, refImg, tcu::RGBA(0,0,0,0), tcu::COMPARE_LOG_RESULT);
166 	if (!same)
167 		throw tcu::TestError("Nondeterministic rendering");
168 }
169 
iterate(void)170 ShaderMetamorphicVariant::IterateResult ShaderMetamorphicVariant::iterate (void)
171 {
172 	TestLog&			log				= m_testCtx.getLog();
173 	const tcu::RGBA		threshold		= tcu::RGBA(1,1,1,1) + m_context.getRenderTarget().getPixelFormat().getColorThreshold();
174 	std::string			vertexSrc		= fileContents(m_vertexFilename);
175 	std::string			recipientSrc	= fileContents(m_recipientFilename);
176 	std::string			variantSrc		= fileContents(m_variantFilename);
177 	const int			width			= deMin32(m_context.getRenderTarget().getWidth(), MAX_RENDER_WIDTH);
178 	const int			height			= deMin32(m_context.getRenderTarget().getHeight(), MAX_RENDER_HEIGHT);
179 	tcu::Surface		recipientImg	= tcu::Surface(width, height);
180 	tcu::Surface		variantImg		= tcu::Surface(width, height);
181 
182 	render(recipientImg.getAccess(), vertexSrc, recipientSrc);
183 	render(variantImg.getAccess(), vertexSrc, variantSrc);
184 
185 	checkNondet(recipientImg, vertexSrc, recipientSrc);
186 	checkNondet(variantImg, vertexSrc, variantSrc);
187 
188 	if (m_sanityCheck != DE_NULL)
189 	{
190 		bool isSane = m_sanityCheck(recipientImg.getAccess());
191 		if (!isSane)
192 			throw tcu::TestError("Sanity check fails on recipient");
193 	}
194 
195 	bool isOk = tcu::pixelThresholdCompare(log, "Result", "Image comparison result", recipientImg, variantImg, threshold, tcu::COMPARE_LOG_RESULT);
196 
197 	m_testCtx.setTestResult(isOk ? QP_TEST_RESULT_PASS	: QP_TEST_RESULT_FAIL,
198 							isOk ? "Pass"				: "Image comparison failed");
199 
200 	return STOP;
201 }
202 
203 /*--------------------------------------------------------------------*//*!
204  * \brief ShaderMetamorphicShaderset
205  *
206  * ShaderMetamorphicShaderset gathers a set of ShaderMetamorphicVariant
207  * for a similar recipient.
208  *//*--------------------------------------------------------------------*/
209 class ShaderMetamorphicShaderset : public TestCaseGroup
210 {
211 public:
212 					ShaderMetamorphicShaderset	(Context& context, const char* name, const std::string& vertexFilename, const std::string& recipientFilename, std::vector<std::string> variantFilenames, SanityCheckFunc sanityCheck);
213 					~ShaderMetamorphicShaderset	(void);
214 	virtual void	init						(void);
215 
216 private:
217 	const std::string			m_vertexFilename;
218 	const std::string			m_recipientFilename;
219 	std::vector<std::string>	m_variantFilenames;
220 	SanityCheckFunc				m_sanityCheck;
221 
222 								ShaderMetamorphicShaderset	(const ShaderMetamorphicShaderset&);	// Not allowed!
223 	ShaderMetamorphicShaderset&	operator=					(const ShaderMetamorphicShaderset&);	// Not allowed!
224 };
225 
ShaderMetamorphicShaderset(Context & context,const char * name,const std::string & vertexFilename,const std::string & recipientFilename,std::vector<std::string> variantFilenames,SanityCheckFunc sanityCheck)226 ShaderMetamorphicShaderset::ShaderMetamorphicShaderset (Context& context, const char *name, const std::string& vertexFilename, const std::string& recipientFilename, std::vector<std::string> variantFilenames, SanityCheckFunc sanityCheck)
227 	: TestCaseGroup			(context, name, "Metamorphic Shader Set")
228 	, m_vertexFilename		(vertexFilename)
229 	, m_recipientFilename	(recipientFilename)
230 	, m_variantFilenames	(variantFilenames)
231 	, m_sanityCheck			(sanityCheck)
232 {
233 }
234 
~ShaderMetamorphicShaderset(void)235 ShaderMetamorphicShaderset::~ShaderMetamorphicShaderset (void)
236 {
237 }
238 
init(void)239 void ShaderMetamorphicShaderset::init(void)
240 {
241 	for (size_t variantNdx = 0; variantNdx < m_variantFilenames.size(); variantNdx++)
242 	{
243 		std::string variantName = de::FilePath(m_variantFilenames[variantNdx]).getBaseName();
244 		// Remove extension
245 		size_t pos	= variantName.find_last_of(".");
246 		variantName	= variantName.substr(0, pos);
247 
248 		addChild(new ShaderMetamorphicVariant(m_context, variantName.c_str(), m_vertexFilename, m_recipientFilename, m_variantFilenames[variantNdx], m_sanityCheck));
249 	}
250 }
251 
252 /*--------------------------------------------------------------------*//*!
253  * \brief SanityPixel
254  *
255  * A place holder to store info on reference pixel for sanity checking.
256  *//*--------------------------------------------------------------------*/
257 class SanityPixel
258 {
259 public:
260 	float		m_xRelative;
261 	float		m_yRelative;
262 	tcu::Vec4	m_RGBA;
263 
264 	SanityPixel (float xRelative, float yRelative, tcu::Vec4 RGBA);
265 };
266 
SanityPixel(float xRelative,float yRelative,tcu::Vec4 RGBA)267 SanityPixel::SanityPixel (float xRelative, float yRelative, tcu::Vec4 RGBA)
268 	: m_xRelative	(xRelative)
269 	, m_yRelative	(yRelative)
270 	, m_RGBA		(RGBA)
271 {
272 }
273 
sanityComparePixels(const tcu::ConstPixelBufferAccess & img,std::vector<SanityPixel> sanityPixels)274 static bool sanityComparePixels (const tcu::ConstPixelBufferAccess& img, std::vector<SanityPixel> sanityPixels)
275 {
276 	const int			depth		= 0;
277 	const tcu::Vec4		threshold	= tcu::Vec4(0.01f, 0.01f, 0.01f, 0.01f);
278 
279 	for (deUint32 i = 0; i < sanityPixels.size(); i++)
280 	{
281 		SanityPixel		sanPix	= sanityPixels[i];
282 		int				x		= (int)((float)img.getWidth() * sanPix.m_xRelative);
283 		int				y		= (int)((float)img.getHeight() * sanPix.m_yRelative);
284 		tcu::Vec4		RGBA	= img.getPixel(x, y, depth);
285 		tcu::Vec4		diff	= abs(RGBA - sanPix.m_RGBA);
286 		for (int j = 0; j < 4; j++)
287 			if (diff[j] >= threshold[j])
288 				return false;
289 	}
290 	return true;
291 }
292 
sanityCheck_synthetic(const tcu::ConstPixelBufferAccess & img)293 static bool sanityCheck_synthetic (const tcu::ConstPixelBufferAccess& img)
294 {
295 	std::vector<SanityPixel>	sanityPixels;
296 	bool						isOK;
297 
298 	sanityPixels.push_back(SanityPixel(0.5f, 0.5f, tcu::Vec4(0.0f, 1.0f, 1.0f, 1.0f)));
299 
300 	isOK = sanityComparePixels(img, sanityPixels);
301 	return isOK;
302 }
303 
sanityCheck_bubblesort_flag(const tcu::ConstPixelBufferAccess & img)304 static bool sanityCheck_bubblesort_flag (const tcu::ConstPixelBufferAccess& img)
305 {
306 	std::vector<SanityPixel>	sanityPixels;
307 	bool						isOK;
308 
309 	sanityPixels.push_back(SanityPixel(0.25f, 0.25f, tcu::Vec4(0.1f, 0.6f, 1.0f, 1.0f)));
310 	sanityPixels.push_back(SanityPixel(0.25f, 0.75f, tcu::Vec4(1.0f, 0.5f, 0.1f, 1.0f)));
311 	sanityPixels.push_back(SanityPixel(0.75f, 0.25f, tcu::Vec4(0.6f, 1.0f, 0.1f, 1.0f)));
312 	sanityPixels.push_back(SanityPixel(0.75f, 0.75f, tcu::Vec4(0.5f, 0.1f, 1.0f, 1.0f)));
313 
314 	isOK = sanityComparePixels(img, sanityPixels);
315 	return isOK;
316 }
317 
318 /*--------------------------------------------------------------------*//*!
319  * \brief ShaderMetamorphicTests
320  *
321  * ShaderMetamorphicTests regroups metamorphic shadersets.
322  *//*--------------------------------------------------------------------*/
ShaderMetamorphicTests(Context & context)323 ShaderMetamorphicTests::ShaderMetamorphicTests (Context& context)
324 : TestCaseGroup(context, "metamorphic", "Shader Metamorphic Tests")
325 {
326 }
327 
~ShaderMetamorphicTests(void)328 ShaderMetamorphicTests::~ShaderMetamorphicTests (void)
329 {
330 }
331 
init(void)332 void ShaderMetamorphicTests::init (void)
333 {
334 	std::vector<std::string>	fragNames;
335 	std::string					vertexFilename = "graphicsfuzz/vertexShader.glsl";
336 
337 	// synthetic
338 	fragNames.clear();
339 	fragNames.push_back("graphicsfuzz/synthetic/variant_1.frag");
340 	fragNames.push_back("graphicsfuzz/synthetic/variant_2.frag");
341 	fragNames.push_back("graphicsfuzz/synthetic/variant_3.frag");
342 	fragNames.push_back("graphicsfuzz/synthetic/variant_4.frag");
343 	addChild(new ShaderMetamorphicShaderset (m_context, "synthetic", vertexFilename, "graphicsfuzz/synthetic/recipient.frag", fragNames, sanityCheck_synthetic));
344 
345 	// bubblesort_flag
346 	fragNames.clear();
347 	fragNames.push_back("graphicsfuzz/bubblesort_flag/variant_1.frag");
348 	fragNames.push_back("graphicsfuzz/bubblesort_flag/variant_2.frag");
349 	addChild(new ShaderMetamorphicShaderset (m_context, "bubblesort_flag", vertexFilename, "graphicsfuzz/bubblesort_flag/recipient.frag", fragNames, sanityCheck_bubblesort_flag));
350 
351 }
352 
353 } // Functional
354 } // gles3
355 } // deqp
356