Home | History | Annotate | Download | only in bench
      1 /*
      2  * Copyright 2015 Google Inc.
      3  *
      4  * Use of this source code is governed by a BSD-style license that can be
      5  * found in the LICENSE file.
      6  */
      7 
      8 #include "SkMatrix.h"
      9 #include "SkPoint.h"
     10 #include "SkString.h"
     11 
     12 #if SK_SUPPORT_GPU
     13 #include "GLBench.h"
     14 #include "GrShaderCaps.h"
     15 #include "GrShaderVar.h"
     16 #include "gl/GrGLContext.h"
     17 #include "gl/GrGLInterface.h"
     18 #include "gl/GrGLUtil.h"
     19 #include "../private/GrGLSL.h"
     20 
     21 #include <stdio.h>
     22 
     23 /**
     24  * This is a GL benchmark for comparing the performance of using vec4 or float for coverage in GLSL.
     25  * The generated shader code from this bench will draw several overlapping circles, one in each
     26  * stage, to simulate coverage calculations.  The number of circles (i.e. the number of stages) can
     27  * be set as a parameter.
     28  */
     29 
     30 class GLVec4ScalarBench : public GLBench {
     31 public:
     32     /*
     33      * Use float or vec4 as GLSL data type for the output coverage
     34      */
     35     enum CoverageSetup {
     36         kUseScalar_CoverageSetup,
     37         kUseVec4_CoverageSetup,
     38     };
     39 
     40     /*
     41      * numStages determines the number of shader stages before the XP,
     42      * which consequently determines how many circles are drawn
     43      */
     44     GLVec4ScalarBench(CoverageSetup coverageSetup, uint32_t numStages)
     45         : fCoverageSetup(coverageSetup)
     46         , fNumStages(numStages)
     47         , fVboId(0)
     48         , fProgram(0) {
     49         fName = NumStagesSetupToStr(coverageSetup, numStages);
     50     }
     51 
     52 protected:
     53     const char* onGetName() override {
     54         return fName.c_str();
     55     }
     56 
     57     void setup(const GrGLContext*) override;
     58     void glDraw(int loops, const GrGLContext*) override;
     59     void teardown(const GrGLInterface*) override;
     60 
     61 private:
     62     void setupSingleVbo(const GrGLInterface*, const SkMatrix*);
     63     GrGLuint setupShader(const GrGLContext*);
     64 
     65 
     66     static SkString NumStagesSetupToStr(CoverageSetup coverageSetup, uint32_t numStages) {
     67         SkString name("GLVec4ScalarBench");
     68         switch (coverageSetup) {
     69             default:
     70             case kUseScalar_CoverageSetup:
     71                 name.appendf("_scalar_%u_stage", numStages);
     72                 break;
     73             case kUseVec4_CoverageSetup:
     74                 name.appendf("_vec4_%u_stage", numStages);
     75                 break;
     76         }
     77         return name;
     78     }
     79 
     80     static const GrGLuint kScreenWidth = 800;
     81     static const GrGLuint kScreenHeight = 600;
     82     static const uint32_t kNumTriPerDraw = 512;
     83     static const uint32_t kVerticesPerTri = 3;
     84 
     85     SkString fName;
     86     CoverageSetup fCoverageSetup;
     87     uint32_t fNumStages;
     88     GrGLuint fVboId;
     89     GrGLuint fProgram;
     90     GrGLuint fFboTextureId;
     91 };
     92 
     93 ///////////////////////////////////////////////////////////////////////////////////////////////////
     94 
     95 GrGLuint GLVec4ScalarBench::setupShader(const GrGLContext* ctx) {
     96     const GrShaderCaps* shaderCaps = ctx->caps()->shaderCaps();
     97     const char* version = shaderCaps->versionDeclString();
     98 
     99     // this shader draws fNumStages overlapping circles of increasing opacity (coverage) and
    100     // decreasing size, with the center of each subsequent circle closer to the bottom-right
    101     // corner of the screen than the previous circle.
    102 
    103     // set up vertex shader; this is a trivial vertex shader that passes through position and color
    104     GrShaderVar aPosition("a_position", kVec2f_GrSLType, GrShaderVar::kIn_TypeModifier);
    105     GrShaderVar oPosition("o_position", kVec2f_GrSLType, GrShaderVar::kOut_TypeModifier);
    106     GrShaderVar aColor("a_color", kVec3f_GrSLType, GrShaderVar::kIn_TypeModifier);
    107     GrShaderVar oColor("o_color", kVec3f_GrSLType, GrShaderVar::kOut_TypeModifier);
    108 
    109     SkString vshaderTxt(version);
    110     aPosition.appendDecl(shaderCaps, &vshaderTxt);
    111     vshaderTxt.append(";\n");
    112     aColor.appendDecl(shaderCaps, &vshaderTxt);
    113     vshaderTxt.append(";\n");
    114     oPosition.appendDecl(shaderCaps, &vshaderTxt);
    115     vshaderTxt.append(";\n");
    116     oColor.appendDecl(shaderCaps, &vshaderTxt);
    117     vshaderTxt.append(";\n");
    118 
    119     vshaderTxt.append(
    120             "void main()\n"
    121             "{\n"
    122             "    gl_Position = vec4(a_position, 0.0, 1.0);\n"
    123             "    o_position = a_position;\n"
    124             "    o_color = a_color;\n"
    125             "}\n");
    126 
    127     // set up fragment shader; this fragment shader will have fNumStages coverage stages plus an
    128     // XP stage at the end.  Each coverage stage computes the pixel's distance from some hard-
    129     // coded center and compare that to some hard-coded circle radius to compute a coverage.
    130     // Then, this coverage is mixed with the coverage from the previous stage and passed to the
    131     // next stage.
    132     GrShaderVar oFragColor("o_FragColor", kVec4f_GrSLType, GrShaderVar::kOut_TypeModifier);
    133     SkString fshaderTxt(version);
    134     GrGLSLAppendDefaultFloatPrecisionDeclaration(kMedium_GrSLPrecision, *shaderCaps, &fshaderTxt);
    135     oPosition.setTypeModifier(GrShaderVar::kIn_TypeModifier);
    136     oPosition.appendDecl(shaderCaps, &fshaderTxt);
    137     fshaderTxt.append(";\n");
    138     oColor.setTypeModifier(GrShaderVar::kIn_TypeModifier);
    139     oColor.appendDecl(shaderCaps, &fshaderTxt);
    140     fshaderTxt.append(";\n");
    141 
    142     const char* fsOutName;
    143     if (shaderCaps->mustDeclareFragmentShaderOutput()) {
    144         oFragColor.appendDecl(shaderCaps, &fshaderTxt);
    145         fshaderTxt.append(";\n");
    146         fsOutName = oFragColor.c_str();
    147     } else {
    148         fsOutName = "sk_FragColor";
    149     }
    150 
    151 
    152     fshaderTxt.appendf(
    153             "void main()\n"
    154             "{\n"
    155             "    vec4 outputColor;\n"
    156             "    %s outputCoverage;\n"
    157             "    outputColor = vec4(%s, 1.0);\n"
    158             "    outputCoverage = %s;\n",
    159             fCoverageSetup == kUseVec4_CoverageSetup ? "vec4" : "float",
    160             oColor.getName().c_str(),
    161             fCoverageSetup == kUseVec4_CoverageSetup ? "vec4(1.0)" : "1.0"
    162             );
    163 
    164     float radius = 1.0f;
    165     for (uint32_t i = 0; i < fNumStages; i++) {
    166         float centerX = 1.0f - radius;
    167         float centerY = 1.0f - radius;
    168         fshaderTxt.appendf(
    169             "    {\n"
    170             "        float d = length(%s - vec2(%f, %f));\n"
    171             "        float edgeAlpha = clamp(100.0 * (%f - d), 0.0, 1.0);\n"
    172             "        outputCoverage = 0.5 * outputCoverage + 0.5 * %s;\n"
    173             "    }\n",
    174             oPosition.getName().c_str(), centerX, centerY,
    175             radius,
    176             fCoverageSetup == kUseVec4_CoverageSetup ? "vec4(edgeAlpha)" : "edgeAlpha"
    177             );
    178         radius *= 0.8f;
    179     }
    180     fshaderTxt.appendf(
    181             "    {\n"
    182             "        %s = outputColor * outputCoverage;\n"
    183             "    }\n"
    184             "}\n",
    185             fsOutName);
    186 
    187     return CreateProgram(ctx, vshaderTxt.c_str(), fshaderTxt.c_str());
    188 }
    189 
    190 template<typename Func>
    191 static void setup_matrices(int numQuads, Func f) {
    192     // We draw a really small triangle so we are not fill rate limited
    193     for (int i = 0 ; i < numQuads; i++) {
    194         SkMatrix m = SkMatrix::I();
    195         m.setScale(0.01f, 0.01f);
    196         f(m);
    197     }
    198 }
    199 
    200 ///////////////////////////////////////////////////////////////////////////////////////////////////
    201 
    202 struct Vertex {
    203     SkPoint fPositions;
    204     GrGLfloat fColors[3];
    205 };
    206 
    207 void GLVec4ScalarBench::setupSingleVbo(const GrGLInterface* gl, const SkMatrix* viewMatrices) {
    208     // triangles drawn will alternate between the top-right half of the screen and the bottom-left
    209     // half of the screen
    210     Vertex vertices[kVerticesPerTri * kNumTriPerDraw];
    211     for (uint32_t i = 0; i < kNumTriPerDraw; i++) {
    212         Vertex* v = &vertices[i * kVerticesPerTri];
    213         if (i % 2 == 0) {
    214             v[0].fPositions.set(-1.0f, -1.0f);
    215             v[1].fPositions.set( 1.0f, -1.0f);
    216             v[2].fPositions.set( 1.0f,  1.0f);
    217         } else {
    218             v[0].fPositions.set(-1.0f, -1.0f);
    219             v[1].fPositions.set( 1.0f, 1.0f);
    220             v[2].fPositions.set( -1.0f, 1.0f);
    221         }
    222         SkPoint* position = reinterpret_cast<SkPoint*>(v);
    223         viewMatrices[i].mapPointsWithStride(position, sizeof(Vertex), kVerticesPerTri);
    224 
    225         GrGLfloat color[3] = {1.0f, 0.0f, 1.0f};
    226         for (uint32_t j = 0; j < kVerticesPerTri; j++) {
    227             v->fColors[0] = color[0];
    228             v->fColors[1] = color[1];
    229             v->fColors[2] = color[2];
    230             v++;
    231         }
    232     }
    233 
    234     GR_GL_CALL(gl, GenBuffers(1, &fVboId));
    235     GR_GL_CALL(gl, BindBuffer(GR_GL_ARRAY_BUFFER, fVboId));
    236     GR_GL_CALL(gl, EnableVertexAttribArray(0));
    237     GR_GL_CALL(gl, EnableVertexAttribArray(1));
    238     GR_GL_CALL(gl, VertexAttribPointer(0, 2, GR_GL_FLOAT, GR_GL_FALSE, sizeof(Vertex),
    239                                        (GrGLvoid*)0));
    240     GR_GL_CALL(gl, VertexAttribPointer(1, 3, GR_GL_FLOAT, GR_GL_FALSE, sizeof(Vertex),
    241                                        (GrGLvoid*)(sizeof(SkPoint))));
    242     GR_GL_CALL(gl, BufferData(GR_GL_ARRAY_BUFFER, sizeof(vertices), vertices, GR_GL_STATIC_DRAW));
    243 }
    244 
    245 void GLVec4ScalarBench::setup(const GrGLContext* ctx) {
    246     const GrGLInterface* gl = ctx->interface();
    247     if (!gl) {
    248         SkFAIL("GL interface is nullptr in setup()!\n");
    249     }
    250     fFboTextureId = SetupFramebuffer(gl, kScreenWidth, kScreenHeight);
    251 
    252     fProgram = this->setupShader(ctx);
    253 
    254     int index = 0;
    255     SkMatrix viewMatrices[kNumTriPerDraw];
    256     setup_matrices(kNumTriPerDraw, [&index, &viewMatrices](const SkMatrix& m) {
    257         viewMatrices[index++] = m;
    258     });
    259     this->setupSingleVbo(gl, viewMatrices);
    260 
    261     GR_GL_CALL(gl, UseProgram(fProgram));
    262 }
    263 
    264 void GLVec4ScalarBench::glDraw(int loops, const GrGLContext* ctx) {
    265     const GrGLInterface* gl = ctx->interface();
    266 
    267     for (int i = 0; i < loops; i++) {
    268         GR_GL_CALL(gl, DrawArrays(GR_GL_TRIANGLES, 0, kVerticesPerTri * kNumTriPerDraw));
    269     }
    270 
    271 // using -w when running nanobench will not produce correct images;
    272 // changing this to #if 1 will write the correct images to the Skia folder.
    273 #if 0
    274     SkString filename("out");
    275     filename.appendf("_%s.png", this->getName());
    276     DumpImage(gl, kScreenWidth, kScreenHeight, filename.c_str());
    277 #endif
    278 }
    279 
    280 void GLVec4ScalarBench::teardown(const GrGLInterface* gl) {
    281     GR_GL_CALL(gl, BindBuffer(GR_GL_ARRAY_BUFFER, 0));
    282     GR_GL_CALL(gl, BindTexture(GR_GL_TEXTURE_2D, 0));
    283     GR_GL_CALL(gl, BindFramebuffer(GR_GL_FRAMEBUFFER, 0));
    284     GR_GL_CALL(gl, DeleteTextures(1, &fFboTextureId));
    285     GR_GL_CALL(gl, DeleteProgram(fProgram));
    286     GR_GL_CALL(gl, DeleteBuffers(1, &fVboId));
    287 }
    288 
    289 ///////////////////////////////////////////////////////////////////////////////
    290 
    291 DEF_BENCH( return new GLVec4ScalarBench(GLVec4ScalarBench::kUseScalar_CoverageSetup, 1) )
    292 DEF_BENCH( return new GLVec4ScalarBench(GLVec4ScalarBench::kUseVec4_CoverageSetup, 1) )
    293 DEF_BENCH( return new GLVec4ScalarBench(GLVec4ScalarBench::kUseScalar_CoverageSetup, 2) )
    294 DEF_BENCH( return new GLVec4ScalarBench(GLVec4ScalarBench::kUseVec4_CoverageSetup, 2) )
    295 DEF_BENCH( return new GLVec4ScalarBench(GLVec4ScalarBench::kUseScalar_CoverageSetup, 4) )
    296 DEF_BENCH( return new GLVec4ScalarBench(GLVec4ScalarBench::kUseVec4_CoverageSetup, 4) )
    297 DEF_BENCH( return new GLVec4ScalarBench(GLVec4ScalarBench::kUseScalar_CoverageSetup, 6) )
    298 DEF_BENCH( return new GLVec4ScalarBench(GLVec4ScalarBench::kUseVec4_CoverageSetup, 6) )
    299 DEF_BENCH( return new GLVec4ScalarBench(GLVec4ScalarBench::kUseScalar_CoverageSetup, 8) )
    300 DEF_BENCH( return new GLVec4ScalarBench(GLVec4ScalarBench::kUseVec4_CoverageSetup, 8) )
    301 
    302 #endif
    303