1 /*-------------------------------------------------------------------------
2  * OpenGL Conformance Test Suite
3  * -----------------------------
4  *
5  * Copyright (c) 2022-2022 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 /*!
21  * \file  esextcFragmentShadingRateBasic.hpp
22  * \brief FragmentShadingRateEXT basic
23  */ /*-------------------------------------------------------------------*/
24 
25 #include "esextcFragmentShadingRateBasic.hpp"
26 #include "deRandom.h"
27 #include "esextcFragmentShadingRateTests.hpp"
28 #include "gluContextInfo.hpp"
29 #include "gluDefs.hpp"
30 #include "gluShaderProgram.hpp"
31 #include "glwEnums.hpp"
32 #include "glwFunctions.hpp"
33 #include "tcuTestLog.hpp"
34 
35 #define DEFAULT_COLOR_FBO_SIZE 255
36 #define TRIANGLE_COUNT 100
37 
38 namespace glcts
39 {
40 
41 enum
42 {
43 	ERROR_NONE				 = 0,
44 	ERROR_SHADING_RATE_ERROR = 1,
45 };
46 
47 ///  Constructor
48 ///
49 /// @param context     Test context
50 /// @param name        Test case's name
51 /// @param description Test case's description
FragmentShadingRateBasic(Context& context, const ExtParameters& extParams, const char* name, const char* description)52 FragmentShadingRateBasic::FragmentShadingRateBasic(Context& context, const ExtParameters& extParams, const char* name,
53 												   const char* description)
54 	: TestCaseBase(context, extParams, name, description), m_program(nullptr)
55 {
56 }
57 
58 /// Initialize test
init(void)59 void FragmentShadingRateBasic::init(void)
60 {
61 	TestCaseBase::init();
62 
63 	// Skip if required extensions are not supported.
64 	if (!m_is_fragment_shading_rate_supported)
65 	{
66 		throw tcu::NotSupportedError(FRAGMENT_SHADING_RATE_NOT_SUPPORTED, "", __FILE__, __LINE__);
67 	}
68 }
69 
70 /// Deinitializes all GLES objects created for the test.
deinit(void)71 void FragmentShadingRateBasic::deinit(void)
72 {
73 	// Retrieve GLES entry points.
74 	const glw::Functions& gl = m_context.getRenderContext().getFunctions();
75 
76 	// Reset GLES state
77 	gl.bindTexture(GL_TEXTURE_2D, 0);
78 	gl.bindBuffer(GL_ARRAY_BUFFER, 0);
79 	gl.bindFramebuffer(GL_FRAMEBUFFER, 0);
80 
81 	gl.deleteTextures(1, &m_to_id);
82 	gl.deleteFramebuffers(1, &m_fbo_id);
83 	gl.deleteBuffers(1, &m_vbo_id);
84 
85 	delete m_program;
86 
87 	// Deinitialize base class
88 	TestCaseBase::deinit();
89 }
90 
91 /// Generate Vertex Shader string
genVS() const92 std::string FragmentShadingRateBasic::genVS() const
93 {
94 	std::ostringstream os;
95 	os << "#version 310 es                        \n"
96 	   << "precision highp float;                 \n"
97 	   << "precision highp int;                   \n"
98 	   << "layout(location = 0) in vec4 position; \n"
99 	   << "void main() {                          \n"
100 	   << "    gl_Position = position;            \n"
101 	   << "}";
102 	return os.str();
103 }
104 
105 /// Generate Fragment Shader string
genFS() const106 std::string FragmentShadingRateBasic::genFS() const
107 {
108 	std::ostringstream os;
109 	os << "#version 310 es\n"
110 	   << "#extension GL_EXT_fragment_shading_rate : enable\n"
111 	   << "precision highp float;\n"
112 	   << "precision highp int;\n"
113 	   << "layout(location = 0) out ivec4 color0;\n"
114 	   << "uniform int drawID;\n"
115 	   << "uniform int shadingRate;\n"
116 	   << "void main() {\n"
117 	   << "    color0.x = gl_ShadingRateEXT;\n"
118 	   << "    color0.y = drawID;\n"
119 	   << "    color0.z = 0;\n"
120 	   << "    color0.w = 0;\n"
121 	   << "    if (gl_ShadingRateEXT != shadingRate) { \n"
122 	   << "        color0.w = " << ERROR_SHADING_RATE_ERROR << ";\n"
123 	   << "    }\n"
124 	   << "}";
125 
126 	return os.str();
127 }
128 
129 /// Initializes all GLES objects and reference values for the test.
setupTest(void)130 void FragmentShadingRateBasic::setupTest(void)
131 {
132 	m_program =
133 		new glu::ShaderProgram(m_context.getRenderContext(), glu::makeVtxFragSources(genVS().c_str(), genFS().c_str()));
134 
135 	if (!m_program->isOk())
136 	{
137 		m_testCtx.getLog() << tcu::TestLog::Message << "" << tcu::TestLog::EndMessage
138 						   << tcu::TestLog::ShaderProgram(false, "")
139 						   << tcu::TestLog::Shader(QP_SHADER_TYPE_VERTEX,
140 												   m_program->getShaderInfo(glu::SHADERTYPE_VERTEX, 0).source, false,
141 												   m_program->getShaderInfo(glu::SHADERTYPE_VERTEX, 0).infoLog)
142 
143 						   << tcu::TestLog::Shader(QP_SHADER_TYPE_FRAGMENT,
144 												   m_program->getShaderInfo(glu::SHADERTYPE_FRAGMENT, 0).source, false,
145 												   m_program->getShaderInfo(glu::SHADERTYPE_FRAGMENT, 0).infoLog)
146 						   << tcu::TestLog::EndShaderProgram;
147 		TCU_FAIL("Shader creation failed");
148 	}
149 
150 	const glw::Functions& gl = m_context.getRenderContext().getFunctions();
151 
152 	// Generate framebuffer objects
153 	gl.genFramebuffers(1, &m_fbo_id);
154 	GLU_EXPECT_NO_ERROR(gl.getError(), "Error setting up framebuffer objects");
155 
156 	gl.bindFramebuffer(GL_FRAMEBUFFER, m_fbo_id);
157 	GLU_EXPECT_NO_ERROR(gl.getError(), "Error binding frame buffer object!");
158 
159 	// Generate a new texture name
160 	gl.genTextures(1, &m_to_id);
161 	GLU_EXPECT_NO_ERROR(gl.getError(), "Error generating texture objects");
162 
163 	// Allocate unsigned integer storage
164 	gl.bindTexture(GL_TEXTURE_2D, m_to_id);
165 	GLU_EXPECT_NO_ERROR(gl.getError(), "Error binding texture object!");
166 	gl.texStorage2D(GL_TEXTURE_2D, 1, GL_RGBA32UI, m_tcParam.width, m_tcParam.height);
167 	GLU_EXPECT_NO_ERROR(gl.getError(), "Error allocating texture object!");
168 
169 	// Attach it to the framebuffer
170 	gl.framebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, m_to_id, 0);
171 	GLU_EXPECT_NO_ERROR(gl.getError(), "Error attaching texture to frame buffer");
172 
173 	constexpr deUint32 kVerticesCount = (TRIANGLE_COUNT * 3 * 2);
174 	float			   randomVertices[kVerticesCount];
175 
176 	deRandom rnd;
177 	deRandom_init(&rnd, m_tcParam.seed);
178 	for (deUint32 i = 0; i < kVerticesCount; i++)
179 	{
180 		randomVertices[i] = deRandom_getFloat(&rnd) * 2.0f - 1.0f;
181 	}
182 
183 	gl.genBuffers(1, &m_vbo_id);
184 	GLU_EXPECT_NO_ERROR(gl.getError(), "Error generate buffer objects");
185 
186 	gl.bindBuffer(GL_ARRAY_BUFFER, m_vbo_id);
187 	GLU_EXPECT_NO_ERROR(gl.getError(), "Error binding buffer objects");
188 
189 	gl.bufferData(GL_ARRAY_BUFFER, sizeof(randomVertices), randomVertices, GL_STATIC_DRAW);
190 	GLU_EXPECT_NO_ERROR(gl.getError(), "Error uploading buffer data");
191 }
192 
193 /// Test if the error code returned by glGetError is the same as expected.
194 /// If the error is different from expected description is logged.
195 ///
196 /// @param expected_error    GLenum error which is expected
197 /// @param description       Log message in the case of failure.
198 ///
199 /// @return true if error is equal to expected, false otherwise.
verifyError(const glw::GLenum expected_error, const char* description) const200 glw::GLboolean FragmentShadingRateBasic::verifyError(const glw::GLenum expected_error, const char* description) const
201 {
202 	// Retrieve GLES entry points.
203 	const glw::Functions& gl = m_context.getRenderContext().getFunctions();
204 
205 	glw::GLboolean test_passed = true;
206 	glw::GLenum	   error_code  = gl.getError();
207 
208 	if (error_code != expected_error)
209 	{
210 		test_passed = false;
211 
212 		m_testCtx.getLog() << tcu::TestLog::Message << description << tcu::TestLog::EndMessage;
213 	}
214 
215 	return test_passed;
216 }
217 
218 /// Executes the test.
219 ///  Sets the test result to QP_TEST_RESULT_FAIL if the test failed, QP_TEST_RESULT_PASS otherwise.
220 ///  Note the function throws exception should an error occur!
221 ///
222 ///  @return STOP if the test has finished, CONTINUE to indicate iterate should be called once again.
223 ///
iterate(void)224 tcu::TestNode::IterateResult FragmentShadingRateBasic::iterate(void)
225 {
226 	const glw::Functions& gl = m_context.getRenderContext().getFunctions();
227 
228 	// Initialization
229 	m_tcParam.width	 = DEFAULT_COLOR_FBO_SIZE;
230 	m_tcParam.height = DEFAULT_COLOR_FBO_SIZE;
231 
232 	setupTest();
233 
234 	constexpr deUint32 kMaxRateCount =
235 		16; // SHADING_RATE_1X1_PIXELS_EXT ~ SHADING_RATE_4X4_PIXELS_EXT, actually 9 is enough
236 	glw::GLenum	 shadingRates[kMaxRateCount];
237 	glw::GLsizei count = 0;
238 
239 	gl.getFragmentShadingRatesEXT(1, kMaxRateCount, &count, shadingRates);
240 	GLU_EXPECT_NO_ERROR(gl.getError(), "Error to get shading rate getFragmentShadingRatesEXT");
241 	DE_ASSERT(count > 0);
242 
243 	for (glw::GLsizei i = 0; i < count; i++)
244 	{
245 		m_availableShadingRates.push_back(shadingRates[i]);
246 	}
247 
248 	gl.shadingRateEXT(GL_SHADING_RATE_1X1_PIXELS_EXT);
249 	GLU_EXPECT_NO_ERROR(gl.getError(), "Error to set shadingRateEXT as default");
250 
251 	gl.clearColor(0.0f, 0.0f, 0.0f, 0.0f);
252 	gl.clear(GL_COLOR_BUFFER_BIT);
253 
254 	gl.useProgram(m_program->getProgram());
255 	GLU_EXPECT_NO_ERROR(gl.getError(), "Error use program");
256 
257 	gl.bindBuffer(GL_ARRAY_BUFFER, m_vbo_id);
258 	GLU_EXPECT_NO_ERROR(gl.getError(), "Error bind buffer vertex data");
259 
260 	gl.enableVertexAttribArray(0);
261 	GLU_EXPECT_NO_ERROR(gl.getError(), "Error enabling vertex attrib pointer 0");
262 
263 	gl.vertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, sizeof(float) * 2, nullptr);
264 	GLU_EXPECT_NO_ERROR(gl.getError(), "Error binding vertex attrib pointer 0");
265 
266 	// draw ID start from 1
267 	for (deUint32 drawID = 1; drawID < TRIANGLE_COUNT; drawID++)
268 	{
269 		gl.uniform1i(gl.getUniformLocation(m_program->getProgram(), "shadingRate"),
270 					 fsrutils::packShadingRate(translateDrawIDToShadingRate(drawID)));
271 		GLU_EXPECT_NO_ERROR(gl.getError(), "Error set uniform shading Rate value");
272 
273 		gl.uniform1i(gl.getUniformLocation(m_program->getProgram(), "drawID"), drawID);
274 		GLU_EXPECT_NO_ERROR(gl.getError(), "Error set uniform drawID value");
275 
276 		gl.shadingRateEXT(translateDrawIDToShadingRate(drawID));
277 		GLU_EXPECT_NO_ERROR(gl.getError(), "Error set shading rate");
278 		gl.drawArrays(GL_TRIANGLES, drawID * 2, 3);
279 		GLU_EXPECT_NO_ERROR(gl.getError(), "Error draw a triangle");
280 	}
281 
282 	const deUint32		  dataSize = m_tcParam.width * m_tcParam.height * 4;
283 	std::vector<deUint32> resultData(dataSize);
284 
285 	gl.readPixels(0, 0, m_tcParam.width, m_tcParam.height, GL_RGBA_INTEGER, GL_UNSIGNED_INT, resultData.data());
286 	GLU_EXPECT_NO_ERROR(gl.getError(), "Error reading pixels from frame buffer!");
287 
288 	for (deUint32 y = 0; y < m_tcParam.height; y++)
289 	{
290 		for (deUint32 x = 0; x < m_tcParam.width; x++)
291 		{
292 			const deUint32* sample = &resultData[(y * m_tcParam.width + x) * 4];
293 			if (sample[1] == 0) // nothing rendered
294 			{
295 				continue;
296 			}
297 
298 			const deUint32 shadingRate = sample[0];
299 			const deUint32 drawID	   = sample[1];
300 
301 			if (fsrutils::packShadingRate(translateDrawIDToShadingRate(drawID)) != shadingRate)
302 			{
303 				DE_ASSERT(sample[3] == ERROR_SHADING_RATE_ERROR); // sample 3 is error code
304 
305 				std::stringstream error_sstream;
306 
307 				error_sstream << "The draw ID is " << drawID << "Shading Rate is" << shadingRate << ", But we expect "
308 							  << fsrutils::packShadingRate(translateDrawIDToShadingRate(drawID));
309 
310 				m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, error_sstream.str().c_str());
311 
312 				return STOP;
313 			}
314 		}
315 	}
316 
317 	// All done
318 	if (m_testCtx.getTestResult() != QP_TEST_RESULT_FAIL)
319 	{
320 		m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
321 	}
322 
323 	return STOP;
324 }
325 
326 /// Translate draw ID to ShadingRate enumeration
327 ///
328 /// @param drawID draw ID to translate shading rate
329 ///
330 /// @return shading rate enumeration
translateDrawIDToShadingRate(deUint32 drawID) const331 glw::GLenum FragmentShadingRateBasic::translateDrawIDToShadingRate(deUint32 drawID) const
332 {
333 	return m_availableShadingRates[drawID % m_availableShadingRates.size()];
334 }
335 
336 } // namespace glcts
337