/// LSU EE 7700-1 (Sp 2009), Graphics Processors -*- c++ -*-
//
/// CPU-Only Demos' Include File
// $Id:$
/// Purpose
// Frame buffer simulation class and support functions.
#ifndef FRAME_BUFFER_H
#define FRAME_BUFFER_H
#define GL_GLEXT_PROTOTYPES
#include <GL/gl.h>
#include <GL/glext.h>
#include <GL/freeglut.h>
#include <stdarg.h>
#include <deque>
#include <string>
#ifdef MAGICK
#include <Magick++.h>
#endif
// Rename keys so a single namespace can be used for regular (ASCII)
// keys and "special" ones.
#define FB_KEY_F1 ( GLUT_KEY_F1 + 0x100 )
#define FB_KEY_F2 ( GLUT_KEY_F2 + 0x100 )
#define FB_KEY_F3 ( GLUT_KEY_F3 + 0x100 )
#define FB_KEY_F4 ( GLUT_KEY_F4 + 0x100 )
#define FB_KEY_F5 ( GLUT_KEY_F5 + 0x100 )
#define FB_KEY_F6 ( GLUT_KEY_F6 + 0x100 )
#define FB_KEY_F7 ( GLUT_KEY_F7 + 0x100 )
#define FB_KEY_F8 ( GLUT_KEY_F8 + 0x100 )
#define FB_KEY_F9 ( GLUT_KEY_F9 + 0x100 )
#define FB_KEY_F10 ( GLUT_KEY_F10 + 0x100 )
#define FB_KEY_F11 ( GLUT_KEY_F11 + 0x100 )
#define FB_KEY_F12 ( GLUT_KEY_F12 + 0x100 )
#define FB_KEY_LEFT ( GLUT_KEY_LEFT + 0x100 )
#define FB_KEY_UP ( GLUT_KEY_UP + 0x100 )
#define FB_KEY_RIGHT ( GLUT_KEY_RIGHT + 0x100 )
#define FB_KEY_DOWN ( GLUT_KEY_DOWN + 0x100 )
#define FB_KEY_PAGE_UP ( GLUT_KEY_PAGE_UP + 0x100 )
#define FB_KEY_PAGE_DOWN ( GLUT_KEY_PAGE_DOWN + 0x100 )
#define FB_KEY_HOME ( GLUT_KEY_HOME + 0x100 )
#define FB_KEY_END ( GLUT_KEY_END + 0x100 )
#define FB_KEY_INSERT ( GLUT_KEY_INSERT + 0x100 )
#define FB_KEY_DELETE 127
typedef int int32_t;
class pFrame_Buffer;
pFrame_Buffer* frame_buffer_self_ = NULL;
// Print OpenGL string attribute corresponding to token.
//
#define FB_GL_PRINT_STRING(token) \
{ const char* const str = (char*) glGetString(token); \
printf("S %s - ",#token); \
if ( str ) printf("\"%s\"\n",str); else printf("not available.\n"); \
}
// Return number of seconds since 1970 UTC.
//
double
time_wall_fp()
{
struct timespec now;
clock_gettime(CLOCK_REALTIME,&now);
return now.tv_sec + ((double)now.tv_nsec) * 1e-9;
}
// Basic Frame Buffer Simulation
//
class pFrame_Buffer {
public:
pFrame_Buffer(int& argc, char** argv)
{
frame_buffer_self_ = this;
width = height = 0;
buffer = (int32_t*) malloc(0);
cb_keyboard();
init_gl(argc, argv);
}
~pFrame_Buffer(){ delete buffer; }
// Use DISPLAY_FUNC to write frame buffer.
//
void show(void (*display_func)(pFrame_Buffer& fb))
{
user_display_func = display_func;
glutDisplayFunc(&cb_display_w);
glutKeyboardFunc(&cb_keyboard_w);
glutSpecialFunc(&cb_keyboard_special_w);
glutMainLoop();
}
// Return width and height of frame buffer.
//
int get_width() { return width; }
int get_height() { return height; }
// Key pressed by user since last call of DISPLAY_FUNC.
// ASCII value, one of the FB_KEY_XXXX macros below, or 0 if
// no key pressed.
//
int keyboard_key;
int keyboard_x, keyboard_y; // Mouse location when key pressed.
// Return address of frame buffer.
//
int32_t* get_buffer() { return buffer; }
// Print text in frame buffer, starting at upper left.
// Arguments same as printf.
//
void fbprintf(const char* fmt,...)
{
va_list ap;
va_start(ap,fmt);
const int size = vsnprintf(NULL,0,fmt,ap);
va_end(ap);
char* const buffer = (char*) malloc(size+1);
va_start(ap,fmt);
vsprintf(buffer,fmt,ap);
va_end(ap);
print_list.push_back(buffer);
}
// Tell frame buffer object that this is when DISPLAY_FUNC will
// start rendering computations. Should be placed where application-related
// code ends and rendering pipeline code starts.
//
void render_timing_start()
{
render_start = time_wall_fp();
}
private:
void init_gl(int& argc, char** argv)
{
exe_file_name = argv && argv[0] ? argv[0] : "unknown name";
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE );
glutInitWindowSize(640,480+status_height);
std::string title("Frame Buffer Simulator - " + exe_file_name);
glut_window_id = glutCreateWindow(title.c_str());
}
static void cb_display_w(void){ frame_buffer_self_->cb_display(); }
void cb_display(void)
{
if ( keyboard_key == FB_KEY_F12 ) write_img();
frame_buffer_allocate();
reset(0xff000000);
glClearColor(0.5,0.5,0.5,1);
glClear(GL_COLOR_BUFFER_BIT);
const double frame_start = time_wall_fp();
render_timing_start(); // User may call this in display func.
user_display_func(*this);
const double end_time = time_wall_fp();
const double elapsed_time = end_time - frame_start;
const double render_elapsed_time = end_time - render_start;
glWindowPos2i(10,height+status_height-20);
fbprintf
("Size %dx%d, Render Time %.3f ms, Frame Time %.3f ms, "
"Potential Frame Rate %.1f\n",
width, height,
render_elapsed_time * 1000, elapsed_time * 1000, 1.0 / elapsed_time);
char* const str = print_list.back();
glutBitmapString(GLUT_BITMAP_HELVETICA_12,(unsigned char*)str);
free(str);
print_list.pop_back();
glWindowPos2i(0,0);
glPixelZoom(1,1);
glDrawPixels(width, height, GL_RGBA, GL_UNSIGNED_BYTE, buffer);
glWindowPos2i(10,height-20);
for ( ; !print_list.empty(); print_list.pop_front() )
{
char* const str = print_list.front();
glutBitmapString(GLUT_BITMAP_HELVETICA_12,(unsigned char*)str);
free(str);
}
cb_keyboard();
glutSwapBuffers();
}
void frame_buffer_allocate()
{
const int width_new = glutGet(GLUT_WINDOW_WIDTH);
const int height_new = glutGet(GLUT_WINDOW_HEIGHT) - status_height;
if ( height_new < 1 ) return;
if ( width_new == width && height_new == height ) return;
const int new_amt = width_new * height_new * sizeof(*buffer) ;
buffer = (int32_t*) realloc(buffer,new_amt);
width = width_new;
height = height_new;
}
void reset(int32_t color)
{
const int size = width * height;
for ( int i=0; i<size; i++ ) buffer[i] = color;
}
static void cb_keyboard_w(unsigned char key, int x, int y)
{frame_buffer_self_->cb_keyboard(key,x,y);}
static void cb_keyboard_special_w(int key, int x, int y)
{frame_buffer_self_->cb_keyboard(key+0x100,x,y);}
void cb_keyboard(int key=0, int x=0, int y=0)
{
keyboard_key = key;
keyboard_x = x;
keyboard_y = y;
if ( !key ) return;
glutPostRedisplay();
}
void write_img()
{
if ( !buffer ) return;
#ifndef MAGICK
fbprintf("Cannot write image without ImageMagick library.\n");
#else
std::string image_file_name(exe_file_name + ".png");
const int width_raw = glutGet(GLUT_WINDOW_WIDTH);
const int width = width_raw & ~0x3; // Don't want to deal with padding.
const int height = glutGet(GLUT_WINDOW_HEIGHT);
glReadBuffer(GL_FRONT);
const int size = width_raw * height; // Note: width might work too.
char* const pbuffer = (char*) malloc(size * 3);
glReadPixels(0,0,width,height,GL_RGB,GL_UNSIGNED_BYTE,pbuffer);
Magick::Image image(width,height,"RGB",Magick::CharPixel,pbuffer);
image.flip();
image.write(image_file_name);
free(pbuffer);
fbprintf("*** Wrote screenshot to %s ***\n",image_file_name.c_str());
#endif
}
private:
static const int status_height = 26;
std::string exe_file_name;
double render_start;
int width;
int height; // Height of simulated frame buffer, not displayed window.
int32_t *buffer;
int glut_window_id;
void (*user_display_func)(pFrame_Buffer& frame_buffer);
std::deque<char*> print_list;
};
#endif