1 /*------------------------------------------------------------------------
2  * Vulkan Conformance Tests
3  * ------------------------
4  *
5  * Copyright (c) 2015 The Khronos Group Inc.
6  * Copyright (c) 2015 Samsung Electronics Co., Ltd.
7  * Copyright (c) 2016 The Android Open Source Project
8  *
9  * Licensed under the Apache License, Version 2.0 (the "License");
10  * you may not use this file except in compliance with the License.
11  * You may obtain a copy of the License at
12  *
13  *      http://www.apache.org/licenses/LICENSE-2.0
14  *
15  * Unless required by applicable law or agreed to in writing, software
16  * distributed under the License is distributed on an "AS IS" BASIS,
17  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18  * See the License for the specific language governing permissions and
19  * limitations under the License.
20  *
21  *//*!
22  * \file
23  * \brief Shader discard statement tests.
24  *//*--------------------------------------------------------------------*/
25 
26 #include "vktShaderRenderDiscardTests.hpp"
27 #include "vktShaderRender.hpp"
28 #include "tcuStringTemplate.hpp"
29 #include "gluTexture.hpp"
30 
31 #include <string>
32 
33 using tcu::StringTemplate;
34 
35 namespace vkt
36 {
37 namespace sr
38 {
39 namespace
40 {
41 
42 class SamplerUniformSetup : public UniformSetup
43 {
44 public:
SamplerUniformSetup(bool useSampler)45     SamplerUniformSetup(bool useSampler) : m_useSampler(useSampler)
46     {
47     }
48 
setup(ShaderRenderCaseInstance & instance,const tcu::Vec4 &) const49     virtual void setup(ShaderRenderCaseInstance &instance, const tcu::Vec4 &) const
50     {
51         instance.useUniform(0u, UI_ONE);
52         instance.useUniform(1u, UI_TWO);
53         if (m_useSampler)
54             instance.useSampler(2u, 0u); // To the uniform binding location 2 bind the texture 0
55     }
56 
57 private:
58     const bool m_useSampler;
59 };
60 
61 class ShaderDiscardCaseInstance : public ShaderRenderCaseInstance
62 {
63 public:
64     ShaderDiscardCaseInstance(Context &context, bool isVertexCase, const ShaderEvaluator &evaluator,
65                               const UniformSetup &uniformSetup, bool usesTexture, bool fuzzyCompare);
66     virtual ~ShaderDiscardCaseInstance(void);
67 };
68 
ShaderDiscardCaseInstance(Context & context,bool isVertexCase,const ShaderEvaluator & evaluator,const UniformSetup & uniformSetup,bool usesTexture,bool fuzzyCompare)69 ShaderDiscardCaseInstance::ShaderDiscardCaseInstance(Context &context, bool isVertexCase,
70                                                      const ShaderEvaluator &evaluator, const UniformSetup &uniformSetup,
71                                                      bool usesTexture, bool fuzzyCompare)
72     : ShaderRenderCaseInstance(context, isVertexCase, evaluator, uniformSetup, DE_NULL, IMAGE_BACKING_MODE_REGULAR,
73                                static_cast<uint32_t>(GRID_SIZE_DEFAULTS), fuzzyCompare)
74 {
75     if (usesTexture)
76     {
77         de::SharedPtr<TextureBinding> brickTexture(new TextureBinding(
78             m_context.getTestContext().getArchive(), "vulkan/data/brick.png", TextureBinding::TYPE_2D,
79             tcu::Sampler(tcu::Sampler::CLAMP_TO_EDGE, tcu::Sampler::CLAMP_TO_EDGE, tcu::Sampler::CLAMP_TO_EDGE,
80                          tcu::Sampler::LINEAR, tcu::Sampler::LINEAR, 0.0f, true, tcu::Sampler::COMPAREMODE_NONE, 0,
81                          tcu::Vec4(0.0f, 0.0f, 0.0f, 0.0f), true)));
82         m_textures.push_back(brickTexture);
83     }
84 }
85 
~ShaderDiscardCaseInstance(void)86 ShaderDiscardCaseInstance::~ShaderDiscardCaseInstance(void)
87 {
88 }
89 
90 class ShaderDiscardCase : public ShaderRenderCase
91 {
92 public:
93     ShaderDiscardCase(tcu::TestContext &testCtx, const char *name, const char *shaderSource,
94                       const ShaderEvalFunc evalFunc, bool usesTexture, bool fuzzyCompare, bool demote);
createInstance(Context & context) const95     virtual TestInstance *createInstance(Context &context) const
96     {
97         DE_ASSERT(m_evaluator != DE_NULL);
98         DE_ASSERT(m_uniformSetup != DE_NULL);
99         return new ShaderDiscardCaseInstance(context, m_isVertexCase, *m_evaluator, *m_uniformSetup, m_usesTexture,
100                                              m_fuzzyCompare);
101     }
102 
103     virtual void checkSupport(Context &context) const;
104 
105 private:
106     const bool m_usesTexture;
107     const bool m_fuzzyCompare;
108 #ifndef CTS_USES_VULKANSC
109     const bool m_demote;
110 #endif // CTS_USES_VULKANSC
111 };
112 
ShaderDiscardCase(tcu::TestContext & testCtx,const char * name,const char * shaderSource,const ShaderEvalFunc evalFunc,bool usesTexture,bool fuzzyCompare,bool demote)113 ShaderDiscardCase::ShaderDiscardCase(tcu::TestContext &testCtx, const char *name, const char *shaderSource,
114                                      const ShaderEvalFunc evalFunc, bool usesTexture, bool fuzzyCompare, bool demote)
115     : ShaderRenderCase(testCtx, name, false, evalFunc, new SamplerUniformSetup(usesTexture), DE_NULL)
116     , m_usesTexture(usesTexture)
117     , m_fuzzyCompare(fuzzyCompare)
118 #ifndef CTS_USES_VULKANSC
119     , m_demote(demote)
120 #endif // CTS_USES_VULKANSC
121 {
122 #ifdef CTS_USES_VULKANSC
123     DE_UNREF(demote);
124 #endif // CTS_USES_VULKANSC
125 
126     m_fragShaderSource = shaderSource;
127     m_vertShaderSource = "#version 310 es\n"
128                          "layout(location=0) in  highp   vec4 a_position;\n"
129                          "layout(location=1) in  highp   vec4 a_coords;\n"
130                          "layout(location=2) in  highp   vec4 a_one;\n"
131                          "layout(location=0) out mediump vec4 v_color;\n"
132                          "layout(location=1) out mediump vec4 v_coords;\n\n"
133                          "layout(location=2) out mediump vec4 v_one;\n"
134                          "void main (void)\n"
135                          "{\n"
136                          "    gl_Position = a_position;\n"
137                          "    v_color = vec4(a_coords.xyz, 1.0);\n"
138                          "    v_coords = a_coords;\n"
139                          "    v_one = a_one;\n"
140                          "}\n";
141 }
142 
checkSupport(Context & context) const143 void ShaderDiscardCase::checkSupport(Context &context) const
144 {
145 #ifndef CTS_USES_VULKANSC
146     if (m_demote && !context.getShaderDemoteToHelperInvocationFeatures().shaderDemoteToHelperInvocation)
147         TCU_THROW(NotSupportedError, "VK_EXT_shader_demote_to_helper_invocation not supported");
148 #else
149     DE_UNREF(context);
150 #endif // CTS_USES_VULKANSC
151 }
152 
153 enum DiscardMode
154 {
155     DISCARDMODE_ALWAYS = 0,
156     DISCARDMODE_NEVER,
157     DISCARDMODE_UNIFORM,
158     DISCARDMODE_DYNAMIC,
159     DISCARDMODE_TEXTURE,
160     DISCARDMODE_DERIV,
161 
162     DISCARDMODE_LAST
163 };
164 
165 enum DiscardTemplate
166 {
167     DISCARDTEMPLATE_MAIN_BASIC = 0,
168     DISCARDTEMPLATE_FUNCTION_BASIC,
169     DISCARDTEMPLATE_MAIN_STATIC_LOOP,
170     DISCARDTEMPLATE_MAIN_DYNAMIC_LOOP,
171     DISCARDTEMPLATE_FUNCTION_STATIC_LOOP,
172 
173     DISCARDTEMPLATE_LAST
174 };
175 
176 // Evaluation functions
evalDiscardAlways(ShaderEvalContext & c)177 inline void evalDiscardAlways(ShaderEvalContext &c)
178 {
179     c.discard();
180 }
evalDiscardNever(ShaderEvalContext & c)181 inline void evalDiscardNever(ShaderEvalContext &c)
182 {
183     c.color.xyz() = c.coords.swizzle(0, 1, 2);
184 }
evalDiscardDynamic(ShaderEvalContext & c)185 inline void evalDiscardDynamic(ShaderEvalContext &c)
186 {
187     c.color.xyz() = c.coords.swizzle(0, 1, 2);
188     if (c.coords.x() + c.coords.y() > 0.0f)
189         c.discard();
190 }
191 
evalDiscardTexture(ShaderEvalContext & c)192 inline void evalDiscardTexture(ShaderEvalContext &c)
193 {
194     c.color.xyz() = c.coords.swizzle(0, 1, 2);
195     if (c.texture2D(0, c.coords.swizzle(0, 1) * 0.25f + 0.5f).x() < 0.7f)
196         c.discard();
197 }
198 
getEvalFunc(DiscardMode mode)199 static ShaderEvalFunc getEvalFunc(DiscardMode mode)
200 {
201     switch (mode)
202     {
203     case DISCARDMODE_ALWAYS:
204         return evalDiscardAlways;
205     case DISCARDMODE_NEVER:
206         return evalDiscardNever;
207     case DISCARDMODE_UNIFORM:
208         return evalDiscardAlways;
209     case DISCARDMODE_DYNAMIC:
210         return evalDiscardDynamic;
211     case DISCARDMODE_TEXTURE:
212         return evalDiscardTexture;
213     case DISCARDMODE_DERIV:
214         return evalDiscardAlways;
215     default:
216         DE_ASSERT(false);
217         return evalDiscardAlways;
218     }
219 }
220 
getTemplate(DiscardTemplate variant)221 static const char *getTemplate(DiscardTemplate variant)
222 {
223 #define GLSL_SHADER_TEMPLATE_HEADER                                 \
224     "#version 310 es\n"                                             \
225     "#extension GL_EXT_demote_to_helper_invocation : enable\n"      \
226     "layout(location = 0) in mediump vec4 v_color;\n"               \
227     "layout(location = 1) in mediump vec4 v_coords;\n"              \
228     "layout(location = 2) in mediump vec4 a_one;\n"                 \
229     "layout(location = 0) out mediump vec4 o_color;\n"              \
230     "layout(set = 0, binding = 2) uniform sampler2D    ut_brick;\n" \
231     "layout(set = 0, binding = 0) uniform block0 { mediump int  ui_one; };\n\n"
232 
233     switch (variant)
234     {
235     case DISCARDTEMPLATE_MAIN_BASIC:
236         return GLSL_SHADER_TEMPLATE_HEADER "void main (void)\n"
237                                            "{\n"
238                                            "    o_color = v_color;\n"
239                                            "    ${DISCARD};\n"
240                                            "}\n";
241 
242     case DISCARDTEMPLATE_FUNCTION_BASIC:
243         return GLSL_SHADER_TEMPLATE_HEADER "void myfunc (void)\n"
244                                            "{\n"
245                                            "    ${DISCARD};\n"
246                                            "}\n\n"
247                                            "void main (void)\n"
248                                            "{\n"
249                                            "    o_color = v_color;\n"
250                                            "    myfunc();\n"
251                                            "}\n";
252 
253     case DISCARDTEMPLATE_MAIN_STATIC_LOOP:
254         return GLSL_SHADER_TEMPLATE_HEADER "void main (void)\n"
255                                            "{\n"
256                                            "    o_color = v_color;\n"
257                                            "    for (int i = 0; i < 2; i++)\n"
258                                            "    {\n"
259                                            "        if (i > 0) {\n"
260                                            "            ${DISCARD};\n"
261                                            "        }\n"
262                                            "    }\n"
263                                            "}\n";
264 
265     case DISCARDTEMPLATE_MAIN_DYNAMIC_LOOP:
266         return GLSL_SHADER_TEMPLATE_HEADER "layout(set = 0, binding = 1) uniform block1 { mediump int  ui_two; };\n\n"
267                                            "void main (void)\n"
268                                            "{\n"
269                                            "    o_color = v_color;\n"
270                                            "    for (int i = 0; i < ui_two; i++)\n"
271                                            "    {\n"
272                                            "        if (i > 0) {\n"
273                                            "            ${DISCARD};\n"
274                                            "        }\n"
275                                            "    }\n"
276                                            "}\n";
277 
278     case DISCARDTEMPLATE_FUNCTION_STATIC_LOOP:
279         return GLSL_SHADER_TEMPLATE_HEADER "void myfunc (void)\n"
280                                            "{\n"
281                                            "    for (int i = 0; i < 2; i++)\n"
282                                            "    {\n"
283                                            "        if (i > 0) {\n"
284                                            "            ${DISCARD};\n"
285                                            "        }\n"
286                                            "    }\n"
287                                            "}\n\n"
288                                            "void main (void)\n"
289                                            "{\n"
290                                            "    o_color = v_color;\n"
291                                            "    myfunc();\n"
292                                            "}\n";
293 
294     default:
295         DE_ASSERT(false);
296         return DE_NULL;
297     }
298 
299 #undef GLSL_SHADER_TEMPLATE_HEADER
300 }
301 
getTemplateName(DiscardTemplate variant)302 static const char *getTemplateName(DiscardTemplate variant)
303 {
304     switch (variant)
305     {
306     case DISCARDTEMPLATE_MAIN_BASIC:
307         return "basic";
308     case DISCARDTEMPLATE_FUNCTION_BASIC:
309         return "function";
310     case DISCARDTEMPLATE_MAIN_STATIC_LOOP:
311         return "static_loop";
312     case DISCARDTEMPLATE_MAIN_DYNAMIC_LOOP:
313         return "dynamic_loop";
314     case DISCARDTEMPLATE_FUNCTION_STATIC_LOOP:
315         return "function_static_loop";
316     default:
317         DE_ASSERT(false);
318         return DE_NULL;
319     }
320 }
321 
getModeName(DiscardMode mode)322 static const char *getModeName(DiscardMode mode)
323 {
324     switch (mode)
325     {
326     case DISCARDMODE_ALWAYS:
327         return "always";
328     case DISCARDMODE_NEVER:
329         return "never";
330     case DISCARDMODE_UNIFORM:
331         return "uniform";
332     case DISCARDMODE_DYNAMIC:
333         return "dynamic";
334     case DISCARDMODE_TEXTURE:
335         return "texture";
336     case DISCARDMODE_DERIV:
337         return "deriv";
338     default:
339         DE_ASSERT(false);
340         return DE_NULL;
341     }
342 }
343 
makeDiscardCase(tcu::TestContext & testCtx,DiscardTemplate tmpl,DiscardMode mode,const std::string & discardStr)344 de::MovePtr<ShaderDiscardCase> makeDiscardCase(tcu::TestContext &testCtx, DiscardTemplate tmpl, DiscardMode mode,
345                                                const std::string &discardStr)
346 {
347     StringTemplate shaderTemplate(getTemplate(tmpl));
348 
349     std::map<std::string, std::string> params;
350 
351     switch (mode)
352     {
353     case DISCARDMODE_ALWAYS:
354         params["DISCARD"] = discardStr;
355         break;
356     case DISCARDMODE_NEVER:
357         params["DISCARD"] = "if (false) " + discardStr;
358         break;
359     case DISCARDMODE_UNIFORM:
360         params["DISCARD"] = "if (ui_one > 0) " + discardStr;
361         break;
362     case DISCARDMODE_DYNAMIC:
363         params["DISCARD"] = "if (v_coords.x+v_coords.y > 0.0) " + discardStr;
364         break;
365     case DISCARDMODE_TEXTURE:
366         params["DISCARD"] = "if (texture(ut_brick, v_coords.xy*0.25+0.5).x < 0.7) " + discardStr;
367         break;
368     case DISCARDMODE_DERIV:
369         params["DISCARD"] =
370             // First demote pixels where fragCoord.xy LSBs are not both zero, leaving only one
371             // non-helper pixel per quad. Then compute derivatives of "one+fragCoord" and check they
372             // are 0 or 1 as appropriate. Also check that helperInvocationEXT varies in the quad and
373             // is false on non-helper pixels. Demote the pixel if it gets the right values, so the final
374             // image should be entirely the clear color. If we don't get the right values, output red.
375             // This test case would not work for discard, because derivatives become undefined.
376             "  ivec2 f = ivec2(gl_FragCoord.xy);\n"
377             "  int lsb = (f.x | f.y)&1;\n"
378             "  if (lsb != 0) demote;\n"
379             "  bool isHelper = helperInvocationEXT();\n"
380             "  highp vec2 dx = dFdx(a_one.xy + gl_FragCoord.xy);\n"
381             "  highp vec2 dy = dFdy(a_one.xy + gl_FragCoord.xy);\n"
382             "  highp float dh = dFdx(float(isHelper));\n"
383             "  bool valid = abs(dx.x-1.0) < 0.01 && dx.y == 0.0 && dy.x == 0.0 && abs(dy.y-1.0) < 0.01 && abs(dh-1.0) "
384             "< 0.1 && !isHelper;\n"
385             "  if (valid) demote;\n"
386             "  o_color = vec4(1,0,0,1);\n";
387         break;
388     default:
389         DE_ASSERT(false);
390         break;
391     }
392 
393     std::string name = std::string(getTemplateName(tmpl)) + "_" + getModeName(mode);
394 
395     return de::MovePtr<ShaderDiscardCase>(
396         new ShaderDiscardCase(testCtx, name.c_str(), shaderTemplate.specialize(params).c_str(), getEvalFunc(mode),
397                               mode == DISCARDMODE_TEXTURE, // usesTexture
398                               mode != DISCARDMODE_DERIV,   // fuzzyCompare
399                               discardStr == "demote"));    // demote
400 }
401 
402 class ShaderDiscardTests : public tcu::TestCaseGroup
403 {
404 public:
405     ShaderDiscardTests(tcu::TestContext &textCtx, const char *groupName);
406     virtual ~ShaderDiscardTests(void);
407 
408     virtual void init(void);
409 
410 private:
411     ShaderDiscardTests(const ShaderDiscardTests &);            // not allowed!
412     ShaderDiscardTests &operator=(const ShaderDiscardTests &); // not allowed!
413     const std::string m_groupName;
414 };
415 
ShaderDiscardTests(tcu::TestContext & testCtx,const char * groupName)416 ShaderDiscardTests::ShaderDiscardTests(tcu::TestContext &testCtx, const char *groupName)
417     : TestCaseGroup(testCtx, groupName)
418     , m_groupName(groupName)
419 {
420 }
421 
~ShaderDiscardTests(void)422 ShaderDiscardTests::~ShaderDiscardTests(void)
423 {
424 }
425 
init(void)426 void ShaderDiscardTests::init(void)
427 {
428     for (int tmpl = 0; tmpl < DISCARDTEMPLATE_LAST; tmpl++)
429     {
430         for (int mode = 0; mode < DISCARDMODE_LAST; mode++)
431         {
432             if (mode == DISCARDMODE_DERIV && m_groupName == "discard")
433                 continue;
434             addChild(makeDiscardCase(m_testCtx, (DiscardTemplate)tmpl, (DiscardMode)mode, m_groupName).release());
435         }
436     }
437 }
438 
439 } // namespace
440 
createDiscardTests(tcu::TestContext & testCtx)441 tcu::TestCaseGroup *createDiscardTests(tcu::TestContext &testCtx)
442 {
443     return new ShaderDiscardTests(testCtx, "discard");
444 }
445 
createDemoteTests(tcu::TestContext & testCtx)446 tcu::TestCaseGroup *createDemoteTests(tcu::TestContext &testCtx)
447 {
448     return new ShaderDiscardTests(testCtx, "demote");
449 }
450 
451 } // namespace sr
452 } // namespace vkt
453