Merge "Merge "Fix color change verification in dithering tests" into nougat-cts-dev...
[platform/upstream/VK-GL-CTS.git] / modules / gles3 / performance / es3pDepthTests.cpp
1 /*-------------------------------------------------------------------------
2  * drawElements Quality Program OpenGL ES 3.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 Depth buffer performance tests.
22  *//*--------------------------------------------------------------------*/
23
24 #include "es3pDepthTests.hpp"
25
26 #include "glsCalibration.hpp"
27
28 #include "gluShaderProgram.hpp"
29 #include "gluObjectWrapper.hpp"
30 #include "gluPixelTransfer.hpp"
31
32 #include "glwFunctions.hpp"
33 #include "glwEnums.hpp"
34
35 #include "tcuTestLog.hpp"
36 #include "tcuStringTemplate.hpp"
37 #include "tcuCPUWarmup.hpp"
38 #include "tcuCommandLine.hpp"
39 #include "tcuResultCollector.hpp"
40
41 #include "deClock.h"
42 #include "deString.h"
43 #include "deMath.h"
44 #include "deStringUtil.hpp"
45 #include "deRandom.hpp"
46 #include "deUniquePtr.hpp"
47
48 #include <vector>
49 #include <algorithm>
50
51 namespace deqp
52 {
53 namespace gles3
54 {
55 namespace Performance
56 {
57 namespace
58 {
59 using namespace glw;
60 using de::MovePtr;
61 using tcu::TestContext;
62 using tcu::TestLog;
63 using tcu::Vec4;
64 using tcu::Vec3;
65 using tcu::Vec2;
66 using glu::RenderContext;
67 using glu::ProgramSources;
68 using glu::ShaderSource;
69 using std::vector;
70 using std::string;
71 using std::map;
72
73 struct Sample
74 {
75         deInt64 nullTime;
76         deInt64 baseTime;
77         deInt64 testTime;
78         int             order;
79         int             workload;
80 };
81
82 struct SampleParams
83 {
84         int step;
85         int measurement;
86
87         SampleParams(int step_, int measurement_) : step(step_), measurement(measurement_) {}
88 };
89
90 typedef vector<float> Geometry;
91
92 struct ObjectData
93 {
94         ProgramSources  shader;
95         Geometry                geometry;
96
97         ObjectData (const ProgramSources& shader_, const Geometry& geometry_) : shader(shader_), geometry(geometry_) {}
98 };
99
100 class RenderData
101 {
102 public:
103                                                                 RenderData              (const ObjectData& object, const glu::RenderContext& renderCtx, TestLog& log);
104                                                                 ~RenderData             (void) {};
105
106         const glu::ShaderProgram        m_program;
107         const glu::VertexArray          m_vao;
108         const glu::Buffer                       m_vbo;
109
110         const int                                       m_numVertices;
111 };
112
113 RenderData::RenderData (const ObjectData& object, const  glu::RenderContext& renderCtx, TestLog& log)
114         : m_program             (renderCtx, object.shader)
115         , m_vao                 (renderCtx.getFunctions())
116         , m_vbo                 (renderCtx.getFunctions())
117         , m_numVertices (int(object.geometry.size())/4)
118 {
119         const glw::Functions& gl = renderCtx.getFunctions();
120
121         if (!m_program.isOk())
122                 log << m_program;
123
124         gl.bindBuffer(GL_ARRAY_BUFFER, *m_vbo);
125         gl.bufferData(GL_ARRAY_BUFFER, object.geometry.size() * sizeof(float), &object.geometry[0], GL_STATIC_DRAW);
126         gl.bindAttribLocation(m_program.getProgram(), 0, "a_position");
127
128         gl.bindVertexArray(*m_vao);
129         gl.vertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 0, DE_NULL);
130         gl.enableVertexAttribArray(0);
131         gl.bindVertexArray(0);
132 }
133
134 namespace Utils
135 {
136         vector<float> getFullscreenQuad (float depth)
137         {
138                 const float data[] =
139                 {
140                         +1.0f, +1.0f, depth, 0.0f, // .w is gl_VertexId%3 since Nexus 4&5 can't handle that on their own
141                         +1.0f, -1.0f, depth, 1.0f,
142                         -1.0f, -1.0f, depth, 2.0f,
143                         -1.0f, -1.0f, depth, 0.0f,
144                         -1.0f, +1.0f, depth, 1.0f,
145                         +1.0f, +1.0f, depth, 2.0f,
146                 };
147
148                 return vector<float>(DE_ARRAY_BEGIN(data), DE_ARRAY_END(data));
149         }
150
151         vector<float> getFullscreenQuadWithGradient (float depth0, float depth1)
152         {
153                 const float data[] =
154                 {
155                         +1.0f, +1.0f, depth0, 0.0f,
156                         +1.0f, -1.0f, depth0, 1.0f,
157                         -1.0f, -1.0f, depth1, 2.0f,
158                         -1.0f, -1.0f, depth1, 0.0f,
159                         -1.0f, +1.0f, depth1, 1.0f,
160                         +1.0f, +1.0f, depth0, 2.0f,
161                 };
162
163                 return vector<float>(DE_ARRAY_BEGIN(data), DE_ARRAY_END(data));
164         }
165
166         vector<float> getPartScreenQuad (float coverage, float depth)
167         {
168                 const float xMax        = -1.0f + 2.0f*coverage;
169                 const float data[]      =
170                 {
171                          xMax, +1.0f, depth, 0.0f,
172                          xMax, -1.0f, depth, 1.0f,
173                         -1.0f, -1.0f, depth, 2.0f,
174                         -1.0f, -1.0f, depth, 0.0f,
175                         -1.0f, +1.0f, depth, 1.0f,
176                          xMax, +1.0f, depth, 2.0f,
177                 };
178
179                 return vector<float>(DE_ARRAY_BEGIN(data), DE_ARRAY_END(data));
180         }
181
182         // Axis aligned grid. Depth of vertices is baseDepth +/- depthNoise
183         vector<float> getFullScreenGrid (int resolution, deUint32 seed, float baseDepth, float depthNoise, float xyNoise)
184         {
185                 const int               gridsize        = resolution+1;
186                 vector<Vec3>    vertices        (gridsize*gridsize);
187                 vector<float>   retval;
188                 de::Random              rng                     (seed);
189
190                 for (int y = 0; y < gridsize; y++)
191                 for (int x = 0; x < gridsize; x++)
192                 {
193                         const bool      isEdge  = x == 0 || y == 0 || x == resolution || y == resolution;
194                         const float x_          = float(x)/float(resolution)*2.0f - 1.0f + (isEdge ? 0.0f : rng.getFloat(-xyNoise, +xyNoise));
195                         const float y_          = float(y)/float(resolution)*2.0f - 1.0f + (isEdge ? 0.0f : rng.getFloat(-xyNoise, +xyNoise));
196                         const float z_          = baseDepth + rng.getFloat(-depthNoise, +depthNoise);
197
198                         vertices[y*gridsize + x] = Vec3(x_, y_, z_);
199                 }
200
201                 retval.reserve(resolution*resolution*6);
202
203                 for (int y = 0; y < resolution; y++)
204                 for (int x = 0; x < resolution; x++)
205                 {
206                         const Vec3& p0 = vertices[(y+0)*gridsize + (x+0)];
207                         const Vec3& p1 = vertices[(y+0)*gridsize + (x+1)];
208                         const Vec3& p2 = vertices[(y+1)*gridsize + (x+0)];
209                         const Vec3& p3 = vertices[(y+1)*gridsize + (x+1)];
210
211                         const float temp[6*4] =
212                         {
213                                 p0.x(), p0.y(), p0.z(), 0.0f,
214                                 p2.x(), p2.y(), p2.z(), 1.0f,
215                                 p1.x(), p1.y(), p1.z(), 2.0f,
216
217                                 p3.x(), p3.y(), p3.z(), 0.0f,
218                                 p1.x(), p1.y(), p1.z(), 1.0f,
219                                 p2.x(), p2.y(), p2.z(), 2.0f,
220                         };
221
222                         retval.insert(retval.end(), DE_ARRAY_BEGIN(temp), DE_ARRAY_END(temp));
223                 }
224
225                 return retval;
226         }
227
228         // Outputs barycentric coordinates as v_bcoords. Otherwise a passthrough shader
229         string getBaseVertexShader (void)
230         {
231                 return "#version 300 es\n"
232                                 "in highp vec4 a_position;\n"
233                                 "out mediump vec3 v_bcoords;\n"
234                                 "void main()\n"
235                                 "{\n"
236                                 "       v_bcoords = vec3(0, 0, 0);\n"
237                                 "       v_bcoords[int(a_position.w)] = 1.0;\n"
238                                 "       gl_Position = vec4(a_position.xyz, 1.0);\n"
239                                 "}\n";
240         }
241
242         // Adds noise to coordinates based on InstanceID Outputs barycentric coordinates as v_bcoords
243         string getInstanceNoiseVertexShader (void)
244         {
245                 return "#version 300 es\n"
246                                 "in highp vec4 a_position;\n"
247                                 "out mediump vec3 v_bcoords;\n"
248                                 "void main()\n"
249                                 "{\n"
250                                 "       v_bcoords = vec3(0, 0, 0);\n"
251                                 "       v_bcoords[int(a_position.w)] = 1.0;\n"
252                                 "       vec3 noise = vec3(sin(float(gl_InstanceID)*1.05), sin(float(gl_InstanceID)*1.23), sin(float(gl_InstanceID)*1.71));\n"
253                                 "       gl_Position = vec4(a_position.xyz + noise * 0.005, 1.0);\n"
254                                 "}\n";
255         }
256
257         // Renders green triangles with edges highlighted. Exact shade depends on depth.
258         string getDepthAsGreenFragmentShader (void)
259         {
260                 return  "#version 300 es\n"
261                                 "in mediump vec3 v_bcoords;\n"
262                                 "out mediump vec4 fragColor;\n"
263                                 "void main()\n"
264                                 "{\n"
265                                 "       mediump float d = gl_FragCoord.z;\n"
266                                 "       if (v_bcoords.x < 0.02 || v_bcoords.y < 0.02 || v_bcoords.z < 0.02)\n"
267                                 "               fragColor = vec4(d,1,d,1);\n"
268                                 "       else\n"
269                                 "               fragColor = vec4(0,d,0,1);\n"
270                                 "}\n";
271         }
272
273         // Renders green triangles with edges highlighted. Exact shade depends on depth.
274         string getDepthAsRedFragmentShader (void)
275         {
276                 return  "#version 300 es\n"
277                                 "in mediump vec3 v_bcoords;\n"
278                                 "out mediump vec4 fragColor;\n"
279                                 "void main()\n"
280                                 "{\n"
281                                 "       mediump float d = gl_FragCoord.z;\n"
282                                 "       if (v_bcoords.x < 0.02 || v_bcoords.y < 0.02 || v_bcoords.z < 0.02)\n"
283                                 "               fragColor = vec4(1,d,d,1);\n"
284                                 "       else\n"
285                                 "               fragColor = vec4(d,0,0,1);\n"
286                                 "}\n";
287         }
288
289         // Basic time waster. Renders red triangles with edges highlighted. Exact shade depends on depth.
290         string getArithmeticWorkloadFragmentShader (void)
291         {
292
293                 return  "#version 300 es\n"
294                                 "in mediump vec3 v_bcoords;\n"
295                                 "out mediump vec4 fragColor;\n"
296                                 "uniform mediump int u_iterations;\n"
297                                 "void main()\n"
298                                 "{\n"
299                                 "       mediump float d = gl_FragCoord.z;\n"
300                                 "       for (int i = 0; i<u_iterations; i++)\n"
301                                 // cos(a)^2 + sin(a)^2 == 1. since d is in range [0,1] this will lose a few ULP's of precision per iteration but should not significantly change the value of d without extreme iteration counts
302                                 "               d = d*sin(d)*sin(d) + d*cos(d)*cos(d);\n"
303                                 "       if (v_bcoords.x < 0.02 || v_bcoords.y < 0.02 || v_bcoords.z < 0.02)\n"
304                                 "               fragColor = vec4(1,d,d,1);\n"
305                                 "       else\n"
306                                 "               fragColor = vec4(d,0,0,1);\n"
307                                 "}\n";
308         }
309
310         // Arithmetic workload shader but contains discard
311         string getArithmeticWorkloadDiscardFragmentShader (void)
312         {
313                 return  "#version 300 es\n"
314                                 "in mediump vec3 v_bcoords;\n"
315                                 "out mediump vec4 fragColor;\n"
316                                 "uniform mediump int u_iterations;\n"
317                                 "void main()\n"
318                                 "{\n"
319                                 "       mediump float d = gl_FragCoord.z;\n"
320                                 "       for (int i = 0; i<u_iterations; i++)\n"
321                                 "               d = d*sin(d)*sin(d) + d*cos(d)*cos(d);\n"
322                                 "       if (d < 0.5) discard;\n"
323                                 "       if (v_bcoords.x < 0.02 || v_bcoords.y < 0.02 || v_bcoords.z < 0.02)\n"
324                                 "               fragColor = vec4(1,d,d,1);\n"
325                                 "       else\n"
326                                 "               fragColor = vec4(d,0,0,1);\n"
327                                 "}\n";
328         }
329
330         // Texture fetch based time waster. Renders red triangles with edges highlighted. Exact shade depends on depth.
331         string getTextureWorkloadFragmentShader (void)
332         {
333                 return  "#version 300 es\n"
334                                 "in mediump vec3 v_bcoords;\n"
335                                 "out mediump vec4 fragColor;\n"
336                                 "uniform mediump int u_iterations;\n"
337                                 "uniform sampler2D u_texture;\n"
338                                 "void main()\n"
339                                 "{\n"
340                                 "       mediump float d = gl_FragCoord.z;\n"
341                                 "       for (int i = 0; i<u_iterations; i++)\n"
342                                 "               d *= texture(u_texture, (gl_FragCoord.xy+vec2(i))/512.0).r;\n" // Texture is expected to be fully white
343                                 "       if (v_bcoords.x < 0.02 || v_bcoords.y < 0.02 || v_bcoords.z < 0.02)\n"
344                                 "               fragColor = vec4(1,1,1,1);\n"
345                                 "       else\n"
346                                 "               fragColor = vec4(d,0,0,1);\n"
347                                 "}\n";
348         }
349
350         // Discard fragments in a grid pattern
351         string getGridDiscardFragmentShader (int gridsize)
352         {
353                 const string            fragSrc = "#version 300 es\n"
354                                                                           "in mediump vec3 v_bcoords;\n"
355                                                                           "out mediump vec4 fragColor;\n"
356                                                                           "void main()\n"
357                                                                           "{\n"
358                                                                           "     mediump float d = gl_FragCoord.z;\n"
359                                                                           "     if ((int(gl_FragCoord.x)/${GRIDRENDER_SIZE} + int(gl_FragCoord.y)/${GRIDRENDER_SIZE})%2 == 0)\n"
360                                                                           "             discard;\n"
361                                                                           "     if (v_bcoords.x < 0.02 || v_bcoords.y < 0.02 || v_bcoords.z < 0.02)\n"
362                                                                           "             fragColor = vec4(d,1,d,1);\n"
363                                                                           "     else\n"
364                                                                           "             fragColor = vec4(0,d,0,1);\n"
365                                                                           "}\n";
366                 map<string, string>     params;
367
368                 params["GRIDRENDER_SIZE"] = de::toString(gridsize);
369
370                 return tcu::StringTemplate(fragSrc).specialize(params);
371         }
372
373         // A static increment to frag depth
374         string getStaticFragDepthFragmentShader (void)
375         {
376                 return  "#version 300 es\n"
377                                 "in mediump vec3 v_bcoords;\n"
378                                 "out mediump vec4 fragColor;\n"
379                                 "void main()\n"
380                                 "{\n"
381                                 "       mediump float d = gl_FragCoord.z;\n"
382                                 "       gl_FragDepth = gl_FragCoord.z + 0.1;\n"
383                                 "       if (v_bcoords.x < 0.02 || v_bcoords.y < 0.02 || v_bcoords.z < 0.02)\n"
384                                 "               fragColor = vec4(d,1,d,1);\n"
385                                 "       else\n"
386                                 "               fragColor = vec4(0,d,0,1);\n"
387                                 "}\n";
388         }
389
390         // A trivial dynamic change to frag depth
391         string getDynamicFragDepthFragmentShader (void)
392         {
393                 return  "#version 300 es\n"
394                                 "in mediump vec3 v_bcoords;\n"
395                                 "out mediump vec4 fragColor;\n"
396                                 "void main()\n"
397                                 "{\n"
398                                 "       mediump float d = gl_FragCoord.z;\n"
399                                 "       gl_FragDepth = gl_FragCoord.z + (v_bcoords.x + v_bcoords.y + v_bcoords.z)*0.05;\n" // Sum of v_bcoords components is allways 1
400                                 "       if (v_bcoords.x < 0.02 || v_bcoords.y < 0.02 || v_bcoords.z < 0.02)\n"
401                                 "               fragColor = vec4(d,1,d,1);\n"
402                                 "       else\n"
403                                 "               fragColor = vec4(0,d,0,1);\n"
404                                 "}\n";
405         }
406
407         // A static increment to frag depth
408         string getStaticFragDepthArithmeticWorkloadFragmentShader (void)
409         {
410                 return  "#version 300 es\n"
411                                 "in mediump vec3 v_bcoords;\n"
412                                 "out mediump vec4 fragColor;\n"
413                                 "uniform mediump int u_iterations;\n"
414                                 "void main()\n"
415                                 "{\n"
416                                 "       mediump float d = gl_FragCoord.z;\n"
417                                 "       gl_FragDepth = gl_FragCoord.z + 0.1;\n"
418                                 "       for (int i = 0; i<u_iterations; i++)\n"
419                                 "               d = d*sin(d)*sin(d) + d*cos(d)*cos(d);\n"
420                                 "       if (v_bcoords.x < 0.02 || v_bcoords.y < 0.02 || v_bcoords.z < 0.02)\n"
421                                 "               fragColor = vec4(1,d,d,1);\n"
422                                 "       else\n"
423                                 "               fragColor = vec4(d,0,0,1);\n"
424                                 "}\n";
425         }
426
427         // A trivial dynamic change to frag depth
428         string getDynamicFragDepthArithmeticWorkloadFragmentShader (void)
429         {
430                 return  "#version 300 es\n"
431                                 "in mediump vec3 v_bcoords;\n"
432                                 "out mediump vec4 fragColor;\n"
433                                 "uniform mediump int u_iterations;\n"
434                                 "void main()\n"
435                                 "{\n"
436                                 "       mediump float d = gl_FragCoord.z;\n"
437                                 "       gl_FragDepth = gl_FragCoord.z + (v_bcoords.x + v_bcoords.y + v_bcoords.z)*0.05;\n" // Sum of v_bcoords components is allways 1
438                                 "       for (int i = 0; i<u_iterations; i++)\n"
439                                 "               d = d*sin(d)*sin(d) + d*cos(d)*cos(d);\n"
440                                 "       if (v_bcoords.x < 0.02 || v_bcoords.y < 0.02 || v_bcoords.z < 0.02)\n"
441                                 "               fragColor = vec4(1,d,d,1);\n"
442                                 "       else\n"
443                                 "               fragColor = vec4(d,0,0,1);\n"
444                                 "}\n";
445         }
446
447         glu::ProgramSources getBaseShader (void)
448         {
449                 return glu::makeVtxFragSources(getBaseVertexShader(), getDepthAsGreenFragmentShader());
450         }
451
452         glu::ProgramSources getArithmeticWorkloadShader (void)
453         {
454                 return glu::makeVtxFragSources(getBaseVertexShader(), getArithmeticWorkloadFragmentShader());
455         }
456
457         glu::ProgramSources getArithmeticWorkloadDiscardShader (void)
458         {
459                 return glu::makeVtxFragSources(getBaseVertexShader(), getArithmeticWorkloadDiscardFragmentShader());
460         }
461
462         glu::ProgramSources getTextureWorkloadShader (void)
463         {
464                 return glu::makeVtxFragSources(getBaseVertexShader(), getTextureWorkloadFragmentShader());
465         }
466
467         glu::ProgramSources getGridDiscardShader (int gridsize)
468         {
469                 return glu::makeVtxFragSources(getBaseVertexShader(), getGridDiscardFragmentShader(gridsize));
470         }
471
472         inline ObjectData quadWith (const glu::ProgramSources& shader, float depth)
473         {
474                 return ObjectData(shader, getFullscreenQuad(depth));
475         }
476
477         inline ObjectData quadWith (const string& fragShader, float depth)
478         {
479                 return ObjectData(glu::makeVtxFragSources(getBaseVertexShader(), fragShader), getFullscreenQuad(depth));
480         }
481
482         inline ObjectData variableQuad (float depth)
483         {
484                 return ObjectData(glu::makeVtxFragSources(getInstanceNoiseVertexShader(), getDepthAsRedFragmentShader()), getFullscreenQuad(depth));
485         }
486
487         inline ObjectData fastQuad (float depth)
488         {
489                 return ObjectData(getBaseShader(), getFullscreenQuad(depth));
490         }
491
492         inline ObjectData slowQuad (float depth)
493         {
494                 return ObjectData(getArithmeticWorkloadShader(), getFullscreenQuad(depth));
495         }
496
497         inline ObjectData fastQuadWithGradient (float depth0, float depth1)
498         {
499                 return ObjectData(getBaseShader(), getFullscreenQuadWithGradient(depth0, depth1));
500         }
501 } // Utils
502
503 // Shared base
504 class BaseCase : public tcu::TestCase
505 {
506 public:
507         enum {RENDER_SIZE = 512};
508
509                                                         BaseCase                        (TestContext& testCtx, const RenderContext& renderCtx, const char* name, const char* desc);
510         virtual                                 ~BaseCase                       (void) {}
511
512         virtual IterateResult   iterate                         (void);
513
514 protected:
515         void                                    logSamples                      (const vector<Sample>& samples, const string& name, const string& desc);
516         void                                    logGeometry                     (const tcu::ConstPixelBufferAccess& sample, const glu::ShaderProgram& occluderProg, const glu::ShaderProgram& occludedProg);
517         virtual void                    logAnalysis                     (const vector<Sample>& samples) = 0;
518         virtual void                    logDescription          (void) = 0;
519
520         virtual ObjectData              genOccluderGeometry     (void) const = 0;
521         virtual ObjectData              genOccludedGeometry     (void) const = 0;
522
523         virtual int                             calibrate                       (void) const = 0;
524         virtual Sample                  renderSample            (const RenderData& occluder, const RenderData& occluded, int workload) const = 0;
525
526         void                                    render                          (const RenderData& data) const;
527         void                                    render                          (const RenderData& data, int instances) const;
528
529         const RenderContext&    m_renderCtx;
530         tcu::ResultCollector    m_results;
531
532         enum {ITERATION_STEPS = 10, ITERATION_SAMPLES = 16};
533 };
534
535 BaseCase::BaseCase (TestContext& testCtx, const RenderContext& renderCtx, const char* name, const char* desc)
536         : TestCase              (testCtx, tcu::NODETYPE_PERFORMANCE, name, desc)
537         , m_renderCtx   (renderCtx)
538 {
539 }
540
541 BaseCase::IterateResult BaseCase::iterate (void)
542 {
543         typedef de::MovePtr<RenderData> RenderDataP;
544
545         const glw::Functions&   gl                                      = m_renderCtx.getFunctions();
546         TestLog&                                log                                     = m_testCtx.getLog();
547
548         const glu::Framebuffer  framebuffer                     (gl);
549         const glu::Renderbuffer renderbuffer            (gl);
550         const glu::Renderbuffer depthbuffer                     (gl);
551
552         vector<Sample>                  results;
553         vector<int>                             params;
554         RenderDataP                             occluderData;
555         RenderDataP                             occludedData;
556         tcu::TextureLevel               resultTex                       (tcu::TextureFormat(tcu::TextureFormat::RGBA, tcu::TextureFormat::UNORM_INT8), RENDER_SIZE, RENDER_SIZE);
557         int                                             maxWorkload                     = 0;
558         de::Random                              rng                                     (deInt32Hash(deStringHash(getName())) ^ m_testCtx.getCommandLine().getBaseSeed());
559
560         logDescription();
561
562         gl.bindRenderbuffer(GL_RENDERBUFFER, *renderbuffer);
563         gl.renderbufferStorage(GL_RENDERBUFFER, GL_RGBA8, RENDER_SIZE, RENDER_SIZE);
564         gl.bindRenderbuffer(GL_RENDERBUFFER, *depthbuffer);
565         gl.renderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, RENDER_SIZE, RENDER_SIZE);
566
567         gl.bindFramebuffer(GL_FRAMEBUFFER, *framebuffer);
568         gl.framebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, *renderbuffer);
569         gl.framebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, *depthbuffer);
570         gl.viewport(0, 0, RENDER_SIZE, RENDER_SIZE);
571         gl.clearColor(0.125f, 0.25f, 0.5f, 1.0f);
572
573         maxWorkload = calibrate();
574
575         // Setup data
576         occluderData = RenderDataP(new RenderData (genOccluderGeometry(), m_renderCtx, log));
577         occludedData = RenderDataP(new RenderData (genOccludedGeometry(), m_renderCtx, log));
578
579         TCU_CHECK(occluderData->m_program.isOk());
580         TCU_CHECK(occludedData->m_program.isOk());
581
582         // Force initialization of GPU resources
583         gl.clear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
584         gl.enable(GL_DEPTH_TEST);
585
586         render(*occluderData);
587         render(*occludedData);
588         glu::readPixels(m_renderCtx, 0, 0, resultTex.getAccess());
589
590         logGeometry(resultTex.getAccess(), occluderData->m_program, occludedData->m_program);
591
592         params.reserve(ITERATION_STEPS*ITERATION_SAMPLES);
593
594         // Setup parameters
595         for (int step = 0; step < ITERATION_STEPS; step++)
596         {
597                 const int workload = maxWorkload*step/ITERATION_STEPS;
598
599                 for (int count = 0; count < ITERATION_SAMPLES; count++)
600                         params.push_back(workload);
601         }
602
603         rng.shuffle(params.begin(), params.end());
604
605         // Render samples
606         for (size_t ndx = 0; ndx < params.size(); ndx++)
607         {
608                 const int       workload        = params[ndx];
609                 Sample          sample          = renderSample(*occluderData, *occludedData, workload);
610
611                 sample.workload = workload;
612                 sample.order = int(ndx);
613
614                 results.push_back(sample);
615         }
616
617         logSamples(results, "Samples", "Samples");
618         logAnalysis(results);
619
620         m_results.setTestContextResult(m_testCtx);
621
622         return STOP;
623 }
624
625 void BaseCase::logSamples (const vector<Sample>& samples, const string& name, const string& desc)
626 {
627         TestLog& log = m_testCtx.getLog();
628
629         bool testOnly = true;
630
631         for (size_t ndx = 0; ndx < samples.size(); ndx++)
632         {
633                 if (samples[ndx].baseTime != 0 || samples[ndx].nullTime != 0)
634                 {
635                         testOnly = false;
636                         break;
637                 }
638         }
639
640         log << TestLog::SampleList(name, desc);
641
642         if (testOnly)
643         {
644                 log << TestLog::SampleInfo
645                         << TestLog::ValueInfo("Workload",       "Workload",                     "",                             QP_SAMPLE_VALUE_TAG_PREDICTOR)
646                         << TestLog::ValueInfo("Order",          "Order of sample",      "",                             QP_SAMPLE_VALUE_TAG_PREDICTOR)
647                         << TestLog::ValueInfo("TestTime",       "Test render time",     "us",                   QP_SAMPLE_VALUE_TAG_RESPONSE)
648                         << TestLog::EndSampleInfo;
649
650                 for (size_t sampleNdx = 0; sampleNdx < samples.size(); sampleNdx++)
651                 {
652                         const Sample& sample = samples[sampleNdx];
653
654                         log << TestLog::Sample << sample.workload << sample.order << sample.testTime << TestLog::EndSample;
655                 }
656         }
657         else
658         {
659                 log << TestLog::SampleInfo
660                         << TestLog::ValueInfo("Workload",       "Workload",                     "",                             QP_SAMPLE_VALUE_TAG_PREDICTOR)
661                         << TestLog::ValueInfo("Order",          "Order of sample",      "",                             QP_SAMPLE_VALUE_TAG_PREDICTOR)
662                         << TestLog::ValueInfo("TestTime",       "Test render time",     "us",                   QP_SAMPLE_VALUE_TAG_RESPONSE)
663                         << TestLog::ValueInfo("NullTime",       "Read pixels time",     "us",                   QP_SAMPLE_VALUE_TAG_RESPONSE)
664                         << TestLog::ValueInfo("BaseTime",       "Base render time",     "us",                   QP_SAMPLE_VALUE_TAG_RESPONSE)
665                         << TestLog::EndSampleInfo;
666
667                 for (size_t sampleNdx = 0; sampleNdx < samples.size(); sampleNdx++)
668                 {
669                         const Sample& sample = samples[sampleNdx];
670
671                         log << TestLog::Sample << sample.workload << sample.order << sample.testTime << sample.nullTime << sample.baseTime << TestLog::EndSample;
672                 }
673         }
674
675         log << TestLog::EndSampleList;
676 }
677
678 void BaseCase::logGeometry (const tcu::ConstPixelBufferAccess& sample, const glu::ShaderProgram& occluderProg, const glu::ShaderProgram& occludedProg)
679 {
680         TestLog& log = m_testCtx.getLog();
681
682         log << TestLog::Section("Geometry", "Geometry");
683         log << TestLog::Message << "Occluding geometry is green with shade dependent on depth (rgb == 0, depth, 0)" << TestLog::EndMessage;
684         log << TestLog::Message << "Occluded geometry is red with shade dependent on depth (rgb == depth, 0, 0)" << TestLog::EndMessage;
685         log << TestLog::Message << "Primitive edges are a lighter shade of red/green" << TestLog::EndMessage;
686
687         log << TestLog::Image("Test Geometry", "Test Geometry",  sample);
688         log << TestLog::EndSection;
689
690         log << TestLog::Section("Occluder", "Occluder");
691         log << occluderProg;
692         log << TestLog::EndSection;
693
694         log << TestLog::Section("Occluded", "Occluded");
695         log << occludedProg;
696         log << TestLog::EndSection;
697 }
698
699 void BaseCase::render (const RenderData& data) const
700 {
701         const glw::Functions& gl = m_renderCtx.getFunctions();
702
703         gl.useProgram(data.m_program.getProgram());
704
705         gl.bindVertexArray(*data.m_vao);
706         gl.drawArrays(GL_TRIANGLES, 0, data.m_numVertices);
707         gl.bindVertexArray(0);
708 }
709
710 void BaseCase::render (const RenderData& data, int instances) const
711 {
712         const glw::Functions& gl = m_renderCtx.getFunctions();
713
714         gl.useProgram(data.m_program.getProgram());
715
716         gl.bindVertexArray(*data.m_vao);
717         gl.drawArraysInstanced(GL_TRIANGLES, 0, data.m_numVertices, instances);
718         gl.bindVertexArray(0);
719 }
720
721 // Render occluder once, then repeatedly render occluded geometry. Sample with multiple repetition counts & establish time per call with linear regression
722 class RenderCountCase : public BaseCase
723 {
724 public:
725                                         RenderCountCase         (TestContext& testCtx, const RenderContext& renderCtx, const char* name, const char* desc);
726                                         ~RenderCountCase        (void) {}
727
728 protected:
729         virtual void    logAnalysis                     (const vector<Sample>& samples);
730
731 private:
732         virtual int             calibrate                       (void) const;
733         virtual Sample  renderSample            (const RenderData& occluder, const RenderData& occluded, int callcount) const;
734 };
735
736 RenderCountCase::RenderCountCase (TestContext& testCtx, const RenderContext& renderCtx, const char* name, const char* desc)
737         : BaseCase      (testCtx, renderCtx, name, desc)
738 {
739 }
740
741 void RenderCountCase::logAnalysis (const vector<Sample>& samples)
742 {
743         using namespace gls;
744
745         TestLog&                log                     = m_testCtx.getLog();
746         int                             maxWorkload     = 0;
747         vector<Vec2>    testSamples     (samples.size());
748
749         for (size_t ndx = 0; ndx < samples.size(); ndx++)
750         {
751                 const Sample& sample = samples[ndx];
752
753                 testSamples[ndx] = Vec2((float)sample.workload, (float)sample.testTime);
754
755                 maxWorkload = de::max(maxWorkload, sample.workload);
756         }
757
758         {
759                 const float                                                     confidence      = 0.60f;
760                 const LineParametersWithConfidence      testParam       = theilSenSiegelLinearRegression(testSamples, confidence);
761                 const float                                                     usPerCall       = testParam.coefficient;
762                 const float                                                     pxPerCall       = RENDER_SIZE*RENDER_SIZE;
763                 const float                                                     pxPerUs         = pxPerCall/usPerCall;
764                 const float                                                     mpxPerS         = pxPerUs;
765
766                 log << TestLog::Section("Linear Regression", "Linear Regression");
767                 log << TestLog::Message << "Offset & coefficient presented as [confidence interval min, estimate, confidence interval max]. Reported confidence interval for this test is " << confidence << TestLog::EndMessage;
768                 log << TestLog::Message << "Render time for scene with depth test was\n\t"
769                         << "[" << testParam.offsetConfidenceLower << ", " << testParam.offset <<  ", " << testParam.offsetConfidenceUpper << "]us +"
770                         << "[" << testParam.coefficientConfidenceLower << ", " << testParam.coefficient << ", " << testParam.coefficientConfidenceUpper << "]"
771                         << "us/workload" << TestLog::EndMessage;
772                 log << TestLog::EndSection;
773
774                 log << TestLog::Section("Result", "Result");
775
776                 if (testParam.coefficientConfidenceLower < 0.0f)
777                 {
778                         log << TestLog::Message << "Coefficient confidence bounds include values below 0.0, the operation likely has neglible per-pixel cost" << TestLog::EndMessage;
779                         m_results.addResult(QP_TEST_RESULT_PASS, "Pass");
780                 }
781                 else if (testParam.coefficientConfidenceLower < testParam.coefficientConfidenceUpper*0.25)
782                 {
783                         log << TestLog::Message << "Coefficient confidence range is extremely large, cannot give reliable result" << TestLog::EndMessage;
784                         m_results.addResult(QP_TEST_RESULT_PASS, "Result confidence extremely low");
785                 }
786                 else
787                 {
788                         log << TestLog::Message << "Culled hidden pixels @ " << mpxPerS << "Mpx/s" << TestLog::EndMessage;
789                         m_results.addResult(QP_TEST_RESULT_PASS, de::floatToString(mpxPerS, 2));
790                 }
791
792                 log << TestLog::EndSection;
793         }
794 }
795
796 Sample RenderCountCase::renderSample (const RenderData& occluder, const RenderData& occluded, int callcount) const
797 {
798         const glw::Functions&   gl              = m_renderCtx.getFunctions();
799         Sample                                  sample;
800         deUint64                                now             = 0;
801         deUint64                                prev    = 0;
802         deUint8                                 buffer[4];
803
804         // Stabilize
805         {
806                 gl.clear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
807                 gl.enable(GL_DEPTH_TEST);
808                 gl.readPixels(0, 0, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, buffer);
809         }
810
811         prev = deGetMicroseconds();
812
813         gl.clear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
814         gl.enable(GL_DEPTH_TEST);
815
816         render(occluder);
817         render(occluded, callcount);
818
819         gl.readPixels(0, 0, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, buffer);
820
821         now = deGetMicroseconds();
822
823         sample.testTime = now - prev;
824         sample.baseTime = 0;
825         sample.nullTime = 0;
826         sample.workload = callcount;
827
828         return sample;
829 }
830
831 int RenderCountCase::calibrate (void) const
832 {
833         using namespace gls;
834
835         const glw::Functions&   gl                                      = m_renderCtx.getFunctions();
836         TestLog&                                log                                     = m_testCtx.getLog();
837
838         const RenderData                occluderGeometry        (genOccluderGeometry(), m_renderCtx, log);
839         const RenderData                occludedGeometry        (genOccludedGeometry(), m_renderCtx, log);
840
841         TheilSenCalibrator              calibrator                      (CalibratorParameters(20, // Initial workload
842                                                                                                                                           10, // Max iteration frames
843                                                                                                                                           20.0f, // Iteration shortcut threshold ms
844                                                                                                                                           20, // Max iterations
845                                                                                                                                           33.0f, // Target frame time
846                                                                                                                                           40.0f, // Frame time cap
847                                                                                                                                           1000.0f // Target measurement duration
848                                                                                                                                           ));
849
850         while (true)
851         {
852                 switch(calibrator.getState())
853                 {
854                         case TheilSenCalibrator::STATE_FINISHED:
855                                 logCalibrationInfo(m_testCtx.getLog(), calibrator);
856                                 return calibrator.getCallCount();
857
858                         case TheilSenCalibrator::STATE_MEASURE:
859                         {
860                                 deUint8 buffer[4];
861                                 deInt64 now;
862                                 deInt64 prev;
863
864                                 prev = deGetMicroseconds();
865
866                                 gl.clear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
867                                 gl.disable(GL_DEPTH_TEST);
868
869                                 render(occluderGeometry);
870                                 render(occludedGeometry, calibrator.getCallCount());
871
872                                 gl.readPixels(0, 0, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, buffer);
873
874                                 now = deGetMicroseconds();
875
876                                 calibrator.recordIteration(now - prev);
877                                 break;
878                         }
879
880                         case TheilSenCalibrator::STATE_RECOMPUTE_PARAMS:
881                                 calibrator.recomputeParameters();
882                                 break;
883                         default:
884                                 DE_ASSERT(false);
885                                 return 1;
886                 }
887         }
888 }
889
890 // Compares time/workload gradients of same geometry with and without depth testing
891 class RelativeChangeCase : public BaseCase
892 {
893 public:
894                                         RelativeChangeCase      (TestContext& testCtx, const RenderContext& renderCtx, const char* name, const char* desc);
895         virtual                 ~RelativeChangeCase     (void) {}
896
897 protected:
898         Sample                  renderSample            (const RenderData& occluder, const RenderData& occluded, int workload) const;
899
900         virtual void    logAnalysis                     (const vector<Sample>& samples);
901
902 private:
903         int                             calibrate                       (void) const;
904 };
905
906 RelativeChangeCase::RelativeChangeCase (TestContext& testCtx, const RenderContext& renderCtx, const char* name, const char* desc)
907         : BaseCase              (testCtx, renderCtx, name, desc)
908 {
909 }
910
911 int RelativeChangeCase::calibrate (void) const
912 {
913         using namespace gls;
914
915         const glw::Functions&   gl              = m_renderCtx.getFunctions();
916         TestLog&                                log             = m_testCtx.getLog();
917
918         const RenderData                geom    (genOccludedGeometry(), m_renderCtx, log);
919
920         TheilSenCalibrator calibrator(CalibratorParameters( 20, // Initial workload
921                                                                                                                 10, // Max iteration frames
922                                                                                                                 20.0f, // Iteration shortcut threshold ms
923                                                                                                                 20, // Max iterations
924                                                                                                                 33.0f, // Target frame time
925                                                                                                                 40.0f, // Frame time cap
926                                                                                                                 1000.0f // Target measurement duration
927                                                                                                                 ));
928
929         while (true)
930         {
931                 switch(calibrator.getState())
932                 {
933                         case TheilSenCalibrator::STATE_FINISHED:
934                                 logCalibrationInfo(m_testCtx.getLog(), calibrator);
935                                 return calibrator.getCallCount();
936
937                         case TheilSenCalibrator::STATE_MEASURE:
938                         {
939                                 deUint8                 buffer[4];
940                                 const GLuint    program = geom.m_program.getProgram();
941
942                                 gl.useProgram(program);
943                                 gl.uniform1i(gl.getUniformLocation(program, "u_iterations"), calibrator.getCallCount());
944
945                                 const deInt64 prev = deGetMicroseconds();
946
947                                 gl.clear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
948                                 gl.disable(GL_DEPTH_TEST);
949
950                                 render(geom);
951
952                                 gl.readPixels(0, 0, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, buffer);
953
954                                 const deInt64 now = deGetMicroseconds();
955
956                                 calibrator.recordIteration(now - prev);
957                                 break;
958                         }
959
960                         case TheilSenCalibrator::STATE_RECOMPUTE_PARAMS:
961                                 calibrator.recomputeParameters();
962                                 break;
963                         default:
964                                 DE_ASSERT(false);
965                                 return 1;
966                 }
967         }
968 }
969
970 Sample RelativeChangeCase::renderSample (const RenderData& occluder, const RenderData& occluded, int workload) const
971 {
972         const glw::Functions&   gl              = m_renderCtx.getFunctions();
973         const GLuint                    program = occluded.m_program.getProgram();
974         Sample                                  sample;
975         deUint64                                now             = 0;
976         deUint64                                prev    = 0;
977         deUint8                                 buffer[4];
978
979         gl.useProgram(program);
980         gl.uniform1i(gl.getUniformLocation(program, "u_iterations"), workload);
981
982         // Warmup (this workload seems to reduce variation in following workloads)
983         {
984                 gl.clear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
985                 gl.disable(GL_DEPTH_TEST);
986
987                 gl.readPixels(0, 0, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, buffer);
988         }
989
990         // Null time
991         {
992                 prev = deGetMicroseconds();
993
994                 gl.clear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
995                 gl.disable(GL_DEPTH_TEST);
996
997                 gl.readPixels(0, 0, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, buffer);
998
999                 now = deGetMicroseconds();
1000
1001                 sample.nullTime = now - prev;
1002         }
1003
1004         // Test time
1005         {
1006                 prev = deGetMicroseconds();
1007
1008                 gl.clear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
1009                 gl.enable(GL_DEPTH_TEST);
1010
1011                 render(occluder);
1012                 render(occluded);
1013
1014                 gl.readPixels(0, 0, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, buffer);
1015
1016                 now = deGetMicroseconds();
1017
1018                 sample.testTime = now - prev;
1019         }
1020
1021         // Base time
1022         {
1023                 prev = deGetMicroseconds();
1024
1025                 gl.clear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
1026                 gl.disable(GL_DEPTH_TEST);
1027
1028                 render(occluder);
1029                 render(occluded);
1030
1031                 gl.readPixels(0, 0, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, buffer);
1032
1033                 now = deGetMicroseconds();
1034
1035                 sample.baseTime = now - prev;
1036         }
1037
1038         sample.workload = 0;
1039
1040         return sample;
1041 }
1042
1043 void RelativeChangeCase::logAnalysis (const vector<Sample>& samples)
1044 {
1045         using namespace gls;
1046
1047         TestLog&                log                     = m_testCtx.getLog();
1048
1049         int                             maxWorkload     = 0;
1050
1051         vector<Vec2>    nullSamples     (samples.size());
1052         vector<Vec2>    baseSamples     (samples.size());
1053         vector<Vec2>    testSamples     (samples.size());
1054
1055         for (size_t ndx = 0; ndx < samples.size(); ndx++)
1056         {
1057                 const Sample& sample = samples[ndx];
1058
1059                 nullSamples[ndx] = Vec2((float)sample.workload, (float)sample.nullTime);
1060                 baseSamples[ndx] = Vec2((float)sample.workload, (float)sample.baseTime);
1061                 testSamples[ndx] = Vec2((float)sample.workload, (float)sample.testTime);
1062
1063                 maxWorkload = de::max(maxWorkload, sample.workload);
1064         }
1065
1066         {
1067                 const float                                                     confidence      = 0.60f;
1068
1069                 const LineParametersWithConfidence      nullParam       = theilSenSiegelLinearRegression(nullSamples, confidence);
1070                 const LineParametersWithConfidence      baseParam       = theilSenSiegelLinearRegression(baseSamples, confidence);
1071                 const LineParametersWithConfidence      testParam       = theilSenSiegelLinearRegression(testSamples, confidence);
1072
1073                 if (!de::inRange(0.0f, nullParam.coefficientConfidenceLower, nullParam.coefficientConfidenceUpper))
1074                 {
1075                         m_results.addResult(QP_TEST_RESULT_FAIL, "Constant operation sequence duration not constant");
1076                         log << TestLog::Message << "Constant operation sequence timing may vary as a function of workload. Result quality extremely low" << TestLog::EndMessage;
1077                 }
1078
1079                 if (de::inRange(0.0f, baseParam.coefficientConfidenceLower, baseParam.coefficientConfidenceUpper))
1080                 {
1081                         m_results.addResult(QP_TEST_RESULT_FAIL, "Workload has no effect on duration");
1082                         log << TestLog::Message << "Workload factor has no effect on duration of sample (smart optimizer?)" << TestLog::EndMessage;
1083                 }
1084
1085                 log << TestLog::Section("Linear Regression", "Linear Regression");
1086                 log << TestLog::Message << "Offset & coefficient presented as [confidence interval min, estimate, confidence interval max]. Reported confidence interval for this test is " << confidence << TestLog::EndMessage;
1087
1088                 log << TestLog::Message << "Render time for empty scene was\n\t"
1089                         << "[" << nullParam.offsetConfidenceLower << ", " << nullParam.offset <<  ", " << nullParam.offsetConfidenceUpper << "]us +"
1090                         << "[" << nullParam.coefficientConfidenceLower << ", " << nullParam.coefficient << ", " << nullParam.coefficientConfidenceUpper << "]"
1091                         << "us/workload" << TestLog::EndMessage;
1092
1093                 log << TestLog::Message << "Render time for scene without depth test was\n\t"
1094                         << "[" << baseParam.offsetConfidenceLower << ", " << baseParam.offset <<  ", " << baseParam.offsetConfidenceUpper << "]us +"
1095                         << "[" << baseParam.coefficientConfidenceLower << ", " << baseParam.coefficient << ", " << baseParam.coefficientConfidenceUpper << "]"
1096                         << "us/workload" << TestLog::EndMessage;
1097
1098                 log << TestLog::Message << "Render time for scene with depth test was\n\t"
1099                         << "[" << testParam.offsetConfidenceLower << ", " << testParam.offset <<  ", " << testParam.offsetConfidenceUpper << "]us +"
1100                         << "[" << testParam.coefficientConfidenceLower << ", " << testParam.coefficient << ", " << testParam.coefficientConfidenceUpper << "]"
1101                         << "us/workload" << TestLog::EndMessage;
1102
1103                 log << TestLog::EndSection;
1104
1105                 if (de::inRange(0.0f, testParam.coefficientConfidenceLower, testParam.coefficientConfidenceUpper))
1106                 {
1107                         log << TestLog::Message << "Test duration not dependent on culled workload" << TestLog::EndMessage;
1108                         m_results.addResult(QP_TEST_RESULT_PASS, "0.0");
1109                 }
1110                 else if (testParam.coefficientConfidenceLower < testParam.coefficientConfidenceUpper*0.25)
1111                 {
1112                         log << TestLog::Message << "Coefficient confidence range is extremely large, cannot give reliable result" << TestLog::EndMessage;
1113                         m_results.addResult(QP_TEST_RESULT_PASS, "Result confidence extremely low");
1114                 }
1115                 else if (baseParam.coefficientConfidenceLower < baseParam.coefficientConfidenceUpper*0.25)
1116                 {
1117                         log << TestLog::Message << "Coefficient confidence range for base render time is extremely large, cannot give reliable result" << TestLog::EndMessage;
1118                         m_results.addResult(QP_TEST_RESULT_PASS, "Result confidence extremely low");
1119                 }
1120                 else
1121                 {
1122                         log << TestLog::Message << "Test duration is dependent on culled workload" << TestLog::EndMessage;
1123                         m_results.addResult(QP_TEST_RESULT_PASS, de::floatToString(de::abs(testParam.coefficient)/de::abs(baseParam.coefficient), 2));
1124                 }
1125         }
1126 }
1127
1128 // Speed of trivial culling
1129 class BaseCostCase : public RenderCountCase
1130 {
1131 public:
1132                                                 BaseCostCase            (TestContext& testCtx, const RenderContext& renderCtx, const char* name, const char* desc)
1133                                                         : RenderCountCase (testCtx, renderCtx, name, desc) {}
1134
1135                                                 ~BaseCostCase           (void) {}
1136
1137 private:
1138         virtual ObjectData      genOccluderGeometry     (void) const { return Utils::fastQuad(0.2f); }
1139         virtual ObjectData      genOccludedGeometry     (void) const { return Utils::variableQuad(0.8f); }
1140
1141         virtual void            logDescription          (void)
1142         {
1143                 TestLog& log = m_testCtx.getLog();
1144
1145                 log << TestLog::Section("Description", "Test description");
1146                 log << TestLog::Message << "Testing hidden fragment culling speed" << TestLog::EndMessage;
1147                 log << TestLog::Message << "Geometry consists of two fullsceen quads. The first (occluding) is rendered once, the second (occluded) is rendered repeatedly" << TestLog::EndMessage;
1148                 log << TestLog::Message << "Workload indicates the number of times the occluded quad is rendered"  << TestLog::EndMessage;
1149                 log << TestLog::Message << "The time per culled pixel is estimated from the rate of change of rendering time as a function of workload"  << TestLog::EndMessage;
1150                 log << TestLog::EndSection;
1151         }
1152 };
1153
1154 // Gradient
1155 class GradientCostCase : public RenderCountCase
1156 {
1157 public:
1158                                                 GradientCostCase        (TestContext& testCtx, const RenderContext& renderCtx, const char* name, const char* desc, float gradientDistance)
1159                                                         : RenderCountCase               (testCtx, renderCtx, name, desc)
1160                                                         , m_gradientDistance    (gradientDistance)
1161                                                 {
1162                                                 }
1163
1164                                                 ~GradientCostCase       (void) {}
1165
1166 private:
1167         virtual ObjectData      genOccluderGeometry     (void) const { return Utils::fastQuadWithGradient(0.0f, 1.0f - m_gradientDistance); }
1168         virtual ObjectData      genOccludedGeometry     (void) const
1169         {
1170                 return ObjectData(glu::makeVtxFragSources(Utils::getInstanceNoiseVertexShader(), Utils::getDepthAsRedFragmentShader()), Utils::getFullscreenQuadWithGradient(m_gradientDistance, 1.0f));
1171         }
1172
1173         virtual void            logDescription          (void)
1174         {
1175                 TestLog& log = m_testCtx.getLog();
1176
1177                 log << TestLog::Section("Description", "Test description");
1178                 log << TestLog::Message << "Testing hidden fragment culling speed" << TestLog::EndMessage;
1179                 log << TestLog::Message << "Geometry consists of two fullsceen quads. The first (occluding) is rendered once, the second (occluded) is rendered repeatedly" << TestLog::EndMessage;
1180                 log << TestLog::Message << "Workload indicates the number of times the occluded quad is rendered" << TestLog::EndMessage;
1181                 log << TestLog::Message << "The quads are tilted so that the left edge of the occluded quad has a depth of 1.0 and the right edge of the occluding quad has a depth of 0.0." << TestLog::EndMessage;
1182                 log << TestLog::Message << "The quads are spaced to have a depth difference of " << m_gradientDistance << " at all points." << TestLog::EndMessage;
1183                 log << TestLog::Message << "The time per culled pixel is estimated from the rate of change of rendering time as a function of workload"  << TestLog::EndMessage;
1184                 log << TestLog::EndSection;
1185         }
1186
1187         const float                     m_gradientDistance;
1188 };
1189
1190 // Constant offset to frag depth in occluder
1191 class OccluderStaticFragDepthCostCase : public RenderCountCase
1192 {
1193 public:
1194                                                 OccluderStaticFragDepthCostCase         (TestContext& testCtx, const RenderContext& renderCtx, const char* name, const char* desc)
1195                                                         : RenderCountCase(testCtx, renderCtx, name, desc)
1196                                                 {
1197                                                 }
1198
1199                                                 ~OccluderStaticFragDepthCostCase        (void) {}
1200
1201 private:
1202         virtual ObjectData      genOccluderGeometry                                     (void) const { return Utils::quadWith(Utils::getStaticFragDepthFragmentShader(), 0.2f); }
1203         virtual ObjectData      genOccludedGeometry                                     (void) const { return Utils::fastQuad(0.8f); }
1204
1205         virtual void            logDescription                                          (void)
1206         {
1207                 TestLog& log = m_testCtx.getLog();
1208
1209                 log << TestLog::Section("Description", "Test description");
1210                 log << TestLog::Message << "Testing hidden fragment culling speed" << TestLog::EndMessage;
1211                 log << TestLog::Message << "Geometry consists of two fullsceen quads. The first (occluding) is rendered once, the second (occluded) is rendered repeatedly" << TestLog::EndMessage;
1212                 log << TestLog::Message << "Workload indicates the number of times the occluded quad is rendered" << TestLog::EndMessage;
1213                 log << TestLog::Message << "The occluder quad has a static offset applied to gl_FragDepth" << TestLog::EndMessage;
1214                 log << TestLog::Message << "The time per culled pixel is estimated from the rate of change of rendering time as a function of workload"  << TestLog::EndMessage;
1215                 log << TestLog::EndSection;
1216         }
1217 };
1218
1219 // Dynamic offset to frag depth in occluder
1220 class OccluderDynamicFragDepthCostCase : public RenderCountCase
1221 {
1222 public:
1223                                                 OccluderDynamicFragDepthCostCase        (TestContext& testCtx, const RenderContext& renderCtx, const char* name, const char* desc)
1224                                                         : RenderCountCase(testCtx, renderCtx, name, desc)
1225                                                 {
1226                                                 }
1227
1228                                                 ~OccluderDynamicFragDepthCostCase       (void) {}
1229
1230 private:
1231         virtual ObjectData      genOccluderGeometry                                     (void) const { return Utils::quadWith(Utils::getDynamicFragDepthFragmentShader(), 0.2f); }
1232         virtual ObjectData      genOccludedGeometry                                     (void) const { return Utils::fastQuad(0.8f); }
1233
1234         virtual void            logDescription                                          (void)
1235         {
1236                 TestLog& log = m_testCtx.getLog();
1237
1238                 log << TestLog::Section("Description", "Test description");
1239                 log << TestLog::Message << "Testing hidden fragment culling speed" << TestLog::EndMessage;
1240                 log << TestLog::Message << "Geometry consists of two fullsceen quads. The first (occluding) is rendered once, the second (occluded) is rendered repeatedly" << TestLog::EndMessage;
1241                 log << TestLog::Message << "Workload indicates the number of times the occluded quad is rendered" << TestLog::EndMessage;
1242                 log << TestLog::Message << "The occluder quad has a dynamic offset applied to gl_FragDepth" << TestLog::EndMessage;
1243                 log << TestLog::Message << "The time per culled pixel is estimated from the rate of change of rendering time as a function of workload"  << TestLog::EndMessage;
1244                 log << TestLog::EndSection;
1245         }
1246 };
1247
1248 // Constant offset to frag depth in occluder
1249 class OccludedStaticFragDepthCostCase : public RenderCountCase
1250 {
1251 public:
1252                                                 OccludedStaticFragDepthCostCase         (TestContext& testCtx, const RenderContext& renderCtx, const char* name, const char* desc)
1253                                                         : RenderCountCase(testCtx, renderCtx, name, desc)
1254                                                 {
1255                                                 }
1256
1257                                                 ~OccludedStaticFragDepthCostCase        (void) {}
1258
1259 private:
1260         virtual ObjectData      genOccluderGeometry                                     (void) const { return Utils::fastQuad(0.2f); }
1261         virtual ObjectData      genOccludedGeometry                                     (void) const { return Utils::quadWith(Utils::getStaticFragDepthFragmentShader(), 0.2f); }
1262
1263         virtual void            logDescription                                          (void)
1264         {
1265                 TestLog& log = m_testCtx.getLog();
1266
1267                 log << TestLog::Section("Description", "Test description");
1268                 log << TestLog::Message << "Testing hidden fragment culling speed" << TestLog::EndMessage;
1269                 log << TestLog::Message << "Geometry consists of two fullsceen quads. The first (occluding) is rendered once, the second (occluded) is rendered repeatedly" << TestLog::EndMessage;
1270                 log << TestLog::Message << "Workload indicates the number of times the occluded quad is rendered" << TestLog::EndMessage;
1271                 log << TestLog::Message << "The occluded quad has a static offset applied to gl_FragDepth" << TestLog::EndMessage;
1272                 log << TestLog::Message << "The time per culled pixel is estimated from the rate of change of rendering time as a function of workload"  << TestLog::EndMessage;
1273                 log << TestLog::EndSection;
1274         }
1275 };
1276
1277 // Dynamic offset to frag depth in occluder
1278 class OccludedDynamicFragDepthCostCase : public RenderCountCase
1279 {
1280 public:
1281                                                 OccludedDynamicFragDepthCostCase        (TestContext& testCtx, const RenderContext& renderCtx, const char* name, const char* desc)
1282                                                         : RenderCountCase(testCtx, renderCtx, name, desc)
1283                                                 {
1284                                                 }
1285
1286                                                 ~OccludedDynamicFragDepthCostCase       (void) {}
1287
1288 private:
1289         virtual ObjectData      genOccluderGeometry                                     (void) const { return Utils::fastQuad(0.2f); }
1290         virtual ObjectData      genOccludedGeometry                                     (void) const { return Utils::quadWith(Utils::getDynamicFragDepthFragmentShader(), 0.2f); }
1291
1292         virtual void            logDescription                                          (void)
1293         {
1294                 TestLog& log = m_testCtx.getLog();
1295
1296                 log << TestLog::Section("Description", "Test description");
1297                 log << TestLog::Message << "Testing hidden fragment culling speed" << TestLog::EndMessage;
1298                 log << TestLog::Message << "Geometry consists of two fullsceen quads. The first (occluding) is rendered once, the second (occluded) is rendered repeatedly" << TestLog::EndMessage;
1299                 log << TestLog::Message << "Workload indicates the number of times the occluded quad is rendered" << TestLog::EndMessage;
1300                 log << TestLog::Message << "The occluded quad has a dynamic offset applied to gl_FragDepth" << TestLog::EndMessage;
1301                 log << TestLog::Message << "The time per culled pixel is estimated from the rate of change of rendering time as a function of workload"  << TestLog::EndMessage;
1302                 log << TestLog::EndSection;
1303         }
1304 };
1305
1306 // Culling speed with slightly less trivial geometry
1307 class OccludingGeometryComplexityCostCase : public RenderCountCase
1308 {
1309 public:
1310                                                 OccludingGeometryComplexityCostCase             (TestContext&                   testCtx,
1311                                                                                                                                  const RenderContext&   renderCtx,
1312                                                                                                                                  const char*                    name,
1313                                                                                                                                  const char*                    desc,
1314                                                                                                                                  int                                    resolution,
1315                                                                                                                                  float                                  xyNoise,
1316                                                                                                                                  float                                  zNoise)
1317                                                         : RenderCountCase       (testCtx, renderCtx, name, desc)
1318                                                         , m_resolution          (resolution)
1319                                                         , m_xyNoise                     (xyNoise)
1320                                                         , m_zNoise                      (zNoise)
1321                                                 {
1322                                                 }
1323
1324                                                 ~OccludingGeometryComplexityCostCase    (void) {}
1325
1326 private:
1327         virtual ObjectData      genOccluderGeometry                                             (void) const
1328         {
1329                 return ObjectData(Utils::getBaseShader(),
1330                                                   Utils::getFullScreenGrid(m_resolution,
1331                                                   deInt32Hash(deStringHash(getName())) ^ m_testCtx.getCommandLine().getBaseSeed(),
1332                                                   0.2f,
1333                                                   m_zNoise,
1334                                                   m_xyNoise));
1335         }
1336
1337         virtual ObjectData      genOccludedGeometry                                             (void) const { return Utils::variableQuad(0.8f); }
1338
1339         virtual void            logDescription          (void)
1340         {
1341                 TestLog& log = m_testCtx.getLog();
1342
1343                 log << TestLog::Section("Description", "Test description");
1344                 log << TestLog::Message << "Testing hidden fragment culling speed" << TestLog::EndMessage;
1345                 log << TestLog::Message << "Geometry consists of an occluding grid and an occluded fullsceen quad. The occluding geometry is rendered once, the occluded one is rendered repeatedly" << TestLog::EndMessage;
1346                 log << TestLog::Message << "Workload indicates the number of times the occluded quad is rendered"  << TestLog::EndMessage;
1347                 log << TestLog::Message << "The time per culled pixel is estimated from the rate of change of rendering time as a function of workload"  << TestLog::EndMessage;
1348                 log << TestLog::EndSection;
1349         }
1350
1351         const int                       m_resolution;
1352         const float                     m_xyNoise;
1353         const float                     m_zNoise;
1354 };
1355
1356
1357 // Cases with varying workloads in the fragment shader
1358 class FragmentWorkloadCullCase : public RelativeChangeCase
1359 {
1360 public:
1361                                                 FragmentWorkloadCullCase        (TestContext& testCtx, const RenderContext& renderCtx, const char* name, const char* desc);
1362         virtual                         ~FragmentWorkloadCullCase       (void) {}
1363
1364 private:
1365         virtual ObjectData      genOccluderGeometry                     (void) const { return Utils::fastQuad(0.2f); }
1366
1367         virtual void            logDescription                          (void);
1368 };
1369
1370 FragmentWorkloadCullCase::FragmentWorkloadCullCase (TestContext& testCtx, const RenderContext& renderCtx, const char* name, const char* desc)
1371         : RelativeChangeCase    (testCtx, renderCtx, name, desc)
1372 {
1373 }
1374
1375 void FragmentWorkloadCullCase::logDescription (void)
1376 {
1377         TestLog& log = m_testCtx.getLog();
1378
1379         log << TestLog::Section("Description", "Test description");
1380         log << TestLog::Message << "Testing effects of culled fragment workload on render time" << TestLog::EndMessage;
1381         log << TestLog::Message << "Geometry consists of two fullsceen quads. The first (occluding) quad uses a trivial shader,"
1382                 "the second (occluded) contains significant fragment shader work" << TestLog::EndMessage;
1383         log << TestLog::Message << "Workload indicates the number of iterations of dummy work done in the occluded quad's fragment shader"  << TestLog::EndMessage;
1384         log << TestLog::Message << "The ratio of rendering times of this scene with/without depth testing are compared"  << TestLog::EndMessage;
1385         log << TestLog::Message << "Successfull early Z-testing should result in no correlation between workload and render time"  << TestLog::EndMessage;
1386         log << TestLog::EndSection;
1387 }
1388
1389 // Additional workload consists of texture lookups
1390 class FragmentTextureWorkloadCullCase : public FragmentWorkloadCullCase
1391 {
1392 public:
1393                                                 FragmentTextureWorkloadCullCase         (TestContext& testCtx, const RenderContext& renderCtx, const char* name, const char* desc);
1394         virtual                         ~FragmentTextureWorkloadCullCase        (void) {}
1395
1396         virtual void            init                                                            (void);
1397         virtual void            deinit                                                          (void);
1398
1399 private:
1400         typedef MovePtr<glu::Texture> TexPtr;
1401
1402         virtual ObjectData      genOccludedGeometry                                     (void) const
1403         {
1404                 return ObjectData(Utils::getTextureWorkloadShader(), Utils::getFullscreenQuad(0.8f));
1405         }
1406
1407         TexPtr                          m_texture;
1408 };
1409
1410 FragmentTextureWorkloadCullCase::FragmentTextureWorkloadCullCase (TestContext& testCtx, const RenderContext& renderCtx, const char* name, const char* desc)
1411         : FragmentWorkloadCullCase      (testCtx, renderCtx, name, desc)
1412 {
1413 }
1414
1415 void FragmentTextureWorkloadCullCase::init (void)
1416 {
1417         const glw::Functions&   gl              = m_renderCtx.getFunctions();
1418         const int                               size    = 128;
1419         const vector<deUint8>   data    (size*size*4, 255);
1420
1421         m_texture = MovePtr<glu::Texture>(new glu::Texture(gl));
1422
1423         gl.bindTexture(GL_TEXTURE_2D, m_texture);
1424         gl.texImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, size, size, 0, GL_RGBA, GL_UNSIGNED_BYTE, &data[0]);
1425         gl.texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
1426         gl.texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
1427 }
1428
1429 void FragmentTextureWorkloadCullCase::deinit (void)
1430 {
1431         m_texture.clear();
1432 }
1433
1434 // Additional workload consists of arithmetic
1435 class FragmentArithmeticWorkloadCullCase : public FragmentWorkloadCullCase
1436 {
1437 public:
1438                                                 FragmentArithmeticWorkloadCullCase      (TestContext& testCtx, const RenderContext& renderCtx, const char* name, const char* desc)
1439                                                 : FragmentWorkloadCullCase      (testCtx, renderCtx, name, desc)
1440                                         {
1441                                         }
1442         virtual                         ~FragmentArithmeticWorkloadCullCase     (void) {}
1443
1444 private:
1445         virtual ObjectData      genOccludedGeometry                                     (void) const
1446         {
1447                 return ObjectData(Utils::getArithmeticWorkloadShader(), Utils::getFullscreenQuad(0.8f));
1448         }
1449 };
1450
1451 // Contains dynamicly unused discard after a series of calculations
1452 class FragmentDiscardArithmeticWorkloadCullCase : public FragmentWorkloadCullCase
1453 {
1454 public:
1455                                                 FragmentDiscardArithmeticWorkloadCullCase       (TestContext& testCtx, const RenderContext& renderCtx, const char* name, const char* desc)
1456                                                 : FragmentWorkloadCullCase      (testCtx, renderCtx, name, desc)
1457                                         {
1458                                         }
1459
1460         virtual                         ~FragmentDiscardArithmeticWorkloadCullCase      (void) {}
1461
1462 private:
1463         virtual ObjectData      genOccludedGeometry                                                     (void) const
1464         {
1465                 return ObjectData(Utils::getArithmeticWorkloadDiscardShader(), Utils::getFullscreenQuad(0.8f));
1466         }
1467
1468         virtual void            logDescription                                                          (void)
1469         {
1470                 TestLog& log = m_testCtx.getLog();
1471
1472                 log << TestLog::Section("Description", "Test description");
1473                 log << TestLog::Message << "Testing effects of culled fragment workload on render time" << TestLog::EndMessage;
1474                 log << TestLog::Message << "Geometry consists of two fullsceen quads. The first (occluding) quad uses a trivial shader,"
1475                         "the second (occluded) contains significant fragment shader work and a discard that is never triggers but has a dynamic condition" << TestLog::EndMessage;
1476                 log << TestLog::Message << "Workload indicates the number of iterations of dummy work done in the occluded quad's fragment shader"  << TestLog::EndMessage;
1477                 log << TestLog::Message << "The ratio of rendering times of this scene with/without depth testing are compared"  << TestLog::EndMessage;
1478                 log << TestLog::Message << "Successfull early Z-testing should result in no correlation between workload and render time"  << TestLog::EndMessage;
1479                 log << TestLog::EndSection;
1480         }
1481 };
1482
1483 // Discards fragments from the occluder in a grid pattern
1484 class PartialOccluderDiscardCullCase : public RelativeChangeCase
1485 {
1486 public:
1487                                                 PartialOccluderDiscardCullCase  (TestContext& testCtx, const RenderContext& renderCtx, const char* name, const char* desc, int gridsize)
1488                                                         : RelativeChangeCase            (testCtx, renderCtx, name, desc)
1489                                                         , m_gridsize    (gridsize)
1490                                                 {
1491                                                 }
1492         virtual                         ~PartialOccluderDiscardCullCase (void) {}
1493
1494 private:
1495         virtual ObjectData      genOccluderGeometry                             (void) const { return Utils::quadWith(Utils::getGridDiscardShader(m_gridsize), 0.2f); }
1496         virtual ObjectData      genOccludedGeometry                             (void) const { return Utils::slowQuad(0.8f); }
1497
1498         virtual void            logDescription                                  (void)
1499         {
1500                 TestLog& log = m_testCtx.getLog();
1501
1502                 log << TestLog::Section("Description", "Test description");
1503                 log << TestLog::Message << "Testing effects of partially discarded occluder on rendering time" << TestLog::EndMessage;
1504                 log << TestLog::Message << "Geometry consists of two fullsceen quads. The first (occluding) quad discards half the "
1505                         "fragments in a grid pattern, the second (partially occluded) contains significant fragment shader work" << TestLog::EndMessage;
1506                 log << TestLog::Message << "Workload indicates the number of iterations of dummy work done in the occluded quad's fragment shader"  << TestLog::EndMessage;
1507                 log << TestLog::Message << "The ratio of rendering times of this scene with/without depth testing are compared"  << TestLog::EndMessage;
1508                 log << TestLog::Message << "Successfull early Z-testing should result in depth testing halving the render time"  << TestLog::EndMessage;
1509                 log << TestLog::EndSection;
1510         }
1511
1512         const int                       m_gridsize;
1513 };
1514
1515 // Trivial occluder covering part of screen
1516 class PartialOccluderCullCase : public RelativeChangeCase
1517 {
1518 public:
1519                                                 PartialOccluderCullCase         (TestContext& testCtx, const RenderContext& renderCtx, const char* name, const char* desc, float coverage)
1520                                                         : RelativeChangeCase            (testCtx, renderCtx, name, desc)
1521                                                         , m_coverage    (coverage)
1522                                                 {
1523                                                 }
1524                                                 ~PartialOccluderCullCase        (void) {}
1525
1526 private:
1527         virtual ObjectData      genOccluderGeometry                     (void) const { return ObjectData(Utils::getBaseShader(), Utils::getPartScreenQuad(m_coverage, 0.2f)); }
1528         virtual ObjectData      genOccludedGeometry                     (void) const {return Utils::slowQuad(0.8f); }
1529
1530         virtual void            logDescription                          (void)
1531         {
1532                 TestLog& log = m_testCtx.getLog();
1533
1534                 log << TestLog::Section("Description", "Test description");
1535                 log << TestLog::Message << "Testing effects of partial occluder on rendering time" << TestLog::EndMessage;
1536                 log << TestLog::Message << "Geometry consists of two quads. The first (occluding) quad covers " << m_coverage*100.0f
1537                         << "% of the screen, while the second (partially occluded, fullscreen) contains significant fragment shader work" << TestLog::EndMessage;
1538                 log << TestLog::Message << "Workload indicates the number of iterations of dummy work done in the occluded quad's fragment shader"  << TestLog::EndMessage;
1539                 log << TestLog::Message << "The ratio of rendering times of this scene with/without depth testing are compared"  << TestLog::EndMessage;
1540                 log << TestLog::Message << "Successfull early Z-testing should result in render time increasing proportionally with unoccluded area"  << TestLog::EndMessage;
1541                 log << TestLog::EndSection;
1542         }
1543
1544         const float                     m_coverage;
1545 };
1546
1547 // Constant offset to frag depth in occluder
1548 class StaticOccluderFragDepthCullCase : public RelativeChangeCase
1549 {
1550 public:
1551                                                 StaticOccluderFragDepthCullCase         (TestContext& testCtx, const RenderContext& renderCtx, const char* name, const char* desc)
1552                                                         : RelativeChangeCase(testCtx, renderCtx, name, desc)
1553                                                 {
1554                                                 }
1555
1556                                                 ~StaticOccluderFragDepthCullCase        (void) {}
1557
1558 private:
1559         virtual ObjectData      genOccluderGeometry                                     (void) const { return Utils::quadWith(Utils::getStaticFragDepthFragmentShader(), 0.2f); }
1560         virtual ObjectData      genOccludedGeometry                                     (void) const { return Utils::slowQuad(0.8f); }
1561
1562         virtual void            logDescription                                          (void)
1563         {
1564                 TestLog& log = m_testCtx.getLog();
1565
1566                 log << TestLog::Section("Description", "Test description");
1567                 log << TestLog::Message << "Testing effects of non-default frag depth on culling efficiency" << TestLog::EndMessage;
1568                 log << TestLog::Message << "Geometry consists of two fullscreen quads. The first (occluding) quad is trivial, while the second (occluded) contains significant fragment shader work" << TestLog::EndMessage;
1569                 log << TestLog::Message << "Workload indicates the number of iterations of dummy work done in the occluded quad's fragment shader"  << TestLog::EndMessage;
1570                 log << TestLog::Message << "The occluder quad has a static offset applied to gl_FragDepth" << TestLog::EndMessage;
1571                 log << TestLog::Message << "The ratio of rendering times of this scene with/without depth testing are compared"  << TestLog::EndMessage;
1572                 log << TestLog::Message << "Successfull early Z-testing should result in no correlation between workload and render time"  << TestLog::EndMessage;
1573                 log << TestLog::EndSection;
1574         }
1575 };
1576
1577 // Dynamic offset to frag depth in occluder
1578 class DynamicOccluderFragDepthCullCase : public RelativeChangeCase
1579 {
1580 public:
1581                                                 DynamicOccluderFragDepthCullCase        (TestContext& testCtx, const RenderContext& renderCtx, const char* name, const char* desc)
1582                                                         : RelativeChangeCase(testCtx, renderCtx, name, desc)
1583                                                 {
1584                                                 }
1585
1586                                                 ~DynamicOccluderFragDepthCullCase       (void) {}
1587
1588 private:
1589         virtual ObjectData      genOccluderGeometry                                     (void) const { return Utils::quadWith(Utils::getDynamicFragDepthFragmentShader(), 0.2f); }
1590         virtual ObjectData      genOccludedGeometry                                     (void) const { return Utils::slowQuad(0.8f); }
1591
1592         virtual void            logDescription                                          (void)
1593         {
1594                 TestLog& log = m_testCtx.getLog();
1595
1596                 log << TestLog::Section("Description", "Test description");
1597                 log << TestLog::Message << "Testing effects of non-default frag depth on culling efficiency" << TestLog::EndMessage;
1598                 log << TestLog::Message << "Geometry consists of two fullscreen quads. The first (occluding) quad is trivial, while the second (occluded) contains significant fragment shader work" << TestLog::EndMessage;
1599                 log << TestLog::Message << "Workload indicates the number of iterations of dummy work done in the occluded quad's fragment shader"  << TestLog::EndMessage;
1600                 log << TestLog::Message << "The occluder quad has a dynamic offset applied to gl_FragDepth" << TestLog::EndMessage;
1601                 log << TestLog::Message << "The ratio of rendering times of this scene with/without depth testing are compared"  << TestLog::EndMessage;
1602                 log << TestLog::Message << "Successfull early Z-testing should result in no correlation between workload and render time"  << TestLog::EndMessage;
1603                 log << TestLog::EndSection;
1604         }
1605 };
1606
1607 // Constant offset to frag depth in occluded
1608 class StaticOccludedFragDepthCullCase : public RelativeChangeCase
1609 {
1610 public:
1611                                                 StaticOccludedFragDepthCullCase (TestContext& testCtx, const RenderContext& renderCtx, const char* name, const char* desc)
1612                                                         : RelativeChangeCase(testCtx, renderCtx, name, desc)
1613                                                 {
1614                                                 }
1615
1616                                                 ~StaticOccludedFragDepthCullCase        (void) {}
1617
1618 private:
1619         virtual ObjectData      genOccluderGeometry                                     (void) const { return Utils::fastQuad(0.2f); }
1620         virtual ObjectData      genOccludedGeometry                                     (void) const { return Utils::quadWith(Utils::getStaticFragDepthArithmeticWorkloadFragmentShader(), 0.2f); }
1621
1622         virtual void            logDescription                                          (void)
1623         {
1624                 TestLog& log = m_testCtx.getLog();
1625
1626                 log << TestLog::Section("Description", "Test description");
1627                 log << TestLog::Message << "Testing effects of non-default frag depth on rendering time" << TestLog::EndMessage;
1628                 log << TestLog::Message << "Geometry consists of two fullscreen quads. The first (occluding) quad is trivial, while the second (occluded) contains significant fragment shader work" << TestLog::EndMessage;
1629                 log << TestLog::Message << "Workload indicates the number of iterations of dummy work done in the occluded quad's fragment shader"  << TestLog::EndMessage;
1630                 log << TestLog::Message << "The occluded quad has a static offset applied to gl_FragDepth" << TestLog::EndMessage;
1631                 log << TestLog::Message << "The ratio of rendering times of this scene with/without depth testing are compared"  << TestLog::EndMessage;
1632                 log << TestLog::Message << "Successfull early Z-testing should result in no correlation between workload and render time"  << TestLog::EndMessage;
1633                 log << TestLog::EndSection;
1634         }
1635 };
1636
1637 // Dynamic offset to frag depth in occluded
1638 class DynamicOccludedFragDepthCullCase : public RelativeChangeCase
1639 {
1640 public:
1641                                                 DynamicOccludedFragDepthCullCase        (TestContext& testCtx, const RenderContext& renderCtx, const char* name, const char* desc)
1642                                                         : RelativeChangeCase(testCtx, renderCtx, name, desc)
1643                                                 {
1644                                                 }
1645
1646                                                 ~DynamicOccludedFragDepthCullCase       (void) {}
1647
1648 private:
1649         virtual ObjectData      genOccluderGeometry                                     (void) const { return Utils::fastQuad(0.2f); }
1650         virtual ObjectData      genOccludedGeometry                                     (void) const { return Utils::quadWith(Utils::getDynamicFragDepthArithmeticWorkloadFragmentShader(), 0.2f); }
1651
1652         virtual void            logDescription                                          (void)
1653         {
1654                 TestLog& log = m_testCtx.getLog();
1655
1656                 log << TestLog::Section("Description", "Test description");
1657                 log << TestLog::Message << "Testing effects of non-default frag depth on rendering time" << TestLog::EndMessage;
1658                 log << TestLog::Message << "Geometry consists of two fullscreen quads. The first (occluding) quad is trivial, while the second (occluded) contains significant fragment shader work" << TestLog::EndMessage;
1659                 log << TestLog::Message << "Workload indicates the number of iterations of dummy work done in the occluded quad's fragment shader"  << TestLog::EndMessage;
1660                 log << TestLog::Message << "The occluded quad has a dynamic offset applied to gl_FragDepth" << TestLog::EndMessage;
1661                 log << TestLog::Message << "The ratio of rendering times of this scene with/without depth testing are compared"  << TestLog::EndMessage;
1662                 log << TestLog::Message << "Successfull early Z-testing should result in no correlation between workload and render time"  << TestLog::EndMessage;
1663                 log << TestLog::EndSection;
1664         }
1665 };
1666
1667 // Dynamic offset to frag depth in occluded
1668 class ReversedDepthOrderCullCase : public RelativeChangeCase
1669 {
1670 public:
1671                                                 ReversedDepthOrderCullCase      (TestContext& testCtx, const RenderContext& renderCtx, const char* name, const char* desc)
1672                                                         : RelativeChangeCase(testCtx, renderCtx, name, desc)
1673                                                 {
1674                                                 }
1675
1676                                                 ~ReversedDepthOrderCullCase     (void) {}
1677
1678 private:
1679         virtual ObjectData      genOccluderGeometry                     (void) const { return Utils::fastQuad(0.2f); }
1680         virtual ObjectData      genOccludedGeometry                     (void) const { return Utils::slowQuad(0.8f); }
1681
1682         virtual void            logDescription                          (void)
1683         {
1684                 TestLog& log = m_testCtx.getLog();
1685
1686                 log << TestLog::Section("Description", "Test description");
1687                 log << TestLog::Message << "Testing effects of of back first rendering order on culling efficiency" << TestLog::EndMessage;
1688                 log << TestLog::Message << "Geometry consists of two fullscreen quads. The second (occluding) quad is trivial, while the first (occluded) contains significant fragment shader work" << TestLog::EndMessage;
1689                 log << TestLog::Message << "Workload indicates the number of iterations of dummy work done in the occluded quad's fragment shader"  << TestLog::EndMessage;
1690                 log << TestLog::Message << "The ratio of rendering times of this scene with/without depth testing are compared"  << TestLog::EndMessage;
1691                 log << TestLog::Message << "Successfull early Z-testing should result in no correlation between workload and render time"  << TestLog::EndMessage;
1692                 log << TestLog::EndSection;
1693         }
1694
1695         // Rendering order of occluder & occluded is reversed, otherwise identical to parent version
1696         Sample                          renderSample                            (const RenderData& occluder, const RenderData& occluded, int workload) const
1697         {
1698                 const glw::Functions&   gl              = m_renderCtx.getFunctions();
1699                 const GLuint                    program = occluded.m_program.getProgram();
1700                 Sample                                  sample;
1701                 deUint64                                now             = 0;
1702                 deUint64                                prev    = 0;
1703                 deUint8                                 buffer[4];
1704
1705                 gl.useProgram(program);
1706                 gl.uniform1i(gl.getUniformLocation(program, "u_iterations"), workload);
1707
1708                 // Warmup (this workload seems to reduce variation in following workloads)
1709                 {
1710                         gl.clear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
1711                         gl.disable(GL_DEPTH_TEST);
1712
1713                         gl.readPixels(0, 0, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, buffer);
1714                 }
1715
1716                 // Null time
1717                 {
1718                         prev = deGetMicroseconds();
1719
1720                         gl.clear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
1721                         gl.disable(GL_DEPTH_TEST);
1722
1723                         gl.readPixels(0, 0, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, buffer);
1724
1725                         now = deGetMicroseconds();
1726
1727                         sample.nullTime = now - prev;
1728                 }
1729
1730                 // Test time
1731                 {
1732                         prev = deGetMicroseconds();
1733
1734                         gl.clear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
1735                         gl.enable(GL_DEPTH_TEST);
1736
1737                         render(occluded);
1738                         render(occluder);
1739
1740                         gl.readPixels(0, 0, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, buffer);
1741
1742                         now = deGetMicroseconds();
1743
1744                         sample.testTime = now - prev;
1745                 }
1746
1747                 // Base time
1748                 {
1749                         prev = deGetMicroseconds();
1750
1751                         gl.clear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
1752                         gl.disable(GL_DEPTH_TEST);
1753
1754                         render(occluded);
1755                         render(occluder);
1756
1757                         gl.readPixels(0, 0, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, buffer);
1758
1759                         now = deGetMicroseconds();
1760
1761                         sample.baseTime = now - prev;
1762                 }
1763
1764                 sample.workload = 0;
1765
1766                 return sample;
1767         }
1768 };
1769
1770 } // Anonymous
1771
1772 DepthTests::DepthTests (Context& context)
1773         : TestCaseGroup (context, "depth", "Depth culling performance")
1774 {
1775 }
1776
1777 void DepthTests::init (void)
1778 {
1779         TestContext&                    testCtx         = m_context.getTestContext();
1780         const RenderContext&    renderCtx       = m_context.getRenderContext();
1781
1782         {
1783                 tcu::TestCaseGroup* const cullEfficiencyGroup = new tcu::TestCaseGroup(m_testCtx, "cull_efficiency", "Fragment cull efficiency");
1784
1785                 addChild(cullEfficiencyGroup);
1786
1787                 {
1788                         tcu::TestCaseGroup* const group = new tcu::TestCaseGroup(m_testCtx, "workload", "Workload");
1789
1790                         cullEfficiencyGroup->addChild(group);
1791
1792                         group->addChild(new FragmentTextureWorkloadCullCase(                    testCtx, renderCtx, "workload_texture",                         "Fragment shader with texture lookup workload"));
1793                         group->addChild(new FragmentArithmeticWorkloadCullCase(                 testCtx, renderCtx, "workload_arithmetic",                      "Fragment shader with arithmetic workload"));
1794                         group->addChild(new FragmentDiscardArithmeticWorkloadCullCase(  testCtx, renderCtx, "workload_arithmetic_discard",      "Fragment shader that may discard with arithmetic workload"));
1795                 }
1796
1797                 {
1798                         tcu::TestCaseGroup* const group = new tcu::TestCaseGroup(m_testCtx, "occluder_discard", "Discard");
1799
1800                         cullEfficiencyGroup->addChild(group);
1801
1802                         group->addChild(new PartialOccluderDiscardCullCase(testCtx, renderCtx, "grid_256",      "Parts of occluder geometry discarded", 256));
1803                         group->addChild(new PartialOccluderDiscardCullCase(testCtx, renderCtx, "grid_128",      "Parts of occluder geometry discarded", 128));
1804                         group->addChild(new PartialOccluderDiscardCullCase(testCtx, renderCtx, "grid_64",       "Parts of occluder geometry discarded", 64));
1805                         group->addChild(new PartialOccluderDiscardCullCase(testCtx, renderCtx, "grid_32",       "Parts of occluder geometry discarded", 32));
1806                         group->addChild(new PartialOccluderDiscardCullCase(testCtx, renderCtx, "grid_16",       "Parts of occluder geometry discarded", 16));
1807                         group->addChild(new PartialOccluderDiscardCullCase(testCtx, renderCtx, "grid_8",        "Parts of occluder geometry discarded", 8));
1808                         group->addChild(new PartialOccluderDiscardCullCase(testCtx, renderCtx, "grid_4",        "Parts of occluder geometry discarded", 4));
1809                         group->addChild(new PartialOccluderDiscardCullCase(testCtx, renderCtx, "grid_2",        "Parts of occluder geometry discarded", 2));
1810                         group->addChild(new PartialOccluderDiscardCullCase(testCtx, renderCtx, "grid_1",        "Parts of occluder geometry discarded", 1));
1811                 }
1812
1813                 {
1814                         tcu::TestCaseGroup* const group = new tcu::TestCaseGroup(m_testCtx, "partial_coverage", "Partial Coverage");
1815
1816                         cullEfficiencyGroup->addChild(group);
1817
1818                         group->addChild(new PartialOccluderCullCase(testCtx, renderCtx, "100", "Occluder covering only part of occluded geometry", 1.00f));
1819                         group->addChild(new PartialOccluderCullCase(testCtx, renderCtx, "099", "Occluder covering only part of occluded geometry", 0.99f));
1820                         group->addChild(new PartialOccluderCullCase(testCtx, renderCtx, "095", "Occluder covering only part of occluded geometry", 0.95f));
1821                         group->addChild(new PartialOccluderCullCase(testCtx, renderCtx, "090", "Occluder covering only part of occluded geometry", 0.90f));
1822                         group->addChild(new PartialOccluderCullCase(testCtx, renderCtx, "080", "Occluder covering only part of occluded geometry", 0.80f));
1823                         group->addChild(new PartialOccluderCullCase(testCtx, renderCtx, "070", "Occluder covering only part of occluded geometry", 0.70f));
1824                         group->addChild(new PartialOccluderCullCase(testCtx, renderCtx, "050", "Occluder covering only part of occluded geometry", 0.50f));
1825                         group->addChild(new PartialOccluderCullCase(testCtx, renderCtx, "025", "Occluder covering only part of occluded geometry", 0.25f));
1826                         group->addChild(new PartialOccluderCullCase(testCtx, renderCtx, "010", "Occluder covering only part of occluded geometry", 0.10f));
1827                 }
1828
1829                 {
1830                         tcu::TestCaseGroup* const group = new tcu::TestCaseGroup(m_testCtx, "frag_depth", "Partial Coverage");
1831
1832                         cullEfficiencyGroup->addChild(group);
1833
1834                         group->addChild(new StaticOccluderFragDepthCullCase( testCtx, renderCtx, "occluder_static", ""));
1835                         group->addChild(new DynamicOccluderFragDepthCullCase(testCtx, renderCtx, "occluder_dynamic", ""));
1836                         group->addChild(new StaticOccludedFragDepthCullCase( testCtx, renderCtx, "occluded_static", ""));
1837                         group->addChild(new DynamicOccludedFragDepthCullCase(testCtx, renderCtx, "occluded_dynamic", ""));
1838                 }
1839
1840                 {
1841                         tcu::TestCaseGroup* const group = new tcu::TestCaseGroup(m_testCtx, "order", "Rendering order");
1842
1843                         cullEfficiencyGroup->addChild(group);
1844
1845                         group->addChild(new ReversedDepthOrderCullCase(testCtx, renderCtx, "reversed", "Back to front rendering order"));
1846                 }
1847         }
1848
1849         {
1850                 tcu::TestCaseGroup* const testCostGroup = new tcu::TestCaseGroup(m_testCtx, "culled_pixel_cost", "Fragment cull efficiency");
1851
1852                 addChild(testCostGroup);
1853
1854                 {
1855                         tcu::TestCaseGroup* const group = new tcu::TestCaseGroup(m_testCtx, "gradient", "Gradients with small depth differences");
1856
1857                         testCostGroup->addChild(group);
1858
1859                         group->addChild(new BaseCostCase(testCtx, renderCtx, "flat", ""));
1860                         group->addChild(new GradientCostCase(testCtx, renderCtx, "gradient_050", "", 0.50f));
1861                         group->addChild(new GradientCostCase(testCtx, renderCtx, "gradient_010", "", 0.10f));
1862                         group->addChild(new GradientCostCase(testCtx, renderCtx, "gradient_005", "", 0.05f));
1863                         group->addChild(new GradientCostCase(testCtx, renderCtx, "gradient_002", "", 0.02f));
1864                         group->addChild(new GradientCostCase(testCtx, renderCtx, "gradient_001", "", 0.01f));
1865                 }
1866
1867                 {
1868                         tcu::TestCaseGroup* const group = new tcu::TestCaseGroup(m_testCtx, "occluder_geometry", "Occluders with varying geometry complexity");
1869
1870                         testCostGroup->addChild(group);
1871
1872                         group->addChild(new OccludingGeometryComplexityCostCase(testCtx, renderCtx, "flat_uniform_grid_5",   "", 5,   0.0f, 0.0f));
1873                         group->addChild(new OccludingGeometryComplexityCostCase(testCtx, renderCtx, "flat_uniform_grid_15",  "", 15,  0.0f, 0.0f));
1874                         group->addChild(new OccludingGeometryComplexityCostCase(testCtx, renderCtx, "flat_uniform_grid_25",  "", 25,  0.0f, 0.0f));
1875                         group->addChild(new OccludingGeometryComplexityCostCase(testCtx, renderCtx, "flat_uniform_grid_50",  "", 50,  0.0f, 0.0f));
1876                         group->addChild(new OccludingGeometryComplexityCostCase(testCtx, renderCtx, "flat_uniform_grid_100", "", 100, 0.0f, 0.0f));
1877
1878                         group->addChild(new OccludingGeometryComplexityCostCase(testCtx, renderCtx, "flat_noisy_grid_5",   "", 5,   1.0f/5.0f,   0.0f));
1879                         group->addChild(new OccludingGeometryComplexityCostCase(testCtx, renderCtx, "flat_noisy_grid_15",  "", 15,  1.0f/15.0f,  0.0f));
1880                         group->addChild(new OccludingGeometryComplexityCostCase(testCtx, renderCtx, "flat_noisy_grid_25",  "", 25,  1.0f/25.0f,  0.0f));
1881                         group->addChild(new OccludingGeometryComplexityCostCase(testCtx, renderCtx, "flat_noisy_grid_50",  "", 50,  1.0f/50.0f,  0.0f));
1882                         group->addChild(new OccludingGeometryComplexityCostCase(testCtx, renderCtx, "flat_noisy_grid_100", "", 100, 1.0f/100.0f, 0.0f));
1883
1884                         group->addChild(new OccludingGeometryComplexityCostCase(testCtx, renderCtx, "uneven_uniform_grid_5",   "", 5,   0.0f, 0.2f));
1885                         group->addChild(new OccludingGeometryComplexityCostCase(testCtx, renderCtx, "uneven_uniform_grid_15",  "", 15,  0.0f, 0.2f));
1886                         group->addChild(new OccludingGeometryComplexityCostCase(testCtx, renderCtx, "uneven_uniform_grid_25",  "", 25,  0.0f, 0.2f));
1887                         group->addChild(new OccludingGeometryComplexityCostCase(testCtx, renderCtx, "uneven_uniform_grid_50",  "", 50,  0.0f, 0.2f));
1888                         group->addChild(new OccludingGeometryComplexityCostCase(testCtx, renderCtx, "uneven_uniform_grid_100", "", 100, 0.0f, 0.2f));
1889
1890                         group->addChild(new OccludingGeometryComplexityCostCase(testCtx, renderCtx, "uneven_noisy_grid_5",   "", 5,   1.0f/5.0f,   0.2f));
1891                         group->addChild(new OccludingGeometryComplexityCostCase(testCtx, renderCtx, "uneven_noisy_grid_15",  "", 15,  1.0f/15.0f,  0.2f));
1892                         group->addChild(new OccludingGeometryComplexityCostCase(testCtx, renderCtx, "uneven_noisy_grid_25",  "", 25,  1.0f/25.0f,  0.2f));
1893                         group->addChild(new OccludingGeometryComplexityCostCase(testCtx, renderCtx, "uneven_noisy_grid_50",  "", 50,  1.0f/50.0f,  0.2f));
1894                         group->addChild(new OccludingGeometryComplexityCostCase(testCtx, renderCtx, "uneven_noisy_grid_100", "", 100, 1.0f/100.0f, 0.2f));
1895                 }
1896
1897                 {
1898                         tcu::TestCaseGroup* const group = new tcu::TestCaseGroup(m_testCtx, "frag_depth", "Modifying gl_FragDepth");
1899
1900                         testCostGroup->addChild(group);
1901
1902                         group->addChild(new OccluderStaticFragDepthCostCase( testCtx, renderCtx, "occluder_static", ""));
1903                         group->addChild(new OccluderDynamicFragDepthCostCase(testCtx, renderCtx, "occluder_dynamic", ""));
1904                         group->addChild(new OccludedStaticFragDepthCostCase( testCtx, renderCtx, "occluded_static", ""));
1905                         group->addChild(new OccludedDynamicFragDepthCostCase(testCtx, renderCtx, "occluded_dynamic", ""));
1906                 }
1907         }
1908 }
1909
1910 } // Performance
1911 } // gles3
1912 } // deqp