OLD | NEW |
1 // Copyright 2015 The Chromium Authors. All rights reserved. | 1 // Copyright 2015 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include <algorithm> | 5 #include <algorithm> |
6 #include <vector> | 6 #include <vector> |
7 | 7 |
8 #include "base/containers/small_map.h" | 8 #include "base/containers/small_map.h" |
9 #include "base/logging.h" | 9 #include "base/logging.h" |
10 #include "base/memory/ref_counted.h" | 10 #include "base/memory/ref_counted.h" |
(...skipping 16 matching lines...) Expand all Loading... |
27 namespace { | 27 namespace { |
28 | 28 |
29 const int kUploadPerfWarmupRuns = 10; | 29 const int kUploadPerfWarmupRuns = 10; |
30 const int kUploadPerfTestRuns = 100; | 30 const int kUploadPerfTestRuns = 100; |
31 | 31 |
32 #define SHADER(Src) #Src | 32 #define SHADER(Src) #Src |
33 | 33 |
34 // clang-format off | 34 // clang-format off |
35 const char kVertexShader[] = | 35 const char kVertexShader[] = |
36 SHADER( | 36 SHADER( |
37 uniform vec2 translation = vec2(0.0, 0.0); | 37 uniform vec2 translation; |
38 attribute vec2 a_position; | 38 attribute vec2 a_position; |
39 attribute vec2 a_texCoord; | 39 attribute vec2 a_texCoord; |
40 varying vec2 v_texCoord; | 40 varying vec2 v_texCoord; |
41 void main() { | 41 void main() { |
42 gl_Position = vec4( | 42 gl_Position = vec4( |
43 translation.x + a_position.x, translation.y + a_position.y, 0.0, 1.0); | 43 translation.x + a_position.x, translation.y + a_position.y, 0.0, 1.0); |
44 v_texCoord = a_texCoord; | 44 v_texCoord = a_texCoord; |
45 } | 45 } |
46 ); | 46 ); |
47 const char kFragmentShader[] = | 47 const char kFragmentShader[] = |
48 SHADER( | 48 SHADER( |
| 49 precision mediump float; |
49 uniform sampler2D a_texture; | 50 uniform sampler2D a_texture; |
50 varying vec2 v_texCoord; | 51 varying vec2 v_texCoord; |
51 void main() { | 52 void main() { |
52 gl_FragColor = texture2D(a_texture, v_texCoord); | 53 gl_FragColor = texture2D(a_texture, v_texCoord); |
53 } | 54 } |
54 ); | 55 ); |
55 // clang-format on | 56 // clang-format on |
56 | 57 |
57 void CheckNoGlError() { | 58 void CheckNoGlError() { |
58 CHECK_EQ(static_cast<GLenum>(GL_NO_ERROR), glGetError()); | 59 CHECK_EQ(static_cast<GLenum>(GL_NO_ERROR), glGetError()); |
(...skipping 15 matching lines...) Expand all Loading... |
74 if (len > 1) { | 75 if (len > 1) { |
75 scoped_ptr<char> error_log(new char[len]); | 76 scoped_ptr<char> error_log(new char[len]); |
76 glGetShaderInfoLog(shader, len, NULL, error_log.get()); | 77 glGetShaderInfoLog(shader, len, NULL, error_log.get()); |
77 LOG(ERROR) << "Error compiling shader: " << error_log.get(); | 78 LOG(ERROR) << "Error compiling shader: " << error_log.get(); |
78 } | 79 } |
79 } | 80 } |
80 CHECK_NE(0, compiled); | 81 CHECK_NE(0, compiled); |
81 return shader; | 82 return shader; |
82 } | 83 } |
83 | 84 |
| 85 int GLFormatBytePerPixel(GLenum format) { |
| 86 DCHECK(format == GL_RGBA || format == GL_LUMINANCE || format == GL_RED_EXT); |
| 87 return format == GL_RGBA ? 4 : 1; |
| 88 } |
| 89 |
84 void GenerateTextureData(const gfx::Size& size, | 90 void GenerateTextureData(const gfx::Size& size, |
85 int bytes_per_pixel, | 91 int bytes_per_pixel, |
86 const int seed, | 92 const int seed, |
87 std::vector<uint8>* const pixels) { | 93 std::vector<uint8>* const pixels) { |
88 int bytes = size.GetArea() * bytes_per_pixel; | 94 // Row bytes has to be multiple of 4 (GL_PACK_ALIGNMENT defaults to 4). |
89 pixels->resize(bytes); | 95 int stride = ((size.width() * bytes_per_pixel) + 3) & ~0x3; |
90 for (int i = 0; i < bytes; ++i) { | 96 pixels->resize(size.height() * stride); |
91 int channel = i % bytes_per_pixel; | 97 for (int y = 0; y < size.height(); ++y) { |
92 if (channel == 3) { // Alpha channel. | 98 for (int x = 0; x < size.width(); ++x) { |
93 pixels->at(i) = 255; | 99 for (int channel = 0; channel < bytes_per_pixel; ++channel) { |
94 } else { | 100 int index = y * stride + x * bytes_per_pixel; |
95 pixels->at(i) = (i + (seed << 2)) % (32 << channel); | 101 pixels->at(index) = (index + (seed << 2)) % (0x20 << channel); |
| 102 } |
96 } | 103 } |
97 } | 104 } |
98 } | 105 } |
99 | 106 |
100 // Compare a buffer containing pixels in a specified format to GL_RGBA buffer | 107 // Compare a buffer containing pixels in a specified format to GL_RGBA buffer |
101 // where the former buffer have been uploaded as a texture and drawn on the | 108 // where the former buffer have been uploaded as a texture and drawn on the |
102 // RGBA buffer. | 109 // RGBA buffer. |
103 bool CompareBufferToRGBABuffer(GLenum format, | 110 bool CompareBufferToRGBABuffer(GLenum format, |
| 111 const gfx::Size& size, |
104 const std::vector<uint8>& pixels, | 112 const std::vector<uint8>& pixels, |
105 const std::vector<uint8>& pixels_rgba) { | 113 const std::vector<uint8>& rgba) { |
106 for (size_t i = 0; i < pixels.size(); i += 4) { | 114 int bytes_per_pixel = GLFormatBytePerPixel(format); |
107 switch (format) { | 115 int pixels_stride = ((size.width() * bytes_per_pixel) + 3) & ~0x3; |
108 case GL_RED_EXT: // (R_t, 0, 0, 1) | 116 int rgba_stride = size.width() * GLFormatBytePerPixel(GL_RGBA); |
109 if (pixels_rgba[i] != pixels[i / 4] || pixels_rgba[i + 1] != 0 || | 117 for (int y = 0; y < size.height(); ++y) { |
110 pixels_rgba[i + 2] != 0 || pixels_rgba[i + 3] != 255) { | 118 for (int x = 0; x < size.width(); ++x) { |
111 return false; | 119 int rgba_index = y * rgba_stride + x * GLFormatBytePerPixel(GL_RGBA); |
112 } | 120 int pixels_index = y * pixels_stride + x * bytes_per_pixel; |
113 break; | 121 uint8 expected[4] = {0}; |
114 case GL_LUMINANCE: // (L_t, L_t, L_t, 1) | 122 switch (format) { |
115 if (pixels_rgba[i] != pixels[i / 4] || | 123 case GL_LUMINANCE: // (L_t, L_t, L_t, 1) |
116 pixels_rgba[i + 1] != pixels[i / 4] || | 124 expected[1] = pixels[pixels_index]; |
117 pixels_rgba[i + 2] != pixels[i / 4] || pixels_rgba[i + 3] != 255) { | 125 expected[2] = pixels[pixels_index]; |
118 return false; | 126 case GL_RED_EXT: // (R_t, 0, 0, 1)n |
119 } | 127 expected[0] = pixels[pixels_index]; |
120 break; | 128 expected[3] = 255; |
121 case GL_RGBA: // (R_t, G_t, B_t, A_t) | 129 break; |
122 if (pixels_rgba[i] != pixels[i] || | 130 case GL_RGBA: // (R_t, G_t, B_t, A_t) |
123 pixels_rgba[i + 1] != pixels[i + 1] || | 131 memcpy(expected, &pixels[pixels_index], 4); |
124 pixels_rgba[i + 2] != pixels[i + 2] || | 132 break; |
125 pixels_rgba[i + 3] != pixels[i + 3]) { | 133 default: |
126 return false; | 134 NOTREACHED(); |
127 } | 135 } |
128 break; | 136 if (memcmp(&rgba[rgba_index], expected, 4)) { |
129 default: | 137 return false; |
130 NOTREACHED(); | 138 } |
131 } | 139 } |
132 } | 140 } |
133 return true; | 141 return true; |
134 } | 142 } |
135 | 143 |
136 // PerfTest to check costs of texture upload at different stages | 144 // PerfTest to check costs of texture upload at different stages |
137 // on different platforms. | 145 // on different platforms. |
138 class TextureUploadPerfTest : public testing::Test { | 146 class TextureUploadPerfTest : public testing::Test { |
139 public: | 147 public: |
140 TextureUploadPerfTest() : fbo_size_(1024, 1024) {} | 148 TextureUploadPerfTest() : fbo_size_(1024, 1024) {} |
(...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
182 fragment_shader_ = LoadShader(GL_FRAGMENT_SHADER, kFragmentShader); | 190 fragment_shader_ = LoadShader(GL_FRAGMENT_SHADER, kFragmentShader); |
183 program_object_ = glCreateProgram(); | 191 program_object_ = glCreateProgram(); |
184 CHECK_NE(0u, program_object_); | 192 CHECK_NE(0u, program_object_); |
185 | 193 |
186 glAttachShader(program_object_, vertex_shader_); | 194 glAttachShader(program_object_, vertex_shader_); |
187 glAttachShader(program_object_, fragment_shader_); | 195 glAttachShader(program_object_, fragment_shader_); |
188 glBindAttribLocation(program_object_, 0, "a_position"); | 196 glBindAttribLocation(program_object_, 0, "a_position"); |
189 glBindAttribLocation(program_object_, 1, "a_texCoord"); | 197 glBindAttribLocation(program_object_, 1, "a_texCoord"); |
190 glLinkProgram(program_object_); | 198 glLinkProgram(program_object_); |
191 | 199 |
192 translation_location_ = | |
193 glGetUniformLocation(program_object_, "translation"); | |
194 DCHECK_NE(-1, translation_location_); | |
195 | |
196 GLint linked = -1; | 200 GLint linked = -1; |
197 glGetProgramiv(program_object_, GL_LINK_STATUS, &linked); | 201 glGetProgramiv(program_object_, GL_LINK_STATUS, &linked); |
198 CHECK_NE(0, linked); | 202 CHECK_NE(0, linked); |
199 glUseProgram(program_object_); | 203 glUseProgram(program_object_); |
200 glUniform1i(sampler_location_, 0); | 204 glUniform1i(sampler_location_, 0); |
| 205 translation_location_ = |
| 206 glGetUniformLocation(program_object_, "translation"); |
| 207 DCHECK_NE(-1, translation_location_); |
| 208 glUniform2f(translation_location_, 0.0f, 0.0f); |
201 | 209 |
202 sampler_location_ = glGetUniformLocation(program_object_, "a_texture"); | 210 sampler_location_ = glGetUniformLocation(program_object_, "a_texture"); |
203 CHECK_NE(-1, sampler_location_); | 211 CHECK_NE(-1, sampler_location_); |
204 | 212 |
205 glGenBuffersARB(1, &vertex_buffer_); | 213 glGenBuffersARB(1, &vertex_buffer_); |
206 CHECK_NE(0u, vertex_buffer_); | 214 CHECK_NE(0u, vertex_buffer_); |
207 DCHECK_NE(0u, vertex_buffer_); | 215 DCHECK_NE(0u, vertex_buffer_); |
208 glBindBuffer(GL_ARRAY_BUFFER, vertex_buffer_); | 216 glBindBuffer(GL_ARRAY_BUFFER, vertex_buffer_); |
209 glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, sizeof(GLfloat) * 4, 0); | 217 glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, sizeof(GLfloat) * 4, 0); |
210 glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, sizeof(GLfloat) * 4, | 218 glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, sizeof(GLfloat) * 4, |
(...skipping 84 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
295 glFinish(); | 303 glFinish(); |
296 CheckNoGlError(); | 304 CheckNoGlError(); |
297 finish_timers.Record(); | 305 finish_timers.Record(); |
298 | 306 |
299 glDeleteTextures(1, &texture_id); | 307 glDeleteTextures(1, &texture_id); |
300 | 308 |
301 std::vector<uint8> pixels_rendered(size.GetArea() * 4); | 309 std::vector<uint8> pixels_rendered(size.GetArea() * 4); |
302 glReadPixels(0, 0, size.width(), size.height(), GL_RGBA, GL_UNSIGNED_BYTE, | 310 glReadPixels(0, 0, size.width(), size.height(), GL_RGBA, GL_UNSIGNED_BYTE, |
303 &pixels_rendered[0]); | 311 &pixels_rendered[0]); |
304 CheckNoGlError(); | 312 CheckNoGlError(); |
305 EXPECT_TRUE(CompareBufferToRGBABuffer(format, pixels, pixels_rendered)) | 313 EXPECT_TRUE( |
| 314 CompareBufferToRGBABuffer(format, size, pixels, pixels_rendered)) |
306 << "Format is: " << gfx::GLEnums::GetStringEnum(format); | 315 << "Format is: " << gfx::GLEnums::GetStringEnum(format); |
307 | 316 |
308 std::vector<Measurement> measurements; | 317 std::vector<Measurement> measurements; |
309 bool gpu_timer_errors = | 318 bool gpu_timer_errors = |
310 gpu_timing_client_->IsAvailable() && | 319 gpu_timing_client_->IsAvailable() && |
311 gpu_timing_client_->CheckAndResetTimerErrors(); | 320 gpu_timing_client_->CheckAndResetTimerErrors(); |
312 if (!gpu_timer_errors) { | 321 if (!gpu_timer_errors) { |
313 measurements.push_back(tex_timers.GetAsMeasurement("teximage2d")); | 322 measurements.push_back(tex_timers.GetAsMeasurement("teximage2d")); |
314 measurements.push_back(draw_timers.GetAsMeasurement("drawarrays")); | 323 measurements.push_back(draw_timers.GetAsMeasurement("drawarrays")); |
315 measurements.push_back(finish_timers.GetAsMeasurement("finish")); | 324 measurements.push_back(finish_timers.GetAsMeasurement("finish")); |
316 } | 325 } |
317 return measurements; | 326 return measurements; |
318 } | 327 } |
319 | 328 |
320 void RunUploadAndDrawMultipleTimes(const gfx::Size& size, | 329 void RunUploadAndDrawMultipleTimes(const gfx::Size& size, |
321 const GLenum format) { | 330 const GLenum format) { |
322 std::vector<uint8> pixels; | 331 std::vector<uint8> pixels; |
323 base::SmallMap<std::map<std::string, Measurement>> | 332 base::SmallMap<std::map<std::string, Measurement>> |
324 aggregates; // indexed by name | 333 aggregates; // indexed by name |
325 int successful_runs = 0; | 334 int successful_runs = 0; |
326 ASSERT_THAT(format, testing::AnyOf(GL_RGBA, GL_LUMINANCE, GL_RED_EXT)); | |
327 for (int i = 0; i < kUploadPerfWarmupRuns + kUploadPerfTestRuns; ++i) { | 335 for (int i = 0; i < kUploadPerfWarmupRuns + kUploadPerfTestRuns; ++i) { |
328 GenerateTextureData(size, format == GL_RGBA ? 4 : 1, i + 1, &pixels); | 336 GenerateTextureData(size, GLFormatBytePerPixel(format), i + 1, &pixels); |
329 auto run = UploadAndDraw(size, pixels, format); | 337 auto run = UploadAndDraw(size, pixels, format); |
330 if (i < kUploadPerfWarmupRuns || !run.size()) { | 338 if (i < kUploadPerfWarmupRuns || !run.size()) { |
331 continue; | 339 continue; |
332 } | 340 } |
333 successful_runs++; | 341 successful_runs++; |
334 for (const Measurement& measurement : run) { | 342 for (const Measurement& measurement : run) { |
335 auto& aggregate = aggregates[measurement.name]; | 343 auto& aggregate = aggregates[measurement.name]; |
336 aggregate.name = measurement.name; | 344 aggregate.name = measurement.name; |
337 aggregate.Increment(measurement); | 345 aggregate.Increment(measurement); |
338 } | 346 } |
339 } | 347 } |
340 std::string suffix = base::StringPrintf( | 348 std::string graph_name = base::StringPrintf( |
341 "_%d_%s", size.width(), gfx::GLEnums::GetStringEnum(format).c_str()); | 349 "%d_%s", size.width(), gfx::GLEnums::GetStringEnum(format).c_str()); |
342 if (successful_runs) { | 350 if (successful_runs) { |
343 for (const auto& entry : aggregates) { | 351 for (const auto& entry : aggregates) { |
344 const auto m = entry.second.Divide(successful_runs); | 352 const auto m = entry.second.Divide(successful_runs); |
345 m.PrintResult(suffix); | 353 m.PrintResult(graph_name); |
346 } | 354 } |
347 } | 355 } |
348 perf_test::PrintResult("sample_runs", suffix, "", | 356 perf_test::PrintResult("sample_runs", "", graph_name, |
349 static_cast<size_t>(successful_runs), "laps", true); | 357 static_cast<size_t>(successful_runs), "laps", true); |
350 } | 358 } |
351 | 359 |
352 const gfx::Size fbo_size_; // for the fbo | 360 const gfx::Size fbo_size_; // for the fbo |
353 scoped_refptr<gfx::GLContext> gl_context_; | 361 scoped_refptr<gfx::GLContext> gl_context_; |
354 scoped_refptr<gfx::GLSurface> surface_; | 362 scoped_refptr<gfx::GLSurface> surface_; |
355 scoped_refptr<gfx::GPUTimingClient> gpu_timing_client_; | 363 scoped_refptr<gfx::GPUTimingClient> gpu_timing_client_; |
356 | 364 |
357 GLuint color_texture_ = 0; | 365 GLuint color_texture_ = 0; |
358 GLuint framebuffer_object_ = 0; | 366 GLuint framebuffer_object_ = 0; |
359 GLuint vertex_shader_ = 0; | 367 GLuint vertex_shader_ = 0; |
360 GLuint fragment_shader_ = 0; | 368 GLuint fragment_shader_ = 0; |
361 GLuint program_object_ = 0; | 369 GLuint program_object_ = 0; |
362 GLint sampler_location_ = -1; | 370 GLint sampler_location_ = -1; |
363 GLint translation_location_ = -1; | 371 GLint translation_location_ = -1; |
364 GLuint vertex_buffer_ = 0; | 372 GLuint vertex_buffer_ = 0; |
365 }; | 373 }; |
366 | 374 |
367 // Perf test that generates, uploads and draws a texture on a surface repeatedly | 375 // Perf test that generates, uploads and draws a texture on a surface repeatedly |
368 // and prints out aggregated measurements for all the runs. | 376 // and prints out aggregated measurements for all the runs. |
369 TEST_F(TextureUploadPerfTest, glTexImage2d) { | 377 TEST_F(TextureUploadPerfTest, glTexImage2d) { |
370 int sizes[] = {128, 256, 512, 1024}; | 378 int sizes[] = {21, 128, 256, 512, 1024}; |
371 std::vector<GLenum> formats; | 379 std::vector<GLenum> formats; |
372 formats.push_back(GL_RGBA); | 380 formats.push_back(GL_RGBA); |
373 // Used by default for ResourceProvider::yuv_resource_format_. | 381 // Used by default for ResourceProvider::yuv_resource_format_. |
374 formats.push_back(GL_LUMINANCE); | 382 formats.push_back(GL_LUMINANCE); |
375 | 383 |
376 ui::ScopedMakeCurrent smc(gl_context_.get(), surface_.get()); | 384 ui::ScopedMakeCurrent smc(gl_context_.get(), surface_.get()); |
377 bool has_texture_rg = gl_context_->HasExtension("GL_EXT_texture_rg") || | 385 bool has_texture_rg = gl_context_->HasExtension("GL_EXT_texture_rg") || |
378 gl_context_->HasExtension("GL_ARB_texture_rg"); | 386 gl_context_->HasExtension("GL_ARB_texture_rg"); |
379 | 387 |
380 if (has_texture_rg) { | 388 if (has_texture_rg) { |
381 // Used as ResourceProvider::yuv_resource_format_ if | 389 // Used as ResourceProvider::yuv_resource_format_ if |
382 // {ARB,EXT}_texture_rg is available. | 390 // {ARB,EXT}_texture_rg are available. |
383 formats.push_back(GL_RED_EXT); | 391 formats.push_back(GL_RED_EXT); |
384 } | 392 } |
385 for (int side : sizes) { | 393 for (int side : sizes) { |
386 ASSERT_GE(fbo_size_.width(), side); | 394 ASSERT_GE(fbo_size_.width(), side); |
387 ASSERT_GE(fbo_size_.height(), side); | 395 ASSERT_GE(fbo_size_.height(), side); |
388 gfx::Size size(side, side); | 396 gfx::Size size(side, side); |
389 GenerateVertexBuffer(size); | 397 GenerateVertexBuffer(size); |
390 for (GLenum format : formats) { | 398 for (GLenum format : formats) { |
391 RunUploadAndDrawMultipleTimes(size, format); | 399 RunUploadAndDrawMultipleTimes(size, format); |
392 } | 400 } |
(...skipping 55 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
448 texture_size.height(), GL_RGBA, GL_UNSIGNED_BYTE, | 456 texture_size.height(), GL_RGBA, GL_UNSIGNED_BYTE, |
449 &pixels_rendered[0]); | 457 &pixels_rendered[0]); |
450 CheckNoGlError(); | 458 CheckNoGlError(); |
451 ASSERT_EQ(pixels[i].size(), pixels_rendered.size()); | 459 ASSERT_EQ(pixels[i].size(), pixels_rendered.size()); |
452 EXPECT_EQ(pixels[i], pixels_rendered); | 460 EXPECT_EQ(pixels[i], pixels_rendered); |
453 } | 461 } |
454 | 462 |
455 bool gpu_timer_errors = gpu_timing_client_->IsAvailable() && | 463 bool gpu_timer_errors = gpu_timing_client_->IsAvailable() && |
456 gpu_timing_client_->CheckAndResetTimerErrors(); | 464 gpu_timing_client_->CheckAndResetTimerErrors(); |
457 if (!gpu_timer_errors) { | 465 if (!gpu_timer_errors) { |
458 upload_and_draw_timers.GetAsMeasurement("upload_and_draw").PrintResult(""); | 466 upload_and_draw_timers.GetAsMeasurement("upload_and_draw") |
459 finish_timers.GetAsMeasurement("finish").PrintResult(""); | 467 .PrintResult("renaming"); |
| 468 finish_timers.GetAsMeasurement("finish").PrintResult("renaming"); |
460 } | 469 } |
461 } | 470 } |
462 | 471 |
463 } // namespace | 472 } // namespace |
464 } // namespace gpu | 473 } // namespace gpu |
OLD | NEW |