| /* |
| * Copyright 2015 Google Inc. |
| * |
| * Use of this source code is governed by a BSD-style license that can be |
| * found in the LICENSE file. |
| */ |
| |
| #include "Benchmark.h" |
| #include "SkCanvas.h" |
| #include "SkImageEncoder.h" |
| |
| #if SK_SUPPORT_GPU |
| #include "GLBench.h" |
| #include "gl/GrGLContext.h" |
| #include "gl/GrGLInterface.h" |
| #include "gl/GrGLUtil.h" |
| #include "glsl/GrGLSL.h" |
| #include "glsl/GrGLSLCaps.h" |
| #include "glsl/GrGLSLShaderVar.h" |
| #include <stdio.h> |
| |
| /* |
| * This is a native GL benchmark for determining the cost of uploading vertex attributes |
| */ |
| class GLVertexAttributesBench : public GLBench { |
| public: |
| GLVertexAttributesBench(uint32_t attribs) |
| : fTexture(0) |
| , fBuffers(0) |
| , fProgram(0) |
| , fVBO(0) |
| , fAttribs(attribs) |
| , fStride(2 * sizeof(SkPoint) + fAttribs * sizeof(GrGLfloat) * 4) { |
| fName.appendf("GLVertexAttributesBench_%d", fAttribs); |
| } |
| |
| protected: |
| const char* onGetName() override { return fName.c_str(); } |
| void setup(const GrGLContext*) override; |
| void glDraw(int loops, const GrGLContext*) override; |
| void teardown(const GrGLInterface*) override; |
| |
| static const GrGLuint kScreenWidth = 800; |
| static const GrGLuint kScreenHeight = 600; |
| static const uint32_t kNumTri = 10000; |
| static const uint32_t kVerticesPerTri = 3; |
| static const uint32_t kDrawMultiplier = 512; |
| static const uint32_t kMaxAttribs = 7; |
| |
| private: |
| GrGLuint setupShader(const GrGLContext*, uint32_t attribs, uint32_t maxAttribs); |
| |
| GrGLuint fTexture; |
| SkTArray<GrGLuint> fBuffers; |
| GrGLuint fProgram; |
| GrGLuint fVBO; |
| SkTArray<unsigned char> fVertices; |
| uint32_t fAttribs; |
| size_t fStride; |
| SkString fName; |
| typedef Benchmark INHERITED; |
| }; |
| |
| /////////////////////////////////////////////////////////////////////////////////////////////////// |
| |
| GrGLuint GLVertexAttributesBench::setupShader(const GrGLContext* ctx, uint32_t attribs, |
| uint32_t maxAttribs) { |
| const GrGLSLCaps* glslCaps = ctx->caps()->glslCaps(); |
| const char* version = glslCaps->versionDeclString(); |
| |
| // setup vertex shader |
| GrGLSLShaderVar aPosition("a_position", kVec4f_GrSLType, GrShaderVar::kIn_TypeModifier); |
| SkTArray<GrGLSLShaderVar> aVars; |
| SkTArray<GrGLSLShaderVar> oVars; |
| |
| SkString vshaderTxt(version); |
| aPosition.appendDecl(glslCaps, &vshaderTxt); |
| vshaderTxt.append(";\n"); |
| |
| for (uint32_t i = 0; i < attribs; i++) { |
| SkString aname; |
| aname.appendf("a_color_%d", i); |
| aVars.push_back(GrGLSLShaderVar(aname.c_str(), |
| kVec4f_GrSLType, |
| GrShaderVar::kIn_TypeModifier)); |
| aVars.back().appendDecl(glslCaps, &vshaderTxt); |
| vshaderTxt.append(";\n"); |
| |
| } |
| |
| for (uint32_t i = 0; i < maxAttribs; i++) { |
| SkString oname; |
| oname.appendf("o_color_%d", i); |
| oVars.push_back(GrGLSLShaderVar(oname.c_str(), |
| kVec4f_GrSLType, |
| GrShaderVar::kOut_TypeModifier)); |
| oVars.back().appendDecl(glslCaps, &vshaderTxt); |
| vshaderTxt.append(";\n"); |
| } |
| |
| vshaderTxt.append( |
| "void main()\n" |
| "{\n" |
| "gl_Position = a_position;\n"); |
| |
| for (uint32_t i = 0; i < attribs; i++) { |
| vshaderTxt.appendf("%s = %s;\n", oVars[i].c_str(), aVars[i].c_str()); |
| } |
| |
| // Passthrough position as a dummy |
| for (uint32_t i = attribs; i < maxAttribs; i++) { |
| vshaderTxt.appendf("%s = vec4(0, 0, 0, 1);\n", oVars[i].c_str()); |
| } |
| |
| vshaderTxt.append("}\n"); |
| |
| // setup fragment shader |
| GrGLSLShaderVar oFragColor("o_FragColor", kVec4f_GrSLType, GrShaderVar::kOut_TypeModifier); |
| SkString fshaderTxt(version); |
| GrGLSLAppendDefaultFloatPrecisionDeclaration(kDefault_GrSLPrecision, *glslCaps, &fshaderTxt); |
| |
| const char* fsOutName; |
| if (glslCaps->mustDeclareFragmentShaderOutput()) { |
| oFragColor.appendDecl(glslCaps, &fshaderTxt); |
| fshaderTxt.append(";\n"); |
| fsOutName = oFragColor.c_str(); |
| } else { |
| fsOutName = "sk_FragColor"; |
| } |
| |
| for (uint32_t i = 0; i < maxAttribs; i++) { |
| oVars[i].setTypeModifier(GrShaderVar::kIn_TypeModifier); |
| oVars[i].appendDecl(glslCaps, &fshaderTxt); |
| fshaderTxt.append(";\n"); |
| } |
| |
| fshaderTxt.appendf( |
| "void main()\n" |
| "{\n" |
| "%s = ", fsOutName); |
| |
| fshaderTxt.appendf("%s", oVars[0].c_str()); |
| for (uint32_t i = 1; i < maxAttribs; i++) { |
| fshaderTxt.appendf(" + %s", oVars[i].c_str()); |
| } |
| |
| fshaderTxt.append(";\n" |
| "}\n"); |
| |
| return CreateProgram(ctx, vshaderTxt.c_str(), fshaderTxt.c_str()); |
| } |
| |
| /////////////////////////////////////////////////////////////////////////////////////////////////// |
| |
| void GLVertexAttributesBench::setup(const GrGLContext* ctx) { |
| const GrGLInterface* gl = ctx->interface(); |
| fTexture = SetupFramebuffer(gl, kScreenWidth, kScreenHeight); |
| |
| fProgram = setupShader(ctx, fAttribs, kMaxAttribs); |
| |
| // setup matrices |
| SkMatrix viewMatrices[kNumTri]; |
| for (uint32_t i = 0 ; i < kNumTri; i++) { |
| SkMatrix m = SkMatrix::I(); |
| m.setScale(0.0001f, 0.0001f); |
| viewMatrices[i] = m; |
| } |
| |
| // presetup vertex attributes, color is set to be a light gray no matter how many vertex |
| // attributes are used |
| float targetColor = 0.9f; |
| float colorContribution = targetColor / fAttribs; |
| fVertices.reset(static_cast<int>(kVerticesPerTri * kNumTri * fStride)); |
| for (uint32_t i = 0; i < kNumTri; i++) { |
| unsigned char* ptr = &fVertices[static_cast<int>(i * kVerticesPerTri * fStride)]; |
| SkPoint* p = reinterpret_cast<SkPoint*>(ptr); |
| p->set(-1.0f, -1.0f); p++; p->set( 0.0f, 1.0f); |
| p = reinterpret_cast<SkPoint*>(ptr + fStride); |
| p->set( 1.0f, -1.0f); p++; p->set( 0.0f, 1.0f); |
| p = reinterpret_cast<SkPoint*>(ptr + fStride * 2); |
| p->set( 1.0f, 1.0f); p++; p->set( 0.0f, 1.0f); |
| |
| SkPoint* position = reinterpret_cast<SkPoint*>(ptr); |
| viewMatrices[i].mapPointsWithStride(position, fStride, kVerticesPerTri); |
| |
| // set colors |
| for (uint32_t j = 0; j < kVerticesPerTri; j++) { |
| GrGLfloat* f = reinterpret_cast<GrGLfloat*>(ptr + 2 * sizeof(SkPoint) + fStride * j); |
| for (uint32_t k = 0; k < fAttribs * 4; k += 4) { |
| f[k] = colorContribution; |
| f[k + 1] = colorContribution; |
| f[k + 2] = colorContribution; |
| f[k + 3] = 1.0f; |
| } |
| } |
| } |
| |
| GR_GL_CALL(gl, GenBuffers(1, &fVBO)); |
| fBuffers.push_back(fVBO); |
| |
| // clear screen |
| GR_GL_CALL(gl, ClearColor(0.03f, 0.03f, 0.03f, 1.0f)); |
| GR_GL_CALL(gl, Clear(GR_GL_COLOR_BUFFER_BIT)); |
| |
| // set us up to draw |
| GR_GL_CALL(gl, UseProgram(fProgram)); |
| } |
| |
| void GLVertexAttributesBench::glDraw(int loops, const GrGLContext* ctx) { |
| const GrGLInterface* gl = ctx->interface(); |
| |
| // upload vertex attributes |
| GR_GL_CALL(gl, BindBuffer(GR_GL_ARRAY_BUFFER, fVBO)); |
| GR_GL_CALL(gl, EnableVertexAttribArray(0)); |
| GR_GL_CALL(gl, VertexAttribPointer(0, 4, GR_GL_FLOAT, GR_GL_FALSE, (GrGLsizei)fStride, |
| (GrGLvoid*)0)); |
| |
| size_t runningStride = 2 * sizeof(SkPoint); |
| for (uint32_t i = 0; i < fAttribs; i++) { |
| int attribId = i + 1; |
| GR_GL_CALL(gl, EnableVertexAttribArray(attribId)); |
| GR_GL_CALL(gl, VertexAttribPointer(attribId, 4, GR_GL_FLOAT, GR_GL_FALSE, |
| (GrGLsizei)fStride, (GrGLvoid*)(runningStride))); |
| runningStride += sizeof(GrGLfloat) * 4; |
| } |
| |
| GR_GL_CALL(gl, BufferData(GR_GL_ARRAY_BUFFER, fVertices.count(), fVertices.begin(), |
| GR_GL_STREAM_DRAW)); |
| |
| uint32_t maxTrianglesPerFlush = kNumTri; |
| uint32_t trianglesToDraw = loops * kDrawMultiplier; |
| |
| while (trianglesToDraw > 0) { |
| uint32_t triangles = SkTMin(trianglesToDraw, maxTrianglesPerFlush); |
| GR_GL_CALL(gl, DrawArrays(GR_GL_TRIANGLES, 0, kVerticesPerTri * triangles)); |
| trianglesToDraw -= triangles; |
| } |
| |
| #if 0 |
| //const char* filename = "/data/local/tmp/out.png"; |
| SkString filename("out"); |
| filename.appendf("_%s.png", this->getName()); |
| DumpImage(gl, kScreenWidth, kScreenHeight, filename.c_str()); |
| #endif |
| } |
| |
| void GLVertexAttributesBench::teardown(const GrGLInterface* gl) { |
| // teardown |
| GR_GL_CALL(gl, BindBuffer(GR_GL_ARRAY_BUFFER, 0)); |
| GR_GL_CALL(gl, BindTexture(GR_GL_TEXTURE_2D, 0)); |
| GR_GL_CALL(gl, BindFramebuffer(GR_GL_FRAMEBUFFER, 0)); |
| GR_GL_CALL(gl, DeleteTextures(1, &fTexture)); |
| GR_GL_CALL(gl, DeleteProgram(fProgram)); |
| GR_GL_CALL(gl, DeleteBuffers(fBuffers.count(), fBuffers.begin())); |
| fBuffers.reset(); |
| } |
| |
| /////////////////////////////////////////////////////////////////////////////// |
| |
| DEF_BENCH( return new GLVertexAttributesBench(0) ) |
| DEF_BENCH( return new GLVertexAttributesBench(1) ) |
| DEF_BENCH( return new GLVertexAttributesBench(2) ) |
| DEF_BENCH( return new GLVertexAttributesBench(3) ) |
| DEF_BENCH( return new GLVertexAttributesBench(4) ) |
| DEF_BENCH( return new GLVertexAttributesBench(5) ) |
| DEF_BENCH( return new GLVertexAttributesBench(6) ) |
| DEF_BENCH( return new GLVertexAttributesBench(7) ) |
| #endif |