/**
*
* Beryl benchmark plugin
*
* bench.c
*
* Copyright : (C) 2006 by Dennis Kasprzyk
* E-mail : onestone@beryl-project.org
*
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
**/
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <math.h>
#include <X11/Xatom.h>
#include <X11/extensions/Xrender.h>
#include <beryl.h>
#include "bench_tex.h"
#define GET_BENCH_DISPLAY(d) \
((BenchDisplay *) (d)->privates[displayPrivateIndex].ptr)
#define BENCH_DISPLAY(d) \
BenchDisplay *bd = GET_BENCH_DISPLAY (d)
#define GET_BENCH_SCREEN(s, bd) \
((BenchScreen *) (s)->privates[(bd)->screenPrivateIndex].ptr)
#define BENCH_SCREEN(s) \
BenchScreen *bs = GET_BENCH_SCREEN (s, GET_BENCH_DISPLAY (s->display))
#define TIMEVALDIFF(tv1, tv2) \
(((tv1)->tv_sec == (tv2)->tv_sec || (tv1)->tv_usec >= (tv2)->tv_usec) ? \
((((tv1)->tv_sec - (tv2)->tv_sec) * 1000000) + \
((tv1)->tv_usec - (tv2)->tv_usec)) / 1000 : \
((((tv1)->tv_sec - 1 - (tv2)->tv_sec) * 1000000) + \
(1000000 + (tv1)->tv_usec - (tv2)->tv_usec)) / 1000)
#define TIMEVALDIFFU(tv1, tv2) \
(((tv1)->tv_sec == (tv2)->tv_sec || (tv1)->tv_usec >= (tv2)->tv_usec) ? \
((((tv1)->tv_sec - (tv2)->tv_sec) * 1000000) + \
((tv1)->tv_usec - (tv2)->tv_usec)): \
((((tv1)->tv_sec - 1 - (tv2)->tv_sec) * 1000000) + \
(1000000 + (tv1)->tv_usec - (tv2)->tv_usec)))
#ifdef GL_DEBUG
static GLenum gl_error;
#define GLERR gl_error=glGetError(); if (gl_error != GL_NO_ERROR) { fprintf (stderr,"GL error 0x%X has occured at %s:%d\n",gl_error,__FILE__,__LINE__); }
#else
#define GLERR
#endif
#define BENCH_DISPLAY_OPTION_INITIATE 0
#define BENCH_DISPLAY_OPTION_DISABLE_LIMIT 1
#define BENCH_DISPLAY_OPTION_POS_X 2
#define BENCH_DISPLAY_OPTION_POS_Y 3
#define BENCH_DISPLAY_OPTION_OUTPUT_SCREEN 4
#define BENCH_DISPLAY_OPTION_OUTPUT_CONSOLE 5
#define BENCH_DISPLAY_OPTION_CONSOLE_UPDATE_TIME 6
#define BENCH_DISPLAY_OPTION_NUM 7
#define BENCH_DISPLAY_OPTION_DISABLE_LIMIT_DEFAULT TRUE
#define BENCH_DISPLAY_OPTION_POS_X_DEFAULT 0
#define BENCH_DISPLAY_OPTION_POS_Y_DEFAULT 0
#define BENCH_DISPLAY_OPTION_OUTPUT_SCREEN_DEFAULT TRUE
#define BENCH_DISPLAY_OPTION_OUTPUT_CONSOLE_DEFAULT FALSE
#define BENCH_DISPLAY_OPTION_CONSOLE_UPDATE_TIME_DEFAULT 5
#define BENCH_DISPLAY_OPTION_INITIATE_KEY "F12"
#define BENCH_DISPLAY_OPTION_INITIATE_MOD CompSuperMask
#define NUM_OPTIONS(s) (sizeof ((s)->opt) / sizeof (CompOption))
static int displayPrivateIndex = 0;
typedef struct _BenchDisplay
{
int screenPrivateIndex;
Bool active;
CompOption opt[BENCH_DISPLAY_OPTION_NUM];
} BenchDisplay;
typedef struct _BenchScreen
{
GLuint dList;
float rrVal;
float fps;
float alpha;
struct timeval initTime;
struct timeval lastRedraw;
float ctime;
float frames;
GLuint numTex[10];
GLuint backTex;
PreparePaintScreenProc preparePaintScreen;
DonePaintScreenProc donePaintScreen;
PaintScreenProc paintScreen;
} BenchScreen;
static void benchPreparePaintScreen(CompScreen * s, int msSinceLastPaint)
{
BENCH_SCREEN(s);
BENCH_DISPLAY(s->display);
float nRrVal;
float ratio = 0.05;
int timediff;
struct timeval now;
gettimeofday(&now, 0);
timediff = TIMEVALDIFF(&now, &bs->lastRedraw);
nRrVal = MIN(1.1, (float)s->optimalRedrawTime / (float)timediff);
bs->rrVal = (bs->rrVal * (1.0 - ratio)) + (nRrVal * ratio);
bs->fps =
(bs->fps * (1.0 - ratio)) +
(1000000.0 / TIMEVALDIFFU(&now, &bs->lastRedraw) * ratio);
bs->lastRedraw = now;
if (bd->opt[BENCH_DISPLAY_OPTION_OUTPUT_CONSOLE].value.b && bd->active)
{
bs->frames++;
bs->ctime += timediff;
if (bs->ctime >
bd->opt[BENCH_DISPLAY_OPTION_CONSOLE_UPDATE_TIME].value.i * 1000)
{
printf("[BENCH] : %.0f frames in %.1f seconds = %.3f FPS\n",
bs->frames, bs->ctime / 1000.0,
bs->frames / (bs->ctime / 1000.0));
bs->frames = 0;
bs->ctime = 0;
}
}
UNWRAP(bs, s, preparePaintScreen);
(*s->preparePaintScreen) (s,
(bs->alpha >
0.0) ? timediff : msSinceLastPaint);
WRAP(bs, s, preparePaintScreen, benchPreparePaintScreen);
if (bd->active)
bs->alpha += timediff / 1000.0;
else
bs->alpha -= timediff / 1000.0;
bs->alpha = MIN(1.0, MAX(0.0, bs->alpha));
}
static void benchDonePaintScreen(CompScreen * s)
{
BENCH_SCREEN(s);
BENCH_DISPLAY(s->display);
if (bs->alpha > 0.0)
{
damageScreen(s);
glFlush();
XSync(s->display->display, FALSE);
if (bd->opt[BENCH_DISPLAY_OPTION_DISABLE_LIMIT].value.b)
{
s->lastRedraw = bs->initTime;
s->timeMult = 0;
}
if (!bd->active)
s->timeMult = 0;
}
UNWRAP(bs, s, donePaintScreen);
(*s->donePaintScreen) (s);
WRAP(bs, s, donePaintScreen, benchDonePaintScreen);
}
static Bool
benchPaintScreen(CompScreen * s, const ScreenPaintAttrib * sa,
Region region, int output, unsigned int mask)
{
Bool status, isSet;
unsigned int fps;
BENCH_SCREEN(s);
BENCH_DISPLAY(s->display);
UNWRAP(bs, s, paintScreen);
status = (*s->paintScreen) (s, sa, region, output, mask);
WRAP(bs, s, paintScreen, benchPaintScreen);
if (bs->alpha <= 0.0
|| !bd->opt[BENCH_DISPLAY_OPTION_OUTPUT_SCREEN].value.b)
return status;
glGetError();
glPushAttrib(GL_COLOR_BUFFER_BIT | GL_TEXTURE_BIT);
GLERR;
glPushMatrix();
glLoadIdentity();
prepareXCoords(s, output, -DEFAULT_Z_CAMERA);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glColor4f(1.0, 1.0, 1.0, bs->alpha);
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
glTranslatef(bd->opt[BENCH_DISPLAY_OPTION_POS_X].value.i,
bd->opt[BENCH_DISPLAY_OPTION_POS_Y].value.i, 0);
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, bs->backTex);
glBegin(GL_QUADS);
glTexCoord2f(0, 0);
glVertex2f(0, 0);
glTexCoord2f(0, 1);
glVertex2f(0, 256);
glTexCoord2f(1, 1);
glVertex2f(512, 256);
glTexCoord2f(1, 0);
glVertex2f(512, 0);
glEnd();
glBindTexture(GL_TEXTURE_2D, 0);
glDisable(GL_TEXTURE_2D);
glTranslatef(53, 83, 0);
float rrVal = MIN(1.0, MAX(0.0, bs->rrVal));
if (rrVal < 0.5)
{
glBegin(GL_QUADS);
glColor4f(1.0, 0.0, 0.0, bs->alpha);
glVertex2f(0.0, 0.0);
glVertex2f(0.0, 25.0);
glColor4f(1.0, rrVal * 2.0, 0.0, bs->alpha);
glVertex2f(330.0 * rrVal, 25.0);
glVertex2f(330.0 * rrVal, 0.0);
glEnd();
}
else
{
glBegin(GL_QUADS);
glColor4f(1.0, 0.0, 0.0, bs->alpha);
glVertex2f(0.0, 0.0);
glVertex2f(0.0, 25.0);
glColor4f(1.0, 1.0, 0.0, bs->alpha);
glVertex2f(165.0, 25.0);
glVertex2f(165.0, 0.0);
glEnd();
glBegin(GL_QUADS);
glColor4f(1.0, 1.0, 0.0, bs->alpha);
glVertex2f(165.0, 0.0);
glVertex2f(165.0, 25.0);
glColor4f(1.0 - ((rrVal - 0.5) * 2.0), 1.0, 0.0, bs->alpha);
glVertex2f(165.0 + 330.0 * (rrVal - 0.5), 25.0);
glVertex2f(165.0 + 330.0 * (rrVal - 0.5), 0.0);
glEnd();
}
glColor4f(0.0, 0.0, 0.0, bs->alpha);
glCallList(bs->dList);
glTranslatef(72, 45, 0);
float red;
if (bs->fps > 30.0)
red = 0.0;
else
red = 1.0;
if (bs->fps <= 30.0 && bs->fps > 20.0)
red = 1.0 - ((bs->fps - 20.0) / 10.0);
glColor4f(red, 0.0, 0.0, bs->alpha);
glEnable(GL_TEXTURE_2D);
isSet = FALSE;
fps = (bs->fps * 100.0);
fps = MIN(999999, fps);
if (fps >= 100000)
{
glBindTexture(GL_TEXTURE_2D, bs->numTex[fps / 100000]);
glCallList(bs->dList + 1);
isSet = TRUE;
}
fps %= 100000;
glTranslatef(12, 0, 0);
if (fps >= 10000 || isSet)
{
glBindTexture(GL_TEXTURE_2D, bs->numTex[fps / 10000]);
glCallList(bs->dList + 1);
isSet = TRUE;
}
fps %= 10000;
glTranslatef(12, 0, 0);
if (fps >= 1000 || isSet)
{
glBindTexture(GL_TEXTURE_2D, bs->numTex[fps / 1000]);
glCallList(bs->dList + 1);
}
fps %= 1000;
glTranslatef(12, 0, 0);
glBindTexture(GL_TEXTURE_2D, bs->numTex[fps / 100]);
glCallList(bs->dList + 1);
fps %= 100;
glTranslatef(19, 0, 0);
glBindTexture(GL_TEXTURE_2D, bs->numTex[fps / 10]);
glCallList(bs->dList + 1);
fps %= 10;
glTranslatef(12, 0, 0);
glBindTexture(GL_TEXTURE_2D, bs->numTex[fps]);
glCallList(bs->dList + 1);
glBindTexture(GL_TEXTURE_2D, 0);
glDisable(GL_TEXTURE_2D);
glPopMatrix();
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
glColor4f(1.0, 1.0, 1.0, 1.0);
glPopAttrib();
glGetError();
return status;
}
static Bool benchInitScreen(CompPlugin * p, CompScreen * s)
{
int i;
BENCH_DISPLAY(s->display);
BenchScreen *bs = (BenchScreen *) calloc(1, sizeof(BenchScreen));
s->privates[bd->screenPrivateIndex].ptr = bs;
WRAP(bs, s, paintScreen, benchPaintScreen);
WRAP(bs, s, preparePaintScreen, benchPreparePaintScreen);
WRAP(bs, s, donePaintScreen, benchDonePaintScreen);
glGenTextures(10, bs->numTex);
glGenTextures(1, &bs->backTex);
glGetError();
glEnable(GL_TEXTURE_2D);
bs->alpha = 0;
bs->ctime = 0;
bs->frames = 0;
for (i = 0; i < 10; i++)
{
//Bind the texture
glBindTexture(GL_TEXTURE_2D, bs->numTex[i]);
//Load the parameters
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, 16, 32, 0,
GL_RGBA, GL_UNSIGNED_BYTE, number_data[i]);
GLERR;
}
glBindTexture(GL_TEXTURE_2D, bs->backTex);
//Load the parameters
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
glTexImage2D(GL_TEXTURE_2D, 0, 4, 512, 256, 0, GL_RGBA,
GL_UNSIGNED_BYTE, image_data);
GLERR;
glBindTexture(GL_TEXTURE_2D, 0);
glDisable(GL_TEXTURE_2D);
bs->dList = glGenLists(2);
glNewList(bs->dList, GL_COMPILE);
glLineWidth(2.0);
glBegin(GL_LINE_LOOP);
glVertex2f(0, 0);
glVertex2f(0, 25);
glVertex2f(330, 25);
glVertex2f(330, 0);
glEnd();
glLineWidth(1.0);
glBegin(GL_LINES);
for (i = 33; i < 330; i += 33)
{
glVertex2f(i, 15);
glVertex2f(i, 25);
}
for (i = 16; i < 330; i += 33)
{
glVertex2f(i, 20);
glVertex2f(i, 25);
}
glEnd();
glEndList();
glNewList(bs->dList + 1, GL_COMPILE);
glBegin(GL_QUADS);
glTexCoord2f(0, 0);
glVertex2f(0, 0);
glTexCoord2f(0, 1);
glVertex2f(0, 32);
glTexCoord2f(1, 1);
glVertex2f(16, 32);
glTexCoord2f(1, 0);
glVertex2f(16, 0);
glEnd();
glEndList();
gettimeofday(&bs->initTime, 0);
gettimeofday(&bs->lastRedraw, 0);
addScreenAction(s, &bd->opt[BENCH_DISPLAY_OPTION_INITIATE].value.action);
return TRUE;
}
static void benchFiniScreen(CompPlugin * p, CompScreen * s)
{
BENCH_SCREEN(s);
BENCH_DISPLAY(s->display);
glDeleteLists(bs->dList, 2);
glDeleteTextures(10, bs->numTex);
glDeleteTextures(1, &bs->backTex);
//Restore the original function
UNWRAP(bs, s, paintScreen);
UNWRAP(bs, s, preparePaintScreen);
UNWRAP(bs, s, donePaintScreen);
removeScreenAction(s,
&bd->opt[BENCH_DISPLAY_OPTION_INITIATE].value.action);
//Free the pointer
free(bs);
}
static Bool
benchSetDisplayOption(CompDisplay * display, char *name,
CompOptionValue * value)
{
CompOption *o;
int index;
BENCH_DISPLAY(display);
o = compFindOption(bd->opt, NUM_OPTIONS(bd), name, &index);
if (!o)
return FALSE;
switch (index)
{
case BENCH_DISPLAY_OPTION_INITIATE:
if (setDisplayAction(display, o, value))
return TRUE;
break;
case BENCH_DISPLAY_OPTION_DISABLE_LIMIT:
case BENCH_DISPLAY_OPTION_OUTPUT_SCREEN:
case BENCH_DISPLAY_OPTION_OUTPUT_CONSOLE:
if (compSetBoolOption(o, value))
return TRUE;
break;
case BENCH_DISPLAY_OPTION_POS_X:
case BENCH_DISPLAY_OPTION_POS_Y:
case BENCH_DISPLAY_OPTION_CONSOLE_UPDATE_TIME:
if (compSetIntOption(o, value))
return TRUE;
break;
default:
break;
}
return FALSE;
}
static Bool
benchInitiate(CompDisplay * d, CompAction * ac, CompActionState state,
CompOption * option, int nOption)
{
CompScreen *s;
BENCH_DISPLAY(d);
bd->active = !bd->active;
bd->active &= bd->opt[BENCH_DISPLAY_OPTION_OUTPUT_SCREEN].value.b
|| bd->opt[BENCH_DISPLAY_OPTION_OUTPUT_CONSOLE].value.b;
s = findScreenAtDisplay(d, getIntOptionNamed(option, nOption, "root", 0));
if (s)
{
BENCH_SCREEN(s);
damageScreen(s);
bs->ctime = 0;
bs->frames = 0;
}
return FALSE;
}
static void benchDisplayInitOptions(BenchDisplay * bd)
{
CompOption *o;
o = &bd->opt[BENCH_DISPLAY_OPTION_INITIATE];
o->advanced = False;
o->name = "initiate";
o->group = N_("Main");
o->subGroup = N_("");
o->displayHints = "";
o->shortDesc = N_("Initiate");
o->longDesc = N_("Start benchmark");
o->type = CompOptionTypeAction;
o->value.action.initiate = benchInitiate;
o->value.action.terminate = 0;
o->value.action.bell = FALSE;
o->value.action.edgeMask = 0;
o->value.action.type = CompBindingTypeKey;
o->value.action.state = CompActionStateInitKey;
o->value.action.key.modifiers = BENCH_DISPLAY_OPTION_INITIATE_MOD;
o->value.action.key.keysym =
XStringToKeysym(BENCH_DISPLAY_OPTION_INITIATE_KEY);
o = &bd->opt[BENCH_DISPLAY_OPTION_OUTPUT_SCREEN];
o->advanced = False;
o->name = "output_screen";
o->group = N_("Main");
o->subGroup = N_("Screen Output");
o->displayHints = "";
o->shortDesc = N_("Enable");
o->longDesc = N_("Display FPS on screen");
o->type = CompOptionTypeBool;
o->value.b = BENCH_DISPLAY_OPTION_OUTPUT_SCREEN_DEFAULT;
o = &bd->opt[BENCH_DISPLAY_OPTION_OUTPUT_CONSOLE];
o->advanced = False;
o->name = "output_console";
o->group = N_("Main");
o->subGroup = N_("Console Output");
o->displayHints = "";
o->shortDesc = N_("Enable");
o->longDesc = N_("Print FPS to console");
o->type = CompOptionTypeBool;
o->value.b = BENCH_DISPLAY_OPTION_OUTPUT_CONSOLE_DEFAULT;
o = &bd->opt[BENCH_DISPLAY_OPTION_CONSOLE_UPDATE_TIME];
o->advanced = False;
o->name = "console_update_time";
o->group = N_("Main");
o->subGroup = N_("Console Output");
o->displayHints = "";
o->shortDesc = N_("Update time");
o->longDesc = N_("Console output update time");
o->type = CompOptionTypeInt;
o->value.i = BENCH_DISPLAY_OPTION_CONSOLE_UPDATE_TIME_DEFAULT;
o->rest.i.min = 1;
o->rest.i.max = 60;
o = &bd->opt[BENCH_DISPLAY_OPTION_DISABLE_LIMIT];
o->advanced = False;
o->name = "disable_limiter";
o->group = N_("Main");
o->subGroup = N_("");
o->displayHints = "";
o->shortDesc = N_("Disable limiter");
o->longDesc = N_("Disable beryl integrated FPS limiter");
o->type = CompOptionTypeBool;
o->value.b = BENCH_DISPLAY_OPTION_DISABLE_LIMIT_DEFAULT;
o = &bd->opt[BENCH_DISPLAY_OPTION_POS_X];
o->advanced = False;
o->name = "position_x";
o->group = N_("Main");
o->subGroup = N_("Screen Output");
o->displayHints = "";
o->shortDesc = N_("X position");
o->longDesc = N_("X Position of benchmark window");
o->type = CompOptionTypeInt;
o->value.i = BENCH_DISPLAY_OPTION_POS_X_DEFAULT;
o->rest.i.min = 0;
o->rest.i.max = 4096;
o = &bd->opt[BENCH_DISPLAY_OPTION_POS_Y];
o->advanced = False;
o->name = "position_y";
o->group = N_("Main");
o->subGroup = N_("Screen Output");
o->displayHints = "";
o->shortDesc = N_("Y position");
o->longDesc = N_("Y Position of benchmark window");
o->type = CompOptionTypeInt;
o->value.i = BENCH_DISPLAY_OPTION_POS_X_DEFAULT;
o->rest.i.min = 0;
o->rest.i.max = 4096;
}
static CompOption *benchGetDisplayOptions(CompDisplay * display, int *count)
{
if (display)
{
BENCH_DISPLAY(display);
*count = NUM_OPTIONS(bd);
return bd->opt;
}
else
{
BenchDisplay *bd = malloc(sizeof(BenchDisplay));
benchDisplayInitOptions(bd);
*count = NUM_OPTIONS(bd);
return bd->opt;
}
}
static Bool benchInitDisplay(CompPlugin * p, CompDisplay * d)
{
//Generate a bench display
BenchDisplay *bd = (BenchDisplay *) malloc(sizeof(BenchDisplay));
//Allocate a private index
bd->screenPrivateIndex = allocateScreenPrivateIndex(d);
//Check if its valid
if (bd->screenPrivateIndex < 0)
{
//Its invalid so free memory and return
free(bd);
return FALSE;
}
benchDisplayInitOptions(bd);
bd->active = FALSE;
//Record the display
d->privates[displayPrivateIndex].ptr = bd;
return TRUE;
}
static void benchFiniDisplay(CompPlugin * p, CompDisplay * d)
{
BENCH_DISPLAY(d);
//Free the private index
freeScreenPrivateIndex(d, bd->screenPrivateIndex);
//Free the pointer
free(bd);
}
static Bool benchInit(CompPlugin * p)
{
displayPrivateIndex = allocateDisplayPrivateIndex();
if (displayPrivateIndex < 0)
return FALSE;
return TRUE;
}
static void benchFini(CompPlugin * p)
{
if (displayPrivateIndex >= 0)
freeDisplayPrivateIndex(displayPrivateIndex);
}
CompPluginVTable benchVTable = {
"bench",
N_("Benchmark"),
N_("A simple benchmark plugin"),
benchInit,
benchFini,
benchInitDisplay,
benchFiniDisplay,
benchInitScreen,
benchFiniScreen,
0,
0,
benchGetDisplayOptions,
benchSetDisplayOption,
0,
0,
0,
0,
0,
0,
BERYL_ABI_INFO,
"beryl-plugins",
"misc",
0,
0,
False,
};
CompPluginVTable *getCompPluginInfo(void)
{
return &benchVTable;
}
syntax highlighted by Code2HTML, v. 0.9.1