/*------------------------------------------------------------------------- * 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 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 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 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 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 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)); } } // Functional } // gles3 } // deqp