// Copyright (c) 2010 The Chromium OS Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include <fcntl.h> #include <stdio.h> #include <string.h> #include <sys/mman.h> #include <sys/stat.h> #include <unistd.h> #include "base/logging.h" #include "glinterface.h" #include "main.h" #include "utils.h" using base::FilePath; const char* kGlesHeader = "#ifdef GL_ES\n" "precision highp float;\n" "#endif\n"; FilePath *g_base_path = new FilePath(); double g_initial_temperature = -1000.0; // Sets the base path for MmapFile to `dirname($argv0)`/$relative. void SetBasePathFromArgv0(const char* argv0, const char* relative) { if (g_base_path) { delete g_base_path; } FilePath argv0_path = FilePath(argv0).DirName(); FilePath base_path = relative ? argv0_path.Append(relative) : argv0_path; g_base_path = new FilePath(base_path); } const FilePath& GetBasePath() { return *g_base_path; } void *MmapFile(const char* name, size_t* length) { FilePath filename = g_base_path->Append(name); int fd = open(filename.value().c_str(), O_RDONLY); if (fd == -1) return NULL; struct stat sb; CHECK(fstat(fd, &sb) != -1); char *mmap_ptr = static_cast<char *>( mmap(NULL, sb.st_size, PROT_READ, MAP_PRIVATE, fd, 0)); close(fd); if (mmap_ptr) *length = sb.st_size; return mmap_ptr; } bool read_int_from_file(FilePath filename, int *value) { FILE *fd = fopen(filename.value().c_str(), "r"); if (!fd) { return false; } int count = fscanf(fd, "%d", value); if (count != 1) { printf("Error: could not read integer from file. (%s)\n", filename.value().c_str()); if(count != 1) return false; } fclose(fd); return true; } // Returns temperature at which CPU gets throttled. // TODO(ihf): update this based on the outcome of crbug.com/356422. double get_temperature_critical() { FilePath filename = FilePath("/sys/class/hwmon/hwmon0/temp1_crit"); int temperature_mCelsius = 0; if (!read_int_from_file(filename, &temperature_mCelsius)) { // spring is special :-(. filename = FilePath("/sys/devices/virtual/hwmon/hwmon1/temp1_crit"); if (!read_int_from_file(filename, &temperature_mCelsius)) { // 85'C is the minimum observed critical temperature so far. printf("Warning: guessing critical temperature as 85'C.\n"); return 85.0; } } double temperature_Celsius = 0.001 * temperature_mCelsius; // Simple sanity check for reasonable critical temperatures. assert(temperature_Celsius >= 60.0); assert(temperature_Celsius <= 150.0); return temperature_Celsius; } // Returns currently measured temperature. // TODO(ihf): update this based on the outcome of crbug.com/356422. double get_temperature_input() { FilePath filenames[] = { FilePath("/sys/class/hwmon/hwmon0/temp1_input"), FilePath("/sys/class/hwmon/hwmon1/temp1_input"), FilePath("/sys/devices/platform/coretemp.0/temp1_input"), FilePath("/sys/devices/platform/coretemp.0/temp2_input"), FilePath("/sys/devices/platform/coretemp.0/temp3_input"), FilePath("/sys/devices/virtual/hwmon/hwmon0/temp1_input"), FilePath("/sys/devices/virtual/hwmon/hwmon0/temp2_input"), FilePath("/sys/devices/virtual/hwmon/hwmon1/temp1_input"), FilePath("/sys/devices/virtual/hwmon/hwmon2/temp1_input"), FilePath("/sys/devices/virtual/hwmon/hwmon3/temp1_input"), FilePath("/sys/devices/virtual/hwmon/hwmon4/temp1_input"), // kevin & elm FilePath("/sys/devices/virtual/thermal/thermal_zone0/temp"), FilePath("/sys/devices/virtual/thermal/thermal_zone1/temp"), FilePath("/sys/devices/virtual/thermal/thermal_zone2/temp"), FilePath("/sys/devices/virtual/thermal/thermal_zone3/temp"), FilePath("/sys/devices/virtual/thermal/thermal_zone4/temp"), }; int temperature_mCelsius = 0; int max_temperature_mCelsius = -1000000.0; for (unsigned int i = 0; i < sizeof(filenames) / sizeof(FilePath); i++) { if (read_int_from_file(filenames[i], &temperature_mCelsius)) { // Hack: Ignore values outside of 10'C...150'C for now. if (temperature_mCelsius < 10000 || temperature_mCelsius > 150000) { printf("Warning: ignoring temperature reading of %d m'C.\n", temperature_mCelsius); } else { max_temperature_mCelsius = std::max(max_temperature_mCelsius, temperature_mCelsius); } } } double temperature_Celsius = 0.001 * max_temperature_mCelsius; if (temperature_Celsius < 10.0 || temperature_Celsius > 150.0) { printf("Warning: ignoring temperature reading of %f'C.\n", temperature_Celsius); } return temperature_Celsius; } const double GetInitialMachineTemperature() { return g_initial_temperature; } // TODO(ihf): update this based on the outcome of crbug.com/356422. // In particular we should probably just have a system script that we can call // and read the output from. double GetMachineTemperature() { double max_temperature = get_temperature_input(); return max_temperature; } // Waits up to timeout seconds to reach cold_temperature in Celsius. double WaitForCoolMachine(double cold_temperature, double timeout, double *temperature) { // Integer times are in micro-seconds. uint64_t time_start = GetUTime(); uint64_t time_now = time_start; uint64_t time_end = time_now + 1e6 * timeout; *temperature = GetMachineTemperature(); while (time_now < time_end) { if (*temperature < cold_temperature) break; sleep(1.0); time_now = GetUTime(); *temperature = GetMachineTemperature(); } double wait_time = 1.0e-6 * (time_now - time_start); assert(wait_time >= 0); assert(wait_time < timeout + 5.0); return wait_time; } namespace glbench { GLuint SetupTexture(GLsizei size_log2) { GLsizei size = 1 << size_log2; GLuint name = ~0; glGenTextures(1, &name); glBindTexture(GL_TEXTURE_2D, name); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); unsigned char *pixels = new unsigned char[size * size * 4]; if (!pixels) return 0; for (GLint level = 0; size > 0; level++, size /= 2) { unsigned char *p = pixels; for (int i = 0; i < size; i++) { for (int j = 0; j < size; j++) { *p++ = level %3 != 0 ? (i ^ j) << level : 0; *p++ = level %3 != 1 ? (i ^ j) << level : 0; *p++ = level %3 != 2 ? (i ^ j) << level : 0; *p++ = 255; } } if (size == 1) { unsigned char *p = pixels; *p++ = 255; *p++ = 255; *p++ = 255; *p++ = 255; } glTexImage2D(GL_TEXTURE_2D, level, GL_RGBA, size, size, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels); } delete[] pixels; return name; } GLuint SetupVBO(GLenum target, GLsizeiptr size, const GLvoid *data) { GLuint buf = ~0; glGenBuffers(1, &buf); glBindBuffer(target, buf); glBufferData(target, size, data, GL_STATIC_DRAW); CHECK(!glGetError()); return buf; } // Generates a lattice symmetric around the origin (all quadrants). void CreateLattice(GLfloat **vertices, GLsizeiptr *size, GLfloat size_x, GLfloat size_y, int width, int height) { GLfloat *vptr = *vertices = new GLfloat[2 * (width + 1) * (height + 1)]; GLfloat shift_x = size_x * width; GLfloat shift_y = size_y * height; for (int j = 0; j <= height; j++) { for (int i = 0; i <= width; i++) { *vptr++ = 2 * i * size_x - shift_x; *vptr++ = 2 * j * size_y - shift_y; } } *size = (vptr - *vertices) * sizeof(GLfloat); } // Generates a mesh of 2*width*height triangles. The ratio of front facing to // back facing triangles is culled_ratio/RAND_MAX. Returns the number of // vertices in the mesh. int CreateMesh(GLushort **indices, GLsizeiptr *size, int width, int height, int culled_ratio) { srand(0); // We use 16 bit indices for compatibility with GL ES CHECK(height * width + width + height <= 65535); GLushort *iptr = *indices = new GLushort[2 * 3 * (width * height)]; const int swath_height = 4; CHECK(width % swath_height == 0 && height % swath_height == 0); for (int j = 0; j < height; j += swath_height) { for (int i = 0; i < width; i++) { for (int j2 = 0; j2 < swath_height; j2++) { GLushort first = (j + j2) * (width + 1) + i; GLushort second = first + 1; GLushort third = first + (width + 1); GLushort fourth = third + 1; bool flag = rand() < culled_ratio; *iptr++ = first; *iptr++ = flag ? second : third; *iptr++ = flag ? third : second; *iptr++ = fourth; *iptr++ = flag ? third : second; *iptr++ = flag ? second : third; } } } *size = (iptr - *indices) * sizeof(GLushort); return iptr - *indices; } static void print_info_log(int obj, bool shader) { char info_log[4096]; int length; if (shader) glGetShaderInfoLog(obj, sizeof(info_log)-1, &length, info_log); else glGetProgramInfoLog(obj, sizeof(info_log)-1, &length, info_log); char *p = info_log; while (p < info_log + length) { char *newline = strchr(p, '\n'); if (newline) *newline = '\0'; printf("# Info: glGet%sInfoLog: %s\n", shader ? "Shader" : "Program", p); if (!newline) break; p = newline + 1; } } static void print_shader_log(int shader) { print_info_log(shader, true); } static void print_program_log(int program) { print_info_log(program, false); } GLuint InitShaderProgram(const char *vertex_src, const char *fragment_src) { return InitShaderProgramWithHeader(NULL, vertex_src, fragment_src); } GLuint InitShaderProgramWithHeader(const char* header, const char* vertex_src, const char* fragment_src) { const char* headers[] = {kGlesHeader, header}; return InitShaderProgramWithHeaders(headers, arraysize(headers) - (header ? 0 : 1), vertex_src, fragment_src); } GLuint InitShaderProgramWithHeaders(const char** headers, int count, const char* vertex_src, const char* fragment_src) { GLuint vertex_shader = glCreateShader(GL_VERTEX_SHADER); GLuint fragment_shader = glCreateShader(GL_FRAGMENT_SHADER); const char** header_and_body = new const char*[count + 1]; if (count != 0) memcpy(header_and_body, headers, count * sizeof(const char*)); header_and_body[count] = vertex_src; glShaderSource(vertex_shader, count + 1, header_and_body, NULL); header_and_body[count] = fragment_src; glShaderSource(fragment_shader, count + 1, header_and_body, NULL); delete[] header_and_body; glCompileShader(vertex_shader); print_shader_log(vertex_shader); glCompileShader(fragment_shader); print_shader_log(fragment_shader); GLuint program = glCreateProgram(); glAttachShader(program, vertex_shader); glAttachShader(program, fragment_shader); glLinkProgram(program); print_program_log(program); glUseProgram(program); glDeleteShader(vertex_shader); glDeleteShader(fragment_shader); return program; } void ClearBuffers() { glClearColor(1.f, 0, 0, 1.f); glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT); g_main_gl_interface->SwapBuffers(); glClearColor(0, 1.f, 0, 1.f); glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT); g_main_gl_interface->SwapBuffers(); glClearColor(0, 0, 0.f, 1.f); } } // namespace glbench