Index: bench/GLVec4ScalarBench.cpp |
diff --git a/bench/GLVec4ScalarBench.cpp b/bench/GLVec4ScalarBench.cpp |
new file mode 100644 |
index 0000000000000000000000000000000000000000..1ba8855862c92a55d7ed8f6212f47a34bf126c17 |
--- /dev/null |
+++ b/bench/GLVec4ScalarBench.cpp |
@@ -0,0 +1,311 @@ |
+/* |
+ * Copyright 2015 Google Inc. |
+ * |
+ * Use of this source code is governed by a BSD-style license that can be |
+ * found in the LICENSE file. |
+ */ |
+ |
+#include "SkMatrix.h" |
+#include "SkPoint.h" |
+#include "SkString.h" |
+ |
+#if SK_SUPPORT_GPU |
+#include "GLBench.h" |
+#include "gl/GrGLGLSL.h" |
+#include "gl/GrGLInterface.h" |
+#include "gl/GrGLShaderVar.h" |
+#include "gl/GrGLUtil.h" |
+#include "glsl/GrGLSLCaps.h" |
+ |
+#include <stdio.h> |
+ |
+/** |
+ * This is a GL benchmark for comparing the performance of using vec4 or float for coverage in GLSL. |
+ * The generated shader code from this bench will draw several overlapping circles, one in each |
+ * stage, to simulate coverage calculations. The number of circles (i.e. the number of stages) can |
+ * be set as a parameter. |
+ */ |
+ |
+class GLVec4ScalarBench : public GLBench { |
+public: |
+ /* |
+ * Use float or vec4 as GLSL data type for the output coverage |
+ */ |
+ enum CoverageSetup { |
+ kUseScalar_CoverageSetup, |
+ kUseVec4_CoverageSetup, |
+ }; |
+ |
+ /* |
+ * numStages determines the number of shader stages before the XP, |
+ * which consequently determines how many circles are drawn |
+ */ |
+ GLVec4ScalarBench(CoverageSetup coverageSetup, uint32_t numStages) |
+ : fCoverageSetup(coverageSetup) |
+ , fNumStages(numStages) |
+ , fVboId(0) |
+ , fProgram(0) |
+ , fVaoId(0) { |
+ fName = NumStagesSetupToStr(coverageSetup, numStages); |
+ } |
+ |
+protected: |
+ const char* onGetName() override { |
+ return fName.c_str(); |
+ } |
+ |
+ void setup(const GrGLContext*) override; |
+ void glDraw(const int loops, const GrGLContext*) override; |
+ void teardown(const GrGLInterface*) override; |
+ |
+private: |
+ void setupSingleVbo(const GrGLInterface*, const SkMatrix*); |
+ GrGLuint setupShader(const GrGLContext*); |
+ |
+ |
+ static SkString NumStagesSetupToStr(CoverageSetup coverageSetup, uint32_t numStages) { |
+ SkString name("GLVec4ScalarBench"); |
+ switch (coverageSetup) { |
+ default: |
+ case kUseScalar_CoverageSetup: |
+ name.appendf("_scalar_%u_stage", numStages); |
+ break; |
+ case kUseVec4_CoverageSetup: |
+ name.appendf("_vec4_%u_stage", numStages); |
+ break; |
+ } |
+ return name; |
+ } |
+ |
+ static const GrGLuint kScreenWidth = 800; |
+ static const GrGLuint kScreenHeight = 600; |
+ static const uint32_t kNumTriPerDraw = 512; |
+ static const uint32_t kVerticesPerTri = 3; |
+ |
+ SkString fName; |
+ CoverageSetup fCoverageSetup; |
+ uint32_t fNumStages; |
+ GrGLuint fVboId; |
+ GrGLuint fProgram; |
+ GrGLuint fVaoId; |
+ GrGLuint fFboTextureId; |
+}; |
+ |
+/////////////////////////////////////////////////////////////////////////////////////////////////// |
+ |
+GrGLuint GLVec4ScalarBench::setupShader(const GrGLContext* ctx) { |
+ const char* version = GrGLGetGLSLVersionDecl(*ctx); |
+ |
+ // this shader draws fNumStages overlapping circles of increasing opacity (coverage) and |
+ // decreasing size, with the center of each subsequent circle closer to the bottom-right |
+ // corner of the screen than the previous circle. |
+ |
+ // set up vertex shader; this is a trivial vertex shader that passes through position and color |
+ GrGLShaderVar aPosition("a_position", kVec2f_GrSLType, GrShaderVar::kAttribute_TypeModifier); |
+ GrGLShaderVar oPosition("o_position", kVec2f_GrSLType, GrShaderVar::kVaryingOut_TypeModifier); |
+ GrGLShaderVar aColor("a_color", kVec3f_GrSLType, GrShaderVar::kAttribute_TypeModifier); |
+ GrGLShaderVar oColor("o_color", kVec3f_GrSLType, GrShaderVar::kVaryingOut_TypeModifier); |
+ |
+ SkString vshaderTxt(version); |
+ aPosition.appendDecl(*ctx, &vshaderTxt); |
+ vshaderTxt.append(";\n"); |
+ aColor.appendDecl(*ctx, &vshaderTxt); |
+ vshaderTxt.append(";\n"); |
+ oPosition.appendDecl(*ctx, &vshaderTxt); |
+ vshaderTxt.append(";\n"); |
+ oColor.appendDecl(*ctx, &vshaderTxt); |
+ vshaderTxt.append(";\n"); |
+ |
+ vshaderTxt.append( |
+ "void main()\n" |
+ "{\n" |
+ " gl_Position = vec4(a_position, 0.0, 1.0);\n" |
+ " o_position = a_position;\n" |
+ " o_color = a_color;\n" |
+ "}\n"); |
+ |
+ const GrGLInterface* gl = ctx->interface(); |
+ |
+ // set up fragment shader; this fragment shader will have fNumStages coverage stages plus an |
+ // XP stage at the end. Each coverage stage computes the pixel's distance from some hard- |
+ // coded center and compare that to some hard-coded circle radius to compute a coverage. |
+ // Then, this coverage is mixed with the coverage from the previous stage and passed to the |
+ // next stage. |
+ GrGLShaderVar oFragColor("o_FragColor", kVec4f_GrSLType, GrShaderVar::kOut_TypeModifier); |
+ SkString fshaderTxt(version); |
+ GrGLAppendGLSLDefaultFloatPrecisionDeclaration(kDefault_GrSLPrecision, gl->fStandard, |
+ &fshaderTxt); |
+ oPosition.setTypeModifier(GrShaderVar::kVaryingIn_TypeModifier); |
+ oPosition.appendDecl(*ctx, &fshaderTxt); |
+ fshaderTxt.append(";\n"); |
+ oColor.setTypeModifier(GrShaderVar::kVaryingIn_TypeModifier); |
+ oColor.appendDecl(*ctx, &fshaderTxt); |
+ fshaderTxt.append(";\n"); |
+ |
+ const char* fsOutName; |
+ if (ctx->caps()->glslCaps()->mustDeclareFragmentShaderOutput()) { |
+ oFragColor.appendDecl(*ctx, &fshaderTxt); |
+ fshaderTxt.append(";\n"); |
+ fsOutName = oFragColor.c_str(); |
+ } else { |
+ fsOutName = "gl_FragColor"; |
+ } |
+ |
+ |
+ fshaderTxt.appendf( |
+ "void main()\n" |
+ "{\n" |
+ " vec4 outputColor;\n" |
+ " %s outputCoverage;\n" |
+ " outputColor = vec4(%s, 1.0);\n" |
+ " outputCoverage = %s;\n", |
+ fCoverageSetup == kUseVec4_CoverageSetup ? "vec4" : "float", |
+ oColor.getName().c_str(), |
+ fCoverageSetup == kUseVec4_CoverageSetup ? "vec4(1.0)" : "1.0" |
+ ); |
+ |
+ float radius = 1.0f; |
+ for (uint32_t i = 0; i < fNumStages; i++) { |
+ float centerX = 1.0f - radius; |
+ float centerY = 1.0f - radius; |
+ fshaderTxt.appendf( |
+ " {\n" |
+ " float d = length(%s - vec2(%f, %f));\n" |
+ " float edgeAlpha = clamp(100.0 * (%f - d), 0.0, 1.0);\n" |
+ " outputCoverage = 0.5 * outputCoverage + 0.5 * %s;\n" |
+ " }\n", |
+ oPosition.getName().c_str(), centerX, centerY, |
+ radius, |
+ fCoverageSetup == kUseVec4_CoverageSetup ? "vec4(edgeAlpha)" : "edgeAlpha" |
+ ); |
+ radius *= 0.8f; |
+ } |
+ fshaderTxt.appendf( |
+ " {\n" |
+ " %s = outputColor * outputCoverage;\n" |
+ " }\n" |
+ "}\n", |
+ fsOutName); |
+ |
+ return CreateProgram(gl, vshaderTxt.c_str(), fshaderTxt.c_str()); |
+} |
+ |
+template<typename Func> |
+static void setup_matrices(int numQuads, Func f) { |
+ // We draw a really small triangle so we are not fill rate limited |
+ for (int i = 0 ; i < numQuads; i++) { |
+ SkMatrix m = SkMatrix::I(); |
+ m.setScale(0.01f, 0.01f); |
+ f(m); |
+ } |
+} |
+ |
+/////////////////////////////////////////////////////////////////////////////////////////////////// |
+ |
+struct Vertex { |
+ SkPoint fPositions; |
+ GrGLfloat fColors[3]; |
+}; |
+ |
+void GLVec4ScalarBench::setupSingleVbo(const GrGLInterface* gl, const SkMatrix* viewMatrices) { |
+ // triangles drawn will alternate between the top-right half of the screen and the bottom-left |
+ // half of the screen |
+ Vertex vertices[kVerticesPerTri * kNumTriPerDraw]; |
+ for (uint32_t i = 0; i < kNumTriPerDraw; i++) { |
+ Vertex* v = &vertices[i * kVerticesPerTri]; |
+ if (i % 2 == 0) { |
+ v[0].fPositions.set(-1.0f, -1.0f); |
+ v[1].fPositions.set( 1.0f, -1.0f); |
+ v[2].fPositions.set( 1.0f, 1.0f); |
+ } else { |
+ v[0].fPositions.set(-1.0f, -1.0f); |
+ v[1].fPositions.set( 1.0f, 1.0f); |
+ v[2].fPositions.set( -1.0f, 1.0f); |
+ } |
+ SkPoint* position = reinterpret_cast<SkPoint*>(v); |
+ viewMatrices[i].mapPointsWithStride(position, sizeof(Vertex), kVerticesPerTri); |
+ |
+ GrGLfloat color[3] = {1.0f, 0.0f, 1.0f}; |
+ for (uint32_t j = 0; j < kVerticesPerTri; j++) { |
+ v->fColors[0] = color[0]; |
+ v->fColors[1] = color[1]; |
+ v->fColors[2] = color[2]; |
+ v++; |
+ } |
+ } |
+ |
+ GR_GL_CALL(gl, GenBuffers(1, &fVboId)); |
+ GR_GL_CALL(gl, BindBuffer(GR_GL_ARRAY_BUFFER, fVboId)); |
+ GR_GL_CALL(gl, EnableVertexAttribArray(0)); |
+ GR_GL_CALL(gl, EnableVertexAttribArray(1)); |
+ GR_GL_CALL(gl, VertexAttribPointer(0, 2, GR_GL_FLOAT, GR_GL_FALSE, sizeof(Vertex), |
+ (GrGLvoid*)0)); |
+ GR_GL_CALL(gl, VertexAttribPointer(1, 3, GR_GL_FLOAT, GR_GL_FALSE, sizeof(Vertex), |
+ (GrGLvoid*)(sizeof(SkPoint)))); |
+ GR_GL_CALL(gl, BufferData(GR_GL_ARRAY_BUFFER, sizeof(vertices), vertices, GR_GL_STATIC_DRAW)); |
+} |
+ |
+void GLVec4ScalarBench::setup(const GrGLContext* ctx) { |
+ const GrGLInterface* gl = ctx->interface(); |
+ if (!gl) { |
+ SkFAIL("GL interface is null in setup()\n"); |
+ } |
+ fFboTextureId = SetupFramebuffer(gl, kScreenWidth, kScreenHeight); |
+ |
+ fProgram = this->setupShader(ctx); |
+ |
+ GR_GL_CALL(gl, GenVertexArrays(1, &fVaoId)); |
+ GR_GL_CALL(gl, BindVertexArray(fVaoId)); |
+ |
+ int index = 0; |
+ SkMatrix viewMatrices[kNumTriPerDraw]; |
+ setup_matrices(kNumTriPerDraw, [&index, &viewMatrices](const SkMatrix& m) { |
+ viewMatrices[index++] = m; |
+ }); |
+ this->setupSingleVbo(gl, viewMatrices); |
+ |
+ GR_GL_CALL(gl, UseProgram(fProgram)); |
+ GR_GL_CALL(gl, BindVertexArray(fVaoId)); |
+} |
+ |
+void GLVec4ScalarBench::glDraw(const int loops, const GrGLContext* ctx) { |
+ const GrGLInterface* gl = ctx->interface(); |
+ |
+ for (int i = 0; i < loops; i++) { |
+ GR_GL_CALL(gl, DrawArrays(GR_GL_TRIANGLES, 0, kVerticesPerTri * kNumTriPerDraw)); |
+ } |
+ |
+// using -w when running nanobench will not produce correct images; |
+// changing this to #if 1 will write the correct images to the Skia folder. |
+#if 0 |
+ SkString filename("out"); |
+ filename.appendf("_%s.png", this->getName()); |
+ DumpImage(gl, kScreenWidth, kScreenHeight, filename.c_str()); |
+#endif |
+} |
+ |
+void GLVec4ScalarBench::teardown(const GrGLInterface* gl) { |
+ GR_GL_CALL(gl, BindBuffer(GR_GL_ARRAY_BUFFER, 0)); |
+ GR_GL_CALL(gl, BindVertexArray(0)); |
+ GR_GL_CALL(gl, BindTexture(GR_GL_TEXTURE_2D, 0)); |
+ GR_GL_CALL(gl, BindFramebuffer(GR_GL_FRAMEBUFFER, 0)); |
+ GR_GL_CALL(gl, DeleteTextures(1, &fFboTextureId)); |
+ GR_GL_CALL(gl, DeleteProgram(fProgram)); |
+ GR_GL_CALL(gl, DeleteBuffers(1, &fVboId)); |
+ GR_GL_CALL(gl, DeleteVertexArrays(1, &fVaoId)); |
+} |
+ |
+/////////////////////////////////////////////////////////////////////////////// |
+ |
+DEF_BENCH( return new GLVec4ScalarBench(GLVec4ScalarBench::kUseScalar_CoverageSetup, 1) ) |
+DEF_BENCH( return new GLVec4ScalarBench(GLVec4ScalarBench::kUseVec4_CoverageSetup, 1) ) |
+DEF_BENCH( return new GLVec4ScalarBench(GLVec4ScalarBench::kUseScalar_CoverageSetup, 2) ) |
+DEF_BENCH( return new GLVec4ScalarBench(GLVec4ScalarBench::kUseVec4_CoverageSetup, 2) ) |
+DEF_BENCH( return new GLVec4ScalarBench(GLVec4ScalarBench::kUseScalar_CoverageSetup, 4) ) |
+DEF_BENCH( return new GLVec4ScalarBench(GLVec4ScalarBench::kUseVec4_CoverageSetup, 4) ) |
+DEF_BENCH( return new GLVec4ScalarBench(GLVec4ScalarBench::kUseScalar_CoverageSetup, 6) ) |
+DEF_BENCH( return new GLVec4ScalarBench(GLVec4ScalarBench::kUseVec4_CoverageSetup, 6) ) |
+DEF_BENCH( return new GLVec4ScalarBench(GLVec4ScalarBench::kUseScalar_CoverageSetup, 8) ) |
+DEF_BENCH( return new GLVec4ScalarBench(GLVec4ScalarBench::kUseVec4_CoverageSetup, 8) ) |
+ |
+#endif |