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