blob: 5d7dd3da6d8f9a218065d8d6d190955faa3be8b3 [file] [log] [blame] [edit]
/*-------------------------------------------------------------------------
* drawElements Quality Program OpenGL ES 3.0 Module
* -------------------------------------------------
*
* Copyright 2014 The Android Open Source Project
*
* 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 Multisampling tests.
*//*--------------------------------------------------------------------*/
#include "es3fMultisampleTests.hpp"
#include "gluStrUtil.hpp"
#include "gluShaderProgram.hpp"
#include "gluPixelTransfer.hpp"
#include "tcuSurface.hpp"
#include "tcuImageCompare.hpp"
#include "tcuRenderTarget.hpp"
#include "tcuTestLog.hpp"
#include "tcuTextureUtil.hpp"
#include "tcuCommandLine.hpp"
#include "deStringUtil.hpp"
#include "deRandom.hpp"
#include "deMath.h"
#include "deString.h"
#include <string>
#include <vector>
#include "glw.h"
namespace deqp
{
namespace gles3
{
namespace Functional
{
using std::vector;
using tcu::IVec2;
using tcu::IVec4;
using tcu::TestLog;
using tcu::Vec2;
using tcu::Vec3;
using tcu::Vec4;
static const GLenum FBO_COLOR_FORMAT = GL_RGBA8;
static const float SQRT_HALF = 0.707107f;
namespace
{
struct QuadCorners
{
Vec2 p0;
Vec2 p1;
Vec2 p2;
Vec2 p3;
QuadCorners(const Vec2 &p0_, const Vec2 &p1_, const Vec2 &p2_, const Vec2 &p3_) : p0(p0_), p1(p1_), p2(p2_), p3(p3_)
{
}
};
} // namespace
static inline int getIterationCount(const tcu::TestContext &ctx, int defaultCount)
{
int cmdLineValue = ctx.getCommandLine().getTestIterationCount();
return cmdLineValue > 0 ? cmdLineValue : defaultCount;
}
static inline int getGLInteger(GLenum name)
{
int result;
GLU_CHECK_CALL(glGetIntegerv(name, &result));
return result;
}
template <typename T>
static inline T min4(T a, T b, T c, T d)
{
return de::min(de::min(de::min(a, b), c), d);
}
template <typename T>
static inline T max4(T a, T b, T c, T d)
{
return de::max(de::max(de::max(a, b), c), d);
}
static inline bool isInsideQuad(const IVec2 &point, const IVec2 &p0, const IVec2 &p1, const IVec2 &p2, const IVec2 &p3)
{
int dot0 = (point.x() - p0.x()) * (p1.y() - p0.y()) + (point.y() - p0.y()) * (p0.x() - p1.x());
int dot1 = (point.x() - p1.x()) * (p2.y() - p1.y()) + (point.y() - p1.y()) * (p1.x() - p2.x());
int dot2 = (point.x() - p2.x()) * (p3.y() - p2.y()) + (point.y() - p2.y()) * (p2.x() - p3.x());
int dot3 = (point.x() - p3.x()) * (p0.y() - p3.y()) + (point.y() - p3.y()) * (p3.x() - p0.x());
return (dot0 > 0) == (dot1 > 0) && (dot1 > 0) == (dot2 > 0) && (dot2 > 0) == (dot3 > 0);
}
/*--------------------------------------------------------------------*//*!
* \brief Check if a region in an image is unicolored.
*
* Checks if the pixels in img inside the convex quadilateral defined by
* p0, p1, p2 and p3 are all (approximately) of the same color.
*//*--------------------------------------------------------------------*/
static bool isPixelRegionUnicolored(const tcu::Surface &img, const IVec2 &p0, const IVec2 &p1, const IVec2 &p2,
const IVec2 &p3)
{
int xMin = de::clamp(min4(p0.x(), p1.x(), p2.x(), p3.x()), 0, img.getWidth() - 1);
int yMin = de::clamp(min4(p0.y(), p1.y(), p2.y(), p3.y()), 0, img.getHeight() - 1);
int xMax = de::clamp(max4(p0.x(), p1.x(), p2.x(), p3.x()), 0, img.getWidth() - 1);
int yMax = de::clamp(max4(p0.y(), p1.y(), p2.y(), p3.y()), 0, img.getHeight() - 1);
bool insideEncountered = false; //!< Whether we have already seen at least one pixel inside the region.
tcu::RGBA insideColor; //!< Color of the first pixel inside the region.
for (int y = yMin; y <= yMax; y++)
for (int x = xMin; x <= xMax; x++)
{
if (isInsideQuad(IVec2(x, y), p0, p1, p2, p3))
{
tcu::RGBA pixColor = img.getPixel(x, y);
if (insideEncountered)
{
if (!tcu::compareThreshold(
pixColor, insideColor,
tcu::RGBA(
3, 3, 3,
3))) // Pixel color differs from already-detected color inside same region - region not unicolored.
return false;
}
else
{
insideEncountered = true;
insideColor = pixColor;
}
}
}
return true;
}
static bool drawUnicolorTestErrors(tcu::Surface &img, const tcu::PixelBufferAccess &errorImg, const IVec2 &p0,
const IVec2 &p1, const IVec2 &p2, const IVec2 &p3)
{
int xMin = de::clamp(min4(p0.x(), p1.x(), p2.x(), p3.x()), 0, img.getWidth() - 1);
int yMin = de::clamp(min4(p0.y(), p1.y(), p2.y(), p3.y()), 0, img.getHeight() - 1);
int xMax = de::clamp(max4(p0.x(), p1.x(), p2.x(), p3.x()), 0, img.getWidth() - 1);
int yMax = de::clamp(max4(p0.y(), p1.y(), p2.y(), p3.y()), 0, img.getHeight() - 1);
tcu::RGBA refColor = img.getPixel((xMin + xMax) / 2, (yMin + yMax) / 2);
for (int y = yMin; y <= yMax; y++)
for (int x = xMin; x <= xMax; x++)
{
if (isInsideQuad(IVec2(x, y), p0, p1, p2, p3))
{
if (!tcu::compareThreshold(img.getPixel(x, y), refColor, tcu::RGBA(3, 3, 3, 3)))
{
img.setPixel(x, y, tcu::RGBA::red());
errorImg.setPixel(Vec4(1.0f, 0.0f, 0.0f, 1.0f), x, y);
}
}
}
return true;
}
/*--------------------------------------------------------------------*//*!
* \brief Abstract base class handling common stuff for multisample cases.
*//*--------------------------------------------------------------------*/
class MultisampleCase : public TestCase
{
public:
struct FboParams
{
bool useFbo;
int numSamples; //!< If 0, use implementation-defined maximum.
bool useDepth;
bool useStencil;
FboParams(int numSamples_, bool useDepth_, bool useStencil_)
: useFbo(true)
, numSamples(numSamples_)
, useDepth(useDepth_)
, useStencil(useStencil_)
{
}
FboParams(void) : useFbo(false), numSamples(-1), useDepth(false), useStencil(false)
{
}
};
MultisampleCase(Context &context, const char *name, const char *desc, int desiredViewportSize,
const FboParams &fboParams = FboParams());
virtual ~MultisampleCase(void);
virtual void init(void);
virtual void deinit(void);
protected:
void renderTriangle(const Vec3 &p0, const Vec3 &p1, const Vec3 &p2, const Vec4 &c0, const Vec4 &c1,
const Vec4 &c2) const;
void renderTriangle(const Vec3 &p0, const Vec3 &p1, const Vec3 &p2, const Vec4 &color) const;
void renderTriangle(const Vec2 &p0, const Vec2 &p1, const Vec2 &p2, const Vec4 &c0, const Vec4 &c1,
const Vec4 &c2) const;
void renderTriangle(const Vec2 &p0, const Vec2 &p1, const Vec2 &p2, const Vec4 &color) const;
void renderQuad(const Vec2 &p0, const Vec2 &p1, const Vec2 &p2, const Vec2 &p3, const Vec4 &c0, const Vec4 &c1,
const Vec4 &c2, const Vec4 &c3) const;
void renderQuad(const Vec2 &p0, const Vec2 &p1, const Vec2 &p2, const Vec2 &p3, const Vec4 &color) const;
void renderLine(const Vec2 &p0, const Vec2 &p1, const Vec4 &color) const;
void randomizeViewport(void);
void readImage(tcu::Surface &dst) const;
IVec2 getRenderTargetSize(void) const
{
return IVec2(m_renderWidth, m_renderHeight);
}
int m_numSamples;
int m_viewportSize;
private:
MultisampleCase(const MultisampleCase &other);
MultisampleCase &operator=(const MultisampleCase &other);
const int m_desiredViewportSize;
const FboParams m_fboParams;
uint32_t m_msColorRbo;
uint32_t m_msDepthStencilRbo;
uint32_t m_resolveColorRbo;
uint32_t m_msFbo;
uint32_t m_resolveFbo;
glu::ShaderProgram *m_program;
int m_attrPositionLoc;
int m_attrColorLoc;
int m_renderWidth;
int m_renderHeight;
int m_viewportX;
int m_viewportY;
de::Random m_rnd;
};
MultisampleCase::MultisampleCase(Context &context, const char *name, const char *desc, int desiredViewportSize,
const FboParams &fboParams)
: TestCase(context, name, desc)
, m_numSamples(0)
, m_viewportSize(0)
, m_desiredViewportSize(desiredViewportSize)
, m_fboParams(fboParams)
, m_msColorRbo(0)
, m_msDepthStencilRbo(0)
, m_resolveColorRbo(0)
, m_msFbo(0)
, m_resolveFbo(0)
, m_program(DE_NULL)
, m_attrPositionLoc(-1)
, m_attrColorLoc(-1)
, m_renderWidth(fboParams.useFbo ? 2 * desiredViewportSize : context.getRenderTarget().getWidth())
, m_renderHeight(fboParams.useFbo ? 2 * desiredViewportSize : context.getRenderTarget().getHeight())
, m_viewportX(0)
, m_viewportY(0)
, m_rnd(deStringHash(name))
{
if (m_fboParams.useFbo)
DE_ASSERT(m_fboParams.numSamples >= 0);
}
MultisampleCase::~MultisampleCase(void)
{
MultisampleCase::deinit();
}
void MultisampleCase::deinit(void)
{
delete m_program;
m_program = DE_NULL;
GLU_CHECK_CALL(glBindRenderbuffer(GL_RENDERBUFFER, 0));
GLU_CHECK_CALL(glBindFramebuffer(GL_FRAMEBUFFER, 0));
if (m_msColorRbo != 0)
{
GLU_CHECK_CALL(glDeleteRenderbuffers(1, &m_msColorRbo));
m_msColorRbo = 0;
}
if (m_msDepthStencilRbo != 0)
{
GLU_CHECK_CALL(glDeleteRenderbuffers(1, &m_msDepthStencilRbo));
m_msDepthStencilRbo = 0;
}
if (m_resolveColorRbo != 0)
{
GLU_CHECK_CALL(glDeleteRenderbuffers(1, &m_resolveColorRbo));
m_resolveColorRbo = 0;
}
if (m_msFbo != 0)
{
GLU_CHECK_CALL(glDeleteFramebuffers(1, &m_msFbo));
m_msFbo = 0;
}
if (m_resolveFbo != 0)
{
GLU_CHECK_CALL(glDeleteFramebuffers(1, &m_resolveFbo));
m_resolveFbo = 0;
}
}
void MultisampleCase::renderTriangle(const Vec3 &p0, const Vec3 &p1, const Vec3 &p2, const Vec4 &c0, const Vec4 &c1,
const Vec4 &c2) const
{
float vertexPositions[] = {p0.x(), p0.y(), p0.z(), 1.0f, p1.x(), p1.y(),
p1.z(), 1.0f, p2.x(), p2.y(), p2.z(), 1.0f};
float vertexColors[] = {
c0.x(), c0.y(), c0.z(), c0.w(), c1.x(), c1.y(), c1.z(), c1.w(), c2.x(), c2.y(), c2.z(), c2.w(),
};
GLU_CHECK_CALL(glEnableVertexAttribArray(m_attrPositionLoc));
GLU_CHECK_CALL(glVertexAttribPointer(m_attrPositionLoc, 4, GL_FLOAT, false, 0, &vertexPositions[0]));
GLU_CHECK_CALL(glEnableVertexAttribArray(m_attrColorLoc));
GLU_CHECK_CALL(glVertexAttribPointer(m_attrColorLoc, 4, GL_FLOAT, false, 0, &vertexColors[0]));
GLU_CHECK_CALL(glUseProgram(m_program->getProgram()));
GLU_CHECK_CALL(glDrawArrays(GL_TRIANGLES, 0, 3));
}
void MultisampleCase::renderTriangle(const Vec3 &p0, const Vec3 &p1, const Vec3 &p2, const Vec4 &color) const
{
renderTriangle(p0, p1, p2, color, color, color);
}
void MultisampleCase::renderTriangle(const Vec2 &p0, const Vec2 &p1, const Vec2 &p2, const Vec4 &c0, const Vec4 &c1,
const Vec4 &c2) const
{
renderTriangle(Vec3(p0.x(), p0.y(), 0.0f), Vec3(p1.x(), p1.y(), 0.0f), Vec3(p2.x(), p2.y(), 0.0f), c0, c1, c2);
}
void MultisampleCase::renderTriangle(const Vec2 &p0, const Vec2 &p1, const Vec2 &p2, const Vec4 &color) const
{
renderTriangle(p0, p1, p2, color, color, color);
}
void MultisampleCase::renderQuad(const Vec2 &p0, const Vec2 &p1, const Vec2 &p2, const Vec2 &p3, const Vec4 &c0,
const Vec4 &c1, const Vec4 &c2, const Vec4 &c3) const
{
renderTriangle(p0, p1, p2, c0, c1, c2);
renderTriangle(p2, p1, p3, c2, c1, c3);
}
void MultisampleCase::renderQuad(const Vec2 &p0, const Vec2 &p1, const Vec2 &p2, const Vec2 &p3,
const Vec4 &color) const
{
renderQuad(p0, p1, p2, p3, color, color, color, color);
}
void MultisampleCase::renderLine(const Vec2 &p0, const Vec2 &p1, const Vec4 &color) const
{
float vertexPositions[] = {p0.x(), p0.y(), 0.0f, 1.0f, p1.x(), p1.y(), 0.0f, 1.0f};
float vertexColors[] = {color.x(), color.y(), color.z(), color.w(), color.x(), color.y(), color.z(), color.w()};
GLU_CHECK_CALL(glEnableVertexAttribArray(m_attrPositionLoc));
GLU_CHECK_CALL(glVertexAttribPointer(m_attrPositionLoc, 4, GL_FLOAT, false, 0, &vertexPositions[0]));
GLU_CHECK_CALL(glEnableVertexAttribArray(m_attrColorLoc));
GLU_CHECK_CALL(glVertexAttribPointer(m_attrColorLoc, 4, GL_FLOAT, false, 0, &vertexColors[0]));
GLU_CHECK_CALL(glUseProgram(m_program->getProgram()));
GLU_CHECK_CALL(glDrawArrays(GL_LINES, 0, 2));
}
void MultisampleCase::randomizeViewport(void)
{
m_viewportX = m_rnd.getInt(0, m_renderWidth - m_viewportSize);
m_viewportY = m_rnd.getInt(0, m_renderHeight - m_viewportSize);
GLU_CHECK_CALL(glViewport(m_viewportX, m_viewportY, m_viewportSize, m_viewportSize));
}
void MultisampleCase::readImage(tcu::Surface &dst) const
{
if (m_fboParams.useFbo)
{
GLU_CHECK_CALL(glBindFramebuffer(GL_DRAW_FRAMEBUFFER, m_resolveFbo));
GLU_CHECK_CALL(glBlitFramebuffer(0, 0, m_renderWidth, m_renderHeight, 0, 0, m_renderWidth, m_renderHeight,
GL_COLOR_BUFFER_BIT, GL_NEAREST));
GLU_CHECK_CALL(glBindFramebuffer(GL_READ_FRAMEBUFFER, m_resolveFbo));
glu::readPixels(m_context.getRenderContext(), m_viewportX, m_viewportY, dst.getAccess());
GLU_CHECK_CALL(glBindFramebuffer(GL_FRAMEBUFFER, m_msFbo));
}
else
glu::readPixels(m_context.getRenderContext(), m_viewportX, m_viewportY, dst.getAccess());
}
void MultisampleCase::init(void)
{
static const char *vertShaderSource = "#version 300 es\n"
"in highp vec4 a_position;\n"
"in mediump vec4 a_color;\n"
"out mediump vec4 v_color;\n"
"void main()\n"
"{\n"
" gl_Position = a_position;\n"
" v_color = a_color;\n"
"}\n";
static const char *fragShaderSource = "#version 300 es\n"
"in mediump vec4 v_color;\n"
"layout(location = 0) out mediump vec4 o_color;\n"
"void main()\n"
"{\n"
" o_color = v_color;\n"
"}\n";
TestLog &log = m_testCtx.getLog();
if (!m_fboParams.useFbo && m_context.getRenderTarget().getNumSamples() <= 1)
throw tcu::NotSupportedError("No multisample buffers");
if (m_fboParams.useFbo)
{
if (m_fboParams.numSamples > 0)
m_numSamples = m_fboParams.numSamples;
else
{
log << TestLog::Message << "Querying maximum number of samples for "
<< glu::getTextureFormatName(FBO_COLOR_FORMAT) << " with glGetInternalformativ()"
<< TestLog::EndMessage;
GLU_CHECK_CALL(glGetInternalformativ(GL_RENDERBUFFER, FBO_COLOR_FORMAT, GL_SAMPLES, 1, &m_numSamples));
}
log << TestLog::Message << "Using FBO of size (" << m_renderWidth << ", " << m_renderHeight << ") with "
<< m_numSamples << " samples" << TestLog::EndMessage;
}
else
{
// Query and log number of samples per pixel.
m_numSamples = getGLInteger(GL_SAMPLES);
log << TestLog::Message << "GL_SAMPLES = " << m_numSamples << TestLog::EndMessage;
}
// Prepare program.
DE_ASSERT(!m_program);
m_program = new glu::ShaderProgram(m_context.getRenderContext(),
glu::makeVtxFragSources(vertShaderSource, fragShaderSource));
if (!m_program->isOk())
throw tcu::TestError("Failed to compile program", DE_NULL, __FILE__, __LINE__);
GLU_CHECK_CALL(m_attrPositionLoc = glGetAttribLocation(m_program->getProgram(), "a_position"));
GLU_CHECK_CALL(m_attrColorLoc = glGetAttribLocation(m_program->getProgram(), "a_color"));
if (m_attrPositionLoc < 0 || m_attrColorLoc < 0)
{
delete m_program;
throw tcu::TestError("Invalid attribute locations", DE_NULL, __FILE__, __LINE__);
}
if (m_fboParams.useFbo)
{
DE_STATIC_ASSERT(sizeof(uint32_t) == sizeof(GLuint));
// Setup ms color RBO.
GLU_CHECK_CALL(glGenRenderbuffers(1, &m_msColorRbo));
GLU_CHECK_CALL(glBindRenderbuffer(GL_RENDERBUFFER, m_msColorRbo));
// If glRenderbufferStorageMultisample() fails, check if it's because of a too high sample count.
// \note We don't do the check until now because some implementations can't handle the GL_SAMPLES query with glGetInternalformativ(),
// and we don't want that to be the cause of test case failure.
try
{
GLU_CHECK_CALL(glRenderbufferStorageMultisample(GL_RENDERBUFFER, m_numSamples, FBO_COLOR_FORMAT,
m_renderWidth, m_renderHeight));
}
catch (const glu::Error &)
{
GLint maxSampleCount = -1;
GLU_CHECK_CALL(glGetInternalformativ(GL_RENDERBUFFER, FBO_COLOR_FORMAT, GL_SAMPLES, 1, &maxSampleCount));
if (maxSampleCount < m_numSamples)
throw tcu::NotSupportedError(
std::string("") + "Maximum sample count returned by glGetInternalformativ() for " +
glu::getTextureFormatName(FBO_COLOR_FORMAT) + " is only " + de::toString(maxSampleCount));
else
throw;
}
if (m_fboParams.useDepth || m_fboParams.useStencil)
{
// Setup ms depth & stencil RBO.
GLU_CHECK_CALL(glGenRenderbuffers(1, &m_msDepthStencilRbo));
GLU_CHECK_CALL(glBindRenderbuffer(GL_RENDERBUFFER, m_msDepthStencilRbo));
GLU_CHECK_CALL(glRenderbufferStorageMultisample(GL_RENDERBUFFER, m_numSamples, GL_DEPTH24_STENCIL8,
m_renderWidth, m_renderHeight));
}
// Setup ms FBO.
GLU_CHECK_CALL(glGenFramebuffers(1, &m_msFbo));
GLU_CHECK_CALL(glBindFramebuffer(GL_FRAMEBUFFER, m_msFbo));
GLU_CHECK_CALL(glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, m_msColorRbo));
GLU_CHECK_CALL(glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER,
m_msDepthStencilRbo));
// Setup resolve color RBO.
GLU_CHECK_CALL(glGenRenderbuffers(1, &m_resolveColorRbo));
GLU_CHECK_CALL(glBindRenderbuffer(GL_RENDERBUFFER, m_resolveColorRbo));
GLU_CHECK_CALL(glRenderbufferStorage(GL_RENDERBUFFER, FBO_COLOR_FORMAT, m_renderWidth, m_renderHeight));
// Setup resolve FBO.
GLU_CHECK_CALL(glGenFramebuffers(1, &m_resolveFbo));
GLU_CHECK_CALL(glBindFramebuffer(GL_FRAMEBUFFER, m_resolveFbo));
GLU_CHECK_CALL(
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, m_resolveColorRbo));
// Use ms FBO.
GLU_CHECK_CALL(glBindFramebuffer(GL_FRAMEBUFFER, m_msFbo));
}
// Get suitable viewport size.
m_viewportSize = de::min<int>(m_desiredViewportSize, de::min(m_renderWidth, m_renderHeight));
randomizeViewport();
}
/*--------------------------------------------------------------------*//*!
* \brief Base class for cases testing the value of sample count.
*
* Draws a test pattern (defined by renderPattern() of an inheriting class)
* and counts the number of distinct colors in the resulting image. That
* number should be at least the value of sample count plus one. This is
* repeated with increased values of m_currentIteration until this correct
* number of colors is detected or m_currentIteration reaches
* m_maxNumIterations.
*//*--------------------------------------------------------------------*/
class NumSamplesCase : public MultisampleCase
{
public:
NumSamplesCase(Context &context, const char *name, const char *description,
const FboParams &fboParams = FboParams());
~NumSamplesCase(void)
{
}
IterateResult iterate(void);
protected:
virtual void renderPattern(void) const = 0;
int m_currentIteration;
private:
enum
{
DEFAULT_MAX_NUM_ITERATIONS = 16
};
const int m_maxNumIterations;
vector<tcu::RGBA> m_detectedColors;
};
NumSamplesCase::NumSamplesCase(Context &context, const char *name, const char *description, const FboParams &fboParams)
: MultisampleCase(context, name, description, 256, fboParams)
, m_currentIteration(0)
, m_maxNumIterations(getIterationCount(m_testCtx, DEFAULT_MAX_NUM_ITERATIONS))
{
}
NumSamplesCase::IterateResult NumSamplesCase::iterate(void)
{
TestLog &log = m_testCtx.getLog();
tcu::Surface renderedImg(m_viewportSize, m_viewportSize);
randomizeViewport();
GLU_CHECK_CALL(glClearColor(0.0f, 0.0f, 0.0f, 1.0f));
GLU_CHECK_CALL(glClear(GL_COLOR_BUFFER_BIT));
renderPattern();
// Read and log rendered image.
readImage(renderedImg);
log << TestLog::Image("RenderedImage", "Rendered image", renderedImg, QP_IMAGE_COMPRESSION_MODE_PNG);
// Detect new, previously unseen colors from image.
int requiredNumDistinctColors = m_numSamples + 1;
for (int y = 0; y < renderedImg.getHeight() && (int)m_detectedColors.size() < requiredNumDistinctColors; y++)
for (int x = 0; x < renderedImg.getWidth() && (int)m_detectedColors.size() < requiredNumDistinctColors; x++)
{
tcu::RGBA color = renderedImg.getPixel(x, y);
int i;
for (i = 0; i < (int)m_detectedColors.size(); i++)
{
if (tcu::compareThreshold(color, m_detectedColors[i], tcu::RGBA(3, 3, 3, 3)))
break;
}
if (i == (int)m_detectedColors.size())
m_detectedColors.push_back(color); // Color not previously detected.
}
// Log results.
log << TestLog::Message << "Number of distinct colors detected so far: "
<< ((int)m_detectedColors.size() >= requiredNumDistinctColors ? "at least " : "")
<< de::toString(m_detectedColors.size()) << TestLog::EndMessage;
if ((int)m_detectedColors.size() < requiredNumDistinctColors)
{
// Haven't detected enough different colors yet.
m_currentIteration++;
if (m_currentIteration >= m_maxNumIterations)
{
const IVec2 targetSize = getRenderTargetSize();
const int detectedNumSamples = (int)m_detectedColors.size() - 1; // One color is the background
log << TestLog::Message << "Failure: Number of distinct colors detected is lower than sample count+1"
<< TestLog::EndMessage;
// For high resolution render targets the lack of samples is not likely detected by a human
// and for GLES 3.0 the application cannot observe the sample count directly. So, it only
// warrants a quality warning.
if ((targetSize.x() >= 2048 || targetSize.y() >= 2048) && (detectedNumSamples >= (m_numSamples / 2)))
m_context.getTestContext().setTestResult(QP_TEST_RESULT_QUALITY_WARNING,
"Measured sample count below the advertised count");
else
m_context.getTestContext().setTestResult(QP_TEST_RESULT_FAIL, "Failed");
return STOP;
}
else
{
log << TestLog::Message
<< "The number of distinct colors detected is lower than sample count+1 - trying again with a slightly "
"altered pattern"
<< TestLog::EndMessage;
return CONTINUE;
}
}
else
{
log << TestLog::Message << "Success: The number of distinct colors detected is at least sample count+1"
<< TestLog::EndMessage;
m_context.getTestContext().setTestResult(QP_TEST_RESULT_PASS, "Passed");
return STOP;
}
}
class PolygonNumSamplesCase : public NumSamplesCase
{
public:
PolygonNumSamplesCase(Context &context, const char *name, const char *description, int numFboSamples = 0);
~PolygonNumSamplesCase(void)
{
}
protected:
void renderPattern(void) const;
};
PolygonNumSamplesCase::PolygonNumSamplesCase(Context &context, const char *name, const char *description,
int numFboSamples)
: NumSamplesCase(context, name, description,
numFboSamples >= 0 ? FboParams(numFboSamples, false, false) : FboParams())
{
}
void PolygonNumSamplesCase::renderPattern(void) const
{
// The test pattern consists of several triangles with edges at different angles.
const int numTriangles = 25;
for (int i = 0; i < numTriangles; i++)
{
float angle0 = 2.0f * DE_PI * (float)i / (float)numTriangles + 0.001f * (float)m_currentIteration;
float angle1 = 2.0f * DE_PI * ((float)i + 0.5f) / (float)numTriangles + 0.001f * (float)m_currentIteration;
renderTriangle(Vec2(0.0f, 0.0f), Vec2(deFloatCos(angle0) * 0.95f, deFloatSin(angle0) * 0.95f),
Vec2(deFloatCos(angle1) * 0.95f, deFloatSin(angle1) * 0.95f), Vec4(1.0f));
}
}
class LineNumSamplesCase : public NumSamplesCase
{
public:
LineNumSamplesCase(Context &context, const char *name, const char *description, int numFboSamples = 0);
~LineNumSamplesCase(void)
{
}
protected:
void renderPattern(void) const;
};
LineNumSamplesCase::LineNumSamplesCase(Context &context, const char *name, const char *description, int numFboSamples)
: NumSamplesCase(context, name, description,
numFboSamples >= 0 ? FboParams(numFboSamples, false, false) : FboParams())
{
}
void LineNumSamplesCase::renderPattern(void) const
{
// The test pattern consists of several lines at different angles.
// We scale the number of lines based on the viewport size. This is because a gl line's thickness is
// constant in pixel units, i.e. they get relatively thicker as viewport size decreases. Thus we must
// decrease the number of lines in order to decrease the extent of overlap among the lines in the
// center of the pattern.
const int numLines = (int)(100.0f * deFloatSqrt((float)m_viewportSize / 256.0f));
for (int i = 0; i < numLines; i++)
{
float angle = 2.0f * DE_PI * (float)i / (float)numLines + 0.001f * (float)m_currentIteration;
renderLine(Vec2(0.0f, 0.0f), Vec2(deFloatCos(angle) * 0.95f, deFloatSin(angle) * 0.95f), Vec4(1.0f));
}
}
/*--------------------------------------------------------------------*//*!
* \brief Case testing behaviour of common edges when multisampling.
*
* Draws a number of test patterns, each with a number of quads, each made
* of two triangles, rotated at different angles. The inner edge inside the
* quad (i.e. the common edge of the two triangles) still should not be
* visible, despite multisampling - i.e. the two triangles forming the quad
* should never get any common coverage bits in any pixel.
*//*--------------------------------------------------------------------*/
class CommonEdgeCase : public MultisampleCase
{
public:
enum CaseType
{
CASETYPE_SMALL_QUADS = 0, //!< Draw several small quads per iteration.
CASETYPE_BIGGER_THAN_VIEWPORT_QUAD, //!< Draw one bigger-than-viewport quad per iteration.
CASETYPE_FIT_VIEWPORT_QUAD, //!< Draw one exactly viewport-sized, axis aligned quad per iteration.
CASETYPE_LAST
};
CommonEdgeCase(Context &context, const char *name, const char *description, CaseType caseType,
int numFboSamples = 0);
~CommonEdgeCase(void)
{
}
void init(void);
IterateResult iterate(void);
private:
enum
{
DEFAULT_SMALL_QUADS_ITERATIONS = 16,
DEFAULT_BIGGER_THAN_VIEWPORT_QUAD_ITERATIONS = 8 * 8
// \note With CASETYPE_FIT_VIEWPORT_QUAD, we don't do rotations other than multiples of 90 deg -> constant number of iterations.
};
const CaseType m_caseType;
const int m_numIterations;
int m_currentIteration;
};
CommonEdgeCase::CommonEdgeCase(Context &context, const char *name, const char *description, CaseType caseType,
int numFboSamples)
: MultisampleCase(context, name, description, caseType == CASETYPE_SMALL_QUADS ? 128 : 32,
numFboSamples >= 0 ? FboParams(numFboSamples, false, false) : FboParams())
, m_caseType(caseType)
, m_numIterations(caseType == CASETYPE_SMALL_QUADS ?
getIterationCount(m_testCtx, DEFAULT_SMALL_QUADS_ITERATIONS) :
caseType == CASETYPE_BIGGER_THAN_VIEWPORT_QUAD ?
getIterationCount(m_testCtx, DEFAULT_BIGGER_THAN_VIEWPORT_QUAD_ITERATIONS) :
8)
, m_currentIteration(0)
{
}
void CommonEdgeCase::init(void)
{
MultisampleCase::init();
if (m_caseType == CASETYPE_SMALL_QUADS)
{
// Check for a big enough viewport. With too small viewports the test case can't analyze the resulting image well enough.
const int minViewportSize = 32;
if (m_viewportSize < minViewportSize)
throw tcu::InternalError("Render target width or height too low (is " + de::toString(m_viewportSize) +
", should be at least " + de::toString(minViewportSize) + ")");
}
GLU_CHECK_CALL(glEnable(GL_BLEND));
GLU_CHECK_CALL(glBlendEquation(GL_FUNC_ADD));
GLU_CHECK_CALL(glBlendFunc(GL_ONE, GL_ONE));
m_testCtx.getLog() << TestLog::Message
<< "Additive blending enabled in order to detect (erroneously) overlapping samples"
<< TestLog::EndMessage;
}
CommonEdgeCase::IterateResult CommonEdgeCase::iterate(void)
{
TestLog &log = m_testCtx.getLog();
tcu::Surface renderedImg(m_viewportSize, m_viewportSize);
tcu::Surface errorImg(m_viewportSize, m_viewportSize);
randomizeViewport();
GLU_CHECK_CALL(glClearColor(0.0f, 0.0f, 0.0f, 1.0f));
GLU_CHECK_CALL(glClear(GL_COLOR_BUFFER_BIT));
// Draw test pattern. Test patterns consist of quads formed with two triangles.
// After drawing the pattern, we check that the interior pixels of each quad are
// all the same color - this is meant to verify that there are no artifacts on the inner edge.
vector<QuadCorners> unicoloredRegions;
if (m_caseType == CASETYPE_SMALL_QUADS)
{
// Draw several quads, rotated at different angles.
const float quadDiagLen = 2.0f / 3.0f * 0.9f; // \note Fit 3 quads in both x and y directions.
float angleCos;
float angleSin;
// \note First and second iteration get exact 0 (and 90, 180, 270) and 45 (and 135, 225, 315) angle quads, as they are kind of a special case.
if (m_currentIteration == 0)
{
angleCos = 1.0f;
angleSin = 0.0f;
}
else if (m_currentIteration == 1)
{
angleCos = SQRT_HALF;
angleSin = SQRT_HALF;
}
else
{
float angle = 0.5f * DE_PI * (float)(m_currentIteration - 1) / (float)(m_numIterations - 1);
angleCos = deFloatCos(angle);
angleSin = deFloatSin(angle);
}
Vec2 corners[4] = {
0.5f * quadDiagLen * Vec2(angleCos, angleSin), 0.5f * quadDiagLen * Vec2(-angleSin, angleCos),
0.5f * quadDiagLen * Vec2(-angleCos, -angleSin), 0.5f * quadDiagLen * Vec2(angleSin, -angleCos)};
unicoloredRegions.reserve(8);
// Draw 8 quads.
// First four are rotated at angles angle+0, angle+90, angle+180 and angle+270.
// Last four are rotated the same angles as the first four, but the ordering of the last triangle's vertices is reversed.
for (int quadNdx = 0; quadNdx < 8; quadNdx++)
{
Vec2 center = (2.0f - quadDiagLen) * Vec2((float)(quadNdx % 3), (float)(quadNdx / 3)) / 2.0f -
0.5f * (2.0f - quadDiagLen);
renderTriangle(corners[(0 + quadNdx) % 4] + center, corners[(1 + quadNdx) % 4] + center,
corners[(2 + quadNdx) % 4] + center, Vec4(0.5f, 0.5f, 0.5f, 1.0f));
if (quadNdx >= 4)
{
renderTriangle(corners[(3 + quadNdx) % 4] + center, corners[(2 + quadNdx) % 4] + center,
corners[(0 + quadNdx) % 4] + center, Vec4(0.5f, 0.5f, 0.5f, 1.0f));
}
else
{
renderTriangle(corners[(0 + quadNdx) % 4] + center, corners[(2 + quadNdx) % 4] + center,
corners[(3 + quadNdx) % 4] + center, Vec4(0.5f, 0.5f, 0.5f, 1.0f));
}
// The size of the "interior" of a quad is assumed to be approximately unicolorRegionScale*<actual size of quad>.
// By "interior" we here mean the region of non-boundary pixels of the rendered quad for which we can safely assume
// that it has all coverage bits set to 1, for every pixel.
float unicolorRegionScale = 1.0f - 6.0f * 2.0f / (float)m_viewportSize / quadDiagLen;
unicoloredRegions.push_back(
QuadCorners((center + corners[0] * unicolorRegionScale), (center + corners[1] * unicolorRegionScale),
(center + corners[2] * unicolorRegionScale), (center + corners[3] * unicolorRegionScale)));
}
}
else if (m_caseType == CASETYPE_BIGGER_THAN_VIEWPORT_QUAD)
{
// Draw a bigger-than-viewport quad, rotated at an angle depending on m_currentIteration.
int quadBaseAngleNdx = m_currentIteration / 8;
int quadSubAngleNdx = m_currentIteration % 8;
float angleCos;
float angleSin;
if (quadBaseAngleNdx == 0)
{
angleCos = 1.0f;
angleSin = 0.0f;
}
else if (quadBaseAngleNdx == 1)
{
angleCos = SQRT_HALF;
angleSin = SQRT_HALF;
}
else
{
float angle = 0.5f * DE_PI * (float)(m_currentIteration - 1) / (float)(m_numIterations - 1);
angleCos = deFloatCos(angle);
angleSin = deFloatSin(angle);
}
float quadDiagLen = 2.5f / de::max(angleCos, angleSin);
Vec2 corners[4] = {
0.5f * quadDiagLen * Vec2(angleCos, angleSin), 0.5f * quadDiagLen * Vec2(-angleSin, angleCos),
0.5f * quadDiagLen * Vec2(-angleCos, -angleSin), 0.5f * quadDiagLen * Vec2(angleSin, -angleCos)};
renderTriangle(corners[(0 + quadSubAngleNdx) % 4], corners[(1 + quadSubAngleNdx) % 4],
corners[(2 + quadSubAngleNdx) % 4], Vec4(0.5f, 0.5f, 0.5f, 1.0f));
if (quadSubAngleNdx >= 4)
{
renderTriangle(corners[(3 + quadSubAngleNdx) % 4], corners[(2 + quadSubAngleNdx) % 4],
corners[(0 + quadSubAngleNdx) % 4], Vec4(0.5f, 0.5f, 0.5f, 1.0f));
}
else
{
renderTriangle(corners[(0 + quadSubAngleNdx) % 4], corners[(2 + quadSubAngleNdx) % 4],
corners[(3 + quadSubAngleNdx) % 4], Vec4(0.5f, 0.5f, 0.5f, 1.0f));
}
float unicolorRegionScale = 1.0f - 6.0f * 2.0f / (float)m_viewportSize / quadDiagLen;
unicoloredRegions.push_back(QuadCorners((corners[0] * unicolorRegionScale), (corners[1] * unicolorRegionScale),
(corners[2] * unicolorRegionScale),
(corners[3] * unicolorRegionScale)));
}
else if (m_caseType == CASETYPE_FIT_VIEWPORT_QUAD)
{
// Draw an exactly viewport-sized quad, rotated by multiples of 90 degrees angle depending on m_currentIteration.
int quadSubAngleNdx = m_currentIteration % 8;
Vec2 corners[4] = {Vec2(1.0f, 1.0f), Vec2(-1.0f, 1.0f), Vec2(-1.0f, -1.0f), Vec2(1.0f, -1.0f)};
renderTriangle(corners[(0 + quadSubAngleNdx) % 4], corners[(1 + quadSubAngleNdx) % 4],
corners[(2 + quadSubAngleNdx) % 4], Vec4(0.5f, 0.5f, 0.5f, 1.0f));
if (quadSubAngleNdx >= 4)
{
renderTriangle(corners[(3 + quadSubAngleNdx) % 4], corners[(2 + quadSubAngleNdx) % 4],
corners[(0 + quadSubAngleNdx) % 4], Vec4(0.5f, 0.5f, 0.5f, 1.0f));
}
else
{
renderTriangle(corners[(0 + quadSubAngleNdx) % 4], corners[(2 + quadSubAngleNdx) % 4],
corners[(3 + quadSubAngleNdx) % 4], Vec4(0.5f, 0.5f, 0.5f, 1.0f));
}
unicoloredRegions.push_back(QuadCorners(corners[0], corners[1], corners[2], corners[3]));
}
else
DE_ASSERT(false);
// Read pixels and check unicolored regions.
readImage(renderedImg);
tcu::clear(errorImg.getAccess(), Vec4(0.0f, 1.0f, 0.0f, 1.0f));
log << TestLog::Image("RenderedImage", "Rendered image", renderedImg, QP_IMAGE_COMPRESSION_MODE_PNG);
bool errorsDetected = false;
for (int i = 0; i < (int)unicoloredRegions.size(); i++)
{
const QuadCorners &region = unicoloredRegions[i];
IVec2 p0Win = ((region.p0 + 1.0f) * 0.5f * (float)(m_viewportSize - 1) + 0.5f).asInt();
IVec2 p1Win = ((region.p1 + 1.0f) * 0.5f * (float)(m_viewportSize - 1) + 0.5f).asInt();
IVec2 p2Win = ((region.p2 + 1.0f) * 0.5f * (float)(m_viewportSize - 1) + 0.5f).asInt();
IVec2 p3Win = ((region.p3 + 1.0f) * 0.5f * (float)(m_viewportSize - 1) + 0.5f).asInt();
bool errorsInCurrentRegion = !isPixelRegionUnicolored(renderedImg, p0Win, p1Win, p2Win, p3Win);
if (errorsInCurrentRegion)
drawUnicolorTestErrors(renderedImg, errorImg.getAccess(), p0Win, p1Win, p2Win, p3Win);
errorsDetected = errorsDetected || errorsInCurrentRegion;
}
m_currentIteration++;
if (errorsDetected)
{
log << TestLog::Message << "Failure: Not all quad interiors seem unicolored - common-edge artifacts?"
<< TestLog::EndMessage;
log << TestLog::Message << "Erroneous pixels are drawn red in the following image" << TestLog::EndMessage;
log << TestLog::Image("RenderedImageWithErrors", "Rendered image with errors marked", renderedImg,
QP_IMAGE_COMPRESSION_MODE_PNG);
log << TestLog::Image("ErrorsOnly", "Image with error pixels only", errorImg, QP_IMAGE_COMPRESSION_MODE_PNG);
m_context.getTestContext().setTestResult(QP_TEST_RESULT_FAIL, "Failed");
return STOP;
}
else if (m_currentIteration < m_numIterations)
{
log << TestLog::Message << "Quads seem OK - moving on to next pattern" << TestLog::EndMessage;
return CONTINUE;
}
else
{
log << TestLog::Message << "Success: All quad interiors seem unicolored (no common-edge artifacts)"
<< TestLog::EndMessage;
m_context.getTestContext().setTestResult(QP_TEST_RESULT_PASS, "Passed");
return STOP;
}
}
/*--------------------------------------------------------------------*//*!
* \brief Test that depth values are per-sample.
*
* Draws intersecting, differently-colored polygons and checks that there
* are at least sample count+1 distinct colors present, due to some of the
* samples at the intersection line belonging to one and some to another
* polygon.
*//*--------------------------------------------------------------------*/
class SampleDepthCase : public NumSamplesCase
{
public:
SampleDepthCase(Context &context, const char *name, const char *description, int numFboSamples = 0);
~SampleDepthCase(void)
{
}
void init(void);
protected:
void renderPattern(void) const;
};
SampleDepthCase::SampleDepthCase(Context &context, const char *name, const char *description, int numFboSamples)
: NumSamplesCase(context, name, description,
numFboSamples >= 0 ? FboParams(numFboSamples, true, false) : FboParams())
{
}
void SampleDepthCase::init(void)
{
TestLog &log = m_testCtx.getLog();
if (m_context.getRenderTarget().getDepthBits() == 0)
TCU_THROW(NotSupportedError, "Test requires depth buffer");
MultisampleCase::init();
GLU_CHECK_CALL(glEnable(GL_DEPTH_TEST));
GLU_CHECK_CALL(glDepthFunc(GL_LESS));
log << TestLog::Message << "Depth test enabled, depth func is GL_LESS" << TestLog::EndMessage;
log << TestLog::Message << "Drawing several bigger-than-viewport black or white polygons intersecting each other"
<< TestLog::EndMessage;
}
void SampleDepthCase::renderPattern(void) const
{
GLU_CHECK_CALL(glClearColor(0.0f, 0.0f, 0.0f, 0.0f));
GLU_CHECK_CALL(glClearDepthf(1.0f));
GLU_CHECK_CALL(glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT));
{
const int numPolygons = 50;
for (int i = 0; i < numPolygons; i++)
{
Vec4 color = i % 2 == 0 ? Vec4(1.0f, 1.0f, 1.0f, 1.0f) : Vec4(0.0f, 0.0f, 0.0f, 1.0f);
float angle = 2.0f * DE_PI * (float)i / (float)numPolygons + 0.001f * (float)m_currentIteration;
Vec3 pt0(3.0f * deFloatCos(angle + 2.0f * DE_PI * 0.0f / 3.0f),
3.0f * deFloatSin(angle + 2.0f * DE_PI * 0.0f / 3.0f), 1.0f);
Vec3 pt1(3.0f * deFloatCos(angle + 2.0f * DE_PI * 1.0f / 3.0f),
3.0f * deFloatSin(angle + 2.0f * DE_PI * 1.0f / 3.0f), 0.0f);
Vec3 pt2(3.0f * deFloatCos(angle + 2.0f * DE_PI * 2.0f / 3.0f),
3.0f * deFloatSin(angle + 2.0f * DE_PI * 2.0f / 3.0f), 0.0f);
renderTriangle(pt0, pt1, pt2, color);
}
}
}
/*--------------------------------------------------------------------*//*!
* \brief Test that stencil buffer values are per-sample.
*
* Draws a unicolored pattern and marks drawn samples in stencil buffer;
* then clears and draws a viewport-size quad with that color and with
* proper stencil test such that the resulting image should be exactly the
* same as after the pattern was first drawn.
*//*--------------------------------------------------------------------*/
class SampleStencilCase : public MultisampleCase
{
public:
SampleStencilCase(Context &context, const char *name, const char *description, int numFboSamples = 0);
~SampleStencilCase(void)
{
}
void init(void);
IterateResult iterate(void);
};
SampleStencilCase::SampleStencilCase(Context &context, const char *name, const char *description, int numFboSamples)
: MultisampleCase(context, name, description, 256,
numFboSamples >= 0 ? FboParams(numFboSamples, false, true) : FboParams())
{
}
void SampleStencilCase::init(void)
{
if (m_context.getRenderTarget().getStencilBits() == 0)
TCU_THROW(NotSupportedError, "Test requires stencil buffer");
MultisampleCase::init();
}
SampleStencilCase::IterateResult SampleStencilCase::iterate(void)
{
TestLog &log = m_testCtx.getLog();
tcu::Surface renderedImgFirst(m_viewportSize, m_viewportSize);
tcu::Surface renderedImgSecond(m_viewportSize, m_viewportSize);
randomizeViewport();
GLU_CHECK_CALL(glClearColor(0.0f, 0.0f, 0.0f, 1.0f));
GLU_CHECK_CALL(glClearStencil(0));
GLU_CHECK_CALL(glClear(GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT));
GLU_CHECK_CALL(glEnable(GL_STENCIL_TEST));
GLU_CHECK_CALL(glStencilFunc(GL_ALWAYS, 1, 1));
GLU_CHECK_CALL(glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE));
log << TestLog::Message
<< "Drawing a pattern with glStencilFunc(GL_ALWAYS, 1, 1) and glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE)"
<< TestLog::EndMessage;
{
const int numTriangles = 25;
for (int i = 0; i < numTriangles; i++)
{
float angle0 = 2.0f * DE_PI * (float)i / (float)numTriangles;
float angle1 = 2.0f * DE_PI * ((float)i + 0.5f) / (float)numTriangles;
renderTriangle(Vec2(0.0f, 0.0f), Vec2(deFloatCos(angle0) * 0.95f, deFloatSin(angle0) * 0.95f),
Vec2(deFloatCos(angle1) * 0.95f, deFloatSin(angle1) * 0.95f), Vec4(1.0f));
}
}
readImage(renderedImgFirst);
log << TestLog::Image("RenderedImgFirst", "First image rendered", renderedImgFirst, QP_IMAGE_COMPRESSION_MODE_PNG);
log << TestLog::Message << "Clearing color buffer to black" << TestLog::EndMessage;
GLU_CHECK_CALL(glClear(GL_COLOR_BUFFER_BIT));
GLU_CHECK_CALL(glStencilFunc(GL_EQUAL, 1, 1));
GLU_CHECK_CALL(glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP));
{
log << TestLog::Message << "Checking that color buffer was actually cleared to black" << TestLog::EndMessage;
tcu::Surface clearedImg(m_viewportSize, m_viewportSize);
readImage(clearedImg);
for (int y = 0; y < clearedImg.getHeight(); y++)
for (int x = 0; x < clearedImg.getWidth(); x++)
{
const tcu::RGBA &clr = clearedImg.getPixel(x, y);
if (clr != tcu::RGBA::black())
{
log << TestLog::Message << "Failure: first non-black pixel, color " << clr
<< ", detected at coordinates (" << x << ", " << y << ")" << TestLog::EndMessage;
log << TestLog::Image("ClearedImg", "Image after clearing, erroneously non-black", clearedImg);
m_context.getTestContext().setTestResult(QP_TEST_RESULT_FAIL, "Failed");
return STOP;
}
}
}
log << TestLog::Message
<< "Drawing a viewport-sized quad with glStencilFunc(GL_EQUAL, 1, 1) and glStencilOp(GL_KEEP, GL_KEEP, "
"GL_KEEP) - should result in same image as the first"
<< TestLog::EndMessage;
renderQuad(Vec2(-1.0f, -1.0f), Vec2(1.0f, -1.0f), Vec2(-1.0f, 1.0f), Vec2(1.0f, 1.0f), Vec4(1.0f));
readImage(renderedImgSecond);
log << TestLog::Image("RenderedImgSecond", "Second image rendered", renderedImgSecond,
QP_IMAGE_COMPRESSION_MODE_PNG);
bool passed = tcu::pixelThresholdCompare(log, "ImageCompare", "Image comparison", renderedImgFirst,
renderedImgSecond, tcu::RGBA(0), tcu::COMPARE_LOG_ON_ERROR);
if (passed)
log << TestLog::Message << "Success: The two images rendered are identical" << TestLog::EndMessage;
m_context.getTestContext().setTestResult(passed ? QP_TEST_RESULT_PASS : QP_TEST_RESULT_FAIL,
passed ? "Passed" : "Failed");
return STOP;
}
/*--------------------------------------------------------------------*//*!
* \brief Tests coverage mask generation proportionality property.
*
* Tests that the number of coverage bits in a coverage mask created by
* GL_SAMPLE_ALPHA_TO_COVERAGE or GL_SAMPLE_COVERAGE is, on average,
* proportional to the alpha or coverage value, respectively. Draws
* multiple frames, each time increasing the alpha or coverage value used,
* and checks that the average color is changing appropriately.
*//*--------------------------------------------------------------------*/
class MaskProportionalityCase : public MultisampleCase
{
public:
enum CaseType
{
CASETYPE_ALPHA_TO_COVERAGE = 0,
CASETYPE_SAMPLE_COVERAGE,
CASETYPE_SAMPLE_COVERAGE_INVERTED,
CASETYPE_LAST
};
MaskProportionalityCase(Context &context, const char *name, const char *description, CaseType type,
int numFboSamples = 0);
~MaskProportionalityCase(void)
{
}
void init(void);
IterateResult iterate(void);
private:
const CaseType m_type;
int m_numIterations;
int m_currentIteration;
int32_t m_previousIterationColorSum;
};
MaskProportionalityCase::MaskProportionalityCase(Context &context, const char *name, const char *description,
CaseType type, int numFboSamples)
: MultisampleCase(context, name, description, 32,
numFboSamples >= 0 ? FboParams(numFboSamples, false, false) : FboParams())
, m_type(type)
, m_numIterations(0)
, m_currentIteration(0)
, m_previousIterationColorSum(-1)
{
}
void MaskProportionalityCase::init(void)
{
TestLog &log = m_testCtx.getLog();
MultisampleCase::init();
if (m_type == CASETYPE_ALPHA_TO_COVERAGE)
{
GLU_CHECK_CALL(glEnable(GL_SAMPLE_ALPHA_TO_COVERAGE));
log << TestLog::Message << "GL_SAMPLE_ALPHA_TO_COVERAGE is enabled" << TestLog::EndMessage;
}
else
{
DE_ASSERT(m_type == CASETYPE_SAMPLE_COVERAGE || m_type == CASETYPE_SAMPLE_COVERAGE_INVERTED);
GLU_CHECK_CALL(glEnable(GL_SAMPLE_COVERAGE));
log << TestLog::Message << "GL_SAMPLE_COVERAGE is enabled" << TestLog::EndMessage;
}
m_numIterations = de::max(2, getIterationCount(m_testCtx, m_numSamples * 5));
randomizeViewport(); // \note Using the same viewport for every iteration since coverage mask may depend on window-relative pixel coordinate.
}
MaskProportionalityCase::IterateResult MaskProportionalityCase::iterate(void)
{
TestLog &log = m_testCtx.getLog();
tcu::Surface renderedImg(m_viewportSize, m_viewportSize);
int32_t numPixels = (int32_t)renderedImg.getWidth() * (int32_t)renderedImg.getHeight();
log << TestLog::Message << "Clearing color to black" << TestLog::EndMessage;
GLU_CHECK_CALL(glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE));
GLU_CHECK_CALL(glClearColor(0.0f, 0.0f, 0.0f, 1.0f));
GLU_CHECK_CALL(glClear(GL_COLOR_BUFFER_BIT));
if (m_type == CASETYPE_ALPHA_TO_COVERAGE)
{
GLU_CHECK_CALL(glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_FALSE));
log << TestLog::Message << "Using color mask TRUE, TRUE, TRUE, FALSE" << TestLog::EndMessage;
}
// Draw quad.
{
const Vec2 pt0(-1.0f, -1.0f);
const Vec2 pt1(1.0f, -1.0f);
const Vec2 pt2(-1.0f, 1.0f);
const Vec2 pt3(1.0f, 1.0f);
Vec4 quadColor(1.0f, 0.0f, 0.0f, 1.0f);
float alphaOrCoverageValue = (float)m_currentIteration / (float)(m_numIterations - 1);
if (m_type == CASETYPE_ALPHA_TO_COVERAGE)
{
log << TestLog::Message
<< "Drawing a red quad using alpha value " + de::floatToString(alphaOrCoverageValue, 2)
<< TestLog::EndMessage;
quadColor.w() = alphaOrCoverageValue;
}
else
{
DE_ASSERT(m_type == CASETYPE_SAMPLE_COVERAGE || m_type == CASETYPE_SAMPLE_COVERAGE_INVERTED);
bool isInverted = m_type == CASETYPE_SAMPLE_COVERAGE_INVERTED;
float coverageValue = isInverted ? 1.0f - alphaOrCoverageValue : alphaOrCoverageValue;
log << TestLog::Message
<< "Drawing a red quad using sample coverage value " + de::floatToString(coverageValue, 2)
<< (isInverted ? " (inverted)" : "") << TestLog::EndMessage;
GLU_CHECK_CALL(glSampleCoverage(coverageValue, isInverted ? GL_TRUE : GL_FALSE));
}
renderQuad(pt0, pt1, pt2, pt3, quadColor);
}
// Read ang log image.
readImage(renderedImg);
log << TestLog::Image("RenderedImage", "Rendered image", renderedImg, QP_IMAGE_COMPRESSION_MODE_PNG);
// Compute average red component in rendered image.
int32_t sumRed = 0;
for (int y = 0; y < renderedImg.getHeight(); y++)
for (int x = 0; x < renderedImg.getWidth(); x++)
sumRed += renderedImg.getPixel(x, y).getRed();
log << TestLog::Message
<< "Average red color component: " << de::floatToString((float)sumRed / 255.0f / (float)numPixels, 2)
<< TestLog::EndMessage;
// Check if average color has decreased from previous frame's color.
if (sumRed < m_previousIterationColorSum)
{
log << TestLog::Message << "Failure: Current average red color component is lower than previous"
<< TestLog::EndMessage;
m_context.getTestContext().setTestResult(QP_TEST_RESULT_FAIL, "Failed");
return STOP;
}
// Check if coverage mask is not all-zeros if alpha or coverage value is 0 (or 1, if inverted).
if (m_currentIteration == 0 && sumRed != 0)
{
log << TestLog::Message << "Failure: Image should be completely black" << TestLog::EndMessage;
m_context.getTestContext().setTestResult(QP_TEST_RESULT_FAIL, "Failed");
return STOP;
}
if (m_currentIteration == m_numIterations - 1 && sumRed != 0xff * numPixels)
{
log << TestLog::Message << "Failure: Image should be completely red" << TestLog::EndMessage;
m_context.getTestContext().setTestResult(QP_TEST_RESULT_FAIL, "Failed");
return STOP;
}
m_previousIterationColorSum = sumRed;
m_currentIteration++;
if (m_currentIteration >= m_numIterations)
{
log << TestLog::Message
<< "Success: Number of coverage mask bits set appears to be, on average, proportional to "
<< (m_type == CASETYPE_ALPHA_TO_COVERAGE ? "alpha" :
m_type == CASETYPE_SAMPLE_COVERAGE ? "sample coverage value" :
"inverted sample coverage value")
<< TestLog::EndMessage;
m_context.getTestContext().setTestResult(QP_TEST_RESULT_PASS, "Passed");
return STOP;
}
else
return CONTINUE;
}
/*--------------------------------------------------------------------*//*!
* \brief Tests coverage mask generation constancy property.
*
* Tests that the coverage mask created by GL_SAMPLE_ALPHA_TO_COVERAGE or
* GL_SAMPLE_COVERAGE is constant at given pixel coordinates, with a given
* alpha component or coverage value, respectively. Draws two quads, with
* the second one fully overlapping the first one such that at any given
* pixel, both quads have the same alpha or coverage value. This way, if
* the constancy property is fulfilled, only the second quad should be
* visible.
*//*--------------------------------------------------------------------*/
class MaskConstancyCase : public MultisampleCase
{
public:
enum CaseType
{
CASETYPE_ALPHA_TO_COVERAGE = 0, //!< Use only alpha-to-coverage.
CASETYPE_SAMPLE_COVERAGE, //!< Use only sample coverage.
CASETYPE_SAMPLE_COVERAGE_INVERTED, //!< Use only inverted sample coverage.
CASETYPE_BOTH, //!< Use both alpha-to-coverage and sample coverage.
CASETYPE_BOTH_INVERTED, //!< Use both alpha-to-coverage and inverted sample coverage.
CASETYPE_LAST
};
MaskConstancyCase(Context &context, const char *name, const char *description, CaseType type,
int numFboSamples = 0);
~MaskConstancyCase(void)
{
}
IterateResult iterate(void);
private:
const bool m_isAlphaToCoverageCase;
const bool m_isSampleCoverageCase;
const bool m_isInvertedSampleCoverageCase;
};
MaskConstancyCase::MaskConstancyCase(Context &context, const char *name, const char *description, CaseType type,
int numFboSamples)
: MultisampleCase(context, name, description, 256,
numFboSamples >= 0 ? FboParams(numFboSamples, false, false) : FboParams())
, m_isAlphaToCoverageCase(type == CASETYPE_ALPHA_TO_COVERAGE || type == CASETYPE_BOTH ||
type == CASETYPE_BOTH_INVERTED)
, m_isSampleCoverageCase(type == CASETYPE_SAMPLE_COVERAGE || type == CASETYPE_SAMPLE_COVERAGE_INVERTED ||
type == CASETYPE_BOTH || type == CASETYPE_BOTH_INVERTED)
, m_isInvertedSampleCoverageCase(type == CASETYPE_SAMPLE_COVERAGE_INVERTED || type == CASETYPE_BOTH_INVERTED)
{
}
MaskConstancyCase::IterateResult MaskConstancyCase::iterate(void)
{
TestLog &log = m_testCtx.getLog();
tcu::Surface renderedImg(m_viewportSize, m_viewportSize);
randomizeViewport();
log << TestLog::Message << "Clearing color to black" << TestLog::EndMessage;
GLU_CHECK_CALL(glClearColor(0.0f, 0.0f, 0.0f, 1.0f));
GLU_CHECK_CALL(glClear(GL_COLOR_BUFFER_BIT));
if (m_isAlphaToCoverageCase)
{
GLU_CHECK_CALL(glEnable(GL_SAMPLE_ALPHA_TO_COVERAGE));
GLU_CHECK_CALL(glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_FALSE));
log << TestLog::Message << "GL_SAMPLE_ALPHA_TO_COVERAGE is enabled" << TestLog::EndMessage;
log << TestLog::Message << "Color mask is TRUE, TRUE, TRUE, FALSE" << TestLog::EndMessage;
}
if (m_isSampleCoverageCase)
{
GLU_CHECK_CALL(glEnable(GL_SAMPLE_COVERAGE));
log << TestLog::Message << "GL_SAMPLE_COVERAGE is enabled" << TestLog::EndMessage;
}
log << TestLog::Message << "Drawing several green quads, each fully overlapped by a red quad with the same "
<< (m_isAlphaToCoverageCase ? "alpha" : "")
<< (m_isAlphaToCoverageCase && m_isSampleCoverageCase ? " and " : "")
<< (m_isInvertedSampleCoverageCase ? "inverted " : "") << (m_isSampleCoverageCase ? "sample coverage" : "")
<< " values" << TestLog::EndMessage;
const int numQuadRowsCols = m_numSamples * 4;
for (int row = 0; row < numQuadRowsCols; row++)
{
for (int col = 0; col < numQuadRowsCols; col++)
{
float x0 = (float)(col + 0) / (float)numQuadRowsCols * 2.0f - 1.0f;
float x1 = (float)(col + 1) / (float)numQuadRowsCols * 2.0f - 1.0f;
float y0 = (float)(row + 0) / (float)numQuadRowsCols * 2.0f - 1.0f;
float y1 = (float)(row + 1) / (float)numQuadRowsCols * 2.0f - 1.0f;
const Vec4 baseGreen(0.0f, 1.0f, 0.0f, 0.0f);
const Vec4 baseRed(1.0f, 0.0f, 0.0f, 0.0f);
Vec4 alpha0(0.0f, 0.0f, 0.0f, m_isAlphaToCoverageCase ? (float)col / (float)(numQuadRowsCols - 1) : 1.0f);
Vec4 alpha1(0.0f, 0.0f, 0.0f, m_isAlphaToCoverageCase ? (float)row / (float)(numQuadRowsCols - 1) : 1.0f);
if (m_isSampleCoverageCase)
{
float value = (float)(row * numQuadRowsCols + col) / (float)(numQuadRowsCols * numQuadRowsCols - 1);
GLU_CHECK_CALL(glSampleCoverage(m_isInvertedSampleCoverageCase ? 1.0f - value : value,
m_isInvertedSampleCoverageCase ? GL_TRUE : GL_FALSE));
}
renderQuad(Vec2(x0, y0), Vec2(x1, y0), Vec2(x0, y1), Vec2(x1, y1), baseGreen + alpha0, baseGreen + alpha1,
baseGreen + alpha0, baseGreen + alpha1);
renderQuad(Vec2(x0, y0), Vec2(x1, y0), Vec2(x0, y1), Vec2(x1, y1), baseRed + alpha0, baseRed + alpha1,
baseRed + alpha0, baseRed + alpha1);
}
}
readImage(renderedImg);
log << TestLog::Image("RenderedImage", "Rendered image", renderedImg, QP_IMAGE_COMPRESSION_MODE_PNG);
for (int y = 0; y < renderedImg.getHeight(); y++)
for (int x = 0; x < renderedImg.getWidth(); x++)
{
if (renderedImg.getPixel(x, y).getGreen() > 0)
{
log << TestLog::Message
<< "Failure: Non-zero green color component detected - should have been completely overwritten by "
"red quad"
<< TestLog::EndMessage;
m_context.getTestContext().setTestResult(QP_TEST_RESULT_FAIL, "Failed");
return STOP;
}
}
log << TestLog::Message << "Success: Coverage mask appears to be constant at a given pixel coordinate with a given "
<< (m_isAlphaToCoverageCase ? "alpha" : "")
<< (m_isAlphaToCoverageCase && m_isSampleCoverageCase ? " and " : "")
<< (m_isSampleCoverageCase ? "coverage value" : "") << TestLog::EndMessage;
m_context.getTestContext().setTestResult(QP_TEST_RESULT_PASS, "Passed");
return STOP;
}
/*--------------------------------------------------------------------*//*!
* \brief Tests coverage mask inversion validity.
*
* Tests that the coverage masks obtained by glSampleCoverage(..., GL_TRUE)
* and glSampleCoverage(..., GL_FALSE) are indeed each others' inverses.
* This is done by drawing a pattern, with varying coverage values,
* overlapped by a pattern that has inverted masks and is otherwise
* identical. The resulting image is compared to one obtained by drawing
* the same pattern but with all-ones coverage masks.
*//*--------------------------------------------------------------------*/
class CoverageMaskInvertCase : public MultisampleCase
{
public:
CoverageMaskInvertCase(Context &context, const char *name, const char *description, int numFboSamples = 0);
~CoverageMaskInvertCase(void)
{
}
IterateResult iterate(void);
private:
void drawPattern(bool invertSampleCoverage) const;
};
CoverageMaskInvertCase::CoverageMaskInvertCase(Context &context, const char *name, const char *description,
int numFboSamples)
: MultisampleCase(context, name, description, 256,
numFboSamples >= 0 ? FboParams(numFboSamples, false, false) : FboParams())
{
}
void CoverageMaskInvertCase::drawPattern(bool invertSampleCoverage) const
{
const int numTriangles = 25;
for (int i = 0; i < numTriangles; i++)
{
GLU_CHECK_CALL(
glSampleCoverage((float)i / (float)(numTriangles - 1), invertSampleCoverage ? GL_TRUE : GL_FALSE));
float angle0 = 2.0f * DE_PI * (float)i / (float)numTriangles;
float angle1 = 2.0f * DE_PI * ((float)i + 0.5f) / (float)numTriangles;
renderTriangle(Vec2(0.0f, 0.0f), Vec2(deFloatCos(angle0) * 0.95f, deFloatSin(angle0) * 0.95f),
Vec2(deFloatCos(angle1) * 0.95f, deFloatSin(angle1) * 0.95f),
Vec4(0.4f + (float)i / (float)numTriangles * 0.6f, 0.5f + (float)i / (float)numTriangles * 0.3f,
0.6f - (float)i / (float)numTriangles * 0.5f,
0.7f - (float)i / (float)numTriangles * 0.7f));
}
}
CoverageMaskInvertCase::IterateResult CoverageMaskInvertCase::iterate(void)
{
TestLog &log = m_testCtx.getLog();
tcu::Surface renderedImgNoSampleCoverage(m_viewportSize, m_viewportSize);
tcu::Surface renderedImgSampleCoverage(m_viewportSize, m_viewportSize);
randomizeViewport();
GLU_CHECK_CALL(glEnable(GL_BLEND));
GLU_CHECK_CALL(glBlendEquation(GL_FUNC_ADD));
GLU_CHECK_CALL(glBlendFunc(GL_ONE, GL_ONE));
log << TestLog::Message << "Additive blending enabled in order to detect (erroneously) overlapping samples"
<< TestLog::EndMessage;
log << TestLog::Message << "Clearing color to all-zeros" << TestLog::EndMessage;
GLU_CHECK_CALL(glClearColor(0.0f, 0.0f, 0.0f, 0.0f));
GLU_CHECK_CALL(glClear(GL_COLOR_BUFFER_BIT));
log << TestLog::Message << "Drawing the pattern with GL_SAMPLE_COVERAGE disabled" << TestLog::EndMessage;
drawPattern(false);
readImage(renderedImgNoSampleCoverage);
log << TestLog::Image("RenderedImageNoSampleCoverage", "Rendered image with GL_SAMPLE_COVERAGE disabled",
renderedImgNoSampleCoverage, QP_IMAGE_COMPRESSION_MODE_PNG);
log << TestLog::Message << "Clearing color to all-zeros" << TestLog::EndMessage;
GLU_CHECK_CALL(glClear(GL_COLOR_BUFFER_BIT));
GLU_CHECK_CALL(glEnable(GL_SAMPLE_COVERAGE));
log << TestLog::Message << "Drawing the pattern with GL_SAMPLE_COVERAGE enabled, using non-inverted masks"
<< TestLog::EndMessage;
drawPattern(false);
log << TestLog::Message
<< "Drawing the pattern with GL_SAMPLE_COVERAGE enabled, using same sample coverage values but inverted masks"
<< TestLog::EndMessage;
drawPattern(true);
readImage(renderedImgSampleCoverage);
log << TestLog::Image("RenderedImageSampleCoverage", "Rendered image with GL_SAMPLE_COVERAGE enabled",
renderedImgSampleCoverage, QP_IMAGE_COMPRESSION_MODE_PNG);
bool passed = tcu::pixelThresholdCompare(
log, "CoverageVsNoCoverage", "Comparison of same pattern with GL_SAMPLE_COVERAGE disabled and enabled",
renderedImgNoSampleCoverage, renderedImgSampleCoverage, tcu::RGBA(0), tcu::COMPARE_LOG_ON_ERROR);
if (passed)
log << TestLog::Message << "Success: The two images rendered are identical" << TestLog::EndMessage;
m_context.getTestContext().setTestResult(passed ? QP_TEST_RESULT_PASS : QP_TEST_RESULT_FAIL,
passed ? "Passed" : "Failed");
return STOP;
}
MultisampleTests::MultisampleTests(Context &context) : TestCaseGroup(context, "multisample", "Multisampling tests")
{
}
MultisampleTests::~MultisampleTests(void)
{
}
void MultisampleTests::init(void)
{
enum CaseType
{
CASETYPE_DEFAULT_FRAMEBUFFER = 0,
CASETYPE_FBO_4_SAMPLES,
CASETYPE_FBO_8_SAMPLES,
CASETYPE_FBO_MAX_SAMPLES,
CASETYPE_LAST
};
for (int caseTypeI = 0; caseTypeI < (int)CASETYPE_LAST; caseTypeI++)
{
CaseType caseType = (CaseType)caseTypeI;
int numFboSamples = caseType == CASETYPE_DEFAULT_FRAMEBUFFER ? -1 :
caseType == CASETYPE_FBO_4_SAMPLES ? 4 :
caseType == CASETYPE_FBO_8_SAMPLES ? 8 :
caseType == CASETYPE_FBO_MAX_SAMPLES ? 0 :
-2;
TestCaseGroup *group = new TestCaseGroup(
m_context,
caseType == CASETYPE_DEFAULT_FRAMEBUFFER ? "default_framebuffer" :
caseType == CASETYPE_FBO_4_SAMPLES ? "fbo_4_samples" :
caseType == CASETYPE_FBO_8_SAMPLES ? "fbo_8_samples" :
caseType == CASETYPE_FBO_MAX_SAMPLES ? "fbo_max_samples" :
DE_NULL,
caseType == CASETYPE_DEFAULT_FRAMEBUFFER ? "Render into default framebuffer" :
caseType == CASETYPE_FBO_4_SAMPLES ? "Render into a framebuffer object with 4 samples" :
caseType == CASETYPE_FBO_8_SAMPLES ? "Render into a framebuffer object with 8 samples" :
caseType == CASETYPE_FBO_MAX_SAMPLES ?
"Render into a framebuffer object with the maximum number of samples" :
DE_NULL);
DE_ASSERT(group->getName() != DE_NULL);
DE_ASSERT(numFboSamples >= -1);
addChild(group);
group->addChild(new PolygonNumSamplesCase(m_context, "num_samples_polygon",
"Test sanity of the sample count, with polygons", numFboSamples));
group->addChild(new LineNumSamplesCase(m_context, "num_samples_line",
"Test sanity of the sample count, with lines", numFboSamples));
group->addChild(new CommonEdgeCase(m_context, "common_edge_small_quads",
"Test polygons' common edges with small quads",
CommonEdgeCase::CASETYPE_SMALL_QUADS, numFboSamples));
group->addChild(new CommonEdgeCase(m_context, "common_edge_big_quad",
"Test polygons' common edges with bigger-than-viewport quads",
CommonEdgeCase::CASETYPE_BIGGER_THAN_VIEWPORT_QUAD, numFboSamples));
group->addChild(new CommonEdgeCase(m_context, "common_edge_viewport_quad",
"Test polygons' common edges with exactly viewport-sized quads",
CommonEdgeCase::CASETYPE_FIT_VIEWPORT_QUAD, numFboSamples));
group->addChild(
new SampleDepthCase(m_context, "depth", "Test that depth values are per-sample", numFboSamples));
group->addChild(
new SampleStencilCase(m_context, "stencil", "Test that stencil values are per-sample", numFboSamples));
group->addChild(new CoverageMaskInvertCase(
m_context, "sample_coverage_invert",
"Test that non-inverted and inverted sample coverage masks are each other's negations", numFboSamples));
group->addChild(new MaskProportionalityCase(m_context, "proportionality_alpha_to_coverage",
"Test the proportionality property of GL_SAMPLE_ALPHA_TO_COVERAGE",
MaskProportionalityCase::CASETYPE_ALPHA_TO_COVERAGE,
numFboSamples));
group->addChild(new MaskProportionalityCase(m_context, "proportionality_sample_coverage",
"Test the proportionality property of GL_SAMPLE_COVERAGE",
MaskProportionalityCase::CASETYPE_SAMPLE_COVERAGE, numFboSamples));
group->addChild(
new MaskProportionalityCase(m_context, "proportionality_sample_coverage_inverted",
"Test the proportionality property of inverted-mask GL_SAMPLE_COVERAGE",
MaskProportionalityCase::CASETYPE_SAMPLE_COVERAGE_INVERTED, numFboSamples));
group->addChild(new MaskConstancyCase(m_context, "constancy_alpha_to_coverage",
"Test that coverage mask is constant at given coordinates with a given "
"alpha or coverage value, using GL_SAMPLE_ALPHA_TO_COVERAGE",
MaskConstancyCase::CASETYPE_ALPHA_TO_COVERAGE, numFboSamples));
group->addChild(new MaskConstancyCase(m_context, "constancy_sample_coverage",
"Test that coverage mask is constant at given coordinates with a given "
"alpha or coverage value, using GL_SAMPLE_COVERAGE",
MaskConstancyCase::CASETYPE_SAMPLE_COVERAGE, numFboSamples));
group->addChild(new MaskConstancyCase(m_context, "constancy_sample_coverage_inverted",
"Test that coverage mask is constant at given coordinates with a given "
"alpha or coverage value, using inverted-mask GL_SAMPLE_COVERAGE",
MaskConstancyCase::CASETYPE_SAMPLE_COVERAGE_INVERTED, numFboSamples));
group->addChild(
new MaskConstancyCase(m_context, "constancy_both",
"Test that coverage mask is constant at given coordinates with a given alpha or "
"coverage value, using GL_SAMPLE_ALPHA_TO_COVERAGE and GL_SAMPLE_COVERAGE",
MaskConstancyCase::CASETYPE_BOTH, numFboSamples));
group->addChild(new MaskConstancyCase(
m_context, "constancy_both_inverted",
"Test that coverage mask is constant at given coordinates with a given alpha or coverage value, using "
"GL_SAMPLE_ALPHA_TO_COVERAGE and inverted-mask GL_SAMPLE_COVERAGE",
MaskConstancyCase::CASETYPE_BOTH_INVERTED, numFboSamples));
}
}
} // namespace Functional
} // namespace gles3
} // namespace deqp