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
30class GLVec4ScalarBench : public GLBench {
31public:
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
52protected:
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
61private:
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
95GrGLuint 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(kDefault_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
190template<typename Func>
191static 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
202struct Vertex {
203    SkPoint fPositions;
204    GrGLfloat fColors[3];
205};
206
207void 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
245void 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
264void 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
280void 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
291DEF_BENCH( return new GLVec4ScalarBench(GLVec4ScalarBench::kUseScalar_CoverageSetup, 1) )
292DEF_BENCH( return new GLVec4ScalarBench(GLVec4ScalarBench::kUseVec4_CoverageSetup, 1) )
293DEF_BENCH( return new GLVec4ScalarBench(GLVec4ScalarBench::kUseScalar_CoverageSetup, 2) )
294DEF_BENCH( return new GLVec4ScalarBench(GLVec4ScalarBench::kUseVec4_CoverageSetup, 2) )
295DEF_BENCH( return new GLVec4ScalarBench(GLVec4ScalarBench::kUseScalar_CoverageSetup, 4) )
296DEF_BENCH( return new GLVec4ScalarBench(GLVec4ScalarBench::kUseVec4_CoverageSetup, 4) )
297DEF_BENCH( return new GLVec4ScalarBench(GLVec4ScalarBench::kUseScalar_CoverageSetup, 6) )
298DEF_BENCH( return new GLVec4ScalarBench(GLVec4ScalarBench::kUseVec4_CoverageSetup, 6) )
299DEF_BENCH( return new GLVec4ScalarBench(GLVec4ScalarBench::kUseScalar_CoverageSetup, 8) )
300DEF_BENCH( return new GLVec4ScalarBench(GLVec4ScalarBench::kUseVec4_CoverageSetup, 8) )
301
302#endif
303