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