1/*-------------------------------------------------------------------------
2 * OpenGL Conformance Test Suite
3 * -----------------------------
4 *
5 * Copyright (c) 2017 The Khronos Group 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 glcLimitTest.cpp
21 * \brief Definition of template class.
22 */ /*-------------------------------------------------------------------*/
23
24using namespace glw;
25
26template<typename DataType>
27LimitCase<DataType>::LimitCase(deqp::Context& context,
28							   const char* caseName,
29							   deUint32 limitToken,
30							   DataType limitBoundry,
31							   bool isBoundryMaximum,
32							   const char* glslVersion,
33							   const char* glslBuiltin,
34							   const char* glslExtension)
35	: deqp::TestCase(context, caseName, "Token limit validation.")
36	, m_limitToken(limitToken)
37	, m_limitBoundry(limitBoundry)
38	, m_isBoundryMaximum(isBoundryMaximum)
39	, m_glslVersion(glslVersion)
40	, m_glslBuiltin(glslBuiltin)
41	, m_glslExtension(glslExtension)
42{
43}
44
45template<typename DataType>
46LimitCase<DataType>::~LimitCase(void)
47{
48}
49
50template<typename DataType>
51tcu::TestNode::IterateResult LimitCase<DataType>::iterate(void)
52{
53	DataType limitValue = DataType();
54	const Functions& gl = m_context.getRenderContext().getFunctions();
55
56	// make sure that limit or builtin was specified
57	DE_ASSERT(m_limitToken || !m_glslBuiltin.empty());
58
59	// check if limit was specified
60	if (m_limitToken)
61	{
62		// check if limit is not smaller or greater then boundry defined in specification
63		limitValue = getLimitValue(gl);
64		if (!isWithinBoundry(limitValue))
65		{
66			m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Fail");
67			return STOP;
68		}
69
70		// if glsl builtin wasn't defined then test already passed
71		if (m_glslBuiltin.empty())
72		{
73			m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
74			return STOP;
75		}
76	}
77
78	// create compute shader to check glsl builtin
79	std::string shaderSource = createShader();
80	const GLchar* source = shaderSource.c_str();
81	const GLuint program = gl.createProgram();
82	GLuint shader = gl.createShader(GL_COMPUTE_SHADER);
83	gl.attachShader(program, shader);
84	gl.deleteShader(shader);
85	gl.shaderSource(shader, 1, &source, NULL);
86	gl.compileShader(shader);
87	gl.linkProgram(program);
88
89	GLint status;
90	gl.getProgramiv(program, GL_LINK_STATUS, &status);
91	GLint length;
92	gl.getProgramiv(program, GL_INFO_LOG_LENGTH, &length);
93	if (length > 1)
94	{
95		std::vector<GLchar> log(length);
96		gl.getProgramInfoLog(program, length, NULL, &log[0]);
97		m_testCtx.getLog() << tcu::TestLog::Message
98						   << &log[0]
99						   << tcu::TestLog::EndMessage;
100	}
101	if (status == GL_FALSE)
102	{
103		m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Fail");
104		return STOP;
105	}
106
107	gl.useProgram(program);
108
109	GLuint buffer;
110	gl.genBuffers(1, &buffer);
111	gl.bindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, buffer);
112	gl.bufferData(GL_SHADER_STORAGE_BUFFER, sizeof(DataType), NULL, GL_DYNAMIC_DRAW);
113	gl.bindBuffer(GL_SHADER_STORAGE_BUFFER, 0);
114
115	gl.dispatchCompute(1, 1, 1);
116
117	gl.bindBuffer(GL_SHADER_STORAGE_BUFFER, buffer);
118	gl.memoryBarrier(GL_BUFFER_UPDATE_BARRIER_BIT);
119	DataType* data = static_cast<DataType*>(gl.mapBufferRange(GL_SHADER_STORAGE_BUFFER, 0, sizeof(DataType), GL_MAP_READ_BIT));
120	DataType builtinValue = data[0];
121	gl.unmapBuffer(GL_SHADER_STORAGE_BUFFER);
122	gl.bindBuffer(GL_SHADER_STORAGE_BUFFER, 0);
123
124	if (m_limitToken)
125	{
126		// limit token was specified - compare builtin to it
127		if (isEqual(limitValue, builtinValue))
128		{
129			m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
130		}
131		else
132		{
133			m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Fail");
134			m_testCtx.getLog() << tcu::TestLog::Message
135							   << "Shader builtin has value: "
136							   << builtinValue
137							   << " which is different then the value of corresponding limit: "
138							   << limitValue
139							   << tcu::TestLog::EndMessage;
140		}
141	}
142	else
143	{
144		// limit token was not specified - compare builtin to the boundry
145		if (isWithinBoundry(builtinValue, true))
146		{
147			m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
148		}
149		else
150		{
151			m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Fail");
152			m_testCtx.getLog() << tcu::TestLog::Message
153							   << "Shader builtin value is: "
154							   << builtinValue
155							   << " which is outside of specified boundry."
156							   << tcu::TestLog::EndMessage;
157		}
158	}
159
160	return STOP;
161}
162
163template<typename DataType>
164bool LimitCase<DataType>::isWithinBoundry(DataType value, bool isBuiltin) const
165{
166	if (m_isBoundryMaximum)
167	{
168		// value should be smaller or euqual to boundry
169		if (isGreater(value, m_limitBoundry))
170		{
171			m_testCtx.getLog() << tcu::TestLog::Message
172							   << (isBuiltin ? "Builtin" : "Limit")
173							   << " value is: "
174							   << value
175							   << " when it should not be greater than "
176							   << m_limitBoundry
177							   << tcu::TestLog::EndMessage;
178			return false;
179		}
180	}
181	else
182	{
183		// value should be greater or euqual to boundry
184		if (isSmaller(value, m_limitBoundry))
185		{
186			m_testCtx.getLog() << tcu::TestLog::Message
187							   << (isBuiltin ? "Builtin" : "Limit")
188							   << " value is: "
189							   << value
190							   << "when it should not be smaller than "
191							   << m_limitBoundry
192							   << tcu::TestLog::EndMessage;
193			return false;
194		}
195	}
196
197	return true;
198}
199
200template<typename DataType>
201std::string LimitCase<DataType>::createShader() const
202{
203		std::stringstream shader;
204		shader << "#version " << m_glslVersion << "\n";
205		if (!m_glslExtension.empty())
206			shader << "#extension " + m_glslExtension + " : require\n";
207		shader << "layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;\n"
208				  "layout(std430) buffer Output {\n"
209			   << getGLSLDataType() <<" data; } g_out;"
210				  "void main() { "
211				  "g_out.data = " << m_glslBuiltin << "; }";
212		return shader.str();
213}
214
215template<typename DataType>
216std::string LimitCase<DataType>::getGLSLDataType() const
217{
218	return "int";
219}
220
221template<>
222std::string LimitCase<GLfloat>::getGLSLDataType() const
223{
224	return "float";
225}
226
227template<>
228std::string LimitCase<tcu::IVec3>::getGLSLDataType() const
229{
230	return "ivec3";
231}
232
233template<typename DataType>
234bool LimitCase<DataType>::isEqual(DataType a, DataType b) const
235{
236	return a == b;
237}
238
239template<>
240bool LimitCase<tcu::IVec3>::isEqual(tcu::IVec3 a, tcu::IVec3 b) const
241{
242	tcu::BVec3 bVec = tcu::equal(a, b);
243	return tcu::boolAll(bVec);
244}
245
246template<typename DataType>
247bool LimitCase<DataType>::isGreater(DataType a, DataType b) const
248{
249	return a > b;
250}
251
252template<>
253bool LimitCase<tcu::IVec3>::isGreater(tcu::IVec3 a, tcu::IVec3 b) const
254{
255	tcu::BVec3 bVec = tcu::greaterThan(a, b);
256	return tcu::boolAll(bVec);
257}
258
259template<typename DataType>
260bool LimitCase<DataType>::isSmaller(DataType a, DataType b) const
261{
262	return a < b;
263}
264
265template<>
266bool LimitCase<tcu::IVec3>::isSmaller(tcu::IVec3 a, tcu::IVec3 b) const
267{
268	tcu::BVec3 bVec = tcu::lessThan(a, b);
269	return tcu::boolAll(bVec);
270}
271
272template<typename DataType>
273DataType LimitCase<DataType>::getLimitValue(const Functions&) const
274{
275	return DataType();
276}
277
278template<>
279GLint LimitCase<GLint>::getLimitValue(const Functions& gl) const
280{
281	GLint value = -1;
282	gl.getIntegerv(m_limitToken, &value);
283	GLU_EXPECT_NO_ERROR(gl.getError(), "glGetIntegerv");
284	return value;
285}
286
287template<>
288GLint64 LimitCase<GLint64>::getLimitValue(const Functions& gl) const
289{
290	GLint64 value = -1;
291	gl.getInteger64v(m_limitToken, &value);
292	GLU_EXPECT_NO_ERROR(gl.getError(), "glGetInteger64v");
293	return value;
294}
295
296template<>
297GLuint64 LimitCase<GLuint64>::getLimitValue(const Functions& gl) const
298{
299	GLint64 value = -1;
300	gl.getInteger64v(m_limitToken, &value);
301	GLU_EXPECT_NO_ERROR(gl.getError(), "glGetInteger64v");
302	return static_cast<GLuint64>(value);
303}
304
305template<>
306GLfloat LimitCase<GLfloat>::getLimitValue(const Functions& gl) const
307{
308	GLfloat value = -1;
309	gl.getFloatv(m_limitToken, &value);
310	GLU_EXPECT_NO_ERROR(gl.getError(), "glGetFloatv");
311	return value;
312}
313
314template<>
315tcu::IVec3 LimitCase<tcu::IVec3>::getLimitValue(const Functions& gl) const
316{
317	tcu::IVec3 value(-1);
318	for (int i = 0; i < 3; i++)
319		gl.getIntegeri_v(m_limitToken, (GLuint)i, &value[i]);
320	GLU_EXPECT_NO_ERROR(gl.getError(), "glGetIntegeri_v");
321	return value;
322}
323
324