bsnes/ruby/video/glx2.cpp

246 lines
7.2 KiB
C++

//Xorg/GLX OpenGL 2.0 driver
//note: this is a fallback driver for use when OpenGL 3.2 is not available.
//see glx.cpp for comments on how this driver operates (they are very similar.)
struct VideoGLX2 : Video {
~VideoGLX2() { term(); }
auto (*glXSwapInterval)(signed) -> signed = nullptr;
Display* display = nullptr;
signed screen = 0;
Window xwindow = 0;
Colormap colormap = 0;
GLXContext glxcontext = nullptr;
GLXWindow glxwindow = 0;
struct {
Window handle = 0;
bool synchronize = false;
unsigned filter = 1; //linear
unsigned width = 256;
unsigned height = 256;
bool isDoubleBuffered = false;
bool isDirect = false;
} settings;
auto cap(const string& name) -> bool {
if(name == Video::Handle) return true;
if(name == Video::Synchronize) return true;
if(name == Video::Filter) return true;
return false;
}
auto get(const string& name) -> any {
if(name == Video::Handle) return (uintptr_t)settings.handle;
if(name == Video::Synchronize) return settings.synchronize;
if(name == Video::Filter) return settings.filter;
return {};
}
auto set(const string& name, const any& value) -> bool {
if(name == Video::Handle && value.is<uintptr_t>()) {
settings.handle = value.get<uintptr_t>();
return true;
}
if(name == Video::Synchronize && value.is<bool>()) {
if(settings.synchronize != value.get<bool>()) {
settings.synchronize = value.get<bool>();
if(glXSwapInterval) glXSwapInterval(settings.synchronize);
return true;
}
}
if(name == Video::Filter && value.is<unsigned>()) {
settings.filter = value.get<unsigned>();
return true;
}
return false;
}
auto lock(uint32_t*& data, unsigned& pitch, unsigned width, unsigned height) -> bool {
if(width != settings.width || height != settings.height) resize(width, height);
pitch = glwidth * sizeof(uint32_t);
return data = glbuffer;
}
auto unlock() -> void {
}
auto clear() -> void {
memory::fill(glbuffer, glwidth * glheight * sizeof(uint32_t));
glClearColor(0.0, 0.0, 0.0, 1.0);
glClear(GL_COLOR_BUFFER_BIT);
glFlush();
if(settings.isDoubleBuffered) glXSwapBuffers(display, glxwindow);
}
auto refresh() -> void {
XWindowAttributes parent, child;
XGetWindowAttributes(display, settings.handle, &parent);
XGetWindowAttributes(display, xwindow, &child);
if(child.width != parent.width || child.height != parent.height) {
XResizeWindow(display, xwindow, parent.width, parent.height);
}
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, settings.filter ? GL_LINEAR : GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, settings.filter ? GL_LINEAR : GL_NEAREST);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(0, parent.width, 0, parent.height, -1.0, 1.0);
glViewport(0, 0, parent.width, parent.height);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glPixelStorei(GL_UNPACK_ROW_LENGTH, glwidth);
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, settings.width, settings.height,
GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, glbuffer);
double w = (double)settings.width / (double)glwidth;
double h = (double)settings.height / (double)glheight;
signed u = parent.width;
signed v = parent.height;
glBegin(GL_TRIANGLE_STRIP);
glTexCoord2f(0, 0); glVertex3i(0, v, 0);
glTexCoord2f(w, 0); glVertex3i(u, v, 0);
glTexCoord2f(0, h); glVertex3i(0, 0, 0);
glTexCoord2f(w, h); glVertex3i(u, 0, 0);
glEnd();
glFlush();
if(settings.isDoubleBuffered) glXSwapBuffers(display, glxwindow);
}
auto init() -> bool {
display = XOpenDisplay(0);
screen = DefaultScreen(display);
signed versionMajor = 0, versionMinor = 0;
glXQueryVersion(display, &versionMajor, &versionMinor);
if(versionMajor < 1 || (versionMajor == 1 && versionMinor < 2)) return false;
XWindowAttributes windowAttributes;
XGetWindowAttributes(display, settings.handle, &windowAttributes);
signed attributeList[] = {
GLX_DRAWABLE_TYPE, GLX_WINDOW_BIT,
GLX_RENDER_TYPE, GLX_RGBA_BIT,
GLX_DOUBLEBUFFER, True,
GLX_RED_SIZE, 8,
GLX_GREEN_SIZE, 8,
GLX_BLUE_SIZE, 8,
None
};
signed fbCount = 0;
auto fbConfig = glXChooseFBConfig(display, screen, attributeList, &fbCount);
if(fbCount == 0) return false;
auto vi = glXGetVisualFromFBConfig(display, fbConfig[0]);
colormap = XCreateColormap(display, RootWindow(display, vi->screen), vi->visual, AllocNone);
XSetWindowAttributes attributes;
attributes.colormap = colormap;
attributes.border_pixel = 0;
xwindow = XCreateWindow(display, settings.handle, 0, 0, windowAttributes.width, windowAttributes.height,
0, vi->depth, InputOutput, vi->visual, CWColormap | CWBorderPixel, &attributes);
XSetWindowBackground(display, xwindow, 0);
XMapWindow(display, xwindow);
XFlush(display);
while(XPending(display)) {
XEvent event;
XNextEvent(display, &event);
}
glxcontext = glXCreateContext(display, vi, 0, GL_TRUE);
glXMakeCurrent(display, glxwindow = xwindow, glxcontext);
glXSwapInterval = (signed (*)(signed))glGetProcAddress("glXSwapIntervalEXT");
if(!glXSwapInterval) glXSwapInterval = (signed (*)(signed))glGetProcAddress("glXSwapIntervalMESA");
if(!glXSwapInterval) glXSwapInterval = (signed (*)(signed))glGetProcAddress("glXSwapIntervalSGI");
if(glXSwapInterval) glXSwapInterval(settings.synchronize);
signed value = 0;
glXGetConfig(display, vi, GLX_DOUBLEBUFFER, &value);
settings.isDoubleBuffered = value;
settings.isDirect = glXIsDirect(display, glxcontext);
glDisable(GL_ALPHA_TEST);
glDisable(GL_BLEND);
glDisable(GL_DEPTH_TEST);
glDisable(GL_POLYGON_SMOOTH);
glDisable(GL_STENCIL_TEST);
glEnable(GL_DITHER);
glEnable(GL_TEXTURE_2D);
resize(256, 256);
return true;
}
auto term() -> void {
if(gltexture) {
glDeleteTextures(1, &gltexture);
gltexture = 0;
}
if(glbuffer) {
delete[] glbuffer;
glbuffer = 0;
}
glwidth = 0;
glheight = 0;
if(glxcontext) {
glXDestroyContext(display, glxcontext);
glxcontext = nullptr;
}
if(xwindow) {
XUnmapWindow(display, xwindow);
xwindow = 0;
}
if(colormap) {
XFreeColormap(display, colormap);
colormap = 0;
}
if(display) {
XCloseDisplay(display);
display = nullptr;
}
}
private:
GLuint gltexture = 0;
uint32_t* glbuffer = nullptr;
unsigned glwidth = 0;
unsigned glheight = 0;
auto resize(unsigned width, unsigned height) -> void {
settings.width = width;
settings.height = height;
if(gltexture == 0) glGenTextures(1, &gltexture);
glwidth = max(glwidth, width);
glheight = max(glheight, height);
if(glbuffer) delete[] glbuffer;
glbuffer = new uint32_t[glwidth * glheight]();
glBindTexture(GL_TEXTURE_2D, gltexture);
glPixelStorei(GL_UNPACK_ROW_LENGTH, glwidth);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, glwidth, glheight, 0, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, glbuffer);
}
};