xref: /aosp_15_r20/external/deqp/modules/gles2/functional/es2fShaderReturnTests.cpp (revision 35238bce31c2a825756842865a792f8cf7f89930)
1 /*-------------------------------------------------------------------------
2  * drawElements Quality Program OpenGL ES 2.0 Module
3  * -------------------------------------------------
4  *
5  * Copyright 2014 The Android Open Source Project
6  *
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  *
11  *      http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  *
19  *//*!
20  * \file
21  * \brief Shader return statement tests.
22  *//*--------------------------------------------------------------------*/
23 
24 #include "es2fShaderReturnTests.hpp"
25 #include "glsShaderRenderCase.hpp"
26 #include "tcuStringTemplate.hpp"
27 
28 #include <map>
29 #include <sstream>
30 #include <string>
31 
32 using tcu::StringTemplate;
33 
34 using std::map;
35 using std::ostringstream;
36 using std::string;
37 
38 using namespace glu;
39 using namespace deqp::gls;
40 
41 namespace deqp
42 {
43 namespace gles2
44 {
45 namespace Functional
46 {
47 
48 enum ReturnMode
49 {
50     RETURNMODE_ALWAYS = 0,
51     RETURNMODE_NEVER,
52     RETURNMODE_DYNAMIC,
53 
54     RETURNMODE_LAST
55 };
56 
57 enum RequireFlags
58 {
59     REQUIRE_DYNAMIC_LOOPS = (1 << 0),
60 };
61 
62 // Evaluation functions
evalReturnAlways(ShaderEvalContext & c)63 inline void evalReturnAlways(ShaderEvalContext &c)
64 {
65     c.color.xyz() = c.coords.swizzle(0, 1, 2);
66 }
evalReturnNever(ShaderEvalContext & c)67 inline void evalReturnNever(ShaderEvalContext &c)
68 {
69     c.color.xyz() = c.coords.swizzle(3, 2, 1);
70 }
evalReturnDynamic(ShaderEvalContext & c)71 inline void evalReturnDynamic(ShaderEvalContext &c)
72 {
73     c.color.xyz() = (c.coords.x() + c.coords.y() >= 0.0f) ? c.coords.swizzle(0, 1, 2) : c.coords.swizzle(3, 2, 1);
74 }
75 
getEvalFunc(ReturnMode mode)76 static ShaderEvalFunc getEvalFunc(ReturnMode mode)
77 {
78     switch (mode)
79     {
80     case RETURNMODE_ALWAYS:
81         return evalReturnAlways;
82     case RETURNMODE_NEVER:
83         return evalReturnNever;
84     case RETURNMODE_DYNAMIC:
85         return evalReturnDynamic;
86     default:
87         DE_ASSERT(false);
88         return (ShaderEvalFunc)DE_NULL;
89     }
90 }
91 
92 class ShaderReturnCase : public ShaderRenderCase
93 {
94 public:
95     ShaderReturnCase(Context &context, const char *name, const char *description, bool isVertexCase,
96                      const char *shaderSource, ShaderEvalFunc evalFunc, uint32_t requirements = 0);
97     virtual ~ShaderReturnCase(void);
98 
99     void init(void);
100 
101 private:
102     const uint32_t m_requirements;
103 };
104 
ShaderReturnCase(Context & context,const char * name,const char * description,bool isVertexCase,const char * shaderSource,ShaderEvalFunc evalFunc,uint32_t requirements)105 ShaderReturnCase::ShaderReturnCase(Context &context, const char *name, const char *description, bool isVertexCase,
106                                    const char *shaderSource, ShaderEvalFunc evalFunc, uint32_t requirements)
107     : ShaderRenderCase(context.getTestContext(), context.getRenderContext(), context.getContextInfo(), name,
108                        description, isVertexCase, evalFunc)
109     , m_requirements(requirements)
110 {
111     if (isVertexCase)
112     {
113         m_vertShaderSource = shaderSource;
114         m_fragShaderSource = "varying mediump vec4 v_color;\n\n"
115                              "void main (void)\n"
116                              "{\n"
117                              "    gl_FragColor = v_color;\n"
118                              "}\n";
119     }
120     else
121     {
122         m_fragShaderSource = shaderSource;
123         m_vertShaderSource = "attribute highp   vec4 a_position;\n"
124                              "attribute highp   vec4 a_coords;\n"
125                              "varying   mediump vec4 v_coords;\n\n"
126                              "void main (void)\n"
127                              "{\n"
128                              "    gl_Position = a_position;\n"
129                              "    v_coords = a_coords;\n"
130                              "}\n";
131     }
132 }
133 
~ShaderReturnCase(void)134 ShaderReturnCase::~ShaderReturnCase(void)
135 {
136 }
137 
init(void)138 void ShaderReturnCase::init(void)
139 {
140     try
141     {
142         ShaderRenderCase::init();
143     }
144     catch (const CompileFailed &)
145     {
146         if (m_requirements & REQUIRE_DYNAMIC_LOOPS)
147         {
148             const bool isSupported =
149                 m_isVertexCase ? m_ctxInfo.isVertexDynamicLoopSupported() : m_ctxInfo.isFragmentDynamicLoopSupported();
150             if (!isSupported)
151                 throw tcu::NotSupportedError("Dynamic loops not supported");
152         }
153 
154         throw;
155     }
156 }
157 
ShaderReturnTests(Context & context)158 ShaderReturnTests::ShaderReturnTests(Context &context) : TestCaseGroup(context, "return", "Return Statement Tests")
159 {
160 }
161 
~ShaderReturnTests(void)162 ShaderReturnTests::~ShaderReturnTests(void)
163 {
164 }
165 
makeConditionalReturnInFuncCase(Context & context,const char * name,const char * description,ReturnMode returnMode,bool isVertex)166 ShaderReturnCase *makeConditionalReturnInFuncCase(Context &context, const char *name, const char *description,
167                                                   ReturnMode returnMode, bool isVertex)
168 {
169     // Template
170     StringTemplate tmpl("${COORDSTORAGE} ${COORDPREC} vec4 ${COORDS};\n"
171                         "${EXTRADECL}\n"
172                         "${COORDPREC} vec4 getColor (void)\n"
173                         "{\n"
174                         "    if (${RETURNCOND})\n"
175                         "        return vec4(${COORDS}.xyz, 1.0);\n"
176                         "    return vec4(${COORDS}.wzy, 1.0);\n"
177                         "}\n\n"
178                         "void main (void)\n"
179                         "{\n"
180                         "${POSITIONWRITE}"
181                         "    ${OUTPUT} = getColor();\n"
182                         "}\n");
183 
184     const char *coords = isVertex ? "a_coords" : "v_coords";
185 
186     map<string, string> params;
187 
188     params["COORDSTORAGE"]  = isVertex ? "attribute" : "varying";
189     params["COORDPREC"]     = isVertex ? "highp" : "mediump";
190     params["OUTPUT"]        = isVertex ? "v_color" : "gl_FragColor";
191     params["COORDS"]        = coords;
192     params["EXTRADECL"]     = isVertex ? "attribute highp vec4 a_position;\nvarying mediump vec4 v_color;\n" : "";
193     params["POSITIONWRITE"] = isVertex ? "    gl_Position = a_position;\n" : "";
194 
195     switch (returnMode)
196     {
197     case RETURNMODE_ALWAYS:
198         params["RETURNCOND"] = "true";
199         break;
200     case RETURNMODE_NEVER:
201         params["RETURNCOND"] = "false";
202         break;
203     case RETURNMODE_DYNAMIC:
204         params["RETURNCOND"] = string(coords) + ".x+" + coords + ".y >= 0.0";
205         break;
206     default:
207         DE_ASSERT(false);
208     }
209 
210     return new ShaderReturnCase(context, name, description, isVertex, tmpl.specialize(params).c_str(),
211                                 getEvalFunc(returnMode));
212 }
213 
makeOutputWriteReturnCase(Context & context,const char * name,const char * description,bool inFunction,ReturnMode returnMode,bool isVertex)214 ShaderReturnCase *makeOutputWriteReturnCase(Context &context, const char *name, const char *description,
215                                             bool inFunction, ReturnMode returnMode, bool isVertex)
216 {
217     // Template
218     StringTemplate tmpl(inFunction ? "${COORDATTRS} vec4 ${COORDS};\n"
219                                      "${EXTRADECL}\n"
220                                      "void myfunc (void)\n"
221                                      "{\n"
222                                      "    ${OUTPUT} = vec4(${COORDS}.xyz, 1.0);\n"
223                                      "    if (${RETURNCOND})\n"
224                                      "        return;\n"
225                                      "    ${OUTPUT} = vec4(${COORDS}.wzy, 1.0);\n"
226                                      "}\n\n"
227                                      "void main (void)\n"
228                                      "{\n"
229                                      "${POSITIONWRITE}"
230                                      "    myfunc();\n"
231                                      "}\n" :
232                                      "${COORDATTRS} vec4 ${COORDS};\n"
233                                      "uniform mediump int ui_one;\n"
234                                      "${EXTRADECL}\n"
235                                      "void main ()\n"
236                                      "{\n"
237                                      "${POSITIONWRITE}"
238                                      "    ${OUTPUT} = vec4(${COORDS}.xyz, 1.0);\n"
239                                      "    if (${RETURNCOND})\n"
240                                      "        return;\n"
241                                      "    ${OUTPUT} = vec4(${COORDS}.wzy, 1.0);\n"
242                                      "}\n");
243 
244     const char *coords = isVertex ? "a_coords" : "v_coords";
245 
246     map<string, string> params;
247 
248     params["COORDATTRS"]    = isVertex ? "attribute highp" : "varying mediump";
249     params["COORDS"]        = coords;
250     params["OUTPUT"]        = isVertex ? "v_color" : "gl_FragColor";
251     params["EXTRADECL"]     = isVertex ? "attribute highp vec4 a_position;\nvarying mediump vec4 v_color;\n" : "";
252     params["POSITIONWRITE"] = isVertex ? "    gl_Position = a_position;\n" : "";
253 
254     switch (returnMode)
255     {
256     case RETURNMODE_ALWAYS:
257         params["RETURNCOND"] = "true";
258         break;
259     case RETURNMODE_NEVER:
260         params["RETURNCOND"] = "false";
261         break;
262     case RETURNMODE_DYNAMIC:
263         params["RETURNCOND"] = string(coords) + ".x+" + coords + ".y >= 0.0";
264         break;
265     default:
266         DE_ASSERT(false);
267     }
268 
269     return new ShaderReturnCase(context, name, description, isVertex, tmpl.specialize(params).c_str(),
270                                 getEvalFunc(returnMode));
271 }
272 
makeReturnInLoopCase(Context & context,const char * name,const char * description,bool isDynamicLoop,ReturnMode returnMode,bool isVertex)273 ShaderReturnCase *makeReturnInLoopCase(Context &context, const char *name, const char *description, bool isDynamicLoop,
274                                        ReturnMode returnMode, bool isVertex)
275 {
276     // Template
277     StringTemplate tmpl("${COORDSTORAGE} ${COORDPREC} vec4 ${COORDS};\n"
278                         "uniform mediump int ui_one;\n"
279                         "${EXTRADECL}\n"
280                         "${COORDPREC} vec4 getCoords (void)\n"
281                         "{\n"
282                         "    ${COORDPREC} vec4 coords = ${COORDS};\n"
283                         "    for (int i = 0; i < ${ITERLIMIT}; i++)\n"
284                         "    {\n"
285                         "        if (${RETURNCOND})\n"
286                         "            return coords;\n"
287                         "        coords = coords.wzyx;\n"
288                         "    }\n"
289                         "    return coords;\n"
290                         "}\n\n"
291                         "void main (void)\n"
292                         "{\n"
293                         "${POSITIONWRITE}"
294                         "    ${OUTPUT} = vec4(getCoords().xyz, 1.0);\n"
295                         "}\n");
296 
297     const char *coords = isVertex ? "a_coords" : "v_coords";
298 
299     map<string, string> params;
300 
301     params["COORDSTORAGE"]  = isVertex ? "attribute" : "varying";
302     params["COORDPREC"]     = isVertex ? "highp" : "mediump";
303     params["OUTPUT"]        = isVertex ? "v_color" : "gl_FragColor";
304     params["COORDS"]        = coords;
305     params["EXTRADECL"]     = isVertex ? "attribute highp vec4 a_position;\nvarying mediump vec4 v_color;\n" : "";
306     params["POSITIONWRITE"] = isVertex ? "    gl_Position = a_position;\n" : "";
307     params["ITERLIMIT"]     = isDynamicLoop ? "ui_one" : "1";
308 
309     switch (returnMode)
310     {
311     case RETURNMODE_ALWAYS:
312         params["RETURNCOND"] = "true";
313         break;
314     case RETURNMODE_NEVER:
315         params["RETURNCOND"] = "false";
316         break;
317     case RETURNMODE_DYNAMIC:
318         params["RETURNCOND"] = string(coords) + ".x+" + coords + ".y >= 0.0";
319         break;
320     default:
321         DE_ASSERT(false);
322     }
323 
324     return new ShaderReturnCase(context, name, description, isVertex, tmpl.specialize(params).c_str(),
325                                 getEvalFunc(returnMode), isDynamicLoop ? REQUIRE_DYNAMIC_LOOPS : 0);
326 }
327 
getReturnModeName(ReturnMode mode)328 static const char *getReturnModeName(ReturnMode mode)
329 {
330     switch (mode)
331     {
332     case RETURNMODE_ALWAYS:
333         return "always";
334     case RETURNMODE_NEVER:
335         return "never";
336     case RETURNMODE_DYNAMIC:
337         return "dynamic";
338     default:
339         DE_ASSERT(false);
340         return DE_NULL;
341     }
342 }
343 
getReturnModeDesc(ReturnMode mode)344 static const char *getReturnModeDesc(ReturnMode mode)
345 {
346     switch (mode)
347     {
348     case RETURNMODE_ALWAYS:
349         return "Always return";
350     case RETURNMODE_NEVER:
351         return "Never return";
352     case RETURNMODE_DYNAMIC:
353         return "Return based on coords";
354     default:
355         DE_ASSERT(false);
356         return DE_NULL;
357     }
358 }
359 
init(void)360 void ShaderReturnTests::init(void)
361 {
362     // Single return statement in function.
363     addChild(new ShaderReturnCase(m_context, "single_return_vertex", "Single return statement in function", true,
364                                   "attribute highp vec4 a_position;\n"
365                                   "attribute highp vec4 a_coords;\n"
366                                   "varying highp vec4 v_color;\n\n"
367                                   "vec4 getColor (void)\n"
368                                   "{\n"
369                                   "    return vec4(a_coords.xyz, 1.0);\n"
370                                   "}\n\n"
371                                   "void main (void)\n"
372                                   "{\n"
373                                   "    gl_Position = a_position;\n"
374                                   "    v_color = getColor();\n"
375                                   "}\n",
376                                   evalReturnAlways));
377     addChild(new ShaderReturnCase(m_context, "single_return_fragment", "Single return statement in function", false,
378                                   "varying mediump vec4 v_coords;\n"
379                                   "mediump vec4 getColor (void)\n"
380                                   "{\n"
381                                   "    return vec4(v_coords.xyz, 1.0);\n"
382                                   "}\n\n"
383                                   "void main (void)\n"
384                                   "{\n"
385                                   "    gl_FragColor = getColor();\n"
386                                   "}\n",
387                                   evalReturnAlways));
388 
389     // Conditional return statement in function.
390     for (int returnMode = 0; returnMode < RETURNMODE_LAST; returnMode++)
391     {
392         for (int isFragment = 0; isFragment < 2; isFragment++)
393         {
394             string name = string("conditional_return_") + getReturnModeName((ReturnMode)returnMode) +
395                           (isFragment ? "_fragment" : "_vertex");
396             string description = string(getReturnModeDesc((ReturnMode)returnMode)) + " in function";
397             addChild(makeConditionalReturnInFuncCase(m_context, name.c_str(), description.c_str(),
398                                                      (ReturnMode)returnMode, isFragment == 0));
399         }
400     }
401 
402     // Unconditional double return in function.
403     addChild(new ShaderReturnCase(m_context, "double_return_vertex", "Unconditional double return in function", true,
404                                   "attribute highp vec4 a_position;\n"
405                                   "attribute highp vec4 a_coords;\n"
406                                   "varying highp vec4 v_color;\n\n"
407                                   "vec4 getColor (void)\n"
408                                   "{\n"
409                                   "    return vec4(a_coords.xyz, 1.0);\n"
410                                   "    return vec4(a_coords.wzy, 1.0);\n"
411                                   "}\n\n"
412                                   "void main (void)\n"
413                                   "{\n"
414                                   "    gl_Position = a_position;\n"
415                                   "    v_color = getColor();\n"
416                                   "}\n",
417                                   evalReturnAlways));
418     addChild(new ShaderReturnCase(m_context, "double_return_fragment", "Unconditional double return in function", false,
419                                   "varying mediump vec4 v_coords;\n"
420                                   "mediump vec4 getColor (void)\n"
421                                   "{\n"
422                                   "    return vec4(v_coords.xyz, 1.0);\n"
423                                   "    return vec4(v_coords.wzy, 1.0);\n"
424                                   "}\n\n"
425                                   "void main (void)\n"
426                                   "{\n"
427                                   "    gl_FragColor = getColor();\n"
428                                   "}\n",
429                                   evalReturnAlways));
430 
431     // Last statement in main.
432     addChild(new ShaderReturnCase(m_context, "last_statement_in_main_vertex", "Return as a final statement in main()",
433                                   true,
434                                   "attribute highp vec4 a_position;\n"
435                                   "attribute highp vec4 a_coords;\n"
436                                   "varying highp vec4 v_color;\n\n"
437                                   "void main (void)\n"
438                                   "{\n"
439                                   "    gl_Position = a_position;\n"
440                                   "    v_color = vec4(a_coords.xyz, 1.0);\n"
441                                   "    return;\n"
442                                   "}\n",
443                                   evalReturnAlways));
444     addChild(new ShaderReturnCase(m_context, "last_statement_in_main_fragment", "Return as a final statement in main()",
445                                   false,
446                                   "varying mediump vec4 v_coords;\n\n"
447                                   "void main (void)\n"
448                                   "{\n"
449                                   "    gl_FragColor = vec4(v_coords.xyz, 1.0);\n"
450                                   "    return;\n"
451                                   "}\n",
452                                   evalReturnAlways));
453 
454     // Return between output variable writes.
455     for (int inFunc = 0; inFunc < 2; inFunc++)
456     {
457         for (int returnMode = 0; returnMode < RETURNMODE_LAST; returnMode++)
458         {
459             for (int isFragment = 0; isFragment < 2; isFragment++)
460             {
461                 string name = string("output_write_") + (inFunc ? "in_func_" : "") +
462                               getReturnModeName((ReturnMode)returnMode) + (isFragment ? "_fragment" : "_vertex");
463                 string desc = string(getReturnModeDesc((ReturnMode)returnMode)) +
464                               (inFunc ? " in user-defined function" : " in main()") + " between output writes";
465 
466                 addChild(makeOutputWriteReturnCase(m_context, name.c_str(), desc.c_str(), inFunc != 0,
467                                                    (ReturnMode)returnMode, isFragment == 0));
468             }
469         }
470     }
471 
472     // Conditional return statement in loop.
473     for (int isDynamicLoop = 0; isDynamicLoop < 2; isDynamicLoop++)
474     {
475         for (int returnMode = 0; returnMode < RETURNMODE_LAST; returnMode++)
476         {
477             for (int isFragment = 0; isFragment < 2; isFragment++)
478             {
479                 string name = string("return_in_") + (isDynamicLoop ? "dynamic" : "static") + "_loop_" +
480                               getReturnModeName((ReturnMode)returnMode) + (isFragment ? "_fragment" : "_vertex");
481                 string description = string(getReturnModeDesc((ReturnMode)returnMode)) + " in loop";
482                 addChild(makeReturnInLoopCase(m_context, name.c_str(), description.c_str(), isDynamicLoop != 0,
483                                               (ReturnMode)returnMode, isFragment == 0));
484             }
485         }
486     }
487 
488     // Unconditional return in infinite loop.
489     addChild(new ShaderReturnCase(m_context, "return_in_infinite_loop_vertex", "Return in infinite loop", true,
490                                   "attribute highp vec4 a_position;\n"
491                                   "attribute highp vec4 a_coords;\n"
492                                   "varying highp vec4 v_color;\n"
493                                   "uniform int ui_zero;\n\n"
494                                   "highp vec4 getCoords (void)\n"
495                                   "{\n"
496                                   "    for (int i = 1; i < 10; i += ui_zero)\n"
497                                   "        return a_coords;\n"
498                                   "    return a_coords.wzyx;\n"
499                                   "}\n\n"
500                                   "void main (void)\n"
501                                   "{\n"
502                                   "    gl_Position = a_position;\n"
503                                   "    v_color = vec4(getCoords().xyz, 1.0);\n"
504                                   "    return;\n"
505                                   "}\n",
506                                   evalReturnAlways, REQUIRE_DYNAMIC_LOOPS));
507     addChild(new ShaderReturnCase(m_context, "return_in_infinite_loop_fragment", "Return in infinite loop", false,
508                                   "varying mediump vec4 v_coords;\n"
509                                   "uniform int ui_zero;\n\n"
510                                   "mediump vec4 getCoords (void)\n"
511                                   "{\n"
512                                   "    for (int i = 1; i < 10; i += ui_zero)\n"
513                                   "        return v_coords;\n"
514                                   "    return v_coords.wzyx;\n"
515                                   "}\n\n"
516                                   "void main (void)\n"
517                                   "{\n"
518                                   "    gl_FragColor = vec4(getCoords().xyz, 1.0);\n"
519                                   "    return;\n"
520                                   "}\n",
521                                   evalReturnAlways, REQUIRE_DYNAMIC_LOOPS));
522 }
523 
524 } // namespace Functional
525 } // namespace gles2
526 } // namespace deqp
527