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