/*------------------------------------------------------------------------- * drawElements Quality Program OpenGL ES 2.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 Shader discard statement tests. *//*--------------------------------------------------------------------*/ #include "es2fShaderDiscardTests.hpp" #include "glsShaderRenderCase.hpp" #include "tcuStringTemplate.hpp" #include "gluTexture.hpp" #include #include #include #include "glwEnums.hpp" #include "glwFunctions.hpp" using tcu::StringTemplate; using std::map; using std::ostringstream; using std::string; using namespace glu; using namespace deqp::gls; namespace deqp { namespace gles2 { namespace Functional { enum CaseFlags { FLAG_USES_TEXTURES = (1 << 0), FLAG_REQUIRES_DYNAMIC_LOOPS = (1 << 1), }; class ShaderDiscardCase : public ShaderRenderCase { public: ShaderDiscardCase(Context &context, const char *name, const char *description, const char *shaderSource, ShaderEvalFunc evalFunc, uint32_t flags); virtual ~ShaderDiscardCase(void); void init(void); void deinit(void); void setupUniforms(int programID, const tcu::Vec4 &constCoords); private: const uint32_t m_flags; glu::Texture2D *m_brickTexture; }; ShaderDiscardCase::ShaderDiscardCase(Context &context, const char *name, const char *description, const char *shaderSource, ShaderEvalFunc evalFunc, uint32_t flags) : ShaderRenderCase(context.getTestContext(), context.getRenderContext(), context.getContextInfo(), name, description, false, evalFunc) , m_flags(flags) , m_brickTexture(DE_NULL) { m_fragShaderSource = shaderSource; m_vertShaderSource = "attribute highp vec4 a_position;\n" "attribute highp vec4 a_coords;\n" "varying mediump vec4 v_color;\n" "varying mediump vec4 v_coords;\n\n" "void main (void)\n" "{\n" " gl_Position = a_position;\n" " v_color = vec4(a_coords.xyz, 1.0);\n" " v_coords = a_coords;\n" "}\n"; } ShaderDiscardCase::~ShaderDiscardCase(void) { delete m_brickTexture; } void ShaderDiscardCase::init(void) { try { gls::ShaderRenderCase::init(); } catch (const CompileFailed &) { if (m_flags & FLAG_REQUIRES_DYNAMIC_LOOPS) { const bool isSupported = m_isVertexCase ? m_ctxInfo.isVertexDynamicLoopSupported() : m_ctxInfo.isFragmentDynamicLoopSupported(); if (!isSupported) throw tcu::NotSupportedError("Dynamic loops not supported"); } throw; } if (m_flags & FLAG_USES_TEXTURES) { m_brickTexture = glu::Texture2D::create(m_renderCtx, m_ctxInfo, m_testCtx.getArchive(), "data/brick.png"); m_textures.push_back(TextureBinding( m_brickTexture, tcu::Sampler(tcu::Sampler::CLAMP_TO_EDGE, tcu::Sampler::CLAMP_TO_EDGE, tcu::Sampler::CLAMP_TO_EDGE, tcu::Sampler::LINEAR, tcu::Sampler::LINEAR))); } } void ShaderDiscardCase::deinit(void) { gls::ShaderRenderCase::deinit(); delete m_brickTexture; m_brickTexture = DE_NULL; } void ShaderDiscardCase::setupUniforms(int programID, const tcu::Vec4 &) { const glw::Functions &gl = m_renderCtx.getFunctions(); gl.uniform1i(gl.getUniformLocation(programID, "ut_brick"), 0); } ShaderDiscardTests::ShaderDiscardTests(Context &context) : TestCaseGroup(context, "discard", "Discard statement tests") { } ShaderDiscardTests::~ShaderDiscardTests(void) { } enum DiscardMode { DISCARDMODE_ALWAYS = 0, DISCARDMODE_NEVER, DISCARDMODE_UNIFORM, DISCARDMODE_DYNAMIC, DISCARDMODE_TEXTURE, DISCARDMODE_LAST }; enum DiscardTemplate { DISCARDTEMPLATE_MAIN_BASIC = 0, DISCARDTEMPLATE_FUNCTION_BASIC, DISCARDTEMPLATE_MAIN_STATIC_LOOP, DISCARDTEMPLATE_MAIN_DYNAMIC_LOOP, DISCARDTEMPLATE_FUNCTION_STATIC_LOOP, DISCARDTEMPLATE_LAST }; // Evaluation functions inline void evalDiscardAlways(ShaderEvalContext &c) { c.discard(); } inline void evalDiscardNever(ShaderEvalContext &c) { c.color.xyz() = c.coords.swizzle(0, 1, 2); } inline void evalDiscardDynamic(ShaderEvalContext &c) { c.color.xyz() = c.coords.swizzle(0, 1, 2); if (c.coords.x() + c.coords.y() > 0.0f) c.discard(); } inline void evalDiscardTexture(ShaderEvalContext &c) { c.color.xyz() = c.coords.swizzle(0, 1, 2); if (c.texture2D(0, c.coords.swizzle(0, 1) * 0.25f + 0.5f).x() < 0.7f) c.discard(); } static ShaderEvalFunc getEvalFunc(DiscardMode mode) { switch (mode) { case DISCARDMODE_ALWAYS: return evalDiscardAlways; case DISCARDMODE_NEVER: return evalDiscardNever; case DISCARDMODE_UNIFORM: return evalDiscardAlways; case DISCARDMODE_DYNAMIC: return evalDiscardDynamic; case DISCARDMODE_TEXTURE: return evalDiscardTexture; default: DE_ASSERT(false); return evalDiscardAlways; } } static const char *getTemplate(DiscardTemplate variant) { switch (variant) { case DISCARDTEMPLATE_MAIN_BASIC: return "varying mediump vec4 v_color;\n" "varying mediump vec4 v_coords;\n" "uniform sampler2D ut_brick;\n" "uniform mediump int ui_one;\n\n" "void main (void)\n" "{\n" " gl_FragColor = v_color;\n" " ${DISCARD};\n" "}\n"; case DISCARDTEMPLATE_FUNCTION_BASIC: return "varying mediump vec4 v_color;\n" "varying mediump vec4 v_coords;\n" "uniform sampler2D ut_brick;\n" "uniform mediump int ui_one;\n\n" "void myfunc (void)\n" "{\n" " ${DISCARD};\n" "}\n\n" "void main (void)\n" "{\n" " gl_FragColor = v_color;\n" " myfunc();\n" "}\n"; case DISCARDTEMPLATE_MAIN_STATIC_LOOP: return "varying mediump vec4 v_color;\n" "varying mediump vec4 v_coords;\n" "uniform sampler2D ut_brick;\n" "uniform mediump int ui_one;\n\n" "void main (void)\n" "{\n" " gl_FragColor = v_color;\n" " for (int i = 0; i < 2; i++)\n" " {\n" " if (i > 0)\n" " ${DISCARD};\n" " }\n" "}\n"; case DISCARDTEMPLATE_MAIN_DYNAMIC_LOOP: return "varying mediump vec4 v_color;\n" "varying mediump vec4 v_coords;\n" "uniform sampler2D ut_brick;\n" "uniform mediump int ui_one;\n" "uniform mediump int ui_two;\n\n" "void main (void)\n" "{\n" " gl_FragColor = v_color;\n" " for (int i = 0; i < ui_two; i++)\n" " {\n" " if (i > 0)\n" " ${DISCARD};\n" " }\n" "}\n"; case DISCARDTEMPLATE_FUNCTION_STATIC_LOOP: return "varying mediump vec4 v_color;\n" "varying mediump vec4 v_coords;\n" "uniform sampler2D ut_brick;\n" "uniform mediump int ui_one;\n\n" "void myfunc (void)\n" "{\n" " for (int i = 0; i < 2; i++)\n" " {\n" " if (i > 0)\n" " ${DISCARD};\n" " }\n" "}\n\n" "void main (void)\n" "{\n" " gl_FragColor = v_color;\n" " myfunc();\n" "}\n"; default: DE_ASSERT(false); return DE_NULL; } } static const char *getTemplateName(DiscardTemplate variant) { switch (variant) { case DISCARDTEMPLATE_MAIN_BASIC: return "basic"; case DISCARDTEMPLATE_FUNCTION_BASIC: return "function"; case DISCARDTEMPLATE_MAIN_STATIC_LOOP: return "static_loop"; case DISCARDTEMPLATE_MAIN_DYNAMIC_LOOP: return "dynamic_loop"; case DISCARDTEMPLATE_FUNCTION_STATIC_LOOP: return "function_static_loop"; default: DE_ASSERT(false); return DE_NULL; } } static const char *getModeName(DiscardMode mode) { switch (mode) { case DISCARDMODE_ALWAYS: return "always"; case DISCARDMODE_NEVER: return "never"; case DISCARDMODE_UNIFORM: return "uniform"; case DISCARDMODE_DYNAMIC: return "dynamic"; case DISCARDMODE_TEXTURE: return "texture"; default: DE_ASSERT(false); return DE_NULL; } } static const char *getTemplateDesc(DiscardTemplate variant) { switch (variant) { case DISCARDTEMPLATE_MAIN_BASIC: return "main"; case DISCARDTEMPLATE_FUNCTION_BASIC: return "function"; case DISCARDTEMPLATE_MAIN_STATIC_LOOP: return "static loop"; case DISCARDTEMPLATE_MAIN_DYNAMIC_LOOP: return "dynamic loop"; case DISCARDTEMPLATE_FUNCTION_STATIC_LOOP: return "static loop in function"; default: DE_ASSERT(false); return DE_NULL; } } static const char *getModeDesc(DiscardMode mode) { switch (mode) { case DISCARDMODE_ALWAYS: return "Always discard"; case DISCARDMODE_NEVER: return "Never discard"; case DISCARDMODE_UNIFORM: return "Discard based on uniform value"; case DISCARDMODE_DYNAMIC: return "Discard based on varying values"; case DISCARDMODE_TEXTURE: return "Discard based on texture value"; default: DE_ASSERT(false); return DE_NULL; } } ShaderDiscardCase *makeDiscardCase(Context &context, DiscardTemplate tmpl, DiscardMode mode) { StringTemplate shaderTemplate(getTemplate(tmpl)); map params; switch (mode) { case DISCARDMODE_ALWAYS: params["DISCARD"] = "discard"; break; case DISCARDMODE_NEVER: params["DISCARD"] = "if (false) discard"; break; case DISCARDMODE_UNIFORM: params["DISCARD"] = "if (ui_one > 0) discard"; break; case DISCARDMODE_DYNAMIC: params["DISCARD"] = "if (v_coords.x+v_coords.y > 0.0) discard"; break; case DISCARDMODE_TEXTURE: params["DISCARD"] = "if (texture2D(ut_brick, v_coords.xy*0.25+0.5).x < 0.7) discard"; break; default: DE_ASSERT(false); break; } string name = string(getTemplateName(tmpl)) + "_" + getModeName(mode); string description = string(getModeDesc(mode)) + " in " + getTemplateDesc(tmpl); uint32_t flags = (mode == DISCARDMODE_TEXTURE ? FLAG_USES_TEXTURES : 0) | (tmpl == DISCARDTEMPLATE_MAIN_DYNAMIC_LOOP ? FLAG_REQUIRES_DYNAMIC_LOOPS : 0); return new ShaderDiscardCase(context, name.c_str(), description.c_str(), shaderTemplate.specialize(params).c_str(), getEvalFunc(mode), flags); } void ShaderDiscardTests::init(void) { for (int tmpl = 0; tmpl < DISCARDTEMPLATE_LAST; tmpl++) for (int mode = 0; mode < DISCARDMODE_LAST; mode++) addChild(makeDiscardCase(m_context, (DiscardTemplate)tmpl, (DiscardMode)mode)); } } // namespace Functional } // namespace gles2 } // namespace deqp