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 deUint32 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 deUint32 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 deUint32 srWidth = (m_tcParam.framebufferSize + m_srTexelWidth - 1) / m_srTexelWidth;
315 const deUint32 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<deUint8> attachmentShadingRateData;
328 attachmentShadingRateData.reserve(srWidth * srHeight);
329 for (deUint32 sry = 0; sry < srHeight; sry++)
330 {
331 for (deUint32 srx = 0; srx < srWidth; srx++)
332 {
333 deUint8 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 deUint32 kVerticesCount = (TRIANGLE_COUNT * 3 * 2);
350 float randomVertices[kVerticesCount];
351
352 deRandom rnd;
353 deRandom_init(&rnd, 0);
354 for (deUint32 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 deUint32 sampleCount = m_tcParam.msaa ? 4 : 1;
380 constexpr deUint32 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 (deUint32 drawID = 1; drawID < TRIANGLE_COUNT; drawID++)
416 {
417 const deUint32 primID = getPrimitiveID(drawID);
418 const deUint32 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 deUint32 kChannels = 4;
442 const deUint32 dataSize =
443 m_tcParam.framebufferSize * m_tcParam.framebufferSize * sampleCount * sizeof(deUint32) * 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 deUint32* resPtr =
481 static_cast<const deUint32*>(gl.mapBufferRange(GL_SHADER_STORAGE_BUFFER, 0, dataSize, GL_MAP_READ_BIT));
482 for (deInt32 y = 0; y < m_tcParam.framebufferSize; y++)
483 {
484 for (deInt32 x = 0; x < m_tcParam.framebufferSize; x++)
485 {
486 for (deUint32 s = 0; s < sampleCount; s++)
487 {
488 const deUint32 index = ((y * m_tcParam.framebufferSize + x) * sampleCount + s) * kChannels;
489 const deUint32* sample = &resPtr[index];
490 if (sample[1] == 0) // nothing rendered
491 {
492 continue;
493 }
494
495 const deUint32 shadingRate = sample[0];
496 const deUint32 drawID = sample[1];
497 const deUint32 primID = sample[2];
498 const deUint32 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(deUint32 drawID) const532 glw::GLenum FragmentShadingRateCombined::translateDrawIDToShadingRate(deUint32 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(deUint32 primID) const542 glw::GLenum FragmentShadingRateCombined::translatePrimIDToShadingRate(deUint32 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(deUint32 srx,deUint32 sry) const553 glw::GLenum FragmentShadingRateCombined::translateCoordsToShadingRate(deUint32 srx, deUint32 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(deUint32 drawID) const563 deUint32 FragmentShadingRateCombined::getPrimitiveID(deUint32 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 deUint32 FragmentShadingRateCombined::shadingRateExtentToClampedMask(Extent2D ext, bool allowSwap) const
575 {
576 deUint32 desiredSize = ext.width * ext.height;
577
578 deUint32 mask = 0;
579
580 while (desiredSize > 0)
581 {
582 // First, find modes that maximize the area
583 for (deUint32 i = 0; i < m_availableShadingRates.size(); ++i)
584 {
585 deUint32 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 deUint32 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 deUint32 aspectMaskRatio1 = 0x421;
602 // 2x1 = 4, 1x2 = 1, 4x2 = 9, 2x4 = 6
603 static const deUint32 aspectMaskRatio2 = 0x252;
604 // 4x1 = 8, 1x4 = 2,
605 static const deUint32 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(deUint32 drawID,deUint32 primID,deUint32 x,deUint32 y)638 deUint32 FragmentShadingRateCombined::simulate(deUint32 drawID, deUint32 primID, deUint32 x, deUint32 y)
639 {
640
641 const deUint32 rate0 =
642 m_tcParam.useShadingRateAPI ? fsrutils::packShadingRate(translateDrawIDToShadingRate(drawID)) : 0;
643 const deUint32 rate1 =
644 m_tcParam.useShadingRatePrimitive ? fsrutils::packShadingRate(translatePrimIDToShadingRate(primID)) : 0;
645 const deUint32 rate2 =
646 m_tcParam.useShadingRateAttachment ?
647 fsrutils::packShadingRate(translateCoordsToShadingRate(x / m_srTexelWidth, y / m_srTexelWidth)) :
648 0;
649
650 deUint32& 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 deUint32 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 deUint32 intermediateMask = shadingRateExtentToClampedMask(intermediate, allowSwap);
669
670 // For each clamped rate, combine that with rate 2 and accumulate the possible clamped rates
671 for (deUint32 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(deUint32 packedRate) const696 FragmentShadingRateCombined::Extent2D FragmentShadingRateCombined::packedShadingRateToExtent(deUint32 packedRate) const
697 {
698 Extent2D ret = { static_cast<deUint32>(1 << ((packedRate / 4) & 3)), static_cast<deUint32>(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