// 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