blob: ad4c8cd03ff7ea7403c2c87512dc38d54ebb6aa0 [file] [log] [blame]
/*-------------------------------------------------------------------------
* OpenGL Conformance Test Suite
* -----------------------------
*
* Copyright (c) 2016 Google Inc.
* Copyright (c) 2016 The Khronos Group Inc.
*
* 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 gl_FragDepth tests.
*/ /*-------------------------------------------------------------------*/
#include "glcFragDepthTests.hpp"
#include "deMath.h"
#include "deRandom.hpp"
#include "deString.h"
#include "gluDrawUtil.hpp"
#include "gluPixelTransfer.hpp"
#include "gluShaderProgram.hpp"
#include "glwEnums.hpp"
#include "glwFunctions.hpp"
#include "tcuImageCompare.hpp"
#include "tcuRenderTarget.hpp"
#include "tcuStringTemplate.hpp"
#include "tcuSurface.hpp"
#include "tcuTestLog.hpp"
#include "tcuVector.hpp"
// For setupDefaultUniforms()
#include "glcShaderRenderCase.hpp"
namespace deqp
{
using tcu::Vec2;
using tcu::Vec3;
using tcu::Vec4;
using tcu::TestLog;
using std::string;
using std::vector;
typedef float (*EvalFragDepthFunc)(const Vec2& coord);
static const char* s_vertexShaderSrc = "${VERSION_DECL}\n"
"in highp vec4 a_position;\n"
"in highp vec2 a_coord;\n"
"out highp vec2 v_coord;\n"
"void main (void)\n"
"{\n"
" gl_Position = a_position;\n"
" v_coord = a_coord;\n"
"}\n";
static const char* s_defaultFragmentShaderSrc = "${VERSION_DECL}\n"
"uniform highp vec4 u_color;\n"
"layout(location = 0) out mediump vec4 o_color;\n"
"void main (void)\n"
"{\n"
" o_color = u_color;\n"
"}\n";
template <typename T>
static inline bool compare(deUint32 func, T a, T b)
{
switch (func)
{
case GL_NEVER:
return false;
case GL_ALWAYS:
return true;
case GL_LESS:
return a < b;
case GL_LEQUAL:
return a <= b;
case GL_EQUAL:
return a == b;
case GL_NOTEQUAL:
return a != b;
case GL_GEQUAL:
return a >= b;
case GL_GREATER:
return a > b;
default:
DE_ASSERT(DE_FALSE);
return false;
}
}
static std::string specializeVersion(const std::string& source, glu::GLSLVersion version)
{
DE_ASSERT(version == glu::GLSL_VERSION_300_ES || version == glu::GLSL_VERSION_310_ES ||
version >= glu::GLSL_VERSION_330);
std::map<std::string, std::string> args;
args["VERSION_DECL"] = glu::getGLSLVersionDeclaration(version);
return tcu::StringTemplate(source.c_str()).specialize(args);
}
class FragDepthCompareCase : public TestCase
{
public:
FragDepthCompareCase(Context& context, const char* name, const char* desc, glu::GLSLVersion glslVersion,
const char* fragSrc, EvalFragDepthFunc evalFunc, deUint32 compareFunc);
~FragDepthCompareCase(void);
IterateResult iterate(void);
private:
glu::GLSLVersion m_glslVersion;
string m_fragSrc;
EvalFragDepthFunc m_evalFunc;
deUint32 m_compareFunc;
};
FragDepthCompareCase::FragDepthCompareCase(Context& context, const char* name, const char* desc,
glu::GLSLVersion glslVersion, const char* fragSrc,
EvalFragDepthFunc evalFunc, deUint32 compareFunc)
: TestCase(context, name, desc)
, m_glslVersion(glslVersion)
, m_fragSrc(fragSrc)
, m_evalFunc(evalFunc)
, m_compareFunc(compareFunc)
{
}
FragDepthCompareCase::~FragDepthCompareCase(void)
{
}
FragDepthCompareCase::IterateResult FragDepthCompareCase::iterate(void)
{
TestLog& log = m_testCtx.getLog();
const glw::Functions& gl = m_context.getRenderContext().getFunctions();
de::Random rnd(deStringHash(getName()));
const tcu::RenderTarget& renderTarget = m_context.getRenderContext().getRenderTarget();
int viewportW = de::min(128, renderTarget.getWidth());
int viewportH = de::min(128, renderTarget.getHeight());
int viewportX = rnd.getInt(0, renderTarget.getWidth() - viewportW);
int viewportY = rnd.getInt(0, renderTarget.getHeight() - viewportH);
tcu::Surface renderedFrame(viewportW, viewportH);
tcu::Surface referenceFrame(viewportW, viewportH);
const float constDepth = 0.1f;
if (renderTarget.getDepthBits() == 0)
throw tcu::NotSupportedError("Depth buffer is required", "", __FILE__, __LINE__);
gl.viewport(viewportX, viewportY, viewportW, viewportH);
gl.clear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
gl.enable(GL_DEPTH_TEST);
static const deUint16 quadIndices[] = { 0, 1, 2, 2, 1, 3 };
// Fill viewport with 2 quads - one with constant depth and another with d = [-1..1]
{
glu::ShaderProgram basicQuadProgram(
m_context.getRenderContext(),
glu::makeVtxFragSources(specializeVersion(s_vertexShaderSrc, m_glslVersion).c_str(),
specializeVersion(s_defaultFragmentShaderSrc, m_glslVersion).c_str()));
if (!basicQuadProgram.isOk())
{
log << basicQuadProgram;
TCU_FAIL("Compile failed");
}
const float constDepthCoord[] = { -1.0f, -1.0f, constDepth, 1.0f, -1.0f, +1.0f, constDepth, 1.0f,
0.0f, -1.0f, constDepth, 1.0f, 0.0f, +1.0f, constDepth, 1.0f };
const float varyingDepthCoord[] = { 0.0f, -1.0f, +1.0f, 1.0f, 0.0f, +1.0f, 0.0f, 1.0f,
+1.0f, -1.0f, 0.0f, 1.0f, +1.0f, +1.0f, -1.0f, 1.0f };
gl.useProgram(basicQuadProgram.getProgram());
gl.uniform4f(gl.getUniformLocation(basicQuadProgram.getProgram(), "u_color"), 0.0f, 0.0f, 1.0f, 1.0f);
gl.depthFunc(GL_ALWAYS);
{
glu::VertexArrayBinding posBinding = glu::va::Float("a_position", 4, 4, 0, &constDepthCoord[0]);
glu::draw(m_context.getRenderContext(), basicQuadProgram.getProgram(), 1, &posBinding,
glu::pr::Triangles(DE_LENGTH_OF_ARRAY(quadIndices), &quadIndices[0]));
}
{
glu::VertexArrayBinding posBinding = glu::va::Float("a_position", 4, 4, 0, &varyingDepthCoord[0]);
glu::draw(m_context.getRenderContext(), basicQuadProgram.getProgram(), 1, &posBinding,
glu::pr::Triangles(DE_LENGTH_OF_ARRAY(quadIndices), &quadIndices[0]));
}
GLU_EXPECT_NO_ERROR(gl.getError(), "Draw base quads");
}
// Render with depth test.
{
glu::ShaderProgram program(m_context.getRenderContext(),
glu::makeVtxFragSources(specializeVersion(s_vertexShaderSrc, m_glslVersion).c_str(),
specializeVersion(m_fragSrc, m_glslVersion).c_str()));
log << program;
if (!program.isOk())
TCU_FAIL("Compile failed");
const float coord[] = { 0.0f, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f, 1.0f };
const float position[] = { -1.0f, -1.0f, +1.0f, 1.0f, -1.0f, +1.0f, 0.0f, 1.0f,
+1.0f, -1.0f, 0.0f, 1.0f, +1.0f, +1.0f, -1.0f, 1.0f };
gl.useProgram(program.getProgram());
gl.depthFunc(m_compareFunc);
gl.uniform4f(gl.getUniformLocation(program.getProgram(), "u_color"), 0.0f, 1.0f, 0.0f, 1.0f);
// Setup default helper uniforms.
setupDefaultUniforms(m_context.getRenderContext(), program.getProgram());
{
glu::VertexArrayBinding vertexArrays[] = { glu::va::Float("a_position", 4, 4, 0, &position[0]),
glu::va::Float("a_coord", 2, 4, 0, &coord[0]) };
glu::draw(m_context.getRenderContext(), program.getProgram(), DE_LENGTH_OF_ARRAY(vertexArrays),
&vertexArrays[0], glu::pr::Triangles(DE_LENGTH_OF_ARRAY(quadIndices), &quadIndices[0]));
}
GLU_EXPECT_NO_ERROR(gl.getError(), "Draw test quad");
}
glu::readPixels(m_context.getRenderContext(), viewportX, viewportY, renderedFrame.getAccess());
// Render reference.
for (int y = 0; y < referenceFrame.getHeight(); y++)
{
float yf = ((float)y + 0.5f) / (float)referenceFrame.getHeight();
int half = de::clamp((int)((float)referenceFrame.getWidth() * 0.5f + 0.5f), 0, referenceFrame.getWidth());
// Fill left half - comparison to constant 0.5
for (int x = 0; x < half; x++)
{
float xf = ((float)x + 0.5f) / (float)referenceFrame.getWidth();
float d = m_evalFunc(Vec2(xf, yf));
bool dpass = compare(m_compareFunc, d, constDepth * 0.5f + 0.5f);
referenceFrame.setPixel(x, y, dpass ? tcu::RGBA::green() : tcu::RGBA::blue());
}
// Fill right half - comparison to interpolated depth
for (int x = half; x < referenceFrame.getWidth(); x++)
{
float xf = ((float)x + 0.5f) / (float)referenceFrame.getWidth();
float xh = ((float)x - (float)half + 0.5f) / (float)(referenceFrame.getWidth() - half);
float rd = 1.0f - (xh + yf) * 0.5f;
float d = m_evalFunc(Vec2(xf, yf));
bool dpass = compare(m_compareFunc, d, rd);
referenceFrame.setPixel(x, y, dpass ? tcu::RGBA::green() : tcu::RGBA::blue());
}
}
bool isOk = tcu::fuzzyCompare(log, "Result", "Image comparison result", referenceFrame, renderedFrame, 0.05f,
tcu::COMPARE_LOG_RESULT);
m_testCtx.setTestResult(isOk ? QP_TEST_RESULT_PASS : QP_TEST_RESULT_FAIL, isOk ? "Pass" : "Fail");
return STOP;
}
class FragDepthWriteCase : public TestCase
{
public:
FragDepthWriteCase(Context& context, const char* name, const char* desc, glu::GLSLVersion glslVersion,
const char* fragSrc, EvalFragDepthFunc evalFunc);
~FragDepthWriteCase(void);
IterateResult iterate(void);
private:
glu::GLSLVersion m_glslVersion;
string m_fragSrc;
EvalFragDepthFunc m_evalFunc;
};
FragDepthWriteCase::FragDepthWriteCase(Context& context, const char* name, const char* desc,
glu::GLSLVersion glslVersion, const char* fragSrc, EvalFragDepthFunc evalFunc)
: TestCase(context, name, desc), m_glslVersion(glslVersion), m_fragSrc(fragSrc), m_evalFunc(evalFunc)
{
}
FragDepthWriteCase::~FragDepthWriteCase(void)
{
}
FragDepthWriteCase::IterateResult FragDepthWriteCase::iterate(void)
{
TestLog& log = m_testCtx.getLog();
const glw::Functions& gl = m_context.getRenderContext().getFunctions();
de::Random rnd(deStringHash(getName()));
const tcu::RenderTarget& renderTarget = m_context.getRenderContext().getRenderTarget();
int viewportW = de::min(128, renderTarget.getWidth());
int viewportH = de::min(128, renderTarget.getHeight());
int viewportX = rnd.getInt(0, renderTarget.getWidth() - viewportW);
int viewportY = rnd.getInt(0, renderTarget.getHeight() - viewportH);
tcu::Surface renderedFrame(viewportW, viewportH);
tcu::Surface referenceFrame(viewportW, viewportH);
const int numDepthSteps = 16;
const float depthStep = 1.0f / (float)(numDepthSteps - 1);
if (renderTarget.getDepthBits() == 0)
throw tcu::NotSupportedError("Depth buffer is required", "", __FILE__, __LINE__);
gl.viewport(viewportX, viewportY, viewportW, viewportH);
gl.clear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
gl.enable(GL_DEPTH_TEST);
gl.depthFunc(GL_LESS);
static const deUint16 quadIndices[] = { 0, 1, 2, 2, 1, 3 };
// Render with given shader.
{
glu::ShaderProgram program(m_context.getRenderContext(),
glu::makeVtxFragSources(specializeVersion(s_vertexShaderSrc, m_glslVersion).c_str(),
specializeVersion(m_fragSrc, m_glslVersion).c_str()));
log << program;
if (!program.isOk())
TCU_FAIL("Compile failed");
const float coord[] = { 0.0f, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f, 1.0f };
const float position[] = { -1.0f, -1.0f, +1.0f, 1.0f, -1.0f, +1.0f, 0.0f, 1.0f,
+1.0f, -1.0f, 0.0f, 1.0f, +1.0f, +1.0f, -1.0f, 1.0f };
gl.useProgram(program.getProgram());
gl.uniform4f(gl.getUniformLocation(program.getProgram(), "u_color"), 0.0f, 1.0f, 0.0f, 1.0f);
// Setup default helper uniforms.
setupDefaultUniforms(m_context.getRenderContext(), program.getProgram());
{
glu::VertexArrayBinding vertexArrays[] = { glu::va::Float("a_position", 4, 4, 0, &position[0]),
glu::va::Float("a_coord", 2, 4, 0, &coord[0]) };
glu::draw(m_context.getRenderContext(), program.getProgram(), DE_LENGTH_OF_ARRAY(vertexArrays),
&vertexArrays[0], glu::pr::Triangles(DE_LENGTH_OF_ARRAY(quadIndices), &quadIndices[0]));
}
GLU_EXPECT_NO_ERROR(gl.getError(), "Draw test quad");
}
// Visualize by rendering full-screen quads with increasing depth and color.
{
glu::ShaderProgram program(
m_context.getRenderContext(),
glu::makeVtxFragSources(specializeVersion(s_vertexShaderSrc, m_glslVersion).c_str(),
specializeVersion(s_defaultFragmentShaderSrc, m_glslVersion).c_str()));
if (!program.isOk())
{
log << program;
TCU_FAIL("Compile failed");
}
int posLoc = gl.getAttribLocation(program.getProgram(), "a_position");
int colorLoc = gl.getUniformLocation(program.getProgram(), "u_color");
gl.useProgram(program.getProgram());
gl.depthMask(GL_FALSE);
for (int stepNdx = 0; stepNdx < numDepthSteps; stepNdx++)
{
float f = (float)stepNdx * depthStep;
float depth = f * 2.0f - 1.0f;
Vec4 color = Vec4(f, f, f, 1.0f);
const float position[] = { -1.0f, -1.0f, depth, 1.0f, -1.0f, +1.0f, depth, 1.0f,
+1.0f, -1.0f, depth, 1.0f, +1.0f, +1.0f, depth, 1.0f };
glu::VertexArrayBinding posBinding = glu::va::Float(posLoc, 4, 4, 0, &position[0]);
gl.uniform4fv(colorLoc, 1, color.getPtr());
glu::draw(m_context.getRenderContext(), program.getProgram(), 1, &posBinding,
glu::pr::Triangles(DE_LENGTH_OF_ARRAY(quadIndices), &quadIndices[0]));
}
GLU_EXPECT_NO_ERROR(gl.getError(), "Visualization draw");
}
glu::readPixels(m_context.getRenderContext(), viewportX, viewportY, renderedFrame.getAccess());
// Render reference.
for (int y = 0; y < referenceFrame.getHeight(); y++)
{
for (int x = 0; x < referenceFrame.getWidth(); x++)
{
float xf = ((float)x + 0.5f) / (float)referenceFrame.getWidth();
float yf = ((float)y + 0.5f) / (float)referenceFrame.getHeight();
float d = m_evalFunc(Vec2(xf, yf));
int step = (int)deFloatFloor(d / depthStep);
int col = de::clamp(deRoundFloatToInt32((float)step * depthStep * 255.0f), 0, 255);
referenceFrame.setPixel(x, y, tcu::RGBA(col, col, col, 0xff));
}
}
bool isOk = tcu::fuzzyCompare(log, "Result", "Image comparison result", referenceFrame, renderedFrame, 0.05f,
tcu::COMPARE_LOG_RESULT);
m_testCtx.setTestResult(isOk ? QP_TEST_RESULT_PASS : QP_TEST_RESULT_FAIL, isOk ? "Pass" : "Fail");
return STOP;
}
FragDepthTests::FragDepthTests(Context& context, glu::GLSLVersion glslVersion)
: TestCaseGroup(context, "fragdepth", "gl_FragDepth tests"), m_glslVersion(glslVersion)
{
}
FragDepthTests::~FragDepthTests(void)
{
}
static float evalConstDepth(const Vec2& coord)
{
DE_UNREF(coord);
return 0.5f;
}
static float evalDynamicDepth(const Vec2& coord)
{
return (coord.x() + coord.y()) * 0.5f;
}
static float evalNoWrite(const Vec2& coord)
{
return 1.0f - (coord.x() + coord.y()) * 0.5f;
}
static float evalDynamicConditionalDepth(const Vec2& coord)
{
float d = (coord.x() + coord.y()) * 0.5f;
if (coord.y() < 0.5f)
return d;
else
return 1.0f - d;
}
void FragDepthTests::init(void)
{
static const struct
{
const char* name;
const char* desc;
EvalFragDepthFunc evalFunc;
const char* fragSrc;
} cases[] = {
{ "no_write", "No gl_FragDepth write", evalNoWrite, "${VERSION_DECL}\n"
"uniform highp vec4 u_color;\n"
"layout(location = 0) out mediump vec4 o_color;\n"
"void main (void)\n"
"{\n"
" o_color = u_color;\n"
"}\n" },
{ "const", "Const depth write", evalConstDepth, "${VERSION_DECL}\n"
"uniform highp vec4 u_color;\n"
"layout(location = 0) out mediump vec4 o_color;\n"
"void main (void)\n"
"{\n"
" o_color = u_color;\n"
" gl_FragDepth = 0.5;\n"
"}\n" },
{ "uniform", "Uniform depth write", evalConstDepth, "${VERSION_DECL}\n"
"uniform highp vec4 u_color;\n"
"uniform highp float uf_half;\n"
"layout(location = 0) out mediump vec4 o_color;\n"
"void main (void)\n"
"{\n"
" o_color = u_color;\n"
" gl_FragDepth = uf_half;\n"
"}\n" },
{ "dynamic", "Dynamic depth write", evalDynamicDepth, "${VERSION_DECL}\n"
"uniform highp vec4 u_color;\n"
"in highp vec2 v_coord;\n"
"layout(location = 0) out mediump vec4 o_color;\n"
"void main (void)\n"
"{\n"
" o_color = u_color;\n"
" gl_FragDepth = (v_coord.x+v_coord.y)*0.5;\n"
"}\n" },
{ "fragcoord_z", "gl_FragDepth write from gl_FragCoord.z", evalNoWrite,
"${VERSION_DECL}\n"
"uniform highp vec4 u_color;\n"
"layout(location = 0) out mediump vec4 o_color;\n"
"void main (void)\n"
"{\n"
" o_color = u_color;\n"
" gl_FragDepth = gl_FragCoord.z;\n"
"}\n" },
{ "uniform_conditional_write", "Uniform conditional write", evalDynamicDepth,
"${VERSION_DECL}\n"
"uniform highp vec4 u_color;\n"
"uniform bool ub_true;\n"
"in highp vec2 v_coord;\n"
"layout(location = 0) out mediump vec4 o_color;\n"
"void main (void)\n"
"{\n"
" o_color = u_color;\n"
" if (ub_true)\n"
" gl_FragDepth = (v_coord.x+v_coord.y)*0.5;\n"
"}\n" },
{ "dynamic_conditional_write", "Uniform conditional write", evalDynamicConditionalDepth,
"${VERSION_DECL}\n"
"uniform highp vec4 u_color;\n"
"uniform bool ub_true;\n"
"in highp vec2 v_coord;\n"
"layout(location = 0) out mediump vec4 o_color;\n"
"void main (void)\n"
"{\n"
" o_color = u_color;\n"
" mediump float d = (v_coord.x+v_coord.y)*0.5f;\n"
" if (v_coord.y < 0.5)\n"
" gl_FragDepth = d;\n"
" else\n"
" gl_FragDepth = 1.0 - d;\n"
"}\n" },
{ "uniform_loop_write", "Uniform loop write", evalConstDepth, "${VERSION_DECL}\n"
"uniform highp vec4 u_color;\n"
"uniform int ui_two;\n"
"uniform highp float uf_fourth;\n"
"in highp vec2 v_coord;\n"
"layout(location = 0) out mediump vec4 o_color;\n"
"void main (void)\n"
"{\n"
" o_color = u_color;\n"
" gl_FragDepth = 0.0;\n"
" for (int i = 0; i < ui_two; i++)\n"
" gl_FragDepth += uf_fourth;\n"
"}\n" },
{ "write_in_function", "Uniform loop write", evalDynamicDepth,
"${VERSION_DECL}\n"
"uniform highp vec4 u_color;\n"
"uniform highp float uf_half;\n"
"in highp vec2 v_coord;\n"
"layout(location = 0) out mediump vec4 o_color;\n"
"void myfunc (highp vec2 coord)\n"
"{\n"
" gl_FragDepth = (coord.x+coord.y)*0.5;\n"
"}\n"
"void main (void)\n"
"{\n"
" o_color = u_color;\n"
" myfunc(v_coord);\n"
"}\n" }
};
// .write
tcu::TestCaseGroup* writeGroup = new tcu::TestCaseGroup(m_testCtx, "write", "gl_FragDepth write tests");
addChild(writeGroup);
for (int ndx = 0; ndx < DE_LENGTH_OF_ARRAY(cases); ndx++)
writeGroup->addChild(new FragDepthWriteCase(m_context, cases[ndx].name, cases[ndx].desc, m_glslVersion,
cases[ndx].fragSrc, cases[ndx].evalFunc));
// .compare
tcu::TestCaseGroup* compareGroup =
new tcu::TestCaseGroup(m_testCtx, "compare", "gl_FragDepth used with depth comparison");
addChild(compareGroup);
for (int ndx = 0; ndx < DE_LENGTH_OF_ARRAY(cases); ndx++)
compareGroup->addChild(new FragDepthCompareCase(m_context, cases[ndx].name, cases[ndx].desc, m_glslVersion,
cases[ndx].fragSrc, cases[ndx].evalFunc, GL_LESS));
}
} // deqp