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 "Benchmark.h" 9 #include "SkCanvas.h" 10 #include "SkImageEncoder.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 /* 22 * This is a native GL benchmark for instanced arrays vs vertex buffer objects. To benchmark this 23 * functionality, we draw n * kDrawMultipier triangles per run. If this number is less than 24 * kNumTri then we do a single draw, either with instances, or drawArrays. Otherwise we do 25 * multiple draws. 26 * 27 * Additionally, there is a divisor, which if > 0 will act as a multiplier for the number of draws 28 * issued. 29 */ 30 31 class GLCpuPosInstancedArraysBench : public GLBench { 32 public: 33 /* 34 * Clients can decide to use either: 35 * kUseOne_VboSetup - one vertex buffer with colors and positions interleaved 36 * kUseTwo_VboSetup - two vertex buffers, one for colors, one for positions 37 * kUseInstance_VboSetup - two vertex buffers, one with per vertex indices, one with per 38 * instance colors 39 */ 40 enum VboSetup { 41 kUseOne_VboSetup, 42 kUseTwo_VboSetup, 43 kUseInstance_VboSetup, 44 }; 45 46 /* 47 * drawDiv will act as a multiplier for the number of draws we issue if > 0. ie, 2 will issue 48 * 2x as many draws, 4 will issue 4x as many draws etc. There is a limit however, which is 49 * kDrawMultipier. 50 */ 51 GLCpuPosInstancedArraysBench(VboSetup vboSetup, int32_t drawDiv) 52 : fVboSetup(vboSetup) 53 , fDrawDiv(drawDiv) 54 , fProgram(0) 55 , fVAO(0) { 56 fName = VboSetupToStr(vboSetup, fDrawDiv); 57 } 58 59 protected: 60 const char* onGetName() override { 61 return fName.c_str(); 62 } 63 64 const GrGLContext* onGetGLContext(const GrGLContext*) override; 65 void setup(const GrGLContext*) override; 66 void glDraw(int loops, const GrGLContext*) override; 67 void teardown(const GrGLInterface*) override; 68 69 private: 70 void setupInstanceVbo(const GrGLInterface*, const SkMatrix*); 71 void setupDoubleVbo(const GrGLInterface*, const SkMatrix*); 72 void setupSingleVbo(const GrGLInterface*, const SkMatrix*); 73 GrGLuint setupShader(const GrGLContext*); 74 75 static SkString VboSetupToStr(VboSetup vboSetup, uint32_t drawDiv) { 76 SkString name("GLInstancedArraysBench"); 77 switch (vboSetup) { 78 default: 79 case kUseOne_VboSetup: 80 name.appendf("_one_%u", drawDiv); 81 break; 82 case kUseTwo_VboSetup: 83 name.appendf("_two_%u", drawDiv); 84 break; 85 case kUseInstance_VboSetup: 86 name.append("_instance"); 87 break; 88 } 89 return name; 90 } 91 92 static const GrGLuint kScreenWidth = 800; 93 static const GrGLuint kScreenHeight = 600; 94 static const uint32_t kNumTri = 10000; 95 static const uint32_t kVerticesPerTri = 3; 96 static const uint32_t kDrawMultiplier = 512; 97 98 SkString fName; 99 VboSetup fVboSetup; 100 uint32_t fDrawDiv; 101 SkTArray<GrGLuint> fBuffers; 102 GrGLuint fProgram; 103 GrGLuint fVAO; 104 GrGLuint fTexture; 105 }; 106 107 /////////////////////////////////////////////////////////////////////////////////////////////////// 108 109 GrGLuint GLCpuPosInstancedArraysBench::setupShader(const GrGLContext* ctx) { 110 const GrGLSLCaps* glslCaps = ctx->caps()->glslCaps(); 111 const char* version = glslCaps->versionDeclString(); 112 113 // setup vertex shader 114 GrGLSLShaderVar aPosition("a_position", kVec2f_GrSLType, GrShaderVar::kAttribute_TypeModifier); 115 GrGLSLShaderVar aColor("a_color", kVec3f_GrSLType, GrShaderVar::kAttribute_TypeModifier); 116 GrGLSLShaderVar oColor("o_color", kVec3f_GrSLType, GrShaderVar::kVaryingOut_TypeModifier); 117 118 SkString vshaderTxt(version); 119 aPosition.appendDecl(glslCaps, &vshaderTxt); 120 vshaderTxt.append(";\n"); 121 aColor.appendDecl(glslCaps, &vshaderTxt); 122 vshaderTxt.append(";\n"); 123 oColor.appendDecl(glslCaps, &vshaderTxt); 124 vshaderTxt.append(";\n"); 125 126 vshaderTxt.append( 127 "void main()\n" 128 "{\n" 129 "gl_Position = vec4(a_position, 0., 1.);\n" 130 "o_color = a_color;\n" 131 "}\n"); 132 133 const GrGLInterface* gl = ctx->interface(); 134 135 // setup fragment shader 136 GrGLSLShaderVar oFragColor("o_FragColor", kVec4f_GrSLType, GrShaderVar::kOut_TypeModifier); 137 SkString fshaderTxt(version); 138 GrGLSLAppendDefaultFloatPrecisionDeclaration(kDefault_GrSLPrecision, *glslCaps, &fshaderTxt); 139 oColor.setTypeModifier(GrShaderVar::kVaryingIn_TypeModifier); 140 oColor.appendDecl(glslCaps, &fshaderTxt); 141 fshaderTxt.append(";\n"); 142 143 const char* fsOutName; 144 if (glslCaps->mustDeclareFragmentShaderOutput()) { 145 oFragColor.appendDecl(glslCaps, &fshaderTxt); 146 fshaderTxt.append(";\n"); 147 fsOutName = oFragColor.c_str(); 148 } else { 149 fsOutName = "gl_FragColor"; 150 } 151 152 fshaderTxt.appendf( 153 "void main()\n" 154 "{\n" 155 "%s = vec4(o_color, 1.0);\n" 156 "}\n", fsOutName); 157 158 return CreateProgram(gl, vshaderTxt.c_str(), fshaderTxt.c_str()); 159 } 160 161 template<typename Func> 162 static void setup_matrices(int numQuads, Func f) { 163 // We draw a really small triangle so we are not fill rate limited 164 for (int i = 0 ; i < numQuads; i++) { 165 SkMatrix m = SkMatrix::I(); 166 m.setScale(0.0001f, 0.0001f); 167 f(m); 168 } 169 } 170 171 /////////////////////////////////////////////////////////////////////////////////////////////////// 172 173 const GrGLContext* GLCpuPosInstancedArraysBench::onGetGLContext(const GrGLContext* ctx) { 174 // We only care about gpus with drawArraysInstanced support 175 if (!ctx->interface()->fFunctions.fDrawArraysInstanced) { 176 return nullptr; 177 } 178 return ctx; 179 } 180 181 void GLCpuPosInstancedArraysBench::setupInstanceVbo(const GrGLInterface* gl, 182 const SkMatrix* viewMatrices) { 183 // We draw all of the instances at a single place because we aren't allowed to have per vertex 184 // per instance attributes 185 SkPoint positions[kVerticesPerTri]; 186 positions[0].set(-1.0f, -1.0f); 187 positions[1].set( 1.0f, -1.0f); 188 positions[2].set( 1.0f, 1.0f); 189 viewMatrices[0].mapPointsWithStride(positions, sizeof(SkPoint), kVerticesPerTri); 190 191 // setup colors so we can detect we are actually drawing instances(the last triangle will be 192 // a different color) 193 GrGLfloat colors[kVerticesPerTri * kNumTri]; 194 for (uint32_t i = 0; i < kNumTri; i++) { 195 // set colors 196 uint32_t offset = i * kVerticesPerTri; 197 float color = i == kNumTri - 1 ? 1.0f : 0.0f; 198 colors[offset++] = color; colors[offset++] = 0.0f; colors[offset++] = 0.0f; 199 } 200 201 GrGLuint posVBO; 202 // setup position VBO 203 GR_GL_CALL(gl, GenBuffers(1, &posVBO)); 204 GR_GL_CALL(gl, BindBuffer(GR_GL_ARRAY_BUFFER, posVBO)); 205 GR_GL_CALL(gl, BufferData(GR_GL_ARRAY_BUFFER, sizeof(positions), positions, GR_GL_STATIC_DRAW)); 206 GR_GL_CALL(gl, EnableVertexAttribArray(0)); 207 GR_GL_CALL(gl, VertexAttribPointer(0, 2, GR_GL_FLOAT, GR_GL_FALSE, 2 * sizeof(GrGLfloat), 208 (GrGLvoid*)0)); 209 210 // setup color VBO 211 GrGLuint instanceVBO; 212 GR_GL_CALL(gl, GenBuffers(1, &instanceVBO)); 213 GR_GL_CALL(gl, BindBuffer(GR_GL_ARRAY_BUFFER, instanceVBO)); 214 GR_GL_CALL(gl, BufferData(GR_GL_ARRAY_BUFFER, sizeof(colors), colors, GR_GL_STATIC_DRAW)); 215 GR_GL_CALL(gl, EnableVertexAttribArray(1)); 216 GR_GL_CALL(gl, VertexAttribPointer(1, 3, GR_GL_FLOAT, GR_GL_FALSE, 3 * sizeof(GrGLfloat), 217 (GrGLvoid*)0)); 218 GR_GL_CALL(gl, VertexAttribDivisor(1, 1)); 219 fBuffers.push_back(posVBO); 220 fBuffers.push_back(instanceVBO); 221 } 222 223 void GLCpuPosInstancedArraysBench::setupDoubleVbo(const GrGLInterface* gl, 224 const SkMatrix* viewMatrices) { 225 // Constants for our various shader programs 226 SkPoint positions[kVerticesPerTri * kNumTri]; 227 GrGLfloat colors[kVerticesPerTri * kNumTri * 3]; 228 for (uint32_t i = 0; i < kNumTri; i++) { 229 SkPoint* position = &positions[i * kVerticesPerTri]; 230 position[0].set(-1.0f, -1.0f); 231 position[1].set( 1.0f, -1.0f); 232 position[2].set( 1.0f, 1.0f); 233 viewMatrices[i].mapPointsWithStride(position, sizeof(SkPoint), kVerticesPerTri); 234 235 // set colors 236 float color = i == kNumTri - 1 ? 1.0f : 0.0f; 237 uint32_t offset = i * kVerticesPerTri * 3; 238 for (uint32_t j = 0; j < kVerticesPerTri; j++) { 239 colors[offset++] = color; colors[offset++] = 0.0f; colors[offset++] = 0.0f; 240 } 241 } 242 243 GrGLuint posVBO, colorVBO; 244 // setup position VBO 245 GR_GL_CALL(gl, GenBuffers(1, &posVBO)); 246 GR_GL_CALL(gl, BindBuffer(GR_GL_ARRAY_BUFFER, posVBO)); 247 GR_GL_CALL(gl, EnableVertexAttribArray(0)); 248 GR_GL_CALL(gl, VertexAttribPointer(0, 2, GR_GL_FLOAT, GR_GL_FALSE, 2 * sizeof(GrGLfloat), 249 (GrGLvoid*)0)); 250 GR_GL_CALL(gl, BufferData(GR_GL_ARRAY_BUFFER, sizeof(positions), positions, GR_GL_STATIC_DRAW)); 251 252 // setup color VBO 253 GR_GL_CALL(gl, GenBuffers(1, &colorVBO)); 254 GR_GL_CALL(gl, BindBuffer(GR_GL_ARRAY_BUFFER, colorVBO)); 255 GR_GL_CALL(gl, EnableVertexAttribArray(1)); 256 GR_GL_CALL(gl, VertexAttribPointer(1, 3, GR_GL_FLOAT, GR_GL_FALSE, 3 * sizeof(GrGLfloat), 257 (GrGLvoid*)0)); 258 GR_GL_CALL(gl, BufferData(GR_GL_ARRAY_BUFFER, sizeof(colors), colors, GR_GL_STATIC_DRAW)); 259 260 fBuffers.push_back(posVBO); 261 fBuffers.push_back(colorVBO); 262 } 263 264 struct Vertex { 265 SkPoint fPositions; 266 GrGLfloat fColors[3]; 267 }; 268 269 void GLCpuPosInstancedArraysBench::setupSingleVbo(const GrGLInterface* gl, 270 const SkMatrix* viewMatrices) { 271 // Constants for our various shader programs 272 Vertex vertices[kVerticesPerTri * kNumTri]; 273 for (uint32_t i = 0; i < kNumTri; i++) { 274 Vertex* v = &vertices[i * kVerticesPerTri]; 275 v[0].fPositions.set(-1.0f, -1.0f); 276 v[1].fPositions.set( 1.0f, -1.0f); 277 v[2].fPositions.set( 1.0f, 1.0f); 278 279 SkPoint* position = reinterpret_cast<SkPoint*>(v); 280 viewMatrices[i].mapPointsWithStride(position, sizeof(Vertex), kVerticesPerTri); 281 282 // set colors 283 float color = i == kNumTri - 1 ? 1.0f : 0.0f; 284 for (uint32_t j = 0; j < kVerticesPerTri; j++) { 285 uint32_t offset = 0; 286 v->fColors[offset++] = color; v->fColors[offset++] = 0.0f; v->fColors[offset++] = 0.0f; 287 v++; 288 } 289 } 290 291 GrGLuint vbo; 292 // setup VBO 293 GR_GL_CALL(gl, GenBuffers(1, &vbo)); 294 GR_GL_CALL(gl, BindBuffer(GR_GL_ARRAY_BUFFER, vbo)); 295 GR_GL_CALL(gl, EnableVertexAttribArray(0)); 296 GR_GL_CALL(gl, EnableVertexAttribArray(1)); 297 GR_GL_CALL(gl, VertexAttribPointer(0, 2, GR_GL_FLOAT, GR_GL_FALSE, sizeof(Vertex), 298 (GrGLvoid*)0)); 299 GR_GL_CALL(gl, VertexAttribPointer(1, 3, GR_GL_FLOAT, GR_GL_FALSE, sizeof(Vertex), 300 (GrGLvoid*)(sizeof(SkPoint)))); 301 GR_GL_CALL(gl, BufferData(GR_GL_ARRAY_BUFFER, sizeof(vertices), vertices, GR_GL_STATIC_DRAW)); 302 fBuffers.push_back(vbo); 303 } 304 305 void GLCpuPosInstancedArraysBench::setup(const GrGLContext* ctx) { 306 const GrGLInterface* gl = ctx->interface(); 307 fTexture = SetupFramebuffer(gl, kScreenWidth, kScreenHeight); 308 309 fProgram = this->setupShader(ctx); 310 311 // setup matrices 312 int index = 0; 313 SkMatrix viewMatrices[kNumTri]; 314 setup_matrices(kNumTri, [&index, &viewMatrices](const SkMatrix& m) { 315 viewMatrices[index++] = m; 316 }); 317 318 // setup VAO 319 GR_GL_CALL(gl, GenVertexArrays(1, &fVAO)); 320 GR_GL_CALL(gl, BindVertexArray(fVAO)); 321 322 switch (fVboSetup) { 323 case kUseOne_VboSetup: 324 this->setupSingleVbo(gl, viewMatrices); 325 break; 326 case kUseTwo_VboSetup: 327 this->setupDoubleVbo(gl, viewMatrices); 328 break; 329 case kUseInstance_VboSetup: 330 this->setupInstanceVbo(gl, viewMatrices); 331 break; 332 } 333 334 // clear screen 335 GR_GL_CALL(gl, ClearColor(0.03f, 0.03f, 0.03f, 1.0f)); 336 GR_GL_CALL(gl, Clear(GR_GL_COLOR_BUFFER_BIT)); 337 338 // set us up to draw 339 GR_GL_CALL(gl, UseProgram(fProgram)); 340 GR_GL_CALL(gl, BindVertexArray(fVAO)); 341 } 342 343 void GLCpuPosInstancedArraysBench::glDraw(int loops, const GrGLContext* ctx) { 344 const GrGLInterface* gl = ctx->interface(); 345 346 uint32_t maxTrianglesPerFlush = fDrawDiv == 0 ? kNumTri : 347 kDrawMultiplier / fDrawDiv; 348 uint32_t trianglesToDraw = loops * kDrawMultiplier; 349 350 if (kUseInstance_VboSetup == fVboSetup) { 351 while (trianglesToDraw > 0) { 352 uint32_t triangles = SkTMin(trianglesToDraw, maxTrianglesPerFlush); 353 GR_GL_CALL(gl, DrawArraysInstanced(GR_GL_TRIANGLES, 0, kVerticesPerTri, triangles)); 354 trianglesToDraw -= triangles; 355 } 356 } else { 357 while (trianglesToDraw > 0) { 358 uint32_t triangles = SkTMin(trianglesToDraw, maxTrianglesPerFlush); 359 GR_GL_CALL(gl, DrawArrays(GR_GL_TRIANGLES, 0, kVerticesPerTri * triangles)); 360 trianglesToDraw -= triangles; 361 } 362 } 363 364 #if 0 365 //const char* filename = "/data/local/tmp/out.png"; 366 SkString filename("out"); 367 filename.appendf("_%s.png", this->getName()); 368 DumpImage(gl, kScreenWidth, kScreenHeight, filename.c_str()); 369 #endif 370 } 371 372 void GLCpuPosInstancedArraysBench::teardown(const GrGLInterface* gl) { 373 GR_GL_CALL(gl, BindBuffer(GR_GL_ARRAY_BUFFER, 0)); 374 GR_GL_CALL(gl, BindVertexArray(0)); 375 GR_GL_CALL(gl, BindTexture(GR_GL_TEXTURE_2D, 0)); 376 GR_GL_CALL(gl, BindFramebuffer(GR_GL_FRAMEBUFFER, 0)); 377 GR_GL_CALL(gl, DeleteTextures(1, &fTexture)); 378 GR_GL_CALL(gl, DeleteProgram(fProgram)); 379 GR_GL_CALL(gl, DeleteBuffers(fBuffers.count(), fBuffers.begin())); 380 GR_GL_CALL(gl, DeleteVertexArrays(1, &fVAO)); 381 fBuffers.reset(); 382 } 383 384 /////////////////////////////////////////////////////////////////////////////// 385 386 DEF_BENCH( return new GLCpuPosInstancedArraysBench(GLCpuPosInstancedArraysBench::kUseInstance_VboSetup, 0) ) 387 DEF_BENCH( return new GLCpuPosInstancedArraysBench(GLCpuPosInstancedArraysBench::kUseOne_VboSetup, 0) ) 388 DEF_BENCH( return new GLCpuPosInstancedArraysBench(GLCpuPosInstancedArraysBench::kUseTwo_VboSetup, 0) ) 389 DEF_BENCH( return new GLCpuPosInstancedArraysBench(GLCpuPosInstancedArraysBench::kUseOne_VboSetup, 1) ) 390 DEF_BENCH( return new GLCpuPosInstancedArraysBench(GLCpuPosInstancedArraysBench::kUseTwo_VboSetup, 1) ) 391 DEF_BENCH( return new GLCpuPosInstancedArraysBench(GLCpuPosInstancedArraysBench::kUseOne_VboSetup, 2) ) 392 DEF_BENCH( return new GLCpuPosInstancedArraysBench(GLCpuPosInstancedArraysBench::kUseTwo_VboSetup, 2) ) 393 DEF_BENCH( return new GLCpuPosInstancedArraysBench(GLCpuPosInstancedArraysBench::kUseOne_VboSetup, 4) ) 394 DEF_BENCH( return new GLCpuPosInstancedArraysBench(GLCpuPosInstancedArraysBench::kUseTwo_VboSetup, 4) ) 395 DEF_BENCH( return new GLCpuPosInstancedArraysBench(GLCpuPosInstancedArraysBench::kUseOne_VboSetup, 8) ) 396 DEF_BENCH( return new GLCpuPosInstancedArraysBench(GLCpuPosInstancedArraysBench::kUseTwo_VboSetup, 8) ) 397 398 #endif 399