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
39 namespace deqp
40 {
41 namespace gles2
42 {
43 namespace Functional
44 {
45
46 enum
47 {
48 VISUALIZE_DEPTH_STEPS = 32 //!< Number of depth steps in visualization
49 };
50
51 using tcu::Vec2;
52 using tcu::Vec3;
53 using tcu::Vec4;
54 using tcu::TestLog;
55 using std::string;
56 using std::vector;
57
58 static 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";
65 static 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
72 template <typename T>
compare(deUint32 func,T a,T b)73 static 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
triangleInterpolate(const float v0,const float v1,const float v2,const float x,const float y)91 inline 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
triQuadInterpolate(const float x,const float y,const tcu::Vec4 & quad)96 inline 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
depthRangeTransform(const float zd,const float zNear,const float zFar)105 inline 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
112 class DepthRangeCompareCase : public TestCase
113 {
114 public:
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
120 private:
121 const tcu::Vec4 m_depthCoord;
122 const float m_zNear;
123 const float m_zFar;
124 const deUint32 m_compareFunc;
125 };
126
DepthRangeCompareCase(Context & context,const char * name,const char * desc,const tcu::Vec4 & depthCoord,const float zNear,const float zFar,const deUint32 compareFunc)127 DepthRangeCompareCase::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
~DepthRangeCompareCase(void)136 DepthRangeCompareCase::~DepthRangeCompareCase (void)
137 {
138 }
139
iterate(void)140 DepthRangeCompareCase::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) / (float)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
263 class DepthRangeWriteCase : public TestCase
264 {
265 public:
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
271 private:
272 const tcu::Vec4& m_depthCoord;
273 const float m_zNear;
274 const float m_zFar;
275 };
276
DepthRangeWriteCase(Context & context,const char * name,const char * desc,const tcu::Vec4 & depthCoord,const float zNear,const float zFar)277 DepthRangeWriteCase::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
~DepthRangeWriteCase(void)285 DepthRangeWriteCase::~DepthRangeWriteCase (void)
286 {
287 }
288
iterate(void)289 DepthRangeWriteCase::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
DepthRangeTests(Context & context)396 DepthRangeTests::DepthRangeTests (Context& context)
397 : TestCaseGroup(context, "depth_range", "glDepthRangef() tests")
398 {
399 }
400
~DepthRangeTests(void)401 DepthRangeTests::~DepthRangeTests (void)
402 {
403 }
404
init(void)405 void 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