/*------------------------------------------------------------------------- * 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 resource(m_testCtx.getArchive().getResource(filename.c_str())); int size = resource->getSize(); std::vector 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 uint16_t 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("Quick 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 variantFilenames, SanityCheckFunc sanityCheck); ~ShaderMetamorphicShaderset(void); virtual void init(void); private: const std::string m_vertexFilename; const std::string m_recipientFilename; std::vector 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 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 quick 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 sanityPixels) { const int depth = 0; const tcu::Vec4 threshold = tcu::Vec4(0.01f, 0.01f, 0.01f, 0.01f); for (uint32_t 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 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 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 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)); } } // namespace Functional } // namespace gles3 } // namespace deqp