Home | History | Annotate | Download | only in opengl
      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