1 /*------------------------------------------------------------------------- 2 * drawElements Quality Program OpenGL Utilities 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 Draw call utilities. 22 *//*--------------------------------------------------------------------*/ 23 24 #include "gluDrawUtil.hpp" 25 #include "gluRenderContext.hpp" 26 #include "gluObjectWrapper.hpp" 27 #include "glwFunctions.hpp" 28 #include "glwEnums.hpp" 29 #include "deInt32.h" 30 #include "deMemory.h" 31 32 #include <vector> 33 #include <set> 34 #include <iterator> 35 36 namespace glu 37 { 38 namespace 39 { 40 41 struct VertexAttributeDescriptor 42 { 43 int location; 44 VertexComponentType componentType; 45 VertexComponentConversion convert; 46 int numComponents; 47 int numElements; 48 int stride; //!< Stride or 0 if using default stride. 49 const void* pointer; //!< Pointer or offset. 50 51 VertexAttributeDescriptor (int location_, 52 VertexComponentType componentType_, 53 VertexComponentConversion convert_, 54 int numComponents_, 55 int numElements_, 56 int stride_, 57 const void* pointer_) 58 : location (location_) 59 , componentType (componentType_) 60 , convert (convert_) 61 , numComponents (numComponents_) 62 , numElements (numElements_) 63 , stride (stride_) 64 , pointer (pointer_) 65 { 66 } 67 68 VertexAttributeDescriptor (void) 69 : location (0) 70 , componentType (VTX_COMP_TYPE_LAST) 71 , convert (VTX_COMP_CONVERT_LAST) 72 , numComponents (0) 73 , numElements (0) 74 , stride (0) 75 , pointer (0) 76 { 77 } 78 }; 79 80 struct VertexBufferLayout 81 { 82 int size; 83 std::vector<VertexAttributeDescriptor> attributes; 84 85 VertexBufferLayout (int size_ = 0) 86 : size(size_) 87 { 88 } 89 }; 90 91 struct VertexBufferDescriptor 92 { 93 deUint32 buffer; 94 std::vector<VertexAttributeDescriptor> attributes; 95 96 VertexBufferDescriptor (deUint32 buffer_ = 0) 97 : buffer(buffer_) 98 { 99 } 100 }; 101 102 class VertexBuffer : public Buffer 103 { 104 public: 105 enum Type 106 { 107 TYPE_PLANAR = 0, //!< Data for each vertex array resides in a separate contiguous block in buffer. 108 TYPE_STRIDED, //!< Vertex arrays are interleaved. 109 110 TYPE_LAST 111 }; 112 113 VertexBuffer (const RenderContext& context, int numBindings, const VertexArrayBinding* bindings, Type type = TYPE_PLANAR); 114 ~VertexBuffer (void); 115 116 const VertexBufferDescriptor& getDescriptor (void) const { return m_layout; } 117 118 private: 119 VertexBuffer (const VertexBuffer& other); 120 VertexBuffer& operator= (const VertexBuffer& other); 121 122 VertexBufferDescriptor m_layout; 123 }; 124 125 class IndexBuffer : public Buffer 126 { 127 public: 128 IndexBuffer (const RenderContext& context, IndexType indexType, int numIndices, const void* indices); 129 ~IndexBuffer (void); 130 131 private: 132 IndexBuffer (const IndexBuffer& other); 133 IndexBuffer& operator= (const IndexBuffer& other); 134 }; 135 136 static deUint32 getVtxCompGLType (VertexComponentType type) 137 { 138 switch (type) 139 { 140 case VTX_COMP_UNSIGNED_INT8: return GL_UNSIGNED_BYTE; 141 case VTX_COMP_UNSIGNED_INT16: return GL_UNSIGNED_SHORT; 142 case VTX_COMP_UNSIGNED_INT32: return GL_UNSIGNED_INT; 143 case VTX_COMP_SIGNED_INT8: return GL_BYTE; 144 case VTX_COMP_SIGNED_INT16: return GL_SHORT; 145 case VTX_COMP_SIGNED_INT32: return GL_INT; 146 case VTX_COMP_FIXED: return GL_FIXED; 147 case VTX_COMP_HALF_FLOAT: return GL_HALF_FLOAT; 148 case VTX_COMP_FLOAT: return GL_FLOAT; 149 default: 150 DE_ASSERT(false); 151 return GL_NONE; 152 } 153 } 154 155 static int getVtxCompSize (VertexComponentType type) 156 { 157 switch (type) 158 { 159 case VTX_COMP_UNSIGNED_INT8: return 1; 160 case VTX_COMP_UNSIGNED_INT16: return 2; 161 case VTX_COMP_UNSIGNED_INT32: return 4; 162 case VTX_COMP_SIGNED_INT8: return 1; 163 case VTX_COMP_SIGNED_INT16: return 2; 164 case VTX_COMP_SIGNED_INT32: return 4; 165 case VTX_COMP_FIXED: return 4; 166 case VTX_COMP_HALF_FLOAT: return 2; 167 case VTX_COMP_FLOAT: return 4; 168 default: 169 DE_ASSERT(false); 170 return 0; 171 } 172 } 173 174 static deUint32 getIndexGLType (IndexType type) 175 { 176 switch (type) 177 { 178 case INDEXTYPE_UINT8: return GL_UNSIGNED_BYTE; 179 case INDEXTYPE_UINT16: return GL_UNSIGNED_SHORT; 180 case INDEXTYPE_UINT32: return GL_UNSIGNED_INT; 181 default: 182 DE_ASSERT(false); 183 return 0; 184 } 185 } 186 187 static int getIndexSize (IndexType type) 188 { 189 switch (type) 190 { 191 case INDEXTYPE_UINT8: return 1; 192 case INDEXTYPE_UINT16: return 2; 193 case INDEXTYPE_UINT32: return 4; 194 default: 195 DE_ASSERT(false); 196 return 0; 197 } 198 } 199 200 static deUint32 getPrimitiveGLType (PrimitiveType type) 201 { 202 switch (type) 203 { 204 case PRIMITIVETYPE_TRIANGLES: return GL_TRIANGLES; 205 case PRIMITIVETYPE_TRIANGLE_STRIP: return GL_TRIANGLE_STRIP; 206 case PRIMITIVETYPE_TRIANGLE_FAN: return GL_TRIANGLE_FAN; 207 case PRIMITIVETYPE_LINES: return GL_LINES; 208 case PRIMITIVETYPE_LINE_STRIP: return GL_LINE_STRIP; 209 case PRIMITIVETYPE_LINE_LOOP: return GL_LINE_LOOP; 210 case PRIMITIVETYPE_POINTS: return GL_POINTS; 211 case PRIMITIVETYPE_PATCHES: return GL_PATCHES; 212 default: 213 DE_ASSERT(false); 214 return 0; 215 } 216 } 217 218 //! Lower named bindings to locations and eliminate bindings that are not used by program. 219 template<typename InputIter, typename OutputIter> 220 static OutputIter namedBindingsToProgramLocations (const glw::Functions& gl, deUint32 program, InputIter first, InputIter end, OutputIter out) 221 { 222 for (InputIter cur = first; cur != end; ++cur) 223 { 224 const BindingPoint& binding = cur->binding; 225 if (binding.type == BindingPoint::TYPE_NAME) 226 { 227 DE_ASSERT(binding.location >= 0); 228 int location = gl.getAttribLocation(program, binding.name.c_str()); 229 if (location >= 0) 230 { 231 // Add binding.location as an offset to accommodate matrices. 232 *out = VertexArrayBinding(BindingPoint(location + binding.location), cur->pointer); 233 ++out; 234 } 235 } 236 else 237 { 238 *out = *cur; 239 ++out; 240 } 241 } 242 243 return out; 244 } 245 246 static deUint32 getMinimumAlignment (const VertexArrayPointer& pointer) 247 { 248 // \todo [2013-05-07 pyry] What is the actual min? 249 DE_UNREF(pointer); 250 return (deUint32)sizeof(float); 251 } 252 253 template<typename BindingIter> 254 static bool areVertexArrayLocationsValid (BindingIter first, BindingIter end) 255 { 256 std::set<int> usedLocations; 257 for (BindingIter cur = first; cur != end; ++cur) 258 { 259 const BindingPoint& binding = cur->binding; 260 261 if (binding.type != BindingPoint::TYPE_LOCATION) 262 return false; 263 264 if (usedLocations.find(binding.location) != usedLocations.end()) 265 return false; 266 267 usedLocations.insert(binding.location); 268 } 269 270 return true; 271 } 272 273 // \todo [2013-05-08 pyry] Buffer upload should try to match pointers to reduce dataset size. 274 275 static void appendAttributeNonStrided (VertexBufferLayout& layout, const VertexArrayBinding& va) 276 { 277 const int offset = deAlign32(layout.size, getMinimumAlignment(va.pointer)); 278 const int elementSize = getVtxCompSize(va.pointer.componentType)*va.pointer.numComponents; 279 const int size = elementSize*va.pointer.numElements; 280 281 // Must be assigned to location at this point. 282 DE_ASSERT(va.binding.type == BindingPoint::TYPE_LOCATION); 283 284 layout.attributes.push_back(VertexAttributeDescriptor(va.binding.location, 285 va.pointer.componentType, 286 va.pointer.convert, 287 va.pointer.numComponents, 288 va.pointer.numElements, 289 0, // default stride 290 (const void*)(deUintptr)offset)); 291 layout.size = offset+size; 292 } 293 294 template<typename BindingIter> 295 static void computeNonStridedBufferLayout (VertexBufferLayout& layout, BindingIter first, BindingIter end) 296 { 297 for (BindingIter iter = first; iter != end; ++iter) 298 appendAttributeNonStrided(layout, *iter); 299 } 300 301 static void copyToLayout (void* dstBasePtr, const VertexAttributeDescriptor& dstVA, const VertexArrayPointer& srcPtr) 302 { 303 DE_ASSERT(dstVA.componentType == srcPtr.componentType && 304 dstVA.numComponents == srcPtr.numComponents && 305 dstVA.numElements == srcPtr.numElements); 306 307 const int elementSize = getVtxCompSize(dstVA.componentType)*dstVA.numComponents; 308 const bool srcHasCustomStride = srcPtr.stride != 0 && srcPtr.stride != elementSize; 309 const bool dstHasCustomStride = dstVA.stride != 0 && dstVA.stride != elementSize; 310 311 if (srcHasCustomStride || dstHasCustomStride) 312 { 313 const int dstStride = dstVA.stride != 0 ? dstVA.stride : elementSize; 314 const int srcStride = srcPtr.stride != 0 ? srcPtr.stride : elementSize; 315 316 for (int ndx = 0; ndx < dstVA.numElements; ndx++) 317 deMemcpy((deUint8*)dstBasePtr + (deUintptr)dstVA.pointer + ndx*dstStride, (const deUint8*)srcPtr.data + ndx*srcStride, elementSize); 318 } 319 else 320 deMemcpy((deUint8*)dstBasePtr + (deUintptr)dstVA.pointer, srcPtr.data, elementSize*dstVA.numElements); 321 } 322 323 void uploadBufferData (const glw::Functions& gl, deUint32 buffer, deUint32 usage, const VertexBufferLayout& layout, const VertexArrayPointer* srcArrays) 324 { 325 // Create temporary data buffer for upload. 326 std::vector<deUint8> localBuf(layout.size); 327 328 for (int attrNdx = 0; attrNdx < (int)layout.attributes.size(); ++attrNdx) 329 copyToLayout(&localBuf[0], layout.attributes[attrNdx], srcArrays[attrNdx]); 330 331 gl.bindBuffer(GL_ARRAY_BUFFER, buffer); 332 gl.bufferData(GL_ARRAY_BUFFER, (int)localBuf.size(), &localBuf[0], usage); 333 gl.bindBuffer(GL_ARRAY_BUFFER, 0); 334 GLU_EXPECT_NO_ERROR(gl.getError(), "Uploading buffer data failed"); 335 } 336 337 // VertexBuffer 338 339 VertexBuffer::VertexBuffer (const RenderContext& context, int numBindings, const VertexArrayBinding* bindings, Type type) 340 : Buffer(context) 341 { 342 const glw::Functions& gl = context.getFunctions(); 343 const deUint32 usage = GL_STATIC_DRAW; 344 VertexBufferLayout layout; 345 346 if (!areVertexArrayLocationsValid(bindings, bindings+numBindings)) 347 throw tcu::TestError("Invalid vertex array locations"); 348 349 if (type == TYPE_PLANAR) 350 computeNonStridedBufferLayout(layout, bindings, bindings+numBindings); 351 else 352 throw tcu::InternalError("Strided layout is not yet supported"); 353 354 std::vector<VertexArrayPointer> srcPtrs(numBindings); 355 for (int ndx = 0; ndx < numBindings; ndx++) 356 srcPtrs[ndx] = bindings[ndx].pointer; 357 358 DE_ASSERT(srcPtrs.size() == layout.attributes.size()); 359 if (!srcPtrs.empty()) 360 uploadBufferData(gl, m_object, usage, layout, &srcPtrs[0]); 361 362 // Construct descriptor. 363 m_layout.buffer = m_object; 364 m_layout.attributes = layout.attributes; 365 } 366 367 VertexBuffer::~VertexBuffer (void) 368 { 369 } 370 371 // IndexBuffer 372 373 IndexBuffer::IndexBuffer (const RenderContext& context, IndexType indexType, int numIndices, const void* indices) 374 : Buffer(context) 375 { 376 const glw::Functions& gl = context.getFunctions(); 377 const deUint32 usage = GL_STATIC_DRAW; 378 379 gl.bindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_object); 380 gl.bufferData(GL_ELEMENT_ARRAY_BUFFER, numIndices*getIndexSize(indexType), indices, usage); 381 gl.bindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); 382 GLU_EXPECT_NO_ERROR(gl.getError(), "Uploading index data failed"); 383 } 384 385 IndexBuffer::~IndexBuffer (void) 386 { 387 } 388 389 static inline VertexAttributeDescriptor getUserPointerDescriptor (const VertexArrayBinding& vertexArray) 390 { 391 DE_ASSERT(vertexArray.binding.type == BindingPoint::TYPE_LOCATION); 392 393 return VertexAttributeDescriptor(vertexArray.binding.location, 394 vertexArray.pointer.componentType, 395 vertexArray.pointer.convert, 396 vertexArray.pointer.numComponents, 397 vertexArray.pointer.numElements, 398 vertexArray.pointer.stride, 399 vertexArray.pointer.data); 400 } 401 402 //! Setup VA according to allocation spec. Assumes that other state (VAO binding, buffer) is set already. 403 static void setVertexAttribPointer (const glw::Functions& gl, const VertexAttributeDescriptor& va) 404 { 405 const bool isIntType = de::inRange<int>(va.componentType, VTX_COMP_UNSIGNED_INT8, VTX_COMP_SIGNED_INT32); 406 const bool isSpecialType = de::inRange<int>(va.componentType, VTX_COMP_FIXED, VTX_COMP_FLOAT); 407 const deUint32 compTypeGL = getVtxCompGLType(va.componentType); 408 409 DE_ASSERT(isIntType != isSpecialType); // Must be either int or special type. 410 DE_ASSERT(isIntType || va.convert == VTX_COMP_CONVERT_NONE); // Conversion allowed only for special types. 411 DE_UNREF(isSpecialType); 412 413 gl.enableVertexAttribArray(va.location); 414 415 if (isIntType && va.convert == VTX_COMP_CONVERT_NONE) 416 gl.vertexAttribIPointer(va.location, va.numComponents, compTypeGL, va.stride, va.pointer); 417 else 418 gl.vertexAttribPointer(va.location, va.numComponents, compTypeGL, va.convert == VTX_COMP_CONVERT_NORMALIZE_TO_FLOAT ? GL_TRUE : GL_FALSE, va.stride, va.pointer); 419 } 420 421 //! Setup vertex buffer and attributes. 422 static void setVertexBufferAttributes (const glw::Functions& gl, const VertexBufferDescriptor& buffer) 423 { 424 gl.bindBuffer(GL_ARRAY_BUFFER, buffer.buffer); 425 426 for (std::vector<VertexAttributeDescriptor>::const_iterator vaIter = buffer.attributes.begin(); vaIter != buffer.attributes.end(); ++vaIter) 427 setVertexAttribPointer(gl, *vaIter); 428 429 gl.bindBuffer(GL_ARRAY_BUFFER, 0); 430 } 431 432 static void disableVertexArrays (const glw::Functions& gl, const std::vector<VertexArrayBinding>& bindings) 433 { 434 for (std::vector<VertexArrayBinding>::const_iterator vaIter = bindings.begin(); vaIter != bindings.end(); ++vaIter) 435 { 436 DE_ASSERT(vaIter->binding.type == BindingPoint::TYPE_LOCATION); 437 gl.disableVertexAttribArray(vaIter->binding.location); 438 } 439 } 440 441 #if defined(DE_DEBUG) 442 static bool isProgramActive (const RenderContext& context, deUint32 program) 443 { 444 // \todo [2013-05-08 pyry] Is this query broken? 445 /* deUint32 activeProgram = 0; 446 context.getFunctions().getIntegerv(GL_ACTIVE_PROGRAM, (int*)&activeProgram); 447 GLU_EXPECT_NO_ERROR(context.getFunctions().getError(), "oh"); 448 return activeProgram == program;*/ 449 DE_UNREF(context); 450 DE_UNREF(program); 451 return true; 452 } 453 454 static bool isDrawCallValid (int numVertexArrays, const VertexArrayBinding* vertexArrays, const PrimitiveList& primitives) 455 { 456 if (numVertexArrays < 0) 457 return false; 458 459 if ((primitives.indexType == INDEXTYPE_LAST) != (primitives.indices == 0)) 460 return false; 461 462 if (primitives.numElements < 0) 463 return false; 464 465 if (!primitives.indices) 466 { 467 for (int ndx = 0; ndx < numVertexArrays; ndx++) 468 { 469 if (primitives.numElements > vertexArrays[ndx].pointer.numElements) 470 return false; 471 } 472 } 473 // \todo [2013-05-08 pyry] We could walk whole index array and determine index range 474 475 return true; 476 } 477 #endif // DE_DEBUG 478 479 static inline void drawNonIndexed (const glw::Functions& gl, PrimitiveType type, int numElements) 480 { 481 deUint32 mode = getPrimitiveGLType(type); 482 gl.drawArrays(mode, 0, numElements); 483 } 484 485 static inline void drawIndexed (const glw::Functions& gl, PrimitiveType type, int numElements, IndexType indexType, const void* indexPtr) 486 { 487 deUint32 mode = getPrimitiveGLType(type); 488 deUint32 indexGLType = getIndexGLType(indexType); 489 490 gl.drawElements(mode, numElements, indexGLType, indexPtr); 491 } 492 493 } // anonymous 494 495 void drawFromUserPointers (const RenderContext& context, deUint32 program, int numVertexArrays, const VertexArrayBinding* vertexArrays, const PrimitiveList& primitives, DrawUtilCallback* callback) 496 { 497 const glw::Functions& gl = context.getFunctions(); 498 std::vector<VertexArrayBinding> bindingsWithLocations; 499 500 DE_ASSERT(isDrawCallValid(numVertexArrays, vertexArrays, primitives)); 501 DE_ASSERT(isProgramActive(context, program)); 502 503 // Lower bindings to locations. 504 namedBindingsToProgramLocations(gl, program, vertexArrays, vertexArrays+numVertexArrays, std::inserter(bindingsWithLocations, bindingsWithLocations.begin())); 505 506 TCU_CHECK(areVertexArrayLocationsValid(bindingsWithLocations.begin(), bindingsWithLocations.end())); 507 508 // Set VA state. 509 for (std::vector<VertexArrayBinding>::const_iterator vaIter = bindingsWithLocations.begin(); vaIter != bindingsWithLocations.end(); ++vaIter) 510 setVertexAttribPointer(gl, getUserPointerDescriptor(*vaIter)); 511 512 if (callback) 513 callback->beforeDrawCall(); 514 515 if (primitives.indices) 516 drawIndexed(gl, primitives.type, primitives.numElements, primitives.indexType, primitives.indices); 517 else 518 drawNonIndexed(gl, primitives.type, primitives.numElements); 519 520 if (callback) 521 callback->afterDrawCall(); 522 523 // Disable attribute arrays or otherwise someone later on might get crash thanks to invalid pointers. 524 disableVertexArrays(gl, bindingsWithLocations); 525 } 526 527 void drawFromBuffers (const RenderContext& context, deUint32 program, int numVertexArrays, const VertexArrayBinding* vertexArrays, const PrimitiveList& primitives, DrawUtilCallback* callback) 528 { 529 const glw::Functions& gl = context.getFunctions(); 530 std::vector<VertexArrayBinding> bindingsWithLocations; 531 532 DE_ASSERT(isDrawCallValid(numVertexArrays, vertexArrays, primitives)); 533 DE_ASSERT(isProgramActive(context, program)); 534 535 // Lower bindings to locations. 536 namedBindingsToProgramLocations(gl, program, vertexArrays, vertexArrays+numVertexArrays, std::inserter(bindingsWithLocations, bindingsWithLocations.begin())); 537 538 TCU_CHECK(areVertexArrayLocationsValid(bindingsWithLocations.begin(), bindingsWithLocations.end())); 539 540 // Create buffers for duration of draw call. 541 { 542 VertexBuffer vertexBuffer (context, (int)bindingsWithLocations.size(), (bindingsWithLocations.empty()) ? (DE_NULL) : (&bindingsWithLocations[0])); 543 544 // Set state. 545 setVertexBufferAttributes(gl, vertexBuffer.getDescriptor()); 546 547 if (primitives.indices) 548 { 549 IndexBuffer indexBuffer(context, primitives.indexType, primitives.numElements, primitives.indices); 550 551 gl.bindBuffer(GL_ELEMENT_ARRAY_BUFFER, *indexBuffer); 552 553 if (callback) 554 callback->beforeDrawCall(); 555 556 drawIndexed(gl, primitives.type, primitives.numElements, primitives.indexType, 0); 557 558 if (callback) 559 callback->afterDrawCall(); 560 561 gl.bindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); 562 } 563 else 564 { 565 if (callback) 566 callback->beforeDrawCall(); 567 568 drawNonIndexed(gl, primitives.type, primitives.numElements); 569 570 if (callback) 571 callback->afterDrawCall(); 572 } 573 } 574 575 // Disable attribute arrays or otherwise someone later on might get crash thanks to invalid pointers. 576 for (std::vector<VertexArrayBinding>::const_iterator vaIter = bindingsWithLocations.begin(); vaIter != bindingsWithLocations.end(); ++vaIter) 577 gl.disableVertexAttribArray(vaIter->binding.location); 578 } 579 580 void drawFromVAOBuffers (const RenderContext& context, deUint32 program, int numVertexArrays, const VertexArrayBinding* vertexArrays, const PrimitiveList& primitives, DrawUtilCallback* callback) 581 { 582 const glw::Functions& gl = context.getFunctions(); 583 VertexArray vao (context); 584 585 gl.bindVertexArray(*vao); 586 drawFromBuffers(context, program, numVertexArrays, vertexArrays, primitives, callback); 587 gl.bindVertexArray(0); 588 } 589 590 void draw (const RenderContext& context, deUint32 program, int numVertexArrays, const VertexArrayBinding* vertexArrays, const PrimitiveList& primitives, DrawUtilCallback* callback) 591 { 592 const glu::ContextType ctxType = context.getType(); 593 594 if (isContextTypeGLCore(ctxType) || contextSupports(ctxType, ApiType::es(3,1))) 595 drawFromVAOBuffers(context, program, numVertexArrays, vertexArrays, primitives, callback); 596 else 597 { 598 DE_ASSERT(isContextTypeES(ctxType)); 599 drawFromUserPointers(context, program, numVertexArrays, vertexArrays, primitives, callback); 600 } 601 } 602 603 } // glu 604