You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
302 lines
11 KiB
302 lines
11 KiB
/* |
|
* 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 "GrShaderCaps.h" |
|
#include "GrShaderVar.h" |
|
#include "gl/GrGLContext.h" |
|
#include "gl/GrGLInterface.h" |
|
#include "gl/GrGLUtil.h" |
|
#include "../private/GrGLSL.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 GrShaderCaps* shaderCaps = ctx->caps()->shaderCaps(); |
|
const char* version = shaderCaps->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 |
|
GrShaderVar aPosition("a_position", kVec2f_GrSLType, GrShaderVar::kIn_TypeModifier); |
|
GrShaderVar oPosition("o_position", kVec2f_GrSLType, GrShaderVar::kOut_TypeModifier); |
|
GrShaderVar aColor("a_color", kVec3f_GrSLType, GrShaderVar::kIn_TypeModifier); |
|
GrShaderVar oColor("o_color", kVec3f_GrSLType, GrShaderVar::kOut_TypeModifier); |
|
|
|
SkString vshaderTxt(version); |
|
aPosition.appendDecl(shaderCaps, &vshaderTxt); |
|
vshaderTxt.append(";\n"); |
|
aColor.appendDecl(shaderCaps, &vshaderTxt); |
|
vshaderTxt.append(";\n"); |
|
oPosition.appendDecl(shaderCaps, &vshaderTxt); |
|
vshaderTxt.append(";\n"); |
|
oColor.appendDecl(shaderCaps, &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"); |
|
|
|
// 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. |
|
GrShaderVar oFragColor("o_FragColor", kVec4f_GrSLType, GrShaderVar::kOut_TypeModifier); |
|
SkString fshaderTxt(version); |
|
GrGLSLAppendDefaultFloatPrecisionDeclaration(kMedium_GrSLPrecision, *shaderCaps, &fshaderTxt); |
|
oPosition.setTypeModifier(GrShaderVar::kIn_TypeModifier); |
|
oPosition.appendDecl(shaderCaps, &fshaderTxt); |
|
fshaderTxt.append(";\n"); |
|
oColor.setTypeModifier(GrShaderVar::kIn_TypeModifier); |
|
oColor.appendDecl(shaderCaps, &fshaderTxt); |
|
fshaderTxt.append(";\n"); |
|
|
|
const char* fsOutName; |
|
if (shaderCaps->mustDeclareFragmentShaderOutput()) { |
|
oFragColor.appendDecl(shaderCaps, &fshaderTxt); |
|
fshaderTxt.append(";\n"); |
|
fsOutName = oFragColor.c_str(); |
|
} else { |
|
fsOutName = "sk_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(ctx, 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
|
|
|