blob: 95c011c92f2e561f958670451d78e7af07e315bd [file] [log] [blame]
/*
* Copyright (c) 2011 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 <GLES2/gl2.h>
#include <GLES2/gl2ext.h>
#include <EGL/egl.h>
#include <EGL/eglext.h>
#include <X11/Xlib.h>
#include <X11/Xatom.h>
#include <X11/Xutil.h>
#include <stdlib.h>
#include <unistd.h>
#include <getopt.h>
#include <stdio.h>
#include <math.h>
#include <sys/time.h>
#include <string.h>
#include <stdbool.h>
#include <assert.h>
const char vertex_src [] =
" \
uniform mat4 transformMatrix; \
attribute vec4 position; \
attribute vec4 tcoord; \
varying vec2 st; \
\
void main() \
{ \
gl_Position = transformMatrix * position; \
st = tcoord.st; \
} \
";
const char fragment_src [] =
" \
precision highp float; \
uniform sampler2D tex; \
varying vec2 st; \
\
void main() \
{ \
gl_FragColor = texture2D(tex, st); \
} \
";
#define TEST_WIDTH 256
#define TEST_HEIGHT 256
#define TEXTURE_WIDTH 2048
#define TEXTURE_HEIGHT 2048
#define TEXTURE_COUNT 32
#define TEXTURE_DEFAULT_X 0
#define TEXTURE_DEFAULT_Y 0
#define DEFAULT_LOOP_COUNT 100
static const GLfloat sVertData[] = {
-1, -1, 0, 1,
1, -1, 0, 1,
-1, 1, 0, 1,
1, 1, 0, 1
};
static GLuint vertex_obj, fragment_obj, program_obj;
int verbose = 0;
static Display *x_display;
static Window win;
static EGLDisplay egl_display;
static EGLContext egl_context;
static EGLSurface egl_surface;
/*
* This function creates an RGBA texture with a given width and height.
* It also takes in a number which is used to give the texture a slightly
* different shade of blue to be able to distinguish visually between
* different textures.
* Return value: handle to texture
*/
static GLuint CreateTexture(int width, int height, int number)
{
char *data = NULL;
int x, y, bytes_per_pixel;
GLuint tex;
assert(number == (number & 0xF));
// There are 4 bytes per pixel for GL_RGBA & GL_UNSIGNED_BYTE
bytes_per_pixel = 4;
data = (char *)malloc((size_t)(width*height*bytes_per_pixel));
if (!data)
return -1;
for (x = 0; x < width; x++) {
for (y = 0 ; y < height; y++) {
int idx = (y*width + x)*bytes_per_pixel;
data[idx] = (number * 0xF) & 0xFF;
data[idx+1] = (number * 0xF) & 0xFF;
data[idx+2] = 0xFF;
data[idx+3] = 0xFF;
}
}
// create texture
glGenTextures(1, &tex);
if (glGetError() != GL_NO_ERROR)
goto fail;
glActiveTexture(GL_TEXTURE0);
if (glGetError() != GL_NO_ERROR)
goto fail;
glBindTexture(GL_TEXTURE_2D, tex);
if (glGetError() != GL_NO_ERROR)
goto fail;
glTexImage2D(
/* target */ GL_TEXTURE_2D,
/* level */ 0,
/* internalformat */ (GLint)GL_RGBA,
/* width */ width,
/* height */ height,
/* border */ 0,
/* format */ GL_RGBA,
/* type */ GL_UNSIGNED_BYTE,
/* pixels */ data);
if (glGetError() != GL_NO_ERROR)
goto fail;
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
goto done;
fail:
tex = -1;
done:
free(data);
return tex;
}
/*
* Filling in the result array with an identity matrix.
*/
static void LoadIdentity(GLfloat *result)
{
memset(result, 0x0, 16*4);
result[0] = 1;
result[5] = 1;
result[10] = 1;
result[15] = 1;
}
/*
* Create a scaling matrix.
*/
static void Scale(GLfloat *result, GLfloat sx, GLfloat sy, GLfloat sz)
{
result[0] *= sx;
result[1] *= sx;
result[2] *= sx;
result[3] *= sx;
result[4] *= sy;
result[5] *= sy;
result[6] *= sy;
result[7] *= sy;
result[8] *= sz;
result[9] *= sz;
result[10] *= sz;
result[11] *= sz;
}
/*
* Creates a translation matrix.
*/
static void Translate(GLfloat *result, GLfloat tx, GLfloat ty, GLfloat tz)
{
result[12] += (result[0] * tx + result[4] * ty + result[8] * tz);
result[13] += (result[1] * tx + result[5] * ty + result[9] * tz);
result[14] += (result[2] * tx + result[6] * ty + result[10] * tz);
result[15] += (result[3] * tx + result[7] * ty + result[11] * tz);
}
/*
* This function runs the actual test, drawing the texture rects.
* Return value: 0 on success
*/
static int RunTest(int width, int height)
{
GLuint tex[TEXTURE_COUNT];
GLint texSampler;
GLint transformMatrixUniform;
GLfloat vertSTData[8];
int i, j;
GLfloat transformMatrix[16];
int cols = (int)sqrtf(TEXTURE_COUNT);
struct timeval tv;
int rnd;
gettimeofday(&tv, NULL);
rnd = tv.tv_sec * 1000;
glViewport(0, 0, width, height);
glClear(GL_COLOR_BUFFER_BIT);
// Create texture
for (i = 0; i < TEXTURE_COUNT; i++) {
if (verbose)
printf("Allocating texture %d\n", i);
tex[i] = CreateTexture(width / (1 + TEXTURE_COUNT - i),
height / (1 + TEXTURE_COUNT - i), (i % 16));
}
// Texture coords
vertSTData[0] = 0;
vertSTData[1] = 0;
vertSTData[2] = width;
vertSTData[3] = 0;
vertSTData[4] = 0;
vertSTData[5] = height;
vertSTData[6] = width;
vertSTData[7] = height;
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 0, vertSTData);
texSampler = glGetUniformLocation(program_obj, "tex");
transformMatrixUniform = glGetUniformLocation(program_obj,
"transformMatrix");
glUniform1i(texSampler, 0);
// Draw texture rectangles
for (j = 0; j < 50; j++) {
LoadIdentity(transformMatrix);
Scale(transformMatrix, 4.0f/cols, 4.0f/cols, 4.0f/cols);
Translate(transformMatrix, -cols - 1.0f, cols - 1.0f, 0.0f);
for (i = 0; i < TEXTURE_COUNT; i++) {
rnd = rnd * 69069 + 69069;
if(((rnd / 1217) & 255) > 128) {
Translate(transformMatrix, 2.0f, 0.0f, 0.0f);
glUniformMatrix4fv(transformMatrixUniform, 1, GL_FALSE,
transformMatrix);
glBindTexture(GL_TEXTURE_2D, tex[i]);
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
if (((i+1) % cols) == 0) {
Translate(transformMatrix, -2.0f*cols, -2.0f,
0.0f);
}
}
}
glFlush();
}
// Clean up
for (i = 0; i < TEXTURE_COUNT; i++) {
glDeleteTextures(1, &tex[i]);
}
return 0;
}
/*
* This function prints the info log for a given shader (from handle).
*/
void PrintShaderInfoLog(GLuint shader)
{
GLint length;
glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &length);
if (length) {
char buffer[length];
glGetShaderInfoLog(shader, length, NULL, buffer);
printf("shader info: %s\n", buffer);
}
}
GLuint LoadShader(const char *shader_src, GLenum type)
{
GLuint shader = glCreateShader(type);
GLint success;
glShaderSource(shader, 1, &shader_src, NULL);
glCompileShader(shader);
glGetShaderiv(shader, GL_COMPILE_STATUS, &success);
if (success != GL_TRUE) {
printf("FAILED to compile shader. %d\n", success);
return success;
}
if (verbose)
PrintShaderInfoLog(shader);
return shader;
}
static void InitGraphicsState()
{
glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 0, sVertData);
glEnableVertexAttribArray(0);
glEnableVertexAttribArray(1);
vertex_obj = LoadShader(vertex_src, GL_VERTEX_SHADER);
fragment_obj = LoadShader(fragment_src, GL_FRAGMENT_SHADER);
program_obj = glCreateProgram();
glAttachShader(program_obj, vertex_obj);
glAttachShader(program_obj, fragment_obj);
glBindAttribLocation(program_obj, 0, "position");
glBindAttribLocation(program_obj, 1, "tcoord");
glLinkProgram(program_obj);
glUseProgram(program_obj);
// so that odd-sized RGB textures will work nicely
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
glDisable(GL_DEPTH_TEST);
}
int XInitialize(int x, int y, int width, int height)
{
Window root;
XSetWindowAttributes swa;
XSetWindowAttributes xattr;
Atom atom;
XWMHints hints;
int xres;
x_display = XOpenDisplay(NULL);
if (x_display == NULL) {
printf("Cannot connect to X server. Exiting...\n");
return -1;
}
root = DefaultRootWindow(x_display);
swa.event_mask = ExposureMask | PointerMotionMask | KeyPressMask;
if (verbose)
printf("Creating window at (%d,%d) with w=%d, h=%d\n",
x, y, width, height);
win = XCreateWindow(
/* connection to x server */ x_display,
/* parent window */ root,
/* x coord, top left corner */ x,
/* y coord, top left corner */ y,
/* width of window */ width,
/* height of window */ height,
/* border width */ 0,
/* depth of window */ CopyFromParent,
/* window's class */ InputOutput,
/* visual type */ CopyFromParent,
/* valid attribute mask */ CWEventMask,
/* attributes */ &swa);
if (win == BadAlloc ||
win == BadColor ||
win == BadCursor ||
win == BadMatch ||
win == BadPixmap ||
win == BadValue ||
win == BadWindow) {
printf("FAILED to create X window\n");
return -1;
}
xattr.override_redirect = false;
xres = XChangeWindowAttributes(x_display, win, CWOverrideRedirect,
&xattr);
if (xres == BadAccess ||
xres == BadColor ||
xres == BadCursor ||
xres == BadMatch ||
xres == BadPixmap ||
xres == BadValue ||
xres == BadWindow) {
printf("FAIL to change X window attrib: %d\n", xres);
goto fail;
}
atom = XInternAtom(x_display, "_NET_WM_STATE_FULLSCREEN", true);
hints.input = true;
hints.flags = InputHint;
XSetWMHints(x_display, win, &hints);
if (xres == BadAlloc || xres == BadWindow) {
printf("FAIL to set X WM hints: %d\n", xres);
goto fail;
}
XMapWindow(x_display, win);
if (xres == BadWindow) {
printf("FAIL to map X window: %d\n", xres);
goto fail;
}
XStoreName(x_display, win, "GLES2 Texture Test");
if (xres == BadAlloc || xres == BadWindow) {
printf("FAIL to store X window name: %d\n", xres);
goto fail;
}
return 0;
fail:
XDestroyWindow(x_display, win);
XCloseDisplay(x_display);
return -1;
}
int EglInitialize()
{
EGLConfig config;
EGLint numConfig;
EGLint attr[] = {
EGL_BUFFER_SIZE, 16,
EGL_RENDERABLE_TYPE,
EGL_OPENGL_ES2_BIT,
EGL_NONE
};
EGLint ctxattr[] = {
EGL_CONTEXT_CLIENT_VERSION, 2,
EGL_NONE
};
egl_display = eglGetDisplay((EGLNativeDisplayType)x_display);
if (egl_display == EGL_NO_DISPLAY) {
printf("EGL failed to obtain display. Exiting...\n");
return -1;
}
if ( !eglInitialize(egl_display, NULL, NULL)) {
printf("EGL failed to initialize. Exiting...\n");
return -1;
}
if ( !eglChooseConfig(egl_display, attr, &config, 1, &numConfig)) {
printf("EGL failed to choose config. Exiting...\n");
return -1;
}
if (numConfig != 1) {
printf("EGL failed to get 1 config, got %d Exiting...\n",
numConfig);
return -1;
}
egl_surface = eglCreateWindowSurface(egl_display, config, win, NULL);
if (egl_surface == EGL_NO_SURFACE) {
printf("EGL failed to create window surface. Exiting...\n");
return -1;
}
egl_context = eglCreateContext(egl_display, config, EGL_NO_CONTEXT,
ctxattr);
if (egl_context == EGL_NO_CONTEXT) {
printf("EGL failed to create context. Exiting...\n");
return -1;
}
if ( !eglMakeCurrent(egl_display, egl_surface, egl_surface,
egl_context)) {
printf("EGL failed to make context current. Exiting...\n");
return -1;
}
return 0;
}
void PrintUsage()
{
printf("--------------------------------------------\n");
printf("nvmap_iovmm_stress [options]\n");
printf(" --help - Show this help screen\n");
printf(" -x - Set window x coordinate[ def: %d]\n",
TEXTURE_DEFAULT_X);
printf(" -y - Set window y coordinate[ def: %d]\n",
TEXTURE_DEFAULT_Y);
printf(" -w --width - Set window width [ def: %d]\n",
TEXTURE_WIDTH);
printf(" -h | --height - Set window height [ def: %d]\n",
TEXTURE_HEIGHT);
printf(" -i | --infinte_loop - Enables running forever\n");
printf(" -v | --verbose - Enables verbose prints\n");
printf(" -l | --loop_count - # of times to loop [def: %d]\n",
DEFAULT_LOOP_COUNT);
}
void CleanupX()
{
XDestroyWindow(x_display, win);
XCloseDisplay(x_display);
}
void CleanupEgl()
{
eglDestroyContext(egl_display, egl_context);
eglDestroySurface(egl_display, egl_surface);
eglTerminate(egl_display);
}
int main(int argc, char *argv[])
{
int failure = 0;
int i, x = TEXTURE_DEFAULT_X, y = TEXTURE_DEFAULT_Y;
int height = TEST_HEIGHT, width = TEST_WIDTH;
int loop_count = DEFAULT_LOOP_COUNT;
int option_index = 0;
GLenum err_code;
static struct option long_options[] = {
{"help", no_argument, 0, 'p'},
{"verbose", no_argument, 0, 'v'},
{"width", required_argument, 0, 'w'},
{"height", required_argument, 0, 'h'},
{"loop_count", required_argument, 0, 'l'},
{NULL, 0, NULL, 0}
};
if (!getenv("DISPLAY")) {
printf("DISPLAY environmental variable not set.\n");
failure = -1;
goto done;
}
while ((i = getopt_long(argc, argv, "l:x:y:w:h:vp", long_options,
&option_index)) != -1)
switch (i) {
case 'l':
loop_count = atoi(optarg);
break;
case 'x':
x = atoi(optarg);
break;
case 'y':
y = atoi(optarg);
break;
case 'w':
width = atoi(optarg);
break;
case 'h':
height = atoi(optarg);
break;
case 'v':
verbose = 1;
break;
case 'p':
PrintUsage();
return 0;
case '?':
printf("unknown option `\\x%x`.\n", optopt);
return 1;
default:
goto done;
}
failure = XInitialize(x, y, width, height);
if (failure)
goto done;
failure = EglInitialize();
if (failure)
goto clean_x;
InitGraphicsState();
printf("Test started, window (x,y,w,h) = (%d,%d,%d,%d), pid = %d.\n",
x, y, width, height, getpid());
if (verbose)
printf("Looping for %d iterations.\n", loop_count);
for(i = 0; i < loop_count; i++) {
int j;
for(j = 0 ; j < 3 ; j++) {
failure |= RunTest(width, height);
}
eglSwapBuffers(egl_display, egl_surface);
}
if (!failure) {
err_code = glGetError();
if (err_code == GL_NO_ERROR)
failure = false;
else {
printf("GL Error Occured : %d\n", err_code);
failure = 1;
}
}
CleanupEgl();
clean_x:
CleanupX();
done:
printf("Test completed [%s]: pid = %d\n",
(failure ? "FAIL" : "SUCCESS"), getpid());
return failure ? -1 : 0;
}