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