1 /*------------------------------------------------------------------------- 2 * drawElements Quality Program OpenGL ES 2.0 Module 3 * ------------------------------------------------- 4 * 5 * Copyright 2014 The Android Open Source Project 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 Default vertex attribute test 22 *//*--------------------------------------------------------------------*/ 23 24 #include "es2fDefaultVertexAttributeTests.hpp" 25 #include "tcuVector.hpp" 26 #include "tcuRenderTarget.hpp" 27 #include "tcuSurface.hpp" 28 #include "tcuTextureUtil.hpp" 29 #include "gluRenderContext.hpp" 30 #include "gluCallLogWrapper.hpp" 31 #include "gluShaderProgram.hpp" 32 #include "gluObjectWrapper.hpp" 33 #include "gluPixelTransfer.hpp" 34 #include "glwEnums.hpp" 35 #include "glwFunctions.hpp" 36 #include "deMath.h" 37 #include "deStringUtil.hpp" 38 #include "deString.h" 39 40 #include <limits> 41 42 namespace deqp 43 { 44 namespace gles2 45 { 46 namespace Functional 47 { 48 namespace 49 { 50 51 static const int s_valueRange = 10; 52 53 static const char* const s_passThroughFragmentShaderSource = "varying mediump vec4 v_color;\n" 54 "void main (void)\n" 55 "{\n" 56 " gl_FragColor = v_color;\n" 57 "}\n"; 58 59 template <typename T1, int S1, typename T2, int S2> 60 tcu::Vector<T1, S1> convertToTypeVec (const tcu::Vector<T2, S2>& v) 61 { 62 tcu::Vector<T1, S1> retVal; 63 64 for (int ndx = 0; ndx < S1; ++ndx) 65 retVal[ndx] = T1(0); 66 67 if (S1 == 4) 68 retVal[3] = T1(1); 69 70 for (int ndx = 0; ndx < de::min(S1, S2); ++ndx) 71 retVal[ndx] = T1(v[ndx]); 72 73 return retVal; 74 } 75 76 class FloatLoader 77 { 78 public: 79 virtual ~FloatLoader (void) {}; 80 81 // returns the value loaded 82 virtual tcu::Vec4 load (glu::CallLogWrapper& gl, int index, const tcu::Vec4& v) const = 0; 83 }; 84 85 #define GEN_DIRECT_FLOAT_LOADER(TYPE, COMPS, TYPECODE, CASENAME, VALUES) \ 86 class LoaderVertexAttrib##COMPS##TYPECODE : public FloatLoader \ 87 { \ 88 public: \ 89 enum \ 90 { \ 91 NORMALIZING = 0, \ 92 }; \ 93 enum \ 94 { \ 95 COMPONENTS = (COMPS) \ 96 }; \ 97 typedef TYPE Type; \ 98 \ 99 tcu::Vec4 load (glu::CallLogWrapper& gl, int index, const tcu::Vec4& v) const \ 100 { \ 101 tcu::Vector<TYPE, COMPONENTS> value; \ 102 value = convertToTypeVec<Type, COMPONENTS>(v); \ 103 \ 104 gl.glVertexAttrib ##COMPS ##TYPECODE VALUES; \ 105 return convertToTypeVec<float, 4>(value); \ 106 } \ 107 \ 108 static const char* getCaseName (void) \ 109 { \ 110 return CASENAME; \ 111 } \ 112 \ 113 static const char* getName (void) \ 114 { \ 115 return "VertexAttrib" #COMPS #TYPECODE; \ 116 } \ 117 } 118 119 #define GEN_INDIRECT_FLOAT_LOADER(TYPE, COMPS, TYPECODE, CASENAME) \ 120 class LoaderVertexAttrib##COMPS##TYPECODE : public FloatLoader \ 121 { \ 122 public: \ 123 enum \ 124 { \ 125 NORMALIZING = 0, \ 126 }; \ 127 enum \ 128 { \ 129 COMPONENTS = (COMPS) \ 130 }; \ 131 typedef TYPE Type; \ 132 \ 133 tcu::Vec4 load (glu::CallLogWrapper& gl, int index, const tcu::Vec4& v) const \ 134 { \ 135 tcu::Vector<TYPE, COMPONENTS> value; \ 136 value = convertToTypeVec<Type, COMPONENTS>(v); \ 137 \ 138 gl.glVertexAttrib ##COMPS ##TYPECODE (index, value.getPtr()); \ 139 return convertToTypeVec<float, 4>(value); \ 140 } \ 141 \ 142 static const char* getCaseName (void) \ 143 { \ 144 return CASENAME; \ 145 } \ 146 \ 147 static const char* getName (void) \ 148 { \ 149 return "VertexAttrib" #COMPS #TYPECODE; \ 150 } \ 151 } 152 153 GEN_DIRECT_FLOAT_LOADER(float, 1, f, "vertex_attrib_1f", (index, value.x())); 154 GEN_DIRECT_FLOAT_LOADER(float, 2, f, "vertex_attrib_2f", (index, value.x(), value.y())); 155 GEN_DIRECT_FLOAT_LOADER(float, 3, f, "vertex_attrib_3f", (index, value.x(), value.y(), value.z())); 156 GEN_DIRECT_FLOAT_LOADER(float, 4, f, "vertex_attrib_4f", (index, value.x(), value.y(), value.z(), value.w())); 157 158 GEN_INDIRECT_FLOAT_LOADER(float, 1, fv, "vertex_attrib_1fv"); 159 GEN_INDIRECT_FLOAT_LOADER(float, 2, fv, "vertex_attrib_2fv"); 160 GEN_INDIRECT_FLOAT_LOADER(float, 3, fv, "vertex_attrib_3fv"); 161 GEN_INDIRECT_FLOAT_LOADER(float, 4, fv, "vertex_attrib_4fv"); 162 163 class AttributeCase : public TestCase 164 { 165 AttributeCase (Context& ctx, const char* name, const char* desc, const char* funcName, bool normalizing, bool useNegative, glu::DataType dataType); 166 public: 167 template<typename LoaderType> 168 static AttributeCase* create (Context& ctx, glu::DataType dataType); 169 ~AttributeCase (void); 170 171 private: 172 void init (void); 173 void deinit (void); 174 IterateResult iterate (void); 175 176 glu::DataType getTargetType (void) const; 177 std::string genVertexSource (void) const; 178 bool renderWithValue (const tcu::Vec4& v); 179 tcu::Vec4 computeColor (const tcu::Vec4& value); 180 bool verifyUnicoloredBuffer (const tcu::Surface& scene, const tcu::Vec4& refValue); 181 182 const bool m_normalizing; 183 const bool m_useNegativeValues; 184 const char* const m_funcName; 185 const glu::DataType m_dataType; 186 const FloatLoader* m_loader; 187 glu::ShaderProgram* m_program; 188 deUint32 m_bufID; 189 bool m_allIterationsPassed; 190 int m_iteration; 191 192 enum 193 { 194 RENDER_SIZE = 32 195 }; 196 }; 197 198 AttributeCase::AttributeCase (Context& ctx, const char* name, const char* desc, const char* funcName, bool normalizing, bool useNegative, glu::DataType dataType) 199 : TestCase (ctx, name, desc) 200 , m_normalizing (normalizing) 201 , m_useNegativeValues (useNegative) 202 , m_funcName (funcName) 203 , m_dataType (dataType) 204 , m_loader (DE_NULL) 205 , m_program (DE_NULL) 206 , m_bufID (0) 207 , m_allIterationsPassed (true) 208 , m_iteration (0) 209 { 210 } 211 212 template<typename LoaderType> 213 AttributeCase* AttributeCase::create (Context& ctx, glu::DataType dataType) 214 { 215 AttributeCase* retVal = new AttributeCase(ctx, 216 LoaderType::getCaseName(), 217 (std::string("Test ") + LoaderType::getName()).c_str(), 218 LoaderType::getName(), 219 LoaderType::NORMALIZING != 0, 220 std::numeric_limits<typename LoaderType::Type>::is_signed, 221 dataType); 222 retVal->m_loader = new LoaderType(); 223 return retVal; 224 } 225 226 AttributeCase::~AttributeCase (void) 227 { 228 deinit(); 229 } 230 231 void AttributeCase::init (void) 232 { 233 if (m_context.getRenderTarget().getWidth() < RENDER_SIZE || m_context.getRenderTarget().getHeight() < RENDER_SIZE) 234 throw tcu::NotSupportedError("Render target must be at least " + de::toString<int>(RENDER_SIZE) + "x" + de::toString<int>(RENDER_SIZE)); 235 236 // log test info 237 238 { 239 const float maxRange = (m_normalizing) ? (1.0f) : (s_valueRange); 240 const float minRange = (m_useNegativeValues) ? (-maxRange) : (0.0f); 241 242 m_testCtx.getLog() 243 << tcu::TestLog::Message 244 << "Loading attribute values using " << m_funcName << "\n" 245 << "Attribute type: " << glu::getDataTypeName(m_dataType) << "\n" 246 << "Attribute value range: [" << minRange << ", " << maxRange << "]" 247 << tcu::TestLog::EndMessage; 248 } 249 250 // gen shader and base quad 251 252 m_program = new glu::ShaderProgram(m_context.getRenderContext(), glu::ProgramSources() << glu::VertexSource(genVertexSource()) << glu::FragmentSource(s_passThroughFragmentShaderSource)); 253 m_testCtx.getLog() << *m_program; 254 if (!m_program->isOk()) 255 throw tcu::TestError("could not build program"); 256 257 { 258 const tcu::Vec4 fullscreenQuad[] = 259 { 260 tcu::Vec4( 1.0f, 1.0f, 0.0f, 1.0f), 261 tcu::Vec4( 1.0f, -1.0f, 0.0f, 1.0f), 262 tcu::Vec4(-1.0f, 1.0f, 0.0f, 1.0f), 263 tcu::Vec4(-1.0f, -1.0f, 0.0f, 1.0f), 264 }; 265 266 const glw::Functions& gl = m_context.getRenderContext().getFunctions(); 267 268 gl.genBuffers(1, &m_bufID); 269 gl.bindBuffer(GL_ARRAY_BUFFER, m_bufID); 270 gl.bufferData(GL_ARRAY_BUFFER, sizeof(fullscreenQuad), fullscreenQuad, GL_STATIC_DRAW); 271 GLU_EXPECT_NO_ERROR(gl.getError(), "fill buffer"); 272 } 273 } 274 275 void AttributeCase::deinit (void) 276 { 277 delete m_loader; 278 m_loader = DE_NULL; 279 280 delete m_program; 281 m_program = DE_NULL; 282 283 if (m_bufID) 284 { 285 m_context.getRenderContext().getFunctions().deleteBuffers(1, &m_bufID); 286 m_bufID = 0; 287 } 288 } 289 290 AttributeCase::IterateResult AttributeCase::iterate (void) 291 { 292 static const tcu::Vec4 testValues[] = 293 { 294 tcu::Vec4(0.0f, 0.5f, 0.2f, 1.0f), 295 tcu::Vec4(0.1f, 0.7f, 1.0f, 0.6f), 296 tcu::Vec4(0.4f, 0.2f, 0.0f, 0.5f), 297 tcu::Vec4(0.5f, 0.0f, 0.9f, 0.1f), 298 tcu::Vec4(0.6f, 0.2f, 0.2f, 0.9f), 299 tcu::Vec4(0.9f, 1.0f, 0.0f, 0.0f), 300 tcu::Vec4(1.0f, 0.5f, 0.3f, 0.8f), 301 }; 302 303 const tcu::ScopedLogSection section(m_testCtx.getLog(), "Iteration", "Iteration " + de::toString(m_iteration+1) + "/" + de::toString(DE_LENGTH_OF_ARRAY(testValues))); 304 305 // Test normalizing transfers with whole range, non-normalizing with up to s_valueRange 306 const tcu::Vec4 testValue = ((m_useNegativeValues) ? (testValues[m_iteration] * 2.0f - tcu::Vec4(1.0f)) : (testValues[m_iteration])) * ((m_normalizing) ? (1.0f) : ((float)s_valueRange)); 307 308 if (!renderWithValue(testValue)) 309 m_allIterationsPassed = false; 310 311 // continue 312 313 if (++m_iteration < DE_LENGTH_OF_ARRAY(testValues)) 314 return CONTINUE; 315 316 if (m_allIterationsPassed) 317 m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass"); 318 else 319 m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Got unexpected values"); 320 321 return STOP; 322 } 323 324 std::string AttributeCase::genVertexSource (void) const 325 { 326 const int vectorSize = (glu::isDataTypeMatrix(m_dataType)) ? (glu::getDataTypeMatrixNumRows(m_dataType)) : (glu::isDataTypeVector(m_dataType)) ? (glu::getDataTypeScalarSize(m_dataType)) : (-1); 327 const char* const vectorType = glu::getDataTypeName((glu::isDataTypeMatrix(m_dataType)) ? (glu::getDataTypeVector(glu::TYPE_FLOAT, vectorSize)) : (glu::isDataTypeVector(m_dataType)) ? (glu::getDataTypeVector(glu::TYPE_FLOAT, vectorSize)) : (glu::TYPE_FLOAT)); 328 const int components = (glu::isDataTypeMatrix(m_dataType)) ? (glu::getDataTypeMatrixNumRows(m_dataType)) : (glu::getDataTypeScalarSize(m_dataType)); 329 std::ostringstream buf; 330 331 buf << "attribute highp vec4 a_position;\n" 332 "attribute highp " << glu::getDataTypeName(m_dataType) << " a_value;\n" 333 "varying highp vec4 v_color;\n" 334 "void main (void)\n" 335 "{\n" 336 " gl_Position = a_position;\n" 337 "\n"; 338 339 if (m_normalizing) 340 buf << " highp " << vectorType << " normalizedValue = " << ((glu::getDataTypeScalarType(m_dataType) == glu::TYPE_FLOAT) ? ("") : (vectorType)) << "(a_value" << ((glu::isDataTypeMatrix(m_dataType)) ? ("[1]") : ("")) << ");\n"; 341 else 342 buf << " highp " << vectorType << " normalizedValue = " << ((glu::getDataTypeScalarType(m_dataType) == glu::TYPE_FLOAT) ? ("") : (vectorType)) << "(a_value" << ((glu::isDataTypeMatrix(m_dataType)) ? ("[1]") : ("")) << ") / float(" << s_valueRange << ");\n"; 343 344 if (m_useNegativeValues) 345 buf << " highp " << vectorType << " positiveNormalizedValue = (normalizedValue + " << vectorType << "(1.0)) / 2.0;\n"; 346 else 347 buf << " highp " << vectorType << " positiveNormalizedValue = normalizedValue;\n"; 348 349 if (components == 1) 350 buf << " v_color = vec4(positiveNormalizedValue, 0.0, 0.0, 1.0);\n"; 351 else if (components == 2) 352 buf << " v_color = vec4(positiveNormalizedValue.xy, 0.0, 1.0);\n"; 353 else if (components == 3) 354 buf << " v_color = vec4(positiveNormalizedValue.xyz, 1.0);\n"; 355 else if (components == 4) 356 buf << " v_color = vec4((positiveNormalizedValue.xy + positiveNormalizedValue.zz) / 2.0, positiveNormalizedValue.w, 1.0);\n"; 357 else 358 DE_ASSERT(DE_FALSE); 359 360 buf << "}\n"; 361 362 return buf.str(); 363 } 364 365 bool AttributeCase::renderWithValue (const tcu::Vec4& v) 366 { 367 glu::CallLogWrapper gl (m_context.getRenderContext().getFunctions(), m_testCtx.getLog()); 368 369 gl.enableLogging(true); 370 371 const int positionIndex = gl.glGetAttribLocation(m_program->getProgram(), "a_position"); 372 const int valueIndex = gl.glGetAttribLocation(m_program->getProgram(), "a_value"); 373 tcu::Surface dest (RENDER_SIZE, RENDER_SIZE); 374 tcu::Vec4 loadedValue; 375 376 gl.glClearColor(0.0f, 0.0f, 0.0f, 0.0f); 377 gl.glClear(GL_COLOR_BUFFER_BIT); 378 gl.glViewport(0, 0, RENDER_SIZE, RENDER_SIZE); 379 GLU_EXPECT_NO_ERROR(gl.glGetError(), "setup"); 380 381 gl.glBindBuffer(GL_ARRAY_BUFFER, m_bufID); 382 gl.glVertexAttribPointer(positionIndex, 4, GL_FLOAT, GL_FALSE, 0, DE_NULL); 383 gl.glEnableVertexAttribArray(positionIndex); 384 GLU_EXPECT_NO_ERROR(gl.glGetError(), "position va"); 385 386 // transfer test value. Load to the second column in the matrix case 387 loadedValue = m_loader->load(gl, (glu::isDataTypeMatrix(m_dataType)) ? (valueIndex + 1) : (valueIndex), v); 388 GLU_EXPECT_NO_ERROR(gl.glGetError(), "default va"); 389 390 gl.glUseProgram(m_program->getProgram()); 391 gl.glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); 392 gl.glUseProgram(0); 393 GLU_EXPECT_NO_ERROR(gl.glGetError(), "draw"); 394 395 glu::readPixels(m_context.getRenderContext(), 0, 0, dest.getAccess()); 396 397 // check whole result is colored correctly 398 return verifyUnicoloredBuffer(dest, computeColor(loadedValue)); 399 } 400 401 tcu::Vec4 AttributeCase::computeColor (const tcu::Vec4& value) 402 { 403 const tcu::Vec4 normalizedValue = value / ((m_normalizing) ? (1.0f) : ((float)s_valueRange)); 404 const tcu::Vec4 positiveNormalizedValue = ((m_useNegativeValues) ? ((normalizedValue + tcu::Vec4(1.0f)) / 2.0f) : (normalizedValue)); 405 const int components = (glu::isDataTypeMatrix(m_dataType)) ? (glu::getDataTypeMatrixNumRows(m_dataType)) : (glu::getDataTypeScalarSize(m_dataType)); 406 407 if (components == 1) 408 return tcu::Vec4(positiveNormalizedValue.x(), 0.0f, 0.0f, 1.0f); 409 else if (components == 2) 410 return tcu::Vec4(positiveNormalizedValue.x(), positiveNormalizedValue.y(), 0.0f, 1.0f); 411 else if (components == 3) 412 return tcu::Vec4(positiveNormalizedValue.x(), positiveNormalizedValue.y(), positiveNormalizedValue.z(), 1.0f); 413 else if (components == 4) 414 return tcu::Vec4((positiveNormalizedValue.x() + positiveNormalizedValue.z()) / 2.0f, (positiveNormalizedValue.y() + positiveNormalizedValue.z()) / 2.0f, positiveNormalizedValue.w(), 1.0f); 415 else 416 DE_ASSERT(DE_FALSE); 417 418 return tcu::Vec4(0.0f, 0.0f, 0.0f, 0.0f); 419 } 420 421 bool AttributeCase::verifyUnicoloredBuffer (const tcu::Surface& scene, const tcu::Vec4& refValue) 422 { 423 tcu::Surface errorMask (RENDER_SIZE, RENDER_SIZE); 424 const tcu::RGBA refColor (refValue); 425 const int resultThreshold = 2; 426 const tcu::RGBA colorThreshold = m_context.getRenderTarget().getPixelFormat().getColorThreshold() * resultThreshold; 427 bool error = false; 428 429 tcu::RGBA exampleColor; 430 tcu::IVec2 examplePos; 431 432 tcu::clear(errorMask.getAccess(), tcu::RGBA::green().toIVec()); 433 434 m_testCtx.getLog() << tcu::TestLog::Message << "Verifying rendered image. Expecting color " << refColor << ", threshold " << colorThreshold << tcu::TestLog::EndMessage; 435 436 for (int y = 0; y < RENDER_SIZE; ++y) 437 for (int x = 0; x < RENDER_SIZE; ++x) 438 { 439 const tcu::RGBA color = scene.getPixel(x, y); 440 441 if (de::abs(color.getRed() - refColor.getRed()) > colorThreshold.getRed() || 442 de::abs(color.getGreen() - refColor.getGreen()) > colorThreshold.getGreen() || 443 de::abs(color.getBlue() - refColor.getBlue()) > colorThreshold.getBlue()) 444 { 445 // first error 446 if (!error) 447 { 448 exampleColor = color; 449 examplePos = tcu::IVec2(x, y); 450 } 451 452 error = true; 453 errorMask.setPixel(x, y, tcu::RGBA::red()); 454 } 455 } 456 457 if (!error) 458 m_testCtx.getLog() << tcu::TestLog::Message << "Rendered image is valid." << tcu::TestLog::EndMessage; 459 else 460 { 461 m_testCtx.getLog() << tcu::TestLog::Message 462 << "Found invalid pixel(s).\n" 463 << "Pixel at (" << examplePos.x() << ", " << examplePos.y() << ") color: " << exampleColor 464 << tcu::TestLog::EndMessage 465 << tcu::TestLog::ImageSet("Result", "Render result") 466 << tcu::TestLog::Image("Result", "Result", scene) 467 << tcu::TestLog::Image("ErrorMask", "Error Mask", errorMask) 468 << tcu::TestLog::EndImageSet; 469 } 470 471 return !error; 472 } 473 474 } // anonymous 475 476 DefaultVertexAttributeTests::DefaultVertexAttributeTests (Context& context) 477 : TestCaseGroup(context, "default_vertex_attrib", "Test default vertex attributes") 478 { 479 } 480 481 DefaultVertexAttributeTests::~DefaultVertexAttributeTests (void) 482 { 483 } 484 485 void DefaultVertexAttributeTests::init (void) 486 { 487 struct Target 488 { 489 const char* name; 490 glu::DataType dataType; 491 bool reducedTestSets; // !< use reduced coverage 492 }; 493 494 static const Target floatTargets[] = 495 { 496 { "float", glu::TYPE_FLOAT, false }, 497 { "vec2", glu::TYPE_FLOAT_VEC2, true }, 498 { "vec3", glu::TYPE_FLOAT_VEC3, true }, 499 { "vec4", glu::TYPE_FLOAT_VEC4, false }, 500 { "mat2", glu::TYPE_FLOAT_MAT2, true }, 501 { "mat3", glu::TYPE_FLOAT_MAT3, true }, 502 { "mat4", glu::TYPE_FLOAT_MAT4, false }, 503 }; 504 505 // float targets 506 507 for (int targetNdx = 0; targetNdx < DE_LENGTH_OF_ARRAY(floatTargets); ++targetNdx) 508 { 509 tcu::TestCaseGroup* const group = new tcu::TestCaseGroup(m_testCtx, floatTargets[targetNdx].name, (std::string("test with ") + floatTargets[targetNdx].name).c_str()); 510 const bool fullSet = !floatTargets[targetNdx].reducedTestSets; 511 512 #define ADD_CASE(X) group->addChild(AttributeCase::create<X>(m_context, floatTargets[targetNdx].dataType)) 513 #define ADD_REDUCED_CASE(X) if (fullSet) ADD_CASE(X) 514 515 ADD_CASE (LoaderVertexAttrib1f); 516 ADD_REDUCED_CASE(LoaderVertexAttrib2f); 517 ADD_REDUCED_CASE(LoaderVertexAttrib3f); 518 ADD_CASE (LoaderVertexAttrib4f); 519 520 ADD_CASE (LoaderVertexAttrib1fv); 521 ADD_REDUCED_CASE(LoaderVertexAttrib2fv); 522 ADD_REDUCED_CASE(LoaderVertexAttrib3fv); 523 ADD_CASE (LoaderVertexAttrib4fv); 524 525 #undef ADD_CASE 526 #undef ADD_REDUCED_CASE 527 528 addChild(group); 529 } 530 } 531 532 } // Functional 533 } // gles2 534 } // deqp 535