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 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. 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 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 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 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 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 184 void DrawArraysTestFunc::FillRateTestNormal(const char* name) { 185 FillRateTestNormalSubWindow(name, g_width, g_height); 186 } 187 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 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 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