C++程序  |  356行  |  12.78 KB

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