1 /*------------------------------------------------------------------------
2  * Vulkan Conformance Tests
3  * ------------------------
4  *
5  * Copyright (c) 2018 Google Inc.
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 limit tests.
22 *//*--------------------------------------------------------------------*/
23 
24 #include "vktShaderRenderLimitTests.hpp"
25 #include "vktShaderRender.hpp"
26 #include "tcuImageCompare.hpp"
27 #include "tcuStringTemplate.hpp"
28 #include "tcuTextureUtil.hpp"
29 #include "tcuTestLog.hpp"
30 #include "vktDrawUtil.hpp"
31 #include "deMath.h"
32 
33 using namespace std;
34 using namespace tcu;
35 using namespace vk;
36 using namespace de;
37 
38 namespace vkt
39 {
40 using namespace drawutil;
41 
42 namespace sr
43 {
44 
45 namespace
46 {
47 
48 class FragmentInputComponentCaseInstance : public ShaderRenderCaseInstance
49 {
50 public:
51     FragmentInputComponentCaseInstance(Context &context);
52 
53     TestStatus iterate(void);
54     virtual void setupDefaultInputs(void);
55 
56 private:
57     const Vec4 m_constantColor;
58 };
59 
FragmentInputComponentCaseInstance(Context & context)60 FragmentInputComponentCaseInstance::FragmentInputComponentCaseInstance(Context &context)
61     : ShaderRenderCaseInstance(context)
62     , m_constantColor(0.1f, 0.05f, 0.2f, 0.0f)
63 {
64 }
65 
iterate(void)66 TestStatus FragmentInputComponentCaseInstance::iterate(void)
67 {
68     const UVec2 viewportSize = getViewportSize();
69     const int width          = viewportSize.x();
70     const int height         = viewportSize.y();
71     const tcu::RGBA threshold(2, 2, 2, 2);
72     Surface resImage(width, height);
73     Surface refImage(width, height);
74     bool compareOk = false;
75 
76     const uint16_t indices[12] = {0, 4, 1, 0, 5, 4, 1, 2, 3, 1, 3, 4};
77 
78     setup();
79     render(6, 4, indices);
80     copy(resImage.getAccess(), getResultImage().getAccess());
81 
82     // Reference image
83     for (int y = 0; y < refImage.getHeight(); y++)
84     {
85         for (int x = 0; x < refImage.getWidth(); x++)
86             refImage.setPixel(x, y, RGBA(0, 255, 0, 255));
87     }
88 
89     compareOk = pixelThresholdCompare(m_context.getTestContext().getLog(), "Result", "Image comparison result",
90                                       refImage, resImage, threshold, COMPARE_LOG_RESULT);
91 
92     if (compareOk)
93         return TestStatus::pass("Result image matches reference");
94     else
95         return TestStatus::fail("Image mismatch");
96 }
97 
setupDefaultInputs(void)98 void FragmentInputComponentCaseInstance::setupDefaultInputs(void)
99 {
100     const float vertices[] = {-1.0f, -1.0f, 0.0f, 1.0f, 0.0f, -1.0f, 0.0f, 1.0f, 1.0f,  -1.0f, 0.0f, 1.0f,
101                               1.0f,  1.0f,  0.0f, 1.0f, 0.0f, 1.0f,  0.0f, 1.0f, -1.0f, 1.0f,  0.0f, 1.0f};
102 
103     addAttribute(0u, VK_FORMAT_R32G32B32A32_SFLOAT, uint16_t(sizeof(float) * 4), 6, vertices);
104 }
105 
106 class FragmentInputComponentCase : public TestCase
107 {
108 public:
109     FragmentInputComponentCase(TestContext &testCtx, const string &name, const uint16_t inputComponents);
110     virtual ~FragmentInputComponentCase(void);
111 
112     void initPrograms(SourceCollections &dst) const;
113     TestInstance *createInstance(Context &context) const;
114 
115 private:
116     FragmentInputComponentCase(const FragmentInputComponentCase &);
117     const uint16_t m_inputComponents;
118 };
119 
FragmentInputComponentCase(TestContext & testCtx,const string & name,const uint16_t inputComponents)120 FragmentInputComponentCase::FragmentInputComponentCase(TestContext &testCtx, const string &name,
121                                                        const uint16_t inputComponents)
122     : TestCase(testCtx, name)
123     , m_inputComponents(inputComponents)
124 {
125 }
126 
~FragmentInputComponentCase(void)127 FragmentInputComponentCase::~FragmentInputComponentCase(void)
128 {
129 }
130 
initPrograms(SourceCollections & dst) const131 void FragmentInputComponentCase::initPrograms(SourceCollections &dst) const
132 {
133     const tcu::StringTemplate vertexCodeTemplate("#version 450\n"
134                                                  "layout(location = 0) in highp vec4 a_position;\n"
135                                                  "${VARYING_OUT}"
136                                                  "void main (void)\n"
137                                                  "{\n"
138                                                  "    gl_Position = a_position;\n"
139                                                  "${VARYING_DECL}"
140                                                  "}\n");
141 
142     const tcu::StringTemplate fragmentCodeTemplate("#version 450\n"
143                                                    "layout(location = 0) out highp vec4 o_color;\n"
144                                                    "${VARYING_IN}"
145                                                    "void main (void)\n"
146                                                    "{\n"
147                                                    "    int errorCount = 0;\n"
148                                                    "${VERIFY}"
149                                                    "\n"
150                                                    "    if (errorCount == 0)\n"
151                                                    "        o_color = vec4(0.0, 1.0, 0.0, 1.0);\n"
152                                                    "    else\n"
153                                                    "        o_color = vec4(1.0, 0.0, 0.0, 1.0);\n"
154                                                    "}\n");
155 
156     //
157     // The number of vertex output/fragment input components is *inclusive* of any built-ins being used,
158     // since gl_Position is always output by the shader, this actually means that there are n - 4 components
159     // available as user specified output data.
160     //
161     // [14.1.4. Location Assignment, para 11]
162     //
163     // "The number of input and output locations available for a shader input or output
164     //  interface are limited, and dependent on the shader stage as described in Shader
165     //  Input and Output Locations. All variables in both the built-in interface block
166     //  and the user-defined variable interface count against these limits."
167     //
168     // So, as an example, the '128' component variant of this test will specify 124 user
169     // declared outputs in addition to gl_Position.
170 
171     uint16_t maxLocations = (uint16_t)deCeilToInt32((float)(m_inputComponents - 4) / 4u);
172     string varyingType;
173     map<string, string> vertexParams;
174     map<string, string> fragmentParams;
175 
176     for (uint16_t loc = 0; loc < maxLocations; loc++)
177     {
178         if (loc == (maxLocations - 1u))
179         {
180             switch (m_inputComponents - loc * 4u)
181             {
182             case 1:
183                 varyingType = "float";
184                 break;
185             case 2:
186                 varyingType = "vec2";
187                 break;
188             case 3:
189                 varyingType = "vec3";
190                 break;
191             default:
192                 varyingType = "vec4";
193             }
194         }
195         else
196             varyingType = "vec4";
197 
198         vertexParams["VARYING_OUT"] += "layout(location = " + de::toString(loc) + ") out highp " + varyingType +
199                                        " o_color" + de::toString(loc) + ";\n";
200         vertexParams["VARYING_DECL"] +=
201             "    o_color" + de::toString(loc) + " = " + varyingType + "(" + de::toString(loc) + ".0);\n";
202         fragmentParams["VARYING_IN"] += "layout(location = " + de::toString(loc) + ") in highp " + varyingType +
203                                         " i_color" + de::toString(loc) + ";\n";
204         fragmentParams["VERIFY"] += "    errorCount += (i_color" + de::toString(loc) + " == " + varyingType + "(" +
205                                     de::toString(loc) + ".0)) ? 0 : 1;\n";
206     }
207 
208     dst.glslSources.add("vert") << glu::VertexSource(vertexCodeTemplate.specialize(vertexParams));
209     dst.glslSources.add("frag") << glu::FragmentSource(fragmentCodeTemplate.specialize(fragmentParams));
210 }
211 
createInstance(Context & context) const212 TestInstance *FragmentInputComponentCase::createInstance(Context &context) const
213 {
214     const InstanceInterface &vki              = context.getInstanceInterface();
215     const VkPhysicalDevice physDevice         = context.getPhysicalDevice();
216     const VkPhysicalDeviceLimits limits       = getPhysicalDeviceProperties(vki, physDevice).limits;
217     const uint16_t maxFragmentInputComponents = (uint16_t)limits.maxFragmentInputComponents;
218     const uint16_t maxVertexOutputComponents  = (uint16_t)limits.maxVertexOutputComponents;
219 
220     if (m_inputComponents > maxFragmentInputComponents)
221     {
222         const std::string notSupportedStr = "Unsupported number of fragment input components (" +
223                                             de::toString(m_inputComponents) +
224                                             ") maxFragmentInputComponents=" + de::toString(maxFragmentInputComponents);
225         TCU_THROW(NotSupportedError, notSupportedStr.c_str());
226     }
227 
228     // gl_Position counts as an output component as well, so outputComponents = inputComponents + 4
229     if (m_inputComponents + 4 > maxVertexOutputComponents)
230     {
231         const std::string notSupportedStr = "Unsupported number of user specified vertex output components (" +
232                                             de::toString(m_inputComponents + 4) +
233                                             ") maxVertexOutputComponents=" + de::toString(maxVertexOutputComponents);
234         TCU_THROW(NotSupportedError, notSupportedStr.c_str());
235     }
236 
237     return new FragmentInputComponentCaseInstance(context);
238 }
239 } // namespace
240 
createLimitTests(TestContext & testCtx)241 TestCaseGroup *createLimitTests(TestContext &testCtx)
242 {
243     de::MovePtr<TestCaseGroup> limitGroup(new TestCaseGroup(testCtx, "limits"));
244     de::MovePtr<TestCaseGroup> nearGroup(new TestCaseGroup(testCtx, "near_max"));
245 
246     de::MovePtr<TestCaseGroup> inputComponentsGroup(new TestCaseGroup(testCtx, "fragment_input"));
247 
248     // Fragment input component case
249     uint16_t fragmentComponentMaxLimits[] = {64u, 128u, 256u};
250 
251     for (uint16_t limitNdx = 0; limitNdx < DE_LENGTH_OF_ARRAY(fragmentComponentMaxLimits); limitNdx++)
252     {
253         for (int16_t cases = 5; cases > 0; cases--)
254             inputComponentsGroup->addChild(new FragmentInputComponentCase(
255                 testCtx, "components_" + de::toString(fragmentComponentMaxLimits[limitNdx] - cases),
256                 (uint16_t)(fragmentComponentMaxLimits[limitNdx] - cases)));
257     }
258 
259     nearGroup->addChild(inputComponentsGroup.release());
260     limitGroup->addChild(nearGroup.release());
261     return limitGroup.release();
262 }
263 
264 } // namespace sr
265 } // namespace vkt
266