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