Add cases for structs as inout and out parameters.
authorKenneth Russell <kbr@google.com>
Mon, 23 Jul 2018 22:26:47 +0000 (15:26 -0700)
committerAlexander Galazin <Alexander.Galazin@arm.com>
Thu, 23 Aug 2018 11:19:34 +0000 (07:19 -0400)
Structs with lowp, mediump and highp (when supported in fragment
shaders) members are tested as inout and out parameters in both ES 2.0
and 3.0 shaders.

The highp variant of this test catches a bug which was found by the
Three.js community in https://github.com/mrdoob/three.js/issues/14137 .
Similar tests were integrated into the WebGL conformance suite in
https://github.com/KhronosGroup/WebGL/pull/2663 .

Verified on:
  Qualcomm Adreno 308 (LG Aristo) - bug reproduces in both ES2 and ES3
                                    highp fragment shaders
  Qualcomm Adreno 540 (Pixel 2),
  NVIDIA Tegra (SHIELD Tablet) - all tests pass

New tests:

dEQP-GLES[23].functional.shaders.struct.local.parameter_inout_*
dEQP-GLES[23].functional.shaders.struct.local.parameter_out_*

VK-GL-CTS Issue 1280

Change-Id: Ie332aede0ad52453815d9e123145ec035009430b

android/cts/master/gles2-master.txt
android/cts/master/gles3-master.txt
external/openglcts/data/mustpass/gles/aosp_mustpass/3.2.5.x/gles2-master.txt
external/openglcts/data/mustpass/gles/aosp_mustpass/3.2.5.x/gles3-master.txt
external/openglcts/data/mustpass/gles/aosp_mustpass/master/gles2-master.txt
external/openglcts/data/mustpass/gles/aosp_mustpass/master/gles3-master.txt
modules/gles2/functional/es2fShaderStructTests.cpp
modules/gles3/functional/es3fShaderStructTests.cpp

index ecf56bb..8217ac7 100644 (file)
@@ -6813,8 +6813,20 @@ dEQP-GLES2.functional.shaders.struct.local.nested_struct_array_dynamic_index_ver
 dEQP-GLES2.functional.shaders.struct.local.nested_struct_array_dynamic_index_fragment
 dEQP-GLES2.functional.shaders.struct.local.parameter_vertex
 dEQP-GLES2.functional.shaders.struct.local.parameter_fragment
+dEQP-GLES2.functional.shaders.struct.local.parameter_inout_lowp_vertex
+dEQP-GLES2.functional.shaders.struct.local.parameter_inout_lowp_fragment
+dEQP-GLES2.functional.shaders.struct.local.parameter_inout_mediump_vertex
+dEQP-GLES2.functional.shaders.struct.local.parameter_inout_mediump_fragment
+dEQP-GLES2.functional.shaders.struct.local.parameter_inout_highp_vertex
+dEQP-GLES2.functional.shaders.struct.local.parameter_inout_highp_fragment
 dEQP-GLES2.functional.shaders.struct.local.parameter_nested_vertex
 dEQP-GLES2.functional.shaders.struct.local.parameter_nested_fragment
+dEQP-GLES2.functional.shaders.struct.local.parameter_out_lowp_vertex
+dEQP-GLES2.functional.shaders.struct.local.parameter_out_lowp_fragment
+dEQP-GLES2.functional.shaders.struct.local.parameter_out_mediump_vertex
+dEQP-GLES2.functional.shaders.struct.local.parameter_out_mediump_fragment
+dEQP-GLES2.functional.shaders.struct.local.parameter_out_highp_vertex
+dEQP-GLES2.functional.shaders.struct.local.parameter_out_highp_fragment
 dEQP-GLES2.functional.shaders.struct.local.return_vertex
 dEQP-GLES2.functional.shaders.struct.local.return_fragment
 dEQP-GLES2.functional.shaders.struct.local.return_nested_vertex
index 3c0b885..e345d89 100644 (file)
@@ -16255,8 +16255,20 @@ dEQP-GLES3.functional.shaders.struct.local.nested_struct_array_dynamic_index_ver
 dEQP-GLES3.functional.shaders.struct.local.nested_struct_array_dynamic_index_fragment
 dEQP-GLES3.functional.shaders.struct.local.parameter_vertex
 dEQP-GLES3.functional.shaders.struct.local.parameter_fragment
+dEQP-GLES3.functional.shaders.struct.local.parameter_inout_lowp_vertex
+dEQP-GLES3.functional.shaders.struct.local.parameter_inout_lowp_fragment
+dEQP-GLES3.functional.shaders.struct.local.parameter_inout_mediump_vertex
+dEQP-GLES3.functional.shaders.struct.local.parameter_inout_mediump_fragment
+dEQP-GLES3.functional.shaders.struct.local.parameter_inout_highp_vertex
+dEQP-GLES3.functional.shaders.struct.local.parameter_inout_highp_fragment
 dEQP-GLES3.functional.shaders.struct.local.parameter_nested_vertex
 dEQP-GLES3.functional.shaders.struct.local.parameter_nested_fragment
+dEQP-GLES3.functional.shaders.struct.local.parameter_out_lowp_vertex
+dEQP-GLES3.functional.shaders.struct.local.parameter_out_lowp_fragment
+dEQP-GLES3.functional.shaders.struct.local.parameter_out_mediump_vertex
+dEQP-GLES3.functional.shaders.struct.local.parameter_out_mediump_fragment
+dEQP-GLES3.functional.shaders.struct.local.parameter_out_highp_vertex
+dEQP-GLES3.functional.shaders.struct.local.parameter_out_highp_fragment
 dEQP-GLES3.functional.shaders.struct.local.return_vertex
 dEQP-GLES3.functional.shaders.struct.local.return_fragment
 dEQP-GLES3.functional.shaders.struct.local.return_nested_vertex
index 1fb12a5..18357ce 100644 (file)
@@ -7657,8 +7657,20 @@ dEQP-GLES2.functional.shaders.struct.local.nested_struct_array_dynamic_index_ver
 dEQP-GLES2.functional.shaders.struct.local.nested_struct_array_dynamic_index_fragment
 dEQP-GLES2.functional.shaders.struct.local.parameter_vertex
 dEQP-GLES2.functional.shaders.struct.local.parameter_fragment
+dEQP-GLES2.functional.shaders.struct.local.parameter_inout_lowp_vertex
+dEQP-GLES2.functional.shaders.struct.local.parameter_inout_lowp_fragment
+dEQP-GLES2.functional.shaders.struct.local.parameter_inout_mediump_vertex
+dEQP-GLES2.functional.shaders.struct.local.parameter_inout_mediump_fragment
+dEQP-GLES2.functional.shaders.struct.local.parameter_inout_highp_vertex
+dEQP-GLES2.functional.shaders.struct.local.parameter_inout_highp_fragment
 dEQP-GLES2.functional.shaders.struct.local.parameter_nested_vertex
 dEQP-GLES2.functional.shaders.struct.local.parameter_nested_fragment
+dEQP-GLES2.functional.shaders.struct.local.parameter_out_lowp_vertex
+dEQP-GLES2.functional.shaders.struct.local.parameter_out_lowp_fragment
+dEQP-GLES2.functional.shaders.struct.local.parameter_out_mediump_vertex
+dEQP-GLES2.functional.shaders.struct.local.parameter_out_mediump_fragment
+dEQP-GLES2.functional.shaders.struct.local.parameter_out_highp_vertex
+dEQP-GLES2.functional.shaders.struct.local.parameter_out_highp_fragment
 dEQP-GLES2.functional.shaders.struct.local.return_vertex
 dEQP-GLES2.functional.shaders.struct.local.return_fragment
 dEQP-GLES2.functional.shaders.struct.local.return_nested_vertex
index d4b8d2a..78a5977 100644 (file)
@@ -16469,8 +16469,20 @@ dEQP-GLES3.functional.shaders.struct.local.nested_struct_array_dynamic_index_ver
 dEQP-GLES3.functional.shaders.struct.local.nested_struct_array_dynamic_index_fragment
 dEQP-GLES3.functional.shaders.struct.local.parameter_vertex
 dEQP-GLES3.functional.shaders.struct.local.parameter_fragment
+dEQP-GLES3.functional.shaders.struct.local.parameter_inout_lowp_vertex
+dEQP-GLES3.functional.shaders.struct.local.parameter_inout_lowp_fragment
+dEQP-GLES3.functional.shaders.struct.local.parameter_inout_mediump_vertex
+dEQP-GLES3.functional.shaders.struct.local.parameter_inout_mediump_fragment
+dEQP-GLES3.functional.shaders.struct.local.parameter_inout_highp_vertex
+dEQP-GLES3.functional.shaders.struct.local.parameter_inout_highp_fragment
 dEQP-GLES3.functional.shaders.struct.local.parameter_nested_vertex
 dEQP-GLES3.functional.shaders.struct.local.parameter_nested_fragment
+dEQP-GLES3.functional.shaders.struct.local.parameter_out_lowp_vertex
+dEQP-GLES3.functional.shaders.struct.local.parameter_out_lowp_fragment
+dEQP-GLES3.functional.shaders.struct.local.parameter_out_mediump_vertex
+dEQP-GLES3.functional.shaders.struct.local.parameter_out_mediump_fragment
+dEQP-GLES3.functional.shaders.struct.local.parameter_out_highp_vertex
+dEQP-GLES3.functional.shaders.struct.local.parameter_out_highp_fragment
 dEQP-GLES3.functional.shaders.struct.local.return_vertex
 dEQP-GLES3.functional.shaders.struct.local.return_fragment
 dEQP-GLES3.functional.shaders.struct.local.return_nested_vertex
index 35e5c28..ab7641d 100644 (file)
@@ -7799,8 +7799,20 @@ dEQP-GLES2.functional.shaders.struct.local.nested_struct_array_dynamic_index_ver
 dEQP-GLES2.functional.shaders.struct.local.nested_struct_array_dynamic_index_fragment
 dEQP-GLES2.functional.shaders.struct.local.parameter_vertex
 dEQP-GLES2.functional.shaders.struct.local.parameter_fragment
+dEQP-GLES2.functional.shaders.struct.local.parameter_inout_lowp_vertex
+dEQP-GLES2.functional.shaders.struct.local.parameter_inout_lowp_fragment
+dEQP-GLES2.functional.shaders.struct.local.parameter_inout_mediump_vertex
+dEQP-GLES2.functional.shaders.struct.local.parameter_inout_mediump_fragment
+dEQP-GLES2.functional.shaders.struct.local.parameter_inout_highp_vertex
+dEQP-GLES2.functional.shaders.struct.local.parameter_inout_highp_fragment
 dEQP-GLES2.functional.shaders.struct.local.parameter_nested_vertex
 dEQP-GLES2.functional.shaders.struct.local.parameter_nested_fragment
+dEQP-GLES2.functional.shaders.struct.local.parameter_out_lowp_vertex
+dEQP-GLES2.functional.shaders.struct.local.parameter_out_lowp_fragment
+dEQP-GLES2.functional.shaders.struct.local.parameter_out_mediump_vertex
+dEQP-GLES2.functional.shaders.struct.local.parameter_out_mediump_fragment
+dEQP-GLES2.functional.shaders.struct.local.parameter_out_highp_vertex
+dEQP-GLES2.functional.shaders.struct.local.parameter_out_highp_fragment
 dEQP-GLES2.functional.shaders.struct.local.return_vertex
 dEQP-GLES2.functional.shaders.struct.local.return_fragment
 dEQP-GLES2.functional.shaders.struct.local.return_nested_vertex
index 89fb870..abc800e 100644 (file)
@@ -16471,8 +16471,20 @@ dEQP-GLES3.functional.shaders.struct.local.nested_struct_array_dynamic_index_ver
 dEQP-GLES3.functional.shaders.struct.local.nested_struct_array_dynamic_index_fragment
 dEQP-GLES3.functional.shaders.struct.local.parameter_vertex
 dEQP-GLES3.functional.shaders.struct.local.parameter_fragment
+dEQP-GLES3.functional.shaders.struct.local.parameter_inout_lowp_vertex
+dEQP-GLES3.functional.shaders.struct.local.parameter_inout_lowp_fragment
+dEQP-GLES3.functional.shaders.struct.local.parameter_inout_mediump_vertex
+dEQP-GLES3.functional.shaders.struct.local.parameter_inout_mediump_fragment
+dEQP-GLES3.functional.shaders.struct.local.parameter_inout_highp_vertex
+dEQP-GLES3.functional.shaders.struct.local.parameter_inout_highp_fragment
 dEQP-GLES3.functional.shaders.struct.local.parameter_nested_vertex
 dEQP-GLES3.functional.shaders.struct.local.parameter_nested_fragment
+dEQP-GLES3.functional.shaders.struct.local.parameter_out_lowp_vertex
+dEQP-GLES3.functional.shaders.struct.local.parameter_out_lowp_fragment
+dEQP-GLES3.functional.shaders.struct.local.parameter_out_mediump_vertex
+dEQP-GLES3.functional.shaders.struct.local.parameter_out_mediump_fragment
+dEQP-GLES3.functional.shaders.struct.local.parameter_out_highp_vertex
+dEQP-GLES3.functional.shaders.struct.local.parameter_out_highp_fragment
 dEQP-GLES3.functional.shaders.struct.local.return_vertex
 dEQP-GLES3.functional.shaders.struct.local.return_fragment
 dEQP-GLES3.functional.shaders.struct.local.return_nested_vertex
index 21d84c5..b7354d8 100644 (file)
@@ -56,6 +56,7 @@ enum CaseFlags
        FLAG_USES_TEXTURES                              = (1<<0),
        FLAG_REQUIRES_DYNAMIC_LOOPS             = (1<<1),
        FLAG_REQUIRES_DYNAMIC_INDEXING  = (1<<2),
+       FLAG_REQUIRES_HIGHP_FRAGMENT    = (1<<3),
 };
 
 typedef void (*SetupUniformsFunc) (const glw::Functions& gl, deUint32 programID, const tcu::Vec4& constCoords);
@@ -122,6 +123,10 @@ void ShaderStructCase::init (void)
                if (m_flags & FLAG_REQUIRES_DYNAMIC_INDEXING)
                        throw tcu::NotSupportedError("Dynamic indexing not supported");
 
+               if (!m_isVertexCase && (m_flags & FLAG_REQUIRES_HIGHP_FRAGMENT) &&
+                       !m_ctxInfo.isFragmentHighPrecisionSupported())
+                       throw tcu::NotSupportedError("Highp in fragment shaders not supported");
+
                throw;
        }
 
@@ -148,7 +153,7 @@ void ShaderStructCase::setupUniforms (int programID, const tcu::Vec4& constCoord
                m_setupUniforms(m_renderCtx.getFunctions(), programID, constCoords);
 }
 
-static ShaderStructCase* createStructCase (Context& context, const char* name, const char* description, bool isVertexCase, deUint32 flags, ShaderEvalFunc evalFunc, SetupUniformsFunc setupUniforms, const LineStream& shaderSrc)
+static ShaderStructCase* createStructCase (Context& context, const char* name, const char* description, bool isVertexCase, deUint32 flags, ShaderEvalFunc evalFunc, SetupUniformsFunc setupUniforms, const LineStream& shaderSrc, const std::map<std::string, std::string>* additionalParams)
 {
        static const char* defaultVertSrc =
                "attribute highp vec4 a_position;\n"
@@ -185,6 +190,8 @@ static ShaderStructCase* createStructCase (Context& context, const char* name, c
                spParams["DST"]                         = "gl_FragColor";
                spParams["ASSIGN_POS"]          = "";
        }
+       if (additionalParams)
+               spParams.insert(additionalParams->begin(), additionalParams->end());
 
        if (isVertexCase)
                return new ShaderStructCase(context, name, description, isVertexCase, flags, evalFunc, setupUniforms, StringTemplate(shaderSrc.str()).specialize(spParams).c_str(), defaultFragSrc);
@@ -209,13 +216,16 @@ public:
 
 void LocalStructTests::init (void)
 {
-       #define LOCAL_STRUCT_CASE(NAME, DESCRIPTION, FLAGS, SHADER_SRC, EVAL_FUNC_BODY)                                                                                                                         \
+       #define LOCAL_STRUCT_CASE_PARAMETERIZED(NAME, DESCRIPTION, FLAGS, SHADER_SRC, EVAL_FUNC_BODY, PARAMS)                                                                           \
                do {                                                                                                                                                                                                                                                                    \
                        struct Eval_##NAME { static void eval (ShaderEvalContext& c) EVAL_FUNC_BODY };  /* NOLINT(EVAL_FUNC_BODY) */                                            \
-                       addChild(createStructCase(m_context, #NAME "_vertex", DESCRIPTION, true, FLAGS, &Eval_##NAME::eval, DE_NULL, SHADER_SRC));                      \
-                       addChild(createStructCase(m_context, #NAME "_fragment", DESCRIPTION, false, FLAGS,&Eval_##NAME::eval, DE_NULL, SHADER_SRC));            \
+                       addChild(createStructCase(m_context, #NAME "_vertex", DESCRIPTION, true, FLAGS, &Eval_##NAME::eval, DE_NULL, SHADER_SRC, PARAMS));      \
+                       addChild(createStructCase(m_context, #NAME "_fragment", DESCRIPTION, false, FLAGS,&Eval_##NAME::eval, DE_NULL, SHADER_SRC, PARAMS));\
                } while (deGetFalse())
 
+       #define LOCAL_STRUCT_CASE(NAME, DESCRIPTION, FLAGS, SHADER_SRC, EVAL_FUNC_BODY) \
+               LOCAL_STRUCT_CASE_PARAMETERIZED(NAME, DESCRIPTION, FLAGS, SHADER_SRC, EVAL_FUNC_BODY, DE_NULL)
+
        LOCAL_STRUCT_CASE(basic, "Basic struct usage", 0,
                LineStream()
                << "${DECLARATIONS}"
@@ -526,6 +536,61 @@ void LocalStructTests::init (void)
                        c.color.xyz() = c.coords.swizzle(0,1,2);
                });
 
+       LineStream inoutSrc;
+       inoutSrc
+                       << "${DECLARATIONS}"
+                       << ""
+                       << "struct S {"
+                       << "    ${PRECISION} vec3 red;"
+                       << "    ${PRECISION} vec3 blue;"
+                       << "};"
+                       << ""
+                       << "void modify (inout S s)"
+                       << "{"
+                       << "    s.red += vec3(0.5, 0.0, 0.0);"
+                       << "    s.blue += vec3(0.0, 0.0, 0.5);"
+                       << "}"
+                       << ""
+                       << "void main (void)"
+                       << "{"
+                       << "    S s;"
+                       << "    s.red = vec3(0.5, 0.0, 0.0);"
+                       << "    s.blue = vec3(0.0, 0.0, 0.5);"
+                       << "    modify(s);"
+                       << "    ${DST} = vec4(0.0, 0.0, 0.0, 1.0);"
+                       << "    if (s.red == vec3(1.0, 0.0, 0.0) && s.blue == vec3(0.0, 0.0, 1.0))"
+                       << "            ${DST} = vec4(1.0, 1.0, 1.0, 1.0);"
+                       << "    ${ASSIGN_POS}"
+                       << "}";
+
+       std::map<std::string, std::string> precisionParams;
+       precisionParams["PRECISION"] = "lowp";
+       LOCAL_STRUCT_CASE_PARAMETERIZED(
+               parameter_inout_lowp, "Struct with lowp members as an inout function parameter", 0,
+               inoutSrc,
+               {
+                       c.color.xyz() = tcu::Vec3(1.0, 1.0, 1.0);
+               },
+               &precisionParams);
+
+       precisionParams["PRECISION"] = "mediump";
+       LOCAL_STRUCT_CASE_PARAMETERIZED(
+               parameter_inout_mediump, "Struct with mediump members as an inout function parameter", 0,
+               inoutSrc,
+               {
+                       c.color.xyz() = tcu::Vec3(1.0, 1.0, 1.0);
+               },
+               &precisionParams);
+
+       precisionParams["PRECISION"] = "highp";
+       LOCAL_STRUCT_CASE_PARAMETERIZED(
+               parameter_inout_highp, "Struct with highp members as an inout function parameter", FLAG_REQUIRES_HIGHP_FRAGMENT,
+               inoutSrc,
+               {
+                       c.color.xyz() = tcu::Vec3(1.0, 1.0, 1.0);
+               },
+               &precisionParams);
+
        LOCAL_STRUCT_CASE(parameter_nested, "Nested struct as a function parameter", 0,
                LineStream()
                << "${DECLARATIONS}"
@@ -558,6 +623,58 @@ void LocalStructTests::init (void)
                        c.color.xyz() = c.coords.swizzle(0,1,2);
                });
 
+       LineStream outSrc;
+       outSrc
+                       << "${DECLARATIONS}"
+                       << ""
+                       << "struct S {"
+                       << "    ${PRECISION} vec3 red;"
+                       << "    ${PRECISION} vec3 blue;"
+                       << "};"
+                       << ""
+                       << "void modify (out S s)"
+                       << "{"
+                       << "    s.red = vec3(1.0, 0.0, 0.0);"
+                       << "    s.blue = vec3(0.0, 0.0, 1.0);"
+                       << "}"
+                       << ""
+                       << "void main (void)"
+                       << "{"
+                       << "    S s;"
+                       << "    modify(s);"
+                       << "    ${DST} = vec4(0.0, 0.0, 0.0, 1.0);"
+                       << "    if (s.red == vec3(1.0, 0.0, 0.0) && s.blue == vec3(0.0, 0.0, 1.0))"
+                       << "            ${DST} = vec4(1.0, 1.0, 1.0, 1.0);"
+                       << "    ${ASSIGN_POS}"
+                       << "}",
+
+
+       precisionParams["PRECISION"] = "lowp";
+       LOCAL_STRUCT_CASE_PARAMETERIZED(
+               parameter_out_lowp, "Struct with lowp members as an out function parameter", 0,
+               outSrc,
+               {
+                       c.color.xyz() = tcu::Vec3(1.0, 1.0, 1.0);
+               },
+               &precisionParams);
+
+       precisionParams["PRECISION"] = "mediump";
+       LOCAL_STRUCT_CASE_PARAMETERIZED(parameter_out_mediump, "Struct with mediump members as an out function parameter", 0,
+               outSrc,
+               {
+                       c.color.xyz() = tcu::Vec3(1.0, 1.0, 1.0);
+               },
+               &precisionParams);
+
+       precisionParams["PRECISION"] = "highp";
+       LOCAL_STRUCT_CASE_PARAMETERIZED(
+               parameter_out_highp, "Struct with highp members as an out function parameter", FLAG_REQUIRES_HIGHP_FRAGMENT,
+               outSrc,
+               {
+                       c.color.xyz() = tcu::Vec3(1.0, 1.0, 1.0);
+               },
+               &precisionParams);
+
        LOCAL_STRUCT_CASE(return, "Struct as a return value", 0,
                LineStream()
                << "${DECLARATIONS}"
@@ -1221,8 +1338,8 @@ void UniformStructTests::init (void)
                                 static void setUniforms (const glw::Functions& gl, deUint32 programID, const tcu::Vec4& constCoords) SET_UNIFORMS_BODY /* NOLINT(SET_UNIFORMS_BODY) */ \
                        };                                                                                                                                                                                                                                                                                                                      \
                        struct Eval_##NAME { static void eval (ShaderEvalContext& c) EVAL_FUNC_BODY };  /* NOLINT(EVAL_FUNC_BODY) */                                                                                            \
-                       addChild(createStructCase(m_context, #NAME "_vertex", DESCRIPTION, true, FLAGS, Eval_##NAME::eval, SetUniforms_##NAME::setUniforms, SHADER_SRC));                       \
-                       addChild(createStructCase(m_context, #NAME "_fragment", DESCRIPTION, false, FLAGS, Eval_##NAME::eval, SetUniforms_##NAME::setUniforms, SHADER_SRC));            \
+                       addChild(createStructCase(m_context, #NAME "_vertex", DESCRIPTION, true, FLAGS, Eval_##NAME::eval, SetUniforms_##NAME::setUniforms, SHADER_SRC, DE_NULL));      \
+                       addChild(createStructCase(m_context, #NAME "_fragment", DESCRIPTION, false, FLAGS, Eval_##NAME::eval, SetUniforms_##NAME::setUniforms, SHADER_SRC, DE_NULL));\
                } while (deGetFalse())
 
        UNIFORM_STRUCT_CASE(basic, "Basic struct usage", 0,
index 66b9559..526db73 100644 (file)
@@ -115,7 +115,7 @@ void ShaderStructCase::setupUniforms (int programID, const tcu::Vec4& constCoord
                m_setupUniforms(m_renderCtx.getFunctions(), programID, constCoords);
 }
 
-static ShaderStructCase* createStructCase (Context& context, const char* name, const char* description, bool isVertexCase, bool usesTextures, ShaderEvalFunc evalFunc, SetupUniformsFunc setupUniforms, const LineStream& shaderSrc)
+static ShaderStructCase* createStructCase (Context& context, const char* name, const char* description, bool isVertexCase, bool usesTextures, ShaderEvalFunc evalFunc, SetupUniformsFunc setupUniforms, const LineStream& shaderSrc, const std::map<std::string, std::string>* additionalParams)
 {
        static const char* defaultVertSrc =
                "#version 300 es\n"
@@ -159,6 +159,8 @@ static ShaderStructCase* createStructCase (Context& context, const char* name, c
                spParams["DST"]                         = "o_color";
                spParams["ASSIGN_POS"]          = "";
        }
+       if (additionalParams)
+               spParams.insert(additionalParams->begin(), additionalParams->end());
 
        if (isVertexCase)
                return new ShaderStructCase(context, name, description, isVertexCase, usesTextures, evalFunc, setupUniforms, StringTemplate(shaderSrc.str()).specialize(spParams).c_str(), defaultFragSrc);
@@ -183,13 +185,16 @@ public:
 
 void LocalStructTests::init (void)
 {
-       #define LOCAL_STRUCT_CASE(NAME, DESCRIPTION, SHADER_SRC, EVAL_FUNC_BODY)                                                                                                                                        \
+       #define LOCAL_STRUCT_CASE_PARAMETERIZED(NAME, DESCRIPTION, SHADER_SRC, EVAL_FUNC_BODY, PARAMS)                                                                                          \
                do {                                                                                                                                                                                                                                                                    \
                        struct Eval_##NAME { static void eval (ShaderEvalContext& c) EVAL_FUNC_BODY };  /* NOLINT(EVAL_FUNC_BODY) */                                            \
-                       addChild(createStructCase(m_context, #NAME "_vertex", DESCRIPTION, true, false, &Eval_##NAME::eval, DE_NULL, SHADER_SRC));                      \
-                       addChild(createStructCase(m_context, #NAME "_fragment", DESCRIPTION, false, false,&Eval_##NAME::eval, DE_NULL, SHADER_SRC));            \
+                       addChild(createStructCase(m_context, #NAME "_vertex", DESCRIPTION, true, false, &Eval_##NAME::eval, DE_NULL, SHADER_SRC, PARAMS));      \
+                       addChild(createStructCase(m_context, #NAME "_fragment", DESCRIPTION, false, false,&Eval_##NAME::eval, DE_NULL, SHADER_SRC, PARAMS));\
                } while (deGetFalse())
 
+       #define LOCAL_STRUCT_CASE(NAME, DESCRIPTION, SHADER_SRC, EVAL_FUNC_BODY) \
+               LOCAL_STRUCT_CASE_PARAMETERIZED(NAME, DESCRIPTION, SHADER_SRC, EVAL_FUNC_BODY, DE_NULL)
+
        LOCAL_STRUCT_CASE(basic, "Basic struct usage",
                LineStream()
                << "${HEADER}"
@@ -500,6 +505,63 @@ void LocalStructTests::init (void)
                        c.color.xyz() = c.coords.swizzle(0,1,2);
                });
 
+       LineStream inoutSrc;
+       inoutSrc
+                       << "${HEADER}"
+                       << ""
+                       << "struct S {"
+                       << "    ${PRECISION} vec3 red;"
+                       << "    ${PRECISION} vec3 blue;"
+                       << "};"
+                       << ""
+                       << "void modify (inout S s)"
+                       << "{"
+                       << "    s.red += vec3(0.5, 0.0, 0.0);"
+                       << "    s.blue += vec3(0.0, 0.0, 0.5);"
+                       << "}"
+                       << ""
+                       << "void main (void)"
+                       << "{"
+                       << "    S s;"
+                       << "    s.red = vec3(0.5, 0.0, 0.0);"
+                       << "    s.blue = vec3(0.0, 0.0, 0.5);"
+                       << "    modify(s);"
+                       << "    ${DST} = vec4(0.0, 0.0, 0.0, 1.0);"
+                       << "    if (s.red == vec3(1.0, 0.0, 0.0) && s.blue == vec3(0.0, 0.0, 1.0))"
+                       << "            ${DST} = vec4(1.0, 1.0, 1.0, 1.0);"
+                       << "    ${ASSIGN_POS}"
+                       << "}";
+
+
+       std::map<std::string, std::string> precisionParams;
+
+       precisionParams["PRECISION"] = "lowp";
+       LOCAL_STRUCT_CASE_PARAMETERIZED(
+               parameter_inout_lowp, "Struct with lowp members as an inout function parameter",
+               inoutSrc,
+               {
+                       c.color.xyz() = tcu::Vec3(1.0, 1.0, 1.0);
+               },
+               &precisionParams);
+
+       precisionParams["PRECISION"] = "mediump";
+       LOCAL_STRUCT_CASE_PARAMETERIZED(
+               parameter_inout_mediump, "Struct with mediump members as an inout function parameter",
+               inoutSrc,
+               {
+                       c.color.xyz() = tcu::Vec3(1.0, 1.0, 1.0);
+               },
+               &precisionParams);
+
+       precisionParams["PRECISION"] = "highp";
+       LOCAL_STRUCT_CASE_PARAMETERIZED(
+               parameter_inout_highp, "Struct with highp members as an inout function parameter",
+               inoutSrc,
+               {
+                       c.color.xyz() = tcu::Vec3(1.0, 1.0, 1.0);
+               },
+               &precisionParams);
+
        LOCAL_STRUCT_CASE(parameter_nested, "Nested struct as a function parameter",
                LineStream()
                << "${HEADER}"
@@ -532,6 +594,58 @@ void LocalStructTests::init (void)
                        c.color.xyz() = c.coords.swizzle(0,1,2);
                });
 
+       LineStream outSrc;
+       outSrc
+                       << "${HEADER}"
+                       << ""
+                       << "struct S {"
+                       << "    ${PRECISION} vec3 red;"
+                       << "    ${PRECISION} vec3 blue;"
+                       << "};"
+                       << ""
+                       << "void modify (out S s)"
+                       << "{"
+                       << "    s.red = vec3(1.0, 0.0, 0.0);"
+                       << "    s.blue = vec3(0.0, 0.0, 1.0);"
+                       << "}"
+                       << ""
+                       << "void main (void)"
+                       << "{"
+                       << "    S s;"
+                       << "    modify(s);"
+                       << "    ${DST} = vec4(0.0, 0.0, 0.0, 1.0);"
+                       << "    if (s.red == vec3(1.0, 0.0, 0.0) && s.blue == vec3(0.0, 0.0, 1.0))"
+                       << "            ${DST} = vec4(1.0, 1.0, 1.0, 1.0);"
+                       << "    ${ASSIGN_POS}"
+                       << "}";
+
+       precisionParams["PRECISION"] = "lowp";
+       LOCAL_STRUCT_CASE_PARAMETERIZED(
+               parameter_out_lowp, "Struct with lowp members as an out function parameter",
+               outSrc,
+               {
+                       c.color.xyz() = tcu::Vec3(1.0, 1.0, 1.0);
+               },
+               &precisionParams);
+
+       precisionParams["PRECISION"] = "mediump";
+       LOCAL_STRUCT_CASE_PARAMETERIZED(
+               parameter_out_mediump, "Struct with mediump members as an out function parameter",
+               outSrc,
+               {
+                       c.color.xyz() = tcu::Vec3(1.0, 1.0, 1.0);
+               },
+               &precisionParams);
+
+       precisionParams["PRECISION"] = "highp";
+       LOCAL_STRUCT_CASE_PARAMETERIZED(
+               parameter_out_highp, "Struct with highp members as an out function parameter",
+               outSrc,
+               {
+                       c.color.xyz() = tcu::Vec3(1.0, 1.0, 1.0);
+               },
+               &precisionParams);
+
        LOCAL_STRUCT_CASE(return, "Struct as a return value",
                LineStream()
                << "${HEADER}"
@@ -1228,8 +1342,8 @@ void UniformStructTests::init (void)
                                 static void setUniforms (const glw::Functions& gl, deUint32 programID, const tcu::Vec4& constCoords) SET_UNIFORMS_BODY /* NOLINT(SET_UNIFORMS_BODY) */ \
                        };                                                                                                                                                                                                                                                                                                                      \
                        struct Eval_##NAME { static void eval (ShaderEvalContext& c) EVAL_FUNC_BODY };  /* NOLINT(EVAL_FUNC_BODY) */                                                                                            \
-                       addChild(createStructCase(m_context, #NAME "_vertex", DESCRIPTION, true, TEXTURES, Eval_##NAME::eval, SetUniforms_##NAME::setUniforms, SHADER_SRC));            \
-                       addChild(createStructCase(m_context, #NAME "_fragment", DESCRIPTION, false, TEXTURES, Eval_##NAME::eval, SetUniforms_##NAME::setUniforms, SHADER_SRC));         \
+                       addChild(createStructCase(m_context, #NAME "_vertex", DESCRIPTION, true, TEXTURES, Eval_##NAME::eval, SetUniforms_##NAME::setUniforms, SHADER_SRC, DE_NULL));\
+                       addChild(createStructCase(m_context, #NAME "_fragment", DESCRIPTION, false, TEXTURES, Eval_##NAME::eval, SetUniforms_##NAME::setUniforms, SHADER_SRC, DE_NULL));\
                } while (deGetFalse())
 
        UNIFORM_STRUCT_CASE(basic, "Basic struct usage", false,