Home | History | Annotate | Download | only in stress
      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 Vertex attribute binding stress tests.
     22  *//*--------------------------------------------------------------------*/
     23 
     24 #include "es31sVertexAttributeBindingTests.hpp"
     25 #include "tcuVector.hpp"
     26 #include "tcuTestLog.hpp"
     27 #include "tcuRenderTarget.hpp"
     28 #include "tcuSurface.hpp"
     29 #include "gluCallLogWrapper.hpp"
     30 #include "gluObjectWrapper.hpp"
     31 #include "gluPixelTransfer.hpp"
     32 #include "gluRenderContext.hpp"
     33 #include "gluShaderProgram.hpp"
     34 #include "gluStrUtil.hpp"
     35 #include "glwFunctions.hpp"
     36 #include "glwEnums.hpp"
     37 #include "deStringUtil.hpp"
     38 
     39 namespace deqp
     40 {
     41 namespace gles31
     42 {
     43 namespace Stress
     44 {
     45 namespace
     46 {
     47 
     48 static const char* const s_vertexSource =				"#version 310 es\n"
     49 														"in highp vec4 a_position;\n"
     50 														"void main (void)\n"
     51 														"{\n"
     52 														"	gl_Position = a_position;\n"
     53 														"}\n";
     54 
     55 static const char* const s_fragmentSource =				"#version 310 es\n"
     56 														"layout(location = 0) out mediump vec4 fragColor;\n"
     57 														"void main (void)\n"
     58 														"{\n"
     59 														"	fragColor = vec4(1.0, 1.0, 1.0, 1.0);\n"
     60 														"}\n";
     61 
     62 static const char* const s_colorFragmentShader =		"#version 310 es\n"
     63 														"in mediump vec4 v_color;\n"
     64 														"layout(location = 0) out mediump vec4 fragColor;\n"
     65 														"void main (void)\n"
     66 														"{\n"
     67 														"	fragColor = v_color;\n"
     68 														"}\n";
     69 
     70 // Verifies image contains only yellow or greeen, or a linear combination
     71 // of these colors.
     72 static bool verifyImageYellowGreen (const tcu::Surface& image, tcu::TestLog& log, bool logImageOnSuccess)
     73 {
     74 	using tcu::TestLog;
     75 
     76 	const tcu::RGBA green		(0, 255, 0, 255);
     77 	const tcu::RGBA yellow		(255, 255, 0, 255);
     78 	const int colorThreshold	= 20;
     79 
     80 	tcu::Surface error			(image.getWidth(), image.getHeight());
     81 	bool isOk					= true;
     82 
     83 	log << TestLog::Message << "Verifying image contents." << TestLog::EndMessage;
     84 
     85 	for (int y = 0; y < image.getHeight(); y++)
     86 	for (int x = 0; x < image.getWidth(); x++)
     87 	{
     88 		const tcu::RGBA pixel = image.getPixel(x, y);
     89 		bool pixelOk = true;
     90 
     91 		// Any pixel with !(G ~= 255) is faulty (not a linear combinations of green and yellow)
     92 		if (de::abs(pixel.getGreen() - 255) > colorThreshold)
     93 			pixelOk = false;
     94 
     95 		// Any pixel with !(B ~= 0) is faulty (not a linear combinations of green and yellow)
     96 		if (de::abs(pixel.getBlue() - 0) > colorThreshold)
     97 			pixelOk = false;
     98 
     99 		error.setPixel(x, y, (pixelOk) ? (tcu::RGBA(0, 255, 0, 255)) : (tcu::RGBA(255, 0, 0, 255)));
    100 		isOk = isOk && pixelOk;
    101 	}
    102 
    103 	if (!isOk)
    104 	{
    105 		log << TestLog::Message << "Image verification failed." << TestLog::EndMessage;
    106 		log << TestLog::ImageSet("Verfication result", "Result of rendering")
    107 			<< TestLog::Image("Result",		"Result",		image)
    108 			<< TestLog::Image("ErrorMask",	"Error mask",	error)
    109 			<< TestLog::EndImageSet;
    110 	}
    111 	else
    112 	{
    113 		log << TestLog::Message << "Image verification passed." << TestLog::EndMessage;
    114 
    115 		if (logImageOnSuccess)
    116 			log << TestLog::ImageSet("Verfication result", "Result of rendering")
    117 				<< TestLog::Image("Result", "Result", image)
    118 				<< TestLog::EndImageSet;
    119 	}
    120 
    121 	return isOk;
    122 }
    123 
    124 class BindingRenderCase : public TestCase
    125 {
    126 public:
    127 	enum
    128 	{
    129 		TEST_RENDER_SIZE = 64
    130 	};
    131 
    132 						BindingRenderCase	(Context& ctx, const char* name, const char* desc, bool unalignedData);
    133 	virtual				~BindingRenderCase	(void);
    134 
    135 	virtual void		init				(void);
    136 	virtual void		deinit				(void);
    137 	IterateResult		iterate				(void);
    138 
    139 private:
    140 	virtual void		renderTo			(tcu::Surface& dst) = 0;
    141 	virtual void		createBuffers		(void) = 0;
    142 	virtual void		createShader		(void) = 0;
    143 
    144 protected:
    145 	const bool			m_unalignedData;
    146 	glw::GLuint			m_vao;
    147 	glu::ShaderProgram*	m_program;
    148 };
    149 
    150 BindingRenderCase::BindingRenderCase (Context& ctx, const char* name, const char* desc, bool unalignedData)
    151 	: TestCase			(ctx, name, desc)
    152 	, m_unalignedData	(unalignedData)
    153 	, m_vao				(0)
    154 	, m_program			(DE_NULL)
    155 {
    156 }
    157 
    158 BindingRenderCase::~BindingRenderCase (void)
    159 {
    160 	deinit();
    161 }
    162 
    163 void BindingRenderCase::init (void)
    164 {
    165 	// check requirements
    166 	if (m_context.getRenderTarget().getWidth() < TEST_RENDER_SIZE || m_context.getRenderTarget().getHeight() < TEST_RENDER_SIZE)
    167 		throw tcu::NotSupportedError("Test requires at least " + de::toString<int>(TEST_RENDER_SIZE) + "x" + de::toString<int>(TEST_RENDER_SIZE) + " render target");
    168 
    169 	// resources
    170 	m_context.getRenderContext().getFunctions().genVertexArrays(1, &m_vao);
    171 	if (m_context.getRenderContext().getFunctions().getError() != GL_NO_ERROR)
    172 		throw tcu::TestError("could not gen vao");
    173 
    174 	createBuffers();
    175 	createShader();
    176 }
    177 
    178 void BindingRenderCase::deinit (void)
    179 {
    180 	if (m_vao)
    181 	{
    182 		m_context.getRenderContext().getFunctions().deleteVertexArrays(1, &m_vao);
    183 		m_vao = 0;
    184 	}
    185 
    186 	delete m_program;
    187 	m_program = DE_NULL;
    188 }
    189 
    190 BindingRenderCase::IterateResult BindingRenderCase::iterate (void)
    191 {
    192 	tcu::Surface surface(TEST_RENDER_SIZE, TEST_RENDER_SIZE);
    193 
    194 	// draw pattern
    195 
    196 	renderTo(surface);
    197 
    198 	// verify results
    199 
    200 	if (verifyImageYellowGreen(surface, m_testCtx.getLog(), false))
    201 		m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
    202 	else if (m_unalignedData)
    203 		m_testCtx.setTestResult(QP_TEST_RESULT_COMPATIBILITY_WARNING, "Failed to draw with unaligned data");
    204 	else
    205 		m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Image verification failed");
    206 
    207 	return STOP;
    208 }
    209 
    210 class SingleBindingCase : public BindingRenderCase
    211 {
    212 public:
    213 
    214 	enum CaseFlag
    215 	{
    216 		FLAG_ATTRIB_UNALIGNED			= (1<<0),		// !< unalign attributes with relativeOffset
    217 		FLAG_ATTRIB_ALIGNED				= (1<<1),		// !< align attributes with relativeOffset to the buffer begin (and not buffer offset)
    218 		FLAG_ATTRIBS_MULTIPLE_ELEMS		= (1<<2),		// !< use multiple attribute elements
    219 		FLAG_ATTRIBS_SHARED_ELEMS		= (1<<3),		// !< use multiple shared attribute elements. xyzw & rgba stored as (x, y, zr, wg, b, a)
    220 
    221 		FLAG_BUF_ALIGNED_OFFSET			= (1<<4),		// !< use aligned offset to the buffer object
    222 		FLAG_BUF_UNALIGNED_OFFSET		= (1<<5),		// !< use unaligned offset to the buffer object
    223 		FLAG_BUF_UNALIGNED_STRIDE		= (1<<6),		// !< unalign buffer elements
    224 	};
    225 						SingleBindingCase	(Context& ctx, const char* name, int flags);
    226 						~SingleBindingCase	(void);
    227 
    228 	void				init				(void);
    229 	void				deinit				(void);
    230 
    231 private:
    232 	struct TestSpec
    233 	{
    234 		int		bufferOffset;
    235 		int		bufferStride;
    236 		int		positionAttrOffset;
    237 		int		colorAttrOffset;
    238 		bool	hasColorAttr;
    239 	};
    240 
    241 	enum
    242 	{
    243 		GRID_SIZE = 20
    244 	};
    245 
    246 	void				renderTo			(tcu::Surface& dst);
    247 
    248 	static TestSpec		genTestSpec			(int flags);
    249 	static std::string	genTestDescription	(int flags);
    250 	static bool			isDataUnaligned		(int flags);
    251 
    252 	void				createBuffers		(void);
    253 	void				createShader		(void);
    254 	std::string			genVertexSource		(void);
    255 
    256 	const TestSpec		m_spec;
    257 	glw::GLuint			m_buf;
    258 };
    259 
    260 SingleBindingCase::SingleBindingCase (Context& ctx, const char* name, int flags)
    261 	: BindingRenderCase	(ctx, name, genTestDescription(flags).c_str(), isDataUnaligned(flags))
    262 	, m_spec			(genTestSpec(flags))
    263 	, m_buf				(0)
    264 {
    265 	DE_ASSERT(!((flags & FLAG_ATTRIB_UNALIGNED) && (flags & FLAG_ATTRIB_ALIGNED)));
    266 	DE_ASSERT(!((flags & FLAG_ATTRIB_ALIGNED) && (flags & FLAG_BUF_UNALIGNED_STRIDE)));
    267 
    268 	DE_ASSERT(isDataUnaligned(flags));
    269 }
    270 
    271 SingleBindingCase::~SingleBindingCase (void)
    272 {
    273 	deinit();
    274 }
    275 
    276 void SingleBindingCase::init (void)
    277 {
    278 	// log what we are trying to do
    279 
    280 	m_testCtx.getLog()	<< tcu::TestLog::Message
    281 						<< "Rendering " << (int)GRID_SIZE << "x" << (int)GRID_SIZE << " grid.\n"
    282 						<< "Buffer format:\n"
    283 						<< "	bufferOffset: " << m_spec.bufferOffset << "\n"
    284 						<< "	bufferStride: " << m_spec.bufferStride << "\n"
    285 						<< "Vertex position format:\n"
    286 						<< "	type: float4\n"
    287 						<< "	offset: " << m_spec.positionAttrOffset << "\n"
    288 						<< "	total offset: " << m_spec.bufferOffset + m_spec.positionAttrOffset << "\n"
    289 						<< tcu::TestLog::EndMessage;
    290 
    291 	if (m_spec.hasColorAttr)
    292 		m_testCtx.getLog()	<< tcu::TestLog::Message
    293 							<< "Color:\n"
    294 							<< "	type: float4\n"
    295 							<< "	offset: " << m_spec.colorAttrOffset << "\n"
    296 							<< "	total offset: " << m_spec.bufferOffset + m_spec.colorAttrOffset << "\n"
    297 							<< tcu::TestLog::EndMessage;
    298 	// init
    299 
    300 	BindingRenderCase::init();
    301 }
    302 
    303 void SingleBindingCase::deinit (void)
    304 {
    305 	if (m_buf)
    306 	{
    307 		m_context.getRenderContext().getFunctions().deleteBuffers(1, &m_buf);
    308 		m_buf = 0;
    309 	}
    310 
    311 	BindingRenderCase::deinit();
    312 }
    313 
    314 void SingleBindingCase::renderTo (tcu::Surface& dst)
    315 {
    316 	glu::CallLogWrapper gl				(m_context.getRenderContext().getFunctions(), m_testCtx.getLog());
    317 	const int			positionLoc		= gl.glGetAttribLocation(m_program->getProgram(), "a_position");
    318 	const int			colorLoc		= gl.glGetAttribLocation(m_program->getProgram(), "a_color");
    319 	const int			colorUniformLoc	= gl.glGetUniformLocation(m_program->getProgram(), "u_color");
    320 
    321 	gl.enableLogging(true);
    322 
    323 	gl.glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
    324 	gl.glClear(GL_COLOR_BUFFER_BIT);
    325 	gl.glViewport(0, 0, dst.getWidth(), dst.getHeight());
    326 	gl.glBindVertexArray(m_vao);
    327 	GLU_EXPECT_NO_ERROR(gl.glGetError(), "set vao");
    328 
    329 	gl.glUseProgram(m_program->getProgram());
    330 	GLU_EXPECT_NO_ERROR(gl.glGetError(), "use program");
    331 
    332 	if (m_spec.hasColorAttr)
    333 	{
    334 		gl.glBindVertexBuffer(3, m_buf, m_spec.bufferOffset, m_spec.bufferStride);
    335 
    336 		gl.glVertexAttribBinding(positionLoc, 3);
    337 		gl.glVertexAttribFormat(positionLoc, 4, GL_FLOAT, GL_FALSE, m_spec.positionAttrOffset);
    338 		gl.glEnableVertexAttribArray(positionLoc);
    339 
    340 		gl.glVertexAttribBinding(colorLoc, 3);
    341 		gl.glVertexAttribFormat(colorLoc, 4, GL_FLOAT, GL_FALSE, m_spec.colorAttrOffset);
    342 		gl.glEnableVertexAttribArray(colorLoc);
    343 
    344 		GLU_EXPECT_NO_ERROR(gl.glGetError(), "set va");
    345 
    346 		gl.glDrawArrays(GL_TRIANGLES, 0, GRID_SIZE*GRID_SIZE*6);
    347 		GLU_EXPECT_NO_ERROR(gl.glGetError(), "draw");
    348 	}
    349 	else
    350 	{
    351 		gl.glBindVertexBuffer(3, m_buf, m_spec.bufferOffset, m_spec.bufferStride);
    352 		gl.glVertexAttribBinding(positionLoc, 3);
    353 		gl.glVertexAttribFormat(positionLoc, 4, GL_FLOAT, GL_FALSE, m_spec.positionAttrOffset);
    354 		gl.glEnableVertexAttribArray(positionLoc);
    355 
    356 		GLU_EXPECT_NO_ERROR(gl.glGetError(), "set va");
    357 		gl.glUniform4f(colorUniformLoc, 0.0f, 1.0f, 0.0f, 1.0f);
    358 
    359 		gl.glDrawArrays(GL_TRIANGLES, 0, GRID_SIZE*GRID_SIZE*6);
    360 		GLU_EXPECT_NO_ERROR(gl.glGetError(), "draw");
    361 	}
    362 
    363 	gl.glFinish();
    364 	gl.glBindVertexArray(0);
    365 	gl.glUseProgram(0);
    366 	GLU_EXPECT_NO_ERROR(gl.glGetError(), "clean");
    367 
    368 	glu::readPixels(m_context.getRenderContext(), 0, 0, dst.getAccess());
    369 }
    370 
    371 SingleBindingCase::TestSpec SingleBindingCase::genTestSpec (int flags)
    372 {
    373 	const int	datumSize				= 4;
    374 	const int	bufferOffset			= (flags & FLAG_BUF_ALIGNED_OFFSET) ? (32) : (flags & FLAG_BUF_UNALIGNED_OFFSET) ? (19) : (0);
    375 	const int	attrBufAlignment		= ((bufferOffset % datumSize) == 0) ? (0) : (datumSize - (bufferOffset % datumSize));
    376 	const int	positionAttrOffset		= (flags & FLAG_ATTRIB_UNALIGNED) ? (3) : (flags & FLAG_ATTRIB_ALIGNED) ? (attrBufAlignment) : (0);
    377 	const bool	hasColorAttr			= (flags & FLAG_ATTRIBS_SHARED_ELEMS) || (flags & FLAG_ATTRIBS_MULTIPLE_ELEMS);
    378 	const int	colorAttrOffset			= (flags & FLAG_ATTRIBS_SHARED_ELEMS) ? (2 * datumSize) : (flags & FLAG_ATTRIBS_MULTIPLE_ELEMS) ? (4 * datumSize) : (-1);
    379 
    380 	const int	bufferStrideBase		= de::max(positionAttrOffset + 4 * datumSize, colorAttrOffset + 4 * datumSize);
    381 	const int	bufferStrideAlignment	= ((bufferStrideBase % datumSize) == 0) ? (0) : (datumSize - (bufferStrideBase % datumSize));
    382 	const int	bufferStridePadding		= ((flags & FLAG_BUF_UNALIGNED_STRIDE) && deIsAligned32(bufferStrideBase, datumSize)) ? (13) : (!(flags & FLAG_BUF_UNALIGNED_STRIDE) && !deIsAligned32(bufferStrideBase, datumSize)) ? (bufferStrideAlignment) : (0);
    383 
    384 	TestSpec spec;
    385 
    386 	spec.bufferOffset			= bufferOffset;
    387 	spec.bufferStride			= bufferStrideBase + bufferStridePadding;
    388 	spec.positionAttrOffset		= positionAttrOffset;
    389 	spec.colorAttrOffset		= colorAttrOffset;
    390 	spec.hasColorAttr			= hasColorAttr;
    391 
    392 	if (flags & FLAG_ATTRIB_UNALIGNED)
    393 		DE_ASSERT(!deIsAligned32(spec.bufferOffset + spec.positionAttrOffset, datumSize));
    394 	else if (flags & FLAG_ATTRIB_ALIGNED)
    395 		DE_ASSERT(deIsAligned32(spec.bufferOffset + spec.positionAttrOffset, datumSize));
    396 
    397 	if (flags & FLAG_BUF_UNALIGNED_STRIDE)
    398 		DE_ASSERT(!deIsAligned32(spec.bufferStride, datumSize));
    399 	else
    400 		DE_ASSERT(deIsAligned32(spec.bufferStride, datumSize));
    401 
    402 	return spec;
    403 }
    404 
    405 std::string SingleBindingCase::genTestDescription (int flags)
    406 {
    407 	std::ostringstream buf;
    408 	buf << "draw test pattern";
    409 
    410 	if (flags & FLAG_ATTRIB_UNALIGNED)
    411 		buf << ", attribute offset (unaligned)";
    412 	if (flags & FLAG_ATTRIB_ALIGNED)
    413 		buf << ", attribute offset (aligned)";
    414 
    415 	if (flags & FLAG_ATTRIBS_MULTIPLE_ELEMS)
    416 		buf << ", 2 attributes";
    417 	if (flags & FLAG_ATTRIBS_SHARED_ELEMS)
    418 		buf << ", 2 attributes (some components shared)";
    419 
    420 	if (flags & FLAG_BUF_ALIGNED_OFFSET)
    421 		buf << ", buffer offset aligned";
    422 	if (flags & FLAG_BUF_UNALIGNED_OFFSET)
    423 		buf << ", buffer offset unaligned";
    424 	if (flags & FLAG_BUF_UNALIGNED_STRIDE)
    425 		buf << ", buffer stride unaligned";
    426 
    427 	return buf.str();
    428 }
    429 
    430 bool SingleBindingCase::isDataUnaligned (int flags)
    431 {
    432 	if (flags & FLAG_ATTRIB_UNALIGNED)
    433 		return true;
    434 	if (flags & FLAG_ATTRIB_ALIGNED)
    435 		return false;
    436 
    437 	return (flags & FLAG_BUF_UNALIGNED_OFFSET) || (flags & FLAG_BUF_UNALIGNED_STRIDE);
    438 }
    439 
    440 void SingleBindingCase::createBuffers (void)
    441 {
    442 	const glw::Functions&	gl		= m_context.getRenderContext().getFunctions();
    443 	std::vector<deUint8>	dataBuf	(m_spec.bufferOffset + m_spec.bufferStride * GRID_SIZE * GRID_SIZE * 6);
    444 
    445 	// In interleaved mode color rg and position zw are the same. Select "good" values for r and g
    446 	const tcu::Vec4			colorA	(0.0f, 1.0f, 0.0f, 1.0f);
    447 	const tcu::Vec4			colorB	(0.5f, 1.0f, 0.0f, 1.0f);
    448 
    449 	for (int y = 0; y < GRID_SIZE; ++y)
    450 	for (int x = 0; x < GRID_SIZE; ++x)
    451 	{
    452 		const tcu::Vec4&	color = ((x + y) % 2 == 0) ? (colorA) : (colorB);
    453 		const tcu::Vec4		positions[6] =
    454 		{
    455 			tcu::Vec4((x+0) / float(GRID_SIZE) * 2.0f - 1.0f, (y+0) / float(GRID_SIZE) * 2.0f - 1.0f, 0.0f, 1.0f),
    456 			tcu::Vec4((x+0) / float(GRID_SIZE) * 2.0f - 1.0f, (y+1) / float(GRID_SIZE) * 2.0f - 1.0f, 0.0f, 1.0f),
    457 			tcu::Vec4((x+1) / float(GRID_SIZE) * 2.0f - 1.0f, (y+1) / float(GRID_SIZE) * 2.0f - 1.0f, 0.0f, 1.0f),
    458 			tcu::Vec4((x+0) / float(GRID_SIZE) * 2.0f - 1.0f, (y+0) / float(GRID_SIZE) * 2.0f - 1.0f, 0.0f, 1.0f),
    459 			tcu::Vec4((x+1) / float(GRID_SIZE) * 2.0f - 1.0f, (y+1) / float(GRID_SIZE) * 2.0f - 1.0f, 0.0f, 1.0f),
    460 			tcu::Vec4((x+1) / float(GRID_SIZE) * 2.0f - 1.0f, (y+0) / float(GRID_SIZE) * 2.0f - 1.0f, 0.0f, 1.0f),
    461 		};
    462 
    463 		// copy cell vertices to the buffer.
    464 		for (int v = 0; v < 6; ++v)
    465 			memcpy(&dataBuf[m_spec.bufferOffset + m_spec.positionAttrOffset + m_spec.bufferStride * ((y * GRID_SIZE + x) * 6 + v)], positions[v].getPtr(), sizeof(positions[v]));
    466 
    467 		// copy color to buffer
    468 		if (m_spec.hasColorAttr)
    469 			for (int v = 0; v < 6; ++v)
    470 				memcpy(&dataBuf[m_spec.bufferOffset + m_spec.colorAttrOffset + m_spec.bufferStride * ((y * GRID_SIZE + x) * 6 + v)], color.getPtr(), sizeof(color));
    471 	}
    472 
    473 	gl.genBuffers(1, &m_buf);
    474 	gl.bindBuffer(GL_ARRAY_BUFFER, m_buf);
    475 	gl.bufferData(GL_ARRAY_BUFFER, (glw::GLsizeiptr)dataBuf.size(), &dataBuf[0], GL_STATIC_DRAW);
    476 	gl.bindBuffer(GL_ARRAY_BUFFER, 0);
    477 
    478 	if (gl.getError() != GL_NO_ERROR)
    479 		throw tcu::TestError("could not init buffer");
    480 }
    481 
    482 void SingleBindingCase::createShader (void)
    483 {
    484 	m_program = new glu::ShaderProgram(m_context.getRenderContext(), glu::ProgramSources() << glu::VertexSource(genVertexSource()) << glu::FragmentSource(s_colorFragmentShader));
    485 	m_testCtx.getLog() << *m_program;
    486 
    487 	if (!m_program->isOk())
    488 		throw tcu::TestError("could not build shader");
    489 }
    490 
    491 std::string SingleBindingCase::genVertexSource (void)
    492 {
    493 	const bool			useUniformColor = !m_spec.hasColorAttr;
    494 	std::ostringstream	buf;
    495 
    496 	buf <<	"#version 310 es\n"
    497 			"in highp vec4 a_position;\n";
    498 
    499 	if (!useUniformColor)
    500 		buf << "in highp vec4 a_color;\n";
    501 	else
    502 		buf << "uniform highp vec4 u_color;\n";
    503 
    504 	buf <<	"out highp vec4 v_color;\n"
    505 			"void main (void)\n"
    506 			"{\n"
    507 			"	gl_Position = a_position;\n"
    508 			"	v_color = " << ((useUniformColor) ? ("u_color") : ("a_color")) << ";\n"
    509 			"}\n";
    510 
    511 	return buf.str();
    512 }
    513 
    514 class BindVertexBufferCase : public TestCase
    515 {
    516 public:
    517 						BindVertexBufferCase	(Context& ctx, const char* name, const char* desc, int offset, int drawCount);
    518 						~BindVertexBufferCase	(void);
    519 
    520 	void				init					(void);
    521 	void				deinit					(void);
    522 	IterateResult		iterate					(void);
    523 
    524 private:
    525 	const int			m_offset;
    526 	const int			m_drawCount;
    527 	deUint32			m_buffer;
    528 	glu::ShaderProgram*	m_program;
    529 };
    530 
    531 BindVertexBufferCase::BindVertexBufferCase (Context& ctx, const char* name, const char* desc, int offset, int drawCount)
    532 	: TestCase		(ctx, name, desc)
    533 	, m_offset		(offset)
    534 	, m_drawCount	(drawCount)
    535 	, m_buffer		(0)
    536 	, m_program		(DE_NULL)
    537 {
    538 }
    539 
    540 BindVertexBufferCase::~BindVertexBufferCase (void)
    541 {
    542 	deinit();
    543 }
    544 
    545 void BindVertexBufferCase::init (void)
    546 {
    547 	const glw::Functions&	gl		= m_context.getRenderContext().getFunctions();
    548 	std::vector<tcu::Vec4>	data	(m_drawCount); // !< some junk data to make sure buffer is really allocated
    549 
    550 	gl.genBuffers(1, &m_buffer);
    551 	gl.bindBuffer(GL_ARRAY_BUFFER, m_buffer);
    552 	gl.bufferData(GL_ARRAY_BUFFER, int(m_drawCount * sizeof(tcu::Vec4)), &data[0], GL_STATIC_DRAW);
    553 	GLU_EXPECT_NO_ERROR(gl.getError(), "buffer gen");
    554 
    555 	m_program = new glu::ShaderProgram(m_context.getRenderContext(), glu::ProgramSources() << glu::VertexSource(s_vertexSource) << glu::FragmentSource(s_fragmentSource));
    556 	if (!m_program->isOk())
    557 	{
    558 		m_testCtx.getLog() << *m_program;
    559 		throw tcu::TestError("could not build program");
    560 	}
    561 }
    562 
    563 void BindVertexBufferCase::deinit (void)
    564 {
    565 	if (m_buffer)
    566 	{
    567 		m_context.getRenderContext().getFunctions().deleteBuffers(1, &m_buffer);
    568 		m_buffer = 0;
    569 	}
    570 
    571 	delete m_program;
    572 	m_program = DE_NULL;
    573 }
    574 
    575 BindVertexBufferCase::IterateResult BindVertexBufferCase::iterate (void)
    576 {
    577 	glu::CallLogWrapper		gl			(m_context.getRenderContext().getFunctions(), m_testCtx.getLog());
    578 	const deInt32			positionLoc = gl.glGetAttribLocation(m_program->getProgram(), "a_position");
    579 	tcu::Surface			dst			(m_context.getRenderTarget().getWidth(), m_context.getRenderTarget().getHeight());
    580 	glu::VertexArray		vao			(m_context.getRenderContext());
    581 
    582 	gl.enableLogging(true);
    583 
    584 	gl.glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
    585 	gl.glClear(GL_COLOR_BUFFER_BIT);
    586 	GLU_EXPECT_NO_ERROR(gl.glGetError(), "setup");
    587 
    588 	gl.glUseProgram(m_program->getProgram());
    589 	GLU_EXPECT_NO_ERROR(gl.glGetError(), "use program");
    590 
    591 	gl.glBindVertexArray(*vao);
    592 	gl.glEnableVertexAttribArray(positionLoc);
    593 	gl.glVertexAttribFormat(positionLoc, 4, GL_FLOAT, GL_FALSE, 0);
    594 	gl.glVertexAttribBinding(positionLoc, 0);
    595 	gl.glBindVertexBuffer(0, m_buffer, m_offset, int(sizeof(tcu::Vec4)));
    596 	GLU_EXPECT_NO_ERROR(gl.glGetError(), "set buffer");
    597 
    598 	gl.glDrawArrays(GL_POINTS, 0, m_drawCount);
    599 
    600 	// allow errors after attempted out-of-bounds memory access
    601 	{
    602 		const deUint32 error = gl.glGetError();
    603 
    604 		if (error != GL_NO_ERROR)
    605 			m_testCtx.getLog() << tcu::TestLog::Message << "Got error: " << glu::getErrorStr(error) << ", ignoring..." << tcu::TestLog::EndMessage;
    606 	}
    607 
    608 	// read pixels to wait for rendering
    609 	gl.glFinish();
    610 	glu::readPixels(m_context.getRenderContext(), 0, 0, dst.getAccess());
    611 
    612 	m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
    613 	return STOP;
    614 }
    615 
    616 } // anonymous
    617 
    618 VertexAttributeBindingTests::VertexAttributeBindingTests (Context& context)
    619 	: TestCaseGroup(context, "vertex_attribute_binding", "Test vertex attribute binding stress tests")
    620 {
    621 }
    622 
    623 VertexAttributeBindingTests::~VertexAttributeBindingTests (void)
    624 {
    625 }
    626 
    627 void VertexAttributeBindingTests::init (void)
    628 {
    629 	tcu::TestCaseGroup* const unalignedGroup	= new tcu::TestCaseGroup(m_testCtx, "unaligned",		"Unaligned access");
    630 	tcu::TestCaseGroup* const bufferRangeGroup	= new tcu::TestCaseGroup(m_testCtx, "buffer_bounds",	"Source data over buffer bounds");
    631 
    632 	addChild(unalignedGroup);
    633 	addChild(bufferRangeGroup);
    634 
    635 	// .unaligned
    636 	{
    637 		unalignedGroup->addChild(new SingleBindingCase(m_context, "elements_1_unaligned",																		  SingleBindingCase::FLAG_ATTRIB_UNALIGNED));
    638 		unalignedGroup->addChild(new SingleBindingCase(m_context, "offset_elements_1_unaligned",				SingleBindingCase::FLAG_BUF_ALIGNED_OFFSET		| SingleBindingCase::FLAG_ATTRIB_UNALIGNED));
    639 
    640 		unalignedGroup->addChild(new SingleBindingCase(m_context, "unaligned_offset_elements_1",				SingleBindingCase::FLAG_BUF_UNALIGNED_OFFSET	| 0));
    641 		unalignedGroup->addChild(new SingleBindingCase(m_context, "unaligned_offset_elements_1_unaligned",		SingleBindingCase::FLAG_BUF_UNALIGNED_OFFSET	| SingleBindingCase::FLAG_ATTRIB_UNALIGNED));
    642 		unalignedGroup->addChild(new SingleBindingCase(m_context, "unaligned_offset_elements_2",				SingleBindingCase::FLAG_BUF_UNALIGNED_OFFSET	| SingleBindingCase::FLAG_ATTRIBS_MULTIPLE_ELEMS));
    643 		unalignedGroup->addChild(new SingleBindingCase(m_context, "unaligned_offset_elements_2_share_elements",	SingleBindingCase::FLAG_BUF_UNALIGNED_OFFSET	| SingleBindingCase::FLAG_ATTRIBS_SHARED_ELEMS));
    644 
    645 		unalignedGroup->addChild(new SingleBindingCase(m_context, "unaligned_stride_elements_1",				SingleBindingCase::FLAG_BUF_UNALIGNED_STRIDE	| 0));
    646 		unalignedGroup->addChild(new SingleBindingCase(m_context, "unaligned_stride_elements_2",				SingleBindingCase::FLAG_BUF_UNALIGNED_STRIDE	| SingleBindingCase::FLAG_ATTRIBS_MULTIPLE_ELEMS));
    647 		unalignedGroup->addChild(new SingleBindingCase(m_context, "unaligned_stride_elements_2_share_elements",	SingleBindingCase::FLAG_BUF_UNALIGNED_STRIDE	| SingleBindingCase::FLAG_ATTRIBS_SHARED_ELEMS));
    648 	}
    649 
    650 	// .buffer_bounds
    651 	{
    652 		// bind buffer offset cases
    653 		bufferRangeGroup->addChild(new BindVertexBufferCase(m_context, "bind_vertex_buffer_offset_over_bounds_10",		"Offset over buffer bounds",				0x00210000, 10));
    654 		bufferRangeGroup->addChild(new BindVertexBufferCase(m_context, "bind_vertex_buffer_offset_over_bounds_1000",	"Offset over buffer bounds",				0x00210000, 1000));
    655 		bufferRangeGroup->addChild(new BindVertexBufferCase(m_context, "bind_vertex_buffer_offset_near_wrap_10",		"Offset over buffer bounds, near wrapping",	0x7FFFFFF0, 10));
    656 		bufferRangeGroup->addChild(new BindVertexBufferCase(m_context, "bind_vertex_buffer_offset_near_wrap_1000",		"Offset over buffer bounds, near wrapping",	0x7FFFFFF0, 1000));
    657 	}
    658 }
    659 
    660 } // Stress
    661 } // gles31
    662 } // deqp
    663