| /*------------------------------------------------------------------------- |
| * 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 |