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