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