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