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