GLVertexAttributesBench.cpp revision bd929d95704d052a5c8477b881761eff03582487
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#if SK_SUPPORT_GPU 12#include "GrTest.h" 13#include "gl/GrGLGLSL.h" 14#include "gl/GrGLInterface.h" 15#include "gl/GrGLShaderVar.h" 16#include "gl/GrGLUtil.h" 17#include "glsl/GrGLSLCaps.h" 18#include <stdio.h> 19 20/* 21 * This is a native GL benchmark for determining the cost of uploading vertex attributes 22 */ 23class GLVertexAttributesBench : public Benchmark { 24public: 25 GLVertexAttributesBench(uint32_t attribs) 26 : fTexture(0) 27 , fBuffers(0) 28 , fProgram(0) 29 , fVBO(0) 30 , fAttribs(attribs) 31 , fStride(2 * sizeof(SkPoint) + fAttribs * sizeof(GrGLfloat) * 4) { 32 fName.appendf("GLVertexAttributesBench_%d", fAttribs); 33 } 34 35protected: 36 const char* onGetName() override { return fName.c_str(); } 37 void onPerCanvasPreDraw(SkCanvas* canvas) override; 38 void setup(const GrGLContext*); 39 void onDraw(const int loops, SkCanvas*) override; 40 void onPerCanvasPostDraw(SkCanvas* canvas) override; 41 42 static const GrGLuint kScreenWidth = 800; 43 static const GrGLuint kScreenHeight = 600; 44 static const uint32_t kNumTri = 10000; 45 static const uint32_t kVerticesPerTri = 3; 46 static const uint32_t kDrawMultiplier = 512; 47 static const uint32_t kMaxAttribs = 7; 48 49private: 50 GrGLuint fTexture; 51 SkTArray<GrGLuint> fBuffers; 52 GrGLuint fProgram; 53 GrGLuint fVBO; 54 SkTArray<unsigned char> fVertices; 55 uint32_t fAttribs; 56 size_t fStride; 57 SkString fName; 58 typedef Benchmark INHERITED; 59}; 60 61static const GrGLContext* get_gl_context(SkCanvas* canvas) { 62 // This bench exclusively tests GL calls directly 63 if (NULL == canvas->getGrContext()) { 64 return NULL; 65 } 66 GrContext* context = canvas->getGrContext(); 67 68 GrTestTarget tt; 69 context->getTestTarget(&tt); 70 if (!tt.target()) { 71 SkDebugf("Couldn't get Gr test target."); 72 return NULL; 73 } 74 75 const GrGLContext* ctx = tt.glContext(); 76 if (!ctx) { 77 SkDebugf("Couldn't get an interface\n"); 78 return NULL; 79 } 80 81 return ctx; 82} 83 84void GLVertexAttributesBench::onPerCanvasPreDraw(SkCanvas* canvas) { 85 // This bench exclusively tests GL calls directly 86 const GrGLContext* ctx = get_gl_context(canvas); 87 if (!ctx) { 88 return; 89 } 90 this->setup(ctx); 91} 92 93void GLVertexAttributesBench::onPerCanvasPostDraw(SkCanvas* canvas) { 94 // This bench exclusively tests GL calls directly 95 const GrGLContext* ctx = get_gl_context(canvas); 96 if (!ctx) { 97 return; 98 } 99 100 const GrGLInterface* gl = ctx->interface(); 101 102 // teardown 103 GR_GL_CALL(gl, BindBuffer(GR_GL_ARRAY_BUFFER, 0)); 104 GR_GL_CALL(gl, BindTexture(GR_GL_TEXTURE_2D, 0)); 105 GR_GL_CALL(gl, BindFramebuffer(GR_GL_FRAMEBUFFER, 0)); 106 GR_GL_CALL(gl, DeleteTextures(1, &fTexture)); 107 GR_GL_CALL(gl, DeleteProgram(fProgram)); 108 GR_GL_CALL(gl, DeleteBuffers(fBuffers.count(), fBuffers.begin())); 109} 110 111/////////////////////////////////////////////////////////////////////////////////////////////////// 112 113static GrGLuint load_shader(const GrGLInterface* gl, const char* shaderSrc, GrGLenum type) { 114 GrGLuint shader; 115 // Create the shader object 116 GR_GL_CALL_RET(gl, shader, CreateShader(type)); 117 118 // Load the shader source 119 GR_GL_CALL(gl, ShaderSource(shader, 1, &shaderSrc, NULL)); 120 121 // Compile the shader 122 GR_GL_CALL(gl, CompileShader(shader)); 123 124 // Check for compile time errors 125 GrGLint success; 126 GrGLchar infoLog[512]; 127 GR_GL_CALL(gl, GetShaderiv(shader, GR_GL_COMPILE_STATUS, &success)); 128 if (!success) { 129 GR_GL_CALL(gl, GetShaderInfoLog(shader, 512, NULL, infoLog)); 130 SkDebugf("ERROR::SHADER::COMPLIATION_FAILED: %s\n", infoLog); 131 } 132 133 return shader; 134} 135 136static GrGLuint compile_shader(const GrGLContext* ctx, uint32_t attribs, uint32_t maxAttribs) { 137 const char* version = GrGLGetGLSLVersionDecl(*ctx); 138 139 // setup vertex shader 140 GrGLShaderVar aPosition("a_position", kVec4f_GrSLType, GrShaderVar::kAttribute_TypeModifier); 141 SkTArray<GrGLShaderVar> aVars; 142 SkTArray<GrGLShaderVar> oVars; 143 144 SkString vshaderTxt(version); 145 aPosition.appendDecl(*ctx, &vshaderTxt); 146 vshaderTxt.append(";\n"); 147 148 for (uint32_t i = 0; i < attribs; i++) { 149 SkString aname; 150 aname.appendf("a_color_%d", i); 151 aVars.push_back(GrGLShaderVar(aname.c_str(), 152 kVec4f_GrSLType, 153 GrShaderVar::kAttribute_TypeModifier)); 154 aVars.back().appendDecl(*ctx, &vshaderTxt); 155 vshaderTxt.append(";\n"); 156 157 } 158 159 for (uint32_t i = 0; i < maxAttribs; i++) { 160 SkString oname; 161 oname.appendf("o_color_%d", i); 162 oVars.push_back(GrGLShaderVar(oname.c_str(), 163 kVec4f_GrSLType, 164 GrShaderVar::kVaryingOut_TypeModifier)); 165 oVars.back().appendDecl(*ctx, &vshaderTxt); 166 vshaderTxt.append(";\n"); 167 } 168 169 vshaderTxt.append( 170 "void main()\n" 171 "{\n" 172 "gl_Position = a_position;\n"); 173 174 for (uint32_t i = 0; i < attribs; i++) { 175 vshaderTxt.appendf("%s = %s;\n", oVars[i].c_str(), aVars[i].c_str()); 176 } 177 178 // Passthrough position as a dummy 179 for (uint32_t i = attribs; i < maxAttribs; i++) { 180 vshaderTxt.appendf("%s = vec4(0, 0, 0, 1);\n", oVars[i].c_str()); 181 } 182 183 vshaderTxt.append("}\n"); 184 185 const GrGLInterface* gl = ctx->interface(); 186 GrGLuint vertexShader = load_shader(gl, vshaderTxt.c_str(), GR_GL_VERTEX_SHADER); 187 188 // setup fragment shader 189 GrGLShaderVar oFragColor("o_FragColor", kVec4f_GrSLType, GrShaderVar::kOut_TypeModifier); 190 SkString fshaderTxt(version); 191 GrGLAppendGLSLDefaultFloatPrecisionDeclaration(kDefault_GrSLPrecision, gl->fStandard, 192 &fshaderTxt); 193 194 const char* fsOutName; 195 if (ctx->caps()->glslCaps()->mustDeclareFragmentShaderOutput()) { 196 oFragColor.appendDecl(*ctx, &fshaderTxt); 197 fshaderTxt.append(";\n"); 198 fsOutName = oFragColor.c_str(); 199 } else { 200 fsOutName = "gl_FragColor"; 201 } 202 203 for (uint32_t i = 0; i < maxAttribs; i++) { 204 oVars[i].setTypeModifier(GrShaderVar::kVaryingIn_TypeModifier); 205 oVars[i].appendDecl(*ctx, &fshaderTxt); 206 fshaderTxt.append(";\n"); 207 } 208 209 fshaderTxt.appendf( 210 "void main()\n" 211 "{\n" 212 "%s = ", fsOutName); 213 214 fshaderTxt.appendf("%s", oVars[0].c_str()); 215 for (uint32_t i = 1; i < maxAttribs; i++) { 216 fshaderTxt.appendf(" + %s", oVars[i].c_str()); 217 } 218 219 fshaderTxt.append(";\n" 220 "}\n"); 221 222 GrGLuint fragmentShader = load_shader(gl, fshaderTxt.c_str(), GR_GL_FRAGMENT_SHADER); 223 224 GrGLint shaderProgram; 225 GR_GL_CALL_RET(gl, shaderProgram, CreateProgram()); 226 GR_GL_CALL(gl, AttachShader(shaderProgram, vertexShader)); 227 GR_GL_CALL(gl, AttachShader(shaderProgram, fragmentShader)); 228 GR_GL_CALL(gl, LinkProgram(shaderProgram)); 229 230 // Check for linking errors 231 GrGLint success; 232 GrGLchar infoLog[512]; 233 GR_GL_CALL(gl, GetProgramiv(shaderProgram, GR_GL_LINK_STATUS, &success)); 234 if (!success) { 235 GR_GL_CALL(gl, GetProgramInfoLog(shaderProgram, 512, NULL, infoLog)); 236 SkDebugf("Linker Error: %s\n", infoLog); 237 } 238 GR_GL_CALL(gl, DeleteShader(vertexShader)); 239 GR_GL_CALL(gl, DeleteShader(fragmentShader)); 240 241 return shaderProgram; 242} 243 244//#define DUMP_IMAGES 245#ifdef DUMP_IMAGES 246static void dump_image(const GrGLInterface* gl, uint32_t screenWidth, uint32_t screenHeight, 247 const char* filename) { 248 // read back pixels 249 uint32_t readback[screenWidth * screenHeight]; 250 GR_GL_CALL(gl, ReadPixels(0, // x 251 0, // y 252 screenWidth, // width 253 screenHeight, // height 254 GR_GL_RGBA, //format 255 GR_GL_UNSIGNED_BYTE, //type 256 readback)); 257 258 // dump png 259 SkBitmap bm; 260 if (!bm.tryAllocPixels(SkImageInfo::MakeN32Premul(screenWidth, screenHeight))) { 261 SkDebugf("couldn't allocate bitmap\n"); 262 return; 263 } 264 265 bm.setPixels(readback); 266 267 if (!SkImageEncoder::EncodeFile(filename, bm, SkImageEncoder::kPNG_Type, 100)) { 268 SkDebugf("------ failed to encode %s\n", filename); 269 remove(filename); // remove any partial file 270 return; 271 } 272} 273#endif 274 275static void setup_framebuffer(const GrGLInterface* gl, int screenWidth, int screenHeight) { 276 //Setup framebuffer 277 GrGLuint texture; 278 GR_GL_CALL(gl, GenTextures(1, &texture)); 279 GR_GL_CALL(gl, ActiveTexture(GR_GL_TEXTURE15)); 280 GR_GL_CALL(gl, BindTexture(GR_GL_TEXTURE_2D, texture)); 281 GR_GL_CALL(gl, TexParameteri(GR_GL_TEXTURE_2D, GR_GL_TEXTURE_MAG_FILTER, GR_GL_NEAREST)); 282 GR_GL_CALL(gl, TexParameteri(GR_GL_TEXTURE_2D, GR_GL_TEXTURE_MIN_FILTER, GR_GL_NEAREST)); 283 GR_GL_CALL(gl, TexParameteri(GR_GL_TEXTURE_2D, GR_GL_TEXTURE_WRAP_S, GR_GL_CLAMP_TO_EDGE)); 284 GR_GL_CALL(gl, TexParameteri(GR_GL_TEXTURE_2D, GR_GL_TEXTURE_WRAP_T, GR_GL_CLAMP_TO_EDGE)); 285 GR_GL_CALL(gl, TexImage2D(GR_GL_TEXTURE_2D, 286 0, //level 287 GR_GL_RGBA, //internal format 288 screenWidth, // width 289 screenHeight, // height 290 0, //border 291 GR_GL_RGBA, //format 292 GR_GL_UNSIGNED_BYTE, // type 293 NULL)); 294 295 // bind framebuffer 296 GrGLuint framebuffer; 297 GR_GL_CALL(gl, BindTexture(GR_GL_TEXTURE_2D, 0)); 298 GR_GL_CALL(gl, GenFramebuffers(1, &framebuffer)); 299 GR_GL_CALL(gl, BindFramebuffer(GR_GL_FRAMEBUFFER, framebuffer)); 300 GR_GL_CALL(gl, FramebufferTexture2D(GR_GL_FRAMEBUFFER, 301 GR_GL_COLOR_ATTACHMENT0, 302 GR_GL_TEXTURE_2D, 303 texture, 0)); 304 GR_GL_CALL(gl, CheckFramebufferStatus(GR_GL_FRAMEBUFFER)); 305 GR_GL_CALL(gl, Viewport(0, 0, screenWidth, screenHeight)); 306} 307 308/////////////////////////////////////////////////////////////////////////////////////////////////// 309 310void GLVertexAttributesBench::setup(const GrGLContext* ctx) { 311 const GrGLInterface* gl = ctx->interface(); 312 setup_framebuffer(gl, kScreenWidth, kScreenHeight); 313 314 fProgram = compile_shader(ctx, fAttribs, kMaxAttribs); 315 316 // setup matrices 317 SkMatrix viewMatrices[kNumTri]; 318 for (uint32_t i = 0 ; i < kNumTri; i++) { 319 SkMatrix m = SkMatrix::I(); 320 m.setScale(0.0001f, 0.0001f); 321 viewMatrices[i] = m; 322 } 323 324 // presetup vertex attributes, color is set to be a light gray no matter how many vertex 325 // attributes are used 326 float targetColor = 0.9f; 327 float colorContribution = targetColor / fAttribs; 328 fVertices.reset(static_cast<int>(kVerticesPerTri * kNumTri * fStride)); 329 for (uint32_t i = 0; i < kNumTri; i++) { 330 unsigned char* ptr = &fVertices[static_cast<int>(i * kVerticesPerTri * fStride)]; 331 SkPoint* p = reinterpret_cast<SkPoint*>(ptr); 332 p->set(-1.0f, -1.0f); p++; p->set( 0.0f, 1.0f); 333 p = reinterpret_cast<SkPoint*>(ptr + fStride); 334 p->set( 1.0f, -1.0f); p++; p->set( 0.0f, 1.0f); 335 p = reinterpret_cast<SkPoint*>(ptr + fStride * 2); 336 p->set( 1.0f, 1.0f); p++; p->set( 0.0f, 1.0f); 337 338 SkPoint* position = reinterpret_cast<SkPoint*>(ptr); 339 viewMatrices[i].mapPointsWithStride(position, fStride, kVerticesPerTri); 340 341 // set colors 342 for (uint32_t j = 0; j < kVerticesPerTri; j++) { 343 GrGLfloat* f = reinterpret_cast<GrGLfloat*>(ptr + 2 * sizeof(SkPoint) + fStride * j); 344 for (uint32_t k = 0; k < fAttribs * 4; k += 4) { 345 f[k] = colorContribution; 346 f[k + 1] = colorContribution; 347 f[k + 2] = colorContribution; 348 f[k + 3] = 1.0f; 349 } 350 } 351 } 352 353 GR_GL_CALL(gl, GenBuffers(1, &fVBO)); 354 fBuffers.push_back(fVBO); 355 356 // clear screen 357 GR_GL_CALL(gl, ClearColor(0.03f, 0.03f, 0.03f, 1.0f)); 358 GR_GL_CALL(gl, Clear(GR_GL_COLOR_BUFFER_BIT)); 359 360 // set us up to draw 361 GR_GL_CALL(gl, UseProgram(fProgram)); 362} 363 364void GLVertexAttributesBench::onDraw(const int loops, SkCanvas* canvas) { 365 const GrGLContext* ctx = get_gl_context(canvas); 366 if (!ctx) { 367 return; 368 } 369 370 const GrGLInterface* gl = ctx->interface(); 371 372 // upload vertex attributes 373 GR_GL_CALL(gl, BindBuffer(GR_GL_ARRAY_BUFFER, fVBO)); 374 GR_GL_CALL(gl, EnableVertexAttribArray(0)); 375 GR_GL_CALL(gl, VertexAttribPointer(0, 4, GR_GL_FLOAT, GR_GL_FALSE, (GrGLsizei)fStride, 376 (GrGLvoid*)0)); 377 378 size_t runningStride = 2 * sizeof(SkPoint); 379 for (uint32_t i = 0; i < fAttribs; i++) { 380 int attribId = i + 1; 381 GR_GL_CALL(gl, EnableVertexAttribArray(attribId)); 382 GR_GL_CALL(gl, VertexAttribPointer(attribId, 4, GR_GL_FLOAT, GR_GL_FALSE, 383 (GrGLsizei)fStride, (GrGLvoid*)(runningStride))); 384 runningStride += sizeof(GrGLfloat) * 4; 385 } 386 387 GR_GL_CALL(gl, BufferData(GR_GL_ARRAY_BUFFER, fVertices.count(), fVertices.begin(), 388 GR_GL_STREAM_DRAW)); 389 390 uint32_t maxTrianglesPerFlush = kNumTri; 391 uint32_t trianglesToDraw = loops * kDrawMultiplier; 392 393 while (trianglesToDraw > 0) { 394 uint32_t triangles = SkTMin(trianglesToDraw, maxTrianglesPerFlush); 395 GR_GL_CALL(gl, DrawArrays(GR_GL_TRIANGLES, 0, kVerticesPerTri * triangles)); 396 trianglesToDraw -= triangles; 397 } 398 399#ifdef DUMP_IMAGES 400 //const char* filename = "/data/local/tmp/out.png"; 401 SkString filename("out"); 402 filename.appendf("_%s.png", this->getName()); 403 dump_image(gl, kScreenWidth, kScreenHeight, filename.c_str()); 404#endif 405} 406 407 408/////////////////////////////////////////////////////////////////////////////// 409 410DEF_BENCH( return new GLVertexAttributesBench(0) ) 411DEF_BENCH( return new GLVertexAttributesBench(1) ) 412DEF_BENCH( return new GLVertexAttributesBench(2) ) 413DEF_BENCH( return new GLVertexAttributesBench(3) ) 414DEF_BENCH( return new GLVertexAttributesBench(4) ) 415DEF_BENCH( return new GLVertexAttributesBench(5) ) 416DEF_BENCH( return new GLVertexAttributesBench(6) ) 417DEF_BENCH( return new GLVertexAttributesBench(7) ) 418#endif 419