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