Vulkan: Add wide-color tests
[platform/upstream/VK-GL-CTS.git] / framework / opengl / gluDrawUtil.cpp
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