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