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" |
11 #include "base/memory/scoped_ptr.h" | 11 #include "base/memory/scoped_ptr.h" |
12 #include "base/strings/stringprintf.h" | 12 #include "base/strings/stringprintf.h" |
13 #include "gpu/perftests/measurements.h" | 13 #include "gpu/perftests/measurements.h" |
14 #include "testing/gmock/include/gmock/gmock.h" | 14 #include "testing/gmock/include/gmock/gmock.h" |
15 #include "testing/gtest/include/gtest/gtest.h" | 15 #include "testing/gtest/include/gtest/gtest.h" |
16 #include "testing/perf/perf_test.h" | 16 #include "testing/perf/perf_test.h" |
17 #include "ui/gfx/geometry/size.h" | 17 #include "ui/gfx/geometry/size.h" |
18 #include "ui/gfx/geometry/vector2d_f.h" | 18 #include "ui/gfx/geometry/vector2d_f.h" |
19 #include "ui/gl/gl_bindings.h" | 19 #include "ui/gl/gl_bindings.h" |
20 #include "ui/gl/gl_context.h" | 20 #include "ui/gl/gl_context.h" |
21 #include "ui/gl/gl_enums.h" | 21 #include "ui/gl/gl_enums.h" |
22 #include "ui/gl/gl_surface.h" | 22 #include "ui/gl/gl_surface.h" |
| 23 #include "ui/gl/gl_version_info.h" |
23 #include "ui/gl/gpu_timing.h" | 24 #include "ui/gl/gpu_timing.h" |
24 #include "ui/gl/scoped_make_current.h" | 25 #include "ui/gl/scoped_make_current.h" |
25 | 26 |
26 namespace gpu { | 27 namespace gpu { |
27 namespace { | 28 namespace { |
28 | 29 |
29 const int kUploadPerfWarmupRuns = 10; | 30 const int kUploadPerfWarmupRuns = 5; |
30 const int kUploadPerfTestRuns = 100; | 31 const int kUploadPerfTestRuns = 30; |
31 | 32 |
32 #define SHADER(Src) #Src | 33 #define SHADER(Src) #Src |
33 | 34 |
34 // clang-format off | 35 // clang-format off |
35 const char kVertexShader[] = | 36 const char kVertexShader[] = |
36 SHADER( | 37 SHADER( |
37 uniform vec2 translation; | 38 uniform vec2 translation; |
38 attribute vec2 a_position; | 39 attribute vec2 a_position; |
39 attribute vec2 a_texCoord; | 40 attribute vec2 a_texCoord; |
40 varying vec2 v_texCoord; | 41 varying vec2 v_texCoord; |
41 void main() { | 42 void main() { |
42 gl_Position = vec4( | 43 gl_Position = vec4( |
43 translation.x + a_position.x, translation.y + a_position.y, 0.0, 1.0); | 44 translation.x + a_position.x, translation.y + a_position.y, 0.0, 1.0); |
44 v_texCoord = a_texCoord; | 45 v_texCoord = a_texCoord; |
45 } | 46 } |
46 ); | 47 ); |
| 48 const char kShaderDefaultFloatPrecision[] = |
| 49 SHADER( |
| 50 precision mediump float; |
| 51 ); |
47 const char kFragmentShader[] = | 52 const char kFragmentShader[] = |
48 SHADER( | 53 SHADER( |
49 precision mediump float; | |
50 uniform sampler2D a_texture; | 54 uniform sampler2D a_texture; |
51 varying vec2 v_texCoord; | 55 varying vec2 v_texCoord; |
52 void main() { | 56 void main() { |
53 gl_FragColor = texture2D(a_texture, v_texCoord); | 57 gl_FragColor = texture2D(a_texture, v_texCoord); |
54 } | 58 } |
55 ); | 59 ); |
56 // clang-format on | 60 // clang-format on |
57 | 61 |
58 void CheckNoGlError() { | 62 void CheckNoGlError(const std::string& msg) { |
59 CHECK_EQ(static_cast<GLenum>(GL_NO_ERROR), glGetError()); | 63 CHECK_EQ(static_cast<GLenum>(GL_NO_ERROR), glGetError()) << " " << msg; |
60 } | 64 } |
61 | 65 |
62 // Utility function to compile a shader from a string. | 66 // Utility function to compile a shader from a string. |
63 GLuint LoadShader(const GLenum type, const char* const src) { | 67 GLuint LoadShader(const GLenum type, const char* const src) { |
64 GLuint shader = 0; | 68 GLuint shader = 0; |
65 shader = glCreateShader(type); | 69 shader = glCreateShader(type); |
66 CHECK_NE(0u, shader); | 70 CHECK_NE(0u, shader); |
67 glShaderSource(shader, 1, &src, NULL); | 71 glShaderSource(shader, 1, &src, NULL); |
68 glCompileShader(shader); | 72 glCompileShader(shader); |
69 | 73 |
(...skipping 10 matching lines...) Expand all Loading... |
80 } | 84 } |
81 CHECK_NE(0, compiled); | 85 CHECK_NE(0, compiled); |
82 return shader; | 86 return shader; |
83 } | 87 } |
84 | 88 |
85 int GLFormatBytePerPixel(GLenum format) { | 89 int GLFormatBytePerPixel(GLenum format) { |
86 DCHECK(format == GL_RGBA || format == GL_LUMINANCE || format == GL_RED_EXT); | 90 DCHECK(format == GL_RGBA || format == GL_LUMINANCE || format == GL_RED_EXT); |
87 return format == GL_RGBA ? 4 : 1; | 91 return format == GL_RGBA ? 4 : 1; |
88 } | 92 } |
89 | 93 |
| 94 GLenum GLFormatToInternalFormat(GLenum format) { |
| 95 return format == GL_RED ? GL_R8 : format; |
| 96 } |
| 97 |
| 98 GLenum GLFormatToStorageFormat(GLenum format) { |
| 99 switch (format) { |
| 100 case GL_RGBA: |
| 101 return GL_RGBA8; |
| 102 case GL_LUMINANCE: |
| 103 return GL_LUMINANCE8; |
| 104 case GL_RED: |
| 105 return GL_R8; |
| 106 default: |
| 107 NOTREACHED(); |
| 108 } |
| 109 return 0; |
| 110 } |
| 111 |
90 void GenerateTextureData(const gfx::Size& size, | 112 void GenerateTextureData(const gfx::Size& size, |
91 int bytes_per_pixel, | 113 int bytes_per_pixel, |
92 const int seed, | 114 const int seed, |
93 std::vector<uint8>* const pixels) { | 115 std::vector<uint8>* const pixels) { |
94 // Row bytes has to be multiple of 4 (GL_PACK_ALIGNMENT defaults to 4). | 116 // Row bytes has to be multiple of 4 (GL_PACK_ALIGNMENT defaults to 4). |
95 int stride = ((size.width() * bytes_per_pixel) + 3) & ~0x3; | 117 int stride = ((size.width() * bytes_per_pixel) + 3) & ~0x3; |
96 pixels->resize(size.height() * stride); | 118 pixels->resize(size.height() * stride); |
97 for (int y = 0; y < size.height(); ++y) { | 119 for (int y = 0; y < size.height(); ++y) { |
98 for (int x = 0; x < size.width(); ++x) { | 120 for (int x = 0; x < size.width(); ++x) { |
99 for (int channel = 0; channel < bytes_per_pixel; ++channel) { | 121 for (int channel = 0; channel < bytes_per_pixel; ++channel) { |
(...skipping 16 matching lines...) Expand all Loading... |
116 int rgba_stride = size.width() * GLFormatBytePerPixel(GL_RGBA); | 138 int rgba_stride = size.width() * GLFormatBytePerPixel(GL_RGBA); |
117 for (int y = 0; y < size.height(); ++y) { | 139 for (int y = 0; y < size.height(); ++y) { |
118 for (int x = 0; x < size.width(); ++x) { | 140 for (int x = 0; x < size.width(); ++x) { |
119 int rgba_index = y * rgba_stride + x * GLFormatBytePerPixel(GL_RGBA); | 141 int rgba_index = y * rgba_stride + x * GLFormatBytePerPixel(GL_RGBA); |
120 int pixels_index = y * pixels_stride + x * bytes_per_pixel; | 142 int pixels_index = y * pixels_stride + x * bytes_per_pixel; |
121 uint8 expected[4] = {0}; | 143 uint8 expected[4] = {0}; |
122 switch (format) { | 144 switch (format) { |
123 case GL_LUMINANCE: // (L_t, L_t, L_t, 1) | 145 case GL_LUMINANCE: // (L_t, L_t, L_t, 1) |
124 expected[1] = pixels[pixels_index]; | 146 expected[1] = pixels[pixels_index]; |
125 expected[2] = pixels[pixels_index]; | 147 expected[2] = pixels[pixels_index]; |
126 case GL_RED_EXT: // (R_t, 0, 0, 1)n | 148 case GL_RED: // (R_t, 0, 0, 1) |
127 expected[0] = pixels[pixels_index]; | 149 expected[0] = pixels[pixels_index]; |
128 expected[3] = 255; | 150 expected[3] = 255; |
129 break; | 151 break; |
130 case GL_RGBA: // (R_t, G_t, B_t, A_t) | 152 case GL_RGBA: // (R_t, G_t, B_t, A_t) |
131 memcpy(expected, &pixels[pixels_index], 4); | 153 memcpy(expected, &pixels[pixels_index], 4); |
132 break; | 154 break; |
133 default: | 155 default: |
134 NOTREACHED(); | 156 NOTREACHED(); |
135 } | 157 } |
136 if (memcmp(&rgba[rgba_index], expected, 4)) { | 158 if (memcmp(&rgba[rgba_index], expected, 4)) { |
(...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
180 if (gpu_timing_client_->IsAvailable()) { | 202 if (gpu_timing_client_->IsAvailable()) { |
181 LOG(INFO) << "Gpu timing initialized with timer type: " | 203 LOG(INFO) << "Gpu timing initialized with timer type: " |
182 << gpu_timing_client_->GetTimerTypeName(); | 204 << gpu_timing_client_->GetTimerTypeName(); |
183 gpu_timing_client_->InvalidateTimerOffset(); | 205 gpu_timing_client_->InvalidateTimerOffset(); |
184 } else { | 206 } else { |
185 LOG(WARNING) << "Can't initialize gpu timing"; | 207 LOG(WARNING) << "Can't initialize gpu timing"; |
186 } | 208 } |
187 // Prepare a simple program and a vertex buffer that will be | 209 // Prepare a simple program and a vertex buffer that will be |
188 // used to draw a quad on the offscreen surface. | 210 // used to draw a quad on the offscreen surface. |
189 vertex_shader_ = LoadShader(GL_VERTEX_SHADER, kVertexShader); | 211 vertex_shader_ = LoadShader(GL_VERTEX_SHADER, kVertexShader); |
190 fragment_shader_ = LoadShader(GL_FRAGMENT_SHADER, kFragmentShader); | 212 |
| 213 bool is_gles = gfx::GetGLImplementation() == gfx::kGLImplementationEGLGLES2; |
| 214 fragment_shader_ = LoadShader( |
| 215 GL_FRAGMENT_SHADER, |
| 216 base::StringPrintf("%s%s", is_gles ? kShaderDefaultFloatPrecision : "", |
| 217 kFragmentShader).c_str()); |
191 program_object_ = glCreateProgram(); | 218 program_object_ = glCreateProgram(); |
192 CHECK_NE(0u, program_object_); | 219 CHECK_NE(0u, program_object_); |
193 | 220 |
194 glAttachShader(program_object_, vertex_shader_); | 221 glAttachShader(program_object_, vertex_shader_); |
195 glAttachShader(program_object_, fragment_shader_); | 222 glAttachShader(program_object_, fragment_shader_); |
196 glBindAttribLocation(program_object_, 0, "a_position"); | 223 glBindAttribLocation(program_object_, 0, "a_position"); |
197 glBindAttribLocation(program_object_, 1, "a_texCoord"); | 224 glBindAttribLocation(program_object_, 1, "a_texCoord"); |
198 glLinkProgram(program_object_); | 225 glLinkProgram(program_object_); |
199 | 226 |
200 GLint linked = -1; | 227 GLint linked = -1; |
(...skipping 11 matching lines...) Expand all Loading... |
212 | 239 |
213 glGenBuffersARB(1, &vertex_buffer_); | 240 glGenBuffersARB(1, &vertex_buffer_); |
214 CHECK_NE(0u, vertex_buffer_); | 241 CHECK_NE(0u, vertex_buffer_); |
215 DCHECK_NE(0u, vertex_buffer_); | 242 DCHECK_NE(0u, vertex_buffer_); |
216 glBindBuffer(GL_ARRAY_BUFFER, vertex_buffer_); | 243 glBindBuffer(GL_ARRAY_BUFFER, vertex_buffer_); |
217 glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, sizeof(GLfloat) * 4, 0); | 244 glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, sizeof(GLfloat) * 4, 0); |
218 glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, sizeof(GLfloat) * 4, | 245 glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, sizeof(GLfloat) * 4, |
219 reinterpret_cast<void*>(sizeof(GLfloat) * 2)); | 246 reinterpret_cast<void*>(sizeof(GLfloat) * 2)); |
220 glEnableVertexAttribArray(0); | 247 glEnableVertexAttribArray(0); |
221 glEnableVertexAttribArray(1); | 248 glEnableVertexAttribArray(1); |
222 CheckNoGlError(); | 249 CheckNoGlError("glEnableVertexAttribArray"); |
| 250 |
| 251 has_texture_storage_ = |
| 252 gl_context_->GetVersionInfo()->is_es3 || |
| 253 gl_context_->HasExtension("GL_EXT_texture_storage") || |
| 254 gl_context_->HasExtension("GL_ARB_texture_storage"); |
223 } | 255 } |
224 | 256 |
225 void GenerateVertexBuffer(const gfx::Size& size) { | 257 void GenerateVertexBuffer(const gfx::Size& size) { |
226 DCHECK_NE(0u, vertex_buffer_); | 258 DCHECK_NE(0u, vertex_buffer_); |
227 glBindBuffer(GL_ARRAY_BUFFER, vertex_buffer_); | 259 glBindBuffer(GL_ARRAY_BUFFER, vertex_buffer_); |
228 // right and top are in clipspace | 260 // right and top are in clipspace |
229 float right = -1.f + 2.f * size.width() / fbo_size_.width(); | 261 float right = -1.f + 2.f * size.width() / fbo_size_.width(); |
230 float top = -1.f + 2.f * size.height() / fbo_size_.height(); | 262 float top = -1.f + 2.f * size.height() / fbo_size_.height(); |
231 // Four vertexes, one per line. Each vertex has two components per | 263 // Four vertexes, one per line. Each vertex has two components per |
232 // position and two per texcoord. | 264 // position and two per texcoord. |
233 // It represents a quad formed by two triangles if interpreted | 265 // It represents a quad formed by two triangles if interpreted |
234 // as a tristrip. | 266 // as a tristrip. |
235 | 267 |
236 // clang-format off | 268 // clang-format off |
237 GLfloat data[16] = { | 269 GLfloat data[16] = { |
238 -1.f, -1.f, 0.f, 0.f, | 270 -1.f, -1.f, 0.f, 0.f, |
239 right, -1.f, 1.f, 0.f, | 271 right, -1.f, 1.f, 0.f, |
240 -1.f, top, 0.f, 1.f, | 272 -1.f, top, 0.f, 1.f, |
241 right, top, 1.f, 1.f}; | 273 right, top, 1.f, 1.f}; |
242 // clang-format on | 274 // clang-format on |
243 glBufferData(GL_ARRAY_BUFFER, sizeof(data), data, GL_STATIC_DRAW); | 275 glBufferData(GL_ARRAY_BUFFER, sizeof(data), data, GL_STATIC_DRAW); |
244 CheckNoGlError(); | 276 CheckNoGlError("glBufferData"); |
245 } | 277 } |
246 | 278 |
247 void TearDown() override { | 279 void TearDown() override { |
248 ui::ScopedMakeCurrent smc(gl_context_.get(), surface_.get()); | 280 ui::ScopedMakeCurrent smc(gl_context_.get(), surface_.get()); |
249 glDeleteProgram(program_object_); | 281 glDeleteProgram(program_object_); |
250 glDeleteShader(vertex_shader_); | 282 glDeleteShader(vertex_shader_); |
251 glDeleteShader(fragment_shader_); | 283 glDeleteShader(fragment_shader_); |
252 glDeleteBuffersARB(1, &vertex_buffer_); | 284 glDeleteBuffersARB(1, &vertex_buffer_); |
253 | 285 |
254 glBindFramebufferEXT(GL_FRAMEBUFFER, 0); | 286 glBindFramebufferEXT(GL_FRAMEBUFFER, 0); |
255 glDeleteFramebuffersEXT(1, &framebuffer_object_); | 287 glDeleteFramebuffersEXT(1, &framebuffer_object_); |
256 glDeleteTextures(1, &color_texture_); | 288 glDeleteTextures(1, &color_texture_); |
257 CheckNoGlError(); | 289 CheckNoGlError("glDeleteTextures"); |
258 | 290 |
259 gpu_timing_client_ = nullptr; | 291 gpu_timing_client_ = nullptr; |
260 gl_context_ = nullptr; | 292 gl_context_ = nullptr; |
261 surface_ = nullptr; | 293 surface_ = nullptr; |
262 } | 294 } |
263 | 295 |
264 protected: | 296 protected: |
265 GLuint CreateGLTexture() { | 297 GLuint CreateGLTexture(const GLenum format, |
| 298 const gfx::Size& size, |
| 299 const bool specify_storage) { |
266 GLuint texture_id = 0; | 300 GLuint texture_id = 0; |
267 glActiveTexture(GL_TEXTURE0); | 301 glActiveTexture(GL_TEXTURE0); |
268 glGenTextures(1, &texture_id); | 302 glGenTextures(1, &texture_id); |
269 glBindTexture(GL_TEXTURE_2D, texture_id); | 303 glBindTexture(GL_TEXTURE_2D, texture_id); |
| 304 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); |
| 305 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); |
| 306 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); |
| 307 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); |
| 308 if (specify_storage) { |
| 309 if (has_texture_storage_) { |
| 310 glTexStorage2DEXT(GL_TEXTURE_2D, 1, GLFormatToStorageFormat(format), |
| 311 size.width(), size.height()); |
| 312 CheckNoGlError("glTexStorage2DEXT"); |
| 313 } else { |
| 314 glTexImage2D(GL_TEXTURE_2D, 0, GLFormatToInternalFormat(format), |
| 315 size.width(), size.height(), 0, format, GL_UNSIGNED_BYTE, |
| 316 nullptr); |
| 317 CheckNoGlError("glTexImage2D"); |
| 318 } |
| 319 } |
270 return texture_id; | 320 return texture_id; |
271 } | 321 } |
272 | 322 |
273 void UploadTexture(GLuint texture_id, | 323 void UploadTexture(GLuint texture_id, |
274 const gfx::Size& size, | 324 const gfx::Size& size, |
275 const std::vector<uint8>& pixels, | 325 const std::vector<uint8>& pixels, |
276 GLenum format) { | 326 GLenum format, |
277 glTexImage2D(GL_TEXTURE_2D, 0, format, size.width(), size.height(), 0, | 327 const bool subimage) { |
278 format, GL_UNSIGNED_BYTE, &pixels[0]); | 328 if (subimage) { |
279 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); | 329 glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, size.width(), size.height(), |
280 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); | 330 format, GL_UNSIGNED_BYTE, &pixels[0]); |
281 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); | 331 CheckNoGlError("glTexSubImage2D"); |
282 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); | 332 } else { |
283 CheckNoGlError(); | 333 glTexImage2D(GL_TEXTURE_2D, 0, GLFormatToInternalFormat(format), |
| 334 size.width(), size.height(), 0, format, GL_UNSIGNED_BYTE, |
| 335 &pixels[0]); |
| 336 CheckNoGlError("glTexImage2D"); |
| 337 } |
284 } | 338 } |
285 | 339 |
286 // Upload and draw on the offscren surface. | 340 // Upload and draw on the offscren surface. |
287 // Return a list of pair. Each pair describe a gl operation and the wall | 341 // Return a list of pair. Each pair describe a gl operation and the wall |
288 // time elapsed in milliseconds. | 342 // time elapsed in milliseconds. |
289 std::vector<Measurement> UploadAndDraw(const gfx::Size& size, | 343 std::vector<Measurement> UploadAndDraw(GLuint texture_id, |
| 344 const gfx::Size& size, |
290 const std::vector<uint8>& pixels, | 345 const std::vector<uint8>& pixels, |
291 const GLenum format) { | 346 const GLenum format, |
292 GLuint texture_id = CreateGLTexture(); | 347 const bool subimage) { |
293 MeasurementTimers tex_timers(gpu_timing_client_.get()); | 348 MeasurementTimers tex_timers(gpu_timing_client_.get()); |
294 UploadTexture(texture_id, size, pixels, format); | 349 UploadTexture(texture_id, size, pixels, format, subimage); |
295 tex_timers.Record(); | 350 tex_timers.Record(); |
296 | 351 |
297 MeasurementTimers draw_timers(gpu_timing_client_.get()); | 352 MeasurementTimers draw_timers(gpu_timing_client_.get()); |
298 | 353 |
299 glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); | 354 glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); |
300 draw_timers.Record(); | 355 draw_timers.Record(); |
301 | 356 |
302 MeasurementTimers finish_timers(gpu_timing_client_.get()); | 357 MeasurementTimers finish_timers(gpu_timing_client_.get()); |
303 glFinish(); | 358 glFinish(); |
304 CheckNoGlError(); | 359 CheckNoGlError("glFinish"); |
305 finish_timers.Record(); | 360 finish_timers.Record(); |
306 | 361 |
307 glDeleteTextures(1, &texture_id); | |
308 | |
309 std::vector<uint8> pixels_rendered(size.GetArea() * 4); | 362 std::vector<uint8> pixels_rendered(size.GetArea() * 4); |
310 glReadPixels(0, 0, size.width(), size.height(), GL_RGBA, GL_UNSIGNED_BYTE, | 363 glReadPixels(0, 0, size.width(), size.height(), GL_RGBA, GL_UNSIGNED_BYTE, |
311 &pixels_rendered[0]); | 364 &pixels_rendered[0]); |
312 CheckNoGlError(); | 365 CheckNoGlError("glReadPixels"); |
313 EXPECT_TRUE( | 366 EXPECT_TRUE( |
314 CompareBufferToRGBABuffer(format, size, pixels, pixels_rendered)) | 367 CompareBufferToRGBABuffer(format, size, pixels, pixels_rendered)) |
315 << "Format is: " << gfx::GLEnums::GetStringEnum(format); | 368 << "Format is: " << gfx::GLEnums::GetStringEnum(format); |
316 | 369 |
317 std::vector<Measurement> measurements; | 370 std::vector<Measurement> measurements; |
318 bool gpu_timer_errors = | 371 bool gpu_timer_errors = |
319 gpu_timing_client_->IsAvailable() && | 372 gpu_timing_client_->IsAvailable() && |
320 gpu_timing_client_->CheckAndResetTimerErrors(); | 373 gpu_timing_client_->CheckAndResetTimerErrors(); |
321 if (!gpu_timer_errors) { | 374 if (!gpu_timer_errors) { |
322 measurements.push_back(tex_timers.GetAsMeasurement("teximage2d")); | 375 measurements.push_back(tex_timers.GetAsMeasurement( |
| 376 subimage ? "texsubimage2d" : "teximage2d")); |
323 measurements.push_back(draw_timers.GetAsMeasurement("drawarrays")); | 377 measurements.push_back(draw_timers.GetAsMeasurement("drawarrays")); |
324 measurements.push_back(finish_timers.GetAsMeasurement("finish")); | 378 measurements.push_back(finish_timers.GetAsMeasurement("finish")); |
325 } | 379 } |
326 return measurements; | 380 return measurements; |
327 } | 381 } |
328 | 382 |
329 void RunUploadAndDrawMultipleTimes(const gfx::Size& size, | 383 void RunUploadAndDrawMultipleTimes(const gfx::Size& size, |
330 const GLenum format) { | 384 const GLenum format, |
| 385 const bool subimage) { |
331 std::vector<uint8> pixels; | 386 std::vector<uint8> pixels; |
332 base::SmallMap<std::map<std::string, Measurement>> | 387 base::SmallMap<std::map<std::string, Measurement>> |
333 aggregates; // indexed by name | 388 aggregates; // indexed by name |
334 int successful_runs = 0; | 389 int successful_runs = 0; |
| 390 GLuint texture_id = CreateGLTexture(format, size, subimage); |
335 for (int i = 0; i < kUploadPerfWarmupRuns + kUploadPerfTestRuns; ++i) { | 391 for (int i = 0; i < kUploadPerfWarmupRuns + kUploadPerfTestRuns; ++i) { |
336 GenerateTextureData(size, GLFormatBytePerPixel(format), i + 1, &pixels); | 392 GenerateTextureData(size, GLFormatBytePerPixel(format), i + 1, &pixels); |
337 auto run = UploadAndDraw(size, pixels, format); | 393 auto run = UploadAndDraw(texture_id, size, pixels, format, subimage); |
338 if (i < kUploadPerfWarmupRuns || !run.size()) { | 394 if (i < kUploadPerfWarmupRuns || !run.size()) { |
339 continue; | 395 continue; |
340 } | 396 } |
341 successful_runs++; | 397 successful_runs++; |
342 for (const Measurement& measurement : run) { | 398 for (const Measurement& measurement : run) { |
343 auto& aggregate = aggregates[measurement.name]; | 399 auto& aggregate = aggregates[measurement.name]; |
344 aggregate.name = measurement.name; | 400 aggregate.name = measurement.name; |
345 aggregate.Increment(measurement); | 401 aggregate.Increment(measurement); |
346 } | 402 } |
347 } | 403 } |
| 404 glDeleteTextures(1, &texture_id); |
| 405 |
348 std::string graph_name = base::StringPrintf( | 406 std::string graph_name = base::StringPrintf( |
349 "%d_%s", size.width(), gfx::GLEnums::GetStringEnum(format).c_str()); | 407 "%d_%s", size.width(), gfx::GLEnums::GetStringEnum(format).c_str()); |
| 408 if (subimage) { |
| 409 graph_name += "_sub"; |
| 410 } |
| 411 |
350 if (successful_runs) { | 412 if (successful_runs) { |
351 for (const auto& entry : aggregates) { | 413 for (const auto& entry : aggregates) { |
352 const auto m = entry.second.Divide(successful_runs); | 414 const auto m = entry.second.Divide(successful_runs); |
353 m.PrintResult(graph_name); | 415 m.PrintResult(graph_name); |
354 } | 416 } |
355 } | 417 } |
356 perf_test::PrintResult("sample_runs", "", graph_name, | 418 perf_test::PrintResult("sample_runs", "", graph_name, |
357 static_cast<size_t>(successful_runs), "laps", true); | 419 static_cast<size_t>(successful_runs), "laps", true); |
358 } | 420 } |
359 | 421 |
360 const gfx::Size fbo_size_; // for the fbo | 422 const gfx::Size fbo_size_; // for the fbo |
361 scoped_refptr<gfx::GLContext> gl_context_; | 423 scoped_refptr<gfx::GLContext> gl_context_; |
362 scoped_refptr<gfx::GLSurface> surface_; | 424 scoped_refptr<gfx::GLSurface> surface_; |
363 scoped_refptr<gfx::GPUTimingClient> gpu_timing_client_; | 425 scoped_refptr<gfx::GPUTimingClient> gpu_timing_client_; |
364 | 426 |
365 GLuint color_texture_ = 0; | 427 GLuint color_texture_ = 0; |
366 GLuint framebuffer_object_ = 0; | 428 GLuint framebuffer_object_ = 0; |
367 GLuint vertex_shader_ = 0; | 429 GLuint vertex_shader_ = 0; |
368 GLuint fragment_shader_ = 0; | 430 GLuint fragment_shader_ = 0; |
369 GLuint program_object_ = 0; | 431 GLuint program_object_ = 0; |
370 GLint sampler_location_ = -1; | 432 GLint sampler_location_ = -1; |
371 GLint translation_location_ = -1; | 433 GLint translation_location_ = -1; |
372 GLuint vertex_buffer_ = 0; | 434 GLuint vertex_buffer_ = 0; |
| 435 |
| 436 bool has_texture_storage_ = false; |
373 }; | 437 }; |
374 | 438 |
375 // Perf test that generates, uploads and draws a texture on a surface repeatedly | 439 // Perf test that generates, uploads and draws a texture on a surface repeatedly |
376 // and prints out aggregated measurements for all the runs. | 440 // and prints out aggregated measurements for all the runs. |
377 TEST_F(TextureUploadPerfTest, glTexImage2d) { | 441 TEST_F(TextureUploadPerfTest, upload) { |
378 int sizes[] = {21, 128, 256, 512, 1024}; | 442 int sizes[] = {21, 128, 256, 512, 1024}; |
379 std::vector<GLenum> formats; | 443 std::vector<GLenum> formats; |
380 formats.push_back(GL_RGBA); | 444 formats.push_back(GL_RGBA); |
381 // Used by default for ResourceProvider::yuv_resource_format_. | 445 |
382 formats.push_back(GL_LUMINANCE); | 446 if (!gl_context_->GetVersionInfo()->is_es3) { |
| 447 // Used by default for ResourceProvider::yuv_resource_format_. |
| 448 formats.push_back(GL_LUMINANCE); |
| 449 } |
383 | 450 |
384 ui::ScopedMakeCurrent smc(gl_context_.get(), surface_.get()); | 451 ui::ScopedMakeCurrent smc(gl_context_.get(), surface_.get()); |
385 bool has_texture_rg = gl_context_->HasExtension("GL_EXT_texture_rg") || | 452 const bool has_texture_rg = gl_context_->GetVersionInfo()->is_es3 || |
386 gl_context_->HasExtension("GL_ARB_texture_rg"); | 453 gl_context_->HasExtension("GL_EXT_texture_rg") || |
| 454 gl_context_->HasExtension("GL_ARB_texture_rg"); |
387 | 455 |
388 if (has_texture_rg) { | 456 if (has_texture_rg) { |
389 // Used as ResourceProvider::yuv_resource_format_ if | 457 // Used as ResourceProvider::yuv_resource_format_ if |
390 // {ARB,EXT}_texture_rg are available. | 458 // {ARB,EXT}_texture_rg are available. |
391 formats.push_back(GL_RED_EXT); | 459 formats.push_back(GL_RED); |
392 } | 460 } |
| 461 |
393 for (int side : sizes) { | 462 for (int side : sizes) { |
394 ASSERT_GE(fbo_size_.width(), side); | 463 ASSERT_GE(fbo_size_.width(), side); |
395 ASSERT_GE(fbo_size_.height(), side); | 464 ASSERT_GE(fbo_size_.height(), side); |
396 gfx::Size size(side, side); | 465 gfx::Size size(side, side); |
397 GenerateVertexBuffer(size); | 466 GenerateVertexBuffer(size); |
398 for (GLenum format : formats) { | 467 for (GLenum format : formats) { |
399 RunUploadAndDrawMultipleTimes(size, format); | 468 RunUploadAndDrawMultipleTimes(size, format, true); // use glTexSubImage2D |
| 469 RunUploadAndDrawMultipleTimes(size, format, false); // use glTexImage2D |
400 } | 470 } |
401 } | 471 } |
402 } | 472 } |
403 | 473 |
404 // Perf test to check if the driver is doing texture renaming. | 474 // Perf test to check if the driver is doing texture renaming. |
405 // This test creates one GL texture_id and four different images. For | 475 // This test creates one GL texture_id and four different images. For |
406 // every image it uploads it using texture_id and it draws multiple | 476 // every image it uploads it using texture_id and it draws multiple |
407 // times. The cpu/wall time and the gpu time for all the uploads and | 477 // times. The cpu/wall time and the gpu time for all the uploads and |
408 // draws, but before glFinish, is computed and is printed out at the end as | 478 // draws, but before glFinish, is computed and is printed out at the end as |
409 // "upload_and_draw". If the gpu time is >> than the cpu/wall time we expect the | 479 // "upload_and_draw". If the gpu time is >> than the cpu/wall time we expect the |
410 // driver to do texture renaming: this means that while the gpu is drawing using | 480 // driver to do texture renaming: this means that while the gpu is drawing using |
411 // texture_id it didn't block cpu side the texture upload using the same | 481 // texture_id it didn't block cpu side the texture upload using the same |
412 // texture_id. | 482 // texture_id. |
413 TEST_F(TextureUploadPerfTest, renaming) { | 483 TEST_F(TextureUploadPerfTest, renaming) { |
414 gfx::Size texture_size(fbo_size_.width() / 2, fbo_size_.height() / 2); | 484 gfx::Size texture_size(fbo_size_.width() / 2, fbo_size_.height() / 2); |
415 | 485 |
416 std::vector<uint8> pixels[4]; | 486 std::vector<uint8> pixels[4]; |
417 for (int i = 0; i < 4; ++i) { | 487 for (int i = 0; i < 4; ++i) { |
418 GenerateTextureData(texture_size, 4, i + 1, &pixels[i]); | 488 GenerateTextureData(texture_size, 4, i + 1, &pixels[i]); |
419 } | 489 } |
420 | 490 |
421 ui::ScopedMakeCurrent smc(gl_context_.get(), surface_.get()); | 491 ui::ScopedMakeCurrent smc(gl_context_.get(), surface_.get()); |
422 GenerateVertexBuffer(texture_size); | 492 GenerateVertexBuffer(texture_size); |
423 | 493 |
424 gfx::Vector2dF positions[] = {gfx::Vector2dF(0.f, 0.f), | 494 gfx::Vector2dF positions[] = {gfx::Vector2dF(0.f, 0.f), |
425 gfx::Vector2dF(1.f, 0.f), | 495 gfx::Vector2dF(1.f, 0.f), |
426 gfx::Vector2dF(0.f, 1.f), | 496 gfx::Vector2dF(0.f, 1.f), |
427 gfx::Vector2dF(1.f, 1.f)}; | 497 gfx::Vector2dF(1.f, 1.f)}; |
428 GLuint texture_id = CreateGLTexture(); | 498 GLuint texture_id = CreateGLTexture(GL_RGBA, texture_size, true); |
429 | 499 |
430 MeasurementTimers upload_and_draw_timers(gpu_timing_client_.get()); | 500 MeasurementTimers upload_and_draw_timers(gpu_timing_client_.get()); |
431 | 501 |
432 for (int i = 0; i < 4; ++i) { | 502 for (int i = 0; i < 4; ++i) { |
433 UploadTexture(texture_id, texture_size, pixels[i % 4], GL_RGBA); | 503 UploadTexture(texture_id, texture_size, pixels[i % 4], GL_RGBA, true); |
434 DCHECK_NE(-1, translation_location_); | 504 DCHECK_NE(-1, translation_location_); |
435 glUniform2f(translation_location_, positions[i % 4].x(), | 505 glUniform2f(translation_location_, positions[i % 4].x(), |
436 positions[i % 4].y()); | 506 positions[i % 4].y()); |
437 // Draw the same quad multiple times to make sure that the time spent on the | 507 // Draw the same quad multiple times to make sure that the time spent on the |
438 // gpu is more than the cpu time. | 508 // gpu is more than the cpu time. |
439 for (int draw = 0; draw < 128; ++draw) { | 509 for (int draw = 0; draw < 128; ++draw) { |
440 glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); | 510 glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); |
441 } | 511 } |
442 } | 512 } |
443 | 513 |
444 upload_and_draw_timers.Record(); | 514 upload_and_draw_timers.Record(); |
445 MeasurementTimers finish_timers(gpu_timing_client_.get()); | 515 MeasurementTimers finish_timers(gpu_timing_client_.get()); |
446 glFinish(); | 516 glFinish(); |
447 CheckNoGlError(); | 517 CheckNoGlError("glFinish"); |
448 finish_timers.Record(); | 518 finish_timers.Record(); |
449 | 519 |
450 glDeleteTextures(1, &texture_id); | 520 glDeleteTextures(1, &texture_id); |
451 | 521 |
452 for (int i = 0; i < 4; ++i) { | 522 for (int i = 0; i < 4; ++i) { |
453 std::vector<uint8> pixels_rendered(texture_size.GetArea() * 4); | 523 std::vector<uint8> pixels_rendered(texture_size.GetArea() * 4); |
454 glReadPixels(texture_size.width() * positions[i].x(), | 524 glReadPixels(texture_size.width() * positions[i].x(), |
455 texture_size.height() * positions[i].y(), texture_size.width(), | 525 texture_size.height() * positions[i].y(), texture_size.width(), |
456 texture_size.height(), GL_RGBA, GL_UNSIGNED_BYTE, | 526 texture_size.height(), GL_RGBA, GL_UNSIGNED_BYTE, |
457 &pixels_rendered[0]); | 527 &pixels_rendered[0]); |
458 CheckNoGlError(); | 528 CheckNoGlError("glReadPixels"); |
459 ASSERT_EQ(pixels[i].size(), pixels_rendered.size()); | 529 ASSERT_EQ(pixels[i].size(), pixels_rendered.size()); |
460 EXPECT_EQ(pixels[i], pixels_rendered); | 530 EXPECT_EQ(pixels[i], pixels_rendered); |
461 } | 531 } |
462 | 532 |
463 bool gpu_timer_errors = gpu_timing_client_->IsAvailable() && | 533 bool gpu_timer_errors = gpu_timing_client_->IsAvailable() && |
464 gpu_timing_client_->CheckAndResetTimerErrors(); | 534 gpu_timing_client_->CheckAndResetTimerErrors(); |
465 if (!gpu_timer_errors) { | 535 if (!gpu_timer_errors) { |
466 upload_and_draw_timers.GetAsMeasurement("upload_and_draw") | 536 upload_and_draw_timers.GetAsMeasurement("upload_and_draw") |
467 .PrintResult("renaming"); | 537 .PrintResult("renaming"); |
468 finish_timers.GetAsMeasurement("finish").PrintResult("renaming"); | 538 finish_timers.GetAsMeasurement("finish").PrintResult("renaming"); |
469 } | 539 } |
470 } | 540 } |
471 | 541 |
472 } // namespace | 542 } // namespace |
473 } // namespace gpu | 543 } // namespace gpu |
OLD | NEW |