1 // Copyright (c) 2010 The Chromium OS 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 <gflags/gflags.h>
6 #include <png.h>
7 #include <stdio.h>
8 #include <unistd.h>
9
10 #include <memory>
11
12 #include "filepath.h"
13 #include "glinterface.h"
14 #include "md5.h"
15 #include "png_helper.h"
16 #include "testbase.h"
17 #include "utils.h"
18
19 extern bool g_hasty;
20 extern bool g_notemp;
21
22 DEFINE_bool(save, false, "save images after each test case");
23 DEFINE_string(outdir, "", "directory to save images");
24
25 namespace glbench {
26
TimeTest(TestBase * test,uint64_t iterations)27 uint64_t TimeTest(TestBase* test, uint64_t iterations) {
28 g_main_gl_interface->SwapBuffers();
29 glFinish();
30 uint64_t time1 = GetUTime();
31 if (!test->TestFunc(iterations))
32 return ~0;
33 glFinish();
34 uint64_t time2 = GetUTime();
35 return time2 - time1;
36 }
37
38 // Target minimum iteration duration of 1s. This means the final/longest
39 // iteration is between 1s and 2s and the machine is active for 2s to 4s.
40 // Notice as of March 2014 the BVT suite has a hard limit per job of 20 minutes.
41 #define MIN_ITERATION_DURATION_US 1000000
42
43 #define MAX_TESTNAME 46
44
45 // Benchmark some draw commands, by running it many times. We want to measure
46 // the marginal cost, so we try more and more iterations until we reach the
47 // minimum specified iteration time.
Bench(TestBase * test)48 double Bench(TestBase* test) {
49 // Try to wait a bit to let machine cool down for next test. We allow for a
50 // bit of hysteresis as it might take too long to do a perfect job, which is
51 // probably not required. But these parameters could be tuned.
52 double initial_temperature = GetInitialMachineTemperature();
53 double cooldown_temperature = std::max(45.0, initial_temperature + 6.0);
54 double temperature = 0;
55 double wait = 0;
56
57 // By default we try to cool to initial + 6'C (don't bother below 45'C), but
58 // don't wait longer than 30s. In hasty mode we really don't want to spend
59 // too much time to get the numbers right, so we don't wait at all.
60 if (!::g_notemp) {
61 wait = WaitForCoolMachine(cooldown_temperature, 30.0, &temperature);
62 printf(
63 "Bench: Cooled down to %.1f'C (initial=%.1f'C) after waiting %.1fs.\n",
64 temperature, initial_temperature, wait);
65 if (temperature > cooldown_temperature + 5.0)
66 printf("Warning: Machine did not cool down enough for next test!");
67 }
68
69 // Do two iterations because initial timings can vary wildly.
70 TimeTest(test, 2);
71
72 // We average the times for the last two runs to reduce noise. We could
73 // sum up all runs but the initial measurements have high CPU overhead,
74 // while the last two runs are both on the order of MIN_ITERATION_DURATION_US.
75 uint64_t iterations = 1;
76 uint64_t iterations_prev = 0;
77 uint64_t time = 0;
78 uint64_t time_prev = 0;
79 do {
80 time = TimeTest(test, iterations);
81 dbg_printf("iterations: %llu: time: %llu time/iter: %llu\n", iterations,
82 time, time / iterations);
83
84 // If we are running in hasty mode we will stop after a fraction of the
85 // testing time and return much more noisy performance numbers. The MD5s
86 // of the images should stay the same though.
87 if (time > MIN_ITERATION_DURATION_US / (::g_hasty ? 20.0 : 1.0))
88 return (static_cast<double>(time + time_prev) /
89 (iterations + iterations_prev));
90
91 time_prev = time;
92 iterations_prev = iterations;
93 iterations *= 2;
94 } while (iterations < (1ULL << 40));
95
96 return 0.0;
97 }
98
SaveImage(const char * name,const int width,const int height)99 void SaveImage(const char* name, const int width, const int height) {
100 const int size = width * height * 4;
101 std::unique_ptr<char[]> pixels(new char[size]);
102 glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, pixels.get());
103 // I really think we want to use outdir as a straight argument
104 FilePath dirname = FilePath(FLAGS_outdir);
105 CreateDirectory(dirname);
106 FilePath filename = dirname.Append(name);
107 write_png_file(filename.value().c_str(), pixels.get(), width, height);
108 }
109
ComputeMD5(unsigned char digest[16],const int width,const int height)110 void ComputeMD5(unsigned char digest[16], const int width, const int height) {
111 MD5Context ctx;
112 MD5Init(&ctx);
113 const int size = width * height * 4;
114 std::unique_ptr<char[]> pixels(new char[size]);
115 glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, pixels.get());
116 MD5Update(&ctx, (unsigned char*)pixels.get(), size);
117 MD5Final(digest, &ctx);
118 }
119
RunTest(TestBase * test,const char * testname,const double coefficient,const int width,const int height,bool inverse)120 void RunTest(TestBase* test,
121 const char* testname,
122 const double coefficient,
123 const int width,
124 const int height,
125 bool inverse) {
126 double value;
127 char name_png[512] = "";
128 GLenum error = glGetError();
129
130 if (error != GL_NO_ERROR) {
131 value = -1.0;
132 printf("# Error: %s aborted, glGetError returned 0x%02x.\n", testname,
133 error);
134 sprintf(name_png, "glGetError=0x%02x", error);
135 } else {
136 value = Bench(test);
137
138 // Bench returns 0.0 if it ran max iterations in less than a min test time.
139 if (value == 0.0) {
140 strcpy(name_png, "no_score");
141 } else {
142 value = coefficient * (inverse ? 1.0 / value : value);
143
144 if (!test->IsDrawTest()) {
145 strcpy(name_png, "none");
146 } else {
147 // save as png with MD5 as hex string attached
148 char pixmd5[33];
149 unsigned char d[16];
150 ComputeMD5(d, width, height);
151 // translate to hexadecimal ASCII of MD5
152 sprintf(
153 pixmd5,
154 "%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x",
155 d[0], d[1], d[2], d[3], d[4], d[5], d[6], d[7], d[8], d[9], d[10],
156 d[11], d[12], d[13], d[14], d[15]);
157 sprintf(name_png, "%s.pixmd5-%s.png", testname, pixmd5);
158
159 if (FLAGS_save)
160 SaveImage(name_png, width, height);
161 }
162 }
163 }
164
165 // TODO(ihf) adjust string length based on longest test name
166 int name_length = strlen(testname);
167 if (name_length > MAX_TESTNAME)
168 printf("# Warning: adjust string formatting to length = %d\n", name_length);
169 // Results are marked using a leading '@RESULT: ' to allow parsing.
170 printf("@RESULT: %-*s = %10.2f %-15s [%s]\n", MAX_TESTNAME, testname, value,
171 test->Unit(), name_png);
172 }
173
TestFunc(uint64_t iterations)174 bool DrawArraysTestFunc::TestFunc(uint64_t iterations) {
175 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
176 glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
177 glFlush();
178 for (uint64_t i = 0; i < iterations - 1; ++i) {
179 glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
180 }
181 return true;
182 }
183
FillRateTestNormal(const char * name)184 void DrawArraysTestFunc::FillRateTestNormal(const char* name) {
185 FillRateTestNormalSubWindow(name, g_width, g_height);
186 }
187
FillRateTestNormalSubWindow(const char * name,const int width,const int height)188 void DrawArraysTestFunc::FillRateTestNormalSubWindow(const char* name,
189 const int width,
190 const int height) {
191 RunTest(this, name, width * height, width, height, true);
192 }
193
FillRateTestBlendDepth(const char * name)194 void DrawArraysTestFunc::FillRateTestBlendDepth(const char* name) {
195 const int buffer_len = 64;
196 char buffer[buffer_len];
197
198 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
199 glEnable(GL_BLEND);
200 snprintf(buffer, buffer_len, "%s_blended", name);
201 RunTest(this, buffer, g_width * g_height, g_width, g_height, true);
202 glDisable(GL_BLEND);
203
204 // We are relying on the default depth clear value of 1 here.
205 // Fragments should have depth 0.
206 glEnable(GL_DEPTH_TEST);
207 glDepthFunc(GL_NOTEQUAL);
208 snprintf(buffer, buffer_len, "%s_depth_neq", name);
209 RunTest(this, buffer, g_width * g_height, g_width, g_height, true);
210
211 // The DrawArrays call invoked by this test shouldn't render anything
212 // because every fragment will fail the depth test. Therefore we
213 // should see the clear color.
214 glDepthFunc(GL_NEVER);
215 snprintf(buffer, buffer_len, "%s_depth_never", name);
216 RunTest(this, buffer, g_width * g_height, g_width, g_height, true);
217 glDisable(GL_DEPTH_TEST);
218 }
219
TestFunc(uint64_t iterations)220 bool DrawElementsTestFunc::TestFunc(uint64_t iterations) {
221 glClearColor(0, 1.f, 0, 1.f);
222 glClear(GL_COLOR_BUFFER_BIT);
223 glDrawElements(GL_TRIANGLES, count_, GL_UNSIGNED_SHORT, 0);
224 glFlush();
225 for (uint64_t i = 0; i < iterations - 1; ++i) {
226 glDrawElements(GL_TRIANGLES, count_, GL_UNSIGNED_SHORT, 0);
227 }
228 return true;
229 }
230
231 } // namespace glbench
232