/*------------------------------------------------------------------------- * 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 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 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 <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; } } class FragDepthCompareCase : public TestCase { public: FragDepthCompareCase (Context& context, const char* name, const char* desc, const char* fragSrc, EvalFragDepthFunc evalFunc, deUint32 compareFunc); ~FragDepthCompareCase (void); IterateResult iterate (void); private: string m_fragSrc; EvalFragDepthFunc m_evalFunc; deUint32 m_compareFunc; }; FragDepthCompareCase::FragDepthCompareCase (Context& context, const char* name, const char* desc, const char* fragSrc, EvalFragDepthFunc evalFunc, deUint32 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 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(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) / 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 deUint16 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)); } } // Functional } // gles3 } // deqp