Added GLVec4ScalarBench that doesn't use VAOs

BUG=skia:

Review URL: https://codereview.chromium.org/1235153008
diff --git a/bench/GLVec4ScalarBench.cpp b/bench/GLVec4ScalarBench.cpp
new file mode 100644
index 0000000..e8c40f4
--- /dev/null
+++ b/bench/GLVec4ScalarBench.cpp
@@ -0,0 +1,303 @@
+/*
+ * 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) {
+        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 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);
+
+    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));
+}
+
+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, 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));
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+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