You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
356 lines
13 KiB
356 lines
13 KiB
/*-------------------------------------------------------------------------
|
|
* 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
|