/* Affichage des informations relatives         */
/* a la librairie OpenGL utilisee               */
/*                                              */
/* Auteur: Nicolas JANEY                        */
/* nicolas.janey@univ-fcomte.fr                 */
/* Septembre 2009                               */

#include <stdio.h>
#include <conio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>

#include <GL/glut.h>
#include <GL/gl.h>
#include <GL/glu.h>

typedef struct
{
  char *name;
  char *unit;
  void(*init) (void);
  int(*run) (int, int);
  int type;
  int numsize;
  int size[10];
}
benchmark;

static int frontbuffer = 1;

static void init_test01(void)
{
  glMatrixMode(GL_PROJECTION);
  glLoadIdentity();
  gluOrtho2D(-0.5, 639.5, -0.5, 479.5);
  glMatrixMode(GL_MODELVIEW);
  glShadeModel(GL_FLAT);
  glDisable(GL_DEPTH_TEST);
  glClearColor(0.0, 0.1, 1.0, 0.0);
  glClear(GL_COLOR_BUFFER_BIT);
  glColor3f(1.0, 0.0, 0.0);
}

static int
test01(int size, int num)
{
  int x, y;

  glBegin(GL_POINTS);
  for (y = 0; y < num; y++)
    for (x = 0; x < 480; x++)
      glVertex2i(x, x);
  glEnd();

  return 480 * num;
}

/***************************************************************************/

static void
init_test02(void)
{
  glMatrixMode(GL_PROJECTION);
  glLoadIdentity();
  gluOrtho2D(-0.5, 639.5, -0.5, 479.5);
  glMatrixMode(GL_MODELVIEW);

  glShadeModel(GL_SMOOTH);
  glDisable(GL_DEPTH_TEST);

  glClearColor(0.0, 0.1, 1.0, 0.0);
  glClear(GL_COLOR_BUFFER_BIT);
}

static int
test02(int size, int num)
{
  int x, y;

  glBegin(GL_LINES);
  for (y = 0; y < num; y++)
    for (x = 0; x < size; x++) {
      glColor3f(0.0, 1.0, y / (float)num);
      glVertex2i(0, size - 1);
      glColor3f(1.0, 0.0, x / (float)size);
      glVertex2i(x, x);
    }
  glEnd();

  return num * size;
}

/***************************************************************************/

static void
init_test03(void)
{
  glMatrixMode(GL_PROJECTION);
  glLoadIdentity();
  glOrtho(-0.5, 639.5, -0.5, 479.5, 1.0, -1000.0 * 480.0);
  glMatrixMode(GL_MODELVIEW);

  glShadeModel(GL_SMOOTH);
  glEnable(GL_DEPTH_TEST);

  glClearColor(0.0, 0.1, 1.0, 0.0);
  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
}

static int
test03(int size, int num)
{
  int x, y, z;

  glBegin(GL_TRIANGLES);
  for (y = 0; y < num; y++)
    for (x = 0; x < size; x += 5) {
      z = num * size - (y * size + x);
      glColor3f(0.0, 1.0, 0.0);
      glVertex3i(0, x, z);

      glColor3f(1.0, 0.0, x / (float)size);
      glVertex3i(size - 1 - x, 0, z);

      glColor3f(1.0, x / (float)size, 0.0);
      glVertex3i(x, size - 1 - x, z);
    }
  glEnd();

  return size * num / 5;
}

/***************************************************************************/

static void
init_test04(void)
{
  int x, y;
  GLubyte tex[128 * 128 * 3];
  GLenum gluerr;

  glMatrixMode(GL_PROJECTION);
  glLoadIdentity();
  glOrtho(-0.5, 639.5, -0.5, 479.5, 1.0, -1000.0 * 480.0);

  glMatrixMode(GL_MODELVIEW);

  glShadeModel(GL_SMOOTH);
  glEnable(GL_DEPTH_TEST);

  glEnable(GL_BLEND);
  glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

  for (y = 0; y < 128; y++)
    for (x = 0; x < 128; x++) {
      tex[(x + y * 128) * 3 + 0] = ((x % (128 / 4)) < (128 / 8)) ? 255 : 0;
      tex[(x + y * 128) * 3 + 1] = ((y % (128 / 4)) < (128 / 8)) ? 255 : 0;
      tex[(x + y * 128) * 3 + 2] = x;
    }

  glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
  if ((gluerr = gluBuild2DMipmaps(GL_TEXTURE_2D, 3, 128, 128, GL_RGB,
    GL_UNSIGNED_BYTE, (GLvoid *)(&tex[0])))) {
    fprintf(stderr, "GLULib%s\n", (char *)gluErrorString(gluerr));
    exit(-1);
  }

  glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
  glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);

  glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
    GL_LINEAR_MIPMAP_NEAREST);
  glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

  glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
  glEnable(GL_TEXTURE_2D);

  glClearColor(0.0, 0.1, 1.0, 0.0);
  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
}

static int
test04(int size, int num)
{
  int x, y, z;

  glBegin(GL_TRIANGLES);
  for (y = 0; y < num; y++)
    for (x = 0; x < size; x += 5) {
      z = num * size - (y * size + x);
      glTexCoord2f(1.0, 1.0);
      glColor3f(1.0, 0.0, 0.0);
      glVertex3i(0, x, z);

      glTexCoord2f(0.0, 1.0);
      glColor3f(0.0, 1.0, 0.0);
      glVertex3i(size - 1 - x, 0, z);

      glTexCoord2f(1.0, 0.0);
      glColor3f(0.0, 0.0, 1.0);
      glVertex3i(x, size - 1 - x, z);
    }
  glEnd();

  return num * size / 5;
}

/***************************************************************************/

static void
init_test05(void)
{
  int x, y;
  GLubyte tex[128 * 128 * 3];
  GLenum gluerr;

  glMatrixMode(GL_PROJECTION);
  glLoadIdentity();
  glOrtho(-0.5, 639.5, -0.5, 479.5, -1.0, 1.0);

  glMatrixMode(GL_MODELVIEW);

  glShadeModel(GL_SMOOTH);
  glEnable(GL_DEPTH_TEST);

  glEnable(GL_BLEND);
  glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

  for (y = 0; y < 128; y++)
    for (x = 0; x < 128; x++) {
      tex[(x + y * 128) * 3 + 0] = ((x % (128 / 4)) < (128 / 8)) ? 255 : 0;
      tex[(x + y * 128) * 3 + 1] = ((y % (128 / 4)) < (128 / 8)) ? 255 : 0;
      tex[(x + y * 128) * 3 + 2] = x;
    }

  glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
  if ((gluerr = gluBuild2DMipmaps(GL_TEXTURE_2D, 3, 128, 128, GL_RGB,
    GL_UNSIGNED_BYTE, (GLvoid *)(&tex[0])))) {
    fprintf(stderr, "GLULib%s\n", (char *)gluErrorString(gluerr));
    exit(-1);
  }

  glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
  glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);

  glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
    GL_LINEAR_MIPMAP_NEAREST);
  glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

  glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
  glEnable(GL_TEXTURE_2D);

  glDepthFunc(GL_ALWAYS);

  glClearColor(0.0, 0.1, 1.0, 0.0);
  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
}

static int
test05(int size, int num)
{
  int y;
  float v0[3], v1[3], v2[3], v3[3];
  float cv0[3], cv1[3], cv2[3], cv3[3];
  float tv0[3], tv1[3], tv2[3], tv3[3];

  v0[0] = 320 - size / 2;
  v0[1] = 240 - size / 2;
  v0[2] = 0.0;
  v1[0] = 320 + size / 2;
  v1[1] = 240 - size / 2;
  v1[2] = 0.0;
  v2[0] = 320 - size / 2;
  v2[1] = 240 + size / 2;
  v2[2] = 0.0;
  v3[0] = 320 + size / 2;
  v3[1] = 240 + size / 2;
  v3[2] = 0.0;
  cv0[0] = 1.0;
  cv0[1] = 0.0;
  cv0[2] = 0.0;
  cv1[0] = 1.0;
  cv1[1] = 1.0;
  cv1[2] = 0.0;
  cv2[0] = 1.0;
  cv2[1] = 0.0;
  cv2[2] = 1.0;
  cv3[0] = 1.0;
  cv3[1] = 1.0;
  cv3[2] = 1.0;
  tv0[0] = 0.0;
  tv0[1] = 0.0;
  tv0[2] = 0.0;
  tv1[0] = 1.0;
  tv1[1] = 0.0;
  tv1[2] = 0.0;
  tv2[0] = 0.0;
  tv2[1] = 1.0;
  tv2[2] = 0.0;
  tv3[0] = 1.0;
  tv3[1] = 1.0;
  tv3[2] = 0.0;

  glBegin(GL_TRIANGLE_STRIP);
  for (y = 0; y < num; y++) {
    glColor3fv(cv0);
    glTexCoord2fv(tv0);
    glVertex3fv(v0);

    glColor3fv(cv1);
    glTexCoord2fv(tv1);
    glVertex3fv(v1);

    glColor3fv(cv2);
    glTexCoord2fv(tv2);
    glVertex3fv(v2);

    glColor3fv(cv3);
    glTexCoord2fv(tv3);
    glVertex3fv(v3);
  }
  glEnd();

  return 4 * num - 2;
}

/***************************************************************************/

static void
init_test06(void)
{
  glMatrixMode(GL_PROJECTION);
  glLoadIdentity();
  gluOrtho2D(-0.5, 639.5, -0.5, 479.5);
  glMatrixMode(GL_MODELVIEW);

  glShadeModel(GL_SMOOTH);
  glEnable(GL_DEPTH_TEST);

  glClearColor(0.0, 0.1, 1.0, 0.0);
  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
}

static int
test06(int size, int num)
{
  int y;

  for (y = 0; y < num; y++) {
    glClearColor(y / (float)num, 0.1, 1.0, 0.0);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
  }

  return num;
}

/***************************************************************************/

#define BMARKS_TIME 5.0

#define NUM_BMARKS 6

/* 554 ~= sqrt(640*480) */

static benchmark bmarks[NUM_BMARKS] = {
   {"Simple Points", "Pnts", init_test01, test01, 0, 0,
    {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}},
   {"Smooth Lines", "Lins", init_test02, test02, 1, 5,
    {480, 250, 100, 50, 25, 0, 0, 0, 0, 0}},
   {"ZSmooth Triangles", "Tris", init_test03, test03, 1, 5,
    {480, 250, 100, 50, 25, 0, 0, 0, 0, 0}},
   {"ZSmooth Tex Blend Triangles", "Tris", init_test04, test04, 1, 5,
    {480, 250, 100, 50, 25, 0, 0, 0, 0, 0}},
   {"ZSmooth Tex Blend TMesh Triangles", "Tris", init_test05, test05, 2, 8,
    {400, 250, 100, 50, 25, 10, 5, 2, 0, 0}},
   {"Color/Depth Buffer Clears", "Clrs", init_test06, test06, 3, 0,
    {554, 0, 0, 0, 0, 0, 0, 0, 0, 0}}
};

/***************************************************************************/

static void
dotest0param(benchmark * bmark)
{
  float stime, etime, dtime, tottime, maxtime, mintime;
  int num, numelem, calibnum, j;

  glPushAttrib(GL_ALL_ATTRIB_BITS);
  bmark->init();

  stime = glutGet(GLUT_ELAPSED_TIME);

  dtime = 0.0;
  calibnum = 0;
  while (dtime < 10.0) {
    bmark->run(0, 1);
    glFinish();
    etime = glutGet(GLUT_ELAPSED_TIME);
    dtime = (etime - stime) / 1000.0;
    calibnum++;
  }
  glPopAttrib();

  fprintf(stderr, "Elapsed time for the calibration test (%d): %f\n",
    calibnum, dtime);

  num = (int)((BMARKS_TIME / dtime) * calibnum);

  if (num < 1)
    num = 1;

  fprintf(stderr, "Selected number of benchmark iterations: %d\n", num);

  mintime = HUGE_VAL;
  maxtime = -HUGE_VAL;

  for (tottime = 0.0, j = 0; j < 5; j++) {
    glPushAttrib(GL_ALL_ATTRIB_BITS);
    bmark->init();

    stime = glutGet(GLUT_ELAPSED_TIME);
    numelem = bmark->run(0, num);
    glFinish();
    etime = glutGet(GLUT_ELAPSED_TIME);

    glPopAttrib();

    dtime = etime - stime;
    tottime += dtime;

    fprintf(stderr, "Elapsed time for run %d: %f\n", j, dtime);

    if (dtime < mintime)
      mintime = dtime;
    if (dtime > maxtime)
      maxtime = dtime;
  }

  tottime -= mintime + maxtime;

  fprintf(stdout, "%s\n%f %s/sec", bmark->name, numelem / (tottime / 3.0),
    bmark->unit);

  if (bmark->type == 3)
    fprintf(stdout, ", MPixel Fill/sec: %f\n\n",
    (numelem * bmark->size[0] * (float)bmark->size[0]) /
      (1000000.0 * tottime / 3.0));
  else
    fprintf(stdout, "\n\n");
}

/***************************************************************************/

static void
dotest1param(benchmark * bmark)
{
  float stime, etime, dtime, tottime, maxtime, mintime;
  int num, numelem, calibnum, j, k;

  fprintf(stdout, "%s\n", bmark->name);

  for (j = 0; j < bmark->numsize; j++) {
    fprintf(stderr, "Current size: %d\n", bmark->size[j]);

    glPushAttrib(GL_ALL_ATTRIB_BITS);
    bmark->init();

    stime = glutGet(GLUT_ELAPSED_TIME);

    dtime = 0.0;
    calibnum = 0;
    while (dtime < 2.0) {
      bmark->run(bmark->size[j], 1);
      glFinish();
      etime = glutGet(GLUT_ELAPSED_TIME);
      dtime = (etime - stime) / 1000.0;
      calibnum++;
    }
    glPopAttrib();

    fprintf(stderr, "Elapsed time for the calibration test (%d): %f\n",
      calibnum, dtime);

    num = (int)((BMARKS_TIME / dtime) * calibnum);

    if (num < 1)
      num = 1;

    fprintf(stderr, "Selected number of benchmark iterations: %d\n", num);

    mintime = HUGE_VAL;
    maxtime = -HUGE_VAL;

    for (numelem = 1, tottime = 0.0, k = 0; k < 5; k++) {
      glPushAttrib(GL_ALL_ATTRIB_BITS);
      bmark->init();

      stime = glutGet(GLUT_ELAPSED_TIME);
      numelem = bmark->run(bmark->size[j], num);
      glFinish();
      etime = glutGet(GLUT_ELAPSED_TIME);

      glPopAttrib();

      dtime = (etime - stime) / 1000.0;
      tottime += dtime;

      fprintf(stderr, "Elapsed time for run %d: %f\n", k, dtime);

      if (dtime < mintime)
        mintime = dtime;
      if (dtime > maxtime)
        maxtime = dtime;
    }

    tottime -= mintime + maxtime;

    fprintf(stdout, "SIZE=%03d => %f %s/sec", bmark->size[j],
      numelem / (tottime / 3.0), bmark->unit);
    if (bmark->type == 2)
      fprintf(stdout, ", MPixel Fill/sec: %f\n",
      (numelem * bmark->size[j] * bmark->size[j] / 2) /
        (1000000.0 * tottime / 3.0));
    else
      fprintf(stdout, "\n");
  }

  fprintf(stdout, "\n\n");
}

/***************************************************************************/

static void
display(void)
{
  int i;

  if (frontbuffer)
    glDrawBuffer(GL_FRONT);
  else
    glDrawBuffer(GL_BACK);

  for (i = 0; i < NUM_BMARKS; i++) {
    fprintf(stderr, "Benchmark: %d\n", i);

    switch (bmarks[i].type) {
    case 0:
    case 3:
      dotest0param(&bmarks[i]);
      break;
    case 1:
    case 2:
      dotest1param(&bmarks[i]);
      break;
    }
  }

  exit(0);
}
int main(int ac, char **av) {
  glutInitWindowSize(640, 480);
  glutInit(&ac, av);
  glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);
  glutCreateWindow("OpenGL/Mesa Performances");
  printf("GL_VENDOR     = %s\n", (const char *)glGetString(GL_VENDOR));
  printf("GL_VERSION    = %s\n", (const char *)glGetString(GL_VERSION));
  printf("GL_RENDERER   = %s\n", (const char *)glGetString(GL_RENDERER));
  printf("GL_EXTENSIONS = %s\n", (const char *)glGetString(GL_EXTENSIONS));
  _getch();

#ifdef GL_VERSION_2_0
  {
    const GLubyte *v = glGetString(GL_VERSION);
    if (v[0] * 10 + v[2] >= 20) {
      const GLubyte *slv = glGetString(GL_SHADING_LANGUAGE_VERSION);
      printf("GL_SHADING_LANGUAGE_VERSION = %s\n", slv);
    }
  }
#endif

  printf("GLU_VERSION: %s\n", (char *)gluGetString(GLU_VERSION));
  printf("GLU_EXTENSIONS: %s\n", (char *)gluGetString(GLU_EXTENSIONS));
  printf("GLUT_API_VERSION: %d\n", GLUT_API_VERSION);
#ifdef GLUT_XLIB_IMPLEMENTATION
  printf("GLUT_XLIB_IMPLEMENTATION: %d\n", GLUT_XLIB_IMPLEMENTATION);
#endif

  _getch();

  fprintf(stderr, "GLTest v1.0\nWritten by David Bucciarelli\n");

  if (ac == 2)
    frontbuffer = 0;

  glutDisplayFunc(display);
  glutMainLoop();

  return(0);
}