1/*-------------------------------------------------------------------------
2 * drawElements Quality Program OpenGL ES 2.0 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 glDepthRangef() tests.
22 *//*--------------------------------------------------------------------*/
23
24#include "es2fDepthRangeTests.hpp"
25#include "tcuVector.hpp"
26#include "tcuTestLog.hpp"
27#include "tcuSurface.hpp"
28#include "tcuImageCompare.hpp"
29#include "tcuRenderTarget.hpp"
30#include "gluPixelTransfer.hpp"
31#include "gluShaderProgram.hpp"
32#include "gluRenderContext.hpp"
33#include "deRandom.hpp"
34#include "deMath.h"
35#include "deString.h"
36
37#include "glw.h"
38
39namespace deqp
40{
41namespace gles2
42{
43namespace Functional
44{
45
46enum
47{
48	VISUALIZE_DEPTH_STEPS	= 32 //!< Number of depth steps in visualization
49};
50
51using tcu::Vec2;
52using tcu::Vec3;
53using tcu::Vec4;
54using tcu::TestLog;
55using std::string;
56using std::vector;
57
58static const char* s_vertexShaderSrc =
59	"attribute highp vec4 a_position;\n"
60	"attribute highp vec2 a_coord;\n"
61	"void main (void)\n"
62	"{\n"
63	"	gl_Position = a_position;\n"
64	"}\n";
65static const char* s_fragmentShaderSrc =
66	"uniform mediump vec4 u_color;\n"
67	"void main (void)\n"
68	"{\n"
69	"	gl_FragColor = u_color;\n"
70	"}\n";
71
72template <typename T>
73static inline bool compare (deUint32 func, T a, T b)
74{
75	switch (func)
76	{
77		case GL_NEVER:		return false;
78		case GL_ALWAYS:		return true;
79		case GL_LESS:		return a < b;
80		case GL_LEQUAL:		return a <= b;
81		case GL_EQUAL:		return a == b;
82		case GL_NOTEQUAL:	return a != b;
83		case GL_GEQUAL:		return a >= b;
84		case GL_GREATER:	return a > b;
85		default:
86			DE_ASSERT(DE_FALSE);
87			return false;
88	}
89}
90
91inline float triangleInterpolate (const float v0, const float v1, const float v2, const float x, const float y)
92{
93	return v0 + (v2-v0)*x + (v1-v0)*y;
94}
95
96inline float triQuadInterpolate (const float x, const float y, const tcu::Vec4& quad)
97{
98	// \note Top left fill rule.
99	if (x + y < 1.0f)
100		return triangleInterpolate(quad.x(), quad.y(), quad.z(), x, y);
101	else
102		return triangleInterpolate(quad.w(), quad.z(), quad.y(), 1.0f-x, 1.0f-y);
103}
104
105inline float depthRangeTransform (const float zd, const float zNear, const float zFar)
106{
107	const float	cNear	= de::clamp(zNear, 0.0f, 1.0f);
108	const float	cFar	= de::clamp(zFar, 0.0f, 1.0f);
109	return ((cFar - cNear)/2.0f) * zd + (cNear + cFar)/2.0f;
110}
111
112class DepthRangeCompareCase : public TestCase
113{
114public:
115							DepthRangeCompareCase	(Context& context, const char* name, const char* desc, const tcu::Vec4& depthCoord, const float zNear, const float zFar, const deUint32 compareFunc);
116							~DepthRangeCompareCase	(void);
117
118	IterateResult			iterate					(void);
119
120private:
121	const tcu::Vec4			m_depthCoord;
122	const float				m_zNear;
123	const float				m_zFar;
124	const deUint32			m_compareFunc;
125};
126
127DepthRangeCompareCase::DepthRangeCompareCase (Context& context, const char* name, const char* desc, const tcu::Vec4& depthCoord, const float zNear, const float zFar, const deUint32 compareFunc)
128	: TestCase			(context, name, desc)
129	, m_depthCoord		(depthCoord)
130	, m_zNear			(zNear)
131	, m_zFar			(zFar)
132	, m_compareFunc		(compareFunc)
133{
134}
135
136DepthRangeCompareCase::~DepthRangeCompareCase (void)
137{
138}
139
140DepthRangeCompareCase::IterateResult DepthRangeCompareCase::iterate (void)
141{
142	TestLog&					log					= m_testCtx.getLog();
143	de::Random					rnd					(deStringHash(getName()));
144	const tcu::RenderTarget&	renderTarget		= m_context.getRenderContext().getRenderTarget();
145	const int					viewportW			= de::min(128, renderTarget.getWidth());
146	const int					viewportH			= de::min(128, renderTarget.getHeight());
147	const int					viewportX			= rnd.getInt(0, renderTarget.getWidth()-viewportW);
148	const int					viewportY			= rnd.getInt(0, renderTarget.getHeight()-viewportH);
149	tcu::Surface				renderedFrame		(viewportW, viewportH);
150	tcu::Surface				referenceFrame		(viewportW, viewportH);
151	const float					constDepth			= 0.1f;
152
153	if (renderTarget.getDepthBits() == 0)
154		throw tcu::NotSupportedError("Depth buffer is required", "", __FILE__, __LINE__);
155
156	const glu::ShaderProgram	program				(m_context.getRenderContext(), glu::makeVtxFragSources(s_vertexShaderSrc, s_fragmentShaderSrc));
157
158	if (!program.isOk())
159	{
160		log << program;
161		TCU_FAIL("Compile failed");
162	}
163
164	const int					colorLoc			= glGetUniformLocation(program.getProgram(), "u_color");
165	const int					posLoc				= glGetAttribLocation(program.getProgram(), "a_position");
166
167	m_testCtx.getLog() << TestLog::Message << "glDepthRangef(" << m_zNear << ", " << m_zFar << ")" << TestLog::EndMessage;
168
169	glViewport(viewportX, viewportY, viewportW, viewportH);
170	glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT|GL_STENCIL_BUFFER_BIT);
171	glEnable(GL_DEPTH_TEST);
172	glUseProgram(program.getProgram());
173	glEnableVertexAttribArray(posLoc);
174
175	static const deUint16 quadIndices[] = { 0, 1, 2, 2, 1, 3 };
176
177	// Fill viewport with 2 quads - one with constant depth and another with d = [-1..1]
178	{
179		static const float constDepthCoord[] =
180		{
181			-1.0f, -1.0f, constDepth, 1.0f,
182			-1.0f, +1.0f, constDepth, 1.0f,
183			 0.0f, -1.0f, constDepth, 1.0f,
184			 0.0f, +1.0f, constDepth, 1.0f
185		};
186		static const float varyingDepthCoord[] =
187		{
188			 0.0f, -1.0f, +1.0f, 1.0f,
189			 0.0f, +1.0f,  0.0f, 1.0f,
190			+1.0f, -1.0f,  0.0f, 1.0f,
191			+1.0f, +1.0f, -1.0f, 1.0f
192		};
193
194		glUniform4f(colorLoc, 0.0f, 0.0f, 1.0f, 1.0f);
195		glDepthFunc(GL_ALWAYS);
196
197		glVertexAttribPointer(posLoc, 4, GL_FLOAT, GL_FALSE, 0, &constDepthCoord);
198		glDrawElements(GL_TRIANGLES, DE_LENGTH_OF_ARRAY(quadIndices), GL_UNSIGNED_SHORT, &quadIndices[0]);
199
200		glVertexAttribPointer(posLoc, 4, GL_FLOAT, GL_FALSE, 0, &varyingDepthCoord);
201		glDrawElements(GL_TRIANGLES, DE_LENGTH_OF_ARRAY(quadIndices), GL_UNSIGNED_SHORT, &quadIndices[0]);
202
203		GLU_CHECK();
204	}
205
206	// Render with depth test.
207	{
208		const float position[] =
209		{
210			-1.0f, -1.0f, m_depthCoord[0], 1.0f,
211			-1.0f, +1.0f, m_depthCoord[1], 1.0f,
212			+1.0f, -1.0f, m_depthCoord[2], 1.0f,
213			+1.0f, +1.0f, m_depthCoord[3], 1.0f
214		};
215
216		glDepthRangef(m_zNear, m_zFar);
217		glDepthFunc(m_compareFunc);
218		glUniform4f(colorLoc, 0.0f, 1.0f, 0.0f, 1.0f);
219
220		glVertexAttribPointer(posLoc, 4, GL_FLOAT, GL_FALSE, 0, &position[0]);
221		glDrawElements(GL_TRIANGLES, DE_LENGTH_OF_ARRAY(quadIndices), GL_UNSIGNED_SHORT, &quadIndices[0]);
222
223		GLU_CHECK();
224	}
225
226	glu::readPixels(m_context.getRenderContext(), viewportX, viewportY, renderedFrame.getAccess());
227
228	// Render reference.
229	for (int y = 0; y < referenceFrame.getHeight(); y++)
230	{
231		float	yf		= ((float)y + 0.5f) / referenceFrame.getHeight();
232		int		half	= de::clamp((int)((float)referenceFrame.getWidth()*0.5f + 0.5f), 0, referenceFrame.getWidth());
233
234		// Fill left half - comparison to constant 0.5
235		for (int x = 0; x < half; x++)
236		{
237			float	xf		= ((float)x + 0.5f) / (float)referenceFrame.getWidth();
238			float	d		= depthRangeTransform(triQuadInterpolate(xf, yf, m_depthCoord), m_zNear, m_zFar);
239			bool	dpass	= compare(m_compareFunc, d, constDepth*0.5f + 0.5f);
240
241			referenceFrame.setPixel(x, y, dpass ? tcu::RGBA::green : tcu::RGBA::blue);
242		}
243
244		// Fill right half - comparison to interpolated depth
245		for (int x = half; x < referenceFrame.getWidth(); x++)
246		{
247			float	xf		= ((float)x + 0.5f) / (float)referenceFrame.getWidth();
248			float	xh		= ((float)x - half + 0.5f) / (float)(referenceFrame.getWidth()-half);
249			float	rd		= 1.0f - (xh + yf) * 0.5f;
250			float	d		= depthRangeTransform(triQuadInterpolate(xf, yf, m_depthCoord), m_zNear, m_zFar);
251			bool	dpass	= compare(m_compareFunc, d, rd);
252
253			referenceFrame.setPixel(x, y, dpass ? tcu::RGBA::green : tcu::RGBA::blue);
254		}
255	}
256
257	bool isOk = tcu::fuzzyCompare(log, "Result", "Image comparison result", referenceFrame, renderedFrame, 0.05f, tcu::COMPARE_LOG_RESULT);
258	m_testCtx.setTestResult(isOk ? QP_TEST_RESULT_PASS	: QP_TEST_RESULT_FAIL,
259							isOk ? "Pass"				: "Fail");
260	return STOP;
261}
262
263class DepthRangeWriteCase : public TestCase
264{
265public:
266							DepthRangeWriteCase		(Context& context, const char* name, const char* desc, const tcu::Vec4& depthCoord, const float zNear, const float zFar);
267							~DepthRangeWriteCase	(void);
268
269	IterateResult			iterate					(void);
270
271private:
272	const tcu::Vec4&		m_depthCoord;
273	const float				m_zNear;
274	const float				m_zFar;
275};
276
277DepthRangeWriteCase::DepthRangeWriteCase (Context& context, const char* name, const char* desc, const tcu::Vec4& depthCoord, const float zNear, const float zFar)
278	: TestCase			(context, name, desc)
279	, m_depthCoord		(depthCoord)
280	, m_zNear			(zNear)
281	, m_zFar			(zFar)
282{
283}
284
285DepthRangeWriteCase::~DepthRangeWriteCase (void)
286{
287}
288
289DepthRangeWriteCase::IterateResult DepthRangeWriteCase::iterate (void)
290{
291	TestLog&					log				= m_testCtx.getLog();
292	de::Random					rnd				(deStringHash(getName()));
293	const tcu::RenderTarget&	renderTarget	= m_context.getRenderContext().getRenderTarget();
294	const int					viewportW		= de::min(128, renderTarget.getWidth());
295	const int					viewportH		= de::min(128, renderTarget.getHeight());
296	const int					viewportX		= rnd.getInt(0, renderTarget.getWidth()-viewportW);
297	const int					viewportY		= rnd.getInt(0, renderTarget.getHeight()-viewportH);
298	tcu::Surface				renderedFrame	(viewportW, viewportH);
299	tcu::Surface				referenceFrame	(viewportW, viewportH);
300	const int					numDepthSteps	= VISUALIZE_DEPTH_STEPS;
301	const float					depthStep		= 1.0f/(float)(numDepthSteps-1);
302
303	if (renderTarget.getDepthBits() == 0)
304		throw tcu::NotSupportedError("Depth buffer is required", "", __FILE__, __LINE__);
305
306	const glu::ShaderProgram	program			(m_context.getRenderContext(), glu::makeVtxFragSources(s_vertexShaderSrc, s_fragmentShaderSrc));
307
308	if (!program.isOk())
309	{
310		log << program;
311		TCU_FAIL("Compile failed");
312	}
313
314	const int					colorLoc		= glGetUniformLocation(program.getProgram(), "u_color");
315	const int					posLoc			= glGetAttribLocation(program.getProgram(), "a_position");
316
317	m_testCtx.getLog() << TestLog::Message << "glDepthRangef(" << m_zNear << ", " << m_zFar << ")" << TestLog::EndMessage;
318
319	glViewport(viewportX, viewportY, viewportW, viewportH);
320	glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT|GL_STENCIL_BUFFER_BIT);
321	glEnable(GL_DEPTH_TEST);
322	glUseProgram(program.getProgram());
323	glEnableVertexAttribArray(posLoc);
324
325	static const deUint16 quadIndices[] = { 0, 1, 2, 2, 1, 3 };
326
327	// Render with depth range.
328	{
329		const float position[] =
330		{
331			-1.0f, -1.0f, m_depthCoord[0], 1.0f,
332			-1.0f, +1.0f, m_depthCoord[1], 1.0f,
333			+1.0f, -1.0f, m_depthCoord[2], 1.0f,
334			+1.0f, +1.0f, m_depthCoord[3], 1.0f
335		};
336
337		glDepthFunc(GL_ALWAYS);
338		glDepthRangef(m_zNear, m_zFar);
339		glUniform4f(colorLoc, 0.0f, 1.0f, 0.0f, 1.0f);
340		glVertexAttribPointer(posLoc, 4, GL_FLOAT, GL_FALSE, 0, &position[0]);
341		glDrawElements(GL_TRIANGLES, DE_LENGTH_OF_ARRAY(quadIndices), GL_UNSIGNED_SHORT, &quadIndices[0]);
342		GLU_CHECK();
343	}
344
345	// Visualize by rendering full-screen quads with increasing depth and color.
346	{
347		glDepthFunc(GL_LEQUAL);
348		glDepthMask(GL_FALSE);
349		glDepthRangef(0.0f, 1.0f);
350
351		for (int stepNdx = 0; stepNdx < numDepthSteps; stepNdx++)
352		{
353			float	f		= (float)stepNdx*depthStep;
354			float	depth	= f*2.0f - 1.0f;
355			Vec4	color	= Vec4(f, f, f, 1.0f);
356
357			float position[] =
358			{
359				-1.0f, -1.0f, depth, 1.0f,
360				-1.0f, +1.0f, depth, 1.0f,
361				+1.0f, -1.0f, depth, 1.0f,
362				+1.0f, +1.0f, depth, 1.0f
363			};
364
365			glUniform4fv(colorLoc, 1, color.getPtr());
366			glVertexAttribPointer(posLoc, 4, GL_FLOAT, GL_FALSE, 0, &position[0]);
367			glDrawElements(GL_TRIANGLES, DE_LENGTH_OF_ARRAY(quadIndices), GL_UNSIGNED_SHORT, &quadIndices[0]);
368		}
369
370		GLU_CHECK();
371	}
372
373	glu::readPixels(m_context.getRenderContext(), viewportX, viewportY, renderedFrame.getAccess());
374
375	// Render reference.
376	for (int y = 0; y < referenceFrame.getHeight(); y++)
377	{
378		for (int x = 0; x < referenceFrame.getWidth(); x++)
379		{
380			float	xf		= ((float)x + 0.5f) / (float)referenceFrame.getWidth();
381			float	yf		= ((float)y + 0.5f) / (float)referenceFrame.getHeight();
382			float	d		= depthRangeTransform(triQuadInterpolate(xf, yf, m_depthCoord), m_zNear, m_zFar);
383			int		step	= (int)deFloatFloor(d / depthStep);
384			int		col		= de::clamp(deRoundFloatToInt32((float)step*depthStep*255.0f), 0, 255);
385
386			referenceFrame.setPixel(x, y, tcu::RGBA(col, col, col, 0xff));
387		}
388	}
389
390	bool isOk = tcu::fuzzyCompare(log, "Result", "Image comparison result", referenceFrame, renderedFrame, 0.05f, tcu::COMPARE_LOG_RESULT);
391	m_testCtx.setTestResult(isOk ? QP_TEST_RESULT_PASS	: QP_TEST_RESULT_FAIL,
392							isOk ? "Pass"				: "Fail");
393	return STOP;
394}
395
396DepthRangeTests::DepthRangeTests (Context& context)
397	: TestCaseGroup(context, "depth_range", "glDepthRangef() tests")
398{
399}
400
401DepthRangeTests::~DepthRangeTests (void)
402{
403}
404
405void DepthRangeTests::init (void)
406{
407	static const struct
408	{
409		const char*			name;
410		const char*			desc;
411		const tcu::Vec4		depthCoord;
412		const float			zNear;
413		const float			zFar;
414	} cases[] =
415	{
416		{ "default",		"Default depth range",		tcu::Vec4(-1.0f, 0.2f, -0.3f, 1.0f),	0.0f,		1.0f },
417		{ "reverse",		"Reversed default range",	tcu::Vec4(-1.0f, 0.2f, -0.3f, 1.0f),	1.0f,		0.0f },
418		{ "zero_to_half",	"From 0 to 0.5",			tcu::Vec4(-1.0f, 0.2f, -0.3f, 1.0f),	0.0f,		0.5f },
419		{ "half_to_one",	"From 0.5 to 1",			tcu::Vec4(-1.0f, 0.2f, -0.3f, 1.0f),	0.5f,		1.0f },
420		{ "half_to_zero",	"From 0.5 to 0",			tcu::Vec4(-1.0f, 0.2f, -0.3f, 1.0f),	0.5f,		0.0f },
421		{ "one_to_half",	"From 1 to 0.5",			tcu::Vec4(-1.0f, 0.2f, -0.3f, 1.0f),	1.0f,		0.5f },
422		{ "third_to_0_8",	"From 1/3 to 0.8",			tcu::Vec4(-1.0f, 0.2f, -0.3f, 1.0f),	1.0f/3.0f,	0.8f },
423		{ "0_8_to_third",	"From 0.8 to 1/3",			tcu::Vec4(-1.0f, 0.2f, -0.3f, 1.0f),	0.8f,		1.0f/3.0f },
424		{ "zero_to_zero",	"From 0 to 0",				tcu::Vec4(-1.0f, 0.2f, -0.3f, 1.0f),	0.0f,		0.0f },
425		{ "half_to_half",	"From 0.5 to 0.5",			tcu::Vec4(-1.0f, 0.2f, -0.3f, 1.0f),	0.5f,		0.5f },
426		{ "one_to_one",		"From 1 to 1",				tcu::Vec4(-1.0f, 0.2f, -0.3f, 1.0f),	1.0f,		1.0f },
427		{ "clamp_near",		"From -1 to 1",				tcu::Vec4(-1.0f, 0.2f, -0.3f, 1.0f),	-1.0f,		1.0f },
428		{ "clamp_far",		"From 0 to 2",				tcu::Vec4(-1.0f, 0.2f, -0.3f, 1.0f),	0.0f,		2.0 },
429		{ "clamp_both",		"From -1 to 2",				tcu::Vec4(-1.0f, 0.2f, -0.3f, 1.0f),	-1.0,		2.0 }
430	};
431
432	// .write
433	tcu::TestCaseGroup* writeGroup = new tcu::TestCaseGroup(m_testCtx, "write", "gl_FragDepth write tests");
434	addChild(writeGroup);
435	for (int ndx = 0; ndx < DE_LENGTH_OF_ARRAY(cases); ndx++)
436		writeGroup->addChild(new DepthRangeWriteCase(m_context, cases[ndx].name, cases[ndx].desc, cases[ndx].depthCoord, cases[ndx].zNear, cases[ndx].zFar));
437
438	// .compare
439	tcu::TestCaseGroup* compareGroup = new tcu::TestCaseGroup(m_testCtx, "compare", "gl_FragDepth used with depth comparison");
440	addChild(compareGroup);
441	for (int ndx = 0; ndx < DE_LENGTH_OF_ARRAY(cases); ndx++)
442		compareGroup->addChild(new DepthRangeCompareCase(m_context, cases[ndx].name, cases[ndx].desc, cases[ndx].depthCoord, cases[ndx].zNear, cases[ndx].zFar, GL_LESS));
443}
444
445} // Functional
446} // gles3
447} // deqp
448