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
36namespace glu
37{
38namespace
39{
40
41struct 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
80struct VertexBufferLayout
81{
82	int										size;
83	std::vector<VertexAttributeDescriptor>	attributes;
84
85	VertexBufferLayout (int size_ = 0)
86		: size(size_)
87	{
88	}
89};
90
91struct VertexBufferDescriptor
92{
93	deUint32								buffer;
94	std::vector<VertexAttributeDescriptor>	attributes;
95
96	VertexBufferDescriptor (deUint32 buffer_ = 0)
97		: buffer(buffer_)
98	{
99	}
100};
101
102class VertexBuffer : public Buffer
103{
104public:
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
118private:
119									VertexBuffer		(const VertexBuffer& other);
120	VertexBuffer&					operator=			(const VertexBuffer& other);
121
122	VertexBufferDescriptor			m_layout;
123};
124
125class IndexBuffer : public Buffer
126{
127public:
128									IndexBuffer			(const RenderContext& context, IndexType indexType, int numIndices, const void* indices);
129									~IndexBuffer		(void);
130
131private:
132									IndexBuffer			(const IndexBuffer& other);
133	IndexBuffer&					operator=			(const IndexBuffer& other);
134};
135
136static 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
155static 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
174static 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
187static 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
200static 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.
219template<typename InputIter, typename OutputIter>
220static 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
246static 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
253template<typename BindingIter>
254static 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
275static 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
294template<typename BindingIter>
295static void computeNonStridedBufferLayout (VertexBufferLayout& layout, BindingIter first, BindingIter end)
296{
297	for (BindingIter iter = first; iter != end; ++iter)
298		appendAttributeNonStrided(layout, *iter);
299}
300
301static 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
323void 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
339VertexBuffer::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
367VertexBuffer::~VertexBuffer (void)
368{
369}
370
371// IndexBuffer
372
373IndexBuffer::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
385IndexBuffer::~IndexBuffer (void)
386{
387}
388
389static 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.
403static 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.
422static 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
432static 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)
442static 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
454static 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
479static 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
485static 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
495void 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
527void 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
580void 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
590void 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