/* * 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 "GrShaderCaps.h" #include "GrShaderVar.h" #include "gl/GrGLContext.h" #include "gl/GrGLInterface.h" #include "gl/GrGLUtil.h" #include "../private/GrGLSL.h" #include /* * 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 fBuffers; GrGLuint fProgram; GrGLuint fVBO; SkTArray 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 GrShaderCaps* shaderCaps = ctx->caps()->shaderCaps(); const char* version = shaderCaps->versionDeclString(); // setup vertex shader GrShaderVar aPosition("a_position", kFloat4_GrSLType, GrShaderVar::kIn_TypeModifier); SkTArray aVars; SkTArray oVars; SkString vshaderTxt(version); aPosition.appendDecl(shaderCaps, &vshaderTxt); vshaderTxt.append(";\n"); for (uint32_t i = 0; i < attribs; i++) { SkString aname; aname.appendf("a_color_%d", i); aVars.push_back(GrShaderVar(aname.c_str(), kHalf4_GrSLType, GrShaderVar::kIn_TypeModifier)); aVars.back().appendDecl(shaderCaps, &vshaderTxt); vshaderTxt.append(";\n"); } for (uint32_t i = 0; i < maxAttribs; i++) { SkString oname; oname.appendf("o_color_%d", i); oVars.push_back(GrShaderVar(oname.c_str(), kHalf4_GrSLType, GrShaderVar::kOut_TypeModifier)); oVars.back().appendDecl(shaderCaps, &vshaderTxt); vshaderTxt.append(";\n"); } vshaderTxt.append( "void main()\n" "{\n" "sk_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 = float4(0, 0, 0, 1);\n", oVars[i].c_str()); } vshaderTxt.append("}\n"); // setup fragment shader GrShaderVar oFragColor("o_FragColor", kHalf4_GrSLType, GrShaderVar::kOut_TypeModifier); SkString fshaderTxt(version); const char* fsOutName; if (shaderCaps->mustDeclareFragmentShaderOutput()) { oFragColor.appendDecl(shaderCaps, &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(shaderCaps, &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(kVerticesPerTri * kNumTri * fStride)); for (uint32_t i = 0; i < kNumTri; i++) { unsigned char* ptr = &fVertices[static_cast(i * kVerticesPerTri * fStride)]; SkPoint* p = reinterpret_cast(ptr); p->set(-1.0f, -1.0f); p++; p->set( 0.0f, 1.0f); p = reinterpret_cast(ptr + fStride); p->set( 1.0f, -1.0f); p++; p->set( 0.0f, 1.0f); p = reinterpret_cast(ptr + fStride * 2); p->set( 1.0f, 1.0f); p++; p->set( 0.0f, 1.0f); SkPoint* position = reinterpret_cast(ptr); viewMatrices[i].mapPointsWithStride(position, fStride, kVerticesPerTri); // set colors for (uint32_t j = 0; j < kVerticesPerTri; j++) { GrGLfloat* f = reinterpret_cast(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