1 /*-------------------------------------------------------------------------
2  * OpenGL Conformance Test Suite
3  * -----------------------------
4  *
5  * Copyright (c) 2015-2016 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  * \file  esextcDrawBuffersIndexedBlending.hpp
26  * \brief Draw Buffers Indexed tests 5. Blending
27  */ /*-------------------------------------------------------------------*/
28 
29 #include "esextcDrawBuffersIndexedBlending.hpp"
30 #include "gluPixelTransfer.hpp"
31 #include "gluShaderProgram.hpp"
32 #include "tcuTestLog.hpp"
33 #include <cmath>
34 
35 namespace glcts
36 {
37 
38 /** Constructor
39  *
40  *  @param context     Test context
41  *  @param name        Test case's name
42  *  @param description Test case's description
43  **/
DrawBuffersIndexedBlending(Context & context,const ExtParameters & extParams,const char * name,const char * description)44 DrawBuffersIndexedBlending::DrawBuffersIndexedBlending(Context &context, const ExtParameters &extParams,
45                                                        const char *name, const char *description)
46     : DrawBuffersIndexedBase(context, extParams, name, description)
47     , m_fbo(0)
48 {
49     /* Left blank on purpose */
50 }
51 
prepareFramebuffer()52 void DrawBuffersIndexedBlending::prepareFramebuffer()
53 {
54     const glw::Functions &gl = m_context.getRenderContext().getFunctions();
55 
56     glw::GLint maxDrawBuffers = 0;
57     gl.getIntegerv(GL_MAX_DRAW_BUFFERS, &maxDrawBuffers);
58     if (maxDrawBuffers < 4)
59     {
60         throw tcu::ResourceError("Minimum number of draw buffers too low");
61     }
62 
63     gl.genFramebuffers(1, &m_fbo);
64     gl.bindFramebuffer(GL_FRAMEBUFFER, m_fbo);
65 
66     std::vector<glw::GLenum> bufs(maxDrawBuffers);
67     for (int i = 0; i < maxDrawBuffers; ++i)
68     {
69         bufs[i] = GL_COLOR_ATTACHMENT0 + i;
70     }
71     gl.drawBuffers(maxDrawBuffers, &bufs[0]);
72 }
73 
releaseFramebuffer()74 void DrawBuffersIndexedBlending::releaseFramebuffer()
75 {
76     const glw::Functions &gl = m_context.getRenderContext().getFunctions();
77 
78     glw::GLint maxDrawBuffers = 0;
79     gl.getIntegerv(GL_MAX_DRAW_BUFFERS, &maxDrawBuffers);
80     if (maxDrawBuffers < 4)
81     {
82         throw tcu::ResourceError("Minimum number of draw buffers too low");
83     }
84 
85     BlendMaskStateMachine state(m_context, m_testCtx.getLog(), maxDrawBuffers);
86     state.SetDefaults();
87     gl.deleteFramebuffers(1, &m_fbo);
88     gl.bindFramebuffer(GL_FRAMEBUFFER, 0);
89     glw::GLenum bufs[1] = {GL_BACK};
90     gl.drawBuffers(1, bufs);
91     gl.readBuffer(GL_BACK);
92 }
93 
iterate()94 tcu::TestNode::IterateResult DrawBuffersIndexedBlending::iterate()
95 {
96     static const glw::GLenum BlendFormats[] = {
97         GL_R8, GL_RG8, GL_RGB8, GL_RGB565, GL_RGBA4, GL_RGBA8,
98     };
99     static const int kSize       = 32;
100     static unsigned int formatId = 0;
101 
102     const glw::Functions &gl = m_context.getRenderContext().getFunctions();
103     glw::GLenum format       = BlendFormats[formatId];
104 
105     prepareFramebuffer();
106 
107     // Check number of available draw buffers
108     glw::GLint maxDrawBuffers = 0;
109     gl.getIntegerv(GL_MAX_DRAW_BUFFERS, &maxDrawBuffers);
110     if (maxDrawBuffers < 4)
111     {
112         m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Minimum number of draw buffers too low");
113         return STOP;
114     }
115 
116     // Prepare render targets
117     glw::GLuint tex;
118     gl.genTextures(1, &tex);
119     gl.bindTexture(GL_TEXTURE_2D_ARRAY, tex);
120     gl.texStorage3D(GL_TEXTURE_2D_ARRAY, 1, format, kSize, kSize, maxDrawBuffers);
121     for (int i = 0; i < maxDrawBuffers; ++i)
122     {
123         gl.framebufferTextureLayer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + i, tex, 0, i);
124     }
125 
126     // Clear background color
127     tcu::Vec4 background(0.5f, 0.5f, 0.5f, 0.5f);
128     for (int i = 0; i < maxDrawBuffers; ++i)
129     {
130         gl.clearBufferfv(GL_COLOR, i, &background[0]);
131     }
132 
133     // Prepare expected, blended color values
134     tcu::Vec4 colors[] = {tcu::Vec4(0.86f, 0.22f, 0.31f, 0.45f), tcu::Vec4(0.12f, 0.83f, 0.34f, 0.42f),
135                           tcu::Vec4(0.56f, 0.63f, 0.76f, 0.99f), tcu::Vec4(0.14f, 0.34f, 0.34f, 0.22f)};
136 
137     int numComponents    = NumComponents(format);
138     tcu::RGBA expected[] = {
139         // GL_MIN
140         tcu::RGBA(static_cast<unsigned int>(background.x() * 255),
141                   static_cast<unsigned int>((numComponents >= 2 ? colors[0].y() : 0.0f) * 255),
142                   static_cast<unsigned int>((numComponents >= 3 ? colors[0].z() : 0.0f) * 255),
143                   static_cast<unsigned int>((numComponents == 4 ? background.w() : 1.0f) * 255)),
144         // GL_FUNC_ADD
145         tcu::RGBA(static_cast<unsigned int>(background.x() * 255),
146                   static_cast<unsigned int>((numComponents >= 2 ? background.y() : 0.0f) * 255),
147                   static_cast<unsigned int>((numComponents >= 3 ? background.z() : 0.0f) * 255),
148                   static_cast<unsigned int>((numComponents == 4 ? colors[1].w() : 1.0f) * 255)),
149         // GL_FUNC_SUBTRACT
150         tcu::RGBA(
151             static_cast<unsigned int>((colors[2].x() * (numComponents == 4 ? colors[2].w() : 1.0f) -
152                                        background.x() * (numComponents == 4 ? background.w() : 1.0f)) *
153                                       255),
154             static_cast<unsigned int>((numComponents >= 2 ?
155                                            (colors[2].y() * (numComponents == 4 ? colors[2].w() : 1.0f) -
156                                             background.y() * (numComponents == 4 ? background.w() : 1.0f)) :
157                                            0.0f) *
158                                       255),
159             static_cast<unsigned int>((numComponents >= 3 ?
160                                            (colors[2].z() * (numComponents == 4 ? colors[2].w() : 1.0f) -
161                                             background.z() * (numComponents == 4 ? background.w() : 1.0f)) :
162                                            0.0f) *
163                                       255),
164             static_cast<unsigned int>(
165                 (numComponents == 4 ? (colors[2].w() * colors[2].w() - background.w() * background.w()) : 1.0f) * 255)),
166         // GL_FUNC_REVERSE_SUBTRACT
167         tcu::RGBA(static_cast<unsigned int>((background.x() - colors[3].x()) * 255),
168                   static_cast<unsigned int>((numComponents >= 2 ? (background.y() - colors[3].y()) : 0.0f) * 255),
169                   static_cast<unsigned int>((numComponents >= 3 ? (background.z() - colors[3].z()) : 0.0f) * 255),
170                   static_cast<unsigned int>((numComponents == 4 ? (background.w() - colors[3].w()) : 1.0f) * 255))};
171 
172     // Setup blending operations
173     BlendMaskStateMachine state(m_context, m_testCtx.getLog(), maxDrawBuffers);
174     for (int i = 0; i < maxDrawBuffers; ++i)
175     {
176         switch (i % 4)
177         {
178         case 0:
179             // GL_MIN
180             state.SetEnablei(i);
181             state.SetBlendEquationSeparatei(i, GL_MIN, GL_MAX);
182             state.SetBlendFunci(i, GL_ONE, GL_ONE);
183             break;
184         case 1:
185             // GL_FUNC_ADD
186             state.SetEnablei(i);
187             state.SetBlendEquationi(i, GL_FUNC_ADD);
188             state.SetBlendFuncSeparatei(i, GL_ZERO, GL_ONE, GL_ONE, GL_ZERO);
189             break;
190         case 2:
191             // GL_FUNC_SUBTRACT
192             state.SetEnablei(i);
193             state.SetBlendEquationi(i, GL_FUNC_SUBTRACT);
194             state.SetBlendFunci(i, GL_SRC_ALPHA, GL_DST_ALPHA);
195             break;
196         case 3:
197             // GL_FUNC_REVERSE_SUBTRACT
198             state.SetEnablei(i);
199             state.SetBlendEquationi(i, GL_FUNC_REVERSE_SUBTRACT);
200             state.SetBlendFunci(i, GL_ONE, GL_ONE);
201             break;
202         }
203     }
204 
205     // Prepare shader programs and draw fullscreen quad
206     glu::ShaderProgram program(m_context.getRenderContext(),
207                                glu::makeVtxFragSources(GenVS().c_str(), GenFS(maxDrawBuffers).c_str()));
208     if (!program.isOk())
209     {
210         m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Could not create shader program");
211         return STOP;
212     }
213     gl.useProgram(program.getProgram());
214 
215     glw::GLuint positionLocation = gl.getAttribLocation(program.getProgram(), "position");
216     tcu::Vec3 vertices[] = {tcu::Vec3(-1.0f, -1.0f, 0.0f), tcu::Vec3(1.0f, -1.0f, 0.0f), tcu::Vec3(-1.0f, 1.0f, 0.0f),
217                             tcu::Vec3(1.0f, 1.0f, 0.0f),   tcu::Vec3(-1.0f, 1.0f, 0.0f), tcu::Vec3(1.0f, -1.0f, 0.0f)};
218 
219     gl.vertexAttribPointer(positionLocation, 3, GL_FLOAT, GL_FALSE, 0, vertices);
220     gl.enableVertexAttribArray(positionLocation);
221 
222     for (int i = 0; i < maxDrawBuffers; ++i)
223     {
224         std::ostringstream os;
225         os << "c" << i;
226         // i.e.: glUniform4fv(glGetUniformLocation(m_program, "c0"), 1, &colors[i].r);
227         gl.uniform4fv(gl.getUniformLocation(program.getProgram(), os.str().c_str()), 1, &colors[i % 4][0]);
228     }
229 
230     gl.drawArrays(GL_TRIANGLES, 0, 6);
231 
232     // Read buffer colors and validate proper blending behaviour
233     bool success      = true;
234     tcu::RGBA epsilon = GetEpsilon();
235     for (int i = 0; i < maxDrawBuffers; ++i)
236     {
237         gl.readBuffer(GL_COLOR_ATTACHMENT0 + i);
238 
239         tcu::TextureLevel textureLevel(tcu::TextureFormat(tcu::TextureFormat::RGBA, tcu::TextureFormat::UNORM_INT8),
240                                        kSize, kSize);
241         glu::readPixels(m_context.getRenderContext(), 0, 0, textureLevel.getAccess());
242 
243         if (!VerifyImg(textureLevel, expected[i % 4], epsilon))
244         {
245             m_testCtx.getLog() << tcu::TestLog::Message << "Blending error in texture format " << format
246                                << " occurred for draw buffer #" << i << "\n"
247                                << tcu::TestLog::EndMessage;
248             m_testCtx.getLog() << tcu::TestLog::Image("Result", "Rendered result image", textureLevel.getAccess());
249             success = false;
250         }
251     }
252 
253     gl.disable(GL_BLEND);
254     gl.useProgram(0);
255     gl.bindTexture(GL_TEXTURE_2D_ARRAY, 0);
256     gl.deleteTextures(1, &tex);
257     releaseFramebuffer();
258 
259     // Check for error
260     glw::GLenum error_code = gl.getError();
261     if (error_code != GL_NO_ERROR)
262     {
263         m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Some functions generated error");
264         formatId = 0;
265         return STOP;
266     }
267 
268     if (!success)
269     {
270         m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Blending error occurred");
271         formatId = 0;
272         return STOP;
273     }
274     else
275     {
276         ++formatId;
277         if (formatId < (sizeof(BlendFormats) / sizeof(BlendFormats[0])))
278         {
279             return CONTINUE;
280         }
281         else
282         {
283             m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
284             formatId = 0;
285             return STOP;
286         }
287     }
288 }
289 
GenVS()290 std::string DrawBuffersIndexedBlending::GenVS()
291 {
292     std::ostringstream os;
293     os << "#version 300 es                        \n"
294           "precision highp float;                 \n"
295           "precision highp int;                   \n"
296           "layout(location = 0) in vec4 position; \n"
297           "void main() {                          \n"
298           "    gl_Position = position;            \n"
299           "}";
300     return os.str();
301 }
GenFS(int maxDrawBuffers)302 std::string DrawBuffersIndexedBlending::GenFS(int maxDrawBuffers)
303 {
304     std::ostringstream os;
305     os << "#version 300 es                        \n"
306           "precision highp float;                 \n"
307           "precision highp int;                   \n";
308 
309     for (int i = 0; i < maxDrawBuffers; ++i)
310     {
311         os << "\nlayout(location = " << i << ") out vec4 color" << i << ";";
312     }
313     for (int i = 0; i < maxDrawBuffers; ++i)
314     {
315         os << "\nuniform vec4 c" << i << ";";
316     }
317 
318     os << "\nvoid main() {";
319 
320     for (int i = 0; i < maxDrawBuffers; ++i)
321     {
322         os << "\n    color" << i << " = c" << i << ";";
323     }
324 
325     os << "\n}";
326     return os.str();
327 }
328 
NumComponents(glw::GLenum format)329 unsigned int DrawBuffersIndexedBlending::NumComponents(glw::GLenum format)
330 {
331     switch (format)
332     {
333     case GL_R8:
334     case GL_R8I:
335     case GL_R8UI:
336     case GL_R16I:
337     case GL_R16UI:
338     case GL_R32I:
339     case GL_R32UI:
340         return 1;
341     case GL_RG8:
342     case GL_RG8I:
343     case GL_RG8UI:
344     case GL_RG16I:
345     case GL_RG16UI:
346     case GL_RG32I:
347     case GL_RG32UI:
348         return 2;
349     case GL_RGB8:
350     case GL_RGB565:
351         return 3;
352     case GL_RGBA4:
353     case GL_RGB5_A1:
354     case GL_RGBA8:
355     case GL_RGB10_A2:
356     case GL_RGBA8I:
357     case GL_RGBA8UI:
358     case GL_RGBA16I:
359     case GL_RGBA16UI:
360     case GL_RGBA32I:
361     case GL_RGBA32UI:
362         return 4;
363     default:
364         return 0;
365     }
366 }
367 
GetEpsilon()368 tcu::RGBA DrawBuffersIndexedBlending::GetEpsilon()
369 {
370     const glw::Functions &gl = m_context.getRenderContext().getFunctions();
371 
372     tcu::IVec4 bits;
373     tcu::UVec4 epsilon;
374 
375     for (int i = 0; i < 4; ++i)
376     {
377         gl.getIntegerv(GL_RED_BITS + i, &bits[i]);
378         epsilon[i] = de::min(
379             255u, static_cast<unsigned int>(ceil(1.0 + 255.0 * (1.0 / pow(2.0, static_cast<double>(bits[i]))))));
380     }
381 
382     return tcu::RGBA(epsilon.x(), epsilon.y(), epsilon.z(), epsilon.w());
383 }
384 
VerifyImg(const tcu::TextureLevel & textureLevel,tcu::RGBA expectedColor,tcu::RGBA epsilon)385 bool DrawBuffersIndexedBlending::VerifyImg(const tcu::TextureLevel &textureLevel, tcu::RGBA expectedColor,
386                                            tcu::RGBA epsilon)
387 {
388     for (int y = 0; y < textureLevel.getHeight(); ++y)
389     {
390         for (int x = 0; x < textureLevel.getWidth(); ++x)
391         {
392             tcu::RGBA pixel(textureLevel.getAccess().getPixel(x, y));
393             if (!tcu::compareThreshold(pixel, expectedColor, epsilon))
394             {
395                 m_testCtx.getLog() << tcu::TestLog::Message << "Expected value: " << expectedColor << "\n"
396                                    << "Read value:     " << pixel << "\n"
397                                    << "Epsilon:        " << epsilon << tcu::TestLog::EndMessage;
398                 return false;
399             }
400         }
401     }
402     return true;
403 }
404 
405 } // namespace glcts
406