• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*-------------------------------------------------------------------------
2  * OpenGL Conformance Test Suite
3  * -----------------------------
4  *
5  * Copyright (c) 2015-2016 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  * \file  esextcDrawBuffersIndexedBlending.hpp
26  * \brief Draw Buffers Indexed tests 5. Blending
27  */ /*-------------------------------------------------------------------*/
28 
29 #include "esextcDrawBuffersIndexedBlending.hpp"
30 #include "gluPixelTransfer.hpp"
31 #include "gluShaderProgram.hpp"
32 #include "tcuTestLog.hpp"
33 #include <cmath>
34 
35 namespace glcts
36 {
37 
38 /** Constructor
39  *
40  *  @param context     Test context
41  *  @param name        Test case's name
42  *  @param description Test case's description
43  **/
DrawBuffersIndexedBlending(Context & context,const ExtParameters & extParams,const char * name,const char * description)44 DrawBuffersIndexedBlending::DrawBuffersIndexedBlending(Context& context, const ExtParameters& extParams,
45 													   const char* name, const char* description)
46 	: DrawBuffersIndexedBase(context, extParams, name, description)
47 	, m_fbo(0)
48 {
49 	/* Left blank on purpose */
50 }
51 
prepareFramebuffer()52 void DrawBuffersIndexedBlending::prepareFramebuffer()
53 {
54 	const glw::Functions& gl = m_context.getRenderContext().getFunctions();
55 
56 	glw::GLint maxDrawBuffers = 0;
57 	gl.getIntegerv(GL_MAX_DRAW_BUFFERS, &maxDrawBuffers);
58 	if (maxDrawBuffers < 4)
59 	{
60 		throw tcu::ResourceError("Minimum number of draw buffers too low");
61 	}
62 
63 	gl.genFramebuffers(1, &m_fbo);
64 	gl.bindFramebuffer(GL_FRAMEBUFFER, m_fbo);
65 
66 	std::vector<glw::GLenum> bufs(maxDrawBuffers);
67 	for (int i = 0; i < maxDrawBuffers; ++i)
68 	{
69 		bufs[i] = GL_COLOR_ATTACHMENT0 + i;
70 	}
71 	gl.drawBuffers(maxDrawBuffers, &bufs[0]);
72 }
73 
releaseFramebuffer()74 void DrawBuffersIndexedBlending::releaseFramebuffer()
75 {
76 	const glw::Functions& gl = m_context.getRenderContext().getFunctions();
77 
78 	glw::GLint maxDrawBuffers = 0;
79 	gl.getIntegerv(GL_MAX_DRAW_BUFFERS, &maxDrawBuffers);
80 	if (maxDrawBuffers < 4)
81 	{
82 		throw tcu::ResourceError("Minimum number of draw buffers too low");
83 	}
84 
85 	BlendMaskStateMachine state(m_context, m_testCtx.getLog(), maxDrawBuffers);
86 	state.SetDefaults();
87 	gl.deleteFramebuffers(1, &m_fbo);
88 	gl.bindFramebuffer(GL_FRAMEBUFFER, 0);
89 	glw::GLenum bufs[1] = { GL_BACK };
90 	gl.drawBuffers(1, bufs);
91 	gl.readBuffer(GL_BACK);
92 }
93 
iterate()94 tcu::TestNode::IterateResult DrawBuffersIndexedBlending::iterate()
95 {
96 	static const glw::GLenum BlendFormats[] = {
97 		GL_R8, GL_RG8, GL_RGB8, GL_RGB565, GL_RGBA4, GL_RGBA8,
98 	};
99 	static const int	kSize	= 32;
100 	static unsigned int formatId = 0;
101 
102 	const glw::Functions& gl	 = m_context.getRenderContext().getFunctions();
103 	glw::GLenum			  format = BlendFormats[formatId];
104 
105 	prepareFramebuffer();
106 
107 	// Check number of available draw buffers
108 	glw::GLint maxDrawBuffers = 0;
109 	gl.getIntegerv(GL_MAX_DRAW_BUFFERS, &maxDrawBuffers);
110 	if (maxDrawBuffers < 4)
111 	{
112 		m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Minimum number of draw buffers too low");
113 		return STOP;
114 	}
115 
116 	// Prepare render targets
117 	glw::GLuint tex;
118 	gl.genTextures(1, &tex);
119 	gl.bindTexture(GL_TEXTURE_2D_ARRAY, tex);
120 	gl.texStorage3D(GL_TEXTURE_2D_ARRAY, 1, format, kSize, kSize, maxDrawBuffers);
121 	for (int i = 0; i < maxDrawBuffers; ++i)
122 	{
123 		gl.framebufferTextureLayer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + i, tex, 0, i);
124 	}
125 
126 	// Clear background color
127 	tcu::Vec4 background(0.5f, 0.5f, 0.5f, 0.5f);
128 	for (int i = 0; i < maxDrawBuffers; ++i)
129 	{
130 		gl.clearBufferfv(GL_COLOR, i, &background[0]);
131 	}
132 
133 	// Prepare expected, blended color values
134 	tcu::Vec4 colors[] = { tcu::Vec4(0.86f, 0.22f, 0.31f, 0.45f), tcu::Vec4(0.12f, 0.83f, 0.34f, 0.42f),
135 						   tcu::Vec4(0.56f, 0.63f, 0.76f, 0.99f), tcu::Vec4(0.14f, 0.34f, 0.34f, 0.22f) };
136 
137 	int		  numComponents = NumComponents(format);
138 	tcu::RGBA expected[]	= {
139 		// GL_MIN
140 		tcu::RGBA(static_cast<unsigned int>(background.x() * 255),
141 				  static_cast<unsigned int>((numComponents >= 2 ? colors[0].y() : 0.0f) * 255),
142 				  static_cast<unsigned int>((numComponents >= 3 ? colors[0].z() : 0.0f) * 255),
143 				  static_cast<unsigned int>((numComponents == 4 ? background.w() : 1.0f) * 255)),
144 		// GL_FUNC_ADD
145 		tcu::RGBA(static_cast<unsigned int>(background.x() * 255),
146 				  static_cast<unsigned int>((numComponents >= 2 ? background.y() : 0.0f) * 255),
147 				  static_cast<unsigned int>((numComponents >= 3 ? background.z() : 0.0f) * 255),
148 				  static_cast<unsigned int>((numComponents == 4 ? colors[1].w() : 1.0f) * 255)),
149 		// GL_FUNC_SUBTRACT
150 		tcu::RGBA(
151 			static_cast<unsigned int>((colors[2].x() * (numComponents == 4 ? colors[2].w() : 1.0f) -
152 									   background.x() * (numComponents == 4 ? background.w() : 1.0f)) *
153 									  255),
154 			static_cast<unsigned int>((numComponents >= 2 ?
155 										   (colors[2].y() * (numComponents == 4 ? colors[2].w() : 1.0f) -
156 											background.y() * (numComponents == 4 ? background.w() : 1.0f)) :
157 										   0.0f) *
158 									  255),
159 			static_cast<unsigned int>((numComponents >= 3 ?
160 										   (colors[2].z() * (numComponents == 4 ? colors[2].w() : 1.0f) -
161 											background.z() * (numComponents == 4 ? background.w() : 1.0f)) :
162 										   0.0f) *
163 									  255),
164 			static_cast<unsigned int>(
165 				(numComponents == 4 ? (colors[2].w() * colors[2].w() - background.w() * background.w()) : 1.0f) * 255)),
166 		// GL_FUNC_REVERSE_SUBTRACT
167 		tcu::RGBA(static_cast<unsigned int>((background.x() - colors[3].x()) * 255),
168 				  static_cast<unsigned int>((numComponents >= 2 ? (background.y() - colors[3].y()) : 0.0f) * 255),
169 				  static_cast<unsigned int>((numComponents >= 3 ? (background.z() - colors[3].z()) : 0.0f) * 255),
170 				  static_cast<unsigned int>((numComponents == 4 ? (background.w() - colors[3].w()) : 1.0f) * 255))
171 	};
172 
173 	// Setup blending operations
174 	BlendMaskStateMachine state(m_context, m_testCtx.getLog(), maxDrawBuffers);
175 	for (int i = 0; i < maxDrawBuffers; ++i)
176 	{
177 		switch (i % 4)
178 		{
179 		case 0:
180 			// GL_MIN
181 			state.SetEnablei(i);
182 			state.SetBlendEquationSeparatei(i, GL_MIN, GL_MAX);
183 			state.SetBlendFunci(i, GL_ONE, GL_ONE);
184 			break;
185 		case 1:
186 			// GL_FUNC_ADD
187 			state.SetEnablei(i);
188 			state.SetBlendEquationi(i, GL_FUNC_ADD);
189 			state.SetBlendFuncSeparatei(i, GL_ZERO, GL_ONE, GL_ONE, GL_ZERO);
190 			break;
191 		case 2:
192 			// GL_FUNC_SUBTRACT
193 			state.SetEnablei(i);
194 			state.SetBlendEquationi(i, GL_FUNC_SUBTRACT);
195 			state.SetBlendFunci(i, GL_SRC_ALPHA, GL_DST_ALPHA);
196 			break;
197 		case 3:
198 			// GL_FUNC_REVERSE_SUBTRACT
199 			state.SetEnablei(i);
200 			state.SetBlendEquationi(i, GL_FUNC_REVERSE_SUBTRACT);
201 			state.SetBlendFunci(i, GL_ONE, GL_ONE);
202 			break;
203 		}
204 	}
205 
206 	// Prepare shader programs and draw fullscreen quad
207 	glu::ShaderProgram program(m_context.getRenderContext(),
208 							   glu::makeVtxFragSources(GenVS().c_str(), GenFS(maxDrawBuffers).c_str()));
209 	if (!program.isOk())
210 	{
211 		m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Could not create shader program");
212 		return STOP;
213 	}
214 	gl.useProgram(program.getProgram());
215 
216 	glw::GLuint positionLocation = gl.getAttribLocation(program.getProgram(), "position");
217 	tcu::Vec3   vertices[]		 = {
218 		tcu::Vec3(-1.0f, -1.0f, 0.0f), tcu::Vec3(1.0f, -1.0f, 0.0f), tcu::Vec3(-1.0f, 1.0f, 0.0f),
219 		tcu::Vec3(1.0f, 1.0f, 0.0f),   tcu::Vec3(-1.0f, 1.0f, 0.0f), tcu::Vec3(1.0f, -1.0f, 0.0f)
220 	};
221 
222 	gl.vertexAttribPointer(positionLocation, 3, GL_FLOAT, GL_FALSE, 0, vertices);
223 	gl.enableVertexAttribArray(positionLocation);
224 
225 	for (int i = 0; i < maxDrawBuffers; ++i)
226 	{
227 		std::ostringstream os;
228 		os << "c" << i;
229 		// i.e.: glUniform4fv(glGetUniformLocation(m_program, "c0"), 1, &colors[i].r);
230 		gl.uniform4fv(gl.getUniformLocation(program.getProgram(), os.str().c_str()), 1, &colors[i % 4][0]);
231 	}
232 
233 	gl.drawArrays(GL_TRIANGLES, 0, 6);
234 
235 	// Read buffer colors and validate proper blending behaviour
236 	bool	  success = true;
237 	tcu::RGBA epsilon = GetEpsilon();
238 	for (int i = 0; i < maxDrawBuffers; ++i)
239 	{
240 		gl.readBuffer(GL_COLOR_ATTACHMENT0 + i);
241 
242 		tcu::TextureLevel textureLevel(tcu::TextureFormat(tcu::TextureFormat::RGBA, tcu::TextureFormat::UNORM_INT8),
243 									   kSize, kSize);
244 		glu::readPixels(m_context.getRenderContext(), 0, 0, textureLevel.getAccess());
245 
246 		if (!VerifyImg(textureLevel, expected[i % 4], epsilon))
247 		{
248 			m_testCtx.getLog() << tcu::TestLog::Message << "Blending error in texture format " << format
249 							   << " occurred for draw buffer #" << i << "\n"
250 							   << tcu::TestLog::EndMessage;
251 			m_testCtx.getLog() << tcu::TestLog::Image("Result", "Rendered result image", textureLevel.getAccess());
252 			success = false;
253 		}
254 	}
255 
256 	gl.disable(GL_BLEND);
257 	gl.useProgram(0);
258 	gl.bindTexture(GL_TEXTURE_2D_ARRAY, 0);
259 	gl.deleteTextures(1, &tex);
260 	releaseFramebuffer();
261 
262 	// Check for error
263 	glw::GLenum error_code = gl.getError();
264 	if (error_code != GL_NO_ERROR)
265 	{
266 		m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Some functions generated error");
267 		formatId = 0;
268 		return STOP;
269 	}
270 
271 	if (!success)
272 	{
273 		m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Blending error occurred");
274 		formatId = 0;
275 		return STOP;
276 	}
277 	else
278 	{
279 		++formatId;
280 		if (formatId < (sizeof(BlendFormats) / sizeof(BlendFormats[0])))
281 		{
282 			return CONTINUE;
283 		}
284 		else
285 		{
286 			m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
287 			formatId = 0;
288 			return STOP;
289 		}
290 	}
291 }
292 
GenVS()293 std::string DrawBuffersIndexedBlending::GenVS()
294 {
295 	std::ostringstream os;
296 	os << "#version 300 es                        \n"
297 		  "precision highp float;                 \n"
298 		  "precision highp int;                   \n"
299 		  "layout(location = 0) in vec4 position; \n"
300 		  "void main() {                          \n"
301 		  "    gl_Position = position;            \n"
302 		  "}";
303 	return os.str();
304 }
GenFS(int maxDrawBuffers)305 std::string DrawBuffersIndexedBlending::GenFS(int maxDrawBuffers)
306 {
307 	std::ostringstream os;
308 	os << "#version 300 es                        \n"
309 		  "precision highp float;                 \n"
310 		  "precision highp int;                   \n";
311 
312 	for (int i = 0; i < maxDrawBuffers; ++i)
313 	{
314 		os << "\nlayout(location = " << i << ") out vec4 color" << i << ";";
315 	}
316 	for (int i = 0; i < maxDrawBuffers; ++i)
317 	{
318 		os << "\nuniform vec4 c" << i << ";";
319 	}
320 
321 	os << "\nvoid main() {";
322 
323 	for (int i = 0; i < maxDrawBuffers; ++i)
324 	{
325 		os << "\n    color" << i << " = c" << i << ";";
326 	}
327 
328 	os << "\n}";
329 	return os.str();
330 }
331 
NumComponents(glw::GLenum format)332 unsigned int DrawBuffersIndexedBlending::NumComponents(glw::GLenum format)
333 {
334 	switch (format)
335 	{
336 	case GL_R8:
337 	case GL_R8I:
338 	case GL_R8UI:
339 	case GL_R16I:
340 	case GL_R16UI:
341 	case GL_R32I:
342 	case GL_R32UI:
343 		return 1;
344 	case GL_RG8:
345 	case GL_RG8I:
346 	case GL_RG8UI:
347 	case GL_RG16I:
348 	case GL_RG16UI:
349 	case GL_RG32I:
350 	case GL_RG32UI:
351 		return 2;
352 	case GL_RGB8:
353 	case GL_RGB565:
354 		return 3;
355 	case GL_RGBA4:
356 	case GL_RGB5_A1:
357 	case GL_RGBA8:
358 	case GL_RGB10_A2:
359 	case GL_RGBA8I:
360 	case GL_RGBA8UI:
361 	case GL_RGBA16I:
362 	case GL_RGBA16UI:
363 	case GL_RGBA32I:
364 	case GL_RGBA32UI:
365 		return 4;
366 	default:
367 		return 0;
368 	}
369 }
370 
GetEpsilon()371 tcu::RGBA DrawBuffersIndexedBlending::GetEpsilon()
372 {
373 	const glw::Functions& gl = m_context.getRenderContext().getFunctions();
374 
375 	tcu::IVec4 bits;
376 	tcu::UVec4 epsilon;
377 
378 	for (int i = 0; i < 4; ++i)
379 	{
380 		gl.getIntegerv(GL_RED_BITS + i, &bits[i]);
381 		epsilon[i] = de::min(
382 			255u, static_cast<unsigned int>(ceil(1.0 + 255.0 * (1.0 / pow(2.0, static_cast<double>(bits[i]))))));
383 	}
384 
385 	return tcu::RGBA(epsilon.x(), epsilon.y(), epsilon.z(), epsilon.w());
386 }
387 
VerifyImg(const tcu::TextureLevel & textureLevel,tcu::RGBA expectedColor,tcu::RGBA epsilon)388 bool DrawBuffersIndexedBlending::VerifyImg(const tcu::TextureLevel& textureLevel, tcu::RGBA expectedColor,
389 										   tcu::RGBA epsilon)
390 {
391 	for (int y = 0; y < textureLevel.getHeight(); ++y)
392 	{
393 		for (int x = 0; x < textureLevel.getWidth(); ++x)
394 		{
395 			tcu::RGBA pixel(textureLevel.getAccess().getPixel(x, y));
396 			if (!tcu::compareThreshold(pixel, expectedColor, epsilon))
397 			{
398 				m_testCtx.getLog() << tcu::TestLog::Message << "Expected value: " << expectedColor << "\n"
399 								   << "Read value:     " << pixel << "\n"
400 								   << "Epsilon:        " << epsilon << tcu::TestLog::EndMessage;
401 				return false;
402 			}
403 		}
404 	}
405 	return true;
406 }
407 
408 } // namespace glcts
409