1/*-------------------------------------------------------------------------
2 * drawElements Quality Program OpenGL ES 3.1 Module
3 * -------------------------------------------------
4 *
5 * Copyright 2014 The Android Open Source Project
6 *
7 * Licensed under the Apache License, Version 2.0 (the "License");
8 * you may not use this file except in compliance with the License.
9 * You may obtain a copy of the License at
10 *
11 *      http://www.apache.org/licenses/LICENSE-2.0
12 *
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS,
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
18 *
19 *//*!
20 * \file
21 * \brief SSBO layout case.
22 *//*--------------------------------------------------------------------*/
23
24#include "es31fSSBOLayoutCase.hpp"
25#include "gluRenderContext.hpp"
26#include "gluShaderProgram.hpp"
27#include "gluPixelTransfer.hpp"
28#include "gluContextInfo.hpp"
29#include "gluRenderContext.hpp"
30#include "gluProgramInterfaceQuery.hpp"
31#include "gluObjectWrapper.hpp"
32#include "gluVarTypeUtil.hpp"
33#include "glwFunctions.hpp"
34#include "glwEnums.hpp"
35#include "tcuTestLog.hpp"
36#include "tcuSurface.hpp"
37#include "tcuRenderTarget.hpp"
38#include "deRandom.hpp"
39#include "deStringUtil.hpp"
40#include "deMemory.h"
41#include "deString.h"
42#include "deMath.h"
43
44#include <algorithm>
45#include <map>
46
47using tcu::TestLog;
48using std::string;
49using std::vector;
50using std::map;
51
52namespace deqp
53{
54namespace gles31
55{
56
57using glu::VarType;
58using glu::StructType;
59using glu::StructMember;
60
61namespace bb
62{
63
64struct LayoutFlagsFmt
65{
66	deUint32 flags;
67	LayoutFlagsFmt (deUint32 flags_) : flags(flags_) {}
68};
69
70std::ostream& operator<< (std::ostream& str, const LayoutFlagsFmt& fmt)
71{
72	static const struct
73	{
74		deUint32	bit;
75		const char*	token;
76	} bitDesc[] =
77	{
78		{ LAYOUT_SHARED,		"shared"		},
79		{ LAYOUT_PACKED,		"packed"		},
80		{ LAYOUT_STD140,		"std140"		},
81		{ LAYOUT_STD430,		"std430"		},
82		{ LAYOUT_ROW_MAJOR,		"row_major"		},
83		{ LAYOUT_COLUMN_MAJOR,	"column_major"	}
84	};
85
86	deUint32 remBits = fmt.flags;
87	for (int descNdx = 0; descNdx < DE_LENGTH_OF_ARRAY(bitDesc); descNdx++)
88	{
89		if (remBits & bitDesc[descNdx].bit)
90		{
91			if (remBits != fmt.flags)
92				str << ", ";
93			str << bitDesc[descNdx].token;
94			remBits &= ~bitDesc[descNdx].bit;
95		}
96	}
97	DE_ASSERT(remBits == 0);
98	return str;
99}
100
101// BufferVar implementation.
102
103BufferVar::BufferVar (const char* name, const VarType& type, deUint32 flags)
104	: m_name	(name)
105	, m_type	(type)
106	, m_flags	(flags)
107{
108}
109
110// BufferBlock implementation.
111
112BufferBlock::BufferBlock (const char* blockName)
113	: m_blockName	(blockName)
114	, m_arraySize	(-1)
115	, m_flags		(0)
116{
117	setArraySize(0);
118}
119
120void BufferBlock::setArraySize (int arraySize)
121{
122	DE_ASSERT(arraySize >= 0);
123	m_lastUnsizedArraySizes.resize(arraySize == 0 ? 1 : arraySize, 0);
124	m_arraySize = arraySize;
125}
126
127struct BlockLayoutEntry
128{
129	BlockLayoutEntry (void)
130		: size(0)
131	{
132	}
133
134	std::string			name;
135	int					size;
136	std::vector<int>	activeVarIndices;
137};
138
139std::ostream& operator<< (std::ostream& stream, const BlockLayoutEntry& entry)
140{
141	stream << entry.name << " { name = " << entry.name
142		   << ", size = " << entry.size
143		   << ", activeVarIndices = [";
144
145	for (vector<int>::const_iterator i = entry.activeVarIndices.begin(); i != entry.activeVarIndices.end(); i++)
146	{
147		if (i != entry.activeVarIndices.begin())
148			stream << ", ";
149		stream << *i;
150	}
151
152	stream << "] }";
153	return stream;
154}
155
156struct BufferVarLayoutEntry
157{
158	BufferVarLayoutEntry (void)
159		: type					(glu::TYPE_LAST)
160		, blockNdx				(-1)
161		, offset				(-1)
162		, arraySize				(-1)
163		, arrayStride			(-1)
164		, matrixStride			(-1)
165		, topLevelArraySize		(-1)
166		, topLevelArrayStride	(-1)
167		, isRowMajor			(false)
168	{
169	}
170
171	std::string			name;
172	glu::DataType		type;
173	int					blockNdx;
174	int					offset;
175	int					arraySize;
176	int					arrayStride;
177	int					matrixStride;
178	int					topLevelArraySize;
179	int					topLevelArrayStride;
180	bool				isRowMajor;
181};
182
183static bool isUnsizedArray (const BufferVarLayoutEntry& entry)
184{
185	DE_ASSERT(entry.arraySize != 0 || entry.topLevelArraySize != 0);
186	return entry.arraySize == 0 || entry.topLevelArraySize == 0;
187}
188
189std::ostream& operator<< (std::ostream& stream, const BufferVarLayoutEntry& entry)
190{
191	stream << entry.name << " { type = " << glu::getDataTypeName(entry.type)
192		   << ", blockNdx = " << entry.blockNdx
193		   << ", offset = " << entry.offset
194		   << ", arraySize = " << entry.arraySize
195		   << ", arrayStride = " << entry.arrayStride
196		   << ", matrixStride = " << entry.matrixStride
197		   << ", topLevelArraySize = " << entry.topLevelArraySize
198		   << ", topLevelArrayStride = " << entry.topLevelArrayStride
199		   << ", isRowMajor = " << (entry.isRowMajor ? "true" : "false")
200		   << " }";
201	return stream;
202}
203
204class BufferLayout
205{
206public:
207	std::vector<BlockLayoutEntry>		blocks;
208	std::vector<BufferVarLayoutEntry>	bufferVars;
209
210	int									getVariableIndex		(const string& name) const;
211	int									getBlockIndex			(const string& name) const;
212};
213
214// \todo [2012-01-24 pyry] Speed up lookups using hash.
215
216int BufferLayout::getVariableIndex (const string& name) const
217{
218	for (int ndx = 0; ndx < (int)bufferVars.size(); ndx++)
219	{
220		if (bufferVars[ndx].name == name)
221			return ndx;
222	}
223	return -1;
224}
225
226int BufferLayout::getBlockIndex (const string& name) const
227{
228	for (int ndx = 0; ndx < (int)blocks.size(); ndx++)
229	{
230		if (blocks[ndx].name == name)
231			return ndx;
232	}
233	return -1;
234}
235
236// ShaderInterface implementation.
237
238ShaderInterface::ShaderInterface (void)
239{
240}
241
242ShaderInterface::~ShaderInterface (void)
243{
244	for (std::vector<StructType*>::iterator i = m_structs.begin(); i != m_structs.end(); i++)
245		delete *i;
246
247	for (std::vector<BufferBlock*>::iterator i = m_bufferBlocks.begin(); i != m_bufferBlocks.end(); i++)
248		delete *i;
249}
250
251StructType& ShaderInterface::allocStruct (const char* name)
252{
253	m_structs.reserve(m_structs.size()+1);
254	m_structs.push_back(new StructType(name));
255	return *m_structs.back();
256}
257
258struct StructNameEquals
259{
260	std::string name;
261
262	StructNameEquals (const char* name_) : name(name_) {}
263
264	bool operator() (const StructType* type) const
265	{
266		return type->getTypeName() && name == type->getTypeName();
267	}
268};
269
270const StructType* ShaderInterface::findStruct (const char* name) const
271{
272	std::vector<StructType*>::const_iterator pos = std::find_if(m_structs.begin(), m_structs.end(), StructNameEquals(name));
273	return pos != m_structs.end() ? *pos : DE_NULL;
274}
275
276void ShaderInterface::getNamedStructs (std::vector<const StructType*>& structs) const
277{
278	for (std::vector<StructType*>::const_iterator i = m_structs.begin(); i != m_structs.end(); i++)
279	{
280		if ((*i)->getTypeName() != DE_NULL)
281			structs.push_back(*i);
282	}
283}
284
285BufferBlock& ShaderInterface::allocBlock (const char* name)
286{
287	m_bufferBlocks.reserve(m_bufferBlocks.size()+1);
288	m_bufferBlocks.push_back(new BufferBlock(name));
289	return *m_bufferBlocks.back();
290}
291
292// BlockDataPtr
293
294struct BlockDataPtr
295{
296	void*		ptr;
297	int			size;						//!< Redundant, for debugging purposes.
298	int			lastUnsizedArraySize;
299
300	BlockDataPtr (void* ptr_, int size_, int lastUnsizedArraySize_)
301		: ptr					(ptr_)
302		, size					(size_)
303		, lastUnsizedArraySize	(lastUnsizedArraySize_)
304	{
305	}
306
307	BlockDataPtr (void)
308		: ptr					(DE_NULL)
309		, size					(0)
310		, lastUnsizedArraySize	(0)
311	{
312	}
313};
314
315namespace // Utilities
316{
317
318int findBlockIndex (const BufferLayout& layout, const string& name)
319{
320	for (int ndx = 0; ndx < (int)layout.blocks.size(); ndx++)
321	{
322		if (layout.blocks[ndx].name == name)
323			return ndx;
324	}
325	return -1;
326}
327
328// Layout computation.
329
330int getDataTypeByteSize (glu::DataType type)
331{
332	return glu::getDataTypeScalarSize(type)*sizeof(deUint32);
333}
334
335int getDataTypeByteAlignment (glu::DataType type)
336{
337	switch (type)
338	{
339		case glu::TYPE_FLOAT:
340		case glu::TYPE_INT:
341		case glu::TYPE_UINT:
342		case glu::TYPE_BOOL:		return 1*sizeof(deUint32);
343
344		case glu::TYPE_FLOAT_VEC2:
345		case glu::TYPE_INT_VEC2:
346		case glu::TYPE_UINT_VEC2:
347		case glu::TYPE_BOOL_VEC2:	return 2*sizeof(deUint32);
348
349		case glu::TYPE_FLOAT_VEC3:
350		case glu::TYPE_INT_VEC3:
351		case glu::TYPE_UINT_VEC3:
352		case glu::TYPE_BOOL_VEC3:	// Fall-through to vec4
353
354		case glu::TYPE_FLOAT_VEC4:
355		case glu::TYPE_INT_VEC4:
356		case glu::TYPE_UINT_VEC4:
357		case glu::TYPE_BOOL_VEC4:	return 4*sizeof(deUint32);
358
359		default:
360			DE_ASSERT(false);
361			return 0;
362	}
363}
364
365static inline int deRoundUp32 (int a, int b)
366{
367	int d = a/b;
368	return d*b == a ? a : (d+1)*b;
369}
370
371int computeStd140BaseAlignment (const VarType& type, deUint32 layoutFlags)
372{
373	const int vec4Alignment = sizeof(deUint32)*4;
374
375	if (type.isBasicType())
376	{
377		glu::DataType basicType = type.getBasicType();
378
379		if (glu::isDataTypeMatrix(basicType))
380		{
381			const bool	isRowMajor	= !!(layoutFlags & LAYOUT_ROW_MAJOR);
382			const int	vecSize		= isRowMajor ? glu::getDataTypeMatrixNumColumns(basicType)
383												 : glu::getDataTypeMatrixNumRows(basicType);
384			const int	vecAlign	= deAlign32(getDataTypeByteAlignment(glu::getDataTypeFloatVec(vecSize)), vec4Alignment);
385
386			return vecAlign;
387		}
388		else
389			return getDataTypeByteAlignment(basicType);
390	}
391	else if (type.isArrayType())
392	{
393		int elemAlignment = computeStd140BaseAlignment(type.getElementType(), layoutFlags);
394
395		// Round up to alignment of vec4
396		return deAlign32(elemAlignment, vec4Alignment);
397	}
398	else
399	{
400		DE_ASSERT(type.isStructType());
401
402		int maxBaseAlignment = 0;
403
404		for (StructType::ConstIterator memberIter = type.getStructPtr()->begin(); memberIter != type.getStructPtr()->end(); memberIter++)
405			maxBaseAlignment = de::max(maxBaseAlignment, computeStd140BaseAlignment(memberIter->getType(), layoutFlags));
406
407		return deAlign32(maxBaseAlignment, vec4Alignment);
408	}
409}
410
411int computeStd430BaseAlignment (const VarType& type, deUint32 layoutFlags)
412{
413	// Otherwise identical to std140 except that alignment of structures and arrays
414	// are not rounded up to alignment of vec4.
415
416	if (type.isBasicType())
417	{
418		glu::DataType basicType = type.getBasicType();
419
420		if (glu::isDataTypeMatrix(basicType))
421		{
422			const bool	isRowMajor	= !!(layoutFlags & LAYOUT_ROW_MAJOR);
423			const int	vecSize		= isRowMajor ? glu::getDataTypeMatrixNumColumns(basicType)
424												 : glu::getDataTypeMatrixNumRows(basicType);
425			const int	vecAlign	= getDataTypeByteAlignment(glu::getDataTypeFloatVec(vecSize));
426
427			return vecAlign;
428		}
429		else
430			return getDataTypeByteAlignment(basicType);
431	}
432	else if (type.isArrayType())
433	{
434		return computeStd430BaseAlignment(type.getElementType(), layoutFlags);
435	}
436	else
437	{
438		DE_ASSERT(type.isStructType());
439
440		int maxBaseAlignment = 0;
441
442		for (StructType::ConstIterator memberIter = type.getStructPtr()->begin(); memberIter != type.getStructPtr()->end(); memberIter++)
443			maxBaseAlignment = de::max(maxBaseAlignment, computeStd430BaseAlignment(memberIter->getType(), layoutFlags));
444
445		return maxBaseAlignment;
446	}
447}
448
449inline deUint32 mergeLayoutFlags (deUint32 prevFlags, deUint32 newFlags)
450{
451	const deUint32	packingMask		= LAYOUT_PACKED|LAYOUT_SHARED|LAYOUT_STD140|LAYOUT_STD430;
452	const deUint32	matrixMask		= LAYOUT_ROW_MAJOR|LAYOUT_COLUMN_MAJOR;
453
454	deUint32 mergedFlags = 0;
455
456	mergedFlags |= ((newFlags & packingMask)	? newFlags : prevFlags) & packingMask;
457	mergedFlags |= ((newFlags & matrixMask)		? newFlags : prevFlags) & matrixMask;
458
459	return mergedFlags;
460}
461
462//! Appends all child elements to layout, returns value that should be appended to offset.
463int computeReferenceLayout (
464	BufferLayout&		layout,
465	int					curBlockNdx,
466	int					baseOffset,
467	const std::string&	curPrefix,
468	const VarType&		type,
469	deUint32			layoutFlags)
470{
471	// Reference layout uses std430 rules by default. std140 rules are
472	// choosen only for blocks that have std140 layout.
473	const bool	isStd140			= (layoutFlags & LAYOUT_STD140) != 0;
474	const int	baseAlignment		= isStd140 ? computeStd140BaseAlignment(type, layoutFlags)
475											   : computeStd430BaseAlignment(type, layoutFlags);
476	int			curOffset			= deAlign32(baseOffset, baseAlignment);
477	const int	topLevelArraySize	= 1; // Default values
478	const int	topLevelArrayStride	= 0;
479
480	if (type.isBasicType())
481	{
482		const glu::DataType		basicType	= type.getBasicType();
483		BufferVarLayoutEntry	entry;
484
485		entry.name					= curPrefix;
486		entry.type					= basicType;
487		entry.arraySize				= 1;
488		entry.arrayStride			= 0;
489		entry.matrixStride			= 0;
490		entry.topLevelArraySize		= topLevelArraySize;
491		entry.topLevelArrayStride	= topLevelArrayStride;
492		entry.blockNdx				= curBlockNdx;
493
494		if (glu::isDataTypeMatrix(basicType))
495		{
496			// Array of vectors as specified in rules 5 & 7.
497			const bool	isRowMajor			= !!(layoutFlags & LAYOUT_ROW_MAJOR);
498			const int	numVecs				= isRowMajor ? glu::getDataTypeMatrixNumRows(basicType)
499														 : glu::getDataTypeMatrixNumColumns(basicType);
500
501			entry.offset		= curOffset;
502			entry.matrixStride	= baseAlignment;
503			entry.isRowMajor	= isRowMajor;
504
505			curOffset += numVecs*baseAlignment;
506		}
507		else
508		{
509			// Scalar or vector.
510			entry.offset = curOffset;
511
512			curOffset += getDataTypeByteSize(basicType);
513		}
514
515		layout.bufferVars.push_back(entry);
516	}
517	else if (type.isArrayType())
518	{
519		const VarType&	elemType	= type.getElementType();
520
521		if (elemType.isBasicType() && !glu::isDataTypeMatrix(elemType.getBasicType()))
522		{
523			// Array of scalars or vectors.
524			const glu::DataType		elemBasicType	= elemType.getBasicType();
525			const int				stride			= baseAlignment;
526			BufferVarLayoutEntry	entry;
527
528			entry.name					= curPrefix + "[0]"; // Array variables are always postfixed with [0]
529			entry.type					= elemBasicType;
530			entry.blockNdx				= curBlockNdx;
531			entry.offset				= curOffset;
532			entry.arraySize				= type.getArraySize();
533			entry.arrayStride			= stride;
534			entry.matrixStride			= 0;
535			entry.topLevelArraySize		= topLevelArraySize;
536			entry.topLevelArrayStride	= topLevelArrayStride;
537
538			curOffset += stride*type.getArraySize();
539
540			layout.bufferVars.push_back(entry);
541		}
542		else if (elemType.isBasicType() && glu::isDataTypeMatrix(elemType.getBasicType()))
543		{
544			// Array of matrices.
545			const glu::DataType			elemBasicType	= elemType.getBasicType();
546			const bool					isRowMajor		= !!(layoutFlags & LAYOUT_ROW_MAJOR);
547			const int					numVecs			= isRowMajor ? glu::getDataTypeMatrixNumRows(elemBasicType)
548																	 : glu::getDataTypeMatrixNumColumns(elemBasicType);
549			const int					vecStride		= baseAlignment;
550			BufferVarLayoutEntry		entry;
551
552			entry.name					= curPrefix + "[0]"; // Array variables are always postfixed with [0]
553			entry.type					= elemBasicType;
554			entry.blockNdx				= curBlockNdx;
555			entry.offset				= curOffset;
556			entry.arraySize				= type.getArraySize();
557			entry.arrayStride			= vecStride*numVecs;
558			entry.matrixStride			= vecStride;
559			entry.isRowMajor			= isRowMajor;
560			entry.topLevelArraySize		= topLevelArraySize;
561			entry.topLevelArrayStride	= topLevelArrayStride;
562
563			curOffset += numVecs*vecStride*type.getArraySize();
564
565			layout.bufferVars.push_back(entry);
566		}
567		else
568		{
569			DE_ASSERT(elemType.isStructType() || elemType.isArrayType());
570
571			for (int elemNdx = 0; elemNdx < type.getArraySize(); elemNdx++)
572				curOffset += computeReferenceLayout(layout, curBlockNdx, curOffset, curPrefix + "[" + de::toString(elemNdx) + "]", type.getElementType(), layoutFlags);
573		}
574	}
575	else
576	{
577		DE_ASSERT(type.isStructType());
578
579		for (StructType::ConstIterator memberIter = type.getStructPtr()->begin(); memberIter != type.getStructPtr()->end(); memberIter++)
580			curOffset += computeReferenceLayout(layout, curBlockNdx, curOffset, curPrefix + "." + memberIter->getName(), memberIter->getType(), layoutFlags);
581
582		curOffset = deAlign32(curOffset, baseAlignment);
583	}
584
585	return curOffset-baseOffset;
586}
587
588//! Appends all child elements to layout, returns offset increment.
589int computeReferenceLayout (BufferLayout& layout, int curBlockNdx, const std::string& blockPrefix, int baseOffset, const BufferVar& bufVar, deUint32 blockLayoutFlags)
590{
591	const VarType&	varType			= bufVar.getType();
592	const deUint32	combinedFlags	= mergeLayoutFlags(blockLayoutFlags, bufVar.getFlags());
593
594	if (varType.isArrayType())
595	{
596		// Top-level arrays need special care.
597		const int		topLevelArraySize	= varType.getArraySize() == VarType::UNSIZED_ARRAY ? 0 : varType.getArraySize();
598		const string	prefix				= blockPrefix + bufVar.getName() + "[0]";
599		const bool		isStd140			= (blockLayoutFlags & LAYOUT_STD140) != 0;
600		const int		vec4Align			= sizeof(deUint32)*4;
601		const int		baseAlignment		= isStd140 ? computeStd140BaseAlignment(varType, combinedFlags)
602													   : computeStd430BaseAlignment(varType, combinedFlags);
603		int				curOffset			= deAlign32(baseOffset, baseAlignment);
604		const VarType&	elemType			= varType.getElementType();
605
606		if (elemType.isBasicType() && !glu::isDataTypeMatrix(elemType.getBasicType()))
607		{
608			// Array of scalars or vectors.
609			const glu::DataType		elemBasicType	= elemType.getBasicType();
610			const int				elemBaseAlign	= getDataTypeByteAlignment(elemBasicType);
611			const int				stride			= isStd140 ? deAlign32(elemBaseAlign, vec4Align) : elemBaseAlign;
612			BufferVarLayoutEntry	entry;
613
614			entry.name					= prefix;
615			entry.topLevelArraySize		= 1;
616			entry.topLevelArrayStride	= 0;
617			entry.type					= elemBasicType;
618			entry.blockNdx				= curBlockNdx;
619			entry.offset				= curOffset;
620			entry.arraySize				= topLevelArraySize;
621			entry.arrayStride			= stride;
622			entry.matrixStride			= 0;
623
624			layout.bufferVars.push_back(entry);
625
626			curOffset += stride*topLevelArraySize;
627		}
628		else if (elemType.isBasicType() && glu::isDataTypeMatrix(elemType.getBasicType()))
629		{
630			// Array of matrices.
631			const glu::DataType		elemBasicType	= elemType.getBasicType();
632			const bool				isRowMajor		= !!(combinedFlags & LAYOUT_ROW_MAJOR);
633			const int				vecSize			= isRowMajor ? glu::getDataTypeMatrixNumColumns(elemBasicType)
634																 : glu::getDataTypeMatrixNumRows(elemBasicType);
635			const int				numVecs			= isRowMajor ? glu::getDataTypeMatrixNumRows(elemBasicType)
636																 : glu::getDataTypeMatrixNumColumns(elemBasicType);
637			const glu::DataType		vecType			= glu::getDataTypeFloatVec(vecSize);
638			const int				vecBaseAlign	= getDataTypeByteAlignment(vecType);
639			const int				stride			= isStd140 ? deAlign32(vecBaseAlign, vec4Align) : vecBaseAlign;
640			BufferVarLayoutEntry	entry;
641
642			entry.name					= prefix;
643			entry.topLevelArraySize		= 1;
644			entry.topLevelArrayStride	= 0;
645			entry.type					= elemBasicType;
646			entry.blockNdx				= curBlockNdx;
647			entry.offset				= curOffset;
648			entry.arraySize				= topLevelArraySize;
649			entry.arrayStride			= stride*numVecs;
650			entry.matrixStride			= stride;
651			entry.isRowMajor			= isRowMajor;
652
653			layout.bufferVars.push_back(entry);
654
655			curOffset += stride*numVecs*topLevelArraySize;
656		}
657		else
658		{
659			DE_ASSERT(elemType.isStructType() || elemType.isArrayType());
660
661			// Struct base alignment is not added multiple times as curOffset supplied to computeReferenceLayout
662			// was already aligned correctly. Thus computeReferenceLayout should not add any extra padding
663			// before struct. Padding after struct will be added as it should.
664			//
665			// Stride could be computed prior to creating child elements, but it would essentially require running
666			// the layout computation twice. Instead we fix stride to child elements afterwards.
667
668			const int	firstChildNdx	= (int)layout.bufferVars.size();
669			const int	stride			= computeReferenceLayout(layout, curBlockNdx, curOffset, prefix, varType.getElementType(), combinedFlags);
670
671			for (int childNdx = firstChildNdx; childNdx < (int)layout.bufferVars.size(); childNdx++)
672			{
673				layout.bufferVars[childNdx].topLevelArraySize	= topLevelArraySize;
674				layout.bufferVars[childNdx].topLevelArrayStride	= stride;
675			}
676
677			curOffset += stride*topLevelArraySize;
678		}
679
680		return curOffset-baseOffset;
681	}
682	else
683		return computeReferenceLayout(layout, curBlockNdx, baseOffset, blockPrefix + bufVar.getName(), varType, combinedFlags);
684}
685
686void computeReferenceLayout (BufferLayout& layout, const ShaderInterface& interface)
687{
688	int numBlocks = interface.getNumBlocks();
689
690	for (int blockNdx = 0; blockNdx < numBlocks; blockNdx++)
691	{
692		const BufferBlock&	block			= interface.getBlock(blockNdx);
693		bool				hasInstanceName	= block.getInstanceName() != DE_NULL;
694		std::string			blockPrefix		= hasInstanceName ? (std::string(block.getBlockName()) + ".") : std::string("");
695		int					curOffset		= 0;
696		int					activeBlockNdx	= (int)layout.blocks.size();
697		int					firstVarNdx		= (int)layout.bufferVars.size();
698
699		for (BufferBlock::const_iterator varIter = block.begin(); varIter != block.end(); varIter++)
700		{
701			const BufferVar& bufVar = *varIter;
702			curOffset += computeReferenceLayout(layout, activeBlockNdx,  blockPrefix, curOffset, bufVar, block.getFlags());
703		}
704
705		int	varIndicesEnd	= (int)layout.bufferVars.size();
706		int	blockSize		= curOffset;
707		int	numInstances	= block.isArray() ? block.getArraySize() : 1;
708
709		// Create block layout entries for each instance.
710		for (int instanceNdx = 0; instanceNdx < numInstances; instanceNdx++)
711		{
712			// Allocate entry for instance.
713			layout.blocks.push_back(BlockLayoutEntry());
714			BlockLayoutEntry& blockEntry = layout.blocks.back();
715
716			blockEntry.name = block.getBlockName();
717			blockEntry.size = blockSize;
718
719			// Compute active variable set for block.
720			for (int varNdx = firstVarNdx; varNdx < varIndicesEnd; varNdx++)
721				blockEntry.activeVarIndices.push_back(varNdx);
722
723			if (block.isArray())
724				blockEntry.name += "[" + de::toString(instanceNdx) + "]";
725		}
726	}
727}
728
729// Value generator.
730
731void generateValue (const BufferVarLayoutEntry& entry, int unsizedArraySize, void* basePtr, de::Random& rnd)
732{
733	const glu::DataType	scalarType		= glu::getDataTypeScalarType(entry.type);
734	const int			scalarSize		= glu::getDataTypeScalarSize(entry.type);
735	const int			arraySize		= entry.arraySize == 0 ? unsizedArraySize : entry.arraySize;
736	const int			arrayStride		= entry.arrayStride;
737	const int			topLevelSize	= entry.topLevelArraySize == 0 ? unsizedArraySize : entry.topLevelArraySize;
738	const int			topLevelStride	= entry.topLevelArrayStride;
739	const bool			isMatrix		= glu::isDataTypeMatrix(entry.type);
740	const int			numVecs			= isMatrix ? (entry.isRowMajor ? glu::getDataTypeMatrixNumRows(entry.type) : glu::getDataTypeMatrixNumColumns(entry.type)) : 1;
741	const int			vecSize			= scalarSize / numVecs;
742	const int			compSize		= sizeof(deUint32);
743
744	DE_ASSERT(scalarSize%numVecs == 0);
745	DE_ASSERT(topLevelSize >= 0);
746	DE_ASSERT(arraySize >= 0);
747
748	for (int topElemNdx = 0; topElemNdx < topLevelSize; topElemNdx++)
749	{
750		deUint8* const topElemPtr = (deUint8*)basePtr + entry.offset + topElemNdx*topLevelStride;
751
752		for (int elemNdx = 0; elemNdx < arraySize; elemNdx++)
753		{
754			deUint8* const elemPtr = topElemPtr + elemNdx*arrayStride;
755
756			for (int vecNdx = 0; vecNdx < numVecs; vecNdx++)
757			{
758				deUint8* const vecPtr = elemPtr + (isMatrix ? vecNdx*entry.matrixStride : 0);
759
760				for (int compNdx = 0; compNdx < vecSize; compNdx++)
761				{
762					deUint8* const compPtr = vecPtr + compSize*compNdx;
763
764					switch (scalarType)
765					{
766						case glu::TYPE_FLOAT:	*((float*)compPtr)		= (float)rnd.getInt(-9, 9);						break;
767						case glu::TYPE_INT:		*((int*)compPtr)		= rnd.getInt(-9, 9);							break;
768						case glu::TYPE_UINT:	*((deUint32*)compPtr)	= (deUint32)rnd.getInt(0, 9);					break;
769						// \note Random bit pattern is used for true values. Spec states that all non-zero values are
770						//       interpreted as true but some implementations fail this.
771						case glu::TYPE_BOOL:	*((deUint32*)compPtr)	= rnd.getBool() ? rnd.getUint32()|1u : 0u;		break;
772						default:
773							DE_ASSERT(false);
774					}
775				}
776			}
777		}
778	}
779}
780
781void generateValues (const BufferLayout& layout, const vector<BlockDataPtr>& blockPointers, deUint32 seed)
782{
783	de::Random	rnd			(seed);
784	const int	numBlocks	= (int)layout.blocks.size();
785
786	DE_ASSERT(numBlocks == (int)blockPointers.size());
787
788	for (int blockNdx = 0; blockNdx < numBlocks; blockNdx++)
789	{
790		const BlockLayoutEntry&	blockLayout	= layout.blocks[blockNdx];
791		const BlockDataPtr&		blockPtr	= blockPointers[blockNdx];
792		const int				numEntries	= (int)layout.blocks[blockNdx].activeVarIndices.size();
793
794		for (int entryNdx = 0; entryNdx < numEntries; entryNdx++)
795		{
796			const int					varNdx		= blockLayout.activeVarIndices[entryNdx];
797			const BufferVarLayoutEntry&	varEntry	= layout.bufferVars[varNdx];
798
799			generateValue(varEntry, blockPtr.lastUnsizedArraySize, blockPtr.ptr, rnd);
800		}
801	}
802}
803
804// Shader generator.
805
806const char* getCompareFuncForType (glu::DataType type)
807{
808	switch (type)
809	{
810		case glu::TYPE_FLOAT:			return "bool compare_float    (highp float a, highp float b)  { return abs(a - b) < 0.05; }\n";
811		case glu::TYPE_FLOAT_VEC2:		return "bool compare_vec2     (highp vec2 a, highp vec2 b)    { return compare_float(a.x, b.x)&&compare_float(a.y, b.y); }\n";
812		case glu::TYPE_FLOAT_VEC3:		return "bool compare_vec3     (highp vec3 a, highp vec3 b)    { return compare_float(a.x, b.x)&&compare_float(a.y, b.y)&&compare_float(a.z, b.z); }\n";
813		case glu::TYPE_FLOAT_VEC4:		return "bool compare_vec4     (highp vec4 a, highp vec4 b)    { return compare_float(a.x, b.x)&&compare_float(a.y, b.y)&&compare_float(a.z, b.z)&&compare_float(a.w, b.w); }\n";
814		case glu::TYPE_FLOAT_MAT2:		return "bool compare_mat2     (highp mat2 a, highp mat2 b)    { return compare_vec2(a[0], b[0])&&compare_vec2(a[1], b[1]); }\n";
815		case glu::TYPE_FLOAT_MAT2X3:	return "bool compare_mat2x3   (highp mat2x3 a, highp mat2x3 b){ return compare_vec3(a[0], b[0])&&compare_vec3(a[1], b[1]); }\n";
816		case glu::TYPE_FLOAT_MAT2X4:	return "bool compare_mat2x4   (highp mat2x4 a, highp mat2x4 b){ return compare_vec4(a[0], b[0])&&compare_vec4(a[1], b[1]); }\n";
817		case glu::TYPE_FLOAT_MAT3X2:	return "bool compare_mat3x2   (highp mat3x2 a, highp mat3x2 b){ return compare_vec2(a[0], b[0])&&compare_vec2(a[1], b[1])&&compare_vec2(a[2], b[2]); }\n";
818		case glu::TYPE_FLOAT_MAT3:		return "bool compare_mat3     (highp mat3 a, highp mat3 b)    { return compare_vec3(a[0], b[0])&&compare_vec3(a[1], b[1])&&compare_vec3(a[2], b[2]); }\n";
819		case glu::TYPE_FLOAT_MAT3X4:	return "bool compare_mat3x4   (highp mat3x4 a, highp mat3x4 b){ return compare_vec4(a[0], b[0])&&compare_vec4(a[1], b[1])&&compare_vec4(a[2], b[2]); }\n";
820		case glu::TYPE_FLOAT_MAT4X2:	return "bool compare_mat4x2   (highp mat4x2 a, highp mat4x2 b){ return compare_vec2(a[0], b[0])&&compare_vec2(a[1], b[1])&&compare_vec2(a[2], b[2])&&compare_vec2(a[3], b[3]); }\n";
821		case glu::TYPE_FLOAT_MAT4X3:	return "bool compare_mat4x3   (highp mat4x3 a, highp mat4x3 b){ return compare_vec3(a[0], b[0])&&compare_vec3(a[1], b[1])&&compare_vec3(a[2], b[2])&&compare_vec3(a[3], b[3]); }\n";
822		case glu::TYPE_FLOAT_MAT4:		return "bool compare_mat4     (highp mat4 a, highp mat4 b)    { return compare_vec4(a[0], b[0])&&compare_vec4(a[1], b[1])&&compare_vec4(a[2], b[2])&&compare_vec4(a[3], b[3]); }\n";
823		case glu::TYPE_INT:				return "bool compare_int      (highp int a, highp int b)      { return a == b; }\n";
824		case glu::TYPE_INT_VEC2:		return "bool compare_ivec2    (highp ivec2 a, highp ivec2 b)  { return a == b; }\n";
825		case glu::TYPE_INT_VEC3:		return "bool compare_ivec3    (highp ivec3 a, highp ivec3 b)  { return a == b; }\n";
826		case glu::TYPE_INT_VEC4:		return "bool compare_ivec4    (highp ivec4 a, highp ivec4 b)  { return a == b; }\n";
827		case glu::TYPE_UINT:			return "bool compare_uint     (highp uint a, highp uint b)    { return a == b; }\n";
828		case glu::TYPE_UINT_VEC2:		return "bool compare_uvec2    (highp uvec2 a, highp uvec2 b)  { return a == b; }\n";
829		case glu::TYPE_UINT_VEC3:		return "bool compare_uvec3    (highp uvec3 a, highp uvec3 b)  { return a == b; }\n";
830		case glu::TYPE_UINT_VEC4:		return "bool compare_uvec4    (highp uvec4 a, highp uvec4 b)  { return a == b; }\n";
831		case glu::TYPE_BOOL:			return "bool compare_bool     (bool a, bool b)                { return a == b; }\n";
832		case glu::TYPE_BOOL_VEC2:		return "bool compare_bvec2    (bvec2 a, bvec2 b)              { return a == b; }\n";
833		case glu::TYPE_BOOL_VEC3:		return "bool compare_bvec3    (bvec3 a, bvec3 b)              { return a == b; }\n";
834		case glu::TYPE_BOOL_VEC4:		return "bool compare_bvec4    (bvec4 a, bvec4 b)              { return a == b; }\n";
835		default:
836			DE_ASSERT(false);
837			return DE_NULL;
838	}
839}
840
841void getCompareDependencies (std::set<glu::DataType>& compareFuncs, glu::DataType basicType)
842{
843	switch (basicType)
844	{
845		case glu::TYPE_FLOAT_VEC2:
846		case glu::TYPE_FLOAT_VEC3:
847		case glu::TYPE_FLOAT_VEC4:
848			compareFuncs.insert(glu::TYPE_FLOAT);
849			compareFuncs.insert(basicType);
850			break;
851
852		case glu::TYPE_FLOAT_MAT2:
853		case glu::TYPE_FLOAT_MAT2X3:
854		case glu::TYPE_FLOAT_MAT2X4:
855		case glu::TYPE_FLOAT_MAT3X2:
856		case glu::TYPE_FLOAT_MAT3:
857		case glu::TYPE_FLOAT_MAT3X4:
858		case glu::TYPE_FLOAT_MAT4X2:
859		case glu::TYPE_FLOAT_MAT4X3:
860		case glu::TYPE_FLOAT_MAT4:
861			compareFuncs.insert(glu::TYPE_FLOAT);
862			compareFuncs.insert(glu::getDataTypeFloatVec(glu::getDataTypeMatrixNumRows(basicType)));
863			compareFuncs.insert(basicType);
864			break;
865
866		default:
867			compareFuncs.insert(basicType);
868			break;
869	}
870}
871
872void collectUniqueBasicTypes (std::set<glu::DataType>& basicTypes, const VarType& type)
873{
874	if (type.isStructType())
875	{
876		for (StructType::ConstIterator iter = type.getStructPtr()->begin(); iter != type.getStructPtr()->end(); ++iter)
877			collectUniqueBasicTypes(basicTypes, iter->getType());
878	}
879	else if (type.isArrayType())
880		collectUniqueBasicTypes(basicTypes, type.getElementType());
881	else
882	{
883		DE_ASSERT(type.isBasicType());
884		basicTypes.insert(type.getBasicType());
885	}
886}
887
888void collectUniqueBasicTypes (std::set<glu::DataType>& basicTypes, const BufferBlock& bufferBlock)
889{
890	for (BufferBlock::const_iterator iter = bufferBlock.begin(); iter != bufferBlock.end(); ++iter)
891		collectUniqueBasicTypes(basicTypes, iter->getType());
892}
893
894void collectUniqueBasicTypes (std::set<glu::DataType>& basicTypes, const ShaderInterface& interface)
895{
896	for (int ndx = 0; ndx < interface.getNumBlocks(); ++ndx)
897		collectUniqueBasicTypes(basicTypes, interface.getBlock(ndx));
898}
899
900void generateCompareFuncs (std::ostream& str, const ShaderInterface& interface)
901{
902	std::set<glu::DataType> types;
903	std::set<glu::DataType> compareFuncs;
904
905	// Collect unique basic types
906	collectUniqueBasicTypes(types, interface);
907
908	// Set of compare functions required
909	for (std::set<glu::DataType>::const_iterator iter = types.begin(); iter != types.end(); ++iter)
910	{
911		getCompareDependencies(compareFuncs, *iter);
912	}
913
914	for (int type = 0; type < glu::TYPE_LAST; ++type)
915	{
916		if (compareFuncs.find(glu::DataType(type)) != compareFuncs.end())
917			str << getCompareFuncForType(glu::DataType(type));
918	}
919}
920
921struct Indent
922{
923	int level;
924	Indent (int level_) : level(level_) {}
925};
926
927std::ostream& operator<< (std::ostream& str, const Indent& indent)
928{
929	for (int i = 0; i < indent.level; i++)
930		str << "\t";
931	return str;
932}
933
934void generateDeclaration (std::ostream& src, const BufferVar& bufferVar, int indentLevel)
935{
936	// \todo [pyry] Qualifiers
937
938	if ((bufferVar.getFlags() & LAYOUT_MASK) != 0)
939		src << "layout(" << LayoutFlagsFmt(bufferVar.getFlags() & LAYOUT_MASK) << ") ";
940
941	src << glu::declare(bufferVar.getType(), bufferVar.getName(), indentLevel);
942}
943
944void generateDeclaration (std::ostream& src, const BufferBlock& block, int bindingPoint)
945{
946	src << "layout(";
947
948	if ((block.getFlags() & LAYOUT_MASK) != 0)
949		src << LayoutFlagsFmt(block.getFlags() & LAYOUT_MASK) << ", ";
950
951	src << "binding = " << bindingPoint;
952
953	src << ") ";
954
955	src << "buffer " << block.getBlockName();
956	src << "\n{\n";
957
958	for (BufferBlock::const_iterator varIter = block.begin(); varIter != block.end(); varIter++)
959	{
960		src << Indent(1);
961		generateDeclaration(src, *varIter, 1 /* indent level */);
962		src << ";\n";
963	}
964
965	src << "}";
966
967	if (block.getInstanceName() != DE_NULL)
968	{
969		src << " " << block.getInstanceName();
970		if (block.isArray())
971			src << "[" << block.getArraySize() << "]";
972	}
973	else
974		DE_ASSERT(!block.isArray());
975
976	src << ";\n";
977}
978
979void generateImmMatrixSrc (std::ostream& src, glu::DataType basicType, int matrixStride, bool isRowMajor, const void* valuePtr)
980{
981	DE_ASSERT(glu::isDataTypeMatrix(basicType));
982
983	const int		compSize		= sizeof(deUint32);
984	const int		numRows			= glu::getDataTypeMatrixNumRows(basicType);
985	const int		numCols			= glu::getDataTypeMatrixNumColumns(basicType);
986
987	src << glu::getDataTypeName(basicType) << "(";
988
989	// Constructed in column-wise order.
990	for (int colNdx = 0; colNdx < numCols; colNdx++)
991	{
992		for (int rowNdx = 0; rowNdx < numRows; rowNdx++)
993		{
994			const deUint8*	compPtr	= (const deUint8*)valuePtr + (isRowMajor ? rowNdx*matrixStride + colNdx*compSize
995																				: colNdx*matrixStride + rowNdx*compSize);
996
997			if (colNdx > 0 || rowNdx > 0)
998				src << ", ";
999
1000			src << de::floatToString(*((const float*)compPtr), 1);
1001		}
1002	}
1003
1004	src << ")";
1005}
1006
1007void generateImmScalarVectorSrc (std::ostream& src, glu::DataType basicType, const void* valuePtr)
1008{
1009	DE_ASSERT(glu::isDataTypeFloatOrVec(basicType)	||
1010			  glu::isDataTypeIntOrIVec(basicType)	||
1011			  glu::isDataTypeUintOrUVec(basicType)	||
1012			  glu::isDataTypeBoolOrBVec(basicType));
1013
1014	const glu::DataType		scalarType		= glu::getDataTypeScalarType(basicType);
1015	const int				scalarSize		= glu::getDataTypeScalarSize(basicType);
1016	const int				compSize		= sizeof(deUint32);
1017
1018	if (scalarSize > 1)
1019		src << glu::getDataTypeName(basicType) << "(";
1020
1021	for (int scalarNdx = 0; scalarNdx < scalarSize; scalarNdx++)
1022	{
1023		const deUint8* compPtr = (const deUint8*)valuePtr + scalarNdx*compSize;
1024
1025		if (scalarNdx > 0)
1026			src << ", ";
1027
1028		switch (scalarType)
1029		{
1030			case glu::TYPE_FLOAT:	src << de::floatToString(*((const float*)compPtr), 1);			break;
1031			case glu::TYPE_INT:		src << *((const int*)compPtr);									break;
1032			case glu::TYPE_UINT:	src << *((const deUint32*)compPtr) << "u";						break;
1033			case glu::TYPE_BOOL:	src << (*((const deUint32*)compPtr) != 0u ? "true" : "false");	break;
1034			default:
1035				DE_ASSERT(false);
1036		}
1037	}
1038
1039	if (scalarSize > 1)
1040		src << ")";
1041}
1042
1043string getAPIName (const BufferBlock& block, const BufferVar& var, const glu::TypeComponentVector& accessPath)
1044{
1045	std::ostringstream name;
1046
1047	if (block.getInstanceName())
1048		name << block.getBlockName() << ".";
1049
1050	name << var.getName();
1051
1052	for (glu::TypeComponentVector::const_iterator pathComp = accessPath.begin(); pathComp != accessPath.end(); pathComp++)
1053	{
1054		if (pathComp->type == glu::VarTypeComponent::STRUCT_MEMBER)
1055		{
1056			const VarType		curType		= glu::getVarType(var.getType(), accessPath.begin(), pathComp);
1057			const StructType*	structPtr	= curType.getStructPtr();
1058
1059			name << "." << structPtr->getMember(pathComp->index).getName();
1060		}
1061		else if (pathComp->type == glu::VarTypeComponent::ARRAY_ELEMENT)
1062		{
1063			if (pathComp == accessPath.begin() || (pathComp+1) == accessPath.end())
1064				name << "[0]"; // Top- / bottom-level array
1065			else
1066				name << "[" << pathComp->index << "]";
1067		}
1068		else
1069			DE_ASSERT(false);
1070	}
1071
1072	return name.str();
1073}
1074
1075string getShaderName (const BufferBlock& block, int instanceNdx, const BufferVar& var, const glu::TypeComponentVector& accessPath)
1076{
1077	std::ostringstream name;
1078
1079	if (block.getInstanceName())
1080	{
1081		name << block.getInstanceName();
1082
1083		if (block.isArray())
1084			name << "[" << instanceNdx << "]";
1085
1086		name << ".";
1087	}
1088	else
1089		DE_ASSERT(instanceNdx == 0);
1090
1091	name << var.getName();
1092
1093	for (glu::TypeComponentVector::const_iterator pathComp = accessPath.begin(); pathComp != accessPath.end(); pathComp++)
1094	{
1095		if (pathComp->type == glu::VarTypeComponent::STRUCT_MEMBER)
1096		{
1097			const VarType		curType		= glu::getVarType(var.getType(), accessPath.begin(), pathComp);
1098			const StructType*	structPtr	= curType.getStructPtr();
1099
1100			name << "." << structPtr->getMember(pathComp->index).getName();
1101		}
1102		else if (pathComp->type == glu::VarTypeComponent::ARRAY_ELEMENT)
1103			name << "[" << pathComp->index << "]";
1104		else
1105			DE_ASSERT(false);
1106	}
1107
1108	return name.str();
1109}
1110
1111int computeOffset (const BufferVarLayoutEntry& varLayout, const glu::TypeComponentVector& accessPath)
1112{
1113	const int	topLevelNdx		= (accessPath.size() > 1 && accessPath.front().type == glu::VarTypeComponent::ARRAY_ELEMENT) ? accessPath.front().index : 0;
1114	const int	bottomLevelNdx	= (!accessPath.empty() && accessPath.back().type == glu::VarTypeComponent::ARRAY_ELEMENT) ? accessPath.back().index : 0;
1115
1116	return varLayout.offset + varLayout.topLevelArrayStride*topLevelNdx + varLayout.arrayStride*bottomLevelNdx;
1117}
1118
1119void generateCompareSrc (
1120	std::ostream&				src,
1121	const char*					resultVar,
1122	const BufferLayout&			bufferLayout,
1123	const BufferBlock&			block,
1124	int							instanceNdx,
1125	const BlockDataPtr&			blockPtr,
1126	const BufferVar&			bufVar,
1127	const glu::SubTypeAccess&	accessPath)
1128{
1129	const VarType curType = accessPath.getType();
1130
1131	if (curType.isArrayType())
1132	{
1133		const int arraySize = curType.getArraySize() == VarType::UNSIZED_ARRAY ? block.getLastUnsizedArraySize(instanceNdx) : curType.getArraySize();
1134
1135		for (int elemNdx = 0; elemNdx < arraySize; elemNdx++)
1136			generateCompareSrc(src, resultVar, bufferLayout, block, instanceNdx, blockPtr, bufVar, accessPath.element(elemNdx));
1137	}
1138	else if (curType.isStructType())
1139	{
1140		const int numMembers = curType.getStructPtr()->getNumMembers();
1141
1142		for (int memberNdx = 0; memberNdx < numMembers; memberNdx++)
1143			generateCompareSrc(src, resultVar, bufferLayout, block, instanceNdx, blockPtr, bufVar, accessPath.member(memberNdx));
1144	}
1145	else
1146	{
1147		DE_ASSERT(curType.isBasicType());
1148
1149		const string	apiName	= getAPIName(block, bufVar, accessPath.getPath());
1150		const int		varNdx	= bufferLayout.getVariableIndex(apiName);
1151
1152		DE_ASSERT(varNdx >= 0);
1153		{
1154			const BufferVarLayoutEntry&	varLayout		= bufferLayout.bufferVars[varNdx];
1155			const string				shaderName		= getShaderName(block, instanceNdx, bufVar, accessPath.getPath());
1156			const glu::DataType			basicType		= curType.getBasicType();
1157			const bool					isMatrix		= glu::isDataTypeMatrix(basicType);
1158			const char*					typeName		= glu::getDataTypeName(basicType);
1159			const void*					valuePtr		= (const deUint8*)blockPtr.ptr + computeOffset(varLayout, accessPath.getPath());
1160
1161			src << "\t" << resultVar << " = " << resultVar << " && compare_" << typeName << "(" << shaderName << ", ";
1162
1163			if (isMatrix)
1164				generateImmMatrixSrc(src, basicType, varLayout.matrixStride, varLayout.isRowMajor, valuePtr);
1165			else
1166				generateImmScalarVectorSrc(src, basicType, valuePtr);
1167
1168			src << ");\n";
1169		}
1170	}
1171}
1172
1173void generateCompareSrc (std::ostream& src, const char* resultVar, const ShaderInterface& interface, const BufferLayout& layout, const vector<BlockDataPtr>& blockPointers)
1174{
1175	for (int declNdx = 0; declNdx < interface.getNumBlocks(); declNdx++)
1176	{
1177		const BufferBlock&	block			= interface.getBlock(declNdx);
1178		const bool			isArray			= block.isArray();
1179		const int			numInstances	= isArray ? block.getArraySize() : 1;
1180
1181		DE_ASSERT(!isArray || block.getInstanceName());
1182
1183		for (int instanceNdx = 0; instanceNdx < numInstances; instanceNdx++)
1184		{
1185			const string		instanceName	= block.getBlockName() + (isArray ? "[" + de::toString(instanceNdx) + "]" : string(""));
1186			const int			blockNdx		= layout.getBlockIndex(instanceName);
1187			const BlockDataPtr&	blockPtr		= blockPointers[blockNdx];
1188
1189			for (BufferBlock::const_iterator varIter = block.begin(); varIter != block.end(); varIter++)
1190			{
1191				const BufferVar& bufVar = *varIter;
1192
1193				if ((bufVar.getFlags() & ACCESS_READ) == 0)
1194					continue; // Don't read from that variable.
1195
1196				generateCompareSrc(src, resultVar, layout, block, instanceNdx, blockPtr, bufVar, glu::SubTypeAccess(bufVar.getType()));
1197			}
1198		}
1199	}
1200}
1201
1202// \todo [2013-10-14 pyry] Almost identical to generateCompareSrc - unify?
1203
1204void generateWriteSrc (
1205	std::ostream&				src,
1206	const BufferLayout&			bufferLayout,
1207	const BufferBlock&			block,
1208	int							instanceNdx,
1209	const BlockDataPtr&			blockPtr,
1210	const BufferVar&			bufVar,
1211	const glu::SubTypeAccess&	accessPath)
1212{
1213	const VarType curType = accessPath.getType();
1214
1215	if (curType.isArrayType())
1216	{
1217		const int arraySize = curType.getArraySize() == VarType::UNSIZED_ARRAY ? block.getLastUnsizedArraySize(instanceNdx) : curType.getArraySize();
1218
1219		for (int elemNdx = 0; elemNdx < arraySize; elemNdx++)
1220			generateWriteSrc(src, bufferLayout, block, instanceNdx, blockPtr, bufVar, accessPath.element(elemNdx));
1221	}
1222	else if (curType.isStructType())
1223	{
1224		const int numMembers = curType.getStructPtr()->getNumMembers();
1225
1226		for (int memberNdx = 0; memberNdx < numMembers; memberNdx++)
1227			generateWriteSrc(src, bufferLayout, block, instanceNdx, blockPtr, bufVar, accessPath.member(memberNdx));
1228	}
1229	else
1230	{
1231		DE_ASSERT(curType.isBasicType());
1232
1233		const string	apiName	= getAPIName(block, bufVar, accessPath.getPath());
1234		const int		varNdx	= bufferLayout.getVariableIndex(apiName);
1235
1236		DE_ASSERT(varNdx >= 0);
1237		{
1238			const BufferVarLayoutEntry&	varLayout		= bufferLayout.bufferVars[varNdx];
1239			const string				shaderName		= getShaderName(block, instanceNdx, bufVar, accessPath.getPath());
1240			const glu::DataType			basicType		= curType.getBasicType();
1241			const bool					isMatrix		= glu::isDataTypeMatrix(basicType);
1242			const void*					valuePtr		= (const deUint8*)blockPtr.ptr + computeOffset(varLayout, accessPath.getPath());
1243
1244			src << "\t" << shaderName << " = ";
1245
1246			if (isMatrix)
1247				generateImmMatrixSrc(src, basicType, varLayout.matrixStride, varLayout.isRowMajor, valuePtr);
1248			else
1249				generateImmScalarVectorSrc(src, basicType, valuePtr);
1250
1251			src << ";\n";
1252		}
1253	}
1254}
1255
1256void generateWriteSrc (std::ostream& src, const ShaderInterface& interface, const BufferLayout& layout, const vector<BlockDataPtr>& blockPointers)
1257{
1258	for (int declNdx = 0; declNdx < interface.getNumBlocks(); declNdx++)
1259	{
1260		const BufferBlock&	block			= interface.getBlock(declNdx);
1261		const bool			isArray			= block.isArray();
1262		const int			numInstances	= isArray ? block.getArraySize() : 1;
1263
1264		DE_ASSERT(!isArray || block.getInstanceName());
1265
1266		for (int instanceNdx = 0; instanceNdx < numInstances; instanceNdx++)
1267		{
1268			const string		instanceName	= block.getBlockName() + (isArray ? "[" + de::toString(instanceNdx) + "]" : string(""));
1269			const int			blockNdx		= layout.getBlockIndex(instanceName);
1270			const BlockDataPtr&	blockPtr		= blockPointers[blockNdx];
1271
1272			for (BufferBlock::const_iterator varIter = block.begin(); varIter != block.end(); varIter++)
1273			{
1274				const BufferVar& bufVar = *varIter;
1275
1276				if ((bufVar.getFlags() & ACCESS_WRITE) == 0)
1277					continue; // Don't write to that variable.
1278
1279				generateWriteSrc(src, layout, block, instanceNdx, blockPtr, bufVar, glu::SubTypeAccess(bufVar.getType()));
1280			}
1281		}
1282	}
1283}
1284
1285string generateComputeShader (glu::GLSLVersion glslVersion, const ShaderInterface& interface, const BufferLayout& layout, const vector<BlockDataPtr>& comparePtrs, const vector<BlockDataPtr>& writePtrs)
1286{
1287	std::ostringstream src;
1288
1289	DE_ASSERT(glslVersion == glu::GLSL_VERSION_310_ES || glslVersion == glu::GLSL_VERSION_430);
1290
1291	src << glu::getGLSLVersionDeclaration(glslVersion) << "\n";
1292	src << "layout(local_size_x = 1) in;\n";
1293	src << "\n";
1294
1295	std::vector<const StructType*> namedStructs;
1296	interface.getNamedStructs(namedStructs);
1297	for (std::vector<const StructType*>::const_iterator structIter = namedStructs.begin(); structIter != namedStructs.end(); structIter++)
1298		src << glu::declare(*structIter) << ";\n";
1299
1300	{
1301		int bindingPoint = 0;
1302
1303		for (int blockNdx = 0; blockNdx < interface.getNumBlocks(); blockNdx++)
1304		{
1305			const BufferBlock& block = interface.getBlock(blockNdx);
1306			generateDeclaration(src, block, bindingPoint);
1307
1308			bindingPoint += block.isArray() ? block.getArraySize() : 1;
1309		}
1310	}
1311
1312	// Atomic counter for counting passed invocations.
1313	src << "\nlayout(binding = 0) uniform atomic_uint ac_numPassed;\n";
1314
1315	// Comparison utilities.
1316	src << "\n";
1317	generateCompareFuncs(src, interface);
1318
1319	src << "\n"
1320		   "void main (void)\n"
1321		   "{\n"
1322		   "	bool allOk = true;\n";
1323
1324	// Value compare.
1325	generateCompareSrc(src, "allOk", interface, layout, comparePtrs);
1326
1327	src << "	if (allOk)\n"
1328		<< "		atomicCounterIncrement(ac_numPassed);\n"
1329		<< "\n";
1330
1331	// Value write.
1332	generateWriteSrc(src, interface, layout, writePtrs);
1333
1334	src << "}\n";
1335
1336	return src.str();
1337}
1338
1339void getGLBufferLayout (const glw::Functions& gl, BufferLayout& layout, deUint32 program)
1340{
1341	int		numActiveBufferVars	= 0;
1342	int		numActiveBlocks		= 0;
1343
1344	gl.getProgramInterfaceiv(program, GL_BUFFER_VARIABLE,		GL_ACTIVE_RESOURCES,	&numActiveBufferVars);
1345	gl.getProgramInterfaceiv(program, GL_SHADER_STORAGE_BLOCK,	GL_ACTIVE_RESOURCES,	&numActiveBlocks);
1346
1347	GLU_EXPECT_NO_ERROR(gl.getError(), "Failed to get number of buffer variables and buffer blocks");
1348
1349	// Block entries.
1350	layout.blocks.resize(numActiveBlocks);
1351	for (int blockNdx = 0; blockNdx < numActiveBlocks; blockNdx++)
1352	{
1353		BlockLayoutEntry&	entry				= layout.blocks[blockNdx];
1354		const deUint32		queryParams[]		= { GL_BUFFER_DATA_SIZE, GL_NUM_ACTIVE_VARIABLES, GL_NAME_LENGTH };
1355		int					returnValues[]		= { 0, 0, 0 };
1356
1357		DE_STATIC_ASSERT(DE_LENGTH_OF_ARRAY(queryParams) == DE_LENGTH_OF_ARRAY(returnValues));
1358
1359		{
1360			int returnLength = 0;
1361			gl.getProgramResourceiv(program, GL_SHADER_STORAGE_BLOCK, (deUint32)blockNdx, DE_LENGTH_OF_ARRAY(queryParams), &queryParams[0], DE_LENGTH_OF_ARRAY(returnValues), &returnLength, &returnValues[0]);
1362			GLU_EXPECT_NO_ERROR(gl.getError(), "glGetProgramResourceiv(GL_SHADER_STORAGE_BLOCK) failed");
1363
1364			if (returnLength != DE_LENGTH_OF_ARRAY(returnValues))
1365				throw tcu::TestError("glGetProgramResourceiv(GL_SHADER_STORAGE_BLOCK) returned wrong number of values");
1366		}
1367
1368		entry.size = returnValues[0];
1369
1370		// Query active variables
1371		if (returnValues[1] > 0)
1372		{
1373			const int		numBlockVars	= returnValues[1];
1374			const deUint32	queryArg		= GL_ACTIVE_VARIABLES;
1375			int				retLength		= 0;
1376
1377			entry.activeVarIndices.resize(numBlockVars);
1378			gl.getProgramResourceiv(program, GL_SHADER_STORAGE_BLOCK, (deUint32)blockNdx, 1, &queryArg, numBlockVars, &retLength, &entry.activeVarIndices[0]);
1379			GLU_EXPECT_NO_ERROR(gl.getError(), "glGetProgramResourceiv(GL_SHADER_STORAGE_BLOCK, GL_ACTIVE_VARIABLES) failed");
1380
1381			if (retLength != numBlockVars)
1382				throw tcu::TestError("glGetProgramResourceiv(GL_SHADER_STORAGE_BLOCK, GL_ACTIVE_VARIABLES) returned wrong number of values");
1383		}
1384
1385		// Query name
1386		if (returnValues[2] > 0)
1387		{
1388			const int		nameLen		= returnValues[2];
1389			int				retLen		= 0;
1390			vector<char>	name		(nameLen);
1391
1392			gl.getProgramResourceName(program, GL_SHADER_STORAGE_BLOCK, (deUint32)blockNdx, (glw::GLsizei)name.size(), &retLen, &name[0]);
1393			GLU_EXPECT_NO_ERROR(gl.getError(), "glGetProgramResourceName(GL_SHADER_STORAGE_BLOCK) failed");
1394
1395			if (retLen+1 != nameLen)
1396				throw tcu::TestError("glGetProgramResourceName(GL_SHADER_STORAGE_BLOCK) returned invalid name. Number of characters written is inconsistent with NAME_LENGTH property.");
1397			if (name[nameLen-1] != 0)
1398				throw tcu::TestError("glGetProgramResourceName(GL_SHADER_STORAGE_BLOCK) returned invalid name. Expected null terminator at index " + de::toString(nameLen-1));
1399
1400			entry.name = &name[0];
1401		}
1402		else
1403			throw tcu::TestError("glGetProgramResourceiv() returned invalid GL_NAME_LENGTH");
1404	}
1405
1406	layout.bufferVars.resize(numActiveBufferVars);
1407	for (int bufVarNdx = 0; bufVarNdx < numActiveBufferVars; bufVarNdx++)
1408	{
1409		BufferVarLayoutEntry&	entry				= layout.bufferVars[bufVarNdx];
1410		const deUint32			queryParams[] =
1411		{
1412			GL_BLOCK_INDEX,					// 0
1413			GL_TYPE,						// 1
1414			GL_OFFSET,						// 2
1415			GL_ARRAY_SIZE,					// 3
1416			GL_ARRAY_STRIDE,				// 4
1417			GL_MATRIX_STRIDE,				// 5
1418			GL_TOP_LEVEL_ARRAY_SIZE,		// 6
1419			GL_TOP_LEVEL_ARRAY_STRIDE,		// 7
1420			GL_IS_ROW_MAJOR,				// 8
1421			GL_NAME_LENGTH					// 9
1422		};
1423		int returnValues[DE_LENGTH_OF_ARRAY(queryParams)];
1424
1425		DE_STATIC_ASSERT(DE_LENGTH_OF_ARRAY(queryParams) == DE_LENGTH_OF_ARRAY(returnValues));
1426
1427		{
1428			int returnLength = 0;
1429			gl.getProgramResourceiv(program, GL_BUFFER_VARIABLE, (deUint32)bufVarNdx, DE_LENGTH_OF_ARRAY(queryParams), &queryParams[0], DE_LENGTH_OF_ARRAY(returnValues), &returnLength, &returnValues[0]);
1430			GLU_EXPECT_NO_ERROR(gl.getError(), "glGetProgramResourceiv(GL_BUFFER_VARIABLE) failed");
1431
1432			if (returnLength != DE_LENGTH_OF_ARRAY(returnValues))
1433				throw tcu::TestError("glGetProgramResourceiv(GL_BUFFER_VARIABLE) returned wrong number of values");
1434		}
1435
1436		// Map values
1437		entry.blockNdx				= returnValues[0];
1438		entry.type					= glu::getDataTypeFromGLType(returnValues[1]);
1439		entry.offset				= returnValues[2];
1440		entry.arraySize				= returnValues[3];
1441		entry.arrayStride			= returnValues[4];
1442		entry.matrixStride			= returnValues[5];
1443		entry.topLevelArraySize		= returnValues[6];
1444		entry.topLevelArrayStride	= returnValues[7];
1445		entry.isRowMajor			= returnValues[8] != 0;
1446
1447		// Query name
1448		DE_ASSERT(queryParams[9] == GL_NAME_LENGTH);
1449		if (returnValues[9] > 0)
1450		{
1451			const int		nameLen		= returnValues[9];
1452			int				retLen		= 0;
1453			vector<char>	name		(nameLen);
1454
1455			gl.getProgramResourceName(program, GL_BUFFER_VARIABLE, (deUint32)bufVarNdx, (glw::GLsizei)name.size(), &retLen, &name[0]);
1456			GLU_EXPECT_NO_ERROR(gl.getError(), "glGetProgramResourceName(GL_BUFFER_VARIABLE) failed");
1457
1458			if (retLen+1 != nameLen)
1459				throw tcu::TestError("glGetProgramResourceName(GL_BUFFER_VARIABLE) returned invalid name. Number of characters written is inconsistent with NAME_LENGTH property.");
1460			if (name[nameLen-1] != 0)
1461				throw tcu::TestError("glGetProgramResourceName(GL_BUFFER_VARIABLE) returned invalid name. Expected null terminator at index " + de::toString(nameLen-1));
1462
1463			entry.name = &name[0];
1464		}
1465		else
1466			throw tcu::TestError("glGetProgramResourceiv() returned invalid GL_NAME_LENGTH");
1467	}
1468}
1469
1470void copyBufferVarData (const BufferVarLayoutEntry& dstEntry, const BlockDataPtr& dstBlockPtr, const BufferVarLayoutEntry& srcEntry, const BlockDataPtr& srcBlockPtr)
1471{
1472	DE_ASSERT(dstEntry.arraySize <= srcEntry.arraySize);
1473	DE_ASSERT(dstEntry.topLevelArraySize <= srcEntry.topLevelArraySize);
1474	DE_ASSERT(dstBlockPtr.lastUnsizedArraySize <= srcBlockPtr.lastUnsizedArraySize);
1475	DE_ASSERT(dstEntry.type == srcEntry.type);
1476
1477	deUint8* const			dstBasePtr			= (deUint8*)dstBlockPtr.ptr + dstEntry.offset;
1478	const deUint8* const	srcBasePtr			= (const deUint8*)srcBlockPtr.ptr + srcEntry.offset;
1479	const int				scalarSize			= glu::getDataTypeScalarSize(dstEntry.type);
1480	const bool				isMatrix			= glu::isDataTypeMatrix(dstEntry.type);
1481	const int				compSize			= sizeof(deUint32);
1482	const int				dstArraySize		= dstEntry.arraySize == 0 ? dstBlockPtr.lastUnsizedArraySize : dstEntry.arraySize;
1483	const int				dstArrayStride		= dstEntry.arrayStride;
1484	const int				dstTopLevelSize		= dstEntry.topLevelArraySize == 0 ? dstBlockPtr.lastUnsizedArraySize : dstEntry.topLevelArraySize;
1485	const int				dstTopLevelStride	= dstEntry.topLevelArrayStride;
1486	const int				srcArraySize		= srcEntry.arraySize == 0 ? srcBlockPtr.lastUnsizedArraySize : srcEntry.arraySize;
1487	const int				srcArrayStride		= srcEntry.arrayStride;
1488	const int				srcTopLevelSize		= srcEntry.topLevelArraySize == 0 ? srcBlockPtr.lastUnsizedArraySize : srcEntry.topLevelArraySize;
1489	const int				srcTopLevelStride	= srcEntry.topLevelArrayStride;
1490
1491	DE_ASSERT(dstArraySize <= srcArraySize && dstTopLevelSize <= srcTopLevelSize);
1492	DE_UNREF(srcArraySize && srcTopLevelSize);
1493
1494	for (int topElemNdx = 0; topElemNdx < dstTopLevelSize; topElemNdx++)
1495	{
1496		deUint8* const			dstTopPtr	= dstBasePtr + topElemNdx*dstTopLevelStride;
1497		const deUint8* const	srcTopPtr	= srcBasePtr + topElemNdx*srcTopLevelStride;
1498
1499		for (int elementNdx = 0; elementNdx < dstArraySize; elementNdx++)
1500		{
1501			deUint8* const			dstElemPtr	= dstTopPtr + elementNdx*dstArrayStride;
1502			const deUint8* const	srcElemPtr	= srcTopPtr + elementNdx*srcArrayStride;
1503
1504			if (isMatrix)
1505			{
1506				const int	numRows	= glu::getDataTypeMatrixNumRows(dstEntry.type);
1507				const int	numCols	= glu::getDataTypeMatrixNumColumns(dstEntry.type);
1508
1509				for (int colNdx = 0; colNdx < numCols; colNdx++)
1510				{
1511					for (int rowNdx = 0; rowNdx < numRows; rowNdx++)
1512					{
1513						deUint8*		dstCompPtr	= dstElemPtr + (dstEntry.isRowMajor ? rowNdx*dstEntry.matrixStride + colNdx*compSize
1514																						: colNdx*dstEntry.matrixStride + rowNdx*compSize);
1515						const deUint8*	srcCompPtr	= srcElemPtr + (srcEntry.isRowMajor ? rowNdx*srcEntry.matrixStride + colNdx*compSize
1516																						: colNdx*srcEntry.matrixStride + rowNdx*compSize);
1517
1518						DE_ASSERT((deIntptr)(srcCompPtr + compSize) - (deIntptr)srcBlockPtr.ptr <= (deIntptr)srcBlockPtr.size);
1519						DE_ASSERT((deIntptr)(dstCompPtr + compSize) - (deIntptr)dstBlockPtr.ptr <= (deIntptr)dstBlockPtr.size);
1520						deMemcpy(dstCompPtr, srcCompPtr, compSize);
1521					}
1522				}
1523			}
1524			else
1525			{
1526				DE_ASSERT((deIntptr)(srcElemPtr + scalarSize*compSize) - (deIntptr)srcBlockPtr.ptr <= (deIntptr)srcBlockPtr.size);
1527				DE_ASSERT((deIntptr)(dstElemPtr + scalarSize*compSize) - (deIntptr)dstBlockPtr.ptr <= (deIntptr)dstBlockPtr.size);
1528				deMemcpy(dstElemPtr, srcElemPtr, scalarSize*compSize);
1529			}
1530		}
1531	}
1532}
1533
1534void copyData (const BufferLayout& dstLayout, const vector<BlockDataPtr>& dstBlockPointers, const BufferLayout& srcLayout, const vector<BlockDataPtr>& srcBlockPointers)
1535{
1536	// \note Src layout is used as reference in case of activeVarIndices happens to be incorrect in dstLayout blocks.
1537	int numBlocks = (int)srcLayout.blocks.size();
1538
1539	for (int srcBlockNdx = 0; srcBlockNdx < numBlocks; srcBlockNdx++)
1540	{
1541		const BlockLayoutEntry&		srcBlock	= srcLayout.blocks[srcBlockNdx];
1542		const BlockDataPtr&			srcBlockPtr	= srcBlockPointers[srcBlockNdx];
1543		int							dstBlockNdx	= dstLayout.getBlockIndex(srcBlock.name.c_str());
1544
1545		if (dstBlockNdx >= 0)
1546		{
1547			DE_ASSERT(de::inBounds(dstBlockNdx, 0, (int)dstBlockPointers.size()));
1548
1549			const BlockDataPtr& dstBlockPtr = dstBlockPointers[dstBlockNdx];
1550
1551			for (vector<int>::const_iterator srcVarNdxIter = srcBlock.activeVarIndices.begin(); srcVarNdxIter != srcBlock.activeVarIndices.end(); srcVarNdxIter++)
1552			{
1553				const BufferVarLayoutEntry&	srcEntry	= srcLayout.bufferVars[*srcVarNdxIter];
1554				int							dstVarNdx	= dstLayout.getVariableIndex(srcEntry.name.c_str());
1555
1556				if (dstVarNdx >= 0)
1557					copyBufferVarData(dstLayout.bufferVars[dstVarNdx], dstBlockPtr, srcEntry, srcBlockPtr);
1558			}
1559		}
1560	}
1561}
1562
1563void copyNonWrittenData (
1564	const BufferLayout&			layout,
1565	const BufferBlock&			block,
1566	int							instanceNdx,
1567	const BlockDataPtr&			srcBlockPtr,
1568	const BlockDataPtr&			dstBlockPtr,
1569	const BufferVar&			bufVar,
1570	const glu::SubTypeAccess&	accessPath)
1571{
1572	const VarType curType = accessPath.getType();
1573
1574	if (curType.isArrayType())
1575	{
1576		const int arraySize = curType.getArraySize() == VarType::UNSIZED_ARRAY ? block.getLastUnsizedArraySize(instanceNdx) : curType.getArraySize();
1577
1578		for (int elemNdx = 0; elemNdx < arraySize; elemNdx++)
1579			copyNonWrittenData(layout, block, instanceNdx, srcBlockPtr, dstBlockPtr, bufVar, accessPath.element(elemNdx));
1580	}
1581	else if (curType.isStructType())
1582	{
1583		const int numMembers = curType.getStructPtr()->getNumMembers();
1584
1585		for (int memberNdx = 0; memberNdx < numMembers; memberNdx++)
1586			copyNonWrittenData(layout, block, instanceNdx, srcBlockPtr, dstBlockPtr, bufVar, accessPath.member(memberNdx));
1587	}
1588	else
1589	{
1590		DE_ASSERT(curType.isBasicType());
1591
1592		const string	apiName	= getAPIName(block, bufVar, accessPath.getPath());
1593		const int		varNdx	= layout.getVariableIndex(apiName);
1594
1595		DE_ASSERT(varNdx >= 0);
1596		{
1597			const BufferVarLayoutEntry& varLayout = layout.bufferVars[varNdx];
1598			copyBufferVarData(varLayout, dstBlockPtr, varLayout, srcBlockPtr);
1599		}
1600	}
1601}
1602
1603void copyNonWrittenData (const ShaderInterface& interface, const BufferLayout& layout, const vector<BlockDataPtr>& srcPtrs, const vector<BlockDataPtr>& dstPtrs)
1604{
1605	for (int declNdx = 0; declNdx < interface.getNumBlocks(); declNdx++)
1606	{
1607		const BufferBlock&	block			= interface.getBlock(declNdx);
1608		const bool			isArray			= block.isArray();
1609		const int			numInstances	= isArray ? block.getArraySize() : 1;
1610
1611		DE_ASSERT(!isArray || block.getInstanceName());
1612
1613		for (int instanceNdx = 0; instanceNdx < numInstances; instanceNdx++)
1614		{
1615			const string		instanceName	= block.getBlockName() + (isArray ? "[" + de::toString(instanceNdx) + "]" : string(""));
1616			const int			blockNdx		= layout.getBlockIndex(instanceName);
1617			const BlockDataPtr&	srcBlockPtr		= srcPtrs[blockNdx];
1618			const BlockDataPtr&	dstBlockPtr		= dstPtrs[blockNdx];
1619
1620			for (BufferBlock::const_iterator varIter = block.begin(); varIter != block.end(); varIter++)
1621			{
1622				const BufferVar& bufVar = *varIter;
1623
1624				if (bufVar.getFlags() & ACCESS_WRITE)
1625					continue;
1626
1627				copyNonWrittenData(layout, block, instanceNdx, srcBlockPtr, dstBlockPtr, bufVar, glu::SubTypeAccess(bufVar.getType()));
1628			}
1629		}
1630	}
1631}
1632
1633bool compareComponents (glu::DataType scalarType, const void* ref, const void* res, int numComps)
1634{
1635	if (scalarType == glu::TYPE_FLOAT)
1636	{
1637		const float threshold = 0.05f; // Same as used in shaders - should be fine for values being used.
1638
1639		for (int ndx = 0; ndx < numComps; ndx++)
1640		{
1641			const float		refVal		= *((const float*)ref + ndx);
1642			const float		resVal		= *((const float*)res + ndx);
1643
1644			if (deFloatAbs(resVal - refVal) >= threshold)
1645				return false;
1646		}
1647	}
1648	else if (scalarType == glu::TYPE_BOOL)
1649	{
1650		for (int ndx = 0; ndx < numComps; ndx++)
1651		{
1652			const deUint32	refVal		= *((const deUint32*)ref + ndx);
1653			const deUint32	resVal		= *((const deUint32*)res + ndx);
1654
1655			if ((refVal != 0) != (resVal != 0))
1656				return false;
1657		}
1658	}
1659	else
1660	{
1661		DE_ASSERT(scalarType == glu::TYPE_INT || scalarType == glu::TYPE_UINT);
1662
1663		for (int ndx = 0; ndx < numComps; ndx++)
1664		{
1665			const deUint32	refVal		= *((const deUint32*)ref + ndx);
1666			const deUint32	resVal		= *((const deUint32*)res + ndx);
1667
1668			if (refVal != resVal)
1669				return false;
1670		}
1671	}
1672
1673	return true;
1674}
1675
1676bool compareBufferVarData (tcu::TestLog& log, const BufferVarLayoutEntry& refEntry, const BlockDataPtr& refBlockPtr, const BufferVarLayoutEntry& resEntry, const BlockDataPtr& resBlockPtr)
1677{
1678	DE_ASSERT(resEntry.arraySize <= refEntry.arraySize);
1679	DE_ASSERT(resEntry.topLevelArraySize <= refEntry.topLevelArraySize);
1680	DE_ASSERT(resBlockPtr.lastUnsizedArraySize <= refBlockPtr.lastUnsizedArraySize);
1681	DE_ASSERT(resEntry.type == refEntry.type);
1682
1683	deUint8* const			resBasePtr			= (deUint8*)resBlockPtr.ptr + resEntry.offset;
1684	const deUint8* const	refBasePtr			= (const deUint8*)refBlockPtr.ptr + refEntry.offset;
1685	const glu::DataType		scalarType			= glu::getDataTypeScalarType(refEntry.type);
1686	const int				scalarSize			= glu::getDataTypeScalarSize(resEntry.type);
1687	const bool				isMatrix			= glu::isDataTypeMatrix(resEntry.type);
1688	const int				compSize			= sizeof(deUint32);
1689	const int				maxPrints			= 3;
1690	int						numFailed			= 0;
1691
1692	const int				resArraySize		= resEntry.arraySize == 0 ? resBlockPtr.lastUnsizedArraySize : resEntry.arraySize;
1693	const int				resArrayStride		= resEntry.arrayStride;
1694	const int				resTopLevelSize		= resEntry.topLevelArraySize == 0 ? resBlockPtr.lastUnsizedArraySize : resEntry.topLevelArraySize;
1695	const int				resTopLevelStride	= resEntry.topLevelArrayStride;
1696	const int				refArraySize		= refEntry.arraySize == 0 ? refBlockPtr.lastUnsizedArraySize : refEntry.arraySize;
1697	const int				refArrayStride		= refEntry.arrayStride;
1698	const int				refTopLevelSize		= refEntry.topLevelArraySize == 0 ? refBlockPtr.lastUnsizedArraySize : refEntry.topLevelArraySize;
1699	const int				refTopLevelStride	= refEntry.topLevelArrayStride;
1700
1701	DE_ASSERT(resArraySize <= refArraySize && resTopLevelSize <= refTopLevelSize);
1702	DE_UNREF(refArraySize && refTopLevelSize);
1703
1704	for (int topElemNdx = 0; topElemNdx < resTopLevelSize; topElemNdx++)
1705	{
1706		deUint8* const			resTopPtr	= resBasePtr + topElemNdx*resTopLevelStride;
1707		const deUint8* const	refTopPtr	= refBasePtr + topElemNdx*refTopLevelStride;
1708
1709		for (int elementNdx = 0; elementNdx < resArraySize; elementNdx++)
1710		{
1711			deUint8* const			resElemPtr	= resTopPtr + elementNdx*resArrayStride;
1712			const deUint8* const	refElemPtr	= refTopPtr + elementNdx*refArrayStride;
1713
1714			if (isMatrix)
1715			{
1716				const int	numRows	= glu::getDataTypeMatrixNumRows(resEntry.type);
1717				const int	numCols	= glu::getDataTypeMatrixNumColumns(resEntry.type);
1718				bool		isOk	= true;
1719
1720				for (int colNdx = 0; colNdx < numCols; colNdx++)
1721				{
1722					for (int rowNdx = 0; rowNdx < numRows; rowNdx++)
1723					{
1724						deUint8*		resCompPtr	= resElemPtr + (resEntry.isRowMajor ? rowNdx*resEntry.matrixStride + colNdx*compSize
1725																						: colNdx*resEntry.matrixStride + rowNdx*compSize);
1726						const deUint8*	refCompPtr	= refElemPtr + (refEntry.isRowMajor ? rowNdx*refEntry.matrixStride + colNdx*compSize
1727																						: colNdx*refEntry.matrixStride + rowNdx*compSize);
1728
1729						DE_ASSERT((deIntptr)(refCompPtr + compSize) - (deIntptr)refBlockPtr.ptr <= (deIntptr)refBlockPtr.size);
1730						DE_ASSERT((deIntptr)(resCompPtr + compSize) - (deIntptr)resBlockPtr.ptr <= (deIntptr)resBlockPtr.size);
1731
1732						isOk = isOk && compareComponents(scalarType, resCompPtr, refCompPtr, 1);
1733					}
1734				}
1735
1736				if (!isOk)
1737				{
1738					numFailed += 1;
1739					if (numFailed < maxPrints)
1740					{
1741						std::ostringstream expected, got;
1742						generateImmMatrixSrc(expected, refEntry.type, refEntry.matrixStride, refEntry.isRowMajor, refElemPtr);
1743						generateImmMatrixSrc(got, resEntry.type, resEntry.matrixStride, resEntry.isRowMajor, resElemPtr);
1744						log << TestLog::Message << "ERROR: mismatch in " << refEntry.name << ", top-level ndx " << topElemNdx << ", bottom-level ndx " << elementNdx << ":\n"
1745												<< "  expected " << expected.str() << "\n"
1746												<< "  got " << got.str()
1747							<< TestLog::EndMessage;
1748					}
1749				}
1750			}
1751			else
1752			{
1753				DE_ASSERT((deIntptr)(refElemPtr + scalarSize*compSize) - (deIntptr)refBlockPtr.ptr <= (deIntptr)refBlockPtr.size);
1754				DE_ASSERT((deIntptr)(resElemPtr + scalarSize*compSize) - (deIntptr)resBlockPtr.ptr <= (deIntptr)resBlockPtr.size);
1755
1756				const bool isOk = compareComponents(scalarType, resElemPtr, refElemPtr, scalarSize);
1757
1758				if (!isOk)
1759				{
1760					numFailed += 1;
1761					if (numFailed < maxPrints)
1762					{
1763						std::ostringstream expected, got;
1764						generateImmScalarVectorSrc(expected, refEntry.type, refElemPtr);
1765						generateImmScalarVectorSrc(got, resEntry.type, resElemPtr);
1766						log << TestLog::Message << "ERROR: mismatch in " << refEntry.name << ", top-level ndx " << topElemNdx << ", bottom-level ndx " << elementNdx << ":\n"
1767												<< "  expected " << expected.str() << "\n"
1768												<< "  got " << got.str()
1769							<< TestLog::EndMessage;
1770					}
1771				}
1772			}
1773		}
1774	}
1775
1776	if (numFailed >= maxPrints)
1777		log << TestLog::Message << "... (" << numFailed << " failures for " << refEntry.name << " in total)" << TestLog::EndMessage;
1778
1779	return numFailed == 0;
1780}
1781
1782bool compareData (tcu::TestLog& log, const BufferLayout& refLayout, const vector<BlockDataPtr>& refBlockPointers, const BufferLayout& resLayout, const vector<BlockDataPtr>& resBlockPointers)
1783{
1784	const int	numBlocks	= (int)refLayout.blocks.size();
1785	bool		allOk		= true;
1786
1787	for (int refBlockNdx = 0; refBlockNdx < numBlocks; refBlockNdx++)
1788	{
1789		const BlockLayoutEntry&		refBlock	= refLayout.blocks[refBlockNdx];
1790		const BlockDataPtr&			refBlockPtr	= refBlockPointers[refBlockNdx];
1791		int							resBlockNdx	= resLayout.getBlockIndex(refBlock.name.c_str());
1792
1793		if (resBlockNdx >= 0)
1794		{
1795			DE_ASSERT(de::inBounds(resBlockNdx, 0, (int)resBlockPointers.size()));
1796
1797			const BlockDataPtr& resBlockPtr = resBlockPointers[resBlockNdx];
1798
1799			for (vector<int>::const_iterator refVarNdxIter = refBlock.activeVarIndices.begin(); refVarNdxIter != refBlock.activeVarIndices.end(); refVarNdxIter++)
1800			{
1801				const BufferVarLayoutEntry&	refEntry	= refLayout.bufferVars[*refVarNdxIter];
1802				int							resVarNdx	= resLayout.getVariableIndex(refEntry.name.c_str());
1803
1804				if (resVarNdx >= 0)
1805				{
1806					const BufferVarLayoutEntry& resEntry = resLayout.bufferVars[resVarNdx];
1807					allOk = compareBufferVarData(log, refEntry, refBlockPtr, resEntry, resBlockPtr) && allOk;
1808				}
1809			}
1810		}
1811	}
1812
1813	return allOk;
1814}
1815
1816string getBlockAPIName (const BufferBlock& block, int instanceNdx)
1817{
1818	DE_ASSERT(block.isArray() || instanceNdx == 0);
1819	return block.getBlockName() + (block.isArray() ? ("[" + de::toString(instanceNdx) + "]") : string());
1820}
1821
1822// \note Some implementations don't report block members in the order they are declared.
1823//		 For checking whether size has to be adjusted by some top-level array actual size,
1824//		 we only need to know a) whether there is a unsized top-level array, and b)
1825//		 what is stride of that array.
1826
1827static bool hasUnsizedArray (const BufferLayout& layout, const BlockLayoutEntry& entry)
1828{
1829	for (vector<int>::const_iterator varNdx = entry.activeVarIndices.begin(); varNdx != entry.activeVarIndices.end(); ++varNdx)
1830	{
1831		if (isUnsizedArray(layout.bufferVars[*varNdx]))
1832			return true;
1833	}
1834
1835	return false;
1836}
1837
1838static int getUnsizedArrayStride (const BufferLayout& layout, const BlockLayoutEntry& entry)
1839{
1840	for (vector<int>::const_iterator varNdx = entry.activeVarIndices.begin(); varNdx != entry.activeVarIndices.end(); ++varNdx)
1841	{
1842		const BufferVarLayoutEntry& varEntry = layout.bufferVars[*varNdx];
1843
1844		if (varEntry.arraySize == 0)
1845			return varEntry.arrayStride;
1846		else if (varEntry.topLevelArraySize == 0)
1847			return varEntry.topLevelArrayStride;
1848	}
1849
1850	return 0;
1851}
1852
1853vector<int> computeBufferSizes (const ShaderInterface& interface, const BufferLayout& layout)
1854{
1855	vector<int> sizes(layout.blocks.size());
1856
1857	for (int declNdx = 0; declNdx < interface.getNumBlocks(); declNdx++)
1858	{
1859		const BufferBlock&	block			= interface.getBlock(declNdx);
1860		const bool			isArray			= block.isArray();
1861		const int			numInstances	= isArray ? block.getArraySize() : 1;
1862
1863		for (int instanceNdx = 0; instanceNdx < numInstances; instanceNdx++)
1864		{
1865			const string	apiName		= getBlockAPIName(block, instanceNdx);
1866			const int		blockNdx	= layout.getBlockIndex(apiName);
1867
1868			if (blockNdx >= 0)
1869			{
1870				const BlockLayoutEntry&		blockLayout		= layout.blocks[blockNdx];
1871				const int					baseSize		= blockLayout.size;
1872				const bool					isLastUnsized	= hasUnsizedArray(layout, blockLayout);
1873				const int					lastArraySize	= isLastUnsized ? block.getLastUnsizedArraySize(instanceNdx) : 0;
1874				const int					stride			= isLastUnsized ? getUnsizedArrayStride(layout, blockLayout) : 0;
1875
1876				sizes[blockNdx] = baseSize + lastArraySize*stride;
1877			}
1878		}
1879	}
1880
1881	return sizes;
1882}
1883
1884BlockDataPtr getBlockDataPtr (const BufferLayout& layout, const BlockLayoutEntry& blockLayout, void* ptr, int bufferSize)
1885{
1886	const bool	isLastUnsized	= hasUnsizedArray(layout, blockLayout);
1887	const int	baseSize		= blockLayout.size;
1888
1889	if (isLastUnsized)
1890	{
1891		const int		lastArrayStride	= getUnsizedArrayStride(layout, blockLayout);
1892		const int		lastArraySize	= (bufferSize-baseSize) / (lastArrayStride ? lastArrayStride : 1);
1893
1894		DE_ASSERT(baseSize + lastArraySize*lastArrayStride == bufferSize);
1895
1896		return BlockDataPtr(ptr, bufferSize, lastArraySize);
1897	}
1898	else
1899		return BlockDataPtr(ptr, bufferSize, 0);
1900}
1901
1902struct RefDataStorage
1903{
1904	vector<deUint8>			data;
1905	vector<BlockDataPtr>	pointers;
1906};
1907
1908struct Buffer
1909{
1910	deUint32				buffer;
1911	int						size;
1912
1913	Buffer (deUint32 buffer_, int size_) : buffer(buffer_), size(size_) {}
1914	Buffer (void) : buffer(0), size(0) {}
1915};
1916
1917struct BlockLocation
1918{
1919	int						index;
1920	int						offset;
1921	int						size;
1922
1923	BlockLocation (int index_, int offset_, int size_) : index(index_), offset(offset_), size(size_) {}
1924	BlockLocation (void) : index(0), offset(0), size(0) {}
1925};
1926
1927void initRefDataStorage (const ShaderInterface& interface, const BufferLayout& layout, RefDataStorage& storage)
1928{
1929	DE_ASSERT(storage.data.empty() && storage.pointers.empty());
1930
1931	const vector<int>	bufferSizes = computeBufferSizes(interface, layout);
1932	int					totalSize	= 0;
1933
1934	for (vector<int>::const_iterator sizeIter = bufferSizes.begin(); sizeIter != bufferSizes.end(); ++sizeIter)
1935		totalSize += *sizeIter;
1936
1937	storage.data.resize(totalSize);
1938
1939	// Pointers for each block.
1940	{
1941		deUint8*	basePtr		= storage.data.empty() ? DE_NULL : &storage.data[0];
1942		int			curOffset	= 0;
1943
1944		DE_ASSERT(bufferSizes.size() == layout.blocks.size());
1945		DE_ASSERT(totalSize == 0 || basePtr);
1946
1947		storage.pointers.resize(layout.blocks.size());
1948
1949		for (int blockNdx = 0; blockNdx < (int)layout.blocks.size(); blockNdx++)
1950		{
1951			const BlockLayoutEntry&	blockLayout		= layout.blocks[blockNdx];
1952			const int				bufferSize		= bufferSizes[blockNdx];
1953
1954			storage.pointers[blockNdx] = getBlockDataPtr(layout, blockLayout, basePtr + curOffset, bufferSize);
1955
1956			curOffset += bufferSize;
1957		}
1958	}
1959}
1960
1961vector<BlockDataPtr> blockLocationsToPtrs (const BufferLayout& layout, const vector<BlockLocation>& blockLocations, const vector<void*>& bufPtrs)
1962{
1963	vector<BlockDataPtr> blockPtrs(blockLocations.size());
1964
1965	DE_ASSERT(layout.blocks.size() == blockLocations.size());
1966
1967	for (int blockNdx = 0; blockNdx < (int)layout.blocks.size(); blockNdx++)
1968	{
1969		const BlockLayoutEntry&	blockLayout		= layout.blocks[blockNdx];
1970		const BlockLocation&	location		= blockLocations[blockNdx];
1971
1972		blockPtrs[blockNdx] = getBlockDataPtr(layout, blockLayout, (deUint8*)bufPtrs[location.index] + location.offset, location.size);
1973	}
1974
1975	return blockPtrs;
1976}
1977
1978vector<void*> mapBuffers (const glw::Functions& gl, const vector<Buffer>& buffers, deUint32 access)
1979{
1980	vector<void*> mapPtrs(buffers.size(), DE_NULL);
1981
1982	try
1983	{
1984		for (int ndx = 0; ndx < (int)buffers.size(); ndx++)
1985		{
1986			if (buffers[ndx].size > 0)
1987			{
1988				gl.bindBuffer(GL_SHADER_STORAGE_BUFFER, buffers[ndx].buffer);
1989				mapPtrs[ndx] = gl.mapBufferRange(GL_SHADER_STORAGE_BUFFER, 0, buffers[ndx].size, access);
1990				GLU_EXPECT_NO_ERROR(gl.getError(), "Failed to map buffer");
1991				TCU_CHECK(mapPtrs[ndx]);
1992			}
1993			else
1994				mapPtrs[ndx] = DE_NULL;
1995		}
1996
1997		return mapPtrs;
1998	}
1999	catch (...)
2000	{
2001		for (int ndx = 0; ndx < (int)buffers.size(); ndx++)
2002		{
2003			if (mapPtrs[ndx])
2004			{
2005				gl.bindBuffer(GL_SHADER_STORAGE_BUFFER, buffers[ndx].buffer);
2006				gl.unmapBuffer(GL_SHADER_STORAGE_BUFFER);
2007			}
2008		}
2009
2010		throw;
2011	}
2012}
2013
2014void unmapBuffers (const glw::Functions& gl, const vector<Buffer>& buffers)
2015{
2016	for (int ndx = 0; ndx < (int)buffers.size(); ndx++)
2017	{
2018		if (buffers[ndx].size > 0)
2019		{
2020			gl.bindBuffer(GL_SHADER_STORAGE_BUFFER, buffers[ndx].buffer);
2021			gl.unmapBuffer(GL_SHADER_STORAGE_BUFFER);
2022		}
2023	}
2024
2025	GLU_EXPECT_NO_ERROR(gl.getError(), "Failed to unmap buffer");
2026}
2027
2028} // anonymous (utilities)
2029
2030class BufferManager
2031{
2032public:
2033								BufferManager	(const glu::RenderContext& renderCtx);
2034								~BufferManager	(void);
2035
2036	deUint32					allocBuffer		(void);
2037
2038private:
2039								BufferManager	(const BufferManager& other);
2040	BufferManager&				operator=		(const BufferManager& other);
2041
2042	const glu::RenderContext&	m_renderCtx;
2043	std::vector<deUint32>		m_buffers;
2044};
2045
2046BufferManager::BufferManager (const glu::RenderContext& renderCtx)
2047	: m_renderCtx(renderCtx)
2048{
2049}
2050
2051BufferManager::~BufferManager (void)
2052{
2053	if (!m_buffers.empty())
2054		m_renderCtx.getFunctions().deleteBuffers((glw::GLsizei)m_buffers.size(), &m_buffers[0]);
2055}
2056
2057deUint32 BufferManager::allocBuffer (void)
2058{
2059	deUint32 buf = 0;
2060
2061	m_buffers.reserve(m_buffers.size()+1);
2062	m_renderCtx.getFunctions().genBuffers(1, &buf);
2063	GLU_EXPECT_NO_ERROR(m_renderCtx.getFunctions().getError(), "Failed to allocate buffer");
2064	m_buffers.push_back(buf);
2065
2066	return buf;
2067}
2068
2069} // bb
2070
2071using namespace bb;
2072
2073// SSBOLayoutCase.
2074
2075SSBOLayoutCase::SSBOLayoutCase (tcu::TestContext& testCtx, glu::RenderContext& renderCtx, const char* name, const char* description, glu::GLSLVersion glslVersion, BufferMode bufferMode)
2076	: TestCase		(testCtx, name, description)
2077	, m_renderCtx	(renderCtx)
2078	, m_glslVersion	(glslVersion)
2079	, m_bufferMode	(bufferMode)
2080{
2081	DE_ASSERT(glslVersion == glu::GLSL_VERSION_310_ES || glslVersion == glu::GLSL_VERSION_430);
2082}
2083
2084SSBOLayoutCase::~SSBOLayoutCase (void)
2085{
2086}
2087
2088SSBOLayoutCase::IterateResult SSBOLayoutCase::iterate (void)
2089{
2090	TestLog&					log				= m_testCtx.getLog();
2091	const glw::Functions&		gl				= m_renderCtx.getFunctions();
2092
2093	BufferLayout				refLayout;		// std140 / std430 layout.
2094	BufferLayout				glLayout;		// Layout reported by GL.
2095	RefDataStorage				initialData;	// Initial data stored in buffer.
2096	RefDataStorage				writeData;		// Data written by compute shader.
2097
2098	BufferManager				bufferManager	(m_renderCtx);
2099	vector<Buffer>				buffers;		// Buffers allocated for storage
2100	vector<BlockLocation>		blockLocations;	// Block locations in storage (index, offset)
2101
2102	// Initialize result to pass.
2103	m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
2104
2105	computeReferenceLayout	(refLayout, m_interface);
2106	initRefDataStorage		(m_interface, refLayout, initialData);
2107	initRefDataStorage		(m_interface, refLayout, writeData);
2108	generateValues			(refLayout, initialData.pointers, deStringHash(getName()) ^ 0xad2f7214);
2109	generateValues			(refLayout, writeData.pointers, deStringHash(getName()) ^ 0x25ca4e7);
2110	copyNonWrittenData		(m_interface, refLayout, initialData.pointers, writeData.pointers);
2111
2112	const glu::ShaderProgram program(m_renderCtx, glu::ProgramSources() << glu::ComputeSource(generateComputeShader(m_glslVersion, m_interface, refLayout, initialData.pointers, writeData.pointers)));
2113	log << program;
2114
2115	if (!program.isOk())
2116	{
2117		// Compile failed.
2118		m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Compile failed");
2119		return STOP;
2120	}
2121
2122	// Query layout from GL.
2123	getGLBufferLayout(gl, glLayout, program.getProgram());
2124
2125	// Print layout to log.
2126	{
2127		tcu::ScopedLogSection section(log, "ActiveBufferBlocks", "Active Buffer Blocks");
2128		for (int blockNdx = 0; blockNdx < (int)glLayout.blocks.size(); blockNdx++)
2129			log << TestLog::Message << blockNdx << ": " << glLayout.blocks[blockNdx] << TestLog::EndMessage;
2130	}
2131
2132	{
2133		tcu::ScopedLogSection section(log, "ActiveBufferVars", "Active Buffer Variables");
2134		for (int varNdx = 0; varNdx < (int)glLayout.bufferVars.size(); varNdx++)
2135			log << TestLog::Message << varNdx << ": " << glLayout.bufferVars[varNdx] << TestLog::EndMessage;
2136	}
2137
2138	// Verify layouts.
2139	{
2140		if (!checkLayoutIndices(glLayout) || !checkLayoutBounds(glLayout) || !compareTypes(refLayout, glLayout))
2141		{
2142			m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Invalid layout");
2143			return STOP; // It is not safe to use the given layout.
2144		}
2145
2146		if (!compareStdBlocks(refLayout, glLayout))
2147			m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Invalid std140 or std430 layout");
2148
2149		if (!compareSharedBlocks(refLayout, glLayout))
2150			m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Invalid shared layout");
2151
2152		if (!checkIndexQueries(program.getProgram(), glLayout))
2153			m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Inconsintent block index query results");
2154	}
2155
2156	// Allocate GL buffers & compute placement.
2157	{
2158		const int			numBlocks		= (int)glLayout.blocks.size();
2159		const vector<int>	bufferSizes		= computeBufferSizes(m_interface, glLayout);
2160
2161		DE_ASSERT(bufferSizes.size() == glLayout.blocks.size());
2162
2163		blockLocations.resize(numBlocks);
2164
2165		if (m_bufferMode == BUFFERMODE_PER_BLOCK)
2166		{
2167			buffers.resize(numBlocks);
2168
2169			for (int blockNdx = 0; blockNdx < numBlocks; blockNdx++)
2170			{
2171				const int bufferSize = bufferSizes[blockNdx];
2172
2173				buffers[blockNdx].size = bufferSize;
2174				blockLocations[blockNdx] = BlockLocation(blockNdx, 0, bufferSize);
2175			}
2176		}
2177		else
2178		{
2179			DE_ASSERT(m_bufferMode == BUFFERMODE_SINGLE);
2180
2181			int		bindingAlignment	= 0;
2182			int		totalSize			= 0;
2183
2184			gl.getIntegerv(GL_SHADER_STORAGE_BUFFER_OFFSET_ALIGNMENT, &bindingAlignment);
2185
2186			{
2187				int curOffset = 0;
2188				DE_ASSERT(bufferSizes.size() == glLayout.blocks.size());
2189				for (int blockNdx = 0; blockNdx < numBlocks; blockNdx++)
2190				{
2191					const int bufferSize = bufferSizes[blockNdx];
2192
2193					if (bindingAlignment > 0)
2194						curOffset = deRoundUp32(curOffset, bindingAlignment);
2195
2196					blockLocations[blockNdx] = BlockLocation(0, curOffset, bufferSize);
2197					curOffset += bufferSize;
2198				}
2199				totalSize = curOffset;
2200			}
2201
2202			buffers.resize(1);
2203			buffers[0].size = totalSize;
2204		}
2205
2206		for (int bufNdx = 0; bufNdx < (int)buffers.size(); bufNdx++)
2207		{
2208			const int		bufferSize	= buffers[bufNdx].size;
2209			const deUint32	buffer		= bufferManager.allocBuffer();
2210
2211			gl.bindBuffer(GL_SHADER_STORAGE_BUFFER, buffer);
2212			gl.bufferData(GL_SHADER_STORAGE_BUFFER, bufferSize, DE_NULL, GL_STATIC_DRAW);
2213			GLU_EXPECT_NO_ERROR(gl.getError(), "Failed to allocate buffer");
2214
2215			buffers[bufNdx].buffer = buffer;
2216		}
2217	}
2218
2219	{
2220		const vector<void*>			mapPtrs			= mapBuffers(gl, buffers, GL_MAP_WRITE_BIT);
2221		const vector<BlockDataPtr>	mappedBlockPtrs	= blockLocationsToPtrs(glLayout, blockLocations, mapPtrs);
2222
2223		copyData(glLayout, mappedBlockPtrs, refLayout, initialData.pointers);
2224
2225		unmapBuffers(gl, buffers);
2226	}
2227
2228	{
2229		int bindingPoint = 0;
2230
2231		for (int blockDeclNdx = 0; blockDeclNdx < m_interface.getNumBlocks(); blockDeclNdx++)
2232		{
2233			const BufferBlock&	block		= m_interface.getBlock(blockDeclNdx);
2234			const int			numInst		= block.isArray() ? block.getArraySize() : 1;
2235
2236			for (int instNdx = 0; instNdx < numInst; instNdx++)
2237			{
2238				const string	instName	= getBlockAPIName(block, instNdx);
2239				const int		layoutNdx	= findBlockIndex(glLayout, instName);
2240
2241				if (layoutNdx >= 0)
2242				{
2243					const BlockLocation& blockLoc = blockLocations[layoutNdx];
2244
2245					if (blockLoc.size > 0)
2246						gl.bindBufferRange(GL_SHADER_STORAGE_BUFFER, bindingPoint, buffers[blockLoc.index].buffer, blockLoc.offset, blockLoc.size);
2247				}
2248
2249				bindingPoint += 1;
2250			}
2251		}
2252	}
2253
2254	GLU_EXPECT_NO_ERROR(gl.getError(), "Failed to bind buffers");
2255
2256	{
2257		const bool execOk = execute(program.getProgram());
2258
2259		if (execOk)
2260		{
2261			const vector<void*>			mapPtrs			= mapBuffers(gl, buffers, GL_MAP_READ_BIT);
2262			const vector<BlockDataPtr>	mappedBlockPtrs	= blockLocationsToPtrs(glLayout, blockLocations, mapPtrs);
2263
2264			const bool					compareOk		= compareData(m_testCtx.getLog(), refLayout, writeData.pointers, glLayout, mappedBlockPtrs);
2265
2266			unmapBuffers(gl, buffers);
2267
2268			if (!compareOk)
2269				m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Result comparison failed");
2270		}
2271		else
2272			m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Shader execution failed");
2273	}
2274
2275	return STOP;
2276}
2277
2278bool SSBOLayoutCase::compareStdBlocks (const BufferLayout& refLayout, const BufferLayout& cmpLayout) const
2279{
2280	TestLog&	log			= m_testCtx.getLog();
2281	bool		isOk		= true;
2282	int			numBlocks	= m_interface.getNumBlocks();
2283
2284	for (int blockNdx = 0; blockNdx < numBlocks; blockNdx++)
2285	{
2286		const BufferBlock&		block			= m_interface.getBlock(blockNdx);
2287		bool					isArray			= block.isArray();
2288		std::string				instanceName	= string(block.getBlockName()) + (isArray ? "[0]" : "");
2289		int						refBlockNdx		= refLayout.getBlockIndex(instanceName.c_str());
2290		int						cmpBlockNdx		= cmpLayout.getBlockIndex(instanceName.c_str());
2291
2292		if ((block.getFlags() & (LAYOUT_STD140|LAYOUT_STD430)) == 0)
2293			continue; // Not std* layout.
2294
2295		DE_ASSERT(refBlockNdx >= 0);
2296
2297		if (cmpBlockNdx < 0)
2298		{
2299			// Not found.
2300			log << TestLog::Message << "Error: Buffer block '" << instanceName << "' not found" << TestLog::EndMessage;
2301			isOk = false;
2302			continue;
2303		}
2304
2305		const BlockLayoutEntry&		refBlockLayout	= refLayout.blocks[refBlockNdx];
2306		const BlockLayoutEntry&		cmpBlockLayout	= cmpLayout.blocks[cmpBlockNdx];
2307
2308		// \todo [2012-01-24 pyry] Verify that activeVarIndices is correct.
2309		// \todo [2012-01-24 pyry] Verify all instances.
2310		if (refBlockLayout.activeVarIndices.size() != cmpBlockLayout.activeVarIndices.size())
2311		{
2312			log << TestLog::Message << "Error: Number of active variables differ in block '" << instanceName
2313				<< "' (expected " << refBlockLayout.activeVarIndices.size()
2314				<< ", got " << cmpBlockLayout.activeVarIndices.size()
2315				<< ")" << TestLog::EndMessage;
2316			isOk = false;
2317		}
2318
2319		for (vector<int>::const_iterator ndxIter = refBlockLayout.activeVarIndices.begin(); ndxIter != refBlockLayout.activeVarIndices.end(); ndxIter++)
2320		{
2321			const BufferVarLayoutEntry&	refEntry	= refLayout.bufferVars[*ndxIter];
2322			int							cmpEntryNdx	= cmpLayout.getVariableIndex(refEntry.name.c_str());
2323
2324			if (cmpEntryNdx < 0)
2325			{
2326				log << TestLog::Message << "Error: Buffer variable '" << refEntry.name << "' not found" << TestLog::EndMessage;
2327				isOk = false;
2328				continue;
2329			}
2330
2331			const BufferVarLayoutEntry&	cmpEntry	= cmpLayout.bufferVars[cmpEntryNdx];
2332
2333			if (refEntry.type					!= cmpEntry.type				||
2334				refEntry.arraySize				!= cmpEntry.arraySize			||
2335				refEntry.offset					!= cmpEntry.offset				||
2336				refEntry.arrayStride			!= cmpEntry.arrayStride			||
2337				refEntry.matrixStride			!= cmpEntry.matrixStride		||
2338				refEntry.topLevelArraySize		!= cmpEntry.topLevelArraySize	||
2339				refEntry.topLevelArrayStride	!= cmpEntry.topLevelArrayStride	||
2340				refEntry.isRowMajor				!= cmpEntry.isRowMajor)
2341			{
2342				log << TestLog::Message << "Error: Layout mismatch in '" << refEntry.name << "':\n"
2343					<< "  expected: " << refEntry << "\n"
2344					<< "  got: " << cmpEntry
2345					<< TestLog::EndMessage;
2346				isOk = false;
2347			}
2348		}
2349	}
2350
2351	return isOk;
2352}
2353
2354bool SSBOLayoutCase::compareSharedBlocks (const BufferLayout& refLayout, const BufferLayout& cmpLayout) const
2355{
2356	TestLog&	log			= m_testCtx.getLog();
2357	bool		isOk		= true;
2358	int			numBlocks	= m_interface.getNumBlocks();
2359
2360	for (int blockNdx = 0; blockNdx < numBlocks; blockNdx++)
2361	{
2362		const BufferBlock&		block			= m_interface.getBlock(blockNdx);
2363		bool					isArray			= block.isArray();
2364		std::string				instanceName	= string(block.getBlockName()) + (isArray ? "[0]" : "");
2365		int						refBlockNdx		= refLayout.getBlockIndex(instanceName.c_str());
2366		int						cmpBlockNdx		= cmpLayout.getBlockIndex(instanceName.c_str());
2367
2368		if ((block.getFlags() & LAYOUT_SHARED) == 0)
2369			continue; // Not shared layout.
2370
2371		DE_ASSERT(refBlockNdx >= 0);
2372
2373		if (cmpBlockNdx < 0)
2374		{
2375			// Not found, should it?
2376			log << TestLog::Message << "Error: Buffer block '" << instanceName << "' not found" << TestLog::EndMessage;
2377			isOk = false;
2378			continue;
2379		}
2380
2381		const BlockLayoutEntry&		refBlockLayout	= refLayout.blocks[refBlockNdx];
2382		const BlockLayoutEntry&		cmpBlockLayout	= cmpLayout.blocks[cmpBlockNdx];
2383
2384		if (refBlockLayout.activeVarIndices.size() != cmpBlockLayout.activeVarIndices.size())
2385		{
2386			log << TestLog::Message << "Error: Number of active variables differ in block '" << instanceName
2387				<< "' (expected " << refBlockLayout.activeVarIndices.size()
2388				<< ", got " << cmpBlockLayout.activeVarIndices.size()
2389				<< ")" << TestLog::EndMessage;
2390			isOk = false;
2391		}
2392
2393		for (vector<int>::const_iterator ndxIter = refBlockLayout.activeVarIndices.begin(); ndxIter != refBlockLayout.activeVarIndices.end(); ndxIter++)
2394		{
2395			const BufferVarLayoutEntry&	refEntry	= refLayout.bufferVars[*ndxIter];
2396			int							cmpEntryNdx	= cmpLayout.getVariableIndex(refEntry.name.c_str());
2397
2398			if (cmpEntryNdx < 0)
2399			{
2400				log << TestLog::Message << "Error: Buffer variable '" << refEntry.name << "' not found" << TestLog::EndMessage;
2401				isOk = false;
2402				continue;
2403			}
2404
2405			const BufferVarLayoutEntry&	cmpEntry	= cmpLayout.bufferVars[cmpEntryNdx];
2406
2407			if (refEntry.type				!= cmpEntry.type				||
2408				refEntry.arraySize			!= cmpEntry.arraySize			||
2409				refEntry.topLevelArraySize	!= cmpEntry.topLevelArraySize	||
2410				refEntry.isRowMajor	!= cmpEntry.isRowMajor)
2411			{
2412				log << TestLog::Message << "Error: Type / array size mismatch in '" << refEntry.name << "':\n"
2413					<< "  expected: " << refEntry << "\n"
2414					<< "  got: " << cmpEntry
2415					<< TestLog::EndMessage;
2416				isOk = false;
2417			}
2418		}
2419	}
2420
2421	return isOk;
2422}
2423
2424bool SSBOLayoutCase::compareTypes (const BufferLayout& refLayout, const BufferLayout& cmpLayout) const
2425{
2426	TestLog&	log			= m_testCtx.getLog();
2427	bool		isOk		= true;
2428	int			numBlocks	= m_interface.getNumBlocks();
2429
2430	for (int blockNdx = 0; blockNdx < numBlocks; blockNdx++)
2431	{
2432		const BufferBlock&		block			= m_interface.getBlock(blockNdx);
2433		bool					isArray			= block.isArray();
2434		int						numInstances	= isArray ? block.getArraySize() : 1;
2435
2436		for (int instanceNdx = 0; instanceNdx < numInstances; instanceNdx++)
2437		{
2438			std::ostringstream instanceName;
2439
2440			instanceName << block.getBlockName();
2441			if (isArray)
2442				instanceName << "[" << instanceNdx << "]";
2443
2444			int cmpBlockNdx = cmpLayout.getBlockIndex(instanceName.str().c_str());
2445
2446			if (cmpBlockNdx < 0)
2447				continue;
2448
2449			const BlockLayoutEntry& cmpBlockLayout = cmpLayout.blocks[cmpBlockNdx];
2450
2451			for (vector<int>::const_iterator ndxIter = cmpBlockLayout.activeVarIndices.begin(); ndxIter != cmpBlockLayout.activeVarIndices.end(); ndxIter++)
2452			{
2453				const BufferVarLayoutEntry&	cmpEntry	= cmpLayout.bufferVars[*ndxIter];
2454				int							refEntryNdx	= refLayout.getVariableIndex(cmpEntry.name.c_str());
2455
2456				if (refEntryNdx < 0)
2457				{
2458					log << TestLog::Message << "Error: Buffer variable '" << cmpEntry.name << "' not found in reference layout" << TestLog::EndMessage;
2459					isOk = false;
2460					continue;
2461				}
2462
2463				const BufferVarLayoutEntry&	refEntry	= refLayout.bufferVars[refEntryNdx];
2464
2465				if (refEntry.type != cmpEntry.type)
2466				{
2467					log << TestLog::Message << "Error: Buffer variable type mismatch in '" << refEntry.name << "':\n"
2468						<< "  expected: " << glu::getDataTypeName(refEntry.type) << "\n"
2469						<< "  got: " << glu::getDataTypeName(cmpEntry.type)
2470						<< TestLog::EndMessage;
2471					isOk = false;
2472				}
2473
2474				if (refEntry.arraySize < cmpEntry.arraySize)
2475				{
2476					log << TestLog::Message << "Error: Invalid array size in '" << refEntry.name << "': expected <= " << refEntry.arraySize << TestLog::EndMessage;
2477					isOk = false;
2478				}
2479
2480				if (refEntry.topLevelArraySize < cmpEntry.topLevelArraySize)
2481				{
2482					log << TestLog::Message << "Error: Invalid top-level array size in '" << refEntry.name << "': expected <= " << refEntry.topLevelArraySize << TestLog::EndMessage;
2483					isOk = false;
2484				}
2485			}
2486		}
2487	}
2488
2489	return isOk;
2490}
2491
2492bool SSBOLayoutCase::checkLayoutIndices (const BufferLayout& layout) const
2493{
2494	TestLog&	log			= m_testCtx.getLog();
2495	int			numVars		= (int)layout.bufferVars.size();
2496	int			numBlocks	= (int)layout.blocks.size();
2497	bool		isOk		= true;
2498
2499	// Check variable block indices.
2500	for (int varNdx = 0; varNdx < numVars; varNdx++)
2501	{
2502		const BufferVarLayoutEntry& bufVar = layout.bufferVars[varNdx];
2503
2504		if (bufVar.blockNdx < 0 || !deInBounds32(bufVar.blockNdx, 0, numBlocks))
2505		{
2506			log << TestLog::Message << "Error: Invalid block index in buffer variable '" << bufVar.name << "'" << TestLog::EndMessage;
2507			isOk = false;
2508		}
2509	}
2510
2511	// Check active variables.
2512	for (int blockNdx = 0; blockNdx < numBlocks; blockNdx++)
2513	{
2514		const BlockLayoutEntry& block = layout.blocks[blockNdx];
2515
2516		for (vector<int>::const_iterator varNdxIter = block.activeVarIndices.begin(); varNdxIter != block.activeVarIndices.end(); varNdxIter++)
2517		{
2518			if (!deInBounds32(*varNdxIter, 0, numVars))
2519			{
2520				log << TestLog::Message << "Error: Invalid active variable index " << *varNdxIter << " in block '" << block.name << "'" << TestLog::EndMessage;
2521				isOk = false;
2522			}
2523		}
2524	}
2525
2526	return isOk;
2527}
2528
2529bool SSBOLayoutCase::checkLayoutBounds (const BufferLayout& layout) const
2530{
2531	TestLog&	log			= m_testCtx.getLog();
2532	const int	numVars		= (int)layout.bufferVars.size();
2533	bool		isOk		= true;
2534
2535	for (int varNdx = 0; varNdx < numVars; varNdx++)
2536	{
2537		const BufferVarLayoutEntry& var = layout.bufferVars[varNdx];
2538
2539		if (var.blockNdx < 0 || isUnsizedArray(var))
2540			continue;
2541
2542		const BlockLayoutEntry&		block			= layout.blocks[var.blockNdx];
2543		const bool					isMatrix		= glu::isDataTypeMatrix(var.type);
2544		const int					numVecs			= isMatrix ? (var.isRowMajor ? glu::getDataTypeMatrixNumRows(var.type) : glu::getDataTypeMatrixNumColumns(var.type)) : 1;
2545		const int					numComps		= isMatrix ? (var.isRowMajor ? glu::getDataTypeMatrixNumColumns(var.type) : glu::getDataTypeMatrixNumRows(var.type)) : glu::getDataTypeScalarSize(var.type);
2546		const int					numElements		= var.arraySize;
2547		const int					topLevelSize	= var.topLevelArraySize;
2548		const int					arrayStride		= var.arrayStride;
2549		const int					topLevelStride	= var.topLevelArrayStride;
2550		const int					compSize		= sizeof(deUint32);
2551		const int					vecSize			= numComps*compSize;
2552
2553		int							minOffset		= 0;
2554		int							maxOffset		= 0;
2555
2556		// For negative strides.
2557		minOffset	= de::min(minOffset, (numVecs-1)*var.matrixStride);
2558		minOffset	= de::min(minOffset, (numElements-1)*arrayStride);
2559		minOffset	= de::min(minOffset, (topLevelSize-1)*topLevelStride + (numElements-1)*arrayStride + (numVecs-1)*var.matrixStride);
2560
2561		maxOffset	= de::max(maxOffset, vecSize);
2562		maxOffset	= de::max(maxOffset, (numVecs-1)*var.matrixStride + vecSize);
2563		maxOffset	= de::max(maxOffset, (numElements-1)*arrayStride + vecSize);
2564		maxOffset	= de::max(maxOffset, (topLevelSize-1)*topLevelStride + (numElements-1)*arrayStride + vecSize);
2565		maxOffset	= de::max(maxOffset, (topLevelSize-1)*topLevelStride + (numElements-1)*arrayStride + (numVecs-1)*var.matrixStride + vecSize);
2566
2567		if (var.offset+minOffset < 0 || var.offset+maxOffset > block.size)
2568		{
2569			log << TestLog::Message << "Error: Variable '" << var.name << "' out of block bounds" << TestLog::EndMessage;
2570			isOk = false;
2571		}
2572	}
2573
2574	return isOk;
2575}
2576
2577bool SSBOLayoutCase::checkIndexQueries (deUint32 program, const BufferLayout& layout) const
2578{
2579	tcu::TestLog&				log			= m_testCtx.getLog();
2580	const glw::Functions&		gl			= m_renderCtx.getFunctions();
2581	bool						allOk		= true;
2582
2583	// \note Spec mandates that buffer blocks are assigned consecutive locations from 0.
2584	//		 BlockLayoutEntries are stored in that order in UniformLayout.
2585	for (int blockNdx = 0; blockNdx < (int)layout.blocks.size(); blockNdx++)
2586	{
2587		const BlockLayoutEntry&		block		= layout.blocks[blockNdx];
2588		const int					queriedNdx	= gl.getProgramResourceIndex(program, GL_SHADER_STORAGE_BLOCK, block.name.c_str());
2589
2590		if (queriedNdx != blockNdx)
2591		{
2592			log << TestLog::Message << "ERROR: glGetProgramResourceIndex(" << block.name << ") returned " << queriedNdx << ", expected " << blockNdx << "!" << TestLog::EndMessage;
2593			allOk = false;
2594		}
2595
2596		GLU_EXPECT_NO_ERROR(gl.getError(), "glGetUniformBlockIndex()");
2597	}
2598
2599	return allOk;
2600}
2601
2602bool SSBOLayoutCase::execute (deUint32 program)
2603{
2604	const glw::Functions&				gl				= m_renderCtx.getFunctions();
2605	const deUint32						numPassedLoc	= gl.getProgramResourceIndex(program, GL_UNIFORM, "ac_numPassed");
2606	const glu::InterfaceVariableInfo	acVarInfo		= numPassedLoc != GL_INVALID_INDEX ? glu::getProgramInterfaceVariableInfo(gl, program, GL_UNIFORM, numPassedLoc)
2607																						   : glu::InterfaceVariableInfo();
2608	const glu::InterfaceBlockInfo		acBufferInfo	= acVarInfo.atomicCounterBufferIndex != GL_INVALID_INDEX ? glu::getProgramInterfaceBlockInfo(gl, program, GL_ATOMIC_COUNTER_BUFFER, acVarInfo.atomicCounterBufferIndex)
2609																												 : glu::InterfaceBlockInfo();
2610	const glu::Buffer					acBuffer		(m_renderCtx);
2611	bool								isOk			= true;
2612
2613	if (numPassedLoc == GL_INVALID_INDEX)
2614		throw tcu::TestError("No location for ac_numPassed found");
2615
2616	if (acBufferInfo.index == GL_INVALID_INDEX)
2617		throw tcu::TestError("ac_numPassed buffer index is GL_INVALID_INDEX");
2618
2619	if (acBufferInfo.dataSize == 0)
2620		throw tcu::TestError("ac_numPassed buffer size = 0");
2621
2622	// Initialize atomic counter buffer.
2623	{
2624		vector<deUint8> emptyData(acBufferInfo.dataSize, 0);
2625
2626		gl.bindBuffer(GL_ATOMIC_COUNTER_BUFFER, *acBuffer);
2627		gl.bufferData(GL_ATOMIC_COUNTER_BUFFER, (glw::GLsizeiptr)emptyData.size(), &emptyData[0], GL_STATIC_READ);
2628		gl.bindBufferBase(GL_ATOMIC_COUNTER_BUFFER, acBufferInfo.index, *acBuffer);
2629		GLU_EXPECT_NO_ERROR(gl.getError(), "Setting up buffer for ac_numPassed failed");
2630	}
2631
2632	gl.useProgram(program);
2633	gl.dispatchCompute(1, 1, 1);
2634	GLU_EXPECT_NO_ERROR(gl.getError(), "glDispatchCompute() failed");
2635
2636	// Read back ac_numPassed data.
2637	{
2638		const void*	mapPtr		= gl.mapBufferRange(GL_ATOMIC_COUNTER_BUFFER, 0, acBufferInfo.dataSize, GL_MAP_READ_BIT);
2639		const int	refCount	= 1;
2640		int			resCount	= 0;
2641
2642		GLU_EXPECT_NO_ERROR(gl.getError(), "glMapBufferRange(GL_ATOMIC_COUNTER_BUFFER) failed");
2643		TCU_CHECK(mapPtr);
2644
2645		resCount = *(const int*)((const deUint8*)mapPtr + acVarInfo.offset);
2646
2647		gl.unmapBuffer(GL_ATOMIC_COUNTER_BUFFER);
2648		GLU_EXPECT_NO_ERROR(gl.getError(), "glUnmapBuffer(GL_ATOMIC_COUNTER_BUFFER) failed");
2649
2650		if (refCount != resCount)
2651		{
2652			m_testCtx.getLog() << TestLog::Message << "ERROR: ac_numPassed = " << resCount << ", expected " << refCount << TestLog::EndMessage;
2653			isOk = false;
2654		}
2655	}
2656
2657	GLU_EXPECT_NO_ERROR(gl.getError(), "Shader execution failed");
2658
2659	return isOk;
2660}
2661
2662} // gles31
2663} // deqp
2664