blob: 7b5df9e9ed888ad6ca931d75cc8573a114dd4f1f [file] [log] [blame]
/*-------------------------------------------------------------------------
* 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