blob: 7b186b9f0a56688f46d42aac50c6315260b8ece3 [file] [log] [blame]
/*------------------------------------------------------------------------
* Vulkan Conformance Tests
* ------------------------
*
* Copyright (c) 2015 The Khronos Group Inc.
* Copyright (c) 2015 Samsung Electronics Co., Ltd.
* Copyright (c) 2016 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 return statement tests.
*//*--------------------------------------------------------------------*/
#include "vktShaderRenderReturnTests.hpp"
#include "vktShaderRender.hpp"
#include "tcuStringTemplate.hpp"
#include <map>
#include <string>
namespace vkt
{
namespace sr
{
namespace
{
enum ReturnMode
{
RETURNMODE_ALWAYS = 0,
RETURNMODE_NEVER,
RETURNMODE_DYNAMIC,
RETURNMODE_LAST
};
// Evaluation functions
inline void evalReturnAlways (ShaderEvalContext& c) { c.color.xyz() = c.coords.swizzle(0,1,2); }
inline void evalReturnNever (ShaderEvalContext& c) { c.color.xyz() = c.coords.swizzle(3,2,1); }
inline void evalReturnDynamic (ShaderEvalContext& c) { c.color.xyz() = (c.coords.x()+c.coords.y() >= 0.0f) ? c.coords.swizzle(0,1,2) : c.coords.swizzle(3,2,1); }
static ShaderEvalFunc getEvalFunc (ReturnMode mode)
{
switch (mode)
{
case RETURNMODE_ALWAYS: return evalReturnAlways;
case RETURNMODE_NEVER: return evalReturnNever;
case RETURNMODE_DYNAMIC: return evalReturnDynamic;
default:
DE_ASSERT(DE_FALSE);
return (ShaderEvalFunc)DE_NULL;
}
}
class ShaderReturnCase : public ShaderRenderCase
{
public:
ShaderReturnCase (tcu::TestContext& testCtx,
const std::string& name,
const std::string& description,
bool isVertexCase,
const std::string& shaderSource,
const ShaderEvalFunc evalFunc,
const UniformSetup* uniformFunc);
virtual ~ShaderReturnCase (void);
};
ShaderReturnCase::ShaderReturnCase (tcu::TestContext& testCtx,
const std::string& name,
const std::string& description,
bool isVertexCase,
const std::string& shaderSource,
const ShaderEvalFunc evalFunc,
const UniformSetup* uniformFunc)
: ShaderRenderCase(testCtx, name, description, isVertexCase, evalFunc, uniformFunc, DE_NULL)
{
if (isVertexCase)
{
m_vertShaderSource = shaderSource;
m_fragShaderSource =
"#version 310 es\n"
"layout(location = 0) in mediump vec4 v_color;\n"
"layout(location = 0) out mediump vec4 o_color;\n\n"
"void main (void)\n"
"{\n"
" o_color = v_color;\n"
"}\n";
}
else
{
m_fragShaderSource = shaderSource;
m_vertShaderSource =
"#version 310 es\n"
"layout(location = 0) in highp vec4 a_position;\n"
"layout(location = 1) in highp vec4 a_coords;\n"
"layout(location = 0) out mediump vec4 v_coords;\n\n"
"void main (void)\n"
"{\n"
" gl_Position = a_position;\n"
" v_coords = a_coords;\n"
"}\n";
}
}
ShaderReturnCase::~ShaderReturnCase (void)
{
}
class ReturnTestUniformSetup : public UniformSetup
{
public:
ReturnTestUniformSetup (const BaseUniformType uniformType)
: m_uniformType(uniformType)
{}
virtual void setup (ShaderRenderCaseInstance& instance, const tcu::Vec4&) const
{
instance.useUniform(0u, m_uniformType);
}
private:
const BaseUniformType m_uniformType;
};
// Test case builders.
de::MovePtr<ShaderReturnCase> makeConditionalReturnInFuncCase (tcu::TestContext& context, const std::string& name, const std::string& description, ReturnMode returnMode, bool isVertex)
{
tcu::StringTemplate tmpl(
"#version 310 es\n"
"layout(location = ${COORDLOC}) in ${COORDPREC} vec4 ${COORDS};\n"
"${EXTRADECL}\n"
"${COORDPREC} vec4 getColor (void)\n"
"{\n"
" if (${RETURNCOND})\n"
" return vec4(${COORDS}.xyz, 1.0);\n"
" return vec4(${COORDS}.wzy, 1.0);\n"
"}\n\n"
"void main (void)\n"
"{\n"
"${POSITIONWRITE}"
" ${OUTPUT} = getColor();\n"
"}\n");
const char* coords = isVertex ? "a_coords" : "v_coords";
std::map<std::string, std::string> params;
params["COORDLOC"] = isVertex ? "1" : "0";
params["COORDPREC"] = isVertex ? "highp" : "mediump";
params["OUTPUT"] = isVertex ? "v_color" : "o_color";
params["COORDS"] = coords;
params["EXTRADECL"] = isVertex ? "layout(location = 0) in highp vec4 a_position;\nlayout(location = 0) out mediump vec4 v_color;\n" : "layout(location = 0) out mediump vec4 o_color;\n";
params["POSITIONWRITE"] = isVertex ? " gl_Position = a_position;\n" : "";
switch (returnMode)
{
case RETURNMODE_ALWAYS: params["RETURNCOND"] = "true"; break;
case RETURNMODE_NEVER: params["RETURNCOND"] = "false"; break;
case RETURNMODE_DYNAMIC: params["RETURNCOND"] = std::string(coords) + ".x+" + coords + ".y >= 0.0"; break;
default: DE_ASSERT(DE_FALSE);
}
return de::MovePtr<ShaderReturnCase>(new ShaderReturnCase(context, name, description, isVertex, tmpl.specialize(params), getEvalFunc(returnMode), DE_NULL));
}
de::MovePtr<ShaderReturnCase> makeOutputWriteReturnCase (tcu::TestContext& context, const std::string& name, const std::string& description, bool inFunction, ReturnMode returnMode, bool isVertex)
{
tcu::StringTemplate tmpl(
inFunction
?
"#version 310 es\n"
"layout(location = ${COORDLOC}) in ${COORDPREC} vec4 ${COORDS};\n"
"${EXTRADECL}\n"
"void myfunc (void)\n"
"{\n"
" ${OUTPUT} = vec4(${COORDS}.xyz, 1.0);\n"
" if (${RETURNCOND})\n"
" return;\n"
" ${OUTPUT} = vec4(${COORDS}.wzy, 1.0);\n"
"}\n\n"
"void main (void)\n"
"{\n"
"${POSITIONWRITE}"
" myfunc();\n"
"}\n"
:
"#version 310 es\n"
"layout(location = ${COORDLOC}) in ${COORDPREC} vec4 ${COORDS};\n"
"${EXTRADECL}\n"
"void main ()\n"
"{\n"
"${POSITIONWRITE}"
" ${OUTPUT} = vec4(${COORDS}.xyz, 1.0);\n"
" if (${RETURNCOND})\n"
" return;\n"
" ${OUTPUT} = vec4(${COORDS}.wzy, 1.0);\n"
"}\n");
const char* coords = isVertex ? "a_coords" : "v_coords";
std::map<std::string, std::string> params;
params["COORDLOC"] = isVertex ? "1" : "0";
params["COORDPREC"] = isVertex ? "highp" : "mediump";
params["COORDS"] = coords;
params["OUTPUT"] = isVertex ? "v_color" : "o_color";
params["EXTRADECL"] = isVertex ? "layout(location = 0) in highp vec4 a_position;\nlayout(location = 0) out mediump vec4 v_color;\n" : "layout(location = 0) out mediump vec4 o_color;\n";
params["POSITIONWRITE"] = isVertex ? " gl_Position = a_position;\n" : "";
switch (returnMode)
{
case RETURNMODE_ALWAYS: params["RETURNCOND"] = "true"; break;
case RETURNMODE_NEVER: params["RETURNCOND"] = "false"; break;
case RETURNMODE_DYNAMIC: params["RETURNCOND"] = std::string(coords) + ".x+" + coords + ".y >= 0.0"; break;
default: DE_ASSERT(DE_FALSE);
}
return de::MovePtr<ShaderReturnCase>(new ShaderReturnCase(context, name, description, isVertex, tmpl.specialize(params), getEvalFunc(returnMode), DE_NULL));
}
de::MovePtr<ShaderReturnCase> makeReturnInLoopCase (tcu::TestContext& context, const std::string& name, const std::string& description, bool isDynamicLoop, ReturnMode returnMode, bool isVertex)
{
tcu::StringTemplate tmpl(
"#version 310 es\n"
"layout(location = ${COORDLOC}) in ${COORDPREC} vec4 ${COORDS};\n"
"layout(binding = 0, std140) uniform something { mediump int ui_one; };\n"
"${EXTRADECL}\n"
"${COORDPREC} vec4 getCoords (void)\n"
"{\n"
" ${COORDPREC} vec4 coords = ${COORDS};\n"
" for (int i = 0; i < ${ITERLIMIT}; i++)\n"
" {\n"
" if (${RETURNCOND})\n"
" return coords;\n"
" coords = coords.wzyx;\n"
" }\n"
" return coords;\n"
"}\n\n"
"void main (void)\n"
"{\n"
"${POSITIONWRITE}"
" ${OUTPUT} = vec4(getCoords().xyz, 1.0);\n"
"}\n");
const char* coords = isVertex ? "a_coords" : "v_coords";
std::map<std::string, std::string> params;
params["COORDLOC"] = isVertex ? "1" : "0";
params["COORDPREC"] = isVertex ? "highp" : "mediump";
params["OUTPUT"] = isVertex ? "v_color" : "o_color";
params["COORDS"] = coords;
params["EXTRADECL"] = isVertex ? "layout(location = 0) in highp vec4 a_position;\nlayout(location = 0) out mediump vec4 v_color;\n" : "layout(location = 0) out mediump vec4 o_color;\n";
params["POSITIONWRITE"] = isVertex ? " gl_Position = a_position;\n" : "";
params["ITERLIMIT"] = isDynamicLoop ? "ui_one" : "1";
switch (returnMode)
{
case RETURNMODE_ALWAYS: params["RETURNCOND"] = "true"; break;
case RETURNMODE_NEVER: params["RETURNCOND"] = "false"; break;
case RETURNMODE_DYNAMIC: params["RETURNCOND"] = std::string(coords) + ".x+" + coords + ".y >= 0.0"; break;
default: DE_ASSERT(DE_FALSE);
}
return de::MovePtr<ShaderReturnCase>(new ShaderReturnCase(context, name, description, isVertex, tmpl.specialize(params), getEvalFunc(returnMode), new ReturnTestUniformSetup(UI_ONE)));
}
static const char* getReturnModeName (ReturnMode mode)
{
switch (mode)
{
case RETURNMODE_ALWAYS: return "always";
case RETURNMODE_NEVER: return "never";
case RETURNMODE_DYNAMIC: return "dynamic";
default:
DE_ASSERT(DE_FALSE);
return DE_NULL;
}
}
static const char* getReturnModeDesc (ReturnMode mode)
{
switch (mode)
{
case RETURNMODE_ALWAYS: return "Always return";
case RETURNMODE_NEVER: return "Never return";
case RETURNMODE_DYNAMIC: return "Return based on coords";
default:
DE_ASSERT(DE_FALSE);
return DE_NULL;
}
}
class ShaderReturnTests : public tcu::TestCaseGroup
{
public:
ShaderReturnTests (tcu::TestContext& context);
virtual ~ShaderReturnTests (void);
virtual void init (void);
private:
ShaderReturnTests (const ShaderReturnTests&); // not allowed!
ShaderReturnTests& operator= (const ShaderReturnTests&); // not allowed!
};
ShaderReturnTests::ShaderReturnTests (tcu::TestContext& context)
: TestCaseGroup(context, "return", "Return Statement Tests")
{
}
ShaderReturnTests::~ShaderReturnTests (void)
{
}
void ShaderReturnTests::init (void)
{
addChild(new ShaderReturnCase(m_testCtx, "single_return_vertex", "Single return statement in function", true,
"#version 310 es\n"
"layout(location = 0) in highp vec4 a_position;\n"
"layout(location = 1) in highp vec4 a_coords;\n"
"layout(location = 0) out mediump vec4 v_color;\n\n"
"vec4 getColor (void)\n"
"{\n"
" return vec4(a_coords.xyz, 1.0);\n"
"}\n\n"
"void main (void)\n"
"{\n"
" gl_Position = a_position;\n"
" v_color = getColor();\n"
"}\n", evalReturnAlways, DE_NULL));
addChild(new ShaderReturnCase(m_testCtx, "single_return_fragment", "Single return statement in function", false,
"#version 310 es\n"
"layout(location = 0) in mediump vec4 v_coords;\n"
"layout(location = 0) out mediump vec4 o_color;\n"
"mediump vec4 getColor (void)\n"
"{\n"
" return vec4(v_coords.xyz, 1.0);\n"
"}\n\n"
"void main (void)\n"
"{\n"
" o_color = getColor();\n"
"}\n", evalReturnAlways, DE_NULL));
// Conditional return statement in function.
for (int returnMode = 0; returnMode < RETURNMODE_LAST; returnMode++)
{
for (int isFragment = 0; isFragment < 2; isFragment++)
{
std::string name = std::string("conditional_return_") + getReturnModeName((ReturnMode)returnMode) + (isFragment ? "_fragment" : "_vertex");
std::string description = std::string(getReturnModeDesc((ReturnMode)returnMode)) + " in function";
de::MovePtr<ShaderReturnCase> testCase (makeConditionalReturnInFuncCase(m_testCtx, name, description, (ReturnMode)returnMode, isFragment == 0));
addChild(testCase.release());
}
}
// Unconditional double return in function.
addChild(new ShaderReturnCase(m_testCtx, "double_return_vertex", "Unconditional double return in function", true,
"#version 310 es\n"
"layout(location = 0) in highp vec4 a_position;\n"
"layout(location = 1) in highp vec4 a_coords;\n"
"layout(location = 0) out mediump vec4 v_color;\n\n"
"vec4 getColor (void)\n"
"{\n"
" return vec4(a_coords.xyz, 1.0);\n"
" return vec4(a_coords.wzy, 1.0);\n"
"}\n\n"
"void main (void)\n"
"{\n"
" gl_Position = a_position;\n"
" v_color = getColor();\n"
"}\n", evalReturnAlways, DE_NULL));
addChild(new ShaderReturnCase(m_testCtx, "double_return_fragment", "Unconditional double return in function", false,
"#version 310 es\n"
"layout(location = 0) in mediump vec4 v_coords;\n"
"layout(location = 0) out mediump vec4 o_color;\n\n"
"mediump vec4 getColor (void)\n"
"{\n"
" return vec4(v_coords.xyz, 1.0);\n"
" return vec4(v_coords.wzy, 1.0);\n"
"}\n\n"
"void main (void)\n"
"{\n"
" o_color = getColor();\n"
"}\n", evalReturnAlways, DE_NULL));
// Last statement in main.
addChild(new ShaderReturnCase(m_testCtx, "last_statement_in_main_vertex", "Return as a final statement in main()", true,
"#version 310 es\n"
"layout(location = 0) in highp vec4 a_position;\n"
"layout(location = 1) in highp vec4 a_coords;\n"
"layout(location = 0) out mediump vec4 v_color;\n\n"
"void main (void)\n"
"{\n"
" gl_Position = a_position;\n"
" v_color = vec4(a_coords.xyz, 1.0);\n"
" return;\n"
"}\n", evalReturnAlways, DE_NULL));
addChild(new ShaderReturnCase(m_testCtx, "last_statement_in_main_fragment", "Return as a final statement in main()", false,
"#version 310 es\n"
"layout(location = 0) in mediump vec4 v_coords;\n"
"layout(location = 0) out mediump vec4 o_color;\n\n"
"void main (void)\n"
"{\n"
" o_color = vec4(v_coords.xyz, 1.0);\n"
" return;\n"
"}\n", evalReturnAlways, DE_NULL));
// Return between output variable writes.
for (int inFunc = 0; inFunc < 2; inFunc++)
{
for (int returnMode = 0; returnMode < RETURNMODE_LAST; returnMode++)
{
for (int isFragment = 0; isFragment < 2; isFragment++)
{
std::string name = std::string("output_write_") + (inFunc ? "in_func_" : "") + getReturnModeName((ReturnMode)returnMode) + (isFragment ? "_fragment" : "_vertex");
std::string desc = std::string(getReturnModeDesc((ReturnMode)returnMode)) + (inFunc ? " in user-defined function" : " in main()") + " between output writes";
de::MovePtr<ShaderReturnCase> testCase = (makeOutputWriteReturnCase(m_testCtx, name, desc, inFunc != 0, (ReturnMode)returnMode, isFragment == 0));
addChild(testCase.release());
}
}
}
// Conditional return statement in loop.
for (int isDynamicLoop = 0; isDynamicLoop < 2; isDynamicLoop++)
{
for (int returnMode = 0; returnMode < RETURNMODE_LAST; returnMode++)
{
for (int isFragment = 0; isFragment < 2; isFragment++)
{
std::string name = std::string("return_in_") + (isDynamicLoop ? "dynamic" : "static") + "_loop_" + getReturnModeName((ReturnMode)returnMode) + (isFragment ? "_fragment" : "_vertex");
std::string description = std::string(getReturnModeDesc((ReturnMode)returnMode)) + " in loop";
de::MovePtr<ShaderReturnCase> testCase (makeReturnInLoopCase(m_testCtx, name, description, isDynamicLoop != 0, (ReturnMode)returnMode, isFragment == 0));
addChild(testCase.release());
}
}
}
// Unconditional return in infinite loop.
addChild(new ShaderReturnCase(m_testCtx, "return_in_infinite_loop_vertex", "Return in infinite loop", true,
"#version 310 es\n"
"layout(location = 0) in highp vec4 a_position;\n"
"layout(location = 1) in highp vec4 a_coords;\n"
"layout(location = 0) out mediump vec4 v_color;\n"
"layout(binding = 0, std140) uniform something { int ui_zero; };\n"
"highp vec4 getCoords (void)\n"
"{\n"
" for (int i = 1; i < 10; i += ui_zero)\n"
" return a_coords;\n"
" return a_coords.wzyx;\n"
"}\n\n"
"void main (void)\n"
"{\n"
" gl_Position = a_position;\n"
" v_color = vec4(getCoords().xyz, 1.0);\n"
" return;\n"
"}\n", evalReturnAlways, new ReturnTestUniformSetup(UI_ZERO)));
addChild(new ShaderReturnCase(m_testCtx, "return_in_infinite_loop_fragment", "Return in infinite loop", false,
"#version 310 es\n"
"layout(location = 0) in mediump vec4 v_coords;\n"
"layout(location = 0) out mediump vec4 o_color;\n"
"layout(binding = 0, std140) uniform something { int ui_zero; };\n\n"
"mediump vec4 getCoords (void)\n"
"{\n"
" for (int i = 1; i < 10; i += ui_zero)\n"
" return v_coords;\n"
" return v_coords.wzyx;\n"
"}\n\n"
"void main (void)\n"
"{\n"
" o_color = vec4(getCoords().xyz, 1.0);\n"
" return;\n"
"}\n", evalReturnAlways, new ReturnTestUniformSetup(UI_ZERO)));
}
} // anonymous
tcu::TestCaseGroup* createReturnTests (tcu::TestContext& testCtx)
{
return new ShaderReturnTests(testCtx);
}
} // sr
} // vkt