• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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