| /*------------------------------------------------------------------------- |
| * drawElements Quality Program OpenGL ES 3.0 Module |
| * ------------------------------------------------- |
| * |
| * Copyright 2014 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| * |
| *//*! |
| * \file |
| * \brief Depth buffer performance tests. |
| *//*--------------------------------------------------------------------*/ |
| |
| #include "es3pDepthTests.hpp" |
| |
| #include "glsCalibration.hpp" |
| |
| #include "gluShaderProgram.hpp" |
| #include "gluObjectWrapper.hpp" |
| #include "gluPixelTransfer.hpp" |
| |
| #include "glwFunctions.hpp" |
| #include "glwEnums.hpp" |
| |
| #include "tcuTestLog.hpp" |
| #include "tcuStringTemplate.hpp" |
| #include "tcuCPUWarmup.hpp" |
| #include "tcuCommandLine.hpp" |
| #include "tcuResultCollector.hpp" |
| |
| #include "deClock.h" |
| #include "deString.h" |
| #include "deMath.h" |
| #include "deStringUtil.hpp" |
| #include "deRandom.hpp" |
| #include "deUniquePtr.hpp" |
| |
| #include <vector> |
| #include <algorithm> |
| |
| namespace deqp |
| { |
| namespace gles3 |
| { |
| namespace Performance |
| { |
| namespace |
| { |
| using namespace glw; |
| using de::MovePtr; |
| using tcu::TestContext; |
| using tcu::TestLog; |
| using tcu::Vec4; |
| using tcu::Vec3; |
| using tcu::Vec2; |
| using glu::RenderContext; |
| using glu::ProgramSources; |
| using glu::ShaderSource; |
| using std::vector; |
| using std::string; |
| using std::map; |
| |
| struct Sample |
| { |
| deInt64 nullTime; |
| deInt64 baseTime; |
| deInt64 testTime; |
| int order; |
| int workload; |
| }; |
| |
| struct SampleParams |
| { |
| int step; |
| int measurement; |
| |
| SampleParams(int step_, int measurement_) : step(step_), measurement(measurement_) {} |
| }; |
| |
| typedef vector<float> Geometry; |
| |
| struct ObjectData |
| { |
| ProgramSources shader; |
| Geometry geometry; |
| |
| ObjectData (const ProgramSources& shader_, const Geometry& geometry_) : shader(shader_), geometry(geometry_) {} |
| }; |
| |
| class RenderData |
| { |
| public: |
| RenderData (const ObjectData& object, const glu::RenderContext& renderCtx, TestLog& log); |
| ~RenderData (void) {}; |
| |
| const glu::ShaderProgram m_program; |
| const glu::VertexArray m_vao; |
| const glu::Buffer m_vbo; |
| |
| const int m_numVertices; |
| }; |
| |
| RenderData::RenderData (const ObjectData& object, const glu::RenderContext& renderCtx, TestLog& log) |
| : m_program (renderCtx, object.shader) |
| , m_vao (renderCtx.getFunctions()) |
| , m_vbo (renderCtx.getFunctions()) |
| , m_numVertices (int(object.geometry.size())/4) |
| { |
| const glw::Functions& gl = renderCtx.getFunctions(); |
| |
| if (!m_program.isOk()) |
| log << m_program; |
| |
| gl.bindBuffer(GL_ARRAY_BUFFER, *m_vbo); |
| gl.bufferData(GL_ARRAY_BUFFER, object.geometry.size() * sizeof(float), &object.geometry[0], GL_STATIC_DRAW); |
| gl.bindAttribLocation(m_program.getProgram(), 0, "a_position"); |
| |
| gl.bindVertexArray(*m_vao); |
| gl.vertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 0, DE_NULL); |
| gl.enableVertexAttribArray(0); |
| gl.bindVertexArray(0); |
| } |
| |
| namespace Utils |
| { |
| vector<float> getFullscreenQuad (float depth) |
| { |
| const float data[] = |
| { |
| +1.0f, +1.0f, depth, 0.0f, // .w is gl_VertexId%3 since Nexus 4&5 can't handle that on their own |
| +1.0f, -1.0f, depth, 1.0f, |
| -1.0f, -1.0f, depth, 2.0f, |
| -1.0f, -1.0f, depth, 0.0f, |
| -1.0f, +1.0f, depth, 1.0f, |
| +1.0f, +1.0f, depth, 2.0f, |
| }; |
| |
| return vector<float>(DE_ARRAY_BEGIN(data), DE_ARRAY_END(data)); |
| } |
| |
| vector<float> getFullscreenQuadWithGradient (float depth0, float depth1) |
| { |
| const float data[] = |
| { |
| +1.0f, +1.0f, depth0, 0.0f, |
| +1.0f, -1.0f, depth0, 1.0f, |
| -1.0f, -1.0f, depth1, 2.0f, |
| -1.0f, -1.0f, depth1, 0.0f, |
| -1.0f, +1.0f, depth1, 1.0f, |
| +1.0f, +1.0f, depth0, 2.0f, |
| }; |
| |
| return vector<float>(DE_ARRAY_BEGIN(data), DE_ARRAY_END(data)); |
| } |
| |
| vector<float> getPartScreenQuad (float coverage, float depth) |
| { |
| const float xMax = -1.0f + 2.0f*coverage; |
| const float data[] = |
| { |
| xMax, +1.0f, depth, 0.0f, |
| xMax, -1.0f, depth, 1.0f, |
| -1.0f, -1.0f, depth, 2.0f, |
| -1.0f, -1.0f, depth, 0.0f, |
| -1.0f, +1.0f, depth, 1.0f, |
| xMax, +1.0f, depth, 2.0f, |
| }; |
| |
| return vector<float>(DE_ARRAY_BEGIN(data), DE_ARRAY_END(data)); |
| } |
| |
| // Axis aligned grid. Depth of vertices is baseDepth +/- depthNoise |
| vector<float> getFullScreenGrid (int resolution, deUint32 seed, float baseDepth, float depthNoise, float xyNoise) |
| { |
| const int gridsize = resolution+1; |
| vector<Vec3> vertices (gridsize*gridsize); |
| vector<float> retval; |
| de::Random rng (seed); |
| |
| for (int y = 0; y < gridsize; y++) |
| for (int x = 0; x < gridsize; x++) |
| { |
| const bool isEdge = x == 0 || y == 0 || x == resolution || y == resolution; |
| const float x_ = float(x)/float(resolution)*2.0f - 1.0f + (isEdge ? 0.0f : rng.getFloat(-xyNoise, +xyNoise)); |
| const float y_ = float(y)/float(resolution)*2.0f - 1.0f + (isEdge ? 0.0f : rng.getFloat(-xyNoise, +xyNoise)); |
| const float z_ = baseDepth + rng.getFloat(-depthNoise, +depthNoise); |
| |
| vertices[y*gridsize + x] = Vec3(x_, y_, z_); |
| } |
| |
| retval.reserve(resolution*resolution*6); |
| |
| for (int y = 0; y < resolution; y++) |
| for (int x = 0; x < resolution; x++) |
| { |
| const Vec3& p0 = vertices[(y+0)*gridsize + (x+0)]; |
| const Vec3& p1 = vertices[(y+0)*gridsize + (x+1)]; |
| const Vec3& p2 = vertices[(y+1)*gridsize + (x+0)]; |
| const Vec3& p3 = vertices[(y+1)*gridsize + (x+1)]; |
| |
| const float temp[6*4] = |
| { |
| p0.x(), p0.y(), p0.z(), 0.0f, |
| p2.x(), p2.y(), p2.z(), 1.0f, |
| p1.x(), p1.y(), p1.z(), 2.0f, |
| |
| p3.x(), p3.y(), p3.z(), 0.0f, |
| p1.x(), p1.y(), p1.z(), 1.0f, |
| p2.x(), p2.y(), p2.z(), 2.0f, |
| }; |
| |
| retval.insert(retval.end(), DE_ARRAY_BEGIN(temp), DE_ARRAY_END(temp)); |
| } |
| |
| return retval; |
| } |
| |
| // Outputs barycentric coordinates as v_bcoords. Otherwise a passthrough shader |
| string getBaseVertexShader (void) |
| { |
| return "#version 300 es\n" |
| "in highp vec4 a_position;\n" |
| "out mediump vec3 v_bcoords;\n" |
| "void main()\n" |
| "{\n" |
| " v_bcoords = vec3(0, 0, 0);\n" |
| " v_bcoords[int(a_position.w)] = 1.0;\n" |
| " gl_Position = vec4(a_position.xyz, 1.0);\n" |
| "}\n"; |
| } |
| |
| // Adds noise to coordinates based on InstanceID Outputs barycentric coordinates as v_bcoords |
| string getInstanceNoiseVertexShader (void) |
| { |
| return "#version 300 es\n" |
| "in highp vec4 a_position;\n" |
| "out mediump vec3 v_bcoords;\n" |
| "void main()\n" |
| "{\n" |
| " v_bcoords = vec3(0, 0, 0);\n" |
| " v_bcoords[int(a_position.w)] = 1.0;\n" |
| " vec3 noise = vec3(sin(float(gl_InstanceID)*1.05), sin(float(gl_InstanceID)*1.23), sin(float(gl_InstanceID)*1.71));\n" |
| " gl_Position = vec4(a_position.xyz + noise * 0.005, 1.0);\n" |
| "}\n"; |
| } |
| |
| // Renders green triangles with edges highlighted. Exact shade depends on depth. |
| string getDepthAsGreenFragmentShader (void) |
| { |
| return "#version 300 es\n" |
| "in mediump vec3 v_bcoords;\n" |
| "out mediump vec4 fragColor;\n" |
| "void main()\n" |
| "{\n" |
| " mediump float d = gl_FragCoord.z;\n" |
| " if (v_bcoords.x < 0.02 || v_bcoords.y < 0.02 || v_bcoords.z < 0.02)\n" |
| " fragColor = vec4(d,1,d,1);\n" |
| " else\n" |
| " fragColor = vec4(0,d,0,1);\n" |
| "}\n"; |
| } |
| |
| // Renders green triangles with edges highlighted. Exact shade depends on depth. |
| string getDepthAsRedFragmentShader (void) |
| { |
| return "#version 300 es\n" |
| "in mediump vec3 v_bcoords;\n" |
| "out mediump vec4 fragColor;\n" |
| "void main()\n" |
| "{\n" |
| " mediump float d = gl_FragCoord.z;\n" |
| " if (v_bcoords.x < 0.02 || v_bcoords.y < 0.02 || v_bcoords.z < 0.02)\n" |
| " fragColor = vec4(1,d,d,1);\n" |
| " else\n" |
| " fragColor = vec4(d,0,0,1);\n" |
| "}\n"; |
| } |
| |
| // Basic time waster. Renders red triangles with edges highlighted. Exact shade depends on depth. |
| string getArithmeticWorkloadFragmentShader (void) |
| { |
| |
| return "#version 300 es\n" |
| "in mediump vec3 v_bcoords;\n" |
| "out mediump vec4 fragColor;\n" |
| "uniform mediump int u_iterations;\n" |
| "void main()\n" |
| "{\n" |
| " mediump float d = gl_FragCoord.z;\n" |
| " for (int i = 0; i<u_iterations; i++)\n" |
| // 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 |
| " d = d*sin(d)*sin(d) + d*cos(d)*cos(d);\n" |
| " if (v_bcoords.x < 0.02 || v_bcoords.y < 0.02 || v_bcoords.z < 0.02)\n" |
| " fragColor = vec4(1,d,d,1);\n" |
| " else\n" |
| " fragColor = vec4(d,0,0,1);\n" |
| "}\n"; |
| } |
| |
| // Arithmetic workload shader but contains discard |
| string getArithmeticWorkloadDiscardFragmentShader (void) |
| { |
| return "#version 300 es\n" |
| "in mediump vec3 v_bcoords;\n" |
| "out mediump vec4 fragColor;\n" |
| "uniform mediump int u_iterations;\n" |
| "void main()\n" |
| "{\n" |
| " mediump float d = gl_FragCoord.z;\n" |
| " for (int i = 0; i<u_iterations; i++)\n" |
| " d = d*sin(d)*sin(d) + d*cos(d)*cos(d);\n" |
| " if (d < 0.5) discard;\n" |
| " if (v_bcoords.x < 0.02 || v_bcoords.y < 0.02 || v_bcoords.z < 0.02)\n" |
| " fragColor = vec4(1,d,d,1);\n" |
| " else\n" |
| " fragColor = vec4(d,0,0,1);\n" |
| "}\n"; |
| } |
| |
| // Texture fetch based time waster. Renders red triangles with edges highlighted. Exact shade depends on depth. |
| string getTextureWorkloadFragmentShader (void) |
| { |
| return "#version 300 es\n" |
| "in mediump vec3 v_bcoords;\n" |
| "out mediump vec4 fragColor;\n" |
| "uniform mediump int u_iterations;\n" |
| "uniform sampler2D u_texture;\n" |
| "void main()\n" |
| "{\n" |
| " mediump float d = gl_FragCoord.z;\n" |
| " for (int i = 0; i<u_iterations; i++)\n" |
| " d *= texture(u_texture, (gl_FragCoord.xy+vec2(i))/512.0).r;\n" // Texture is expected to be fully white |
| " if (v_bcoords.x < 0.02 || v_bcoords.y < 0.02 || v_bcoords.z < 0.02)\n" |
| " fragColor = vec4(1,1,1,1);\n" |
| " else\n" |
| " fragColor = vec4(d,0,0,1);\n" |
| "}\n"; |
| } |
| |
| // Discard fragments in a grid pattern |
| string getGridDiscardFragmentShader (int gridsize) |
| { |
| const string fragSrc = "#version 300 es\n" |
| "in mediump vec3 v_bcoords;\n" |
| "out mediump vec4 fragColor;\n" |
| "void main()\n" |
| "{\n" |
| " mediump float d = gl_FragCoord.z;\n" |
| " if ((int(gl_FragCoord.x)/${GRIDRENDER_SIZE} + int(gl_FragCoord.y)/${GRIDRENDER_SIZE})%2 == 0)\n" |
| " discard;\n" |
| " if (v_bcoords.x < 0.02 || v_bcoords.y < 0.02 || v_bcoords.z < 0.02)\n" |
| " fragColor = vec4(d,1,d,1);\n" |
| " else\n" |
| " fragColor = vec4(0,d,0,1);\n" |
| "}\n"; |
| map<string, string> params; |
| |
| params["GRIDRENDER_SIZE"] = de::toString(gridsize); |
| |
| return tcu::StringTemplate(fragSrc).specialize(params); |
| } |
| |
| // A static increment to frag depth |
| string getStaticFragDepthFragmentShader (void) |
| { |
| return "#version 300 es\n" |
| "in mediump vec3 v_bcoords;\n" |
| "out mediump vec4 fragColor;\n" |
| "void main()\n" |
| "{\n" |
| " mediump float d = gl_FragCoord.z;\n" |
| " gl_FragDepth = gl_FragCoord.z + 0.1;\n" |
| " if (v_bcoords.x < 0.02 || v_bcoords.y < 0.02 || v_bcoords.z < 0.02)\n" |
| " fragColor = vec4(d,1,d,1);\n" |
| " else\n" |
| " fragColor = vec4(0,d,0,1);\n" |
| "}\n"; |
| } |
| |
| // A trivial dynamic change to frag depth |
| string getDynamicFragDepthFragmentShader (void) |
| { |
| return "#version 300 es\n" |
| "in mediump vec3 v_bcoords;\n" |
| "out mediump vec4 fragColor;\n" |
| "void main()\n" |
| "{\n" |
| " mediump float d = gl_FragCoord.z;\n" |
| " 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 |
| " if (v_bcoords.x < 0.02 || v_bcoords.y < 0.02 || v_bcoords.z < 0.02)\n" |
| " fragColor = vec4(d,1,d,1);\n" |
| " else\n" |
| " fragColor = vec4(0,d,0,1);\n" |
| "}\n"; |
| } |
| |
| // A static increment to frag depth |
| string getStaticFragDepthArithmeticWorkloadFragmentShader (void) |
| { |
| return "#version 300 es\n" |
| "in mediump vec3 v_bcoords;\n" |
| "out mediump vec4 fragColor;\n" |
| "uniform mediump int u_iterations;\n" |
| "void main()\n" |
| "{\n" |
| " mediump float d = gl_FragCoord.z;\n" |
| " gl_FragDepth = gl_FragCoord.z + 0.1;\n" |
| " for (int i = 0; i<u_iterations; i++)\n" |
| " d = d*sin(d)*sin(d) + d*cos(d)*cos(d);\n" |
| " if (v_bcoords.x < 0.02 || v_bcoords.y < 0.02 || v_bcoords.z < 0.02)\n" |
| " fragColor = vec4(1,d,d,1);\n" |
| " else\n" |
| " fragColor = vec4(d,0,0,1);\n" |
| "}\n"; |
| } |
| |
| // A trivial dynamic change to frag depth |
| string getDynamicFragDepthArithmeticWorkloadFragmentShader (void) |
| { |
| return "#version 300 es\n" |
| "in mediump vec3 v_bcoords;\n" |
| "out mediump vec4 fragColor;\n" |
| "uniform mediump int u_iterations;\n" |
| "void main()\n" |
| "{\n" |
| " mediump float d = gl_FragCoord.z;\n" |
| " 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 |
| " for (int i = 0; i<u_iterations; i++)\n" |
| " d = d*sin(d)*sin(d) + d*cos(d)*cos(d);\n" |
| " if (v_bcoords.x < 0.02 || v_bcoords.y < 0.02 || v_bcoords.z < 0.02)\n" |
| " fragColor = vec4(1,d,d,1);\n" |
| " else\n" |
| " fragColor = vec4(d,0,0,1);\n" |
| "}\n"; |
| } |
| |
| glu::ProgramSources getBaseShader (void) |
| { |
| return glu::makeVtxFragSources(getBaseVertexShader(), getDepthAsGreenFragmentShader()); |
| } |
| |
| glu::ProgramSources getArithmeticWorkloadShader (void) |
| { |
| return glu::makeVtxFragSources(getBaseVertexShader(), getArithmeticWorkloadFragmentShader()); |
| } |
| |
| glu::ProgramSources getArithmeticWorkloadDiscardShader (void) |
| { |
| return glu::makeVtxFragSources(getBaseVertexShader(), getArithmeticWorkloadDiscardFragmentShader()); |
| } |
| |
| glu::ProgramSources getTextureWorkloadShader (void) |
| { |
| return glu::makeVtxFragSources(getBaseVertexShader(), getTextureWorkloadFragmentShader()); |
| } |
| |
| glu::ProgramSources getGridDiscardShader (int gridsize) |
| { |
| return glu::makeVtxFragSources(getBaseVertexShader(), getGridDiscardFragmentShader(gridsize)); |
| } |
| |
| inline ObjectData quadWith (const glu::ProgramSources& shader, float depth) |
| { |
| return ObjectData(shader, getFullscreenQuad(depth)); |
| } |
| |
| inline ObjectData quadWith (const string& fragShader, float depth) |
| { |
| return ObjectData(glu::makeVtxFragSources(getBaseVertexShader(), fragShader), getFullscreenQuad(depth)); |
| } |
| |
| inline ObjectData variableQuad (float depth) |
| { |
| return ObjectData(glu::makeVtxFragSources(getInstanceNoiseVertexShader(), getDepthAsRedFragmentShader()), getFullscreenQuad(depth)); |
| } |
| |
| inline ObjectData fastQuad (float depth) |
| { |
| return ObjectData(getBaseShader(), getFullscreenQuad(depth)); |
| } |
| |
| inline ObjectData slowQuad (float depth) |
| { |
| return ObjectData(getArithmeticWorkloadShader(), getFullscreenQuad(depth)); |
| } |
| |
| inline ObjectData fastQuadWithGradient (float depth0, float depth1) |
| { |
| return ObjectData(getBaseShader(), getFullscreenQuadWithGradient(depth0, depth1)); |
| } |
| } // Utils |
| |
| // Shared base |
| class BaseCase : public tcu::TestCase |
| { |
| public: |
| enum {RENDER_SIZE = 512}; |
| |
| BaseCase (TestContext& testCtx, const RenderContext& renderCtx, const char* name, const char* desc); |
| virtual ~BaseCase (void) {} |
| |
| virtual IterateResult iterate (void); |
| |
| protected: |
| void logSamples (const vector<Sample>& samples, const string& name, const string& desc); |
| void logGeometry (const tcu::ConstPixelBufferAccess& sample, const glu::ShaderProgram& occluderProg, const glu::ShaderProgram& occludedProg); |
| virtual void logAnalysis (const vector<Sample>& samples) = 0; |
| virtual void logDescription (void) = 0; |
| |
| virtual ObjectData genOccluderGeometry (void) const = 0; |
| virtual ObjectData genOccludedGeometry (void) const = 0; |
| |
| virtual int calibrate (void) const = 0; |
| virtual Sample renderSample (const RenderData& occluder, const RenderData& occluded, int workload) const = 0; |
| |
| void render (const RenderData& data) const; |
| void render (const RenderData& data, int instances) const; |
| |
| const RenderContext& m_renderCtx; |
| tcu::ResultCollector m_results; |
| |
| enum {ITERATION_STEPS = 10, ITERATION_SAMPLES = 16}; |
| }; |
| |
| BaseCase::BaseCase (TestContext& testCtx, const RenderContext& renderCtx, const char* name, const char* desc) |
| : TestCase (testCtx, tcu::NODETYPE_PERFORMANCE, name, desc) |
| , m_renderCtx (renderCtx) |
| { |
| } |
| |
| BaseCase::IterateResult BaseCase::iterate (void) |
| { |
| typedef de::MovePtr<RenderData> RenderDataP; |
| |
| const glw::Functions& gl = m_renderCtx.getFunctions(); |
| TestLog& log = m_testCtx.getLog(); |
| |
| const glu::Framebuffer framebuffer (gl); |
| const glu::Renderbuffer renderbuffer (gl); |
| const glu::Renderbuffer depthbuffer (gl); |
| |
| vector<Sample> results; |
| vector<int> params; |
| RenderDataP occluderData; |
| RenderDataP occludedData; |
| tcu::TextureLevel resultTex (tcu::TextureFormat(tcu::TextureFormat::RGBA, tcu::TextureFormat::UNORM_INT8), RENDER_SIZE, RENDER_SIZE); |
| int maxWorkload = 0; |
| de::Random rng (deInt32Hash(deStringHash(getName())) ^ m_testCtx.getCommandLine().getBaseSeed()); |
| |
| logDescription(); |
| |
| gl.bindRenderbuffer(GL_RENDERBUFFER, *renderbuffer); |
| gl.renderbufferStorage(GL_RENDERBUFFER, GL_RGBA8, RENDER_SIZE, RENDER_SIZE); |
| gl.bindRenderbuffer(GL_RENDERBUFFER, *depthbuffer); |
| gl.renderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, RENDER_SIZE, RENDER_SIZE); |
| |
| gl.bindFramebuffer(GL_FRAMEBUFFER, *framebuffer); |
| gl.framebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, *renderbuffer); |
| gl.framebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, *depthbuffer); |
| gl.viewport(0, 0, RENDER_SIZE, RENDER_SIZE); |
| gl.clearColor(0.125f, 0.25f, 0.5f, 1.0f); |
| |
| maxWorkload = calibrate(); |
| |
| // Setup data |
| occluderData = RenderDataP(new RenderData (genOccluderGeometry(), m_renderCtx, log)); |
| occludedData = RenderDataP(new RenderData (genOccludedGeometry(), m_renderCtx, log)); |
| |
| TCU_CHECK(occluderData->m_program.isOk()); |
| TCU_CHECK(occludedData->m_program.isOk()); |
| |
| // Force initialization of GPU resources |
| gl.clear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); |
| gl.enable(GL_DEPTH_TEST); |
| |
| render(*occluderData); |
| render(*occludedData); |
| glu::readPixels(m_renderCtx, 0, 0, resultTex.getAccess()); |
| |
| logGeometry(resultTex.getAccess(), occluderData->m_program, occludedData->m_program); |
| |
| params.reserve(ITERATION_STEPS*ITERATION_SAMPLES); |
| |
| // Setup parameters |
| for (int step = 0; step < ITERATION_STEPS; step++) |
| { |
| const int workload = maxWorkload*step/ITERATION_STEPS; |
| |
| for (int count = 0; count < ITERATION_SAMPLES; count++) |
| params.push_back(workload); |
| } |
| |
| rng.shuffle(params.begin(), params.end()); |
| |
| // Render samples |
| for (size_t ndx = 0; ndx < params.size(); ndx++) |
| { |
| const int workload = params[ndx]; |
| Sample sample = renderSample(*occluderData, *occludedData, workload); |
| |
| sample.workload = workload; |
| sample.order = int(ndx); |
| |
| results.push_back(sample); |
| } |
| |
| logSamples(results, "Samples", "Samples"); |
| logAnalysis(results); |
| |
| m_results.setTestContextResult(m_testCtx); |
| |
| return STOP; |
| } |
| |
| void BaseCase::logSamples (const vector<Sample>& samples, const string& name, const string& desc) |
| { |
| TestLog& log = m_testCtx.getLog(); |
| |
| bool testOnly = true; |
| |
| for (size_t ndx = 0; ndx < samples.size(); ndx++) |
| { |
| if (samples[ndx].baseTime != 0 || samples[ndx].nullTime != 0) |
| { |
| testOnly = false; |
| break; |
| } |
| } |
| |
| log << TestLog::SampleList(name, desc); |
| |
| if (testOnly) |
| { |
| log << TestLog::SampleInfo |
| << TestLog::ValueInfo("Workload", "Workload", "", QP_SAMPLE_VALUE_TAG_PREDICTOR) |
| << TestLog::ValueInfo("Order", "Order of sample", "", QP_SAMPLE_VALUE_TAG_PREDICTOR) |
| << TestLog::ValueInfo("TestTime", "Test render time", "us", QP_SAMPLE_VALUE_TAG_RESPONSE) |
| << TestLog::EndSampleInfo; |
| |
| for (size_t sampleNdx = 0; sampleNdx < samples.size(); sampleNdx++) |
| { |
| const Sample& sample = samples[sampleNdx]; |
| |
| log << TestLog::Sample << sample.workload << sample.order << sample.testTime << TestLog::EndSample; |
| } |
| } |
| else |
| { |
| log << TestLog::SampleInfo |
| << TestLog::ValueInfo("Workload", "Workload", "", QP_SAMPLE_VALUE_TAG_PREDICTOR) |
| << TestLog::ValueInfo("Order", "Order of sample", "", QP_SAMPLE_VALUE_TAG_PREDICTOR) |
| << TestLog::ValueInfo("TestTime", "Test render time", "us", QP_SAMPLE_VALUE_TAG_RESPONSE) |
| << TestLog::ValueInfo("NullTime", "Read pixels time", "us", QP_SAMPLE_VALUE_TAG_RESPONSE) |
| << TestLog::ValueInfo("BaseTime", "Base render time", "us", QP_SAMPLE_VALUE_TAG_RESPONSE) |
| << TestLog::EndSampleInfo; |
| |
| for (size_t sampleNdx = 0; sampleNdx < samples.size(); sampleNdx++) |
| { |
| const Sample& sample = samples[sampleNdx]; |
| |
| log << TestLog::Sample << sample.workload << sample.order << sample.testTime << sample.nullTime << sample.baseTime << TestLog::EndSample; |
| } |
| } |
| |
| log << TestLog::EndSampleList; |
| } |
| |
| void BaseCase::logGeometry (const tcu::ConstPixelBufferAccess& sample, const glu::ShaderProgram& occluderProg, const glu::ShaderProgram& occludedProg) |
| { |
| TestLog& log = m_testCtx.getLog(); |
| |
| log << TestLog::Section("Geometry", "Geometry"); |
| log << TestLog::Message << "Occluding geometry is green with shade dependent on depth (rgb == 0, depth, 0)" << TestLog::EndMessage; |
| log << TestLog::Message << "Occluded geometry is red with shade dependent on depth (rgb == depth, 0, 0)" << TestLog::EndMessage; |
| log << TestLog::Message << "Primitive edges are a lighter shade of red/green" << TestLog::EndMessage; |
| |
| log << TestLog::Image("Test Geometry", "Test Geometry", sample); |
| log << TestLog::EndSection; |
| |
| log << TestLog::Section("Occluder", "Occluder"); |
| log << occluderProg; |
| log << TestLog::EndSection; |
| |
| log << TestLog::Section("Occluded", "Occluded"); |
| log << occludedProg; |
| log << TestLog::EndSection; |
| } |
| |
| void BaseCase::render (const RenderData& data) const |
| { |
| const glw::Functions& gl = m_renderCtx.getFunctions(); |
| |
| gl.useProgram(data.m_program.getProgram()); |
| |
| gl.bindVertexArray(*data.m_vao); |
| gl.drawArrays(GL_TRIANGLES, 0, data.m_numVertices); |
| gl.bindVertexArray(0); |
| } |
| |
| void BaseCase::render (const RenderData& data, int instances) const |
| { |
| const glw::Functions& gl = m_renderCtx.getFunctions(); |
| |
| gl.useProgram(data.m_program.getProgram()); |
| |
| gl.bindVertexArray(*data.m_vao); |
| gl.drawArraysInstanced(GL_TRIANGLES, 0, data.m_numVertices, instances); |
| gl.bindVertexArray(0); |
| } |
| |
| // Render occluder once, then repeatedly render occluded geometry. Sample with multiple repetition counts & establish time per call with linear regression |
| class RenderCountCase : public BaseCase |
| { |
| public: |
| RenderCountCase (TestContext& testCtx, const RenderContext& renderCtx, const char* name, const char* desc); |
| ~RenderCountCase (void) {} |
| |
| protected: |
| virtual void logAnalysis (const vector<Sample>& samples); |
| |
| private: |
| virtual int calibrate (void) const; |
| virtual Sample renderSample (const RenderData& occluder, const RenderData& occluded, int callcount) const; |
| }; |
| |
| RenderCountCase::RenderCountCase (TestContext& testCtx, const RenderContext& renderCtx, const char* name, const char* desc) |
| : BaseCase (testCtx, renderCtx, name, desc) |
| { |
| } |
| |
| void RenderCountCase::logAnalysis (const vector<Sample>& samples) |
| { |
| using namespace gls; |
| |
| TestLog& log = m_testCtx.getLog(); |
| int maxWorkload = 0; |
| vector<Vec2> testSamples (samples.size()); |
| |
| for (size_t ndx = 0; ndx < samples.size(); ndx++) |
| { |
| const Sample& sample = samples[ndx]; |
| |
| testSamples[ndx] = Vec2((float)sample.workload, (float)sample.testTime); |
| |
| maxWorkload = de::max(maxWorkload, sample.workload); |
| } |
| |
| { |
| const float confidence = 0.60f; |
| const LineParametersWithConfidence testParam = theilSenSiegelLinearRegression(testSamples, confidence); |
| const float usPerCall = testParam.coefficient; |
| const float pxPerCall = RENDER_SIZE*RENDER_SIZE; |
| const float pxPerUs = pxPerCall/usPerCall; |
| const float mpxPerS = pxPerUs; |
| |
| log << TestLog::Section("Linear Regression", "Linear Regression"); |
| log << TestLog::Message << "Offset & coefficient presented as [confidence interval min, estimate, confidence interval max]. Reported confidence interval for this test is " << confidence << TestLog::EndMessage; |
| log << TestLog::Message << "Render time for scene with depth test was\n\t" |
| << "[" << testParam.offsetConfidenceLower << ", " << testParam.offset << ", " << testParam.offsetConfidenceUpper << "]us +" |
| << "[" << testParam.coefficientConfidenceLower << ", " << testParam.coefficient << ", " << testParam.coefficientConfidenceUpper << "]" |
| << "us/workload" << TestLog::EndMessage; |
| log << TestLog::EndSection; |
| |
| log << TestLog::Section("Result", "Result"); |
| |
| if (testParam.coefficientConfidenceLower < 0.0f) |
| { |
| log << TestLog::Message << "Coefficient confidence bounds include values below 0.0, the operation likely has neglible per-pixel cost" << TestLog::EndMessage; |
| m_results.addResult(QP_TEST_RESULT_PASS, "Pass"); |
| } |
| else if (testParam.coefficientConfidenceLower < testParam.coefficientConfidenceUpper*0.25) |
| { |
| log << TestLog::Message << "Coefficient confidence range is extremely large, cannot give reliable result" << TestLog::EndMessage; |
| m_results.addResult(QP_TEST_RESULT_PASS, "Result confidence extremely low"); |
| } |
| else |
| { |
| log << TestLog::Message << "Culled hidden pixels @ " << mpxPerS << "Mpx/s" << TestLog::EndMessage; |
| m_results.addResult(QP_TEST_RESULT_PASS, de::floatToString(mpxPerS, 2)); |
| } |
| |
| log << TestLog::EndSection; |
| } |
| } |
| |
| Sample RenderCountCase::renderSample (const RenderData& occluder, const RenderData& occluded, int callcount) const |
| { |
| const glw::Functions& gl = m_renderCtx.getFunctions(); |
| Sample sample; |
| deUint64 now = 0; |
| deUint64 prev = 0; |
| deUint8 buffer[4]; |
| |
| // Stabilize |
| { |
| gl.clear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); |
| gl.enable(GL_DEPTH_TEST); |
| gl.readPixels(0, 0, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, buffer); |
| } |
| |
| prev = deGetMicroseconds(); |
| |
| gl.clear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); |
| gl.enable(GL_DEPTH_TEST); |
| |
| render(occluder); |
| render(occluded, callcount); |
| |
| gl.readPixels(0, 0, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, buffer); |
| |
| now = deGetMicroseconds(); |
| |
| sample.testTime = now - prev; |
| sample.baseTime = 0; |
| sample.nullTime = 0; |
| sample.workload = callcount; |
| |
| return sample; |
| } |
| |
| int RenderCountCase::calibrate (void) const |
| { |
| using namespace gls; |
| |
| const glw::Functions& gl = m_renderCtx.getFunctions(); |
| TestLog& log = m_testCtx.getLog(); |
| |
| const RenderData occluderGeometry (genOccluderGeometry(), m_renderCtx, log); |
| const RenderData occludedGeometry (genOccludedGeometry(), m_renderCtx, log); |
| |
| TheilSenCalibrator calibrator (CalibratorParameters(20, // Initial workload |
| 10, // Max iteration frames |
| 20.0f, // Iteration shortcut threshold ms |
| 20, // Max iterations |
| 33.0f, // Target frame time |
| 40.0f, // Frame time cap |
| 1000.0f // Target measurement duration |
| )); |
| |
| while (true) |
| { |
| switch(calibrator.getState()) |
| { |
| case TheilSenCalibrator::STATE_FINISHED: |
| logCalibrationInfo(m_testCtx.getLog(), calibrator); |
| return calibrator.getCallCount(); |
| |
| case TheilSenCalibrator::STATE_MEASURE: |
| { |
| deUint8 buffer[4]; |
| deInt64 now; |
| deInt64 prev; |
| |
| prev = deGetMicroseconds(); |
| |
| gl.clear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); |
| gl.disable(GL_DEPTH_TEST); |
| |
| render(occluderGeometry); |
| render(occludedGeometry, calibrator.getCallCount()); |
| |
| gl.readPixels(0, 0, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, buffer); |
| |
| now = deGetMicroseconds(); |
| |
| calibrator.recordIteration(now - prev); |
| break; |
| } |
| |
| case TheilSenCalibrator::STATE_RECOMPUTE_PARAMS: |
| calibrator.recomputeParameters(); |
| break; |
| default: |
| DE_ASSERT(false); |
| return 1; |
| } |
| } |
| } |
| |
| // Compares time/workload gradients of same geometry with and without depth testing |
| class RelativeChangeCase : public BaseCase |
| { |
| public: |
| RelativeChangeCase (TestContext& testCtx, const RenderContext& renderCtx, const char* name, const char* desc); |
| virtual ~RelativeChangeCase (void) {} |
| |
| protected: |
| Sample renderSample (const RenderData& occluder, const RenderData& occluded, int workload) const; |
| |
| virtual void logAnalysis (const vector<Sample>& samples); |
| |
| private: |
| int calibrate (void) const; |
| }; |
| |
| RelativeChangeCase::RelativeChangeCase (TestContext& testCtx, const RenderContext& renderCtx, const char* name, const char* desc) |
| : BaseCase (testCtx, renderCtx, name, desc) |
| { |
| } |
| |
| int RelativeChangeCase::calibrate (void) const |
| { |
| using namespace gls; |
| |
| const glw::Functions& gl = m_renderCtx.getFunctions(); |
| TestLog& log = m_testCtx.getLog(); |
| |
| const RenderData geom (genOccludedGeometry(), m_renderCtx, log); |
| |
| TheilSenCalibrator calibrator(CalibratorParameters( 20, // Initial workload |
| 10, // Max iteration frames |
| 20.0f, // Iteration shortcut threshold ms |
| 20, // Max iterations |
| 33.0f, // Target frame time |
| 40.0f, // Frame time cap |
| 1000.0f // Target measurement duration |
| )); |
| |
| while (true) |
| { |
| switch(calibrator.getState()) |
| { |
| case TheilSenCalibrator::STATE_FINISHED: |
| logCalibrationInfo(m_testCtx.getLog(), calibrator); |
| return calibrator.getCallCount(); |
| |
| case TheilSenCalibrator::STATE_MEASURE: |
| { |
| deUint8 buffer[4]; |
| const GLuint program = geom.m_program.getProgram(); |
| |
| gl.useProgram(program); |
| gl.uniform1i(gl.getUniformLocation(program, "u_iterations"), calibrator.getCallCount()); |
| |
| const deInt64 prev = deGetMicroseconds(); |
| |
| gl.clear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); |
| gl.disable(GL_DEPTH_TEST); |
| |
| render(geom); |
| |
| gl.readPixels(0, 0, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, buffer); |
| |
| const deInt64 now = deGetMicroseconds(); |
| |
| calibrator.recordIteration(now - prev); |
| break; |
| } |
| |
| case TheilSenCalibrator::STATE_RECOMPUTE_PARAMS: |
| calibrator.recomputeParameters(); |
| break; |
| default: |
| DE_ASSERT(false); |
| return 1; |
| } |
| } |
| } |
| |
| Sample RelativeChangeCase::renderSample (const RenderData& occluder, const RenderData& occluded, int workload) const |
| { |
| const glw::Functions& gl = m_renderCtx.getFunctions(); |
| const GLuint program = occluded.m_program.getProgram(); |
| Sample sample; |
| deUint64 now = 0; |
| deUint64 prev = 0; |
| deUint8 buffer[4]; |
| |
| gl.useProgram(program); |
| gl.uniform1i(gl.getUniformLocation(program, "u_iterations"), workload); |
| |
| // Warmup (this workload seems to reduce variation in following workloads) |
| { |
| gl.clear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); |
| gl.disable(GL_DEPTH_TEST); |
| |
| gl.readPixels(0, 0, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, buffer); |
| } |
| |
| // Null time |
| { |
| prev = deGetMicroseconds(); |
| |
| gl.clear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); |
| gl.disable(GL_DEPTH_TEST); |
| |
| gl.readPixels(0, 0, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, buffer); |
| |
| now = deGetMicroseconds(); |
| |
| sample.nullTime = now - prev; |
| } |
| |
| // Test time |
| { |
| prev = deGetMicroseconds(); |
| |
| gl.clear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); |
| gl.enable(GL_DEPTH_TEST); |
| |
| render(occluder); |
| render(occluded); |
| |
| gl.readPixels(0, 0, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, buffer); |
| |
| now = deGetMicroseconds(); |
| |
| sample.testTime = now - prev; |
| } |
| |
| // Base time |
| { |
| prev = deGetMicroseconds(); |
| |
| gl.clear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); |
| gl.disable(GL_DEPTH_TEST); |
| |
| render(occluder); |
| render(occluded); |
| |
| gl.readPixels(0, 0, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, buffer); |
| |
| now = deGetMicroseconds(); |
| |
| sample.baseTime = now - prev; |
| } |
| |
| sample.workload = 0; |
| |
| return sample; |
| } |
| |
| void RelativeChangeCase::logAnalysis (const vector<Sample>& samples) |
| { |
| using namespace gls; |
| |
| TestLog& log = m_testCtx.getLog(); |
| |
| int maxWorkload = 0; |
| |
| vector<Vec2> nullSamples (samples.size()); |
| vector<Vec2> baseSamples (samples.size()); |
| vector<Vec2> testSamples (samples.size()); |
| |
| for (size_t ndx = 0; ndx < samples.size(); ndx++) |
| { |
| const Sample& sample = samples[ndx]; |
| |
| nullSamples[ndx] = Vec2((float)sample.workload, (float)sample.nullTime); |
| baseSamples[ndx] = Vec2((float)sample.workload, (float)sample.baseTime); |
| testSamples[ndx] = Vec2((float)sample.workload, (float)sample.testTime); |
| |
| maxWorkload = de::max(maxWorkload, sample.workload); |
| } |
| |
| { |
| const float confidence = 0.60f; |
| |
| const LineParametersWithConfidence nullParam = theilSenSiegelLinearRegression(nullSamples, confidence); |
| const LineParametersWithConfidence baseParam = theilSenSiegelLinearRegression(baseSamples, confidence); |
| const LineParametersWithConfidence testParam = theilSenSiegelLinearRegression(testSamples, confidence); |
| |
| if (!de::inRange(0.0f, nullParam.coefficientConfidenceLower, nullParam.coefficientConfidenceUpper)) |
| { |
| m_results.addResult(QP_TEST_RESULT_FAIL, "Constant operation sequence duration not constant"); |
| log << TestLog::Message << "Constant operation sequence timing may vary as a function of workload. Result quality extremely low" << TestLog::EndMessage; |
| } |
| |
| if (de::inRange(0.0f, baseParam.coefficientConfidenceLower, baseParam.coefficientConfidenceUpper)) |
| { |
| m_results.addResult(QP_TEST_RESULT_FAIL, "Workload has no effect on duration"); |
| log << TestLog::Message << "Workload factor has no effect on duration of sample (smart optimizer?)" << TestLog::EndMessage; |
| } |
| |
| log << TestLog::Section("Linear Regression", "Linear Regression"); |
| log << TestLog::Message << "Offset & coefficient presented as [confidence interval min, estimate, confidence interval max]. Reported confidence interval for this test is " << confidence << TestLog::EndMessage; |
| |
| log << TestLog::Message << "Render time for empty scene was\n\t" |
| << "[" << nullParam.offsetConfidenceLower << ", " << nullParam.offset << ", " << nullParam.offsetConfidenceUpper << "]us +" |
| << "[" << nullParam.coefficientConfidenceLower << ", " << nullParam.coefficient << ", " << nullParam.coefficientConfidenceUpper << "]" |
| << "us/workload" << TestLog::EndMessage; |
| |
| log << TestLog::Message << "Render time for scene without depth test was\n\t" |
| << "[" << baseParam.offsetConfidenceLower << ", " << baseParam.offset << ", " << baseParam.offsetConfidenceUpper << "]us +" |
| << "[" << baseParam.coefficientConfidenceLower << ", " << baseParam.coefficient << ", " << baseParam.coefficientConfidenceUpper << "]" |
| << "us/workload" << TestLog::EndMessage; |
| |
| log << TestLog::Message << "Render time for scene with depth test was\n\t" |
| << "[" << testParam.offsetConfidenceLower << ", " << testParam.offset << ", " << testParam.offsetConfidenceUpper << "]us +" |
| << "[" << testParam.coefficientConfidenceLower << ", " << testParam.coefficient << ", " << testParam.coefficientConfidenceUpper << "]" |
| << "us/workload" << TestLog::EndMessage; |
| |
| log << TestLog::EndSection; |
| |
| if (de::inRange(0.0f, testParam.coefficientConfidenceLower, testParam.coefficientConfidenceUpper)) |
| { |
| log << TestLog::Message << "Test duration not dependent on culled workload" << TestLog::EndMessage; |
| m_results.addResult(QP_TEST_RESULT_PASS, "0.0"); |
| } |
| else if (testParam.coefficientConfidenceLower < testParam.coefficientConfidenceUpper*0.25) |
| { |
| log << TestLog::Message << "Coefficient confidence range is extremely large, cannot give reliable result" << TestLog::EndMessage; |
| m_results.addResult(QP_TEST_RESULT_PASS, "Result confidence extremely low"); |
| } |
| else if (baseParam.coefficientConfidenceLower < baseParam.coefficientConfidenceUpper*0.25) |
| { |
| log << TestLog::Message << "Coefficient confidence range for base render time is extremely large, cannot give reliable result" << TestLog::EndMessage; |
| m_results.addResult(QP_TEST_RESULT_PASS, "Result confidence extremely low"); |
| } |
| else |
| { |
| log << TestLog::Message << "Test duration is dependent on culled workload" << TestLog::EndMessage; |
| m_results.addResult(QP_TEST_RESULT_PASS, de::floatToString(de::abs(testParam.coefficient)/de::abs(baseParam.coefficient), 2)); |
| } |
| } |
| } |
| |
| // Speed of trivial culling |
| class BaseCostCase : public RenderCountCase |
| { |
| public: |
| BaseCostCase (TestContext& testCtx, const RenderContext& renderCtx, const char* name, const char* desc) |
| : RenderCountCase (testCtx, renderCtx, name, desc) {} |
| |
| ~BaseCostCase (void) {} |
| |
| private: |
| virtual ObjectData genOccluderGeometry (void) const { return Utils::fastQuad(0.2f); } |
| virtual ObjectData genOccludedGeometry (void) const { return Utils::variableQuad(0.8f); } |
| |
| virtual void logDescription (void) |
| { |
| TestLog& log = m_testCtx.getLog(); |
| |
| log << TestLog::Section("Description", "Test description"); |
| log << TestLog::Message << "Testing hidden fragment culling speed" << TestLog::EndMessage; |
| log << TestLog::Message << "Geometry consists of two fullsceen quads. The first (occluding) is rendered once, the second (occluded) is rendered repeatedly" << TestLog::EndMessage; |
| log << TestLog::Message << "Workload indicates the number of times the occluded quad is rendered" << TestLog::EndMessage; |
| 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; |
| log << TestLog::EndSection; |
| } |
| }; |
| |
| // Gradient |
| class GradientCostCase : public RenderCountCase |
| { |
| public: |
| GradientCostCase (TestContext& testCtx, const RenderContext& renderCtx, const char* name, const char* desc, float gradientDistance) |
| : RenderCountCase (testCtx, renderCtx, name, desc) |
| , m_gradientDistance (gradientDistance) |
| { |
| } |
| |
| ~GradientCostCase (void) {} |
| |
| private: |
| virtual ObjectData genOccluderGeometry (void) const { return Utils::fastQuadWithGradient(0.0f, 1.0f - m_gradientDistance); } |
| virtual ObjectData genOccludedGeometry (void) const |
| { |
| return ObjectData(glu::makeVtxFragSources(Utils::getInstanceNoiseVertexShader(), Utils::getDepthAsRedFragmentShader()), Utils::getFullscreenQuadWithGradient(m_gradientDistance, 1.0f)); |
| } |
| |
| virtual void logDescription (void) |
| { |
| TestLog& log = m_testCtx.getLog(); |
| |
| log << TestLog::Section("Description", "Test description"); |
| log << TestLog::Message << "Testing hidden fragment culling speed" << TestLog::EndMessage; |
| log << TestLog::Message << "Geometry consists of two fullsceen quads. The first (occluding) is rendered once, the second (occluded) is rendered repeatedly" << TestLog::EndMessage; |
| log << TestLog::Message << "Workload indicates the number of times the occluded quad is rendered" << TestLog::EndMessage; |
| 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; |
| log << TestLog::Message << "The quads are spaced to have a depth difference of " << m_gradientDistance << " at all points." << TestLog::EndMessage; |
| 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; |
| log << TestLog::EndSection; |
| } |
| |
| const float m_gradientDistance; |
| }; |
| |
| // Constant offset to frag depth in occluder |
| class OccluderStaticFragDepthCostCase : public RenderCountCase |
| { |
| public: |
| OccluderStaticFragDepthCostCase (TestContext& testCtx, const RenderContext& renderCtx, const char* name, const char* desc) |
| : RenderCountCase(testCtx, renderCtx, name, desc) |
| { |
| } |
| |
| ~OccluderStaticFragDepthCostCase (void) {} |
| |
| private: |
| virtual ObjectData genOccluderGeometry (void) const { return Utils::quadWith(Utils::getStaticFragDepthFragmentShader(), 0.2f); } |
| virtual ObjectData genOccludedGeometry (void) const { return Utils::fastQuad(0.8f); } |
| |
| virtual void logDescription (void) |
| { |
| TestLog& log = m_testCtx.getLog(); |
| |
| log << TestLog::Section("Description", "Test description"); |
| log << TestLog::Message << "Testing hidden fragment culling speed" << TestLog::EndMessage; |
| log << TestLog::Message << "Geometry consists of two fullsceen quads. The first (occluding) is rendered once, the second (occluded) is rendered repeatedly" << TestLog::EndMessage; |
| log << TestLog::Message << "Workload indicates the number of times the occluded quad is rendered" << TestLog::EndMessage; |
| log << TestLog::Message << "The occluder quad has a static offset applied to gl_FragDepth" << TestLog::EndMessage; |
| 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; |
| log << TestLog::EndSection; |
| } |
| }; |
| |
| // Dynamic offset to frag depth in occluder |
| class OccluderDynamicFragDepthCostCase : public RenderCountCase |
| { |
| public: |
| OccluderDynamicFragDepthCostCase (TestContext& testCtx, const RenderContext& renderCtx, const char* name, const char* desc) |
| : RenderCountCase(testCtx, renderCtx, name, desc) |
| { |
| } |
| |
| ~OccluderDynamicFragDepthCostCase (void) {} |
| |
| private: |
| virtual ObjectData genOccluderGeometry (void) const { return Utils::quadWith(Utils::getDynamicFragDepthFragmentShader(), 0.2f); } |
| virtual ObjectData genOccludedGeometry (void) const { return Utils::fastQuad(0.8f); } |
| |
| virtual void logDescription (void) |
| { |
| TestLog& log = m_testCtx.getLog(); |
| |
| log << TestLog::Section("Description", "Test description"); |
| log << TestLog::Message << "Testing hidden fragment culling speed" << TestLog::EndMessage; |
| log << TestLog::Message << "Geometry consists of two fullsceen quads. The first (occluding) is rendered once, the second (occluded) is rendered repeatedly" << TestLog::EndMessage; |
| log << TestLog::Message << "Workload indicates the number of times the occluded quad is rendered" << TestLog::EndMessage; |
| log << TestLog::Message << "The occluder quad has a dynamic offset applied to gl_FragDepth" << TestLog::EndMessage; |
| 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; |
| log << TestLog::EndSection; |
| } |
| }; |
| |
| // Constant offset to frag depth in occluder |
| class OccludedStaticFragDepthCostCase : public RenderCountCase |
| { |
| public: |
| OccludedStaticFragDepthCostCase (TestContext& testCtx, const RenderContext& renderCtx, const char* name, const char* desc) |
| : RenderCountCase(testCtx, renderCtx, name, desc) |
| { |
| } |
| |
| ~OccludedStaticFragDepthCostCase (void) {} |
| |
| private: |
| virtual ObjectData genOccluderGeometry (void) const { return Utils::fastQuad(0.2f); } |
| virtual ObjectData genOccludedGeometry (void) const { return Utils::quadWith(Utils::getStaticFragDepthFragmentShader(), 0.2f); } |
| |
| virtual void logDescription (void) |
| { |
| TestLog& log = m_testCtx.getLog(); |
| |
| log << TestLog::Section("Description", "Test description"); |
| log << TestLog::Message << "Testing hidden fragment culling speed" << TestLog::EndMessage; |
| log << TestLog::Message << "Geometry consists of two fullsceen quads. The first (occluding) is rendered once, the second (occluded) is rendered repeatedly" << TestLog::EndMessage; |
| log << TestLog::Message << "Workload indicates the number of times the occluded quad is rendered" << TestLog::EndMessage; |
| log << TestLog::Message << "The occluded quad has a static offset applied to gl_FragDepth" << TestLog::EndMessage; |
| 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; |
| log << TestLog::EndSection; |
| } |
| }; |
| |
| // Dynamic offset to frag depth in occluder |
| class OccludedDynamicFragDepthCostCase : public RenderCountCase |
| { |
| public: |
| OccludedDynamicFragDepthCostCase (TestContext& testCtx, const RenderContext& renderCtx, const char* name, const char* desc) |
| : RenderCountCase(testCtx, renderCtx, name, desc) |
| { |
| } |
| |
| ~OccludedDynamicFragDepthCostCase (void) {} |
| |
| private: |
| virtual ObjectData genOccluderGeometry (void) const { return Utils::fastQuad(0.2f); } |
| virtual ObjectData genOccludedGeometry (void) const { return Utils::quadWith(Utils::getDynamicFragDepthFragmentShader(), 0.2f); } |
| |
| virtual void logDescription (void) |
| { |
| TestLog& log = m_testCtx.getLog(); |
| |
| log << TestLog::Section("Description", "Test description"); |
| log << TestLog::Message << "Testing hidden fragment culling speed" << TestLog::EndMessage; |
| log << TestLog::Message << "Geometry consists of two fullsceen quads. The first (occluding) is rendered once, the second (occluded) is rendered repeatedly" << TestLog::EndMessage; |
| log << TestLog::Message << "Workload indicates the number of times the occluded quad is rendered" << TestLog::EndMessage; |
| log << TestLog::Message << "The occluded quad has a dynamic offset applied to gl_FragDepth" << TestLog::EndMessage; |
| 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; |
| log << TestLog::EndSection; |
| } |
| }; |
| |
| // Culling speed with slightly less trivial geometry |
| class OccludingGeometryComplexityCostCase : public RenderCountCase |
| { |
| public: |
| OccludingGeometryComplexityCostCase (TestContext& testCtx, |
| const RenderContext& renderCtx, |
| const char* name, |
| const char* desc, |
| int resolution, |
| float xyNoise, |
| float zNoise) |
| : RenderCountCase (testCtx, renderCtx, name, desc) |
| , m_resolution (resolution) |
| , m_xyNoise (xyNoise) |
| , m_zNoise (zNoise) |
| { |
| } |
| |
| ~OccludingGeometryComplexityCostCase (void) {} |
| |
| private: |
| virtual ObjectData genOccluderGeometry (void) const |
| { |
| return ObjectData(Utils::getBaseShader(), |
| Utils::getFullScreenGrid(m_resolution, |
| deInt32Hash(deStringHash(getName())) ^ m_testCtx.getCommandLine().getBaseSeed(), |
| 0.2f, |
| m_zNoise, |
| m_xyNoise)); |
| } |
| |
| virtual ObjectData genOccludedGeometry (void) const { return Utils::variableQuad(0.8f); } |
| |
| virtual void logDescription (void) |
| { |
| TestLog& log = m_testCtx.getLog(); |
| |
| log << TestLog::Section("Description", "Test description"); |
| log << TestLog::Message << "Testing hidden fragment culling speed" << TestLog::EndMessage; |
| 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; |
| log << TestLog::Message << "Workload indicates the number of times the occluded quad is rendered" << TestLog::EndMessage; |
| 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; |
| log << TestLog::EndSection; |
| } |
| |
| const int m_resolution; |
| const float m_xyNoise; |
| const float m_zNoise; |
| }; |
| |
| |
| // Cases with varying workloads in the fragment shader |
| class FragmentWorkloadCullCase : public RelativeChangeCase |
| { |
| public: |
| FragmentWorkloadCullCase (TestContext& testCtx, const RenderContext& renderCtx, const char* name, const char* desc); |
| virtual ~FragmentWorkloadCullCase (void) {} |
| |
| private: |
| virtual ObjectData genOccluderGeometry (void) const { return Utils::fastQuad(0.2f); } |
| |
| virtual void logDescription (void); |
| }; |
| |
| FragmentWorkloadCullCase::FragmentWorkloadCullCase (TestContext& testCtx, const RenderContext& renderCtx, const char* name, const char* desc) |
| : RelativeChangeCase (testCtx, renderCtx, name, desc) |
| { |
| } |
| |
| void FragmentWorkloadCullCase::logDescription (void) |
| { |
| TestLog& log = m_testCtx.getLog(); |
| |
| log << TestLog::Section("Description", "Test description"); |
| log << TestLog::Message << "Testing effects of culled fragment workload on render time" << TestLog::EndMessage; |
| log << TestLog::Message << "Geometry consists of two fullsceen quads. The first (occluding) quad uses a trivial shader," |
| "the second (occluded) contains significant fragment shader work" << TestLog::EndMessage; |
| log << TestLog::Message << "Workload indicates the number of iterations of dummy work done in the occluded quad's fragment shader" << TestLog::EndMessage; |
| log << TestLog::Message << "The ratio of rendering times of this scene with/without depth testing are compared" << TestLog::EndMessage; |
| log << TestLog::Message << "Successfull early Z-testing should result in no correlation between workload and render time" << TestLog::EndMessage; |
| log << TestLog::EndSection; |
| } |
| |
| // Additional workload consists of texture lookups |
| class FragmentTextureWorkloadCullCase : public FragmentWorkloadCullCase |
| { |
| public: |
| FragmentTextureWorkloadCullCase (TestContext& testCtx, const RenderContext& renderCtx, const char* name, const char* desc); |
| virtual ~FragmentTextureWorkloadCullCase (void) {} |
| |
| virtual void init (void); |
| virtual void deinit (void); |
| |
| private: |
| typedef MovePtr<glu::Texture> TexPtr; |
| |
| virtual ObjectData genOccludedGeometry (void) const |
| { |
| return ObjectData(Utils::getTextureWorkloadShader(), Utils::getFullscreenQuad(0.8f)); |
| } |
| |
| TexPtr m_texture; |
| }; |
| |
| FragmentTextureWorkloadCullCase::FragmentTextureWorkloadCullCase (TestContext& testCtx, const RenderContext& renderCtx, const char* name, const char* desc) |
| : FragmentWorkloadCullCase (testCtx, renderCtx, name, desc) |
| { |
| } |
| |
| void FragmentTextureWorkloadCullCase::init (void) |
| { |
| const glw::Functions& gl = m_renderCtx.getFunctions(); |
| const int size = 128; |
| const vector<deUint8> data (size*size*4, 255); |
| |
| m_texture = MovePtr<glu::Texture>(new glu::Texture(gl)); |
| |
| gl.bindTexture(GL_TEXTURE_2D, m_texture); |
| gl.texImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, size, size, 0, GL_RGBA, GL_UNSIGNED_BYTE, &data[0]); |
| gl.texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); |
| gl.texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); |
| } |
| |
| void FragmentTextureWorkloadCullCase::deinit (void) |
| { |
| m_texture.clear(); |
| } |
| |
| // Additional workload consists of arithmetic |
| class FragmentArithmeticWorkloadCullCase : public FragmentWorkloadCullCase |
| { |
| public: |
| FragmentArithmeticWorkloadCullCase (TestContext& testCtx, const RenderContext& renderCtx, const char* name, const char* desc) |
| : FragmentWorkloadCullCase (testCtx, renderCtx, name, desc) |
| { |
| } |
| virtual ~FragmentArithmeticWorkloadCullCase (void) {} |
| |
| private: |
| virtual ObjectData genOccludedGeometry (void) const |
| { |
| return ObjectData(Utils::getArithmeticWorkloadShader(), Utils::getFullscreenQuad(0.8f)); |
| } |
| }; |
| |
| // Contains dynamicly unused discard after a series of calculations |
| class FragmentDiscardArithmeticWorkloadCullCase : public FragmentWorkloadCullCase |
| { |
| public: |
| FragmentDiscardArithmeticWorkloadCullCase (TestContext& testCtx, const RenderContext& renderCtx, const char* name, const char* desc) |
| : FragmentWorkloadCullCase (testCtx, renderCtx, name, desc) |
| { |
| } |
| |
| virtual ~FragmentDiscardArithmeticWorkloadCullCase (void) {} |
| |
| private: |
| virtual ObjectData genOccludedGeometry (void) const |
| { |
| return ObjectData(Utils::getArithmeticWorkloadDiscardShader(), Utils::getFullscreenQuad(0.8f)); |
| } |
| |
| virtual void logDescription (void) |
| { |
| TestLog& log = m_testCtx.getLog(); |
| |
| log << TestLog::Section("Description", "Test description"); |
| log << TestLog::Message << "Testing effects of culled fragment workload on render time" << TestLog::EndMessage; |
| log << TestLog::Message << "Geometry consists of two fullsceen quads. The first (occluding) quad uses a trivial shader," |
| "the second (occluded) contains significant fragment shader work and a discard that is never triggers but has a dynamic condition" << TestLog::EndMessage; |
| log << TestLog::Message << "Workload indicates the number of iterations of dummy work done in the occluded quad's fragment shader" << TestLog::EndMessage; |
| log << TestLog::Message << "The ratio of rendering times of this scene with/without depth testing are compared" << TestLog::EndMessage; |
| log << TestLog::Message << "Successfull early Z-testing should result in no correlation between workload and render time" << TestLog::EndMessage; |
| log << TestLog::EndSection; |
| } |
| }; |
| |
| // Discards fragments from the occluder in a grid pattern |
| class PartialOccluderDiscardCullCase : public RelativeChangeCase |
| { |
| public: |
| PartialOccluderDiscardCullCase (TestContext& testCtx, const RenderContext& renderCtx, const char* name, const char* desc, int gridsize) |
| : RelativeChangeCase (testCtx, renderCtx, name, desc) |
| , m_gridsize (gridsize) |
| { |
| } |
| virtual ~PartialOccluderDiscardCullCase (void) {} |
| |
| private: |
| virtual ObjectData genOccluderGeometry (void) const { return Utils::quadWith(Utils::getGridDiscardShader(m_gridsize), 0.2f); } |
| virtual ObjectData genOccludedGeometry (void) const { return Utils::slowQuad(0.8f); } |
| |
| virtual void logDescription (void) |
| { |
| TestLog& log = m_testCtx.getLog(); |
| |
| log << TestLog::Section("Description", "Test description"); |
| log << TestLog::Message << "Testing effects of partially discarded occluder on rendering time" << TestLog::EndMessage; |
| log << TestLog::Message << "Geometry consists of two fullsceen quads. The first (occluding) quad discards half the " |
| "fragments in a grid pattern, the second (partially occluded) contains significant fragment shader work" << TestLog::EndMessage; |
| log << TestLog::Message << "Workload indicates the number of iterations of dummy work done in the occluded quad's fragment shader" << TestLog::EndMessage; |
| log << TestLog::Message << "The ratio of rendering times of this scene with/without depth testing are compared" << TestLog::EndMessage; |
| log << TestLog::Message << "Successfull early Z-testing should result in depth testing halving the render time" << TestLog::EndMessage; |
| log << TestLog::EndSection; |
| } |
| |
| const int m_gridsize; |
| }; |
| |
| // Trivial occluder covering part of screen |
| class PartialOccluderCullCase : public RelativeChangeCase |
| { |
| public: |
| PartialOccluderCullCase (TestContext& testCtx, const RenderContext& renderCtx, const char* name, const char* desc, float coverage) |
| : RelativeChangeCase (testCtx, renderCtx, name, desc) |
| , m_coverage (coverage) |
| { |
| } |
| ~PartialOccluderCullCase (void) {} |
| |
| private: |
| virtual ObjectData genOccluderGeometry (void) const { return ObjectData(Utils::getBaseShader(), Utils::getPartScreenQuad(m_coverage, 0.2f)); } |
| virtual ObjectData genOccludedGeometry (void) const {return Utils::slowQuad(0.8f); } |
| |
| virtual void logDescription (void) |
| { |
| TestLog& log = m_testCtx.getLog(); |
| |
| log << TestLog::Section("Description", "Test description"); |
| log << TestLog::Message << "Testing effects of partial occluder on rendering time" << TestLog::EndMessage; |
| log << TestLog::Message << "Geometry consists of two quads. The first (occluding) quad covers " << m_coverage*100.0f |
| << "% of the screen, while the second (partially occluded, fullscreen) contains significant fragment shader work" << TestLog::EndMessage; |
| log << TestLog::Message << "Workload indicates the number of iterations of dummy work done in the occluded quad's fragment shader" << TestLog::EndMessage; |
| log << TestLog::Message << "The ratio of rendering times of this scene with/without depth testing are compared" << TestLog::EndMessage; |
| log << TestLog::Message << "Successfull early Z-testing should result in render time increasing proportionally with unoccluded area" << TestLog::EndMessage; |
| log << TestLog::EndSection; |
| } |
| |
| const float m_coverage; |
| }; |
| |
| // Constant offset to frag depth in occluder |
| class StaticOccluderFragDepthCullCase : public RelativeChangeCase |
| { |
| public: |
| StaticOccluderFragDepthCullCase (TestContext& testCtx, const RenderContext& renderCtx, const char* name, const char* desc) |
| : RelativeChangeCase(testCtx, renderCtx, name, desc) |
| { |
| } |
| |
| ~StaticOccluderFragDepthCullCase (void) {} |
| |
| private: |
| virtual ObjectData genOccluderGeometry (void) const { return Utils::quadWith(Utils::getStaticFragDepthFragmentShader(), 0.2f); } |
| virtual ObjectData genOccludedGeometry (void) const { return Utils::slowQuad(0.8f); } |
| |
| virtual void logDescription (void) |
| { |
| TestLog& log = m_testCtx.getLog(); |
| |
| log << TestLog::Section("Description", "Test description"); |
| log << TestLog::Message << "Testing effects of non-default frag depth on culling efficiency" << TestLog::EndMessage; |
| 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; |
| log << TestLog::Message << "Workload indicates the number of iterations of dummy work done in the occluded quad's fragment shader" << TestLog::EndMessage; |
| log << TestLog::Message << "The occluder quad has a static offset applied to gl_FragDepth" << TestLog::EndMessage; |
| log << TestLog::Message << "The ratio of rendering times of this scene with/without depth testing are compared" << TestLog::EndMessage; |
| log << TestLog::Message << "Successfull early Z-testing should result in no correlation between workload and render time" << TestLog::EndMessage; |
| log << TestLog::EndSection; |
| } |
| }; |
| |
| // Dynamic offset to frag depth in occluder |
| class DynamicOccluderFragDepthCullCase : public RelativeChangeCase |
| { |
| public: |
| DynamicOccluderFragDepthCullCase (TestContext& testCtx, const RenderContext& renderCtx, const char* name, const char* desc) |
| : RelativeChangeCase(testCtx, renderCtx, name, desc) |
| { |
| } |
| |
| ~DynamicOccluderFragDepthCullCase (void) {} |
| |
| private: |
| virtual ObjectData genOccluderGeometry (void) const { return Utils::quadWith(Utils::getDynamicFragDepthFragmentShader(), 0.2f); } |
| virtual ObjectData genOccludedGeometry (void) const { return Utils::slowQuad(0.8f); } |
| |
| virtual void logDescription (void) |
| { |
| TestLog& log = m_testCtx.getLog(); |
| |
| log << TestLog::Section("Description", "Test description"); |
| log << TestLog::Message << "Testing effects of non-default frag depth on culling efficiency" << TestLog::EndMessage; |
| 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; |
| log << TestLog::Message << "Workload indicates the number of iterations of dummy work done in the occluded quad's fragment shader" << TestLog::EndMessage; |
| log << TestLog::Message << "The occluder quad has a dynamic offset applied to gl_FragDepth" << TestLog::EndMessage; |
| log << TestLog::Message << "The ratio of rendering times of this scene with/without depth testing are compared" << TestLog::EndMessage; |
| log << TestLog::Message << "Successfull early Z-testing should result in no correlation between workload and render time" << TestLog::EndMessage; |
| log << TestLog::EndSection; |
| } |
| }; |
| |
| // Constant offset to frag depth in occluded |
| class StaticOccludedFragDepthCullCase : public RelativeChangeCase |
| { |
| public: |
| StaticOccludedFragDepthCullCase (TestContext& testCtx, const RenderContext& renderCtx, const char* name, const char* desc) |
| : RelativeChangeCase(testCtx, renderCtx, name, desc) |
| { |
| } |
| |
| ~StaticOccludedFragDepthCullCase (void) {} |
| |
| private: |
| virtual ObjectData genOccluderGeometry (void) const { return Utils::fastQuad(0.2f); } |
| virtual ObjectData genOccludedGeometry (void) const { return Utils::quadWith(Utils::getStaticFragDepthArithmeticWorkloadFragmentShader(), 0.2f); } |
| |
| virtual void logDescription (void) |
| { |
| TestLog& log = m_testCtx.getLog(); |
| |
| log << TestLog::Section("Description", "Test description"); |
| log << TestLog::Message << "Testing effects of non-default frag depth on rendering time" << TestLog::EndMessage; |
| 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; |
| log << TestLog::Message << "Workload indicates the number of iterations of dummy work done in the occluded quad's fragment shader" << TestLog::EndMessage; |
| log << TestLog::Message << "The occluded quad has a static offset applied to gl_FragDepth" << TestLog::EndMessage; |
| log << TestLog::Message << "The ratio of rendering times of this scene with/without depth testing are compared" << TestLog::EndMessage; |
| log << TestLog::Message << "Successfull early Z-testing should result in no correlation between workload and render time" << TestLog::EndMessage; |
| log << TestLog::EndSection; |
| } |
| }; |
| |
| // Dynamic offset to frag depth in occluded |
| class DynamicOccludedFragDepthCullCase : public RelativeChangeCase |
| { |
| public: |
| DynamicOccludedFragDepthCullCase (TestContext& testCtx, const RenderContext& renderCtx, const char* name, const char* desc) |
| : RelativeChangeCase(testCtx, renderCtx, name, desc) |
| { |
| } |
| |
| ~DynamicOccludedFragDepthCullCase (void) {} |
| |
| private: |
| virtual ObjectData genOccluderGeometry (void) const { return Utils::fastQuad(0.2f); } |
| virtual ObjectData genOccludedGeometry (void) const { return Utils::quadWith(Utils::getDynamicFragDepthArithmeticWorkloadFragmentShader(), 0.2f); } |
| |
| virtual void logDescription (void) |
| { |
| TestLog& log = m_testCtx.getLog(); |
| |
| log << TestLog::Section("Description", "Test description"); |
| log << TestLog::Message << "Testing effects of non-default frag depth on rendering time" << TestLog::EndMessage; |
| 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; |
| log << TestLog::Message << "Workload indicates the number of iterations of dummy work done in the occluded quad's fragment shader" << TestLog::EndMessage; |
| log << TestLog::Message << "The occluded quad has a dynamic offset applied to gl_FragDepth" << TestLog::EndMessage; |
| log << TestLog::Message << "The ratio of rendering times of this scene with/without depth testing are compared" << TestLog::EndMessage; |
| log << TestLog::Message << "Successfull early Z-testing should result in no correlation between workload and render time" << TestLog::EndMessage; |
| log << TestLog::EndSection; |
| } |
| }; |
| |
| // Dynamic offset to frag depth in occluded |
| class ReversedDepthOrderCullCase : public RelativeChangeCase |
| { |
| public: |
| ReversedDepthOrderCullCase (TestContext& testCtx, const RenderContext& renderCtx, const char* name, const char* desc) |
| : RelativeChangeCase(testCtx, renderCtx, name, desc) |
| { |
| } |
| |
| ~ReversedDepthOrderCullCase (void) {} |
| |
| private: |
| virtual ObjectData genOccluderGeometry (void) const { return Utils::fastQuad(0.2f); } |
| virtual ObjectData genOccludedGeometry (void) const { return Utils::slowQuad(0.8f); } |
| |
| virtual void logDescription (void) |
| { |
| TestLog& log = m_testCtx.getLog(); |
| |
| log << TestLog::Section("Description", "Test description"); |
| log << TestLog::Message << "Testing effects of of back first rendering order on culling efficiency" << TestLog::EndMessage; |
| 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; |
| log << TestLog::Message << "Workload indicates the number of iterations of dummy work done in the occluded quad's fragment shader" << TestLog::EndMessage; |
| log << TestLog::Message << "The ratio of rendering times of this scene with/without depth testing are compared" << TestLog::EndMessage; |
| log << TestLog::Message << "Successfull early Z-testing should result in no correlation between workload and render time" << TestLog::EndMessage; |
| log << TestLog::EndSection; |
| } |
| |
| // Rendering order of occluder & occluded is reversed, otherwise identical to parent version |
| Sample renderSample (const RenderData& occluder, const RenderData& occluded, int workload) const |
| { |
| const glw::Functions& gl = m_renderCtx.getFunctions(); |
| const GLuint program = occluded.m_program.getProgram(); |
| Sample sample; |
| deUint64 now = 0; |
| deUint64 prev = 0; |
| deUint8 buffer[4]; |
| |
| gl.useProgram(program); |
| gl.uniform1i(gl.getUniformLocation(program, "u_iterations"), workload); |
| |
| // Warmup (this workload seems to reduce variation in following workloads) |
| { |
| gl.clear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); |
| gl.disable(GL_DEPTH_TEST); |
| |
| gl.readPixels(0, 0, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, buffer); |
| } |
| |
| // Null time |
| { |
| prev = deGetMicroseconds(); |
| |
| gl.clear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); |
| gl.disable(GL_DEPTH_TEST); |
| |
| gl.readPixels(0, 0, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, buffer); |
| |
| now = deGetMicroseconds(); |
| |
| sample.nullTime = now - prev; |
| } |
| |
| // Test time |
| { |
| prev = deGetMicroseconds(); |
| |
| gl.clear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); |
| gl.enable(GL_DEPTH_TEST); |
| |
| render(occluded); |
| render(occluder); |
| |
| gl.readPixels(0, 0, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, buffer); |
| |
| now = deGetMicroseconds(); |
| |
| sample.testTime = now - prev; |
| } |
| |
| // Base time |
| { |
| prev = deGetMicroseconds(); |
| |
| gl.clear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); |
| gl.disable(GL_DEPTH_TEST); |
| |
| render(occluded); |
| render(occluder); |
| |
| gl.readPixels(0, 0, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, buffer); |
| |
| now = deGetMicroseconds(); |
| |
| sample.baseTime = now - prev; |
| } |
| |
| sample.workload = 0; |
| |
| return sample; |
| } |
| }; |
| |
| } // Anonymous |
| |
| DepthTests::DepthTests (Context& context) |
| : TestCaseGroup (context, "depth", "Depth culling performance") |
| { |
| } |
| |
| void DepthTests::init (void) |
| { |
| TestContext& testCtx = m_context.getTestContext(); |
| const RenderContext& renderCtx = m_context.getRenderContext(); |
| |
| { |
| tcu::TestCaseGroup* const cullEfficiencyGroup = new tcu::TestCaseGroup(m_testCtx, "cull_efficiency", "Fragment cull efficiency"); |
| |
| addChild(cullEfficiencyGroup); |
| |
| { |
| tcu::TestCaseGroup* const group = new tcu::TestCaseGroup(m_testCtx, "workload", "Workload"); |
| |
| cullEfficiencyGroup->addChild(group); |
| |
| group->addChild(new FragmentTextureWorkloadCullCase( testCtx, renderCtx, "workload_texture", "Fragment shader with texture lookup workload")); |
| group->addChild(new FragmentArithmeticWorkloadCullCase( testCtx, renderCtx, "workload_arithmetic", "Fragment shader with arithmetic workload")); |
| group->addChild(new FragmentDiscardArithmeticWorkloadCullCase( testCtx, renderCtx, "workload_arithmetic_discard", "Fragment shader that may discard with arithmetic workload")); |
| } |
| |
| { |
| tcu::TestCaseGroup* const group = new tcu::TestCaseGroup(m_testCtx, "occluder_discard", "Discard"); |
| |
| cullEfficiencyGroup->addChild(group); |
| |
| group->addChild(new PartialOccluderDiscardCullCase(testCtx, renderCtx, "grid_256", "Parts of occluder geometry discarded", 256)); |
| group->addChild(new PartialOccluderDiscardCullCase(testCtx, renderCtx, "grid_128", "Parts of occluder geometry discarded", 128)); |
| group->addChild(new PartialOccluderDiscardCullCase(testCtx, renderCtx, "grid_64", "Parts of occluder geometry discarded", 64)); |
| group->addChild(new PartialOccluderDiscardCullCase(testCtx, renderCtx, "grid_32", "Parts of occluder geometry discarded", 32)); |
| group->addChild(new PartialOccluderDiscardCullCase(testCtx, renderCtx, "grid_16", "Parts of occluder geometry discarded", 16)); |
| group->addChild(new PartialOccluderDiscardCullCase(testCtx, renderCtx, "grid_8", "Parts of occluder geometry discarded", 8)); |
| group->addChild(new PartialOccluderDiscardCullCase(testCtx, renderCtx, "grid_4", "Parts of occluder geometry discarded", 4)); |
| group->addChild(new PartialOccluderDiscardCullCase(testCtx, renderCtx, "grid_2", "Parts of occluder geometry discarded", 2)); |
| group->addChild(new PartialOccluderDiscardCullCase(testCtx, renderCtx, "grid_1", "Parts of occluder geometry discarded", 1)); |
| } |
| |
| { |
| tcu::TestCaseGroup* const group = new tcu::TestCaseGroup(m_testCtx, "partial_coverage", "Partial Coverage"); |
| |
| cullEfficiencyGroup->addChild(group); |
| |
| group->addChild(new PartialOccluderCullCase(testCtx, renderCtx, "100", "Occluder covering only part of occluded geometry", 1.00f)); |
| group->addChild(new PartialOccluderCullCase(testCtx, renderCtx, "099", "Occluder covering only part of occluded geometry", 0.99f)); |
| group->addChild(new PartialOccluderCullCase(testCtx, renderCtx, "095", "Occluder covering only part of occluded geometry", 0.95f)); |
| group->addChild(new PartialOccluderCullCase(testCtx, renderCtx, "090", "Occluder covering only part of occluded geometry", 0.90f)); |
| group->addChild(new PartialOccluderCullCase(testCtx, renderCtx, "080", "Occluder covering only part of occluded geometry", 0.80f)); |
| group->addChild(new PartialOccluderCullCase(testCtx, renderCtx, "070", "Occluder covering only part of occluded geometry", 0.70f)); |
| group->addChild(new PartialOccluderCullCase(testCtx, renderCtx, "050", "Occluder covering only part of occluded geometry", 0.50f)); |
| group->addChild(new PartialOccluderCullCase(testCtx, renderCtx, "025", "Occluder covering only part of occluded geometry", 0.25f)); |
| group->addChild(new PartialOccluderCullCase(testCtx, renderCtx, "010", "Occluder covering only part of occluded geometry", 0.10f)); |
| } |
| |
| { |
| tcu::TestCaseGroup* const group = new tcu::TestCaseGroup(m_testCtx, "frag_depth", "Partial Coverage"); |
| |
| cullEfficiencyGroup->addChild(group); |
| |
| group->addChild(new StaticOccluderFragDepthCullCase( testCtx, renderCtx, "occluder_static", "")); |
| group->addChild(new DynamicOccluderFragDepthCullCase(testCtx, renderCtx, "occluder_dynamic", "")); |
| group->addChild(new StaticOccludedFragDepthCullCase( testCtx, renderCtx, "occluded_static", "")); |
| group->addChild(new DynamicOccludedFragDepthCullCase(testCtx, renderCtx, "occluded_dynamic", "")); |
| } |
| |
| { |
| tcu::TestCaseGroup* const group = new tcu::TestCaseGroup(m_testCtx, "order", "Rendering order"); |
| |
| cullEfficiencyGroup->addChild(group); |
| |
| group->addChild(new ReversedDepthOrderCullCase(testCtx, renderCtx, "reversed", "Back to front rendering order")); |
| } |
| } |
| |
| { |
| tcu::TestCaseGroup* const testCostGroup = new tcu::TestCaseGroup(m_testCtx, "culled_pixel_cost", "Fragment cull efficiency"); |
| |
| addChild(testCostGroup); |
| |
| { |
| tcu::TestCaseGroup* const group = new tcu::TestCaseGroup(m_testCtx, "gradient", "Gradients with small depth differences"); |
| |
| testCostGroup->addChild(group); |
| |
| group->addChild(new BaseCostCase(testCtx, renderCtx, "flat", "")); |
| group->addChild(new GradientCostCase(testCtx, renderCtx, "gradient_050", "", 0.50f)); |
| group->addChild(new GradientCostCase(testCtx, renderCtx, "gradient_010", "", 0.10f)); |
| group->addChild(new GradientCostCase(testCtx, renderCtx, "gradient_005", "", 0.05f)); |
| group->addChild(new GradientCostCase(testCtx, renderCtx, "gradient_002", "", 0.02f)); |
| group->addChild(new GradientCostCase(testCtx, renderCtx, "gradient_001", "", 0.01f)); |
| } |
| |
| { |
| tcu::TestCaseGroup* const group = new tcu::TestCaseGroup(m_testCtx, "occluder_geometry", "Occluders with varying geometry complexity"); |
| |
| testCostGroup->addChild(group); |
| |
| group->addChild(new OccludingGeometryComplexityCostCase(testCtx, renderCtx, "flat_uniform_grid_5", "", 5, 0.0f, 0.0f)); |
| group->addChild(new OccludingGeometryComplexityCostCase(testCtx, renderCtx, "flat_uniform_grid_15", "", 15, 0.0f, 0.0f)); |
| group->addChild(new OccludingGeometryComplexityCostCase(testCtx, renderCtx, "flat_uniform_grid_25", "", 25, 0.0f, 0.0f)); |
| group->addChild(new OccludingGeometryComplexityCostCase(testCtx, renderCtx, "flat_uniform_grid_50", "", 50, 0.0f, 0.0f)); |
| group->addChild(new OccludingGeometryComplexityCostCase(testCtx, renderCtx, "flat_uniform_grid_100", "", 100, 0.0f, 0.0f)); |
| |
| group->addChild(new OccludingGeometryComplexityCostCase(testCtx, renderCtx, "flat_noisy_grid_5", "", 5, 1.0f/5.0f, 0.0f)); |
| group->addChild(new OccludingGeometryComplexityCostCase(testCtx, renderCtx, "flat_noisy_grid_15", "", 15, 1.0f/15.0f, 0.0f)); |
| group->addChild(new OccludingGeometryComplexityCostCase(testCtx, renderCtx, "flat_noisy_grid_25", "", 25, 1.0f/25.0f, 0.0f)); |
| group->addChild(new OccludingGeometryComplexityCostCase(testCtx, renderCtx, "flat_noisy_grid_50", "", 50, 1.0f/50.0f, 0.0f)); |
| group->addChild(new OccludingGeometryComplexityCostCase(testCtx, renderCtx, "flat_noisy_grid_100", "", 100, 1.0f/100.0f, 0.0f)); |
| |
| group->addChild(new OccludingGeometryComplexityCostCase(testCtx, renderCtx, "uneven_uniform_grid_5", "", 5, 0.0f, 0.2f)); |
| group->addChild(new OccludingGeometryComplexityCostCase(testCtx, renderCtx, "uneven_uniform_grid_15", "", 15, 0.0f, 0.2f)); |
| group->addChild(new OccludingGeometryComplexityCostCase(testCtx, renderCtx, "uneven_uniform_grid_25", "", 25, 0.0f, 0.2f)); |
| group->addChild(new OccludingGeometryComplexityCostCase(testCtx, renderCtx, "uneven_uniform_grid_50", "", 50, 0.0f, 0.2f)); |
| group->addChild(new OccludingGeometryComplexityCostCase(testCtx, renderCtx, "uneven_uniform_grid_100", "", 100, 0.0f, 0.2f)); |
| |
| group->addChild(new OccludingGeometryComplexityCostCase(testCtx, renderCtx, "uneven_noisy_grid_5", "", 5, 1.0f/5.0f, 0.2f)); |
| group->addChild(new OccludingGeometryComplexityCostCase(testCtx, renderCtx, "uneven_noisy_grid_15", "", 15, 1.0f/15.0f, 0.2f)); |
| group->addChild(new OccludingGeometryComplexityCostCase(testCtx, renderCtx, "uneven_noisy_grid_25", "", 25, 1.0f/25.0f, 0.2f)); |
| group->addChild(new OccludingGeometryComplexityCostCase(testCtx, renderCtx, "uneven_noisy_grid_50", "", 50, 1.0f/50.0f, 0.2f)); |
| group->addChild(new OccludingGeometryComplexityCostCase(testCtx, renderCtx, "uneven_noisy_grid_100", "", 100, 1.0f/100.0f, 0.2f)); |
| } |
| |
| { |
| tcu::TestCaseGroup* const group = new tcu::TestCaseGroup(m_testCtx, "frag_depth", "Modifying gl_FragDepth"); |
| |
| testCostGroup->addChild(group); |
| |
| group->addChild(new OccluderStaticFragDepthCostCase( testCtx, renderCtx, "occluder_static", "")); |
| group->addChild(new OccluderDynamicFragDepthCostCase(testCtx, renderCtx, "occluder_dynamic", "")); |
| group->addChild(new OccludedStaticFragDepthCostCase( testCtx, renderCtx, "occluded_static", "")); |
| group->addChild(new OccludedDynamicFragDepthCostCase(testCtx, renderCtx, "occluded_dynamic", "")); |
| } |
| } |
| } |
| |
| } // Performance |
| } // gles3 |
| } // deqp |