1 /*-------------------------------------------------------------------------
2 * OpenGL Conformance Test Suite
3 * -----------------------------
4 *
5 * Copyright (c) 2024 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
21 * \brief
22 */ /*-------------------------------------------------------------------*/
23
24 /**
25 */ /*!
26 * \file glcTextureLodBasicTests.cpp
27 * \brief Conformance tests for the texture lod selection functionality.
28 */ /*-------------------------------------------------------------------*/
29
30 #include "deMath.h"
31
32 #include "glcTextureLodBasicTests.hpp"
33 #include "gluDefs.hpp"
34 #include "gluShaderProgram.hpp"
35 #include "glwEnums.hpp"
36 #include "glwFunctions.hpp"
37 #include "tcuRenderTarget.hpp"
38 #include "tcuStringTemplate.hpp"
39 #include "tcuTestLog.hpp"
40
41 using namespace glw;
42 using namespace glu;
43
44 namespace
45 {
46 // clang-format off
47 /* full screen quad */
48 const float fs_quad[] = { -1.0f, -1.0f, 0.0f, 1.0f,
49 1.0f, -1.0f, 0.0f, 1.0f,
50 -1.0f, 1.0f, 0.0f, 1.0f,
51 1.0f, 1.0f, 0.0f, 1.0f };
52
53 GLubyte colorarray[][4] = {
54 { 255, 0, 0, 255 }, // red
55 { 0, 255, 0, 255 }, // green
56 { 0, 0, 255, 255 }, // blue
57 { 127, 255, 0, 255 }, // redish green
58 };
59 // clang-format on
60
61 /* maually calculate the result of texturing */
62 /* returned to parameter r. */
colorTexturing(const glw::Functions & gl,float lodBase,float lodBiasSum,float lodMin,float lodMax,int levelBase,int levelMax,int levelBaseMaxSize,GLenum magFilter,GLenum minFilter,bool mipmap,GLubyte * colors,float * r)63 void colorTexturing(const glw::Functions &gl, float lodBase, float lodBiasSum, float lodMin, float lodMax,
64 int levelBase, int levelMax, int levelBaseMaxSize, GLenum magFilter, GLenum minFilter, bool mipmap,
65 GLubyte *colors, float *r)
66 {
67 const float maxColor = 255.0f;
68
69 float lodConstant = 0.f, lod = 0.f;
70 int d1 = 0, d2 = 0;
71
72 auto copy_pixel = [maxColor](float *dst, GLubyte *src)
73 {
74 dst[0] = static_cast<float>(src[0]) / maxColor;
75 dst[1] = static_cast<float>(src[1]) / maxColor;
76 dst[2] = static_cast<float>(src[2]) / maxColor;
77 dst[3] = static_cast<float>(src[3]) / maxColor;
78 };
79
80 if (!mipmap)
81 {
82 /* When not mipmapped, level base is used */
83 d1 = levelBase;
84 copy_pixel(r, &colors[d1 * 4]);
85 return;
86 }
87
88 /* Mipmapped */
89 /* Check the constant divide the mag or min filter */
90 if ((magFilter == GL_LINEAR) &&
91 ((minFilter == GL_NEAREST_MIPMAP_NEAREST) || (minFilter == GL_NEAREST_MIPMAP_LINEAR)))
92 {
93 lodConstant = 0.5f;
94 }
95 else
96 {
97 lodConstant = 0.0f;
98 }
99
100 /* Get final lod which is clamp */
101 lod = lodBase;
102 if (lodBiasSum != 0.0f)
103 {
104 float maxLodBias;
105 gl.getFloatv(GL_MAX_TEXTURE_LOD_BIAS, &maxLodBias);
106 GLU_EXPECT_NO_ERROR(gl.getError(), "getFloatv");
107
108 if (lodBiasSum > maxLodBias)
109 {
110 lodBiasSum = maxLodBias;
111 }
112 else if (lodBiasSum < -maxLodBias)
113 {
114 lodBiasSum = -maxLodBias;
115 }
116 lod += lodBiasSum;
117 }
118 if (lod < lodMin)
119 {
120 lod = lodMin;
121 }
122 else if (lod > lodMax)
123 {
124 lod = lodMax;
125 }
126
127 /* Check which filter is to use */
128 if (lod > lodConstant)
129 {
130 /* Min filter */
131 int p, q, tempMaxSize = 1;
132 int log2TempMaxSize = 0;
133
134 /* Calculate the max level */
135
136 while (tempMaxSize * 2 <= levelBaseMaxSize)
137 {
138 log2TempMaxSize += 1;
139 tempMaxSize <<= 1;
140 }
141
142 p = log2TempMaxSize + levelBase;
143 q = levelMax;
144 if (p < q)
145 {
146 q = p;
147 }
148
149 if ((minFilter == GL_NEAREST) || (minFilter == GL_LINEAR))
150 {
151 /* base level is used */
152 d1 = levelBase;
153 copy_pixel(r, &colors[d1 * 4]);
154 return;
155 }
156 else if ((minFilter == GL_NEAREST_MIPMAP_NEAREST) || (minFilter == GL_LINEAR_MIPMAP_NEAREST))
157 {
158 /* only one array is selected */
159 if (lod <= 0.5f)
160 {
161 d1 = levelBase;
162 }
163 else if (levelBase + lod <= q + 0.5f)
164 {
165 d1 = (int)ceil(levelBase + lod + 0.5f) - 1;
166 }
167 else
168 {
169 d1 = q;
170 }
171
172 copy_pixel(r, &colors[d1 * 4]);
173 return;
174 }
175 else
176 {
177 float fracLod;
178 int i;
179
180 /* interplate between two arrays */
181 if (levelBase + lod >= q)
182 {
183 d1 = q;
184 d2 = q;
185 }
186 else
187 {
188 d1 = (int)floor(levelBase + lod);
189 d2 = d1 + 1;
190 }
191
192 fracLod = (float)fmod(lod, 1.0f);
193
194 for (i = 0; i < 4; ++i)
195 {
196 const float d1norm = static_cast<float>(colors[d1 * 4 + i]) / maxColor;
197 const float d2norm = static_cast<float>(colors[d2 * 4 + i]) / maxColor;
198 r[i] = (1.0f - fracLod) * d1norm + fracLod * d2norm;
199 }
200
201 return;
202 }
203 }
204 else
205 {
206 /* Mag filter, base level is used */
207 d1 = levelBase;
208 copy_pixel(r, &colors[d1 * 4]);
209 return;
210 }
211 }
212
213 } // namespace
214
215 namespace glcts
216 {
217
218 // clang-format off
219 /** @brief Vertex shader source code to test vertex lookup texture lod bias. */
220 const glw::GLchar* glcts::TextureLodSelectionTestCase::m_shader_basic_vert =
221 R"(${VERSION}
222 in vec4 vertex;
223 out vec2 tex;
224
225 void main(void)
226 {
227 gl_Position = vertex;
228 tex = vertex.xy * 0.5 + 0.5;
229 }
230 )";
231
232 /** @brief Vertex shader source code to test fragment lookup texture lod bias. */
233 const glw::GLchar* glcts::TextureLodSelectionTestCase::m_shader_basic_1d_frag =
234 R"(${VERSION}
235 ${PRECISION}
236
237 in vec2 tex;
238 out vec4 frag;
239
240 uniform float scale;
241 uniform sampler1D texture0;
242
243 void main(void)
244 {
245 frag = texture(texture0, tex.x * scale);
246 }
247 )";
248
249 /** @brief Fragment shader source code to test fragment lookup texture lod bias. */
250 const glw::GLchar* glcts::TextureLodSelectionTestCase::m_shader_basic_2d_frag =
251 R"(${VERSION}
252 ${PRECISION}
253
254 in vec2 tex;
255 out vec4 frag;
256
257 uniform float scale;
258 uniform sampler2D texture0;
259
260 void main(void)
261 {
262 frag = texture(texture0, vec2(tex.x * scale, 0));
263 }
264 )";
265 // clang-format on
266
267 /** Constructor.
268 *
269 * @param context Rendering context
270 */
TextureLodSelectionTestCase(deqp::Context & context)271 TextureLodSelectionTestCase::TextureLodSelectionTestCase(deqp::Context &context)
272 : TestCase(context, "lod_selection", "Verifies texture LOD selection functionality")
273 , m_texture(0)
274 , m_vao(0)
275 , m_vbo(0)
276 , m_isContextES(false)
277 {
278 }
279
280 /** Stub deinit method. */
deinit()281 void TextureLodSelectionTestCase::deinit()
282 {
283 /* Left blank intentionally */
284 }
285
286 /** Stub init method */
init()287 void TextureLodSelectionTestCase::init()
288 {
289 const glu::RenderContext &renderContext = m_context.getRenderContext();
290 glu::GLSLVersion glslVersion = glu::getContextTypeGLSLVersion(renderContext.getType());
291 m_isContextES = glu::isContextTypeES(renderContext.getType());
292
293 specializationMap["VERSION"] = glu::getGLSLVersionDeclaration(glslVersion);
294 specializationMap["PRECISION"] = "";
295
296 if (m_isContextES)
297 {
298 specializationMap["PRECISION"] = "precision highp float;";
299 }
300 }
301
302 /** Executes test iteration.
303 *
304 * @return Returns STOP when test has finished executing, CONTINUE if more iterations are needed.
305 */
iterate()306 tcu::TestNode::IterateResult TextureLodSelectionTestCase::iterate()
307 {
308 const glw::Functions &gl = m_context.getRenderContext().getFunctions();
309
310 std::vector<GLenum> textures = {GL_TEXTURE_2D};
311 if (!m_isContextES)
312 textures.push_back(GL_TEXTURE_1D);
313
314 bool ret = true;
315
316 auto create_program = [&](const std::string &vert, const std::string &frag)
317 {
318 /* vertex shader test */
319 std::string vshader = tcu::StringTemplate(vert).specialize(specializationMap);
320 std::string fshader = tcu::StringTemplate(frag).specialize(specializationMap);
321
322 ProgramSources sources = makeVtxFragSources(vshader, fshader);
323 return new ShaderProgram(gl, sources);
324 };
325
326 for (size_t n = 0; n < textures.size(); ++n)
327 {
328 GLenum tex_target = textures[n];
329
330 if (m_isContextES && tex_target == GL_TEXTURE_1D)
331 continue;
332
333 ShaderProgram *program = nullptr;
334 if (tex_target == GL_TEXTURE_2D)
335 {
336 program = create_program(m_shader_basic_vert, m_shader_basic_2d_frag);
337 }
338 else if (tex_target == GL_TEXTURE_1D)
339 {
340 program = create_program(m_shader_basic_vert, m_shader_basic_1d_frag);
341 }
342 else
343 {
344 m_testCtx.getLog() << tcu::TestLog::Message << "Texture target not supported " << tex_target
345 << tcu::TestLog::EndMessage;
346 }
347
348 if (!program->isOk())
349 {
350 m_testCtx.getLog() << tcu::TestLog::Message << "Shader build failed.\n"
351 << "Vertex: " << program->getShaderInfo(SHADERTYPE_VERTEX).infoLog << "\n"
352 << program->getShader(SHADERTYPE_VERTEX)->getSource() << "\n"
353 << "Fragment: " << program->getShaderInfo(SHADERTYPE_FRAGMENT).infoLog << "\n"
354 << program->getShader(SHADERTYPE_FRAGMENT)->getSource() << "\n"
355 << "Program: " << program->getProgramInfo().infoLog << tcu::TestLog::EndMessage;
356 TCU_FAIL("Compile failed");
357 }
358 else
359 {
360 /* fragment shader test */
361 setBuffers(*program);
362
363 GLint locScale = gl.getUniformLocation(program->getProgram(), "scale");
364 GLU_EXPECT_NO_ERROR(gl.getError(), "getUniformLocation");
365 TCU_CHECK_MSG(locScale != -1, "scale location not valid");
366
367 createLodTexture(tex_target);
368
369 /*
370 2. Set TEXTURE_BASE_LEVEL to 1 and TEXTURE_MAX_LEVEL to 2. Render
371 with a LOD of -1, and check that level 1 is used.
372
373 3. Set TEXTURE_BASE_LEVEL to 1 and TEXTURE_MAX_LEVEL to 2. Render
374 with a LOD of 3, and check that level 2 is used.
375
376 4. Set TEXTURE_BASE_LEVEL to 1 and TEXTURE_MAX_LEVEL to 2. Render
377 with a LOD of 0.5, and check that this is correctly interpolated.
378 */
379
380 if (!drawAndVerify(locScale, -1.0f, 1, 2, 4, 0.0f, -1000.0f, 1000.0f, GL_LINEAR, GL_LINEAR_MIPMAP_LINEAR,
381 true))
382 {
383 ret = false;
384 }
385
386 if (!drawAndVerify(locScale, 3.0f, 1, 2, 4, 0.0f, -1000.0f, 1000.0f, GL_LINEAR, GL_LINEAR_MIPMAP_LINEAR,
387 true))
388 {
389 ret = false;
390 }
391
392 if (!drawAndVerify(locScale, 0.5f, 1, 2, 4, 0.0f, -1000.0f, 1000.0f, GL_LINEAR, GL_LINEAR_MIPMAP_LINEAR,
393 true))
394 {
395 ret = false;
396 }
397
398 /*
399 5. Set TEXTURE_BASE_LEVEL to 1, TEXTURE_MAX_LEVEL to 2,
400 TEXTURE_MIN_LOD to 0.5, TEXTURE_MAX_LOD to 1000. Render with a LOD
401 of 0, and check that it is clamped to 0.5.
402 */
403 gl.texParameterf(tex_target, GL_TEXTURE_MIN_LOD, 0.5f);
404 GLU_EXPECT_NO_ERROR(gl.getError(), "texParameterf");
405 gl.texParameterf(tex_target, GL_TEXTURE_MAX_LOD, 1000.0f);
406 GLU_EXPECT_NO_ERROR(gl.getError(), "texParameterf");
407 if (!drawAndVerify(locScale, 0.0f, 1, 2, 4, 0.0f, 0.5f, 1000.0f, GL_LINEAR, GL_LINEAR_MIPMAP_LINEAR, true))
408 {
409 ret = false;
410 }
411
412 /*
413 6. Set TEXTURE_BASE_LEVEL to 1, TEXTURE_MAX_LEVEL to 2,
414 TEXTURE_MIN_LOD to -1000, TEXTURE_MAX_LOD to 0.5. Render with a LOD
415 of 1, and check that it is clamped to 0.5.
416 */
417 gl.texParameterf(tex_target, GL_TEXTURE_MIN_LOD, -1000.0f);
418 GLU_EXPECT_NO_ERROR(gl.getError(), "texParameterf");
419 gl.texParameterf(tex_target, GL_TEXTURE_MAX_LOD, 0.5f);
420 GLU_EXPECT_NO_ERROR(gl.getError(), "texParameterf");
421 if (!drawAndVerify(locScale, 1.0f, 1, 2, 4, 0.0f, -1000.0f, 0.5f, GL_LINEAR, GL_LINEAR_MIPMAP_LINEAR, true))
422 {
423 ret = false;
424 }
425
426 // Release resources
427 if (program)
428 delete program;
429
430 releaseBuffers();
431 releaseTexture();
432 }
433 }
434
435 if (ret)
436 m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
437 else
438 m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Fail");
439 return STOP;
440 }
441
442 /* function activates the program that is given as a argument */
443 /* and sets vertex and texture attributes */
setBuffers(const glu::ShaderProgram & program)444 void TextureLodSelectionTestCase::setBuffers(const glu::ShaderProgram &program)
445 {
446 if (program.isOk())
447 {
448 const glw::Functions &gl = m_context.getRenderContext().getFunctions();
449
450 gl.genVertexArrays(1, &m_vao);
451 GLU_EXPECT_NO_ERROR(gl.getError(), "genVertexArrays");
452 gl.bindVertexArray(m_vao);
453 GLU_EXPECT_NO_ERROR(gl.getError(), "bindVertexArray");
454
455 gl.genBuffers(1, &m_vbo);
456 GLU_EXPECT_NO_ERROR(gl.getError(), "genBuffers");
457 gl.bindBuffer(GL_ARRAY_BUFFER, m_vbo);
458 GLU_EXPECT_NO_ERROR(gl.getError(), "bindBuffer");
459
460 gl.bufferData(GL_ARRAY_BUFFER, sizeof(fs_quad), (GLvoid *)fs_quad, GL_DYNAMIC_DRAW);
461 GLU_EXPECT_NO_ERROR(gl.getError(), "bufferData");
462
463 GLint locVertices = -1, locTexture = -1;
464
465 gl.useProgram(program.getProgram());
466 GLU_EXPECT_NO_ERROR(gl.getError(), "useProgram");
467
468 locVertices = gl.getAttribLocation(program.getProgram(), "vertex");
469 GLU_EXPECT_NO_ERROR(gl.getError(), "getAttribLocation");
470 if (locVertices != -1)
471 {
472 gl.enableVertexAttribArray(0);
473 GLU_EXPECT_NO_ERROR(gl.getError(), "enableVertexAttribArray");
474
475 GLuint strideSize = sizeof(fs_quad) / 4;
476
477 gl.vertexAttribPointer(locVertices, 4, GL_FLOAT, GL_FALSE, strideSize, nullptr);
478 GLU_EXPECT_NO_ERROR(gl.getError(), "vertexAttribPointer");
479 }
480
481 locTexture = gl.getUniformLocation(program.getProgram(), "texture0");
482 GLU_EXPECT_NO_ERROR(gl.getError(), "getUniformLocation");
483 if (locTexture != -1)
484 {
485 gl.uniform1i(locTexture, 0);
486 GLU_EXPECT_NO_ERROR(gl.getError(), "uniform1i");
487 }
488 }
489 }
490
491 /* function releases vertex buffers */
releaseBuffers()492 void TextureLodSelectionTestCase::releaseBuffers()
493 {
494 const glw::Functions &gl = m_context.getRenderContext().getFunctions();
495 gl.disableVertexAttribArray(0);
496 GLU_EXPECT_NO_ERROR(gl.getError(), "disableVertexAttribArray");
497
498 if (m_vbo)
499 {
500 gl.deleteBuffers(1, &m_vbo);
501 GLU_EXPECT_NO_ERROR(gl.getError(), "deleteBuffers");
502 m_vbo = 0;
503 }
504
505 if (m_vao)
506 {
507 gl.deleteVertexArrays(1, &m_vao);
508 GLU_EXPECT_NO_ERROR(gl.getError(), "deleteVertexArrays");
509 m_vao = 0;
510 }
511 }
512
513 /** Texture is generated from constant color array.
514 */
createLodTexture(const GLenum target)515 void TextureLodSelectionTestCase::createLodTexture(const GLenum target)
516 {
517 const glw::Functions &gl = m_context.getRenderContext().getFunctions();
518
519 gl.genTextures(1, &m_texture);
520 GLU_EXPECT_NO_ERROR(gl.getError(), "genTextures");
521
522 gl.bindTexture(target, m_texture);
523 GLU_EXPECT_NO_ERROR(gl.getError(), "bindTexture");
524
525 gl.viewport(0, 0, 4, 4);
526 GLU_EXPECT_NO_ERROR(gl.getError(), "viewport");
527 /*
528 1. Create a texture with 8x8, 4x4, 2x2 and 1x1 images in levels 0, 1, 2
529 and 3 respectively, with consistent formats and types. Set
530 TEXTURE_MAG_FILTER to LINEAR and TEXTURE_MIN_FILTER to
531 LINEAR_MIPMAP_LINEAR.
532 */
533 for (int i = 0; i < 4; ++i)
534 {
535 createSolidTexture(target, i, 8 >> i, colorarray[i]);
536 }
537
538 gl.texParameteri(target, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
539 GLU_EXPECT_NO_ERROR(gl.getError(), "texParameteri");
540
541 gl.texParameteri(target, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
542 GLU_EXPECT_NO_ERROR(gl.getError(), "texParameteri");
543
544 gl.texParameterf(target, GL_TEXTURE_BASE_LEVEL, 1.0f);
545 GLU_EXPECT_NO_ERROR(gl.getError(), "texParameterf");
546
547 gl.texParameterf(target, GL_TEXTURE_MAX_LEVEL, 2.0f);
548 GLU_EXPECT_NO_ERROR(gl.getError(), "texParameterf");
549
550 gl.texParameterf(target, GL_TEXTURE_MIN_LOD, -1000.0f);
551 GLU_EXPECT_NO_ERROR(gl.getError(), "texParameterf");
552
553 gl.texParameterf(target, GL_TEXTURE_MAX_LOD, 1000.0f);
554 GLU_EXPECT_NO_ERROR(gl.getError(), "texParameterf");
555 }
556
557 /* Creates a texture that has solid color in given lod level */
createSolidTexture(const GLenum tex_target,const int level,const int size,const GLubyte * const color)558 void TextureLodSelectionTestCase::createSolidTexture(const GLenum tex_target, const int level, const int size,
559 const GLubyte *const color)
560 {
561 const glw::Functions &gl = m_context.getRenderContext().getFunctions();
562 std::vector<GLubyte> data(size * size * 4);
563
564 for (int i = 0; i < size * size; ++i)
565 {
566 data[i * 4 + 0] = color[0];
567 data[i * 4 + 1] = color[1];
568 data[i * 4 + 2] = color[2];
569 data[i * 4 + 3] = color[3];
570 }
571
572 if (tex_target == GL_TEXTURE_2D)
573 {
574 gl.texImage2D(tex_target, level, GL_RGBA8, size, size, 0, GL_RGBA, GL_UNSIGNED_BYTE, data.data());
575 GLU_EXPECT_NO_ERROR(gl.getError(), "texImage2D");
576 }
577 else if (!m_isContextES && tex_target == GL_TEXTURE_1D)
578 {
579 gl.texImage1D(tex_target, level, GL_RGBA8, size, 0, GL_RGBA, GL_UNSIGNED_BYTE, data.data());
580 GLU_EXPECT_NO_ERROR(gl.getError(), "texImage1D");
581 }
582 }
583
584 /*
585 Function draws a quad using given lod. Minlod and maxlod values are used
586 to calculate reference value.
587 */
drawAndVerify(const GLint locScale,const float lodLevel,const int levelBase,const int levelMax,const int levelBaseMaxSize,const float lodBiasshader,const float lodMin,const float lodMax,const GLenum magFilter,const GLenum minFilter,const bool mipmap)588 bool TextureLodSelectionTestCase::drawAndVerify(const GLint locScale, const float lodLevel, const int levelBase,
589 const int levelMax, const int levelBaseMaxSize,
590 const float lodBiasshader, const float lodMin, const float lodMax,
591 const GLenum magFilter, const GLenum minFilter, const bool mipmap)
592 {
593 const glw::Functions &gl = m_context.getRenderContext().getFunctions();
594 /* is this selection ok? */
595 float scale = 8.0f * (float)pow(2.0f, lodLevel) / 8.0f;
596
597 float result[4] = {0, 0, 0, 0};
598
599 gl.uniform1f(locScale, scale);
600 GLU_EXPECT_NO_ERROR(gl.getError(), "uniform1f");
601
602 gl.clearColor(0.0f, 0.0f, 0.0f, 1.0f);
603 GLU_EXPECT_NO_ERROR(gl.getError(), "clearColor");
604
605 gl.clear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
606 GLU_EXPECT_NO_ERROR(gl.getError(), "clear");
607
608 gl.drawArrays(GL_TRIANGLE_STRIP, 0, 4);
609 GLU_EXPECT_NO_ERROR(gl.getError(), "drawArrays");
610
611 /* Expected result */
612 colorTexturing(gl, lodLevel, lodBiasshader, lodMin, lodMax, levelBase, levelMax, levelBaseMaxSize, magFilter,
613 minFilter, mipmap, (GLubyte *)colorarray, result);
614
615 /* Result of comparison of two arrays are returned */
616 return doComparison(4, result);
617 }
618
619 /* Compares given expected result and framebuffer output. Pixel epsilon is one. */
doComparison(const int size,const float * const expectedcolor)620 bool TextureLodSelectionTestCase::doComparison(const int size, const float *const expectedcolor)
621 {
622 const glw::Functions &gl = m_context.getRenderContext().getFunctions();
623 bool ret = true;
624
625 std::vector<GLuint> data(size * size);
626 data.assign(data.size(), 0u);
627
628 /* one pixel is read */
629 const tcu::PixelFormat &pixelFormat = m_context.getRenderTarget().getPixelFormat();
630 const bool use10Bits = ((pixelFormat.redBits == 10) && (pixelFormat.greenBits == 10) &&
631 (pixelFormat.blueBits == 10) && (pixelFormat.alphaBits == 2 || pixelFormat.alphaBits == 0));
632 GLenum type = (use10Bits ? GL_UNSIGNED_INT_2_10_10_10_REV : GL_UNSIGNED_BYTE);
633 const auto numChannels = (pixelFormat.alphaBits == 0 ? 3 : 4);
634
635 gl.readPixels(0, 0, size, size, GL_RGBA, type, data.data());
636 GLU_EXPECT_NO_ERROR(gl.getError(), "readPixels");
637
638 GLint col_bits[] = {pixelFormat.redBits, pixelFormat.greenBits, pixelFormat.blueBits, pixelFormat.alphaBits};
639
640 auto calcEpsilon = [](const GLint bits)
641 {
642 auto zero = ldexp(1.f, -13);
643 GLfloat e = zero;
644 if (bits != 0)
645 {
646 e = (1.0f / (ldexp(1.0f, bits) - 1.0f)) + zero;
647 if (e > 1.0f)
648 e = 1.f;
649 }
650 return e;
651 };
652
653 float epsilon[4] = {0.0f, 0.0f, 0.0f, 0.0f};
654 for (int i = 0; i < numChannels; ++i)
655 epsilon[i] = calcEpsilon(col_bits[i]);
656
657 for (int i = 0; i < size * size; ++i)
658 {
659 const GLuint &pixel = data.at(i);
660 const GLubyte *pxBytes = reinterpret_cast<const GLubyte *>(&pixel);
661 float resultColor[4] = {0.0f, 0.0f, 0.0f, 0.0f};
662
663 if (use10Bits)
664 {
665 // Note this is a strange way to store RGB10A2 but it matches what implementations do.
666 resultColor[0] = static_cast<float>(pixel & 0x3FF) / 1023.0f;
667 resultColor[1] = static_cast<float>((pixel >> 10) & 0x3FF) / 1023.0f;
668 resultColor[2] = static_cast<float>((pixel >> 20) & 0x3FF) / 1023.0f;
669 resultColor[3] = static_cast<float>((pixel >> 30) & 0x3) / 3.0f;
670 }
671 else
672 {
673 // If not 10-bit then we already converted to 8-bit (UNSIGNED_BYTE) in the ReadPixels call, above.
674 resultColor[0] = static_cast<float>(pxBytes[0]) / 255.0f;
675 resultColor[1] = static_cast<float>(pxBytes[1]) / 255.0f;
676 resultColor[2] = static_cast<float>(pxBytes[2]) / 255.0f;
677 resultColor[3] = static_cast<float>(pxBytes[3]) / 255.0f;
678 }
679
680 for (int j = 0; j < numChannels; ++j)
681 {
682 if (std::abs(resultColor[j] - expectedcolor[j]) > epsilon[j])
683 {
684 m_testCtx.getLog() << tcu::TestLog::Message
685 << "TextureLodSelectionTestCase: Unexpected result of color comparison at pixel "
686 << i << ": " << expectedcolor[0] << " " << expectedcolor[1] << " "
687 << expectedcolor[2] << " " << expectedcolor[3] << " != " << resultColor[0] << " "
688 << resultColor[1] << " " << resultColor[2] << " " << resultColor[3]
689 << tcu::TestLog::EndMessage;
690 ret = false;
691 break;
692 }
693 }
694 }
695
696 return ret;
697 }
698
699 /** Release texture.
700 *
701 * @param gl OpenGL functions wrapper
702 */
releaseTexture()703 void TextureLodSelectionTestCase::releaseTexture()
704 {
705 const glw::Functions &gl = m_context.getRenderContext().getFunctions();
706 if (m_texture)
707 {
708 gl.deleteTextures(1, &m_texture);
709 GLU_EXPECT_NO_ERROR(gl.getError(), "deleteTextures");
710 }
711
712 m_texture = 0;
713 }
714
715 /** Constructor.
716 *
717 * @param context Rendering context.
718 */
TextureLodBasicTests(deqp::Context & context)719 TextureLodBasicTests::TextureLodBasicTests(deqp::Context &context)
720 : TestCaseGroup(context, "texture_lod_basic", "Verify conformance of texture lod basic functionality")
721 {
722 }
723
724 /** Initializes the test group contents. */
init()725 void TextureLodBasicTests::init()
726 {
727 addChild(new TextureLodSelectionTestCase(m_context));
728 }
729
730 } // namespace glcts
731