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  esextcesextcFragmentShadingRateCombinedTests.hpp
22  * \brief FragmentShadingRateEXT combined tests
23  */ /*-------------------------------------------------------------------*/
24 
25 #include "esextcFragmentShadingRateCombinedTests.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 TRIANGLE_COUNT 100
36 constexpr uint32_t kShadingRateCount = 16;
37 
38 namespace glcts
39 {
40 
41 /// check combiner is trivial operation or not
42 ///
43 /// @param combineOp combiner which want to check
44 ///
45 /// @return true for trivial combiner
isTrivialCombiner(glw::GLenum combineOp)46 bool isTrivialCombiner(glw::GLenum combineOp)
47 {
48     return ((combineOp == GL_FRAGMENT_SHADING_RATE_COMBINER_OP_KEEP_EXT) ||
49             (combineOp == GL_FRAGMENT_SHADING_RATE_COMBINER_OP_REPLACE_EXT)) ?
50                true :
51                false;
52 }
53 
54 /// Constructor
55 ///
56 /// @param context     Test context
57 /// @param extParams   Extension Parameter
58 /// @param testcaseParam Test case Parameter
59 /// @param name        Test case's name
60 /// @param description Test case's description
FragmentShadingRateCombined(Context & context,const ExtParameters & extParams,const FragmentShadingRateCombined::TestcaseParam & testcaseParam,const char * name,const char * description)61 FragmentShadingRateCombined::FragmentShadingRateCombined(
62     Context &context, const ExtParameters &extParams, const FragmentShadingRateCombined::TestcaseParam &testcaseParam,
63     const char *name, const char *description)
64     : TestCaseBase(context, extParams, name, description)
65     , m_tcParam(testcaseParam)
66     , m_renderProgram(nullptr)
67     , m_computeProgram(nullptr)
68     , m_to_id(0)
69     , m_sr_to_id(0)
70     , m_fbo_id(0)
71     , m_vbo_id(0)
72     , m_simulationCache(kShadingRateCount * kShadingRateCount * kShadingRateCount, 0xFFFFFFFF)
73 {
74 }
75 
76 /// Initialize test
init(void)77 void FragmentShadingRateCombined::init(void)
78 {
79     TestCaseBase::init();
80 
81     // Skip if required extensions are not supported.
82     if (!m_is_fragment_shading_rate_supported)
83     {
84         throw tcu::NotSupportedError(FRAGMENT_SHADING_RATE_NOT_SUPPORTED, "", __FILE__, __LINE__);
85     }
86 
87     if (!m_is_fragment_shading_rate_primitive_supported)
88     {
89         if (m_tcParam.useShadingRatePrimitive)
90         {
91             throw tcu::NotSupportedError(FRAGMENT_SHADING_RATE_NOT_SUPPORTED, "", __FILE__, __LINE__);
92         }
93 
94         if (m_tcParam.combinerOp0 != GL_FRAGMENT_SHADING_RATE_COMBINER_OP_KEEP_EXT)
95         {
96             throw tcu::NotSupportedError(FRAGMENT_SHADING_RATE_NOT_SUPPORTED, "", __FILE__, __LINE__);
97         }
98     }
99 
100     if (!m_is_fragment_shading_rate_attachment_supported)
101     {
102         if (m_tcParam.useShadingRateAttachment)
103         {
104             throw tcu::NotSupportedError(FRAGMENT_SHADING_RATE_NOT_SUPPORTED, "", __FILE__, __LINE__);
105         }
106 
107         if (m_tcParam.combinerOp1 != GL_FRAGMENT_SHADING_RATE_COMBINER_OP_KEEP_EXT)
108         {
109             throw tcu::NotSupportedError(FRAGMENT_SHADING_RATE_NOT_SUPPORTED, "", __FILE__, __LINE__);
110         }
111     }
112 
113     if (!isTrivialCombiner(m_tcParam.combinerOp0) || !isTrivialCombiner(m_tcParam.combinerOp1))
114     {
115         const glw::Functions &gl                 = m_context.getRenderContext().getFunctions();
116         glw::GLboolean supportNonTrivialCombiner = false;
117         gl.getBooleanv(GL_FRAGMENT_SHADING_RATE_NON_TRIVIAL_COMBINERS_SUPPORTED_EXT, &supportNonTrivialCombiner);
118         GLU_EXPECT_NO_ERROR(gl.getError(), "Error getBooleanv non trivial combiner");
119 
120         if (!supportNonTrivialCombiner)
121         {
122             throw tcu::NotSupportedError("Non trivial combiner is not supported", "", __FILE__, __LINE__);
123         }
124     }
125 }
126 
127 /// Deinitializes all GLES objects created for the test.
deinit(void)128 void FragmentShadingRateCombined::deinit(void)
129 {
130     // Retrieve GLES entry points.
131     const glw::Functions &gl = m_context.getRenderContext().getFunctions();
132 
133     // Reset GLES state
134     gl.bindTexture(GL_TEXTURE_2D, 0);
135     gl.bindTexture(GL_TEXTURE_2D_MULTISAMPLE, 0);
136     gl.bindBuffer(GL_ARRAY_BUFFER, 0);
137     gl.bindFramebuffer(GL_FRAMEBUFFER, 0);
138 
139     gl.deleteTextures(1, &m_to_id);
140     gl.deleteFramebuffers(1, &m_fbo_id);
141     gl.deleteBuffers(1, &m_vbo_id);
142 
143     if (m_tcParam.useShadingRateAttachment)
144     {
145         gl.deleteTextures(1, &m_sr_to_id);
146     }
147 
148     delete m_renderProgram;
149     delete m_computeProgram;
150 
151     // Deinitialize base class
152     TestCaseBase::deinit();
153 }
154 
155 /// Generate Vertex Shader string
genVS()156 std::string FragmentShadingRateCombined::genVS()
157 {
158     std::ostringstream os;
159     os << "#version 310 es                        \n"
160        << "#extension GL_EXT_fragment_shading_rate : enable\n"
161        << "precision highp float;                 \n"
162        << "precision highp int;                   \n"
163        << "layout(location = 0) in vec4 position; \n"
164        << "uniform int primShadingRate;           \n"
165        << "void main() {                          \n"
166        << "    gl_Position = position;            \n";
167 
168     if (m_tcParam.useShadingRatePrimitive)
169     {
170         os << "    gl_PrimitiveShadingRateEXT = primShadingRate;\n";
171     }
172     os << "}";
173     return os.str();
174 }
175 
176 /// Generate Fragment Shader string
genFS()177 std::string FragmentShadingRateCombined::genFS()
178 {
179     std::ostringstream os;
180     os << "#version 310 es\n"
181        << "#extension GL_EXT_fragment_shading_rate : enable\n"
182        << "precision highp float;\n"
183        << "precision highp int;\n"
184        << "layout(location = 0) out vec4 color0;\n"
185        << "uniform int primID;\n"
186        << "uniform int drawID;\n"
187        << "void main() {\n"
188        << "    color0.x = float(gl_ShadingRateEXT);\n"
189        << "    color0.y = float(drawID);\n";
190 
191     if (m_tcParam.useShadingRatePrimitive)
192     {
193         os << "    color0.z = float(primID);\n";
194     };
195 
196     os << "    color0.w = 0.0;\n"
197        << "}";
198 
199     return os.str();
200 }
201 
202 /// Generate Compute Shader string for copy
genCS()203 std::string FragmentShadingRateCombined::genCS()
204 {
205     uint32_t samples = m_tcParam.msaa ? 4 : 1;
206     std::ostringstream os;
207     os << "#version 310 es\n"
208        << "precision highp float;\n"
209        << "precision highp int;\n"
210        << (m_tcParam.msaa ? "uniform highp sampler2DMS colorTex;\n" : "uniform highp sampler2D colorTex;\n")
211        << "layout (binding = 0, std430) buffer ColorBuf {\n"
212        << "    uvec4 values[];\n"
213        << "} colorbuf;\n"
214        << "layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;\n"
215        << "void main()\n"
216        << "{\n"
217        << "    for (uint i = 0u; i < " << samples << "u; ++i) \n"
218        << "    {\n"
219        << "        uint index = ((gl_GlobalInvocationID.y * " << m_tcParam.framebufferSize
220        << "u) + gl_GlobalInvocationID.x) * " << samples << "u + i;\n"
221        << "        colorbuf.values[index] = uvec4(round(texelFetch(colorTex, ivec2(gl_GlobalInvocationID.xy), "
222        << "int(i))));\n"
223        << "    }\n"
224        << "}";
225     return os.str();
226 }
227 
228 /// Initializes all GLES objects and reference values for the test.
setupTest(void)229 void FragmentShadingRateCombined::setupTest(void)
230 {
231     m_renderProgram =
232         new glu::ShaderProgram(m_context.getRenderContext(), glu::makeVtxFragSources(genVS().c_str(), genFS().c_str()));
233 
234     if (!m_renderProgram->isOk())
235     {
236         m_testCtx.getLog() << tcu::TestLog::Message << "" << tcu::TestLog::EndMessage
237                            << tcu::TestLog::ShaderProgram(false, "")
238                            << tcu::TestLog::Shader(QP_SHADER_TYPE_VERTEX,
239                                                    m_renderProgram->getShaderInfo(glu::SHADERTYPE_VERTEX, 0).source,
240                                                    false,
241                                                    m_renderProgram->getShaderInfo(glu::SHADERTYPE_VERTEX, 0).infoLog)
242 
243                            << tcu::TestLog::Shader(QP_SHADER_TYPE_FRAGMENT,
244                                                    m_renderProgram->getShaderInfo(glu::SHADERTYPE_FRAGMENT, 0).source,
245                                                    false,
246                                                    m_renderProgram->getShaderInfo(glu::SHADERTYPE_FRAGMENT, 0).infoLog)
247                            << tcu::TestLog::EndShaderProgram;
248         TCU_FAIL("Shader creation failed");
249     }
250 
251     glu::ProgramSources sourcesCompute;
252     sourcesCompute.sources[glu::SHADERTYPE_COMPUTE].push_back(genCS());
253     m_computeProgram = new glu::ShaderProgram(m_context.getRenderContext(), sourcesCompute);
254     if (!m_computeProgram->isOk())
255     {
256         m_testCtx.getLog() << tcu::TestLog::Message << "" << tcu::TestLog::EndMessage
257                            << tcu::TestLog::ShaderProgram(false, "")
258                            << tcu::TestLog::Shader(QP_SHADER_TYPE_COMPUTE,
259                                                    m_computeProgram->getShaderInfo(glu::SHADERTYPE_COMPUTE, 0).source,
260                                                    false,
261                                                    m_computeProgram->getShaderInfo(glu::SHADERTYPE_COMPUTE, 0).infoLog);
262         TCU_FAIL("Shader creation failed");
263     }
264 
265     const glw::Functions &gl = m_context.getRenderContext().getFunctions();
266 
267     // Generate framebuffer objects
268     gl.genFramebuffers(1, &m_fbo_id);
269     GLU_EXPECT_NO_ERROR(gl.getError(), "Error setting up framebuffer objects");
270 
271     gl.bindFramebuffer(GL_FRAMEBUFFER, m_fbo_id);
272     GLU_EXPECT_NO_ERROR(gl.getError(), "Error binding frame buffer object!");
273 
274     // Generate texture objects
275     gl.genTextures(1, &m_to_id);
276     GLU_EXPECT_NO_ERROR(gl.getError(), "Error generating texture objects");
277 
278     gl.pixelStorei(GL_UNPACK_ALIGNMENT, 1);
279     GLU_EXPECT_NO_ERROR(gl.getError(), "Error set pixelStorei for unpack alignment");
280 
281     // Allocate unsigned integer storage
282     glw::GLenum textureTarget = GL_TEXTURE_2D;
283     if (m_tcParam.msaa)
284     {
285         textureTarget = GL_TEXTURE_2D_MULTISAMPLE;
286         gl.bindTexture(textureTarget, m_to_id);
287         GLU_EXPECT_NO_ERROR(gl.getError(), "Error binding texture object!");
288         gl.texStorage2DMultisample(textureTarget, 4, GL_RGBA32F, m_tcParam.framebufferSize, m_tcParam.framebufferSize,
289                                    true);
290         GLU_EXPECT_NO_ERROR(gl.getError(), "Error allocating texture object!");
291     }
292     else
293     {
294         gl.bindTexture(textureTarget, m_to_id);
295         GLU_EXPECT_NO_ERROR(gl.getError(), "Error binding texture object!");
296         gl.texStorage2D(textureTarget, 1, GL_RGBA32F, m_tcParam.framebufferSize, m_tcParam.framebufferSize);
297         GLU_EXPECT_NO_ERROR(gl.getError(), "Error allocating texture object!");
298     }
299 
300     // Attach it to the framebuffer
301     gl.framebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, textureTarget, m_to_id, 0); /* level */
302     GLU_EXPECT_NO_ERROR(gl.getError(), "Error attaching texture to frame buffer");
303 
304     if (m_tcParam.useShadingRateAttachment)
305     {
306         // generate shading rate texture
307         gl.getIntegerv(GL_MAX_FRAGMENT_SHADING_RATE_ATTACHMENT_TEXEL_WIDTH_EXT, &m_srTexelWidth);
308         GLU_EXPECT_NO_ERROR(gl.getError(),
309                             "Error getIntegerv GL_MAX_FRAGMENT_SHADING_RATE_ATTACHMENT_TEXEL_WIDTH_EXT!");
310         gl.getIntegerv(GL_MAX_FRAGMENT_SHADING_RATE_ATTACHMENT_TEXEL_HEIGHT_EXT, &m_srTexelHeight);
311         GLU_EXPECT_NO_ERROR(gl.getError(),
312                             "Error getIntegerv GL_MAX_FRAGMENT_SHADING_RATE_ATTACHMENT_TEXEL_HEIGHT_EXT!");
313 
314         const uint32_t srWidth  = (m_tcParam.framebufferSize + m_srTexelWidth - 1) / m_srTexelWidth;
315         const uint32_t srHeight = (m_tcParam.framebufferSize + m_srTexelHeight - 1) / m_srTexelHeight;
316 
317         gl.genTextures(1, &m_sr_to_id);
318         GLU_EXPECT_NO_ERROR(gl.getError(), "Error generating texture objects");
319 
320         // Allocate unsigned integer storage
321         gl.bindTexture(GL_TEXTURE_2D, m_sr_to_id);
322         GLU_EXPECT_NO_ERROR(gl.getError(), "Error binding texture object!");
323 
324         gl.texStorage2D(GL_TEXTURE_2D, 1, GL_R8UI, srWidth, srHeight);
325         GLU_EXPECT_NO_ERROR(gl.getError(), "Error allocating texture object!");
326 
327         std::vector<uint8_t> attachmentShadingRateData;
328         attachmentShadingRateData.reserve(srWidth * srHeight);
329         for (uint32_t sry = 0; sry < srHeight; sry++)
330         {
331             for (uint32_t srx = 0; srx < srWidth; srx++)
332             {
333                 uint8_t packedShadingRate =
334                     static_cast<unsigned char>(fsrutils::packShadingRate(translateCoordsToShadingRate(srx, sry)));
335                 attachmentShadingRateData.push_back(packedShadingRate);
336             }
337         }
338 
339         gl.texSubImage2D(GL_TEXTURE_2D, 0, 0, 0, srWidth, srHeight, GL_RED_INTEGER, GL_UNSIGNED_BYTE,
340                          attachmentShadingRateData.data());
341         GLU_EXPECT_NO_ERROR(gl.getError(), "Error updating shading rate data to texture");
342 
343         // Attach it to the framebuffer
344         gl.framebufferShadingRateEXT(GL_FRAMEBUFFER, GL_SHADING_RATE_ATTACHMENT_EXT, m_sr_to_id, 0, 1, m_srTexelWidth,
345                                      m_srTexelHeight);
346         GLU_EXPECT_NO_ERROR(gl.getError(), "Error attaching shading rate attachment to frame buffer");
347     }
348 
349     constexpr uint32_t kVerticesCount = (TRIANGLE_COUNT * 3 * 2);
350     float randomVertices[kVerticesCount];
351 
352     deRandom rnd;
353     deRandom_init(&rnd, 0);
354     for (uint32_t i = 0; i < kVerticesCount; i++)
355     {
356         randomVertices[i] = deRandom_getFloat(&rnd) * 2.0f - 1.0f;
357     }
358 
359     gl.genBuffers(1, &m_vbo_id);
360     GLU_EXPECT_NO_ERROR(gl.getError(), "Error setting up buffer objects");
361 
362     gl.bindBuffer(GL_ARRAY_BUFFER, m_vbo_id);
363     GLU_EXPECT_NO_ERROR(gl.getError(), "Error binding buffer objects");
364 
365     gl.bufferData(GL_ARRAY_BUFFER, sizeof(randomVertices), randomVertices, GL_STATIC_DRAW);
366     GLU_EXPECT_NO_ERROR(gl.getError(), "Error uploading buffer data");
367 }
368 
369 /// Executes the test.
370 /// Sets the test result to QP_TEST_RESULT_FAIL if the test failed, QP_TEST_RESULT_PASS otherwise.
371 /// Note the function throws exception should an error occur!
372 ///
373 /// @return STOP if the test has finished, CONTINUE to indicate iterate should be called once again.
iterate(void)374 tcu::TestNode::IterateResult FragmentShadingRateCombined::iterate(void)
375 {
376     const glw::Functions &gl = m_context.getRenderContext().getFunctions();
377 
378     // Initialization
379     const uint32_t sampleCount = m_tcParam.msaa ? 4 : 1;
380     constexpr uint32_t kMaxRateCount =
381         16; // SHADING_RATE_1X1_PIXELS_EXT ~ SHADING_RATE_4X4_PIXELS_EXT, actually 9 is enough
382     glw::GLenum shadingRates[kMaxRateCount];
383     glw::GLsizei count = 0;
384 
385     gl.getFragmentShadingRatesEXT(sampleCount, kMaxRateCount, &count, shadingRates);
386     GLU_EXPECT_NO_ERROR(gl.getError(), "Error to get shading rate getFragmentShadingRatesEXT");
387     DE_ASSERT(count > 0);
388 
389     for (glw::GLsizei i = 0; i < count; i++)
390     {
391         m_availableShadingRates.push_back(shadingRates[i]);
392     }
393 
394     setupTest();
395 
396     gl.shadingRateEXT(GL_SHADING_RATE_1X1_PIXELS_EXT);
397     GLU_EXPECT_NO_ERROR(gl.getError(), "Error to set shadingRateEXT as default");
398 
399     gl.clearColor(0.0f, 0.0f, 0.0f, 0.0f);
400     gl.clear(GL_COLOR_BUFFER_BIT);
401 
402     gl.useProgram(m_renderProgram->getProgram());
403     GLU_EXPECT_NO_ERROR(gl.getError(), "Error use program");
404 
405     gl.bindBuffer(GL_ARRAY_BUFFER, m_vbo_id);
406     GLU_EXPECT_NO_ERROR(gl.getError(), "Error bind buffer vertex data");
407 
408     gl.enableVertexAttribArray(0);
409     GLU_EXPECT_NO_ERROR(gl.getError(), "Error enabling vertex attrib pointer 0");
410 
411     gl.vertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, sizeof(float) * 2, nullptr);
412     GLU_EXPECT_NO_ERROR(gl.getError(), "Error binding vertex attrib pointer 0");
413 
414     // primitive ID start from 1
415     for (uint32_t drawID = 1; drawID < TRIANGLE_COUNT; drawID++)
416     {
417         const uint32_t primID            = getPrimitiveID(drawID);
418         const uint32_t packedShadingRate = fsrutils::packShadingRate(translatePrimIDToShadingRate(primID));
419         gl.uniform1i(gl.getUniformLocation(m_renderProgram->getProgram(), "primShadingRate"), packedShadingRate);
420         GLU_EXPECT_NO_ERROR(gl.getError(), "Error set uniform shadingRate value");
421 
422         gl.uniform1i(gl.getUniformLocation(m_renderProgram->getProgram(), "primID"), primID);
423         GLU_EXPECT_NO_ERROR(gl.getError(), "Error set uniform primID value");
424 
425         gl.uniform1i(gl.getUniformLocation(m_renderProgram->getProgram(), "drawID"), drawID);
426         GLU_EXPECT_NO_ERROR(gl.getError(), "Error set uniform drawID value");
427 
428         if (m_tcParam.useShadingRateAPI)
429         {
430             gl.shadingRateEXT(translateDrawIDToShadingRate(drawID));
431             GLU_EXPECT_NO_ERROR(gl.getError(), "Error set shading rate");
432         }
433 
434         gl.shadingRateCombinerOpsEXT(m_tcParam.combinerOp0, m_tcParam.combinerOp1);
435         GLU_EXPECT_NO_ERROR(gl.getError(), "Error set Shading Rate combiner operations");
436 
437         gl.drawArrays(GL_TRIANGLES, drawID * 2, 3);
438         GLU_EXPECT_NO_ERROR(gl.getError(), "Error draw a triangle");
439     }
440 
441     constexpr uint32_t kChannels = 4;
442     const uint32_t dataSize =
443         m_tcParam.framebufferSize * m_tcParam.framebufferSize * sampleCount * sizeof(uint32_t) * kChannels;
444 
445     // Copy the result color buffer to shader storage buffer to access for the msaa case
446     glw::GLuint ssbo_id;
447     gl.genBuffers(1, &ssbo_id);
448     GLU_EXPECT_NO_ERROR(gl.getError(), "Error generate buffer object");
449 
450     gl.bindBuffer(GL_SHADER_STORAGE_BUFFER, ssbo_id);
451     GLU_EXPECT_NO_ERROR(gl.getError(), "Error bind buffer object");
452 
453     gl.bufferData(GL_SHADER_STORAGE_BUFFER, dataSize, nullptr, GL_DYNAMIC_COPY);
454     GLU_EXPECT_NO_ERROR(gl.getError(), "Error allocate buffer object");
455 
456     gl.useProgram(m_computeProgram->getProgram());
457     GLU_EXPECT_NO_ERROR(gl.getError(), "Error use compute object");
458 
459     gl.bindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, ssbo_id);
460     GLU_EXPECT_NO_ERROR(gl.getError(), "Error bind buffer object to program");
461 
462     gl.uniform1i(gl.getUniformLocation(m_renderProgram->getProgram(), "colorTex"), 0);
463     GLU_EXPECT_NO_ERROR(gl.getError(), "Error bind set colorTex uniform value");
464 
465     glw::GLenum textureTarget = GL_TEXTURE_2D;
466     if (m_tcParam.msaa)
467     {
468         textureTarget = GL_TEXTURE_2D_MULTISAMPLE;
469     }
470 
471     gl.bindTexture(textureTarget, m_to_id);
472     GLU_EXPECT_NO_ERROR(gl.getError(), "Error bind texture");
473 
474     gl.dispatchCompute(m_tcParam.framebufferSize, m_tcParam.framebufferSize, 1);
475     GLU_EXPECT_NO_ERROR(gl.getError(), "Error dispatching copy compute program");
476 
477     gl.flush();
478     GLU_EXPECT_NO_ERROR(gl.getError(), "Error for flushing");
479 
480     const uint32_t *resPtr =
481         static_cast<const uint32_t *>(gl.mapBufferRange(GL_SHADER_STORAGE_BUFFER, 0, dataSize, GL_MAP_READ_BIT));
482     for (int32_t y = 0; y < m_tcParam.framebufferSize; y++)
483     {
484         for (int32_t x = 0; x < m_tcParam.framebufferSize; x++)
485         {
486             for (uint32_t s = 0; s < sampleCount; s++)
487             {
488                 const uint32_t index   = ((y * m_tcParam.framebufferSize + x) * sampleCount + s) * kChannels;
489                 const uint32_t *sample = &resPtr[index];
490                 if (sample[1] == 0) // nothing rendered
491                 {
492                     continue;
493                 }
494 
495                 const uint32_t shadingRate             = sample[0];
496                 const uint32_t drawID                  = sample[1];
497                 const uint32_t primID                  = sample[2];
498                 const uint32_t expectedShadingRateMask = simulate(drawID, primID, x, y);
499                 if (!(expectedShadingRateMask & (1 << shadingRate)))
500                 {
501                     std::stringstream error_sstream;
502 
503                     error_sstream << "The draw ID is " << drawID << "The primitive ID is " << primID
504                                   << "Shading Rate is" << shadingRate << ", But we expect one mask of "
505                                   << expectedShadingRateMask;
506 
507                     m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, error_sstream.str().c_str());
508 
509                     gl.deleteBuffers(1, &ssbo_id);
510                     return STOP;
511                 }
512             }
513         }
514     }
515 
516     gl.deleteBuffers(1, &ssbo_id);
517 
518     /* All done */
519     if (m_testCtx.getTestResult() != QP_TEST_RESULT_FAIL)
520     {
521         m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
522     }
523 
524     return STOP;
525 }
526 
527 /// Translate Primitive ID to ShadingRate enumeration
528 ///
529 /// @param primID primitive ID to translate shading rate
530 ///
531 /// @return shading rate enumeration
translateDrawIDToShadingRate(uint32_t drawID) const532 glw::GLenum FragmentShadingRateCombined::translateDrawIDToShadingRate(uint32_t drawID) const
533 {
534     return m_availableShadingRates[drawID % m_availableShadingRates.size()];
535 }
536 
537 /// Translate Primitive ID to ShadingRate enumeration
538 ///
539 /// @param primID primitive ID to translate shading rate
540 ///
541 /// @return shading rate enumeration
translatePrimIDToShadingRate(uint32_t primID) const542 glw::GLenum FragmentShadingRateCombined::translatePrimIDToShadingRate(uint32_t primID) const
543 {
544     return m_availableShadingRates[(primID * 7) % m_availableShadingRates.size()];
545 }
546 
547 /// Translate Coordinates ID to ShadingRate enumeration
548 ///
549 ///@param srx x coord in the shading rate attachment to translate shading rate
550 ///@param sry y coord in the shading rate attachment to translate shading rate
551 ///
552 ///@return shading rate enumeration
translateCoordsToShadingRate(uint32_t srx,uint32_t sry) const553 glw::GLenum FragmentShadingRateCombined::translateCoordsToShadingRate(uint32_t srx, uint32_t sry) const
554 {
555     return m_availableShadingRates[(srx + sry) % m_availableShadingRates.size()];
556 }
557 
558 /// getPrimitiveID from drawID
559 ///
560 /// @param drawID draw ID to translate
561 ///
562 /// @return primitive ID
getPrimitiveID(uint32_t drawID) const563 uint32_t FragmentShadingRateCombined::getPrimitiveID(uint32_t drawID) const
564 {
565     return drawID + 1;
566 }
567 
568 /// Map an extent to a mask of all modes smaller than or equal to it in either dimension
569 ///
570 /// @param ext extent to get available shading rate mask
571 /// @param allowSwap swap allowable between width and height
572 ///
573 /// @return shifted mask which is shifted from all candidate rates
shadingRateExtentToClampedMask(Extent2D ext,bool allowSwap) const574 uint32_t FragmentShadingRateCombined::shadingRateExtentToClampedMask(Extent2D ext, bool allowSwap) const
575 {
576     uint32_t desiredSize = ext.width * ext.height;
577 
578     uint32_t mask = 0;
579 
580     while (desiredSize > 0)
581     {
582         // First, find modes that maximize the area
583         for (uint32_t i = 0; i < m_availableShadingRates.size(); ++i)
584         {
585             uint32_t packedShadingRate = fsrutils::packShadingRate(m_availableShadingRates[i]);
586             Extent2D fragmentSize      = packedShadingRateToExtent(packedShadingRate);
587 
588             if (fragmentSize.width * fragmentSize.height == desiredSize &&
589                 ((fragmentSize.width <= ext.width && fragmentSize.height <= ext.height) ||
590                  (fragmentSize.height <= ext.width && fragmentSize.width <= ext.height && allowSwap)))
591             {
592                 uint32_t candidate = (deCtz32(fragmentSize.width) << 2) | deCtz32(fragmentSize.height);
593                 mask |= 1 << candidate;
594             }
595         }
596         if (mask)
597         {
598             // Amongst the modes that maximize the area, pick the ones that
599             // minimize the aspect ratio. Prefer ratio of 1, then 2, then 4.
600             // 1x1 = 0, 2x2 = 5, 4x4 = 10
601             static const uint32_t aspectMaskRatio1 = 0x421;
602             // 2x1 = 4, 1x2 = 1, 4x2 = 9, 2x4 = 6
603             static const uint32_t aspectMaskRatio2 = 0x252;
604             // 4x1 = 8, 1x4 = 2,
605             static const uint32_t aspectMaskRatio4 = 0x104;
606 
607             if (mask & aspectMaskRatio1)
608             {
609                 mask &= aspectMaskRatio1;
610                 break;
611             }
612             if (mask & aspectMaskRatio2)
613             {
614                 mask &= aspectMaskRatio2;
615                 break;
616             }
617             if (mask & aspectMaskRatio4)
618             {
619                 mask &= aspectMaskRatio4;
620                 break;
621             }
622             DE_ASSERT(0);
623         }
624         desiredSize /= 2;
625     }
626 
627     return mask;
628 }
629 
630 /// Software simulate to compare with GPU result
631 ///
632 /// @param drawID draw ID
633 /// @param primID primitive ID
634 /// @param x fragment coordinates X
635 /// @param y fragment coordinates Y
636 ///
637 /// @return shifted mask which is shifted from all candidate rates
simulate(uint32_t drawID,uint32_t primID,uint32_t x,uint32_t y)638 uint32_t FragmentShadingRateCombined::simulate(uint32_t drawID, uint32_t primID, uint32_t x, uint32_t y)
639 {
640 
641     const uint32_t rate0 =
642         m_tcParam.useShadingRateAPI ? fsrutils::packShadingRate(translateDrawIDToShadingRate(drawID)) : 0;
643     const uint32_t rate1 =
644         m_tcParam.useShadingRatePrimitive ? fsrutils::packShadingRate(translatePrimIDToShadingRate(primID)) : 0;
645     const uint32_t rate2 =
646         m_tcParam.useShadingRateAttachment ?
647             fsrutils::packShadingRate(translateCoordsToShadingRate(x / m_srTexelWidth, y / m_srTexelWidth)) :
648             0;
649 
650     uint32_t &cachedRate = m_simulationCache[(rate2 * kShadingRateCount + rate1) * kShadingRateCount + rate0];
651     if (cachedRate != 0xFFFFFFFF)
652     {
653         return cachedRate;
654     }
655 
656     const Extent2D extent0 = packedShadingRateToExtent(rate0);
657     const Extent2D extent1 = packedShadingRateToExtent(rate1);
658     const Extent2D extent2 = packedShadingRateToExtent(rate2);
659 
660     uint32_t finalMask = 0;
661     std::vector<bool> allowSwaps{false, true};
662     // Simulate once for implementations that don't allow swapping rate xy,
663     // and once for those that do. Any of those results is allowed.
664     for (bool allowSwap : allowSwaps)
665     {
666         // Combine rate 0 and 1, get a mask of possible clamped rates
667         Extent2D intermediate     = combine(extent0, extent1, m_tcParam.combinerOp0);
668         uint32_t intermediateMask = shadingRateExtentToClampedMask(intermediate, allowSwap);
669 
670         // For each clamped rate, combine that with rate 2 and accumulate the possible clamped rates
671         for (uint32_t i = 0; i < kShadingRateCount; ++i)
672         {
673             if (intermediateMask & (1 << i))
674             {
675                 Extent2D final = combine(packedShadingRateToExtent(i), extent2, m_tcParam.combinerOp1);
676                 finalMask |= shadingRateExtentToClampedMask(final, allowSwap);
677             }
678         }
679         {
680             // unclamped intermediate value is also permitted
681             Extent2D final = combine(intermediate, extent2, m_tcParam.combinerOp1);
682             finalMask |= shadingRateExtentToClampedMask(final, allowSwap);
683         }
684     }
685 
686     cachedRate = finalMask;
687 
688     return finalMask;
689 }
690 
691 /// translate packed rate to Extent
692 ///
693 /// @param packedRate packed with (log(width) << 2 | log(height))
694 ///
695 /// @return shading rate Extent
packedShadingRateToExtent(uint32_t packedRate) const696 FragmentShadingRateCombined::Extent2D FragmentShadingRateCombined::packedShadingRateToExtent(uint32_t packedRate) const
697 {
698     Extent2D ret = {static_cast<uint32_t>(1 << ((packedRate / 4) & 3)), static_cast<uint32_t>(1 << (packedRate & 3))};
699 
700     return ret;
701 }
702 
703 /// combine the two extent with given combine operation
704 ///
705 /// @param extent0 extent0
706 /// @param extent1 extent1
707 /// @param combineOp combination operation
708 ///
709 /// @return combined extent
combine(Extent2D extent0,Extent2D extent1,glw::GLenum combineOp) const710 FragmentShadingRateCombined::Extent2D FragmentShadingRateCombined::combine(Extent2D extent0, Extent2D extent1,
711                                                                            glw::GLenum combineOp) const
712 {
713     Extent2D resultExtent;
714 
715     switch (combineOp)
716     {
717     case GL_FRAGMENT_SHADING_RATE_COMBINER_OP_KEEP_EXT:
718         return extent0;
719     case GL_FRAGMENT_SHADING_RATE_COMBINER_OP_REPLACE_EXT:
720         return extent1;
721     case GL_FRAGMENT_SHADING_RATE_COMBINER_OP_MIN_EXT:
722         resultExtent.width  = std::min(extent0.width, extent1.width);
723         resultExtent.height = std::min(extent0.height, extent1.height);
724         break;
725     case GL_FRAGMENT_SHADING_RATE_COMBINER_OP_MAX_EXT:
726         resultExtent.width  = std::max(extent0.width, extent1.width);
727         resultExtent.height = std::max(extent0.height, extent1.height);
728         break;
729     case GL_FRAGMENT_SHADING_RATE_COMBINER_OP_MUL_EXT:
730         resultExtent.width  = extent0.width * extent1.width;
731         resultExtent.height = extent0.height * extent1.height;
732         break;
733     }
734 
735     return resultExtent;
736 }
737 
738 } // namespace glcts
739