blob: 2e9ef6cea4346d217eeae515b89db88f9e0cf40f [file] [log] [blame]
Benjamin Lermancdfc88d2015-02-03 14:35:12 +01001// Copyright 2015 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include <algorithm>
6#include <vector>
7
8#include "base/containers/small_map.h"
Benjamin Lermane8ca9b72015-02-24 16:42:13 +01009#include "base/logging.h"
Benjamin Lermancdfc88d2015-02-03 14:35:12 +010010#include "base/memory/ref_counted.h"
11#include "base/memory/scoped_ptr.h"
Benjamin Lermane8ca9b72015-02-24 16:42:13 +010012#include "gpu/command_buffer/service/gpu_timing.h"
Etienne Membrives386015a2015-02-19 17:27:12 +010013#include "gpu/perftests/measurements.h"
Benjamin Lermancdfc88d2015-02-03 14:35:12 +010014#include "testing/gtest/include/gtest/gtest.h"
Benjamin Lermancdfc88d2015-02-03 14:35:12 +010015#include "ui/gfx/geometry/size.h"
16#include "ui/gl/gl_bindings.h"
17#include "ui/gl/gl_context.h"
18#include "ui/gl/gl_surface.h"
19#include "ui/gl/scoped_make_current.h"
20
21namespace gpu {
22namespace {
23
24const int kUploadPerfWarmupRuns = 10;
25const int kUploadPerfTestRuns = 100;
26
27#define SHADER(Src) #Src
28
29// clang-format off
30const char kVertexShader[] =
31SHADER(
32 attribute vec2 a_position;
33 varying vec2 v_texCoord;
34 void main() {
35 gl_Position = vec4(a_position.x, a_position.y, 0.0, 1.0);
Benjamin Lermane8ca9b72015-02-24 16:42:13 +010036 v_texCoord = vec2((a_position.x + 1.0) * 0.5, (a_position.y + 1.0) * 0.5);
Benjamin Lermancdfc88d2015-02-03 14:35:12 +010037 }
38);
39const char kFragmentShader[] =
40SHADER(
41 uniform sampler2D a_texture;
42 varying vec2 v_texCoord;
43 void main() {
44 gl_FragColor = texture2D(a_texture, v_texCoord.xy);
45 }
46);
47// clang-format on
48
49void CheckNoGlError() {
50 CHECK_EQ(static_cast<GLenum>(GL_NO_ERROR), glGetError());
51}
52
53// Utility function to compile a shader from a string.
54GLuint LoadShader(const GLenum type, const char* const src) {
55 GLuint shader = 0;
56 shader = glCreateShader(type);
57 CHECK_NE(0u, shader);
58 glShaderSource(shader, 1, &src, NULL);
59 glCompileShader(shader);
60
61 GLint compiled = 0;
62 glGetShaderiv(shader, GL_COMPILE_STATUS, &compiled);
63 if (compiled == 0) {
64 GLint len = 0;
65 glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &len);
66 if (len > 1) {
67 scoped_ptr<char> error_log(new char[len]);
68 glGetShaderInfoLog(shader, len, NULL, error_log.get());
69 LOG(ERROR) << "Error compiling shader: " << error_log.get();
70 }
71 }
72 CHECK_NE(0, compiled);
73 return shader;
74}
75
76void GenerateTextureData(const gfx::Size& size,
77 const int seed,
78 std::vector<uint8>* const pixels) {
79 pixels->resize(size.GetArea() * 4);
80 for (int y = 0; y < size.height(); ++y) {
81 for (int x = 0; x < size.width(); ++x) {
82 const size_t offset = (y * size.width() + x) * 4;
83 pixels->at(offset) = (y + seed) % 256;
84 pixels->at(offset + 1) = (x + seed) % 256;
85 pixels->at(offset + 2) = (y + x + seed) % 256;
86 pixels->at(offset + 3) = 255;
87 }
88 }
89}
90
91// PerfTest to check costs of texture upload at different stages
92// on different platforms.
93class TextureUploadPerfTest : public testing::Test {
94 public:
95 TextureUploadPerfTest() : size_(512, 512) {}
96
97 // Overridden from testing::Test
98 void SetUp() override {
99 // Initialize an offscreen surface and a gl context.
100 gfx::GLSurface::InitializeOneOff();
Benjamin Lermane8ca9b72015-02-24 16:42:13 +0100101 surface_ = gfx::GLSurface::CreateOffscreenGLSurface(gfx::Size(4, 4));
Benjamin Lermancdfc88d2015-02-03 14:35:12 +0100102 gl_context_ = gfx::GLContext::CreateGLContext(NULL, // share_group
103 surface_.get(),
104 gfx::PreferIntegratedGpu);
Etienne Membrives386015a2015-02-19 17:27:12 +0100105 ui::ScopedMakeCurrent smc(gl_context_.get(), surface_.get());
Benjamin Lermane8ca9b72015-02-24 16:42:13 +0100106 glGenTextures(1, &color_texture_);
107 glBindTexture(GL_TEXTURE_2D, color_texture_);
108 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
109 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
110 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
111 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
112 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, size_.width(), size_.height(), 0,
113 GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
114
115 glGenFramebuffersEXT(1, &framebuffer_object_);
116 glBindFramebufferEXT(GL_FRAMEBUFFER, framebuffer_object_);
117
118 glFramebufferTexture2DEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
119 GL_TEXTURE_2D, color_texture_, 0);
120 DCHECK_EQ(static_cast<GLenum>(GL_FRAMEBUFFER_COMPLETE),
121 glCheckFramebufferStatusEXT(GL_FRAMEBUFFER));
122
123 glViewport(0, 0, size_.width(), size_.height());
124
Etienne Membrives386015a2015-02-19 17:27:12 +0100125 if (gpu_timing_.Initialize(gl_context_.get())) {
126 LOG(INFO) << "Gpu timing initialized with timer type: "
127 << gpu_timing_.GetTimerTypeName();
128 gpu_timing_.CheckAndResetTimerErrors();
129 gpu_timing_.InvalidateTimerOffset();
130 } else {
131 LOG(WARNING) << "Can't initialize gpu timing";
132 }
Benjamin Lermancdfc88d2015-02-03 14:35:12 +0100133 // Prepare a simple program and a vertex buffer that will be
134 // used to draw a quad on the offscreen surface.
Benjamin Lermancdfc88d2015-02-03 14:35:12 +0100135 vertex_shader_ = LoadShader(GL_VERTEX_SHADER, kVertexShader);
136 fragment_shader_ = LoadShader(GL_FRAGMENT_SHADER, kFragmentShader);
137 program_object_ = glCreateProgram();
138 CHECK_NE(0u, program_object_);
139
140 glAttachShader(program_object_, vertex_shader_);
141 glAttachShader(program_object_, fragment_shader_);
142 glBindAttribLocation(program_object_, 0, "a_position");
143 glLinkProgram(program_object_);
144
145 GLint linked = -1;
146 glGetProgramiv(program_object_, GL_LINK_STATUS, &linked);
147 CHECK_NE(0, linked);
148
149 sampler_location_ = glGetUniformLocation(program_object_, "a_texture");
150 CHECK_NE(-1, sampler_location_);
151
152 glGenBuffersARB(1, &vertex_buffer_);
153 CHECK_NE(0u, vertex_buffer_);
154 glBindBuffer(GL_ARRAY_BUFFER, vertex_buffer_);
155 static GLfloat positions[] = {
156 -1.0f, -1.0f, 1.0f, -1.0f, -1.0f, 1.0f, 1.0f, 1.0f,
157 };
158 glBufferData(GL_ARRAY_BUFFER, sizeof(positions), positions, GL_STATIC_DRAW);
159 CheckNoGlError();
160 }
161
162 void TearDown() override {
163 ui::ScopedMakeCurrent smc(gl_context_.get(), surface_.get());
Benjamin Lermane8ca9b72015-02-24 16:42:13 +0100164 glDeleteProgram(program_object_);
165 glDeleteShader(vertex_shader_);
166 glDeleteShader(fragment_shader_);
167 glDeleteShader(vertex_buffer_);
168
169 glBindFramebufferEXT(GL_FRAMEBUFFER, 0);
170 glDeleteFramebuffersEXT(1, &framebuffer_object_);
171 glDeleteTextures(1, &color_texture_);
Benjamin Lermancdfc88d2015-02-03 14:35:12 +0100172
173 gl_context_ = nullptr;
174 surface_ = nullptr;
175 }
176
177 protected:
Benjamin Lermancdfc88d2015-02-03 14:35:12 +0100178 // Upload and draw on the offscren surface.
179 // Return a list of pair. Each pair describe a gl operation and the wall
180 // time elapsed in milliseconds.
181 std::vector<Measurement> UploadAndDraw(const std::vector<uint8>& pixels,
182 const GLenum format,
183 const GLenum type) {
Benjamin Lermancdfc88d2015-02-03 14:35:12 +0100184 ui::ScopedMakeCurrent smc(gl_context_.get(), surface_.get());
Benjamin Lermane8ca9b72015-02-24 16:42:13 +0100185 DCHECK_NE(0u, framebuffer_object_);
186 glBindFramebufferEXT(GL_FRAMEBUFFER, framebuffer_object_);
Benjamin Lermancdfc88d2015-02-03 14:35:12 +0100187
Etienne Membrives386015a2015-02-19 17:27:12 +0100188 MeasurementTimers total_timers(&gpu_timing_);
Benjamin Lermancdfc88d2015-02-03 14:35:12 +0100189 GLuint texture_id = 0;
190
Etienne Membrives386015a2015-02-19 17:27:12 +0100191 MeasurementTimers tex_timers(&gpu_timing_);
Benjamin Lermancdfc88d2015-02-03 14:35:12 +0100192 glActiveTexture(GL_TEXTURE0);
193 glGenTextures(1, &texture_id);
194 glBindTexture(GL_TEXTURE_2D, texture_id);
195
196 glTexImage2D(GL_TEXTURE_2D, 0, format, size_.width(), size_.height(), 0,
197 format, type, &pixels[0]);
Benjamin Lermane8ca9b72015-02-24 16:42:13 +0100198 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
199 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
Benjamin Lermancdfc88d2015-02-03 14:35:12 +0100200 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
201 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
202 CheckNoGlError();
Etienne Membrives386015a2015-02-19 17:27:12 +0100203 tex_timers.Record();
Benjamin Lermancdfc88d2015-02-03 14:35:12 +0100204
Etienne Membrives386015a2015-02-19 17:27:12 +0100205 MeasurementTimers draw_timers(&gpu_timing_);
Benjamin Lermancdfc88d2015-02-03 14:35:12 +0100206 glUseProgram(program_object_);
207 glUniform1i(sampler_location_, 0);
208
209 glBindBuffer(GL_ARRAY_BUFFER, vertex_buffer_);
210 glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, 0);
211 glEnableVertexAttribArray(0);
212
213 glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
Etienne Membrives386015a2015-02-19 17:27:12 +0100214 draw_timers.Record();
Benjamin Lermancdfc88d2015-02-03 14:35:12 +0100215
Etienne Membrives386015a2015-02-19 17:27:12 +0100216 MeasurementTimers finish_timers(&gpu_timing_);
Benjamin Lermancdfc88d2015-02-03 14:35:12 +0100217 glFinish();
218 CheckNoGlError();
Etienne Membrives386015a2015-02-19 17:27:12 +0100219 finish_timers.Record();
220 total_timers.Record();
Benjamin Lermancdfc88d2015-02-03 14:35:12 +0100221
222 glDeleteTextures(1, &texture_id);
223
224 std::vector<uint8> pixels_rendered(size_.GetArea() * 4);
225 glReadPixels(0, 0, size_.width(), size_.height(), GL_RGBA, type,
226 &pixels_rendered[0]);
227 CheckNoGlError();
228
229 // TODO(dcastagna): don't assume the format of the texture and do
230 // the appropriate format conversion.
231 EXPECT_EQ(static_cast<GLenum>(GL_RGBA), format);
232 EXPECT_EQ(pixels, pixels_rendered);
Etienne Membrives386015a2015-02-19 17:27:12 +0100233
234 std::vector<Measurement> measurements;
235 measurements.push_back(total_timers.GetAsMeasurement("total"));
236 measurements.push_back(tex_timers.GetAsMeasurement("teximage2d"));
237 measurements.push_back(draw_timers.GetAsMeasurement("drawarrays"));
238 measurements.push_back(finish_timers.GetAsMeasurement("finish"));
Benjamin Lermancdfc88d2015-02-03 14:35:12 +0100239 return measurements;
240 }
241
Benjamin Lermane8ca9b72015-02-24 16:42:13 +0100242 const gfx::Size size_; // for the fbo and the texture
Benjamin Lermancdfc88d2015-02-03 14:35:12 +0100243 scoped_refptr<gfx::GLContext> gl_context_;
244 scoped_refptr<gfx::GLSurface> surface_;
Etienne Membrives386015a2015-02-19 17:27:12 +0100245 GPUTiming gpu_timing_;
Benjamin Lermancdfc88d2015-02-03 14:35:12 +0100246
Benjamin Lermane8ca9b72015-02-24 16:42:13 +0100247 GLuint color_texture_ = 0;
248 GLuint framebuffer_object_ = 0;
Benjamin Lermancdfc88d2015-02-03 14:35:12 +0100249 GLuint vertex_shader_ = 0;
250 GLuint fragment_shader_ = 0;
251 GLuint program_object_ = 0;
252 GLint sampler_location_ = -1;
253 GLuint vertex_buffer_ = 0;
254};
255
256// Perf test that generates, uploads and draws a texture on a surface repeatedly
257// and prints out aggregated measurements for all the runs.
258TEST_F(TextureUploadPerfTest, glTexImage2d) {
259 std::vector<uint8> pixels;
260 base::SmallMap<std::map<std::string, Measurement>>
261 aggregates; // indexed by name
262 for (int i = 0; i < kUploadPerfWarmupRuns + kUploadPerfTestRuns; ++i) {
263 GenerateTextureData(size_, i + 1, &pixels);
264 auto run = UploadAndDraw(pixels, GL_RGBA, GL_UNSIGNED_BYTE);
265 if (i >= kUploadPerfWarmupRuns) {
266 for (const Measurement& m : run) {
267 auto& agg = aggregates[m.name];
268 agg.name = m.name;
Etienne Membrives386015a2015-02-19 17:27:12 +0100269 agg.Increment(m);
Benjamin Lermancdfc88d2015-02-03 14:35:12 +0100270 }
271 }
272 }
273
274 for (const auto& entry : aggregates) {
Etienne Membrives386015a2015-02-19 17:27:12 +0100275 const auto m = entry.second.Divide(kUploadPerfTestRuns);
276 m.PrintResult();
Benjamin Lermancdfc88d2015-02-03 14:35:12 +0100277 }
278}
279
280} // namespace
281} // namespace gpu