• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "gl/GrGLContext.h"
15 #include "gl/GrGLInterface.h"
16 #include "gl/GrGLUtil.h"
17 #include "glsl/GrGLSL.h"
18 #include "glsl/GrGLSLCaps.h"
19 #include "glsl/GrGLSLShaderVar.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      */
GLVec4ScalarBench(CoverageSetup coverageSetup,uint32_t numStages)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:
onGetName()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 
NumStagesSetupToStr(CoverageSetup coverageSetup,uint32_t numStages)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 
setupShader(const GrGLContext * ctx)95 GrGLuint GLVec4ScalarBench::setupShader(const GrGLContext* ctx) {
96     const GrGLSLCaps* glslCaps = ctx->caps()->glslCaps();
97     const char* version = glslCaps->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     GrGLSLShaderVar aPosition("a_position", kVec2f_GrSLType, GrShaderVar::kAttribute_TypeModifier);
105     GrGLSLShaderVar oPosition("o_position", kVec2f_GrSLType, GrShaderVar::kVaryingOut_TypeModifier);
106     GrGLSLShaderVar aColor("a_color", kVec3f_GrSLType, GrShaderVar::kAttribute_TypeModifier);
107     GrGLSLShaderVar oColor("o_color", kVec3f_GrSLType, GrShaderVar::kVaryingOut_TypeModifier);
108 
109     SkString vshaderTxt(version);
110     aPosition.appendDecl(glslCaps, &vshaderTxt);
111     vshaderTxt.append(";\n");
112     aColor.appendDecl(glslCaps, &vshaderTxt);
113     vshaderTxt.append(";\n");
114     oPosition.appendDecl(glslCaps, &vshaderTxt);
115     vshaderTxt.append(";\n");
116     oColor.appendDecl(glslCaps, &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     const GrGLInterface* gl = ctx->interface();
128 
129     // set up fragment shader; this fragment shader will have fNumStages coverage stages plus an
130     // XP stage at the end.  Each coverage stage computes the pixel's distance from some hard-
131     // coded center and compare that to some hard-coded circle radius to compute a coverage.
132     // Then, this coverage is mixed with the coverage from the previous stage and passed to the
133     // next stage.
134     GrGLSLShaderVar oFragColor("o_FragColor", kVec4f_GrSLType, GrShaderVar::kOut_TypeModifier);
135     SkString fshaderTxt(version);
136     GrGLSLAppendDefaultFloatPrecisionDeclaration(kDefault_GrSLPrecision, *glslCaps, &fshaderTxt);
137     oPosition.setTypeModifier(GrShaderVar::kVaryingIn_TypeModifier);
138     oPosition.appendDecl(glslCaps, &fshaderTxt);
139     fshaderTxt.append(";\n");
140     oColor.setTypeModifier(GrShaderVar::kVaryingIn_TypeModifier);
141     oColor.appendDecl(glslCaps, &fshaderTxt);
142     fshaderTxt.append(";\n");
143 
144     const char* fsOutName;
145     if (glslCaps->mustDeclareFragmentShaderOutput()) {
146         oFragColor.appendDecl(glslCaps, &fshaderTxt);
147         fshaderTxt.append(";\n");
148         fsOutName = oFragColor.c_str();
149     } else {
150         fsOutName = "gl_FragColor";
151     }
152 
153 
154     fshaderTxt.appendf(
155             "void main()\n"
156             "{\n"
157             "    vec4 outputColor;\n"
158             "    %s outputCoverage;\n"
159             "    outputColor = vec4(%s, 1.0);\n"
160             "    outputCoverage = %s;\n",
161             fCoverageSetup == kUseVec4_CoverageSetup ? "vec4" : "float",
162             oColor.getName().c_str(),
163             fCoverageSetup == kUseVec4_CoverageSetup ? "vec4(1.0)" : "1.0"
164             );
165 
166     float radius = 1.0f;
167     for (uint32_t i = 0; i < fNumStages; i++) {
168         float centerX = 1.0f - radius;
169         float centerY = 1.0f - radius;
170         fshaderTxt.appendf(
171             "    {\n"
172             "        float d = length(%s - vec2(%f, %f));\n"
173             "        float edgeAlpha = clamp(100.0 * (%f - d), 0.0, 1.0);\n"
174             "        outputCoverage = 0.5 * outputCoverage + 0.5 * %s;\n"
175             "    }\n",
176             oPosition.getName().c_str(), centerX, centerY,
177             radius,
178             fCoverageSetup == kUseVec4_CoverageSetup ? "vec4(edgeAlpha)" : "edgeAlpha"
179             );
180         radius *= 0.8f;
181     }
182     fshaderTxt.appendf(
183             "    {\n"
184             "        %s = outputColor * outputCoverage;\n"
185             "    }\n"
186             "}\n",
187             fsOutName);
188 
189     return CreateProgram(gl, vshaderTxt.c_str(), fshaderTxt.c_str());
190 }
191 
192 template<typename Func>
setup_matrices(int numQuads,Func f)193 static void setup_matrices(int numQuads, Func f) {
194     // We draw a really small triangle so we are not fill rate limited
195     for (int i = 0 ; i < numQuads; i++) {
196         SkMatrix m = SkMatrix::I();
197         m.setScale(0.01f, 0.01f);
198         f(m);
199     }
200 }
201 
202 ///////////////////////////////////////////////////////////////////////////////////////////////////
203 
204 struct Vertex {
205     SkPoint fPositions;
206     GrGLfloat fColors[3];
207 };
208 
setupSingleVbo(const GrGLInterface * gl,const SkMatrix * viewMatrices)209 void GLVec4ScalarBench::setupSingleVbo(const GrGLInterface* gl, const SkMatrix* viewMatrices) {
210     // triangles drawn will alternate between the top-right half of the screen and the bottom-left
211     // half of the screen
212     Vertex vertices[kVerticesPerTri * kNumTriPerDraw];
213     for (uint32_t i = 0; i < kNumTriPerDraw; i++) {
214         Vertex* v = &vertices[i * kVerticesPerTri];
215         if (i % 2 == 0) {
216             v[0].fPositions.set(-1.0f, -1.0f);
217             v[1].fPositions.set( 1.0f, -1.0f);
218             v[2].fPositions.set( 1.0f,  1.0f);
219         } else {
220             v[0].fPositions.set(-1.0f, -1.0f);
221             v[1].fPositions.set( 1.0f, 1.0f);
222             v[2].fPositions.set( -1.0f, 1.0f);
223         }
224         SkPoint* position = reinterpret_cast<SkPoint*>(v);
225         viewMatrices[i].mapPointsWithStride(position, sizeof(Vertex), kVerticesPerTri);
226 
227         GrGLfloat color[3] = {1.0f, 0.0f, 1.0f};
228         for (uint32_t j = 0; j < kVerticesPerTri; j++) {
229             v->fColors[0] = color[0];
230             v->fColors[1] = color[1];
231             v->fColors[2] = color[2];
232             v++;
233         }
234     }
235 
236     GR_GL_CALL(gl, GenBuffers(1, &fVboId));
237     GR_GL_CALL(gl, BindBuffer(GR_GL_ARRAY_BUFFER, fVboId));
238     GR_GL_CALL(gl, EnableVertexAttribArray(0));
239     GR_GL_CALL(gl, EnableVertexAttribArray(1));
240     GR_GL_CALL(gl, VertexAttribPointer(0, 2, GR_GL_FLOAT, GR_GL_FALSE, sizeof(Vertex),
241                                        (GrGLvoid*)0));
242     GR_GL_CALL(gl, VertexAttribPointer(1, 3, GR_GL_FLOAT, GR_GL_FALSE, sizeof(Vertex),
243                                        (GrGLvoid*)(sizeof(SkPoint))));
244     GR_GL_CALL(gl, BufferData(GR_GL_ARRAY_BUFFER, sizeof(vertices), vertices, GR_GL_STATIC_DRAW));
245 }
246 
setup(const GrGLContext * ctx)247 void GLVec4ScalarBench::setup(const GrGLContext* ctx) {
248     const GrGLInterface* gl = ctx->interface();
249     if (!gl) {
250         SkFAIL("GL interface is nullptr in setup()!\n");
251     }
252     fFboTextureId = SetupFramebuffer(gl, kScreenWidth, kScreenHeight);
253 
254     fProgram = this->setupShader(ctx);
255 
256     int index = 0;
257     SkMatrix viewMatrices[kNumTriPerDraw];
258     setup_matrices(kNumTriPerDraw, [&index, &viewMatrices](const SkMatrix& m) {
259         viewMatrices[index++] = m;
260     });
261     this->setupSingleVbo(gl, viewMatrices);
262 
263     GR_GL_CALL(gl, UseProgram(fProgram));
264 }
265 
glDraw(int loops,const GrGLContext * ctx)266 void GLVec4ScalarBench::glDraw(int loops, const GrGLContext* ctx) {
267     const GrGLInterface* gl = ctx->interface();
268 
269     for (int i = 0; i < loops; i++) {
270         GR_GL_CALL(gl, DrawArrays(GR_GL_TRIANGLES, 0, kVerticesPerTri * kNumTriPerDraw));
271     }
272 
273 // using -w when running nanobench will not produce correct images;
274 // changing this to #if 1 will write the correct images to the Skia folder.
275 #if 0
276     SkString filename("out");
277     filename.appendf("_%s.png", this->getName());
278     DumpImage(gl, kScreenWidth, kScreenHeight, filename.c_str());
279 #endif
280 }
281 
teardown(const GrGLInterface * gl)282 void GLVec4ScalarBench::teardown(const GrGLInterface* gl) {
283     GR_GL_CALL(gl, BindBuffer(GR_GL_ARRAY_BUFFER, 0));
284     GR_GL_CALL(gl, BindTexture(GR_GL_TEXTURE_2D, 0));
285     GR_GL_CALL(gl, BindFramebuffer(GR_GL_FRAMEBUFFER, 0));
286     GR_GL_CALL(gl, DeleteTextures(1, &fFboTextureId));
287     GR_GL_CALL(gl, DeleteProgram(fProgram));
288     GR_GL_CALL(gl, DeleteBuffers(1, &fVboId));
289 }
290 
291 ///////////////////////////////////////////////////////////////////////////////
292 
293 DEF_BENCH( return new GLVec4ScalarBench(GLVec4ScalarBench::kUseScalar_CoverageSetup, 1) )
294 DEF_BENCH( return new GLVec4ScalarBench(GLVec4ScalarBench::kUseVec4_CoverageSetup, 1) )
295 DEF_BENCH( return new GLVec4ScalarBench(GLVec4ScalarBench::kUseScalar_CoverageSetup, 2) )
296 DEF_BENCH( return new GLVec4ScalarBench(GLVec4ScalarBench::kUseVec4_CoverageSetup, 2) )
297 DEF_BENCH( return new GLVec4ScalarBench(GLVec4ScalarBench::kUseScalar_CoverageSetup, 4) )
298 DEF_BENCH( return new GLVec4ScalarBench(GLVec4ScalarBench::kUseVec4_CoverageSetup, 4) )
299 DEF_BENCH( return new GLVec4ScalarBench(GLVec4ScalarBench::kUseScalar_CoverageSetup, 6) )
300 DEF_BENCH( return new GLVec4ScalarBench(GLVec4ScalarBench::kUseVec4_CoverageSetup, 6) )
301 DEF_BENCH( return new GLVec4ScalarBench(GLVec4ScalarBench::kUseScalar_CoverageSetup, 8) )
302 DEF_BENCH( return new GLVec4ScalarBench(GLVec4ScalarBench::kUseVec4_CoverageSetup, 8) )
303 
304 #endif
305