1 /*-------------------------------------------------------------------------
2  * OpenGL Conformance Test Suite
3  * -----------------------------
4  *
5  * Copyright (c) 2017 The Khronos Group Inc.
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
22  */ /*-------------------------------------------------------------------*/
23 
24 /**
25  */ /*!
26  * \file  gl4cShaderViewportLayerArrayTests.cpp
27  * \brief Conformance tests for the ARB_shader_viewport_layer_array functionality.
28  */ /*-------------------------------------------------------------------*/
29 
30 #include "gl4cShaderViewportLayerArrayTests.hpp"
31 #include "gluContextInfo.hpp"
32 #include "gluDefs.hpp"
33 #include "gluDrawUtil.hpp"
34 #include "gluObjectWrapper.hpp"
35 #include "gluShaderProgram.hpp"
36 #include "glwEnums.hpp"
37 #include "glwFunctions.hpp"
38 #include "tcuRenderTarget.hpp"
39 
40 #include <sstream>
41 #include <string>
42 #include <vector>
43 
44 using namespace glw;
45 
46 namespace gl4cts
47 {
48 
ShaderPipeline(bool tessellationShader,bool geometryShader,int maxViewportsLayers,const std::string & varName)49 ShaderViewportLayerArrayUtils::ShaderPipeline::ShaderPipeline(bool tessellationShader, bool geometryShader,
50 															  int maxViewportsLayers, const std::string& varName)
51 	: m_program(NULL)
52 	, m_hasTessellationShader(tessellationShader)
53 	, m_hasGeometryShader(geometryShader)
54 	, m_viewportLayerOffset(m_hasGeometryShader ? OFFSET_GEOMETRY :
55 												  m_hasTessellationShader ? OFFSET_TESSELLATION : OFFSET_VERTEX)
56 	, m_varName(varName)
57 {
58 	m_vs = "#version 450 core\n"
59 		   "#extension GL_ARB_shader_viewport_layer_array: require\n"
60 		   "in highp vec2 inPosition;\n"
61 		   "in int in<var_name>;\n"
62 		   "in highp vec4 inColor;\n"
63 		   "out int vs<var_name>;\n"
64 		   "out highp vec3 vsPosition;\n"
65 		   "out highp vec4 vsColor;\n"
66 		   "void main()\n"
67 		   "{\n"
68 		   "	gl_Position = vec4(inPosition, 0.0, 1.0);\n"
69 		   "	gl_<var_name> = (in<var_name> + <viewport_layer_offset>) % <viewport_layer_max>;\n"
70 		   "	vs<var_name> = in<var_name>;\n"
71 		   "	vsPosition = vec3(inPosition, 0.0);\n"
72 		   "	vsColor = inColor;\n"
73 		   "}\n";
74 
75 	m_tcs = "#version 450 core\n"
76 			"layout(vertices = 3) out;\n"
77 			"in highp vec3 vsPosition[];\n"
78 			"in highp vec4 vsColor[];\n"
79 			"in int vs<var_name>[];\n"
80 			"out highp vec3 tcsPosition[];\n"
81 			"out highp vec4 tcsColor[];\n"
82 			"out int tcs<var_name>[];\n"
83 			"void main()\n"
84 			"{\n"
85 			"	tcsPosition[gl_InvocationID] = vsPosition[gl_InvocationID];\n"
86 			"	tcsColor[gl_InvocationID] = vsColor[gl_InvocationID];\n"
87 			"	tcs<var_name>[gl_InvocationID] = vs<var_name>[gl_InvocationID];\n"
88 			"	gl_TessLevelInner[0] = 3;\n"
89 			"	gl_TessLevelOuter[0] = 3;\n"
90 			"	gl_TessLevelOuter[1] = 3;\n"
91 			"	gl_TessLevelOuter[2] = 3;\n"
92 			"}\n";
93 
94 	m_tes = "#version 450 core\n"
95 			"#extension GL_ARB_shader_viewport_layer_array: require\n"
96 			"layout(triangles, equal_spacing, cw) in;\n"
97 			"in highp vec3 tcsPosition[];\n"
98 			"in highp vec4 tcsColor[];\n"
99 			"in int tcs<var_name>[];\n"
100 			"out highp vec4 tesColor;\n"
101 			"out highp int tes<var_name>;\n"
102 			"void main()\n"
103 			"{\n"
104 			"	vec3 p0 = gl_TessCoord.x * tcsPosition[0];\n"
105 			"	vec3 p1 = gl_TessCoord.y * tcsPosition[1];\n"
106 			"	vec3 p2 = gl_TessCoord.z * tcsPosition[2];\n"
107 			"	tesColor = tcsColor[0];\n"
108 			"	tes<var_name> = tcs<var_name>[0];\n"
109 			"	gl_<var_name> = (tcs<var_name>[0] + <viewport_layer_offset>) % <viewport_layer_max>;\n"
110 			"	gl_Position = vec4(normalize(p0 + p1 + p2), 1.0);\n"
111 			"}\n";
112 
113 	m_gs = "#version 450 core\n"
114 		   "#extension GL_ARB_shader_viewport_layer_array: require\n"
115 		   "layout(triangles) in;\n"
116 		   "layout(triangle_strip, max_vertices = 3) out;\n"
117 		   "in highp vec4 tesColor[];\n"
118 		   "in int tes<var_name>[];\n"
119 		   "out highp vec4 gsColor;\n"
120 		   "void main()\n"
121 		   "{\n"
122 		   "	for (int i = 0; i<3; i++)\n"
123 		   "	{\n"
124 		   "		gl_Position = gl_in[i].gl_Position;\n"
125 		   "		gl_<var_name> = (tes<var_name>[i] + <viewport_layer_offset>) % <viewport_layer_max>;\n"
126 		   "		gsColor = tesColor[i];\n"
127 		   "		EmitVertex();\n"
128 		   "	}\n"
129 		   "	EndPrimitive();\n"
130 		   "}\n";
131 
132 	m_fs = "#version 450 core\n"
133 		   "in highp vec4 <input_color>;\n"
134 		   "out vec4 finalOutColor;\n"
135 		   "void main()\n"
136 		   "{\n"
137 		   "	finalOutColor = <input_color>;\n"
138 		   "}\n";
139 
140 	this->adaptShaderToPipeline(m_vs, "<var_name>", varName);
141 	this->adaptShaderToPipeline(m_vs, "<viewport_layer_offset>", OFFSET_VERTEX);
142 	this->adaptShaderToPipeline(m_vs, "<viewport_layer_max>", maxViewportsLayers);
143 
144 	this->adaptShaderToPipeline(m_tes, "<var_name>", varName);
145 	this->adaptShaderToPipeline(m_tes, "<viewport_layer_offset>", OFFSET_TESSELLATION);
146 	this->adaptShaderToPipeline(m_tes, "<viewport_layer_max>", maxViewportsLayers);
147 
148 	this->adaptShaderToPipeline(m_tcs, "<var_name>", varName);
149 
150 	this->adaptShaderToPipeline(m_gs, "<var_name>", varName);
151 	this->adaptShaderToPipeline(m_gs, "<viewport_layer_offset>", OFFSET_GEOMETRY);
152 	this->adaptShaderToPipeline(m_gs, "<viewport_layer_max>", maxViewportsLayers);
153 
154 	this->adaptShaderToPipeline(m_fs, "<input_color>", "vsColor", "tesColor", "gsColor");
155 }
156 
adaptShaderToPipeline(std::string & shader,const std::string & varKey,const std::string & vsVersion,const std::string & tesVersion,const std::string & gsVersion)157 void ShaderViewportLayerArrayUtils::ShaderPipeline::adaptShaderToPipeline(std::string&		 shader,
158 																		  const std::string& varKey,
159 																		  const std::string& vsVersion,
160 																		  const std::string& tesVersion,
161 																		  const std::string& gsVersion)
162 {
163 	std::string varName = m_hasGeometryShader ? gsVersion : m_hasTessellationShader ? tesVersion : vsVersion;
164 
165 	size_t start = 0;
166 	while ((start = shader.find(varKey, start)) != std::string::npos)
167 	{
168 		shader.replace(start, varKey.length(), varName);
169 		start += varName.length();
170 	}
171 }
172 
adaptShaderToPipeline(std::string & shader,const std::string & varKey,const std::string & value)173 void ShaderViewportLayerArrayUtils::ShaderPipeline::adaptShaderToPipeline(std::string&		 shader,
174 																		  const std::string& varKey,
175 																		  const std::string& value)
176 {
177 	this->adaptShaderToPipeline(shader, varKey, value, value, value);
178 }
179 
adaptShaderToPipeline(std::string & shader,const std::string & varKey,int value)180 void ShaderViewportLayerArrayUtils::ShaderPipeline::adaptShaderToPipeline(std::string&		 shader,
181 																		  const std::string& varKey, int value)
182 {
183 	std::ostringstream valueStr;
184 	valueStr << value;
185 
186 	this->adaptShaderToPipeline(shader, varKey, valueStr.str(), valueStr.str(), valueStr.str());
187 }
188 
~ShaderPipeline()189 ShaderViewportLayerArrayUtils::ShaderPipeline::~ShaderPipeline()
190 {
191 	if (m_program)
192 	{
193 		delete m_program;
194 	}
195 }
196 
create(const glu::RenderContext & context)197 void ShaderViewportLayerArrayUtils::ShaderPipeline::create(const glu::RenderContext& context)
198 {
199 	glu::ProgramSources sources;
200 	sources.sources[glu::SHADERTYPE_VERTEX].push_back(m_vs);
201 	if (m_hasTessellationShader)
202 	{
203 		sources.sources[glu::SHADERTYPE_TESSELLATION_CONTROL].push_back(m_tcs);
204 		sources.sources[glu::SHADERTYPE_TESSELLATION_EVALUATION].push_back(m_tes);
205 	}
206 	if (m_hasGeometryShader)
207 	{
208 		sources.sources[glu::SHADERTYPE_GEOMETRY].push_back(m_gs);
209 	}
210 	sources.sources[glu::SHADERTYPE_FRAGMENT].push_back(m_fs);
211 
212 	m_program = new glu::ShaderProgram(context, sources);
213 	if (!m_program->isOk())
214 	{
215 		TCU_FAIL("Shader compilation failed");
216 	}
217 }
218 
use(const glu::RenderContext & context)219 void ShaderViewportLayerArrayUtils::ShaderPipeline::use(const glu::RenderContext& context)
220 {
221 	const glw::Functions& gl = context.getFunctions();
222 	gl.useProgram(m_program->getProgram());
223 	GLU_EXPECT_NO_ERROR(gl.getError(), "glUseProgram failed");
224 }
225 
renderQuad(const glu::RenderContext & context,ShaderPipeline & shaderPipeline,int viewportLayerIndex,tcu::Vec4 color)226 void ShaderViewportLayerArrayUtils::renderQuad(const glu::RenderContext& context, ShaderPipeline& shaderPipeline,
227 											   int viewportLayerIndex, tcu::Vec4 color)
228 {
229 	const glw::Functions& gl = context.getFunctions();
230 
231 	deUint16 const quadIndices[] = { 0, 1, 2, 2, 1, 3 };
232 
233 	float const position[] = { -1.0f, -1.0f, -1.0f, 1.0f, 1.0f, -1.0f, 1.0f, 1.0f };
234 
235 	int const viewportLayerIndices[] = { viewportLayerIndex, viewportLayerIndex, viewportLayerIndex,
236 										 viewportLayerIndex };
237 
238 	float const colors[] = { color.x(), color.y(), color.z(), color.w(), color.x(), color.y(), color.z(), color.w(),
239 							 color.x(), color.y(), color.z(), color.w(), color.x(), color.y(), color.z(), color.w() };
240 
241 	std::string varName = "in";
242 	varName += shaderPipeline.getVarName();
243 
244 	glu::VertexArrayBinding vertexArrays[] = { glu::va::Float("inPosition", 2, 4, 0, position),
245 											   glu::va::Int32(varName, 1, 4, 0, viewportLayerIndices),
246 											   glu::va::Float("inColor", 4, 4, 0, colors) };
247 
248 	shaderPipeline.use(context);
249 
250 	glu::PrimitiveList primitiveList = shaderPipeline.hasTessellationShader() ?
251 										   glu::pr::Patches(DE_LENGTH_OF_ARRAY(quadIndices), quadIndices) :
252 										   glu::pr::TriangleStrip(DE_LENGTH_OF_ARRAY(quadIndices), quadIndices);
253 
254 	glu::draw(context, shaderPipeline.getShaderProgram()->getProgram(), DE_LENGTH_OF_ARRAY(vertexArrays), vertexArrays,
255 			  primitiveList, (glu::DrawUtilCallback*)DE_NULL);
256 
257 	GLU_EXPECT_NO_ERROR(gl.getError(), "glu::draw error");
258 }
259 
validateColor(tcu::Vec4 renderedColor,tcu::Vec4 referenceColor)260 bool ShaderViewportLayerArrayUtils::validateColor(tcu::Vec4 renderedColor, tcu::Vec4 referenceColor)
261 {
262 	const float epsilon = 1.1f / 31.0f; // Accommodate framebuffers with 5-bit channels.
263 	return de::abs(renderedColor.x() - referenceColor.x()) < epsilon &&
264 		   de::abs(renderedColor.y() - referenceColor.y()) < epsilon &&
265 		   de::abs(renderedColor.z() - referenceColor.z()) < epsilon &&
266 		   de::abs(renderedColor.w() - referenceColor.w()) < epsilon;
267 }
268 
createMaxViewports()269 glw::GLint ShaderViewportIndexTestCase::createMaxViewports()
270 {
271 	const Functions&		gl			 = m_context.getRenderContext().getFunctions();
272 	const tcu::RenderTarget renderTarget = m_context.getRenderContext().getRenderTarget();
273 	const GLfloat			targetWidth  = (GLfloat)renderTarget.getWidth();
274 
275 	GLint maxViewports = 0;
276 	gl.getIntegerv(GL_MAX_VIEWPORTS, &maxViewports);
277 	GLU_EXPECT_NO_ERROR(gl.getError(), "GetIntegerv error");
278 
279 	const int				  viewportDataSize = 4; // x + y + w + h
280 	std::vector<glw::GLfloat> data(maxViewports * viewportDataSize);
281 
282 	GLfloat viewportWidth  = 16.0f;
283 	GLfloat viewportHeight = 16.0f;
284 
285 	int currentX = 0;
286 	int currentY = 0;
287 	for (GLint i = 0; i < maxViewports; ++i)
288 	{
289 		GLfloat x = (GLfloat)currentX * viewportWidth;
290 		if (x > (targetWidth - viewportWidth))
291 		{
292 			x		 = 0.0f;
293 			currentX = 0;
294 			currentY++;
295 		}
296 		GLfloat y = (GLfloat)currentY * viewportHeight;
297 
298 		data[i * viewportDataSize + 0] = x;
299 		data[i * viewportDataSize + 1] = y;
300 		data[i * viewportDataSize + 2] = viewportWidth;
301 		data[i * viewportDataSize + 3] = viewportHeight;
302 
303 		m_viewportData.push_back(tcu::Vec4(x, y, viewportWidth, viewportHeight));
304 
305 		currentX++;
306 	}
307 
308 	gl.viewportArrayv(0, maxViewports, data.data());
309 	GLU_EXPECT_NO_ERROR(gl.getError(), "ViewportArrayv");
310 
311 	return maxViewports;
312 }
313 
314 /** Constructor.
315 *
316 *  @param context Rendering context
317 */
ShaderViewportIndexTestCase(deqp::Context & context)318 ShaderViewportIndexTestCase::ShaderViewportIndexTestCase(deqp::Context& context)
319 	: TestCase(context, "ShaderViewportIndexTestCase",
320 			   "Implements gl_ViewportIndex tests described in CTS_ARB_shader_viewport_layer_array")
321 	, m_maxViewports(0)
322 	, m_currentViewport(0)
323 {
324 	m_isExtensionSupported = context.getContextInfo().isExtensionSupported("GL_ARB_shader_viewport_layer_array");
325 }
326 
init()327 void ShaderViewportIndexTestCase::init()
328 {
329 	if (!m_isExtensionSupported)
330 		return;
331 
332 	m_maxViewports = this->createMaxViewports();
333 
334 	m_shaderPipelines.push_back(
335 		ShaderViewportLayerArrayUtils::ShaderPipeline(false, false, m_maxViewports, "ViewportIndex"));
336 	m_shaderPipelines.push_back(
337 		ShaderViewportLayerArrayUtils::ShaderPipeline(true, false, m_maxViewports, "ViewportIndex"));
338 	m_shaderPipelines.push_back(
339 		ShaderViewportLayerArrayUtils::ShaderPipeline(true, true, m_maxViewports, "ViewportIndex"));
340 
341 	for (ShaderPipelineIter iter = m_shaderPipelines.begin(); iter != m_shaderPipelines.end(); ++iter)
342 	{
343 		iter->create(m_context.getRenderContext());
344 	}
345 }
346 
deinit()347 void ShaderViewportIndexTestCase::deinit()
348 {
349 	const Functions&		gl			 = m_context.getRenderContext().getFunctions();
350 	const tcu::RenderTarget renderTarget = m_context.getRenderContext().getRenderTarget();
351 
352 	gl.viewport(0, 0, renderTarget.getWidth(), renderTarget.getHeight());
353 	GLU_EXPECT_NO_ERROR(gl.getError(), "Viewport");
354 }
355 
356 /** Executes test iteration.
357  *
358  *  @return Returns STOP when test has finished executing, CONTINUE if more iterations are needed.
359  */
iterate()360 tcu::TestNode::IterateResult ShaderViewportIndexTestCase::iterate()
361 {
362 	if (!m_isExtensionSupported)
363 	{
364 		m_testCtx.setTestResult(QP_TEST_RESULT_NOT_SUPPORTED, "Not supported");
365 		return STOP;
366 	}
367 
368 	const glw::Functions&	 gl			= m_context.getRenderContext().getFunctions();
369 	const glu::RenderContext& renderContext = m_context.getRenderContext();
370 
371 	tcu::Vec4 renderColor((m_currentViewport + 1) / (float)m_maxViewports, 0.0f, 0.0f, 1.0f);
372 	tcu::Vec4 backgroundColor(0.0f, 0.0f, 0.0f, 1.0f);
373 
374 	for (ShaderPipelineIter pipelineIter = m_shaderPipelines.begin(); pipelineIter != m_shaderPipelines.end();
375 		 ++pipelineIter)
376 	{
377 		// rendering
378 
379 		gl.clearColor(0.0f, 0.0f, 0.0f, 1.0f);
380 		GLU_EXPECT_NO_ERROR(gl.getError(), "ClearColor");
381 		gl.clear(GL_COLOR_BUFFER_BIT);
382 		GLU_EXPECT_NO_ERROR(gl.getError(), "Clear");
383 		ShaderViewportLayerArrayUtils::renderQuad(renderContext, *pipelineIter, m_currentViewport, renderColor);
384 		gl.flush();
385 		GLU_EXPECT_NO_ERROR(gl.getError(), "Flush");
386 
387 		// verification
388 
389 		std::vector<std::pair<tcu::Vec2, tcu::Vec4> > expectedPixels;
390 
391 		for (size_t i = 0; i < m_viewportData.size(); ++i)
392 		{
393 			tcu::Vec4 viewportData = m_viewportData[i];
394 
395 			int currentViewportWithOffset =
396 				(m_currentViewport + pipelineIter->getViewportLayerOffset()) % m_maxViewports;
397 
398 			tcu::Vec2 center(viewportData.x() + viewportData.z() * 0.5f, viewportData.y() + viewportData.w() * 0.5f);
399 
400 			if (i == (unsigned int)currentViewportWithOffset)
401 			{
402 				expectedPixels.push_back(std::make_pair(center, renderColor));
403 			}
404 			else
405 			{
406 				expectedPixels.push_back(std::make_pair(center, backgroundColor));
407 			}
408 		}
409 
410 		for (size_t i = 0; i < expectedPixels.size(); ++i)
411 		{
412 			glw::GLfloat rgba[4] = { -1.f, -1.f, -1.f, -1.f };
413 			gl.readPixels((glw::GLint)expectedPixels[i].first.x(), (glw::GLint)expectedPixels[i].first.y(), 1, 1,
414 						  GL_RGBA, GL_FLOAT, rgba);
415 			GLU_EXPECT_NO_ERROR(gl.getError(), "ReadPixels");
416 			bool validationResult = ShaderViewportLayerArrayUtils::validateColor(
417 				tcu::Vec4(rgba[0], rgba[1], rgba[2], rgba[3]), expectedPixels[i].second);
418 			TCU_CHECK_MSG(validationResult, "Expected pixel color did not match rendered one.");
419 		}
420 	}
421 
422 	if (m_currentViewport < (m_maxViewports - 1))
423 	{
424 		m_currentViewport++;
425 		return CONTINUE;
426 	}
427 
428 	m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
429 	return STOP;
430 }
431 
ShaderLayerFramebufferTestCaseBase(deqp::Context & context,const char * name,const char * description,bool layered)432 ShaderLayerFramebufferTestCaseBase::ShaderLayerFramebufferTestCaseBase(deqp::Context& context, const char* name,
433 																	   const char* description, bool layered)
434 	: TestCase(context, name, description)
435 	, m_layersNum(layered ? 4 : 1)
436 	, m_fboSize(512)
437 	, m_texture(0)
438 	, m_mainFbo(0)
439 	, m_currentLayer(0)
440 {
441 	m_isExtensionSupported = context.getContextInfo().isExtensionSupported("GL_ARB_shader_viewport_layer_array");
442 }
443 
init()444 void ShaderLayerFramebufferTestCaseBase::init()
445 {
446 	if (!m_isExtensionSupported)
447 		return;
448 
449 	const glw::Functions&	 gl			= m_context.getRenderContext().getFunctions();
450 
451 	this->createFBO();
452 
453 	m_shaderPipelines.push_back(ShaderViewportLayerArrayUtils::ShaderPipeline(false, false, m_layersNum, "Layer"));
454 	m_shaderPipelines.push_back(ShaderViewportLayerArrayUtils::ShaderPipeline(true, false, m_layersNum, "Layer"));
455 	m_shaderPipelines.push_back(ShaderViewportLayerArrayUtils::ShaderPipeline(true, true, m_layersNum, "Layer"));
456 
457 	for (ShaderPipelineIter iter = m_shaderPipelines.begin(); iter != m_shaderPipelines.end(); ++iter)
458 	{
459 		iter->create(m_context.getRenderContext());
460 	}
461 
462 	gl.viewport(0, 0, m_fboSize, m_fboSize);
463 }
464 
deinit()465 void ShaderLayerFramebufferTestCaseBase::deinit()
466 {
467 	const Functions&		gl			 = m_context.getRenderContext().getFunctions();
468 	const tcu::RenderTarget renderTarget = m_context.getRenderContext().getRenderTarget();
469 
470 	gl.viewport(0, 0, renderTarget.getWidth(), renderTarget.getHeight());
471 	GLU_EXPECT_NO_ERROR(gl.getError(), "Viewport");
472 }
473 
474 
iterate()475 tcu::TestNode::IterateResult ShaderLayerFramebufferTestCaseBase::iterate()
476 {
477 	if (!m_isExtensionSupported)
478 	{
479 		m_testCtx.setTestResult(QP_TEST_RESULT_NOT_SUPPORTED, "Not supported");
480 		return STOP;
481 	}
482 
483 	const glw::Functions&	 gl			= m_context.getRenderContext().getFunctions();
484 	const glu::RenderContext& renderContext = m_context.getRenderContext();
485 
486 	tcu::Vec4 renderColor((m_currentLayer + 1) / (float)m_layersNum, 0.0f, 0.0f, 1.0f);
487 
488 	for (ShaderPipelineIter pipelineIter = m_shaderPipelines.begin(); pipelineIter != m_shaderPipelines.end();
489 		 ++pipelineIter)
490 	{
491 		// bind main framebuffer (layered)
492 		gl.bindFramebuffer(GL_DRAW_FRAMEBUFFER, m_mainFbo);
493 		GLU_EXPECT_NO_ERROR(gl.getError(), "BindFramebuffer");
494 
495 		// render
496 		gl.clearColor(0.0f, 0.0f, 0.0f, 1.0f);
497 		GLU_EXPECT_NO_ERROR(gl.getError(), "ClearColor");
498 		gl.clear(GL_COLOR_BUFFER_BIT);
499 		GLU_EXPECT_NO_ERROR(gl.getError(), "Clear");
500 		ShaderViewportLayerArrayUtils::renderQuad(renderContext, *pipelineIter, m_currentLayer, renderColor);
501 		gl.flush();
502 		GLU_EXPECT_NO_ERROR(gl.getError(), "Flush");
503 
504 		// calculate layer offset (same value as gl_Layer in shader)
505 		int currentLayerWithOffset = (m_currentLayer + pipelineIter->getViewportLayerOffset()) % m_layersNum;
506 
507 		// bind framebuffer of this layer
508 		gl.bindFramebuffer(GL_READ_FRAMEBUFFER, m_fbos[currentLayerWithOffset]);
509 		GLU_EXPECT_NO_ERROR(gl.getError(), "bindFramebuffer() call failed.");
510 
511 		// verification
512 		glw::GLfloat rgba[4] = { -1.f, -1.f, -1.f, -1.f };
513 		gl.readPixels(m_fboSize / 2, m_fboSize / 2, 1, 1, GL_RGBA, GL_FLOAT, rgba);
514 		GLU_EXPECT_NO_ERROR(gl.getError(), "ReadPixels");
515 		bool validationResult =
516 			ShaderViewportLayerArrayUtils::validateColor(tcu::Vec4(rgba[0], rgba[1], rgba[2], rgba[3]), renderColor);
517 
518 		TCU_CHECK_MSG(validationResult, "Expected pixel color did not match rendered one.");
519 	}
520 
521 	if (m_currentLayer < (m_layersNum - 1))
522 	{
523 		m_currentLayer++;
524 		return CONTINUE;
525 	}
526 
527 	m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
528 	return STOP;
529 }
530 
531 /** Constructor.
532 *
533 *  @param context Rendering context
534 */
ShaderLayerFramebufferLayeredTestCase(deqp::Context & context)535 ShaderLayerFramebufferLayeredTestCase::ShaderLayerFramebufferLayeredTestCase(deqp::Context& context)
536 	: ShaderLayerFramebufferTestCaseBase(
537 		  context, "ShaderLayerFramebufferLayeredTestCase",
538 		  "Implements gl_Layer tests for layered framebuffer described in CTS_ARB_shader_viewport_layer_array", true)
539 {
540 }
541 
createFBO()542 void ShaderLayerFramebufferLayeredTestCase::createFBO()
543 {
544 	const Functions& gl = m_context.getRenderContext().getFunctions();
545 
546 	gl.genTextures(1, &m_texture);
547 	GLU_EXPECT_NO_ERROR(gl.getError(), "GenTextures");
548 
549 	gl.bindTexture(GL_TEXTURE_3D, m_texture);
550 	GLU_EXPECT_NO_ERROR(gl.getError(), "bindTexture() call failed.");
551 
552 	gl.texImage3D(GL_TEXTURE_3D, 0, GL_RGBA8, m_fboSize, m_fboSize, m_layersNum, 0, GL_RGBA, GL_FLOAT, 0);
553 	GLU_EXPECT_NO_ERROR(gl.getError(), "texImage3D() call failed.");
554 
555 	// create main FBO
556 
557 	gl.genFramebuffers(1, &m_mainFbo);
558 	GLU_EXPECT_NO_ERROR(gl.getError(), "genFramebuffers() call failed.");
559 	gl.bindFramebuffer(GL_FRAMEBUFFER, m_mainFbo);
560 	GLU_EXPECT_NO_ERROR(gl.getError(), "bindFramebuffer() call failed.");
561 	gl.framebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, m_texture, 0);
562 	GLU_EXPECT_NO_ERROR(gl.getError(), "framebufferTexture() call failed.");
563 
564 	// create FBO for each layer
565 
566 	for (int i = 0; i < m_layersNum; ++i)
567 	{
568 		deUint32 layerFbo;
569 
570 		gl.genFramebuffers(1, &layerFbo);
571 		GLU_EXPECT_NO_ERROR(gl.getError(), "genFramebuffers() call failed.");
572 		gl.bindFramebuffer(GL_FRAMEBUFFER, layerFbo);
573 		GLU_EXPECT_NO_ERROR(gl.getError(), "bindFramebuffer() call failed.");
574 		gl.framebufferTextureLayer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, m_texture, 0, i);
575 		GLU_EXPECT_NO_ERROR(gl.getError(), "framebufferTextureLayer() call failed.");
576 
577 		m_fbos.push_back(layerFbo);
578 	}
579 }
580 
deleteFBO()581 void ShaderLayerFramebufferLayeredTestCase::deleteFBO()
582 {
583 	const Functions& gl = m_context.getRenderContext().getFunctions();
584 
585 	gl.deleteFramebuffers(1, &m_mainFbo);
586 	GLU_EXPECT_NO_ERROR(gl.getError(), "DeleteFramebuffers");
587 	for (int i = 0; i < m_layersNum; ++i)
588 	{
589 		gl.deleteFramebuffers(1, &(m_fbos[i]));
590 		GLU_EXPECT_NO_ERROR(gl.getError(), "DeleteFramebuffers");
591 	}
592 	gl.deleteTextures(1, &m_texture);
593 	GLU_EXPECT_NO_ERROR(gl.getError(), "DeleteTextures");
594 }
595 
596 /** Constructor.
597 *
598 *  @param context Rendering context
599 */
ShaderLayerFramebufferNonLayeredTestCase(deqp::Context & context)600 ShaderLayerFramebufferNonLayeredTestCase::ShaderLayerFramebufferNonLayeredTestCase(deqp::Context& context)
601 	: ShaderLayerFramebufferTestCaseBase(
602 		  context, "ShaderLayerFramebufferNonLayeredTestCase",
603 		  "Implements gl_Layer tests for non-layered framebuffer described in CTS_ARB_shader_viewport_layer_array",
604 		  false)
605 {
606 }
607 
createFBO()608 void ShaderLayerFramebufferNonLayeredTestCase::createFBO()
609 {
610 	const Functions& gl = m_context.getRenderContext().getFunctions();
611 	deUint32		 tex;
612 
613 	gl.genTextures(1, &tex);
614 	GLU_EXPECT_NO_ERROR(gl.getError(), "GenTextures");
615 
616 	gl.bindTexture(GL_TEXTURE_2D, tex);
617 	GLU_EXPECT_NO_ERROR(gl.getError(), "bindTexture() call failed.");
618 
619 	gl.texImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, m_fboSize, m_fboSize, 0, GL_RGBA, GL_FLOAT, 0);
620 	GLU_EXPECT_NO_ERROR(gl.getError(), "texImage2D() call failed.");
621 
622 	// create main FBO
623 
624 	gl.genFramebuffers(1, &m_mainFbo);
625 	GLU_EXPECT_NO_ERROR(gl.getError(), "genFramebuffers() call failed.");
626 	gl.bindFramebuffer(GL_FRAMEBUFFER, m_mainFbo);
627 	GLU_EXPECT_NO_ERROR(gl.getError(), "bindFramebuffer() call failed.");
628 	gl.framebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, tex, 0);
629 	GLU_EXPECT_NO_ERROR(gl.getError(), "framebufferTexture() call failed.");
630 
631 	// main FBO is only layer
632 
633 	m_fbos.push_back(m_mainFbo);
634 }
635 
deleteFBO()636 void ShaderLayerFramebufferNonLayeredTestCase::deleteFBO()
637 {
638 	const Functions& gl = m_context.getRenderContext().getFunctions();
639 
640 	gl.deleteFramebuffers(1, &m_mainFbo);
641 	GLU_EXPECT_NO_ERROR(gl.getError(), "DeleteFramebuffers");
642 	gl.deleteTextures(1, &m_texture);
643 	GLU_EXPECT_NO_ERROR(gl.getError(), "DeleteTextures");
644 }
645 
646 /** Constructor.
647  *
648  *  @param context Rendering context.
649  */
ShaderViewportLayerArray(deqp::Context & context)650 ShaderViewportLayerArray::ShaderViewportLayerArray(deqp::Context& context)
651 	: TestCaseGroup(context, "shader_viewport_layer_array",
652 					"Verify conformance of CTS_ARB_shader_viewport_layer_array implementation")
653 {
654 }
655 
656 /** Initializes the test group contents. */
init()657 void ShaderViewportLayerArray::init()
658 {
659 	addChild(new ShaderViewportIndexTestCase(m_context));
660 	addChild(new ShaderLayerFramebufferLayeredTestCase(m_context));
661 	addChild(new ShaderLayerFramebufferNonLayeredTestCase(m_context));
662 }
663 } /* gl4cts namespace */
664