--- /dev/null
+/*-------------------------------------------------------------------------
+ * drawElements Quality Program OpenGL ES 3.0 Module
+ * -------------------------------------------------
+ *
+ * Copyright 2017 Hugues Evrard, Imperial College London
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ *//*!
+ * \file
+ * \brief Shader metamorphic tests.
+ *//*--------------------------------------------------------------------*/
+
+#include "es3fShaderMetamorphicTests.hpp"
+#include "glsShaderRenderCase.hpp"
+#include "deUniquePtr.hpp"
+#include "deFilePath.hpp"
+#include "tcuTestContext.hpp"
+#include "tcuTestLog.hpp"
+#include "tcuRenderTarget.hpp"
+#include "tcuImageCompare.hpp"
+#include "tcuVectorUtil.hpp"
+#include "tcuResource.hpp"
+#include "gluPixelTransfer.hpp"
+#include "gluDrawUtil.hpp"
+
+#include "glwFunctions.hpp"
+
+using std::vector;
+using tcu::TestLog;
+
+namespace deqp
+{
+namespace gles3
+{
+namespace Functional
+{
+
+static const int MAX_RENDER_WIDTH = 256;
+static const int MAX_RENDER_HEIGHT = 256;
+
+typedef bool (*SanityCheckFunc)(const tcu::ConstPixelBufferAccess&);
+
+/*--------------------------------------------------------------------*//*!
+ * \brief ShaderMetamorphicVariant
+ *
+ * ShaderMetamorphicVariant aims at rendering a recipient shader and a
+ * variant shader, and compare whether the resulting images are the
+ * approximately the same. It also checks non-deterministic renderings,
+ * by rendering each fragment shader a couple of times.
+ *//*--------------------------------------------------------------------*/
+class ShaderMetamorphicVariant : public TestCase
+{
+public:
+ ShaderMetamorphicVariant (Context& context, const char* name, const std::string& vertexFilename, const std::string& recipientFilename, const std::string& variantFilename, SanityCheckFunc sanityCheck);
+ ~ShaderMetamorphicVariant (void);
+ IterateResult iterate (void);
+
+private:
+ const std::string m_vertexFilename;
+ const std::string m_recipientFilename;
+ const std::string m_variantFilename;
+ SanityCheckFunc m_sanityCheck;
+
+ std::string fileContents (const std::string& filename);
+ void render (const tcu::PixelBufferAccess& img, const std::string& vertexSrc, const std::string& fragmentSrc);
+ void checkNondet (const tcu::Surface& refImg, const std::string& vertexSrc, const std::string& fragmentSrc);
+};
+
+ShaderMetamorphicVariant::ShaderMetamorphicVariant (Context& context, const char* name, const std::string& vertexFilename, const std::string& recipientFilename, const std::string& variantFilename, SanityCheckFunc sanityCheck)
+ : TestCase (context, name, "Test a given variant")
+ , m_vertexFilename (vertexFilename)
+ , m_recipientFilename (recipientFilename)
+ , m_variantFilename (variantFilename)
+ , m_sanityCheck (sanityCheck)
+{
+}
+
+ShaderMetamorphicVariant::~ShaderMetamorphicVariant (void)
+{
+}
+
+std::string ShaderMetamorphicVariant::fileContents (const std::string& filename)
+{
+ de::UniquePtr<tcu::Resource> resource (m_testCtx.getArchive().getResource(filename.c_str()));
+ int size = resource->getSize();
+ std::vector<deUint8> data;
+
+ data.resize(size + 1);
+ resource->read(&data[0], size);
+ data[size] = '\0';
+ std::string contents = std::string((const char*)(&data[0]));
+ return contents;
+}
+
+void ShaderMetamorphicVariant::render (const tcu::PixelBufferAccess& img, const std::string& vertexSrc, const std::string& fragmentSrc)
+{
+ TestLog& log = m_testCtx.getLog();
+ const glw::Functions& gl = m_context.getRenderContext().getFunctions();
+
+ // Positions, shared between shaders
+ const float positions[] =
+ {
+ -1.0f, 1.0f, // top-left
+ -1.0f, -1.0f, // bottom-left
+ 1.0f, -1.0f, // bottom-right
+ 1.0f, 1.0f, // top-right
+ };
+
+ const deUint16 indices[] =
+ {
+ 0, 1, 2, // bottom-left triangle
+ 0, 3, 2, // top-right triangle
+ };
+
+ glu::VertexArrayBinding posBinding = glu::va::Float("coord2d", 2, 6, 0, &positions[0]);
+
+ const glu::ShaderProgram program(m_context.getRenderContext(), glu::makeVtxFragSources(vertexSrc, fragmentSrc));
+ log << program;
+
+ if (!program.isOk())
+ throw tcu::TestError("Compile failed");
+
+ // Set uniforms expected in GraphicsFuzz generated programs
+ gl.useProgram(program.getProgram());
+ // Uniform: injectionSwitch
+ int uniformLoc = gl.getUniformLocation(program.getProgram(), "injectionSwitch");
+ if (uniformLoc != -1)
+ gl.uniform2f(uniformLoc, 0.0f, 1.0f);
+ // Uniform: resolution
+ uniformLoc = gl.getUniformLocation(program.getProgram(), "resolution");
+ if (uniformLoc != -1)
+ gl.uniform2f(uniformLoc, glw::GLfloat(img.getWidth()), glw::GLfloat(img.getHeight()));
+ // Uniform: mouse
+ uniformLoc = gl.getUniformLocation(program.getProgram(), "mouse");
+ if (uniformLoc != -1)
+ gl.uniform2f(uniformLoc, 0.0f, 0.0f);
+ // Uniform: time
+ uniformLoc = gl.getUniformLocation(program.getProgram(), "time");
+ if (uniformLoc != -1)
+ gl.uniform1f(uniformLoc, 0.0f);
+
+ // Render two times to check nondeterministic renderings
+ glu::draw(m_context.getRenderContext(), program.getProgram(), 1, &posBinding, glu::pr::Triangles(DE_LENGTH_OF_ARRAY(indices), &indices[0]));
+ glu::readPixels(m_context.getRenderContext(), 0, 0, img);
+ GLU_EXPECT_NO_ERROR(gl.getError(), "Draw");
+}
+
+void ShaderMetamorphicVariant::checkNondet (const tcu::Surface& refImg, const std::string& vertexSrc, const std::string& fragmentSrc)
+{
+ TestLog& log = m_testCtx.getLog();
+ tcu::Surface img = tcu::Surface(refImg.getWidth(), refImg.getHeight());
+
+ render(img.getAccess(), vertexSrc, fragmentSrc);
+ bool same = tcu::pixelThresholdCompare(log, "Result", "Image comparison result", img, refImg, tcu::RGBA(0,0,0,0), tcu::COMPARE_LOG_RESULT);
+ if (!same)
+ throw tcu::TestError("Nondeterministic rendering");
+}
+
+ShaderMetamorphicVariant::IterateResult ShaderMetamorphicVariant::iterate (void)
+{
+ TestLog& log = m_testCtx.getLog();
+ const tcu::RGBA threshold = tcu::RGBA(1,1,1,1) + m_context.getRenderTarget().getPixelFormat().getColorThreshold();
+ std::string vertexSrc = fileContents(m_vertexFilename);
+ std::string recipientSrc = fileContents(m_recipientFilename);
+ std::string variantSrc = fileContents(m_variantFilename);
+ const int width = deMin32(m_context.getRenderTarget().getWidth(), MAX_RENDER_WIDTH);
+ const int height = deMin32(m_context.getRenderTarget().getHeight(), MAX_RENDER_HEIGHT);
+ tcu::Surface recipientImg = tcu::Surface(width, height);
+ tcu::Surface variantImg = tcu::Surface(width, height);
+
+ render(recipientImg.getAccess(), vertexSrc, recipientSrc);
+ render(variantImg.getAccess(), vertexSrc, variantSrc);
+
+ checkNondet(recipientImg, vertexSrc, recipientSrc);
+ checkNondet(variantImg, vertexSrc, variantSrc);
+
+ if (m_sanityCheck != DE_NULL)
+ {
+ bool isSane = m_sanityCheck(recipientImg.getAccess());
+ if (!isSane)
+ throw tcu::TestError("Sanity check fails on recipient");
+ }
+
+ bool isOk = tcu::pixelThresholdCompare(log, "Result", "Image comparison result", recipientImg, variantImg, threshold, tcu::COMPARE_LOG_RESULT);
+
+ m_testCtx.setTestResult(isOk ? QP_TEST_RESULT_PASS : QP_TEST_RESULT_FAIL,
+ isOk ? "Pass" : "Image comparison failed");
+
+ return STOP;
+}
+
+/*--------------------------------------------------------------------*//*!
+ * \brief ShaderMetamorphicShaderset
+ *
+ * ShaderMetamorphicShaderset gathers a set of ShaderMetamorphicVariant
+ * for a similar recipient.
+ *//*--------------------------------------------------------------------*/
+class ShaderMetamorphicShaderset : public TestCaseGroup
+{
+public:
+ ShaderMetamorphicShaderset (Context& context, const char* name, const std::string& vertexFilename, const std::string& recipientFilename, std::vector<std::string> variantFilenames, SanityCheckFunc sanityCheck);
+ ~ShaderMetamorphicShaderset (void);
+ virtual void init (void);
+
+private:
+ const std::string m_vertexFilename;
+ const std::string m_recipientFilename;
+ std::vector<std::string> m_variantFilenames;
+ SanityCheckFunc m_sanityCheck;
+
+ ShaderMetamorphicShaderset (const ShaderMetamorphicShaderset&); // Not allowed!
+ ShaderMetamorphicShaderset& operator= (const ShaderMetamorphicShaderset&); // Not allowed!
+};
+
+ShaderMetamorphicShaderset::ShaderMetamorphicShaderset (Context& context, const char *name, const std::string& vertexFilename, const std::string& recipientFilename, std::vector<std::string> variantFilenames, SanityCheckFunc sanityCheck)
+ : TestCaseGroup (context, name, "Metamorphic Shader Set")
+ , m_vertexFilename (vertexFilename)
+ , m_recipientFilename (recipientFilename)
+ , m_variantFilenames (variantFilenames)
+ , m_sanityCheck (sanityCheck)
+{
+}
+
+ShaderMetamorphicShaderset::~ShaderMetamorphicShaderset (void)
+{
+}
+
+void ShaderMetamorphicShaderset::init(void)
+{
+ for (size_t variantNdx = 0; variantNdx < m_variantFilenames.size(); variantNdx++)
+ {
+ std::string variantName = de::FilePath(m_variantFilenames[variantNdx]).getBaseName();
+ // Remove extension
+ size_t pos = variantName.find_last_of(".");
+ variantName = variantName.substr(0, pos);
+
+ addChild(new ShaderMetamorphicVariant(m_context, variantName.c_str(), m_vertexFilename, m_recipientFilename, m_variantFilenames[variantNdx], m_sanityCheck));
+ }
+}
+
+/*--------------------------------------------------------------------*//*!
+ * \brief SanityPixel
+ *
+ * A place holder to store info on reference pixel for sanity checking.
+ *//*--------------------------------------------------------------------*/
+class SanityPixel
+{
+public:
+ float m_xRelative;
+ float m_yRelative;
+ tcu::Vec4 m_RGBA;
+
+ SanityPixel (float xRelative, float yRelative, tcu::Vec4 RGBA);
+};
+
+SanityPixel::SanityPixel (float xRelative, float yRelative, tcu::Vec4 RGBA)
+ : m_xRelative (xRelative)
+ , m_yRelative (yRelative)
+ , m_RGBA (RGBA)
+{
+}
+
+static bool sanityComparePixels (const tcu::ConstPixelBufferAccess& img, std::vector<SanityPixel> sanityPixels)
+{
+ const int depth = 0;
+ const tcu::Vec4 threshold = tcu::Vec4(0.01f, 0.01f, 0.01f, 0.01f);
+
+ for (deUint32 i = 0; i < sanityPixels.size(); i++)
+ {
+ SanityPixel sanPix = sanityPixels[i];
+ int x = (int)((float)img.getWidth() * sanPix.m_xRelative);
+ int y = (int)((float)img.getHeight() * sanPix.m_yRelative);
+ tcu::Vec4 RGBA = img.getPixel(x, y, depth);
+ tcu::Vec4 diff = abs(RGBA - sanPix.m_RGBA);
+ for (int j = 0; j < 4; j++)
+ if (diff[j] >= threshold[j])
+ return false;
+ }
+ return true;
+}
+
+static bool sanityCheck_synthetic (const tcu::ConstPixelBufferAccess& img)
+{
+ std::vector<SanityPixel> sanityPixels;
+ bool isOK;
+
+ sanityPixels.push_back(SanityPixel(0.5f, 0.5f, tcu::Vec4(0.0f, 1.0f, 1.0f, 1.0f)));
+
+ isOK = sanityComparePixels(img, sanityPixels);
+ return isOK;
+}
+
+static bool sanityCheck_bubblesort_flag (const tcu::ConstPixelBufferAccess& img)
+{
+ std::vector<SanityPixel> sanityPixels;
+ bool isOK;
+
+ sanityPixels.push_back(SanityPixel(0.25f, 0.25f, tcu::Vec4(0.1f, 0.6f, 1.0f, 1.0f)));
+ sanityPixels.push_back(SanityPixel(0.25f, 0.75f, tcu::Vec4(1.0f, 0.5f, 0.1f, 1.0f)));
+ sanityPixels.push_back(SanityPixel(0.75f, 0.25f, tcu::Vec4(0.6f, 1.0f, 0.1f, 1.0f)));
+ sanityPixels.push_back(SanityPixel(0.75f, 0.75f, tcu::Vec4(0.5f, 0.1f, 1.0f, 1.0f)));
+
+ isOK = sanityComparePixels(img, sanityPixels);
+ return isOK;
+}
+
+/*--------------------------------------------------------------------*//*!
+ * \brief ShaderMetamorphicTests
+ *
+ * ShaderMetamorphicTests regroups metamorphic shadersets.
+ *//*--------------------------------------------------------------------*/
+ShaderMetamorphicTests::ShaderMetamorphicTests (Context& context)
+: TestCaseGroup(context, "metamorphic", "Shader Metamorphic Tests")
+{
+}
+
+ShaderMetamorphicTests::~ShaderMetamorphicTests (void)
+{
+}
+
+void ShaderMetamorphicTests::init (void)
+{
+ std::vector<std::string> fragNames;
+ std::string vertexFilename = "graphicsfuzz/vertexShader.glsl";
+
+ // synthetic
+ fragNames.clear();
+ fragNames.push_back("graphicsfuzz/synthetic/variant_1.frag");
+ fragNames.push_back("graphicsfuzz/synthetic/variant_2.frag");
+ fragNames.push_back("graphicsfuzz/synthetic/variant_3.frag");
+ fragNames.push_back("graphicsfuzz/synthetic/variant_4.frag");
+ addChild(new ShaderMetamorphicShaderset (m_context, "synthetic", vertexFilename, "graphicsfuzz/synthetic/recipient.frag", fragNames, sanityCheck_synthetic));
+
+ // bubblesort_flag
+ fragNames.clear();
+ fragNames.push_back("graphicsfuzz/bubblesort_flag/variant_1.frag");
+ fragNames.push_back("graphicsfuzz/bubblesort_flag/variant_2.frag");
+ addChild(new ShaderMetamorphicShaderset (m_context, "bubblesort_flag", vertexFilename, "graphicsfuzz/bubblesort_flag/recipient.frag", fragNames, sanityCheck_bubblesort_flag));
+
+}
+
+} // Functional
+} // gles3
+} // deqp