|  | /* | 
|  | * 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 "SkMatrix.h" | 
|  | #include "SkPoint.h" | 
|  | #include "SkString.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 GL benchmark for comparing the performance of using vec4 or float for coverage in GLSL. | 
|  | * The generated shader code from this bench will draw several overlapping circles, one in each | 
|  | * stage, to simulate coverage calculations.  The number of circles (i.e. the number of stages) can | 
|  | * be set as a parameter. | 
|  | */ | 
|  |  | 
|  | class GLVec4ScalarBench : public GLBench { | 
|  | public: | 
|  | /* | 
|  | * Use float or vec4 as GLSL data type for the output coverage | 
|  | */ | 
|  | enum CoverageSetup { | 
|  | kUseScalar_CoverageSetup, | 
|  | kUseVec4_CoverageSetup, | 
|  | }; | 
|  |  | 
|  | /* | 
|  | * numStages determines the number of shader stages before the XP, | 
|  | * which consequently determines how many circles are drawn | 
|  | */ | 
|  | GLVec4ScalarBench(CoverageSetup coverageSetup, uint32_t numStages) | 
|  | : fCoverageSetup(coverageSetup) | 
|  | , fNumStages(numStages) | 
|  | , fVboId(0) | 
|  | , fProgram(0) { | 
|  | fName = NumStagesSetupToStr(coverageSetup, numStages); | 
|  | } | 
|  |  | 
|  | 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; | 
|  |  | 
|  | private: | 
|  | void setupSingleVbo(const GrGLInterface*, const SkMatrix*); | 
|  | GrGLuint setupShader(const GrGLContext*); | 
|  |  | 
|  |  | 
|  | static SkString NumStagesSetupToStr(CoverageSetup coverageSetup, uint32_t numStages) { | 
|  | SkString name("GLVec4ScalarBench"); | 
|  | switch (coverageSetup) { | 
|  | default: | 
|  | case kUseScalar_CoverageSetup: | 
|  | name.appendf("_scalar_%u_stage", numStages); | 
|  | break; | 
|  | case kUseVec4_CoverageSetup: | 
|  | name.appendf("_vec4_%u_stage", numStages); | 
|  | break; | 
|  | } | 
|  | return name; | 
|  | } | 
|  |  | 
|  | static const GrGLuint kScreenWidth = 800; | 
|  | static const GrGLuint kScreenHeight = 600; | 
|  | static const uint32_t kNumTriPerDraw = 512; | 
|  | static const uint32_t kVerticesPerTri = 3; | 
|  |  | 
|  | SkString fName; | 
|  | CoverageSetup fCoverageSetup; | 
|  | uint32_t fNumStages; | 
|  | GrGLuint fVboId; | 
|  | GrGLuint fProgram; | 
|  | GrGLuint fFboTextureId; | 
|  | }; | 
|  |  | 
|  | /////////////////////////////////////////////////////////////////////////////////////////////////// | 
|  |  | 
|  | GrGLuint GLVec4ScalarBench::setupShader(const GrGLContext* ctx) { | 
|  | const GrGLSLCaps* glslCaps = ctx->caps()->glslCaps(); | 
|  | const char* version = glslCaps->versionDeclString(); | 
|  |  | 
|  | // this shader draws fNumStages overlapping circles of increasing opacity (coverage) and | 
|  | // decreasing size, with the center of each subsequent circle closer to the bottom-right | 
|  | // corner of the screen than the previous circle. | 
|  |  | 
|  | // set up vertex shader; this is a trivial vertex shader that passes through position and color | 
|  | GrGLSLShaderVar aPosition("a_position", kVec2f_GrSLType, GrShaderVar::kAttribute_TypeModifier); | 
|  | GrGLSLShaderVar oPosition("o_position", kVec2f_GrSLType, GrShaderVar::kVaryingOut_TypeModifier); | 
|  | GrGLSLShaderVar aColor("a_color", kVec3f_GrSLType, GrShaderVar::kAttribute_TypeModifier); | 
|  | GrGLSLShaderVar oColor("o_color", kVec3f_GrSLType, GrShaderVar::kVaryingOut_TypeModifier); | 
|  |  | 
|  | SkString vshaderTxt(version); | 
|  | aPosition.appendDecl(glslCaps, &vshaderTxt); | 
|  | vshaderTxt.append(";\n"); | 
|  | aColor.appendDecl(glslCaps, &vshaderTxt); | 
|  | vshaderTxt.append(";\n"); | 
|  | oPosition.appendDecl(glslCaps, &vshaderTxt); | 
|  | vshaderTxt.append(";\n"); | 
|  | oColor.appendDecl(glslCaps, &vshaderTxt); | 
|  | vshaderTxt.append(";\n"); | 
|  |  | 
|  | vshaderTxt.append( | 
|  | "void main()\n" | 
|  | "{\n" | 
|  | "    gl_Position = vec4(a_position, 0.0, 1.0);\n" | 
|  | "    o_position = a_position;\n" | 
|  | "    o_color = a_color;\n" | 
|  | "}\n"); | 
|  |  | 
|  | const GrGLInterface* gl = ctx->interface(); | 
|  |  | 
|  | // set up fragment shader; this fragment shader will have fNumStages coverage stages plus an | 
|  | // XP stage at the end.  Each coverage stage computes the pixel's distance from some hard- | 
|  | // coded center and compare that to some hard-coded circle radius to compute a coverage. | 
|  | // Then, this coverage is mixed with the coverage from the previous stage and passed to the | 
|  | // next stage. | 
|  | GrGLSLShaderVar oFragColor("o_FragColor", kVec4f_GrSLType, GrShaderVar::kOut_TypeModifier); | 
|  | SkString fshaderTxt(version); | 
|  | GrGLSLAppendDefaultFloatPrecisionDeclaration(kDefault_GrSLPrecision, *glslCaps, &fshaderTxt); | 
|  | oPosition.setTypeModifier(GrShaderVar::kVaryingIn_TypeModifier); | 
|  | oPosition.appendDecl(glslCaps, &fshaderTxt); | 
|  | fshaderTxt.append(";\n"); | 
|  | oColor.setTypeModifier(GrShaderVar::kVaryingIn_TypeModifier); | 
|  | oColor.appendDecl(glslCaps, &fshaderTxt); | 
|  | fshaderTxt.append(";\n"); | 
|  |  | 
|  | const char* fsOutName; | 
|  | if (glslCaps->mustDeclareFragmentShaderOutput()) { | 
|  | oFragColor.appendDecl(glslCaps, &fshaderTxt); | 
|  | fshaderTxt.append(";\n"); | 
|  | fsOutName = oFragColor.c_str(); | 
|  | } else { | 
|  | fsOutName = "gl_FragColor"; | 
|  | } | 
|  |  | 
|  |  | 
|  | fshaderTxt.appendf( | 
|  | "void main()\n" | 
|  | "{\n" | 
|  | "    vec4 outputColor;\n" | 
|  | "    %s outputCoverage;\n" | 
|  | "    outputColor = vec4(%s, 1.0);\n" | 
|  | "    outputCoverage = %s;\n", | 
|  | fCoverageSetup == kUseVec4_CoverageSetup ? "vec4" : "float", | 
|  | oColor.getName().c_str(), | 
|  | fCoverageSetup == kUseVec4_CoverageSetup ? "vec4(1.0)" : "1.0" | 
|  | ); | 
|  |  | 
|  | float radius = 1.0f; | 
|  | for (uint32_t i = 0; i < fNumStages; i++) { | 
|  | float centerX = 1.0f - radius; | 
|  | float centerY = 1.0f - radius; | 
|  | fshaderTxt.appendf( | 
|  | "    {\n" | 
|  | "        float d = length(%s - vec2(%f, %f));\n" | 
|  | "        float edgeAlpha = clamp(100.0 * (%f - d), 0.0, 1.0);\n" | 
|  | "        outputCoverage = 0.5 * outputCoverage + 0.5 * %s;\n" | 
|  | "    }\n", | 
|  | oPosition.getName().c_str(), centerX, centerY, | 
|  | radius, | 
|  | fCoverageSetup == kUseVec4_CoverageSetup ? "vec4(edgeAlpha)" : "edgeAlpha" | 
|  | ); | 
|  | radius *= 0.8f; | 
|  | } | 
|  | fshaderTxt.appendf( | 
|  | "    {\n" | 
|  | "        %s = outputColor * outputCoverage;\n" | 
|  | "    }\n" | 
|  | "}\n", | 
|  | fsOutName); | 
|  |  | 
|  | return CreateProgram(gl, vshaderTxt.c_str(), fshaderTxt.c_str()); | 
|  | } | 
|  |  | 
|  | template<typename Func> | 
|  | static void setup_matrices(int numQuads, Func f) { | 
|  | // We draw a really small triangle so we are not fill rate limited | 
|  | for (int i = 0 ; i < numQuads; i++) { | 
|  | SkMatrix m = SkMatrix::I(); | 
|  | m.setScale(0.01f, 0.01f); | 
|  | f(m); | 
|  | } | 
|  | } | 
|  |  | 
|  | /////////////////////////////////////////////////////////////////////////////////////////////////// | 
|  |  | 
|  | struct Vertex { | 
|  | SkPoint fPositions; | 
|  | GrGLfloat fColors[3]; | 
|  | }; | 
|  |  | 
|  | void GLVec4ScalarBench::setupSingleVbo(const GrGLInterface* gl, const SkMatrix* viewMatrices) { | 
|  | // triangles drawn will alternate between the top-right half of the screen and the bottom-left | 
|  | // half of the screen | 
|  | Vertex vertices[kVerticesPerTri * kNumTriPerDraw]; | 
|  | for (uint32_t i = 0; i < kNumTriPerDraw; i++) { | 
|  | Vertex* v = &vertices[i * kVerticesPerTri]; | 
|  | if (i % 2 == 0) { | 
|  | v[0].fPositions.set(-1.0f, -1.0f); | 
|  | v[1].fPositions.set( 1.0f, -1.0f); | 
|  | v[2].fPositions.set( 1.0f,  1.0f); | 
|  | } else { | 
|  | v[0].fPositions.set(-1.0f, -1.0f); | 
|  | v[1].fPositions.set( 1.0f, 1.0f); | 
|  | v[2].fPositions.set( -1.0f, 1.0f); | 
|  | } | 
|  | SkPoint* position = reinterpret_cast<SkPoint*>(v); | 
|  | viewMatrices[i].mapPointsWithStride(position, sizeof(Vertex), kVerticesPerTri); | 
|  |  | 
|  | GrGLfloat color[3] = {1.0f, 0.0f, 1.0f}; | 
|  | for (uint32_t j = 0; j < kVerticesPerTri; j++) { | 
|  | v->fColors[0] = color[0]; | 
|  | v->fColors[1] = color[1]; | 
|  | v->fColors[2] = color[2]; | 
|  | v++; | 
|  | } | 
|  | } | 
|  |  | 
|  | GR_GL_CALL(gl, GenBuffers(1, &fVboId)); | 
|  | GR_GL_CALL(gl, BindBuffer(GR_GL_ARRAY_BUFFER, fVboId)); | 
|  | GR_GL_CALL(gl, EnableVertexAttribArray(0)); | 
|  | GR_GL_CALL(gl, EnableVertexAttribArray(1)); | 
|  | GR_GL_CALL(gl, VertexAttribPointer(0, 2, GR_GL_FLOAT, GR_GL_FALSE, sizeof(Vertex), | 
|  | (GrGLvoid*)0)); | 
|  | GR_GL_CALL(gl, VertexAttribPointer(1, 3, GR_GL_FLOAT, GR_GL_FALSE, sizeof(Vertex), | 
|  | (GrGLvoid*)(sizeof(SkPoint)))); | 
|  | GR_GL_CALL(gl, BufferData(GR_GL_ARRAY_BUFFER, sizeof(vertices), vertices, GR_GL_STATIC_DRAW)); | 
|  | } | 
|  |  | 
|  | void GLVec4ScalarBench::setup(const GrGLContext* ctx) { | 
|  | const GrGLInterface* gl = ctx->interface(); | 
|  | if (!gl) { | 
|  | SkFAIL("GL interface is nullptr in setup()!\n"); | 
|  | } | 
|  | fFboTextureId = SetupFramebuffer(gl, kScreenWidth, kScreenHeight); | 
|  |  | 
|  | fProgram = this->setupShader(ctx); | 
|  |  | 
|  | int index = 0; | 
|  | SkMatrix viewMatrices[kNumTriPerDraw]; | 
|  | setup_matrices(kNumTriPerDraw, [&index, &viewMatrices](const SkMatrix& m) { | 
|  | viewMatrices[index++] = m; | 
|  | }); | 
|  | this->setupSingleVbo(gl, viewMatrices); | 
|  |  | 
|  | GR_GL_CALL(gl, UseProgram(fProgram)); | 
|  | } | 
|  |  | 
|  | void GLVec4ScalarBench::glDraw(int loops, const GrGLContext* ctx) { | 
|  | const GrGLInterface* gl = ctx->interface(); | 
|  |  | 
|  | for (int i = 0; i < loops; i++) { | 
|  | GR_GL_CALL(gl, DrawArrays(GR_GL_TRIANGLES, 0, kVerticesPerTri * kNumTriPerDraw)); | 
|  | } | 
|  |  | 
|  | // using -w when running nanobench will not produce correct images; | 
|  | // changing this to #if 1 will write the correct images to the Skia folder. | 
|  | #if 0 | 
|  | SkString filename("out"); | 
|  | filename.appendf("_%s.png", this->getName()); | 
|  | DumpImage(gl, kScreenWidth, kScreenHeight, filename.c_str()); | 
|  | #endif | 
|  | } | 
|  |  | 
|  | void GLVec4ScalarBench::teardown(const GrGLInterface* gl) { | 
|  | 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, &fFboTextureId)); | 
|  | GR_GL_CALL(gl, DeleteProgram(fProgram)); | 
|  | GR_GL_CALL(gl, DeleteBuffers(1, &fVboId)); | 
|  | } | 
|  |  | 
|  | /////////////////////////////////////////////////////////////////////////////// | 
|  |  | 
|  | DEF_BENCH( return new GLVec4ScalarBench(GLVec4ScalarBench::kUseScalar_CoverageSetup, 1) ) | 
|  | DEF_BENCH( return new GLVec4ScalarBench(GLVec4ScalarBench::kUseVec4_CoverageSetup, 1) ) | 
|  | DEF_BENCH( return new GLVec4ScalarBench(GLVec4ScalarBench::kUseScalar_CoverageSetup, 2) ) | 
|  | DEF_BENCH( return new GLVec4ScalarBench(GLVec4ScalarBench::kUseVec4_CoverageSetup, 2) ) | 
|  | DEF_BENCH( return new GLVec4ScalarBench(GLVec4ScalarBench::kUseScalar_CoverageSetup, 4) ) | 
|  | DEF_BENCH( return new GLVec4ScalarBench(GLVec4ScalarBench::kUseVec4_CoverageSetup, 4) ) | 
|  | DEF_BENCH( return new GLVec4ScalarBench(GLVec4ScalarBench::kUseScalar_CoverageSetup, 6) ) | 
|  | DEF_BENCH( return new GLVec4ScalarBench(GLVec4ScalarBench::kUseVec4_CoverageSetup, 6) ) | 
|  | DEF_BENCH( return new GLVec4ScalarBench(GLVec4ScalarBench::kUseScalar_CoverageSetup, 8) ) | 
|  | DEF_BENCH( return new GLVec4ScalarBench(GLVec4ScalarBench::kUseVec4_CoverageSetup, 8) ) | 
|  |  | 
|  | #endif |