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