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