1/*-------------------------------------------------------------------------
2 * drawElements Quality Program OpenGL (ES) 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 Parametrized, long-running stress case.
22 *
23 * \todo [2013-06-27 nuutti] Do certain things in a cleaner and less
24 *							 confusing way, such as the "redundant buffer
25 *							 factor" thing in LongStressCase.
26 *//*--------------------------------------------------------------------*/
27
28#include "glsLongStressCase.hpp"
29#include "tcuTestLog.hpp"
30#include "tcuCommandLine.hpp"
31#include "tcuTextureUtil.hpp"
32#include "tcuVector.hpp"
33#include "tcuVectorUtil.hpp"
34#include "glsTextureTestUtil.hpp"
35#include "gluPixelTransfer.hpp"
36#include "gluTextureUtil.hpp"
37#include "tcuStringTemplate.hpp"
38#include "gluStrUtil.hpp"
39#include "gluShaderProgram.hpp"
40#include "deRandom.hpp"
41#include "deStringUtil.hpp"
42#include "deString.h"
43#include "deSharedPtr.hpp"
44#include "deClock.h"
45
46#include "glw.h"
47
48#include <limits>
49#include <vector>
50#include <iomanip>
51#include <map>
52#include <iomanip>
53
54using tcu::TestLog;
55using tcu::Vec2;
56using tcu::Vec3;
57using tcu::Vec4;
58using tcu::IVec2;
59using tcu::IVec3;
60using tcu::IVec4;
61using tcu::TextureLevel;
62using tcu::TextureFormat;
63using tcu::ConstPixelBufferAccess;
64using tcu::CubeFace;
65using de::SharedPtr;
66using de::Random;
67using de::toString;
68
69using std::vector;
70using std::string;
71using std::map;
72
73namespace deqp
74{
75namespace gls
76{
77
78using TextureTestUtil::TextureType;
79using TextureTestUtil::TEXTURETYPE_2D;
80using TextureTestUtil::TEXTURETYPE_CUBE;
81
82static const float Mi = (float)(1<<20);
83
84static const deUint32 bufferUsages[] =
85{
86	GL_STATIC_DRAW,
87	GL_STREAM_DRAW,
88	GL_DYNAMIC_DRAW,
89
90	GL_STATIC_READ,
91	GL_STREAM_READ,
92	GL_DYNAMIC_READ,
93
94	GL_STATIC_COPY,
95	GL_STREAM_COPY,
96	GL_DYNAMIC_COPY
97};
98
99static const deUint32 bufferUsagesGLES2[] =
100{
101	GL_STATIC_DRAW,
102	GL_DYNAMIC_DRAW,
103	GL_STREAM_DRAW
104};
105
106static const deUint32 bufferTargets[] =
107{
108	GL_ARRAY_BUFFER,
109	GL_ELEMENT_ARRAY_BUFFER,
110
111	GL_COPY_READ_BUFFER,
112	GL_COPY_WRITE_BUFFER,
113	GL_PIXEL_PACK_BUFFER,
114	GL_PIXEL_UNPACK_BUFFER,
115	GL_TRANSFORM_FEEDBACK_BUFFER,
116	GL_UNIFORM_BUFFER
117};
118
119static const deUint32 bufferTargetsGLES2[] =
120{
121	GL_ARRAY_BUFFER,
122	GL_ELEMENT_ARRAY_BUFFER
123};
124
125static inline int computePixelStore (const TextureFormat& format)
126{
127	const int pixelSize = format.getPixelSize();
128	if (deIsPowerOfTwo32(pixelSize))
129		return de::min(pixelSize, 8);
130	else
131		return 1;
132}
133
134static inline int getNumIterations (const tcu::TestContext& testCtx, const int defaultNumIterations)
135{
136	const int cmdLineVal = testCtx.getCommandLine().getTestIterationCount();
137	return cmdLineVal == 0 ? defaultNumIterations : cmdLineVal;
138}
139
140static inline float triangleArea (const Vec2& a, const Vec2& b, const Vec2& c)
141{
142	const Vec2 ab = b-a;
143	const Vec2 ac = c-a;
144	return 0.5f * tcu::length(ab.x()*ac.y() - ab.y()*ac.x());
145}
146
147static inline string mangleShaderNames (const string& source, const string& manglingSuffix)
148{
149	map<string, string> m;
150	m["NS"] = manglingSuffix;
151	return tcu::StringTemplate(source.c_str()).specialize(m);
152}
153
154template <typename T, int N>
155static inline T randomChoose (Random& rnd, const T (&arr)[N])
156{
157	return rnd.choose<T>(DE_ARRAY_BEGIN(arr), DE_ARRAY_END(arr));
158}
159
160static inline int nextDivisible (const int x, const int div)
161{
162	DE_ASSERT(x >= 0);
163	DE_ASSERT(div >= 1);
164	return x == 0 ? 0 : x-1 + div - (x-1) % div;
165}
166
167static inline string getTimeStr (const deUint64 seconds)
168{
169	const deUint64		m = seconds / 60;
170	const deUint64		h = m / 60;
171	const deUint64		d = h / 24;
172	std::ostringstream	res;
173
174	res << d << "d " << h%24 << "h " << m%60 << "m " << seconds%60 << "s";
175	return res.str();
176}
177
178static inline string probabilityStr (const float prob)
179{
180	return prob == 0.0f ? "never"	:
181		   prob == 1.0f ? "ALWAYS"	:
182		   de::floatToString(prob*100.0f, 0) + "%";
183}
184
185static inline deUint32 randomBufferTarget (Random& rnd, const bool isGLES3)
186{
187	return isGLES3 ? randomChoose(rnd, bufferTargets) : randomChoose(rnd, bufferTargetsGLES2);
188}
189
190static inline deUint32 randomBufferUsage (Random& rnd, const bool isGLES3)
191{
192	return isGLES3 ? randomChoose(rnd, bufferUsages) : randomChoose(rnd, bufferUsagesGLES2);
193}
194
195static inline deUint32 cubeFaceToGLFace (tcu::CubeFace face)
196{
197	switch (face)
198	{
199		case tcu::CUBEFACE_NEGATIVE_X: return GL_TEXTURE_CUBE_MAP_NEGATIVE_X;
200		case tcu::CUBEFACE_POSITIVE_X: return GL_TEXTURE_CUBE_MAP_POSITIVE_X;
201		case tcu::CUBEFACE_NEGATIVE_Y: return GL_TEXTURE_CUBE_MAP_NEGATIVE_Y;
202		case tcu::CUBEFACE_POSITIVE_Y: return GL_TEXTURE_CUBE_MAP_POSITIVE_Y;
203		case tcu::CUBEFACE_NEGATIVE_Z: return GL_TEXTURE_CUBE_MAP_NEGATIVE_Z;
204		case tcu::CUBEFACE_POSITIVE_Z: return GL_TEXTURE_CUBE_MAP_POSITIVE_Z;
205		default:
206			DE_ASSERT(false);
207			return GL_NONE;
208	}
209}
210
211#if defined(DE_DEBUG)
212static inline bool isMatchingGLInternalFormat (const deUint32 internalFormat, const TextureFormat& texFormat)
213{
214	switch (internalFormat)
215	{
216		// Unsized formats.
217
218		case GL_RGBA:				return texFormat.order == TextureFormat::RGBA &&
219											   (texFormat.type == TextureFormat::UNORM_INT8			||
220												texFormat.type == TextureFormat::UNORM_SHORT_4444	||
221												texFormat.type == TextureFormat::UNORM_SHORT_5551);
222
223		case GL_RGB:				return texFormat.order == TextureFormat::RGB &&
224											   (texFormat.type == TextureFormat::UNORM_INT8			||
225												texFormat.type == TextureFormat::UNORM_SHORT_565);
226
227		case GL_LUMINANCE_ALPHA:	return texFormat.order == TextureFormat::LA && texFormat.type == TextureFormat::UNORM_INT8;
228		case GL_LUMINANCE:			return texFormat.order == TextureFormat::L && texFormat.type == TextureFormat::UNORM_INT8;
229		case GL_ALPHA:				return texFormat.order == TextureFormat::A && texFormat.type == TextureFormat::UNORM_INT8;
230
231		// Sized formats.
232
233		default:					return glu::mapGLInternalFormat(internalFormat) == texFormat;
234	}
235}
236#endif // DE_DEBUG
237
238static inline bool compileShader (const deUint32 shaderGL)
239{
240	glCompileShader(shaderGL);
241
242	int success = GL_FALSE;
243	glGetShaderiv(shaderGL, GL_COMPILE_STATUS, &success);
244
245	return success == GL_TRUE;
246}
247
248static inline bool linkProgram (const deUint32 programGL)
249{
250	glLinkProgram(programGL);
251
252	int success = GL_FALSE;
253	glGetProgramiv(programGL, GL_LINK_STATUS, &success);
254
255	return success == GL_TRUE;
256}
257
258static inline string getShaderInfoLog (const deUint32 shaderGL)
259{
260	int				infoLogLen = 0;
261	vector<char>	infoLog;
262	glGetShaderiv(shaderGL, GL_INFO_LOG_LENGTH, &infoLogLen);
263	infoLog.resize(infoLogLen+1);
264	glGetShaderInfoLog(shaderGL, (int)infoLog.size(), DE_NULL, &infoLog[0]);
265	return &infoLog[0];
266}
267
268static inline string getProgramInfoLog (const deUint32 programGL)
269{
270	int				infoLogLen = 0;
271	vector<char>	infoLog;
272	glGetProgramiv(programGL, GL_INFO_LOG_LENGTH, &infoLogLen);
273	infoLog.resize(infoLogLen+1);
274	glGetProgramInfoLog(programGL, (int)infoLog.size(), DE_NULL, &infoLog[0]);
275	return &infoLog[0];
276}
277
278namespace LongStressCaseInternal
279{
280
281// A hacky-ish class for drawing text on screen as GL quads.
282class DebugInfoRenderer
283{
284public:
285								DebugInfoRenderer		(const glu::RenderContext& ctx);
286								~DebugInfoRenderer		(void) { delete m_prog; }
287
288	void						drawInfo				(deUint64 secondsElapsed, int texMem, int maxTexMem, int bufMem, int maxBufMem, int iterNdx);
289
290private:
291								DebugInfoRenderer		(const DebugInfoRenderer&);
292	DebugInfoRenderer&			operator=				(const DebugInfoRenderer&);
293
294	void						render					(void);
295	void						addTextToBuffer			(const string& text, int yOffset);
296
297	const glu::RenderContext&	m_ctx;
298	const glu::ShaderProgram*	m_prog;
299	vector<float>				m_posBuf;
300	vector<deUint16>			m_ndxBuf;
301};
302
303void DebugInfoRenderer::drawInfo (const deUint64 secondsElapsed, const int texMem, const int maxTexMem, const int bufMem, const int maxBufMem, const int iterNdx)
304{
305	const deUint64 m = secondsElapsed / 60;
306	const deUint64 h = m / 60;
307	const deUint64 d = h / 24;
308
309	{
310		std::ostringstream text;
311
312		text << std::setw(2) << std::setfill('0') << d << ":"
313			 << std::setw(2) << std::setfill('0') << h % 24 << ":"
314			 << std::setw(2) << std::setfill('0') << m % 60 << ":"
315			 << std::setw(2) << std::setfill('0') << secondsElapsed % 60;
316		addTextToBuffer(text.str(), 0);
317		text.str("");
318
319		text << std::fixed << std::setprecision(2) << (float)texMem/Mi << "/" << (float)maxTexMem/Mi;
320		addTextToBuffer(text.str(), 1);
321		text.str("");
322
323		text << std::fixed << std::setprecision(2) << (float)bufMem/Mi << "/" << (float)maxBufMem/Mi;
324		addTextToBuffer(text.str(), 2);
325		text.str("");
326
327		text << std::setw(0) << iterNdx;
328		addTextToBuffer(text.str(), 3);
329	}
330
331	render();
332}
333
334DebugInfoRenderer::DebugInfoRenderer (const glu::RenderContext& ctx)
335	: m_ctx			(ctx)
336	, m_prog		(DE_NULL)
337{
338	DE_ASSERT(!m_prog);
339	m_prog = new glu::ShaderProgram(ctx, glu::makeVtxFragSources(
340		"attribute highp vec2 a_pos;\n"
341		"void main (void)\n"
342		"{\n"
343		"	gl_Position = vec4(a_pos, -1.0, 1.0);\n"
344		"}\n",
345
346		"void main(void)\n"
347		"{\n"
348		"	gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);\n"
349		"}\n"));
350}
351
352void DebugInfoRenderer::addTextToBuffer (const string& text, const int yOffset)
353{
354	static const char		characters[]	= "0123456789.:/";
355	const int				numCharacters	= DE_LENGTH_OF_ARRAY(characters)-1; // \note -1 for null byte.
356	const int				charWid			= 6;
357	const int				charHei			= 6;
358	static const string		charsStr		(characters);
359
360	static const char font[numCharacters*charWid*charHei + 1]=
361		" #### ""   #  "" #### ""##### ""   #  ""######"" #####""######"" #### "" #### ""      ""  ##  ""     #"
362		"#    #""  ##  ""#    #""     #""  #   ""#     ""#     ""    # ""#    #""#    #""      ""  ##  ""    # "
363		"#    #""   #  ""    # ""  ### "" #  # "" #### ""# ### ""   #  "" #### "" #####""      ""      ""   #  "
364		"#    #""   #  ""   #  ""     #""######""     #""##   #""  #   ""#    #""     #""      ""  ##  ""  #   "
365		"#    #""   #  ""  #   ""#    #""    # ""#    #""#    #"" #    ""#    #""   ## ""  ##  ""  ##  "" #    "
366		" #### ""  ### ""######"" #### ""    # "" #### "" #### ""#     "" #### ""###   ""  ##  ""      ""#     ";
367
368	for (int ndxInText = 0; ndxInText < (int)text.size(); ndxInText++)
369	{
370		const int ndxInCharset	= (int)charsStr.find(text[ndxInText]);
371		DE_ASSERT(ndxInCharset < numCharacters);
372		const int fontXStart	= ndxInCharset*charWid;
373
374		for (int y = 0; y < charHei; y++)
375		{
376			float ay = -1.0f + (float)(y + 0 + yOffset*(charHei+2))*0.1f/(float)(charHei+2);
377			float by = -1.0f + (float)(y + 1 + yOffset*(charHei+2))*0.1f/(float)(charHei+2);
378			for (int x = 0; x < charWid; x++)
379			{
380				// \note Text is mirrored in x direction since on most(?) mobile devices the image is mirrored(?).
381				float ax = 1.0f - (float)(x + 0 + ndxInText*(charWid+2))*0.1f/(float)(charWid+2);
382				float bx = 1.0f - (float)(x + 1 + ndxInText*(charWid+2))*0.1f/(float)(charWid+2);
383
384				if (font[y*numCharacters*charWid + fontXStart + x] != ' ')
385				{
386					const int vtxNdx = (int)m_posBuf.size()/2;
387
388					m_ndxBuf.push_back(vtxNdx+0);
389					m_ndxBuf.push_back(vtxNdx+1);
390					m_ndxBuf.push_back(vtxNdx+2);
391
392					m_ndxBuf.push_back(vtxNdx+2);
393					m_ndxBuf.push_back(vtxNdx+1);
394					m_ndxBuf.push_back(vtxNdx+3);
395
396					m_posBuf.push_back(ax);
397					m_posBuf.push_back(ay);
398
399					m_posBuf.push_back(bx);
400					m_posBuf.push_back(ay);
401
402					m_posBuf.push_back(ax);
403					m_posBuf.push_back(by);
404
405					m_posBuf.push_back(bx);
406					m_posBuf.push_back(by);
407				}
408			}
409		}
410	}
411}
412
413void DebugInfoRenderer::render (void)
414{
415	const int prog		= m_prog->getProgram();
416	const int posloc	= glGetAttribLocation(prog, "a_pos");
417
418	glUseProgram(prog);
419	glBindBuffer(GL_ARRAY_BUFFER, 0);
420	glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
421	glEnableVertexAttribArray(posloc);
422	glVertexAttribPointer(posloc, 2, GL_FLOAT, 0, 0, &m_posBuf[0]);
423	glDrawElements(GL_TRIANGLES, (int)m_ndxBuf.size(), GL_UNSIGNED_SHORT, &m_ndxBuf[0]);
424	glDisableVertexAttribArray(posloc);
425
426	m_posBuf.clear();
427	m_ndxBuf.clear();
428}
429
430/*--------------------------------------------------------------------*//*!
431 * \brief Texture object helper class
432 *
433 * Each Texture owns a GL texture object that is created when the Texture
434 * is constructed and deleted when it's destructed. The class provides some
435 * convenience interface functions to e.g. upload texture data to the GL.
436 *
437 * In addition, the class tracks the approximate amount of GL memory likely
438 * used by the corresponding GL texture object; get this with
439 * getApproxMemUsage(). Also, getApproxMemUsageDiff() returns N-M, where N
440 * is the value that getApproxMemUsage() would return after a call to
441 * setData() with arguments corresponding to those given to
442 * getApproxMemUsageDiff(), and M is the value currently returned by
443 * getApproxMemUsage(). This can be used to check if we need to free some
444 * other memory before performing the setData() call, in case we have an
445 * upper limit on the amount of memory we want to use.
446 *//*--------------------------------------------------------------------*/
447class Texture
448{
449public:
450						Texture					(TextureType type);
451						~Texture				(void);
452
453	// Functions that may change the value returned by getApproxMemUsage().
454	void				setData					(const ConstPixelBufferAccess& src, int width, int height, deUint32 internalFormat, bool useMipmap);
455
456	// Functions that don't change the value returned by getApproxMemUsage().
457	void				setSubData				(const ConstPixelBufferAccess& src, int xOff, int yOff, int width, int height) const;
458	void				toUnit					(int unit) const;
459	void				setFilter				(deUint32 min, deUint32 mag) const;
460	void				setWrap					(deUint32 s, deUint32 t) const;
461
462	int					getApproxMemUsage		(void) const { return m_dataSizeApprox; }
463	int					getApproxMemUsageDiff	(int width, int height, deUint32 internalFormat, bool useMipmap) const;
464
465private:
466						Texture					(const Texture&); // Not allowed.
467	Texture&			operator=				(const Texture&); // Not allowed.
468
469	static deUint32		genTexture				(void) { deUint32 tex = 0; glGenTextures(1, &tex); return tex; }
470
471	deUint32			getGLBindTarget			(void) const { DE_ASSERT(m_type == TEXTURETYPE_2D || m_type == TEXTURETYPE_CUBE); return m_type == TEXTURETYPE_2D ? GL_TEXTURE_2D : GL_TEXTURE_CUBE_MAP; }
472
473	const TextureType	m_type;
474	const deUint32		m_textureGL;
475
476	int					m_numMipLevels;
477	deUint32			m_internalFormat;
478	int					m_dataSizeApprox;
479};
480
481Texture::Texture (const TextureType type)
482	: m_type			(type)
483	, m_textureGL		(genTexture())
484	, m_numMipLevels	(0)
485	, m_internalFormat	(0)
486	, m_dataSizeApprox	(0)
487{
488}
489
490Texture::~Texture (void)
491{
492	glDeleteTextures(1, &m_textureGL);
493}
494
495int Texture::getApproxMemUsageDiff (const int width, const int height, const deUint32 internalFormat, const bool useMipmap) const
496{
497	const int	numLevels				= useMipmap ? deLog2Floor32(de::max(width, height))+1 : 1;
498	const int	pixelSize				= internalFormat == GL_RGBA		? 4
499										: internalFormat == GL_RGB		? 3
500										: internalFormat == GL_ALPHA	? 1
501										: glu::mapGLInternalFormat(internalFormat).getPixelSize();
502	int			memUsageApproxAfter		= 0;
503
504	for (int level = 0; level < numLevels; level++)
505		memUsageApproxAfter += de::max(1, width>>level) * de::max(1, height>>level) * pixelSize * (m_type == TEXTURETYPE_CUBE ? 6 : 1);
506
507	return memUsageApproxAfter - getApproxMemUsage();
508}
509
510void Texture::setData (const ConstPixelBufferAccess& src, const int width, const int height, const deUint32 internalFormat, const bool useMipmap)
511{
512	DE_ASSERT(m_type != TEXTURETYPE_CUBE || width == height);
513	DE_ASSERT(!useMipmap || (deIsPowerOfTwo32(width) && deIsPowerOfTwo32(height)));
514
515	const TextureFormat&		format		= src.getFormat();
516	const glu::TransferFormat	transfer	= glu::getTransferFormat(format);
517
518	m_numMipLevels = useMipmap ? deLog2Floor32(de::max(width, height))+1 : 1;
519
520	m_internalFormat = internalFormat;
521	m_dataSizeApprox = width * height * format.getPixelSize() * (m_type == TEXTURETYPE_CUBE ? 6 : 1);
522
523	DE_ASSERT(src.getRowPitch() == format.getPixelSize()*src.getWidth());
524	DE_ASSERT(isMatchingGLInternalFormat(internalFormat, format));
525	DE_ASSERT(width <= src.getWidth() && height <= src.getHeight());
526
527	glPixelStorei(GL_UNPACK_ALIGNMENT, computePixelStore(format));
528
529	if (m_type == TEXTURETYPE_2D)
530	{
531		m_dataSizeApprox = 0;
532
533		glBindTexture(GL_TEXTURE_2D, m_textureGL);
534		for (int level = 0; level < m_numMipLevels; level++)
535		{
536			const int levelWid = de::max(1, width>>level);
537			const int levelHei = de::max(1, height>>level);
538			m_dataSizeApprox += levelWid * levelHei * format.getPixelSize();
539			glTexImage2D(GL_TEXTURE_2D, level, internalFormat, levelWid, levelHei, 0, transfer.format, transfer.dataType, src.getDataPtr());
540		}
541	}
542	else if (m_type == TEXTURETYPE_CUBE)
543	{
544		m_dataSizeApprox = 0;
545
546		glBindTexture(GL_TEXTURE_CUBE_MAP, m_textureGL);
547		for (int level = 0; level < m_numMipLevels; level++)
548		{
549			const int levelWid = de::max(1, width>>level);
550			const int levelHei = de::max(1, height>>level);
551			m_dataSizeApprox += 6 * levelWid * levelHei * format.getPixelSize();
552			for (int face = 0; face < tcu::CUBEFACE_LAST; face++)
553				glTexImage2D(cubeFaceToGLFace((CubeFace)face), level, internalFormat, levelWid, levelHei, 0, transfer.format, transfer.dataType, src.getDataPtr());
554		}
555	}
556	else
557		DE_ASSERT(false);
558}
559
560void Texture::setSubData (const ConstPixelBufferAccess& src, const int xOff, const int yOff, const int width, const int height) const
561{
562	const TextureFormat&		format		= src.getFormat();
563	const glu::TransferFormat	transfer	= glu::getTransferFormat(format);
564
565	DE_ASSERT(src.getRowPitch() == format.getPixelSize()*src.getWidth());
566	DE_ASSERT(isMatchingGLInternalFormat(m_internalFormat, format));
567	DE_ASSERT(width <= src.getWidth() && height <= src.getHeight());
568
569	glPixelStorei(GL_UNPACK_ALIGNMENT, computePixelStore(format));
570
571	if (m_type == TEXTURETYPE_2D)
572	{
573		glBindTexture(GL_TEXTURE_2D, m_textureGL);
574		for (int level = 0; level < m_numMipLevels; level++)
575			glTexSubImage2D(GL_TEXTURE_2D, level, xOff>>level, yOff>>level, de::max(1, width>>level), de::max(1, height>>level), transfer.format, transfer.dataType, src.getDataPtr());
576	}
577	else if (m_type == TEXTURETYPE_CUBE)
578	{
579		glBindTexture(GL_TEXTURE_CUBE_MAP, m_textureGL);
580		for (int level = 0; level < m_numMipLevels; level++)
581		{
582			for (int face = 0; face < tcu::CUBEFACE_LAST; face++)
583				glTexSubImage2D(cubeFaceToGLFace((CubeFace)face), level, xOff>>level, yOff>>level, de::max(1, width>>level), de::max(1, height>>level), transfer.format, transfer.dataType, src.getDataPtr());
584		}
585	}
586	else
587		DE_ASSERT(false);
588}
589
590void Texture::setFilter (const deUint32 min, const deUint32 mag) const
591{
592	glBindTexture(getGLBindTarget(), m_textureGL);
593	glTexParameteri(getGLBindTarget(), GL_TEXTURE_MIN_FILTER, min);
594	glTexParameteri(getGLBindTarget(), GL_TEXTURE_MAG_FILTER, mag);
595}
596
597void Texture::setWrap (const deUint32 s, const deUint32 t) const
598{
599	glBindTexture(getGLBindTarget(), m_textureGL);
600	glTexParameteri(getGLBindTarget(), GL_TEXTURE_WRAP_S, s);
601	glTexParameteri(getGLBindTarget(), GL_TEXTURE_WRAP_T, t);
602}
603
604void Texture::toUnit (const int unit) const
605{
606	glActiveTexture(GL_TEXTURE0 + unit);
607	glBindTexture(getGLBindTarget(), m_textureGL);
608}
609
610/*--------------------------------------------------------------------*//*!
611 * \brief Buffer object helper class
612 *
613 * Each Buffer owns a GL buffer object that is created when the Buffer
614 * is constructed and deleted when it's destructed. The class provides some
615 * convenience interface functions to e.g. upload buffer data to the GL.
616 *
617 * In addition, the class tracks the approximate amount of GL memory,
618 * similarly to the Texture class (see above). The getApproxMemUsageDiff()
619 * is also analoguous.
620 *//*--------------------------------------------------------------------*/
621class Buffer
622{
623public:
624						Buffer					(void);
625						~Buffer					(void);
626
627	// Functions that may change the value returned by getApproxMemUsage().
628	template <typename T>
629	void				setData					(const vector<T>& src, const deUint32 target, const deUint32 usage) { setData(&src[0], (int)(src.size()*sizeof(T)), target, usage); }
630	void				setData					(const void* src, int size, deUint32 target, deUint32 usage);
631
632	// Functions that don't change the value returned by getApproxMemUsage().
633	template <typename T>
634	void				setSubData				(const vector<T>& src, const int offsetElems, const int numElems, const deUint32 target) { setSubData(&src[offsetElems], offsetElems*(int)sizeof(T), numElems*(int)sizeof(T), target); }
635	void				setSubData				(const void* src, int offsetBytes, int sizeBytes, deUint32 target) const;
636	void				bind					(const deUint32 target) const { glBindBuffer(target, m_bufferGL); }
637
638	int					getApproxMemUsage		(void) const { return m_dataSizeApprox; }
639	template <typename T>
640	int					getApproxMemUsageDiff	(const vector<T>& src) const { return getApproxMemUsageDiff((int)(src.size()*sizeof(T))); }
641	int					getApproxMemUsageDiff	(const int sizeBytes) const { return sizeBytes - getApproxMemUsage(); }
642
643private:
644						Buffer					(const Buffer&); // Not allowed.
645	Buffer&				operator=				(const Buffer&); // Not allowed.
646
647	static deUint32		genBuffer				(void) { deUint32 buf = 0; glGenBuffers(1, &buf); return buf; }
648
649	const deUint32		m_bufferGL;
650	int					m_dataSizeApprox;
651};
652
653Buffer::Buffer (void)
654	: m_bufferGL		(genBuffer())
655	, m_dataSizeApprox	(0)
656{
657}
658
659Buffer::~Buffer (void)
660{
661	glDeleteBuffers(1, &m_bufferGL);
662}
663
664void Buffer::setData (const void* const src, const int size, const deUint32 target, const deUint32 usage)
665{
666	bind(target);
667	glBufferData(target, size, src, usage);
668	glBindBuffer(target, 0);
669
670	m_dataSizeApprox = size;
671}
672
673void Buffer::setSubData (const void* const src, const int offsetBytes, const int sizeBytes, const deUint32 target) const
674{
675	bind(target);
676	glBufferSubData(target, offsetBytes, sizeBytes, src);
677	glBindBuffer(target, 0);
678}
679
680class Program
681{
682public:
683						Program					(void);
684						~Program				(void);
685
686	void				setSources				(const string& vertSource, const string& fragSource);
687	void				build					(TestLog& log);
688	void				use						(void) const { DE_ASSERT(m_isBuilt); glUseProgram(m_programGL); }
689	void				setRandomUniforms		(const vector<VarSpec>& uniforms, const string& shaderNameManglingSuffix, Random& rnd) const;
690	void				setAttribute			(const Buffer& attrBuf, int attrBufOffset, const VarSpec& attrSpec, const string& shaderNameManglingSuffix) const;
691	void				setAttributeClientMem	(const void* attrData, const VarSpec& attrSpec, const string& shaderNameManglingSuffix) const;
692	void				disableAttributeArray	(const VarSpec& attrSpec, const string& shaderNameManglingSuffix) const;
693
694private:
695						Program				(const Program&); // Not allowed.
696	Program&			operator=			(const Program&); // Not allowed.
697
698	string				m_vertSource;
699	string				m_fragSource;
700
701	const deUint32		m_vertShaderGL;
702	const deUint32		m_fragShaderGL;
703	const deUint32		m_programGL;
704	bool				m_hasSources;
705	bool				m_isBuilt;
706};
707
708Program::Program (void)
709	: m_vertShaderGL	(glCreateShader(GL_VERTEX_SHADER))
710	, m_fragShaderGL	(glCreateShader(GL_FRAGMENT_SHADER))
711	, m_programGL		(glCreateProgram())
712	, m_hasSources		(false)
713	, m_isBuilt			(false)
714{
715	glAttachShader(m_programGL, m_vertShaderGL);
716	glAttachShader(m_programGL, m_fragShaderGL);
717}
718
719Program::~Program (void)
720{
721	glDeleteShader(m_vertShaderGL);
722	glDeleteShader(m_fragShaderGL);
723	glDeleteProgram(m_programGL);
724}
725
726void Program::setSources (const string& vertSource, const string& fragSource)
727{
728	const char* const vertSourceCstr = vertSource.c_str();
729	const char* const fragSourceCstr = fragSource.c_str();
730
731	m_vertSource = vertSource;
732	m_fragSource = fragSource;
733
734	// \note In GLES2 api the source parameter type lacks one const.
735	glShaderSource(m_vertShaderGL, 1, (const char**)&vertSourceCstr, DE_NULL);
736	glShaderSource(m_fragShaderGL, 1, (const char**)&fragSourceCstr, DE_NULL);
737
738	m_hasSources = true;
739}
740
741void Program::build (TestLog& log)
742{
743	DE_ASSERT(m_hasSources);
744
745	const bool vertCompileOk	= compileShader(m_vertShaderGL);
746	const bool fragCompileOk	= compileShader(m_fragShaderGL);
747	const bool attemptLink		= vertCompileOk && fragCompileOk;
748	const bool linkOk			= attemptLink && linkProgram(m_programGL);
749
750	if (!(vertCompileOk && fragCompileOk && linkOk))
751	{
752		log << TestLog::ShaderProgram(linkOk, attemptLink ? getProgramInfoLog(m_programGL) : string(""))
753			<< TestLog::Shader(QP_SHADER_TYPE_VERTEX, m_vertSource, vertCompileOk, getShaderInfoLog(m_vertShaderGL))
754			<< TestLog::Shader(QP_SHADER_TYPE_FRAGMENT, m_fragSource, fragCompileOk, getShaderInfoLog(m_fragShaderGL))
755			<< TestLog::EndShaderProgram;
756
757		throw tcu::TestError("Program build failed");
758	}
759
760	m_isBuilt = true;
761}
762
763void Program::setRandomUniforms (const vector<VarSpec>& uniforms, const string& shaderNameManglingSuffix, Random& rnd) const
764{
765	use();
766
767	for (int unifNdx = 0; unifNdx < (int)uniforms.size(); unifNdx++)
768	{
769		const VarSpec&	spec			= uniforms[unifNdx];
770		const int		typeScalarSize	= glu::getDataTypeScalarSize(spec.type);
771		const int		location		= glGetUniformLocation(m_programGL, mangleShaderNames(spec.name, shaderNameManglingSuffix).c_str());
772		if (location < 0)
773			continue;
774
775		if (glu::isDataTypeFloatOrVec(spec.type))
776		{
777			float val[4];
778			for (int i = 0; i < typeScalarSize; i++)
779				val[i] = rnd.getFloat(spec.minValue.f[i], spec.maxValue.f[i]);
780
781			switch (spec.type)
782			{
783				case glu::TYPE_FLOAT:		glUniform1f(location, val[0]);							break;
784				case glu::TYPE_FLOAT_VEC2:	glUniform2f(location, val[0], val[1]);					break;
785				case glu::TYPE_FLOAT_VEC3:	glUniform3f(location, val[0], val[1], val[2]);			break;
786				case glu::TYPE_FLOAT_VEC4:	glUniform4f(location, val[0], val[1], val[2], val[3]);	break;
787				default: DE_ASSERT(false);
788			}
789		}
790		else if (glu::isDataTypeMatrix(spec.type))
791		{
792			float val[4*4];
793			for (int i = 0; i < typeScalarSize; i++)
794				val[i] = rnd.getFloat(spec.minValue.f[i], spec.maxValue.f[i]);
795
796			switch (spec.type)
797			{
798				case glu::TYPE_FLOAT_MAT2:		glUniformMatrix2fv		(location, 1, GL_FALSE, &val[0]); break;
799				case glu::TYPE_FLOAT_MAT3:		glUniformMatrix3fv		(location, 1, GL_FALSE, &val[0]); break;
800				case glu::TYPE_FLOAT_MAT4:		glUniformMatrix4fv		(location, 1, GL_FALSE, &val[0]); break;
801				case glu::TYPE_FLOAT_MAT2X3:	glUniformMatrix2x3fv	(location, 1, GL_FALSE, &val[0]); break;
802				case glu::TYPE_FLOAT_MAT2X4:	glUniformMatrix2x4fv	(location, 1, GL_FALSE, &val[0]); break;
803				case glu::TYPE_FLOAT_MAT3X2:	glUniformMatrix3x2fv	(location, 1, GL_FALSE, &val[0]); break;
804				case glu::TYPE_FLOAT_MAT3X4:	glUniformMatrix3x4fv	(location, 1, GL_FALSE, &val[0]); break;
805				case glu::TYPE_FLOAT_MAT4X2:	glUniformMatrix4x2fv	(location, 1, GL_FALSE, &val[0]); break;
806				case glu::TYPE_FLOAT_MAT4X3:	glUniformMatrix4x3fv	(location, 1, GL_FALSE, &val[0]); break;
807				default: DE_ASSERT(false);
808			}
809		}
810		else if (glu::isDataTypeIntOrIVec(spec.type))
811		{
812			int val[4];
813			for (int i = 0; i < typeScalarSize; i++)
814				val[i] = rnd.getInt(spec.minValue.i[i], spec.maxValue.i[i]);
815
816			switch (spec.type)
817			{
818				case glu::TYPE_INT:			glUniform1i(location, val[0]);							break;
819				case glu::TYPE_INT_VEC2:	glUniform2i(location, val[0], val[1]);					break;
820				case glu::TYPE_INT_VEC3:	glUniform3i(location, val[0], val[1], val[2]);			break;
821				case glu::TYPE_INT_VEC4:	glUniform4i(location, val[0], val[1], val[2], val[3]);	break;
822				default: DE_ASSERT(false);
823			}
824		}
825		else if (glu::isDataTypeUintOrUVec(spec.type))
826		{
827			deUint32 val[4];
828			for (int i = 0; i < typeScalarSize; i++)
829			{
830				DE_ASSERT(spec.minValue.i[i] >= 0 && spec.maxValue.i[i] >= 0);
831				val[i] = (deUint32)rnd.getInt(spec.minValue.i[i], spec.maxValue.i[i]);
832			}
833
834			switch (spec.type)
835			{
836				case glu::TYPE_UINT:		glUniform1ui(location, val[0]);							break;
837				case glu::TYPE_UINT_VEC2:	glUniform2ui(location, val[0], val[1]);					break;
838				case glu::TYPE_UINT_VEC3:	glUniform3ui(location, val[0], val[1], val[2]);			break;
839				case glu::TYPE_UINT_VEC4:	glUniform4ui(location, val[0], val[1], val[2], val[3]);	break;
840				default: DE_ASSERT(false);
841			}
842		}
843		else
844			DE_ASSERT(false);
845	}
846}
847
848void Program::setAttribute (const Buffer& attrBuf, const int attrBufOffset, const VarSpec& attrSpec, const string& shaderNameManglingSuffix) const
849{
850	const int attrLoc = glGetAttribLocation(m_programGL, mangleShaderNames(attrSpec.name, shaderNameManglingSuffix).c_str());
851
852	glEnableVertexAttribArray(attrLoc);
853	attrBuf.bind(GL_ARRAY_BUFFER);
854
855	if (glu::isDataTypeFloatOrVec(attrSpec.type))
856		glVertexAttribPointer(attrLoc, glu::getDataTypeScalarSize(attrSpec.type), GL_FLOAT, GL_FALSE, 0, (GLvoid*)(deIntptr)attrBufOffset);
857	else
858		DE_ASSERT(false);
859}
860
861void Program::setAttributeClientMem (const void* const attrData, const VarSpec& attrSpec, const string& shaderNameManglingSuffix) const
862{
863	const int attrLoc = glGetAttribLocation(m_programGL, mangleShaderNames(attrSpec.name, shaderNameManglingSuffix).c_str());
864
865	glEnableVertexAttribArray(attrLoc);
866	glBindBuffer(GL_ARRAY_BUFFER, 0);
867
868	if (glu::isDataTypeFloatOrVec(attrSpec.type))
869		glVertexAttribPointer(attrLoc, glu::getDataTypeScalarSize(attrSpec.type), GL_FLOAT, GL_FALSE, 0, attrData);
870	else
871		DE_ASSERT(false);
872}
873
874void Program::disableAttributeArray (const VarSpec& attrSpec, const string& shaderNameManglingSuffix) const
875{
876	const int attrLoc = glGetAttribLocation(m_programGL, mangleShaderNames(attrSpec.name, shaderNameManglingSuffix).c_str());
877
878	glDisableVertexAttribArray(attrLoc);
879}
880
881/*--------------------------------------------------------------------*//*!
882 * \brief Container class for managing GL objects
883 *
884 * GLObjectManager can be used for objects of class Program, Buffer or
885 * Texture. In the manager, each such object is associated with a name that
886 * is used to access it.
887 *
888 * In addition to the making, getting and removing functions, the manager
889 * supports marking objects as "garbage", meaning they're not yet
890 * destroyed, but can be later destroyed with removeRandomGarbage(). The
891 * idea is that if we want to stress test with high memory usage, we can
892 * continuously move objects to garbage after using them, and when a memory
893 * limit is reached, we can call removeGarbageUntilUnder(limit, rnd). This
894 * way we can approximately keep our memory usage at just under the wanted
895 * limit.
896 *
897 * The manager also supports querying the approximate amount of GL memory
898 * used by its objects.
899 *
900 * \note The memory usage related functions are not currently supported
901 *		 for Program objects.
902 *//*--------------------------------------------------------------------*/
903template <typename T>
904class GLObjectManager
905{
906public:
907	void						make						(const string& name)								{ DE_ASSERT(!has(name)); m_objects[name] = SharedPtr<T>(new T); }
908	void						make						(const string& name, gls::TextureType texType)		{ DE_ASSERT(!has(name)); m_objects[name] = SharedPtr<T>(new T(texType)); }
909	bool						has							(const string& name) const	{ return m_objects.find(name) != m_objects.end(); }
910	const T&					get							(const string& name) const;
911	T&							get							(const string& name)		{ return const_cast<T&>(((const GLObjectManager<T>*)this)->get(name)); }
912	void						remove						(const string& name)		{ const int removed = (int)m_objects.erase(name); DE_ASSERT(removed); DE_UNREF(removed); }
913	int							computeApproxMemUsage		(void) const;
914	void						markAsGarbage				(const string& name);
915	int							removeRandomGarbage			(Random& rnd);
916	void						removeGarbageUntilUnder		(int limit, Random& rnd);
917
918private:
919	static const char*			objTypeName					(void);
920
921	map<string, SharedPtr<T> >	m_objects;
922	vector<SharedPtr<T> >		m_garbageObjects;
923};
924
925template <> const char* GLObjectManager<Buffer>::objTypeName	(void) { return "buffer"; }
926template <> const char* GLObjectManager<Texture>::objTypeName	(void) { return "texture"; }
927template <> const char* GLObjectManager<Program>::objTypeName	(void) { return "program"; }
928
929template <typename T>
930const T& GLObjectManager<T>::get (const string& name) const
931{
932	const typename map<string, SharedPtr<T> >::const_iterator it = m_objects.find(name);
933	DE_ASSERT(it != m_objects.end());
934	return *it->second;
935}
936
937template <typename T>
938int GLObjectManager<T>::computeApproxMemUsage (void) const
939{
940	int result = 0;
941
942	for (typename map<string, SharedPtr<T> >::const_iterator it = m_objects.begin(); it != m_objects.end(); ++it)
943		result += it->second->getApproxMemUsage();
944
945	for (typename vector<SharedPtr<T> >::const_iterator it = m_garbageObjects.begin(); it != m_garbageObjects.end(); ++it)
946		result += (*it)->getApproxMemUsage();
947
948	return result;
949}
950
951template <typename T>
952void GLObjectManager<T>::markAsGarbage (const string& name)
953{
954	const typename map<string, SharedPtr<T> >::iterator it = m_objects.find(name);
955	DE_ASSERT(it != m_objects.end());
956	m_garbageObjects.push_back(it->second);
957	m_objects.erase(it);
958}
959
960template <typename T>
961int GLObjectManager<T>::removeRandomGarbage (Random& rnd)
962{
963	if (m_garbageObjects.empty())
964		return -1;
965
966	const int removeNdx		= rnd.getInt(0, (int)m_garbageObjects.size()-1);
967	const int memoryFreed	= m_garbageObjects[removeNdx]->getApproxMemUsage();
968	m_garbageObjects.erase(m_garbageObjects.begin() + removeNdx);
969	return memoryFreed;
970}
971
972template <typename T>
973void GLObjectManager<T>::removeGarbageUntilUnder (const int limit, Random& rnd)
974{
975	int memUsage = computeApproxMemUsage();
976
977	while (memUsage > limit)
978	{
979		const int memReleased = removeRandomGarbage(rnd);
980		if (memReleased < 0)
981			throw tcu::InternalError(string("") + "Given " + objTypeName() + " memory usage limit exceeded, and no unneeded " + objTypeName() + " resources available to release");
982		memUsage -= memReleased;
983		DE_ASSERT(memUsage == computeApproxMemUsage());
984	}
985}
986
987} // LongStressCaseInternal
988
989using namespace LongStressCaseInternal;
990
991static int generateRandomAttribData (vector<deUint8>& attrDataBuf, int& dataSizeBytesDst, const VarSpec& attrSpec, const int numVertices, Random& rnd)
992{
993	const bool	isFloat			= glu::isDataTypeFloatOrVec(attrSpec.type);
994	const int	numComponents	= glu::getDataTypeScalarSize(attrSpec.type);
995	const int	componentSize	= (int)(isFloat ? sizeof(GLfloat) : sizeof(GLint));
996	const int	offsetInBuf		= nextDivisible((int)attrDataBuf.size(), componentSize); // Round up for alignment.
997
998	DE_STATIC_ASSERT(sizeof(GLint) == sizeof(int));
999	DE_STATIC_ASSERT(sizeof(GLfloat) == sizeof(float));
1000
1001	dataSizeBytesDst = numComponents*componentSize*numVertices;
1002
1003	attrDataBuf.resize(offsetInBuf + dataSizeBytesDst);
1004
1005	if (isFloat)
1006	{
1007		float* const data = (float*)&attrDataBuf[offsetInBuf];
1008
1009		for (int vtxNdx = 0; vtxNdx < numVertices; vtxNdx++)
1010			for (int compNdx = 0; compNdx < numComponents; compNdx++)
1011				data[vtxNdx*numComponents + compNdx] = rnd.getFloat(attrSpec.minValue.f[compNdx], attrSpec.maxValue.f[compNdx]);
1012	}
1013	else
1014	{
1015		DE_ASSERT(glu::isDataTypeIntOrIVec(attrSpec.type));
1016
1017		int* const data = (int*)&attrDataBuf[offsetInBuf];
1018
1019		for (int vtxNdx = 0; vtxNdx < numVertices; vtxNdx++)
1020			for (int compNdx = 0; compNdx < numComponents; compNdx++)
1021				data[vtxNdx*numComponents + compNdx] = rnd.getInt(attrSpec.minValue.i[compNdx], attrSpec.maxValue.i[compNdx]);
1022	}
1023
1024	return offsetInBuf;
1025}
1026
1027static int generateRandomPositionAttribData (vector<deUint8>& attrDataBuf, int& dataSizeBytesDst, const VarSpec& attrSpec, const int numVertices, Random& rnd)
1028{
1029	DE_ASSERT(glu::isDataTypeFloatOrVec(attrSpec.type));
1030
1031	const int numComponents = glu::getDataTypeScalarSize(attrSpec.type);
1032	DE_ASSERT(numComponents >= 2);
1033	const int offsetInBuf = generateRandomAttribData(attrDataBuf, dataSizeBytesDst, attrSpec, numVertices, rnd);
1034
1035	if (numComponents > 2)
1036	{
1037		float* const data = (float*)&attrDataBuf[offsetInBuf];
1038
1039		for (int vtxNdx = 0; vtxNdx < numVertices; vtxNdx++)
1040			data[vtxNdx*numComponents + 2] = -1.0f;
1041
1042		for (int triNdx = 0; triNdx < numVertices-2; triNdx++)
1043		{
1044			float* const	vtxAComps	= &data[(triNdx+0)*numComponents];
1045			float* const	vtxBComps	= &data[(triNdx+1)*numComponents];
1046			float* const	vtxCComps	= &data[(triNdx+2)*numComponents];
1047
1048			const float		triArea		= triangleArea(Vec2(vtxAComps[0], vtxAComps[1]),
1049													   Vec2(vtxBComps[0], vtxBComps[1]),
1050													   Vec2(vtxCComps[0], vtxCComps[1]));
1051			const float		t			= triArea / (triArea + 1.0f);
1052			const float		z			= (1.0f-t)*attrSpec.minValue.f[2] + t*attrSpec.maxValue.f[2];
1053
1054			vtxAComps[2] = de::max(vtxAComps[2], z);
1055			vtxBComps[2] = de::max(vtxBComps[2], z);
1056			vtxCComps[2] = de::max(vtxCComps[2], z);
1057		}
1058	}
1059
1060	return offsetInBuf;
1061}
1062
1063static void generateAttribs (vector<deUint8>& attrDataBuf, vector<int>& attrDataOffsets, vector<int>& attrDataSizes, const vector<VarSpec>& attrSpecs, const string& posAttrName, const int numVertices, Random& rnd)
1064{
1065	attrDataBuf.clear();
1066	attrDataOffsets.clear();
1067	attrDataSizes.resize(attrSpecs.size());
1068
1069	for (int i = 0; i < (int)attrSpecs.size(); i++)
1070	{
1071		if (attrSpecs[i].name == posAttrName)
1072			attrDataOffsets.push_back(generateRandomPositionAttribData(attrDataBuf, attrDataSizes[i], attrSpecs[i], numVertices, rnd));
1073		else
1074			attrDataOffsets.push_back(generateRandomAttribData(attrDataBuf, attrDataSizes[i], attrSpecs[i], numVertices, rnd));
1075	}
1076}
1077
1078LongStressCase::LongStressCase (tcu::TestContext&				testCtx,
1079								const glu::RenderContext&		renderCtx,
1080								const char* const				name,
1081								const char* const				desc,
1082								const int						maxTexMemoryUsageBytes,
1083								const int						maxBufMemoryUsageBytes,
1084								const int						numDrawCallsPerIteration,
1085								const int						numTrianglesPerDrawCall,
1086								const vector<ProgramContext>&	programContexts,
1087								const FeatureProbabilities&		probabilities,
1088								const deUint32					indexBufferUsage,
1089								const deUint32					attrBufferUsage,
1090								const int						redundantBufferFactor,
1091								const bool						showDebugInfo)
1092	: tcu::TestCase					(testCtx, name, desc)
1093	, m_renderCtx					(renderCtx)
1094	, m_maxTexMemoryUsageBytes		(maxTexMemoryUsageBytes)
1095	, m_maxBufMemoryUsageBytes		(maxBufMemoryUsageBytes)
1096	, m_numDrawCallsPerIteration	(numDrawCallsPerIteration)
1097	, m_numTrianglesPerDrawCall		(numTrianglesPerDrawCall)
1098	, m_numVerticesPerDrawCall		(numTrianglesPerDrawCall+2) // \note Triangle strips are used.
1099	, m_programContexts				(programContexts)
1100	, m_probabilities				(probabilities)
1101	, m_indexBufferUsage			(indexBufferUsage)
1102	, m_attrBufferUsage				(attrBufferUsage)
1103	, m_redundantBufferFactor		(redundantBufferFactor)
1104	, m_showDebugInfo				(showDebugInfo)
1105	, m_numIterations				(getNumIterations(testCtx, 5))
1106	, m_isGLES3						(contextSupports(renderCtx.getType(), glu::ApiType::es(3,0)))
1107	, m_currentIteration			(0)
1108	, m_startTimeSeconds			((deUint64)-1)
1109	, m_lastLogTime					((deUint64)-1)
1110	, m_lastLogIteration			(0)
1111	, m_currentLogEntryNdx			(0)
1112	, m_rnd							(deStringHash(getName()) ^ testCtx.getCommandLine().getBaseSeed())
1113	, m_programs					(DE_NULL)
1114	, m_buffers						(DE_NULL)
1115	, m_textures					(DE_NULL)
1116	, m_debugInfoRenderer			(DE_NULL)
1117{
1118	DE_ASSERT(m_numVerticesPerDrawCall <= (int)std::numeric_limits<deUint16>::max()+1); // \note Vertices are referred to with 16-bit indices.
1119	DE_ASSERT(m_redundantBufferFactor > 0);
1120}
1121
1122LongStressCase::~LongStressCase (void)
1123{
1124	LongStressCase::deinit();
1125}
1126
1127void LongStressCase::init (void)
1128{
1129	// Generate dummy texture data for each texture spec in m_programContexts.
1130
1131	DE_ASSERT(!m_programContexts.empty());
1132	DE_ASSERT(m_programResources.empty());
1133	m_programResources.resize(m_programContexts.size());
1134
1135	for (int progCtxNdx = 0; progCtxNdx < (int)m_programContexts.size(); progCtxNdx++)
1136	{
1137		const ProgramContext&	progCtx = m_programContexts[progCtxNdx];
1138		ProgramResources&		progRes = m_programResources[progCtxNdx];
1139
1140		for (int texSpecNdx = 0; texSpecNdx < (int)progCtx.textureSpecs.size(); texSpecNdx++)
1141		{
1142			const TextureSpec&		spec	= progCtx.textureSpecs[texSpecNdx];
1143			const TextureFormat		format	= glu::mapGLTransferFormat(spec.format, spec.dataType);
1144
1145			// If texture data with the same format has already been generated, re-use that (don't care much about contents).
1146
1147			SharedPtr<TextureLevel> dummyTex;
1148
1149			for (int prevProgCtxNdx = 0; prevProgCtxNdx < (int)m_programResources.size(); prevProgCtxNdx++)
1150			{
1151				const vector<SharedPtr<TextureLevel> >& prevProgCtxTextures = m_programResources[prevProgCtxNdx].dummyTextures;
1152
1153				for (int texNdx = 0; texNdx < (int)prevProgCtxTextures.size(); texNdx++)
1154				{
1155					if (prevProgCtxTextures[texNdx]->getFormat() == format)
1156					{
1157						dummyTex = prevProgCtxTextures[texNdx];
1158						break;
1159					}
1160				}
1161			}
1162
1163			if (!dummyTex)
1164				dummyTex = SharedPtr<TextureLevel>(new TextureLevel(format));
1165
1166			if (dummyTex->getWidth() < spec.width || dummyTex->getHeight() < spec.height)
1167			{
1168				dummyTex->setSize(spec.width, spec.height);
1169				tcu::fillWithComponentGradients(dummyTex->getAccess(), spec.minValue, spec.maxValue);
1170			}
1171
1172			progRes.dummyTextures.push_back(dummyTex);
1173		}
1174	}
1175
1176	m_vertexIndices.clear();
1177	for (int i = 0; i < m_numVerticesPerDrawCall; i++)
1178		m_vertexIndices.push_back((deUint16)i);
1179	m_rnd.shuffle(m_vertexIndices.begin(), m_vertexIndices.end());
1180
1181	DE_ASSERT(!m_programs && !m_buffers && !m_textures);
1182	m_programs = new GLObjectManager<Program>;
1183	m_buffers = new GLObjectManager<Buffer>;
1184	m_textures = new GLObjectManager<Texture>;
1185
1186	m_currentIteration = 0;
1187
1188	{
1189		TestLog& log = m_testCtx.getLog();
1190
1191		log << TestLog::Message << "Number of iterations: "										<< (m_numIterations > 0 ? toString(m_numIterations) : "infinite")				<< TestLog::EndMessage
1192			<< TestLog::Message << "Number of draw calls per iteration: "						<< m_numDrawCallsPerIteration													<< TestLog::EndMessage
1193			<< TestLog::Message << "Number of triangles per draw call: "						<< m_numTrianglesPerDrawCall													<< TestLog::EndMessage
1194			<< TestLog::Message << "Using triangle strips"																														<< TestLog::EndMessage
1195			<< TestLog::Message << "Approximate texture memory usage limit: "					<< de::floatToString((float)m_maxTexMemoryUsageBytes / Mi, 2) << " MiB"			<< TestLog::EndMessage
1196			<< TestLog::Message << "Approximate buffer memory usage limit: "					<< de::floatToString((float)m_maxBufMemoryUsageBytes / Mi, 2) << " MiB"			<< TestLog::EndMessage
1197			<< TestLog::Message << "Default vertex attribute data buffer usage parameter: "		<< glu::getUsageName(m_attrBufferUsage)											<< TestLog::EndMessage
1198			<< TestLog::Message << "Default vertex index data buffer usage parameter: "			<< glu::getUsageName(m_indexBufferUsage)										<< TestLog::EndMessage
1199
1200			<< TestLog::Section("ProbabilityParams", "Per-iteration probability parameters")
1201			<< TestLog::Message << "Program re-build: "															<< probabilityStr(m_probabilities.rebuildProgram)				<< TestLog::EndMessage
1202			<< TestLog::Message << "Texture re-upload: "														<< probabilityStr(m_probabilities.reuploadTexture)				<< TestLog::EndMessage
1203			<< TestLog::Message << "Buffer re-upload: "															<< probabilityStr(m_probabilities.reuploadBuffer)				<< TestLog::EndMessage
1204			<< TestLog::Message << "Use glTexImage* instead of glTexSubImage* when uploading texture: "			<< probabilityStr(m_probabilities.reuploadWithTexImage)			<< TestLog::EndMessage
1205			<< TestLog::Message << "Use glBufferData* instead of glBufferSubData* when uploading buffer: "		<< probabilityStr(m_probabilities.reuploadWithBufferData)		<< TestLog::EndMessage
1206			<< TestLog::Message << "Delete texture after using it, even if could re-use it: "					<< probabilityStr(m_probabilities.deleteTexture)				<< TestLog::EndMessage
1207			<< TestLog::Message << "Delete buffer after using it, even if could re-use it: "					<< probabilityStr(m_probabilities.deleteBuffer)					<< TestLog::EndMessage
1208			<< TestLog::Message << "Don't re-use texture, and only delete if memory limit is hit: "				<< probabilityStr(m_probabilities.wastefulTextureMemoryUsage)	<< TestLog::EndMessage
1209			<< TestLog::Message << "Don't re-use buffer, and only delete if memory limit is hit: "				<< probabilityStr(m_probabilities.wastefulBufferMemoryUsage)	<< TestLog::EndMessage
1210			<< TestLog::Message << "Use client memory (instead of GL buffers) for vertex attribute data: "		<< probabilityStr(m_probabilities.clientMemoryAttributeData)	<< TestLog::EndMessage
1211			<< TestLog::Message << "Use client memory (instead of GL buffers) for vertex index data: "			<< probabilityStr(m_probabilities.clientMemoryIndexData)		<< TestLog::EndMessage
1212			<< TestLog::Message << "Use random target parameter when uploading buffer data: "					<< probabilityStr(m_probabilities.randomBufferUploadTarget)		<< TestLog::EndMessage
1213			<< TestLog::Message << "Use random usage parameter when uploading buffer data: "					<< probabilityStr(m_probabilities.randomBufferUsage)			<< TestLog::EndMessage
1214			<< TestLog::Message << "Use glDrawArrays instead of glDrawElements: "								<< probabilityStr(m_probabilities.useDrawArrays)				<< TestLog::EndMessage
1215			<< TestLog::Message << "Use separate buffers for each attribute, instead of one array for all: "	<< probabilityStr(m_probabilities.separateAttributeBuffers)		<< TestLog::EndMessage
1216			<< TestLog::EndSection
1217			<< TestLog::Message << "Using " << m_programContexts.size() << " program(s)" << TestLog::EndMessage;
1218
1219		bool anyProgramsFailed = false;
1220		for (int progCtxNdx = 0; progCtxNdx < (int)m_programContexts.size(); progCtxNdx++)
1221		{
1222			const ProgramContext& progCtx = m_programContexts[progCtxNdx];
1223			glu::ShaderProgram prog(m_renderCtx, glu::makeVtxFragSources(mangleShaderNames(progCtx.vertexSource, ""), mangleShaderNames(progCtx.fragmentSource, "")));
1224			log << TestLog::Section("ShaderProgram" + toString(progCtxNdx), "Shader program " + toString(progCtxNdx)) << prog << TestLog::EndSection;
1225			if (!prog.isOk())
1226				anyProgramsFailed = true;
1227		}
1228
1229		if (anyProgramsFailed)
1230			throw tcu::TestError("One or more shader programs failed to compile");
1231	}
1232
1233	DE_ASSERT(!m_debugInfoRenderer);
1234	if (m_showDebugInfo)
1235		m_debugInfoRenderer = new DebugInfoRenderer(m_renderCtx);
1236}
1237
1238void LongStressCase::deinit (void)
1239{
1240	m_programResources.clear();
1241
1242	delete m_programs;
1243	m_programs = DE_NULL;
1244
1245	delete m_buffers;
1246	m_buffers = DE_NULL;
1247
1248	delete m_textures;
1249	m_textures = DE_NULL;
1250
1251	delete m_debugInfoRenderer;
1252	m_debugInfoRenderer = DE_NULL;
1253}
1254
1255LongStressCase::IterateResult LongStressCase::iterate (void)
1256{
1257	TestLog&					log							= m_testCtx.getLog();
1258	const int					renderWidth					= m_renderCtx.getRenderTarget().getWidth();
1259	const int					renderHeight				= m_renderCtx.getRenderTarget().getHeight();
1260	const bool					useClientMemoryIndexData	= m_rnd.getFloat() < m_probabilities.clientMemoryIndexData;
1261	const bool					useDrawArrays				= m_rnd.getFloat() < m_probabilities.useDrawArrays;
1262	const bool					separateAttributeBuffers	= m_rnd.getFloat() < m_probabilities.separateAttributeBuffers;
1263	const int					progContextNdx				= m_rnd.getInt(0, (int)m_programContexts.size()-1);
1264	const ProgramContext&		programContext				= m_programContexts[progContextNdx];
1265	ProgramResources&			programResources			= m_programResources[progContextNdx];
1266	const string				programName					= "prog" + toString(progContextNdx);
1267	const string				textureNamePrefix			= "tex" + toString(progContextNdx) + "_";
1268	const string				unitedAttrBufferNamePrefix	= "attrBuf" + toString(progContextNdx) + "_";
1269	const string				indexBufferName				= "indexBuf" + toString(progContextNdx);
1270	const string				separateAttrBufNamePrefix	= "attrBuf" + toString(progContextNdx) + "_";
1271
1272	if (m_currentIteration == 0)
1273		m_lastLogTime = m_startTimeSeconds = deGetTime();
1274
1275	// Make or re-compile programs.
1276	{
1277		const bool hadProgram = m_programs->has(programName);
1278
1279		if (!hadProgram)
1280			m_programs->make(programName);
1281
1282		Program& prog = m_programs->get(programName);
1283
1284		if (!hadProgram || m_rnd.getFloat() < m_probabilities.rebuildProgram)
1285		{
1286			programResources.shaderNameManglingSuffix = toString((deUint16)deUint64Hash((deUint64)m_currentIteration ^ deGetTime()));
1287
1288			prog.setSources(mangleShaderNames(programContext.vertexSource, programResources.shaderNameManglingSuffix),
1289							mangleShaderNames(programContext.fragmentSource, programResources.shaderNameManglingSuffix));
1290
1291			prog.build(log);
1292		}
1293
1294		prog.use();
1295	}
1296
1297	Program& program = m_programs->get(programName);
1298
1299	// Make or re-upload textures.
1300
1301	for (int texNdx = 0; texNdx < (int)programContext.textureSpecs.size(); texNdx++)
1302	{
1303		const string		texName		= textureNamePrefix + toString(texNdx);
1304		const bool			hadTexture	= m_textures->has(texName);
1305		const TextureSpec&	spec		= programContext.textureSpecs[texNdx];
1306
1307		if (!hadTexture)
1308			m_textures->make(texName, spec.textureType);
1309
1310		if (!hadTexture || m_rnd.getFloat() < m_probabilities.reuploadTexture)
1311		{
1312			Texture& texture = m_textures->get(texName);
1313
1314			m_textures->removeGarbageUntilUnder(m_maxTexMemoryUsageBytes - texture.getApproxMemUsageDiff(spec.width, spec.height, spec.internalFormat, spec.useMipmap), m_rnd);
1315
1316			if (!hadTexture || m_rnd.getFloat() < m_probabilities.reuploadWithTexImage)
1317				texture.setData(programResources.dummyTextures[texNdx]->getAccess(), spec.width, spec.height, spec.internalFormat, spec.useMipmap);
1318			else
1319				texture.setSubData(programResources.dummyTextures[texNdx]->getAccess(), 0, 0, spec.width, spec.height);
1320
1321			texture.toUnit(0);
1322			texture.setWrap(spec.sWrap, spec.tWrap);
1323			texture.setFilter(spec.minFilter, spec.magFilter);
1324		}
1325	}
1326
1327	// Bind textures to units, in random order (because when multiple texture specs have same unit, we want to pick one randomly).
1328
1329	{
1330		vector<int> texSpecIndices(programContext.textureSpecs.size());
1331		for (int i = 0; i < (int)texSpecIndices.size(); i++)
1332			texSpecIndices[i] = i;
1333		m_rnd.shuffle(texSpecIndices.begin(), texSpecIndices.end());
1334		for (int i = 0; i < (int)texSpecIndices.size(); i++)
1335			m_textures->get(textureNamePrefix + toString(texSpecIndices[i])).toUnit(programContext.textureSpecs[i].textureUnit);
1336	}
1337
1338	// Make or re-upload index buffer.
1339
1340	if (!useDrawArrays)
1341	{
1342		m_rnd.shuffle(m_vertexIndices.begin(), m_vertexIndices.end());
1343
1344		if (!useClientMemoryIndexData)
1345		{
1346			const bool hadIndexBuffer = m_buffers->has(indexBufferName);
1347
1348			if (!hadIndexBuffer)
1349				m_buffers->make(indexBufferName);
1350
1351			Buffer& indexBuf = m_buffers->get(indexBufferName);
1352
1353			if (!hadIndexBuffer || m_rnd.getFloat() < m_probabilities.reuploadBuffer)
1354			{
1355				m_buffers->removeGarbageUntilUnder(m_maxBufMemoryUsageBytes - indexBuf.getApproxMemUsageDiff(m_vertexIndices), m_rnd);
1356				const deUint32 target = m_rnd.getFloat() < m_probabilities.randomBufferUploadTarget ? randomBufferTarget(m_rnd, m_isGLES3) : GL_ELEMENT_ARRAY_BUFFER;
1357
1358				if (!hadIndexBuffer || m_rnd.getFloat() < m_probabilities.reuploadWithBufferData)
1359					indexBuf.setData(m_vertexIndices, target, m_rnd.getFloat() < m_probabilities.randomBufferUsage ? randomBufferUsage(m_rnd, m_isGLES3) : m_indexBufferUsage);
1360				else
1361					indexBuf.setSubData(m_vertexIndices, 0, m_numVerticesPerDrawCall, target);
1362			}
1363		}
1364	}
1365
1366	// Set vertex attributes. If not using client-memory data, make or re-upload attribute buffers.
1367
1368	generateAttribs(programResources.attrDataBuf, programResources.attrDataOffsets, programResources.attrDataSizes,
1369					programContext.attributes, programContext.positionAttrName, m_numVerticesPerDrawCall, m_rnd);
1370
1371	if (!(m_rnd.getFloat() < m_probabilities.clientMemoryAttributeData))
1372	{
1373		if (separateAttributeBuffers)
1374		{
1375			for (int attrNdx = 0; attrNdx < (int)programContext.attributes.size(); attrNdx++)
1376			{
1377				const int usedRedundantBufferNdx = m_rnd.getInt(0, m_redundantBufferFactor-1);
1378
1379				for (int redundantBufferNdx = 0; redundantBufferNdx < m_redundantBufferFactor; redundantBufferNdx++)
1380				{
1381					const string	curAttrBufName		= separateAttrBufNamePrefix + toString(attrNdx) + "_" + toString(redundantBufferNdx);
1382					const bool		hadCurAttrBuffer	= m_buffers->has(curAttrBufName);
1383
1384					if (!hadCurAttrBuffer)
1385						m_buffers->make(curAttrBufName);
1386
1387					Buffer& curAttrBuf = m_buffers->get(curAttrBufName);
1388
1389					if (!hadCurAttrBuffer || m_rnd.getFloat() < m_probabilities.reuploadBuffer)
1390					{
1391						m_buffers->removeGarbageUntilUnder(m_maxBufMemoryUsageBytes - curAttrBuf.getApproxMemUsageDiff(programResources.attrDataSizes[attrNdx]), m_rnd);
1392						const deUint32 target = m_rnd.getFloat() < m_probabilities.randomBufferUploadTarget ? randomBufferTarget(m_rnd, m_isGLES3) : GL_ARRAY_BUFFER;
1393
1394						if (!hadCurAttrBuffer || m_rnd.getFloat() < m_probabilities.reuploadWithBufferData)
1395							curAttrBuf.setData(&programResources.attrDataBuf[programResources.attrDataOffsets[attrNdx]], programResources.attrDataSizes[attrNdx], target,
1396											   m_rnd.getFloat() < m_probabilities.randomBufferUsage ? randomBufferUsage(m_rnd, m_isGLES3) : m_attrBufferUsage);
1397						else
1398							curAttrBuf.setSubData(&programResources.attrDataBuf[programResources.attrDataOffsets[attrNdx]], 0, programResources.attrDataSizes[attrNdx], target);
1399					}
1400
1401					if (redundantBufferNdx == usedRedundantBufferNdx)
1402						program.setAttribute(curAttrBuf, 0, programContext.attributes[attrNdx], programResources.shaderNameManglingSuffix);
1403				}
1404			}
1405		}
1406		else
1407		{
1408			const int usedRedundantBufferNdx = m_rnd.getInt(0, m_redundantBufferFactor-1);
1409
1410			for (int redundantBufferNdx = 0; redundantBufferNdx < m_redundantBufferFactor; redundantBufferNdx++)
1411			{
1412				const string	attrBufName		= unitedAttrBufferNamePrefix + toString(redundantBufferNdx);
1413				const bool		hadAttrBuffer	= m_buffers->has(attrBufName);
1414
1415				if (!hadAttrBuffer)
1416					m_buffers->make(attrBufName);
1417
1418				Buffer& attrBuf = m_buffers->get(attrBufName);
1419
1420				if (!hadAttrBuffer || m_rnd.getFloat() < m_probabilities.reuploadBuffer)
1421				{
1422					m_buffers->removeGarbageUntilUnder(m_maxBufMemoryUsageBytes - attrBuf.getApproxMemUsageDiff(programResources.attrDataBuf), m_rnd);
1423					const deUint32 target = m_rnd.getFloat() < m_probabilities.randomBufferUploadTarget ? randomBufferTarget(m_rnd, m_isGLES3) : GL_ARRAY_BUFFER;
1424
1425					if (!hadAttrBuffer || m_rnd.getFloat() < m_probabilities.reuploadWithBufferData)
1426						attrBuf.setData(programResources.attrDataBuf, target, m_rnd.getFloat() < m_probabilities.randomBufferUsage ? randomBufferUsage(m_rnd, m_isGLES3) : m_attrBufferUsage);
1427					else
1428						attrBuf.setSubData(programResources.attrDataBuf, 0, (int)programResources.attrDataBuf.size(), target);
1429				}
1430
1431				if (redundantBufferNdx == usedRedundantBufferNdx)
1432				{
1433					for (int i = 0; i < (int)programContext.attributes.size(); i++)
1434						program.setAttribute(attrBuf, programResources.attrDataOffsets[i], programContext.attributes[i], programResources.shaderNameManglingSuffix);
1435				}
1436			}
1437		}
1438	}
1439	else
1440	{
1441		for (int i = 0; i < (int)programContext.attributes.size(); i++)
1442			program.setAttributeClientMem(&programResources.attrDataBuf[programResources.attrDataOffsets[i]], programContext.attributes[i], programResources.shaderNameManglingSuffix);
1443	}
1444
1445	// Draw.
1446
1447	glViewport(0, 0, renderWidth, renderHeight);
1448
1449	glClearDepthf(1.0f);
1450	glClear(GL_DEPTH_BUFFER_BIT);
1451	glEnable(GL_DEPTH_TEST);
1452
1453	for (int i = 0; i < m_numDrawCallsPerIteration; i++)
1454	{
1455		program.use();
1456		program.setRandomUniforms(programContext.uniforms, programResources.shaderNameManglingSuffix, m_rnd);
1457
1458		if (useDrawArrays)
1459			glDrawArrays(GL_TRIANGLE_STRIP, 0, m_numVerticesPerDrawCall);
1460		else
1461		{
1462			if (useClientMemoryIndexData)
1463			{
1464				glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
1465				glDrawElements(GL_TRIANGLE_STRIP, m_numVerticesPerDrawCall, GL_UNSIGNED_SHORT, &m_vertexIndices[0]);
1466			}
1467			else
1468			{
1469				m_buffers->get(indexBufferName).bind(GL_ELEMENT_ARRAY_BUFFER);
1470				glDrawElements(GL_TRIANGLE_STRIP, m_numVerticesPerDrawCall, GL_UNSIGNED_SHORT, DE_NULL);
1471			}
1472		}
1473	}
1474
1475	for(int i = 0; i < (int)programContext.attributes.size(); i++)
1476		program.disableAttributeArray(programContext.attributes[i], programResources.shaderNameManglingSuffix);
1477
1478	if (m_showDebugInfo)
1479		m_debugInfoRenderer->drawInfo(deGetTime()-m_startTimeSeconds, m_textures->computeApproxMemUsage(), m_maxTexMemoryUsageBytes, m_buffers->computeApproxMemUsage(), m_maxBufMemoryUsageBytes, m_currentIteration);
1480
1481	if (m_currentIteration > 0)
1482	{
1483		// Log if a certain amount of time has passed since last log entry (or if this is the last iteration).
1484
1485		const deUint64	loggingIntervalSeconds	= 10;
1486		const deUint64	time					= deGetTime();
1487		const deUint64	timeDiff				= time - m_lastLogTime;
1488		const int		iterDiff				= m_currentIteration - m_lastLogIteration;
1489
1490		if (timeDiff >= loggingIntervalSeconds || m_currentIteration == m_numIterations-1)
1491		{
1492			log << TestLog::Section("LogEntry" + toString(m_currentLogEntryNdx), "Log entry " + toString(m_currentLogEntryNdx))
1493				<< TestLog::Message << "Time elapsed: " << getTimeStr(time - m_startTimeSeconds) << TestLog::EndMessage
1494				<< TestLog::Message << "Frame number: " << m_currentIteration << TestLog::EndMessage
1495				<< TestLog::Message << "Time since last log entry: " << timeDiff << "s" << TestLog::EndMessage
1496				<< TestLog::Message << "Frames since last log entry: " << iterDiff << TestLog::EndMessage
1497				<< TestLog::Message << "Average frame time since last log entry: " << de::floatToString((float)timeDiff / iterDiff, 2) << "s" << TestLog::EndMessage
1498				<< TestLog::Message << "Approximate texture memory usage: "
1499									<< de::floatToString((float)m_textures->computeApproxMemUsage() / Mi, 2) << " MiB / "
1500									<< de::floatToString((float)m_maxTexMemoryUsageBytes / Mi, 2) << " MiB"
1501									<< TestLog::EndMessage
1502				<< TestLog::Message << "Approximate buffer memory usage: "
1503										<< de::floatToString((float)m_buffers->computeApproxMemUsage() / Mi, 2) << " MiB / "
1504										<< de::floatToString((float)m_maxBufMemoryUsageBytes / Mi, 2) << " MiB"
1505										<< TestLog::EndMessage
1506				<< TestLog::EndSection;
1507
1508			m_lastLogTime		= time;
1509			m_lastLogIteration	= m_currentIteration;
1510			m_currentLogEntryNdx++;
1511		}
1512	}
1513
1514	// Possibly remove or set-as-garbage some objects, depending on given probabilities.
1515
1516	for (int texNdx = 0; texNdx < (int)programContext.textureSpecs.size(); texNdx++)
1517	{
1518		const string texName = textureNamePrefix + toString(texNdx);
1519		if (m_rnd.getFloat() < m_probabilities.deleteTexture)
1520			m_textures->remove(texName);
1521		else if (m_rnd.getFloat() < m_probabilities.wastefulTextureMemoryUsage)
1522			m_textures->markAsGarbage(texName);
1523
1524	}
1525
1526	if (m_buffers->has(indexBufferName))
1527	{
1528		if (m_rnd.getFloat() < m_probabilities.deleteBuffer)
1529			m_buffers->remove(indexBufferName);
1530		else if (m_rnd.getFloat() < m_probabilities.wastefulBufferMemoryUsage)
1531			m_buffers->markAsGarbage(indexBufferName);
1532
1533	}
1534
1535	if (separateAttributeBuffers)
1536	{
1537		for (int attrNdx = 0; attrNdx < (int)programContext.attributes.size(); attrNdx++)
1538		{
1539			const string curAttrBufNamePrefix = separateAttrBufNamePrefix + toString(attrNdx) + "_";
1540
1541			if (m_buffers->has(curAttrBufNamePrefix + "0"))
1542			{
1543				if (m_rnd.getFloat() < m_probabilities.deleteBuffer)
1544				{
1545					for (int i = 0; i < m_redundantBufferFactor; i++)
1546						m_buffers->remove(curAttrBufNamePrefix + toString(i));
1547				}
1548				else if (m_rnd.getFloat() < m_probabilities.wastefulBufferMemoryUsage)
1549				{
1550					for (int i = 0; i < m_redundantBufferFactor; i++)
1551						m_buffers->markAsGarbage(curAttrBufNamePrefix + toString(i));
1552				}
1553			}
1554		}
1555	}
1556	else
1557	{
1558		if (m_buffers->has(unitedAttrBufferNamePrefix + "0"))
1559		{
1560			if (m_rnd.getFloat() < m_probabilities.deleteBuffer)
1561			{
1562				for (int i = 0; i < m_redundantBufferFactor; i++)
1563					m_buffers->remove(unitedAttrBufferNamePrefix + toString(i));
1564			}
1565			else if (m_rnd.getFloat() < m_probabilities.wastefulBufferMemoryUsage)
1566			{
1567				for (int i = 0; i < m_redundantBufferFactor; i++)
1568					m_buffers->markAsGarbage(unitedAttrBufferNamePrefix + toString(i));
1569			}
1570		}
1571	}
1572
1573	GLU_CHECK_MSG("End of LongStressCase::iterate()");
1574
1575	m_currentIteration++;
1576	if (m_currentIteration == m_numIterations)
1577	{
1578		m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Passed");
1579		return STOP;
1580	}
1581	else
1582		return CONTINUE;
1583}
1584
1585} // gls
1586} // deqp
1587