| // 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"), |
| }; |
| |
| 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) |
| { |
| char info_log[4096]; |
| int length; |
| glGetError(); |
| glGetShaderInfoLog(obj, sizeof(info_log)-1, &length, info_log); |
| if (glGetError() != 0) |
| 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: glGetShader/ProgramInfoLog: %s\n", p); |
| if (!newline) |
| break; |
| p = newline + 1; |
| } |
| } |
| |
| 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_info_log(vertex_shader); |
| glCompileShader(fragment_shader); |
| print_info_log(fragment_shader); |
| |
| GLuint program = glCreateProgram(); |
| glAttachShader(program, vertex_shader); |
| glAttachShader(program, fragment_shader); |
| glLinkProgram(program); |
| print_info_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, 0.f); |
| } |
| |
| } // namespace glbench |