1 // Copyright (c) 2012 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 // This file looks like a unit test, but it contains benchmarks and test
6 // utilities intended for manual evaluation of the scalers in
7 // gl_helper*. These tests produce output in the form of files and printouts,
8 // but cannot really "fail". There is no point in making these tests part
9 // of any test automation run.
10
11 #include <GLES2/gl2.h>
12 #include <GLES2/gl2ext.h>
13 #include <GLES2/gl2extchromium.h>
14 #include <stddef.h>
15 #include <stdio.h>
16
17 #include <cmath>
18 #include <string>
19 #include <vector>
20
21 #include "base/at_exit.h"
22 #include "base/command_line.h"
23 #include "base/files/file_util.h"
24 #include "base/run_loop.h"
25 #include "base/stl_util.h"
26 #include "base/strings/stringprintf.h"
27 #include "base/test/task_environment.h"
28 #include "base/threading/thread_task_runner_handle.h"
29 #include "base/time/time.h"
30 #include "components/viz/test/test_gpu_service_holder.h"
31 #include "gpu/command_buffer/client/gl_helper.h"
32 #include "gpu/command_buffer/client/gl_helper_scaling.h"
33 #include "gpu/command_buffer/client/gles2_implementation.h"
34 #include "gpu/command_buffer/client/shared_memory_limits.h"
35 #include "gpu/ipc/gl_in_process_context.h"
36 #include "testing/gtest/include/gtest/gtest.h"
37 #include "third_party/skia/include/core/SkBitmap.h"
38 #include "ui/gfx/codec/png_codec.h"
39
40 namespace gpu {
41
42 namespace {
43
44 GLHelper::ScalerQuality kQualities[] = {
45 GLHelper::SCALER_QUALITY_BEST,
46 GLHelper::SCALER_QUALITY_GOOD,
47 GLHelper::SCALER_QUALITY_FAST,
48 };
49
50 const char* const kQualityNames[] = {
51 "best",
52 "good",
53 "fast",
54 };
55
56 } // namespace
57
58 class GLHelperBenchmark : public testing::Test {
59 protected:
SetUp()60 void SetUp() override {
61 ContextCreationAttribs attributes;
62 attributes.alpha_size = 8;
63 attributes.depth_size = 24;
64 attributes.red_size = 8;
65 attributes.green_size = 8;
66 attributes.blue_size = 8;
67 attributes.stencil_size = 8;
68 attributes.samples = 4;
69 attributes.sample_buffers = 1;
70 attributes.bind_generates_resource = false;
71 attributes.gpu_preference = gl::GpuPreference::kHighPerformance;
72
73 context_ = std::make_unique<GLInProcessContext>();
74 auto result = context_->Initialize(
75 viz::TestGpuServiceHolder::GetInstance()->task_executor(),
76 nullptr, /* surface */
77 true, /* offscreen */
78 kNullSurfaceHandle, /* window */
79 attributes, SharedMemoryLimits(),
80 nullptr, /* gpu_memory_buffer_manager */
81 nullptr, /* image_factory */
82 nullptr, /* gpu_task_helper */
83 nullptr, /* display_compositor_memory_and_task_controller */
84 base::ThreadTaskRunnerHandle::Get());
85 DCHECK_EQ(result, ContextResult::kSuccess);
86 gl_ = context_->GetImplementation();
87 ContextSupport* support = context_->GetImplementation();
88
89 helper_.reset(new GLHelper(gl_, support));
90 helper_scaling_.reset(new GLHelperScaling(gl_, helper_.get()));
91 }
92
TearDown()93 void TearDown() override {
94 helper_scaling_.reset(nullptr);
95 helper_.reset(nullptr);
96 context_.reset(nullptr);
97 }
98
LoadPngFileToSkBitmap(const base::FilePath & filename,SkBitmap * bitmap)99 void LoadPngFileToSkBitmap(const base::FilePath& filename, SkBitmap* bitmap) {
100 std::string compressed;
101 base::ReadFileToString(base::MakeAbsoluteFilePath(filename), &compressed);
102 ASSERT_TRUE(compressed.size());
103 ASSERT_TRUE(gfx::PNGCodec::Decode(
104 reinterpret_cast<const unsigned char*>(compressed.data()),
105 compressed.size(), bitmap));
106 }
107
108 // Save the image to a png file. Used to create the initial test files.
SaveToFile(SkBitmap * bitmap,const base::FilePath & filename)109 void SaveToFile(SkBitmap* bitmap, const base::FilePath& filename) {
110 std::vector<unsigned char> compressed;
111 ASSERT_TRUE(gfx::PNGCodec::Encode(
112 static_cast<unsigned char*>(bitmap->getPixels()),
113 gfx::PNGCodec::FORMAT_BGRA,
114 gfx::Size(bitmap->width(), bitmap->height()),
115 static_cast<int>(bitmap->rowBytes()), true,
116 std::vector<gfx::PNGCodec::Comment>(), &compressed));
117 ASSERT_TRUE(compressed.size());
118 FILE* f = base::OpenFile(filename, "wb");
119 ASSERT_TRUE(f);
120 ASSERT_EQ(fwrite(&*compressed.begin(), 1, compressed.size(), f),
121 compressed.size());
122 base::CloseFile(f);
123 }
124
125 base::test::TaskEnvironment task_environment_;
126 std::unique_ptr<GLInProcessContext> context_;
127 gles2::GLES2Interface* gl_;
128 std::unique_ptr<GLHelper> helper_;
129 std::unique_ptr<GLHelperScaling> helper_scaling_;
130 base::circular_deque<GLHelperScaling::ScaleOp> x_ops_, y_ops_;
131 };
132
TEST_F(GLHelperBenchmark,ScaleBenchmark)133 TEST_F(GLHelperBenchmark, ScaleBenchmark) {
134 int output_sizes[] = {1920, 1080, 1249, 720, // Output size on pixel
135 256, 144};
136 int input_sizes[] = {3200, 2040, 2560, 1476, // Pixel tab size
137 1920, 1080, 1280, 720, 800, 480, 256, 144};
138
139 for (size_t q = 0; q < base::size(kQualities); q++) {
140 for (size_t outsize = 0; outsize < base::size(output_sizes); outsize += 2) {
141 for (size_t insize = 0; insize < base::size(input_sizes); insize += 2) {
142 uint32_t src_texture;
143 gl_->GenTextures(1, &src_texture);
144 uint32_t dst_texture;
145 gl_->GenTextures(1, &dst_texture);
146 uint32_t framebuffer;
147 gl_->GenFramebuffers(1, &framebuffer);
148 const gfx::Size src_size(input_sizes[insize], input_sizes[insize + 1]);
149 const gfx::Size dst_size(output_sizes[outsize],
150 output_sizes[outsize + 1]);
151 SkBitmap input;
152 input.allocN32Pixels(src_size.width(), src_size.height());
153
154 SkBitmap output_pixels;
155 output_pixels.allocN32Pixels(dst_size.width(), dst_size.height());
156
157 gl_->BindFramebuffer(GL_FRAMEBUFFER, framebuffer);
158 gl_->BindTexture(GL_TEXTURE_2D, dst_texture);
159 gl_->TexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, dst_size.width(),
160 dst_size.height(), 0, GL_RGBA, GL_UNSIGNED_BYTE,
161 nullptr);
162 gl_->BindTexture(GL_TEXTURE_2D, src_texture);
163 gl_->TexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, src_size.width(),
164 src_size.height(), 0, GL_RGBA, GL_UNSIGNED_BYTE,
165 input.getPixels());
166
167 std::unique_ptr<GLHelper::ScalerInterface> scaler =
168 helper_->CreateScaler(
169 kQualities[q],
170 gfx::Vector2d(src_size.width(), src_size.height()),
171 gfx::Vector2d(dst_size.width(), dst_size.height()), false,
172 false, false);
173 // Scale once beforehand before we start measuring.
174 const gfx::Rect output_rect(dst_size);
175 scaler->Scale(src_texture, src_size, gfx::Vector2dF(), dst_texture,
176 output_rect);
177 gl_->Finish();
178
179 base::TimeTicks start_time = base::TimeTicks::Now();
180 int iterations = 0;
181 base::TimeTicks end_time;
182 while (true) {
183 for (int i = 0; i < 50; i++) {
184 iterations++;
185 scaler->Scale(src_texture, src_size, gfx::Vector2dF(), dst_texture,
186 output_rect);
187 gl_->Flush();
188 }
189 gl_->Finish();
190 end_time = base::TimeTicks::Now();
191 if (iterations > 2000) {
192 break;
193 }
194 if ((end_time - start_time) > base::TimeDelta::FromSeconds(1)) {
195 break;
196 }
197 }
198 gl_->DeleteTextures(1, &dst_texture);
199 gl_->DeleteTextures(1, &src_texture);
200 gl_->DeleteFramebuffers(1, &framebuffer);
201
202 std::string name;
203 name = base::StringPrintf("scale_%dx%d_to_%dx%d_%s", src_size.width(),
204 src_size.height(), dst_size.width(),
205 dst_size.height(), kQualityNames[q]);
206
207 float ms = (end_time - start_time).InMillisecondsF() / iterations;
208 VLOG(0) << base::StringPrintf("*RESULT gpu_scale_time: %s=%.2f ms\n",
209 name.c_str(), ms);
210 }
211 }
212 }
213 }
214
215 } // namespace gpu
216