/*------------------------------------------------------------------------- * 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 gl_FragDepth tests. *//*--------------------------------------------------------------------*/ #include "es3fFragDepthTests.hpp" #include "tcuVector.hpp" #include "tcuTestLog.hpp" #include "tcuSurface.hpp" #include "tcuImageCompare.hpp" #include "tcuRenderTarget.hpp" #include "gluPixelTransfer.hpp" #include "gluShaderProgram.hpp" #include "gluDrawUtil.hpp" #include "deRandom.hpp" #include "deMath.h" #include "deString.h" // For setupDefaultUniforms() #include "glsShaderRenderCase.hpp" #include "glwEnums.hpp" #include "glwFunctions.hpp" namespace deqp { namespace gles3 { namespace Functional { using std::string; using std::vector; using tcu::TestLog; using tcu::Vec2; using tcu::Vec3; using tcu::Vec4; typedef float (*EvalFragDepthFunc)(const Vec2 &coord); static const char *s_vertexShaderSrc = "#version 300 es\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 300 es\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 static inline bool compare(uint32_t 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(false); return false; } } class FragDepthCompareCase : public TestCase { public: FragDepthCompareCase(Context &context, const char *name, const char *desc, const char *fragSrc, EvalFragDepthFunc evalFunc, uint32_t compareFunc); ~FragDepthCompareCase(void); IterateResult iterate(void); private: string m_fragSrc; EvalFragDepthFunc m_evalFunc; uint32_t m_compareFunc; }; FragDepthCompareCase::FragDepthCompareCase(Context &context, const char *name, const char *desc, const char *fragSrc, EvalFragDepthFunc evalFunc, uint32_t compareFunc) : TestCase(context, name, desc) , 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 uint16_t 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(s_vertexShaderSrc, s_defaultFragmentShaderSrc)); 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(s_vertexShaderSrc, m_fragSrc.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. gls::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 - 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, const char *fragSrc, EvalFragDepthFunc evalFunc); ~FragDepthWriteCase(void); IterateResult iterate(void); private: string m_fragSrc; EvalFragDepthFunc m_evalFunc; }; FragDepthWriteCase::FragDepthWriteCase(Context &context, const char *name, const char *desc, const char *fragSrc, EvalFragDepthFunc evalFunc) : TestCase(context, name, desc) , 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 uint16_t quadIndices[] = {0, 1, 2, 2, 1, 3}; // Render with given shader. { glu::ShaderProgram program(m_context.getRenderContext(), glu::makeVtxFragSources(s_vertexShaderSrc, m_fragSrc.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. gls::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(s_vertexShaderSrc, s_defaultFragmentShaderSrc)); 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) : TestCaseGroup(context, "fragdepth", "gl_FragDepth tests") { } 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 300 es\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 300 es\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 300 es\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 300 es\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 300 es\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 300 es\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 300 es\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 300 es\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 300 es\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, 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, cases[ndx].fragSrc, cases[ndx].evalFunc, GL_LESS)); } } // namespace Functional } // namespace gles3 } // namespace deqp