Recently, I was on Stack Overflow and I found an interesting question regarding sprites in the programmable pipeline. I was curious, and did some research. I'm not sure that "sprites" is exactly the right terminology for this, but I believe that this is as close to "sprites" as the programmable pipeline gets. Anyway, I wrote a little test program (for GLX and OpenGL 3.2 and beyond). Hope you find it interesting!
Compile with:
clang++ -DGL_GLEXT_PROTOTYPES -DGLX_GLXEXT_PROTOTYPES -Wall -O0 -ggdb -o main $(pkg-config --libs --cflags gl x11) main.cc
#include <stdlib.h>
#include <stdio.h>
#include <assert.h>
#include <string.h>
#include <time.h>
#include <inttypes.h>
#include <GL/gl.h>
#include <GL/glext.h>
#include <GL/glx.h>
#include <GL/glxext.h>
#include <X11/Xlib.h>
static int done = 0;
static inline uint64_t monotonicTime() {
struct timespec ts;
int res = clock_gettime(CLOCK_MONOTONIC, &ts);
assert(!res);
uint64_t b = ts.tv_sec * 1000000;
return b + ts.tv_nsec / 1000;
}
static int handler(Display *display) {
done = 1;
return 0;
}
class OpenGLStuff {
public:
OpenGLStuff(const int width, const int height) : point_count(30), circle_count(10) {
// Print some debug stuff
printf("%s\n", glGetString(GL_VENDOR));
printf("%s\n", glGetString(GL_RENDERER));
printf("%s\n", glGetString(GL_VERSION));
printf("%s\n", glGetString(GL_SHADING_LANGUAGE_VERSION));
// Set up some boring first-run stuff
glViewport(0, 0, width, height);
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glEnable(GL_PROGRAM_POINT_SIZE);
// I'm actually interested in the maximum sized sprite I can draw is
float range[2];
glGetFloatv(GL_POINT_SIZE_RANGE, range);
printf("Point size range: %f x %f\n", range[0], range[1]);
const GLchar *vertex_shader_source[] = {
//"#version 430\n",
"#version 150\n",
"\n",
"in vec4 in_position;\n",
"uniform int in_point_count;\n",
"uniform int in_circle_count;\n",
"uniform int in_time;\n",
"\n",
"void main() {\n",
" float p = float(gl_InstanceID) / float(in_point_count);\n",
" float pi = asin(1.0) * 2.0;\n",
" float speed = (1.0 / 2.0) * 1000000.0 * 2.0 * pi;\n",
" float radius = 0.1*sin(float(in_time)/(speed/10)) + (float(gl_InstanceID/in_point_count)/float(in_circle_count)) * sin(float(in_time)/speed);\n",
" gl_Position = in_position + vec4(radius*sin(2*pi*p), radius*cos(2*pi*p), 0.0, 0.0);\n",
" gl_PointSize = (sin(radius*2.0*pi + float(in_time)/(speed*4.0))+1.0)/2.0*63.0*20.0;\n",
"}\n",
""
};
const GLchar *fragment_shader_source[] = {
//"#version 430\n",
"#version 150\n",
"\n",
"out vec4 out_color;\n",
"\n",
"void main() {\n",
" vec2 c = (gl_PointCoord - vec2(0.5, 0.5)) * 2;\n",
" float v = c.x*c.x + c.y*c.y;\n",
" if (v < 1.0) {\n",
" out_color = (1.0-v*v)*vec4(0.0, gl_PointCoord.y, gl_PointCoord.x, 1.0);\n",
" } else {\n",
" out_color = vec4(0.0, 0.0, 0.0, 0.0);\n",
" }\n",
"}\n",
""
};
// Set up shader stuff
GLint compile_status;
GLuint vertex_shader = glCreateShader(GL_VERTEX_SHADER);
GLuint fragment_shader = glCreateShader(GL_FRAGMENT_SHADER);
program = glCreateProgram();
glShaderSource(vertex_shader, sizeof(vertex_shader_source)/sizeof(GLchar*), vertex_shader_source, NULL);
glShaderSource(fragment_shader, sizeof(fragment_shader_source)/sizeof(GLchar*), fragment_shader_source, NULL);
glCompileShader(vertex_shader);
glCompileShader(fragment_shader);
glGetShaderiv(vertex_shader, GL_COMPILE_STATUS, &compile_status);
assert(compile_status == GL_TRUE);
glGetShaderiv(fragment_shader, GL_COMPILE_STATUS, &compile_status);
assert(compile_status == GL_TRUE);
glAttachShader(program, vertex_shader);
glAttachShader(program, fragment_shader);
glDeleteShader(vertex_shader);
glDeleteShader(fragment_shader);
glBindFragDataLocation(program, 0, "out_color");
glLinkProgram(program);
// Get variable linkages
glUseProgram(program);
GLint position_attribute = glGetAttribLocation(program, "in_position");
GLint point_count_uniform = glGetUniformLocation(program, "in_point_count");
GLint circle_count_uniform = glGetUniformLocation(program, "in_circle_count");
time_uniform = glGetUniformLocation(program, "in_time");
float position_data[] = {0.0f, 0.0f, 0.0f, 1.0f};
amount_of_data = sizeof(position_data)/(sizeof(float)*4);
// Upload data to the card
glGenVertexArrays(1, &vertex_array);
glBindVertexArray(vertex_array);
glGenBuffers(1, &vertex_position_buffer);
glBindBuffer(GL_ARRAY_BUFFER, vertex_position_buffer);
glBufferData(GL_ARRAY_BUFFER, sizeof(position_data), position_data, GL_STATIC_DRAW);
glEnableVertexAttribArray(position_attribute);
glVertexAttribPointer(position_attribute, 4, GL_FLOAT, GL_FALSE, 0, 0);
// Specify some constants to the shader program
glUniform1i(point_count_uniform, point_count);
glUniform1i(circle_count_uniform, circle_count);
// Make sure everything went well
GLenum error = glGetError();
assert(error == GL_NO_ERROR);
beginning = monotonicTime();
}
~OpenGLStuff() {
glDeleteProgram(program);
glDeleteBuffers(1, &vertex_position_buffer);
glDeleteVertexArrays(1, &vertex_array);
}
void DrawScene() {
uint64_t before = monotonicTime();
glClear(GL_COLOR_BUFFER_BIT);
glUniform1i(time_uniform, (GLint)(before - beginning));
glDrawArraysInstanced(GL_POINTS, 0, amount_of_data, circle_count*point_count);
glFlush();
}
void Resize(const int width, const int height) {
glViewport(0, 0, width, height);
}
private:
uint64_t beginning;
GLint time_uniform;
const int point_count;
const int circle_count;
size_t amount_of_data;
GLuint vertex_array;
GLuint vertex_position_buffer;
GLuint program;
};
class GLXStuff {
public:
GLXStuff(const int width, const int height) {
/*
Step 1: Get a connection to the X server
Step 2: Get a FBConfig
Step 3: Create a versioned context
Step 4: Create a drawable
Step 5: Make the context current on the drawable
*/
int fbcount;
Bool success;
// Step 1
display = XOpenDisplay(NULL);
XSetIOErrorHandler(handler);
int screen = DefaultScreen(display);
Window root_window = RootWindow(display, screen);
assert(display);
printf("%d screen(s)\n", ScreenCount(display));
// Step 2
const int visual_attribs[] = {
GLX_DOUBLEBUFFER, True,
GLX_RED_SIZE, 8,
GLX_GREEN_SIZE, 8,
GLX_BLUE_SIZE, 8,
GLX_ALPHA_SIZE, 8,
GLX_DEPTH_SIZE, 24,
GLX_X_VISUAL_TYPE, GLX_TRUE_COLOR,
//GLX_CONFIG_CAVEAT, GLX_NONE,
//GLX_SAMPLE_BUFFERS, 1,
//GLX_SAMPLES, 16,
None
};
GLXFBConfig* configs = glXChooseFBConfig(display, screen, visual_attribs, &fbcount);
assert(configs);
assert(fbcount);
printf("%d configs\n", fbcount);
// Naively take the first config
GLXFBConfig config = configs[0];
// Step 3
// If you don't want a versioned context, you can use this function and
// GLX will return you ::some:: version of a context. You'll then have to
// use glGet(GL_MAJOR_VERSION) and glGet(GL_MINOR_VERSION) to figure out
// if what it gave us is acceptable.
//context = glXCreateNewContext(display, config, GLX_RGBA_TYPE, NULL, True);
const int context_attribs[] = {
GLX_CONTEXT_MAJOR_VERSION_ARB, 3,
GLX_CONTEXT_MINOR_VERSION_ARB, 2,
//GLX_CONTEXT_MAJOR_VERSION_ARB, 4,
//GLX_CONTEXT_MINOR_VERSION_ARB, 3,
None
};
context = glXCreateContextAttribsARB(display, config, NULL, True, context_attribs);
assert(context);
XFree(configs);
// Step 4
XVisualInfo *visual_info = glXGetVisualFromFBConfig(display, config);
assert(visual_info);
XSetWindowAttributes swa;
Colormap cmap = XCreateColormap(display, root_window, visual_info->visual, AllocNone);
swa.colormap = cmap;
swa.background_pixmap = None;
swa.border_pixel = BlackPixel(display, screen);
swa.event_mask = StructureNotifyMask;
window = XCreateWindow(display, RootWindow(display, screen), 0, 0, width, height, 50, visual_info->depth, InputOutput, visual_info->visual, CWBorderPixel|CWColormap|CWEventMask, &swa);
assert(window);
XMapWindow(display, window);
// Step 5
success = glXMakeCurrent(display, window, context);
assert(success);
}
~GLXStuff() {
XDestroyWindow(display, window);
glXDestroyContext(display, context);
XCloseDisplay(display);
}
void SwapBuffers() {
glXSwapBuffers(display, window);
}
void HandleEvents(OpenGLStuff &opengl_stuff) {
while (XPending(display)) {
XEvent event;
XNextEvent(display, &event);
switch (event.type) {
case ConfigureNotify:
opengl_stuff.Resize(event.xconfigure.width, event.xconfigure.height);
break;
case ClientMessage:
done = 1;
break;
}
}
}
private:
Display* display;
Window window;
GLXContext context;
};
int main(int argc, char *argv[]) {
const int width = 1300, height = 1300;
GLXStuff glx_stuff(width, height);
OpenGLStuff opengl_stuff(width, height);
printf("Done!\n");
// 60 FPS loop. glXSwapBuffers is synchronous.
while (!done) {
glx_stuff.HandleEvents(opengl_stuff);
if (!done) {
opengl_stuff.DrawScene();
glx_stuff.SwapBuffers();
}
}
return 0;
}
No comments:
Post a Comment