• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 <base/files/file_util.h>
11 #include <base/memory/scoped_ptr.h>
12 
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 45
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 temperature = 0;
54   double wait = 0;
55 
56   // By default we try to cool to initial + 5'C but don't wait longer than 30s.
57   // But in hasty mode we really don't want to spend too much time to get the
58   // numbers right, so we don't wait at all.
59   if (!::g_notemp) {
60     wait = WaitForCoolMachine(initial_temperature + 5.0, 30.0, &temperature);
61     printf("Bench: Cooled down to %.1f'C (initial=%.1f'C) after waiting %.1fs.\n",
62            temperature, initial_temperature, wait);
63     if (temperature > initial_temperature + 10.0)
64       printf("Warning: Machine did not cool down enough for next test!");
65   }
66 
67   // Do two iterations because initial timings can vary wildly.
68   TimeTest(test, 2);
69 
70   // We average the times for the last two runs to reduce noise. We could
71   // sum up all runs but the initial measurements have high CPU overhead,
72   // while the last two runs are both on the order of MIN_ITERATION_DURATION_US.
73   uint64_t iterations = 1;
74   uint64_t iterations_prev = 0;
75   uint64_t time = 0;
76   uint64_t time_prev = 0;
77   do {
78     time = TimeTest(test, iterations);
79     dbg_printf("iterations: %llu: time: %llu time/iter: %llu\n",
80            iterations, time, time / iterations);
81 
82     // If we are running in hasty mode we will stop after a fraction of the
83     // testing time and return much more noisy performance numbers. The MD5s
84     // of the images should stay the same though.
85     if (time > MIN_ITERATION_DURATION_US / (::g_hasty ? 20.0 : 1.0))
86       return (static_cast<double>(time + time_prev) /
87               (iterations + iterations_prev));
88 
89     time_prev = time;
90     iterations_prev = iterations;
91     iterations *= 2;
92   } while (iterations < (1ULL<<40));
93 
94   return 0.0;
95 }
96 
SaveImage(const char * name,const int width,const int height)97 void SaveImage(const char* name, const int width, const int height) {
98   const int size = width * height * 4;
99   scoped_ptr<char[]> pixels(new char[size]);
100   glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, pixels.get());
101   // I really think we want to use outdir as a straight argument
102   base::FilePath dirname = base::FilePath(FLAGS_outdir);
103   base::CreateDirectory(dirname);
104   base::FilePath filename = dirname.Append(name);
105   write_png_file(filename.value().c_str(),
106                  pixels.get(), width, height);
107 }
108 
ComputeMD5(unsigned char digest[16],const int width,const int height)109 void ComputeMD5(unsigned char digest[16], const int width, const int height) {
110   MD5Context ctx;
111   MD5Init(&ctx);
112   const int size = width * height * 4;
113   scoped_ptr<char[]> pixels(new char[size]);
114   glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, pixels.get());
115   MD5Update(&ctx, (unsigned char *)pixels.get(), size);
116   MD5Final(digest, &ctx);
117 }
118 
RunTest(TestBase * test,const char * testname,const double coefficient,const int width,const int height,bool inverse)119 void RunTest(TestBase* test, const char* testname, const double coefficient,
120              const int width, const int height, bool inverse) {
121   double value;
122   char name_png[512] = "";
123   GLenum error = glGetError();
124 
125   if (error != GL_NO_ERROR) {
126     value = -1.0;
127     printf("# Error: %s aborted, glGetError returned 0x%02x.\n",
128            testname, error);
129     sprintf(name_png, "glGetError=0x%02x", error);
130   } else {
131     value = Bench(test);
132 
133     // Bench returns 0.0 if it ran max iterations in less than a min test time.
134     if (value == 0.0) {
135       strcpy(name_png, "no_score");
136     } else {
137       value = coefficient * (inverse ? 1.0 / value : value);
138 
139       if (!test->IsDrawTest()) {
140         strcpy(name_png, "none");
141       } else {
142         // save as png with MD5 as hex string attached
143         char          pixmd5[33];
144         unsigned char d[16];
145         ComputeMD5(d, width, height);
146         // translate to hexadecimal ASCII of MD5
147         sprintf(pixmd5,
148           "%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x",
149           d[ 0],d[ 1],d[ 2],d[ 3],d[ 4],d[ 5],d[ 6],d[ 7],
150           d[ 8],d[ 9],d[10],d[11],d[12],d[13],d[14],d[15]);
151         sprintf(name_png, "%s.pixmd5-%s.png", testname, pixmd5);
152 
153         if (FLAGS_save)
154           SaveImage(name_png, width, height);
155       }
156     }
157   }
158 
159   // TODO(ihf) adjust string length based on longest test name
160   int name_length = strlen(testname);
161   if (name_length > MAX_TESTNAME)
162     printf("# Warning: adjust string formatting to length = %d\n",
163            name_length);
164   // Results are marked using a leading '@RESULT: ' to allow parsing.
165   printf("@RESULT: %-*s = %10.2f %-15s [%s]\n",
166          MAX_TESTNAME, testname, value, test->Unit(), name_png);
167 }
168 
TestFunc(uint64_t iterations)169 bool DrawArraysTestFunc::TestFunc(uint64_t iterations) {
170   glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
171   glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
172   glFlush();
173   for (uint64_t i = 0; i < iterations - 1; ++i) {
174     glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
175   }
176   return true;
177 }
178 
179 
FillRateTestNormal(const char * name)180 void DrawArraysTestFunc::FillRateTestNormal(const char* name) {
181   FillRateTestNormalSubWindow(name, g_width, g_height);
182 }
183 
184 
FillRateTestNormalSubWindow(const char * name,const int width,const int height)185 void DrawArraysTestFunc::FillRateTestNormalSubWindow(const char* name,
186                                                      const int width,
187                                                      const int height)
188 {
189   RunTest(this, name, width * height, width, height, true);
190 }
191 
192 
FillRateTestBlendDepth(const char * name)193 void DrawArraysTestFunc::FillRateTestBlendDepth(const char *name) {
194   const int buffer_len = 64;
195   char buffer[buffer_len];
196 
197   glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
198   glEnable(GL_BLEND);
199   snprintf(buffer, buffer_len, "%s_blended", name);
200   RunTest(this, buffer, g_width * g_height, g_width, g_height, true);
201   glDisable(GL_BLEND);
202 
203   // We are relying on the default depth clear value of 1 here.
204   // Fragments should have depth 0.
205   glEnable(GL_DEPTH_TEST);
206   glDepthFunc(GL_NOTEQUAL);
207   snprintf(buffer, buffer_len, "%s_depth_neq", name);
208   RunTest(this, buffer, g_width * g_height, g_width, g_height, true);
209 
210   // The DrawArrays call invoked by this test shouldn't render anything
211   // because every fragment will fail the depth test.  Therefore we
212   // should see the clear color.
213   glDepthFunc(GL_NEVER);
214   snprintf(buffer, buffer_len, "%s_depth_never", name);
215   RunTest(this, buffer, g_width * g_height, g_width, g_height, true);
216   glDisable(GL_DEPTH_TEST);
217 }
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