mirror of https://github.com/bsnes-emu/bsnes.git
246 lines
7.2 KiB
C++
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);
|
|
}
|
|
};
|