Implement offscreen rendering using GL framebuffer objects.
Apparently pbuffers are also a deprecated and unportable way of offscreen rendering. They're not supported by Chromium OpenGL implementation, which is what 3D-accelerated VirtualBox uses. This would also help a future OS X port.
This commit is contained in:
parent
622c9efadb
commit
e27c6b56c3
@ -82,6 +82,7 @@ else()
|
|||||||
find_package(PkgConfig REQUIRED)
|
find_package(PkgConfig REQUIRED)
|
||||||
pkg_check_modules(FONTCONFIG REQUIRED fontconfig)
|
pkg_check_modules(FONTCONFIG REQUIRED fontconfig)
|
||||||
pkg_check_modules(JSONC REQUIRED json-c)
|
pkg_check_modules(JSONC REQUIRED json-c)
|
||||||
|
pkg_check_modules(GLEW REQUIRED glew)
|
||||||
|
|
||||||
set(HAVE_GTK TRUE)
|
set(HAVE_GTK TRUE)
|
||||||
if(GUI STREQUAL "gtk3")
|
if(GUI STREQUAL "gtk3")
|
||||||
|
@ -142,25 +142,30 @@ elseif(HAVE_GTK)
|
|||||||
include_directories(
|
include_directories(
|
||||||
${GTKMM_INCLUDE_DIRS}
|
${GTKMM_INCLUDE_DIRS}
|
||||||
${JSONC_INCLUDE_DIRS}
|
${JSONC_INCLUDE_DIRS}
|
||||||
${FONTCONFIG_INCLUDE_DIRS})
|
${FONTCONFIG_INCLUDE_DIRS}
|
||||||
|
${GLEW_INCLUDE_DIRS})
|
||||||
|
|
||||||
link_directories(
|
link_directories(
|
||||||
${GTKMM_LIBRARY_DIRS}
|
${GTKMM_LIBRARY_DIRS}
|
||||||
${JSONC_LIBRARY_DIRS}
|
${JSONC_LIBRARY_DIRS}
|
||||||
${FONTCONFIG_LIBRARY_DIRS})
|
${FONTCONFIG_LIBRARY_DIRS}
|
||||||
|
${GLEW_LIBRARY_DIRS})
|
||||||
|
|
||||||
add_definitions(
|
add_definitions(
|
||||||
${GTKMM_CFLAGS_OTHER}
|
${GTKMM_CFLAGS_OTHER}
|
||||||
${JSONC_CFLAGS_OTHER}
|
${JSONC_CFLAGS_OTHER}
|
||||||
${FONTCONFIG_CFLAGS_OTHER})
|
${FONTCONFIG_CFLAGS_OTHER}
|
||||||
|
${GLEW_CFLAGS_OTHER})
|
||||||
|
|
||||||
set(platform_SOURCES
|
set(platform_SOURCES
|
||||||
gtk/gtkmain.cpp)
|
gtk/gtkmain.cpp
|
||||||
|
unix/gloffscreen.cpp)
|
||||||
|
|
||||||
set(platform_LIBRARIES
|
set(platform_LIBRARIES
|
||||||
${GTKMM_LIBRARIES}
|
${GTKMM_LIBRARIES}
|
||||||
${JSONC_LIBRARIES}
|
${JSONC_LIBRARIES}
|
||||||
${FONTCONFIG_LIBRARIES})
|
${FONTCONFIG_LIBRARIES}
|
||||||
|
${GLEW_LIBRARIES})
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# solvespace executable
|
# solvespace executable
|
||||||
|
@ -49,11 +49,11 @@
|
|||||||
|
|
||||||
#undef HAVE_STDINT_H /* no thanks, we have our own config.h */
|
#undef HAVE_STDINT_H /* no thanks, we have our own config.h */
|
||||||
|
|
||||||
#define GLX_GLXEXT_PROTOTYPES
|
|
||||||
#include <GL/glx.h>
|
#include <GL/glx.h>
|
||||||
|
|
||||||
#include "solvespace.h"
|
|
||||||
#include <config.h>
|
#include <config.h>
|
||||||
|
#include "solvespace.h"
|
||||||
|
#include "../unix/gloffscreen.h"
|
||||||
|
|
||||||
#ifdef HAVE_SPACEWARE
|
#ifdef HAVE_SPACEWARE
|
||||||
# include <spnav.h>
|
# include <spnav.h>
|
||||||
@ -67,7 +67,8 @@ char RecentFile[MAX_RECENT][MAX_PATH];
|
|||||||
#define GL_CHECK() \
|
#define GL_CHECK() \
|
||||||
do { \
|
do { \
|
||||||
int err = (int)glGetError(); \
|
int err = (int)glGetError(); \
|
||||||
if(err) dbp("%s:%d: glGetError() == 0x%X\n", __FILE__, __LINE__, err); \
|
if(err) dbp("%s:%d: glGetError() == 0x%X %s", \
|
||||||
|
__FILE__, __LINE__, err, gluErrorString(err)); \
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
/* Settings */
|
/* Settings */
|
||||||
@ -254,9 +255,7 @@ void ScheduleLater() {
|
|||||||
namespace SolveSpacePlatf {
|
namespace SolveSpacePlatf {
|
||||||
class GlWidget : public Gtk::DrawingArea {
|
class GlWidget : public Gtk::DrawingArea {
|
||||||
public:
|
public:
|
||||||
GlWidget() : _pixels(NULL), _glpbuffer(0) {
|
GlWidget() : _offscreen(NULL) {
|
||||||
set_double_buffered(false);
|
|
||||||
|
|
||||||
_xdisplay = gdk_x11_get_default_xdisplay();
|
_xdisplay = gdk_x11_get_default_xdisplay();
|
||||||
|
|
||||||
int glxmajor, glxminor;
|
int glxmajor, glxminor;
|
||||||
@ -287,7 +286,10 @@ public:
|
|||||||
/* prefer FBConfigs with depth of 32;
|
/* prefer FBConfigs with depth of 32;
|
||||||
* Mesa software rasterizer explodes with a BadMatch without this;
|
* Mesa software rasterizer explodes with a BadMatch without this;
|
||||||
* without this, Intel on Mesa flickers horribly for some reason.
|
* without this, Intel on Mesa flickers horribly for some reason.
|
||||||
this does not seem to affect other rasterizers (ie NVidia). */
|
this does not seem to affect other rasterizers (ie NVidia).
|
||||||
|
|
||||||
|
see this Mesa bug:
|
||||||
|
http://lists.freedesktop.org/archives/mesa-dev/2015-January/074693.html */
|
||||||
GLXFBConfig fbconfig = fbconfigs[0];
|
GLXFBConfig fbconfig = fbconfigs[0];
|
||||||
for(int i = 0; i < fbconfig_num; i++) {
|
for(int i = 0; i < fbconfig_num; i++) {
|
||||||
XVisualInfo *visual_info = glXGetVisualFromFBConfig(_xdisplay, fbconfigs[i]);
|
XVisualInfo *visual_info = glXGetVisualFromFBConfig(_xdisplay, fbconfigs[i]);
|
||||||
@ -307,63 +309,43 @@ public:
|
|||||||
oops();
|
oops();
|
||||||
}
|
}
|
||||||
|
|
||||||
int buffer_size = 2048;
|
|
||||||
|
|
||||||
_pixels = new uint32_t[buffer_size * buffer_size];
|
|
||||||
_pixels_inv = new uint32_t[buffer_size * buffer_size];
|
|
||||||
|
|
||||||
int pbuffer_attrs[] = {
|
|
||||||
GLX_PBUFFER_WIDTH, 2048,
|
|
||||||
GLX_PBUFFER_HEIGHT, 2048,
|
|
||||||
None
|
|
||||||
};
|
|
||||||
_glpbuffer = glXCreatePbuffer(_xdisplay, fbconfig, pbuffer_attrs);
|
|
||||||
if(!_glpbuffer)
|
|
||||||
oops();
|
|
||||||
|
|
||||||
XFree(fbconfigs);
|
XFree(fbconfigs);
|
||||||
}
|
}
|
||||||
|
|
||||||
~GlWidget() {
|
~GlWidget() {
|
||||||
delete[] _pixels;
|
delete _offscreen;
|
||||||
delete[] _pixels_inv;
|
|
||||||
|
|
||||||
glXDestroyPbuffer(_xdisplay, _glpbuffer);
|
|
||||||
glXDestroyContext(_xdisplay, _glcontext);
|
glXDestroyContext(_xdisplay, _glcontext);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
/* Draw on a GLX pbuffer, then read pixels out and draw them on
|
/* Draw on a GLX framebuffer object, then read pixels out and draw them on
|
||||||
the Cairo context. Slower, but you get to overlay nice widgets. */
|
the Cairo context. Slower, but you get to overlay nice widgets. */
|
||||||
virtual bool on_draw(const Cairo::RefPtr<Cairo::Context> &cr) {
|
virtual bool on_draw(const Cairo::RefPtr<Cairo::Context> &cr) {
|
||||||
if(!glXMakeContextCurrent(_xdisplay, _glpbuffer, _glpbuffer, _glcontext))
|
/* We activate a GL context for the X window, because we need /something/,
|
||||||
|
but we never draw directly onto the window. */
|
||||||
|
#ifdef HAVE_GTK3
|
||||||
|
::Window _xwindow = gdk_x11_window_get_xid(get_window()->gobj());
|
||||||
|
#else
|
||||||
|
::Window _xwindow = gdk_x11_drawable_get_xid(get_window()->gobj());
|
||||||
|
#endif
|
||||||
|
if(!glXMakeCurrent(_xdisplay, _xwindow, _glcontext))
|
||||||
|
oops();
|
||||||
|
|
||||||
|
if(!_offscreen)
|
||||||
|
_offscreen = new GLOffscreen;
|
||||||
|
|
||||||
|
Gdk::Rectangle allocation = get_allocation();
|
||||||
|
if(!_offscreen->begin(allocation.get_width(), allocation.get_height()))
|
||||||
oops();
|
oops();
|
||||||
|
|
||||||
on_gl_draw();
|
on_gl_draw();
|
||||||
glFlush();
|
glFlush();
|
||||||
GL_CHECK();
|
GL_CHECK();
|
||||||
|
|
||||||
Gdk::Rectangle allocation = get_allocation();
|
|
||||||
|
|
||||||
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
|
|
||||||
glReadPixels(0, 0, allocation.get_width(), allocation.get_height(),
|
|
||||||
GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, _pixels);
|
|
||||||
#else
|
|
||||||
glReadPixels(0, 0, allocation.get_width(), allocation.get_height(),
|
|
||||||
GL_BGRA, GL_UNSIGNED_INT_8_8_8_8, _pixels);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* in OpenGL coordinates, bottom is zero Y */
|
|
||||||
int row_size = allocation.get_width() * 4;
|
|
||||||
for(int i = 0; i < allocation.get_height(); i++) {
|
|
||||||
memcpy(&_pixels_inv[allocation.get_width() * i],
|
|
||||||
&_pixels[allocation.get_width() * (allocation.get_height() - i - 1)],
|
|
||||||
row_size);
|
|
||||||
}
|
|
||||||
|
|
||||||
Cairo::RefPtr<Cairo::ImageSurface> surface = Cairo::ImageSurface::create(
|
Cairo::RefPtr<Cairo::ImageSurface> surface = Cairo::ImageSurface::create(
|
||||||
(uint8_t*) _pixels_inv, Cairo::FORMAT_RGB24,
|
_offscreen->end(), Cairo::FORMAT_RGB24,
|
||||||
allocation.get_width(), allocation.get_height(), row_size);
|
allocation.get_width(), allocation.get_height(), allocation.get_width() * 4);
|
||||||
cr->set_source(surface, 0, 0);
|
cr->set_source(surface, 0, 0);
|
||||||
cr->paint();
|
cr->paint();
|
||||||
surface->finish();
|
surface->finish();
|
||||||
@ -382,8 +364,7 @@ protected:
|
|||||||
private:
|
private:
|
||||||
Display *_xdisplay;
|
Display *_xdisplay;
|
||||||
GLXContext _glcontext;
|
GLXContext _glcontext;
|
||||||
GLXPbuffer _glpbuffer;
|
GLOffscreen *_offscreen;
|
||||||
uint32_t *_pixels, *_pixels_inv;
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
76
src/unix/gloffscreen.cpp
Normal file
76
src/unix/gloffscreen.cpp
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// Offscreen rendering in OpenGL using framebuffer objects.
|
||||||
|
//
|
||||||
|
// Copyright 2015 <whitequark@whitequark.org>
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
#include "gloffscreen.h"
|
||||||
|
|
||||||
|
#include <cstring>
|
||||||
|
#include <cstdio>
|
||||||
|
#include <cstdlib>
|
||||||
|
|
||||||
|
#include <GL/glew.h>
|
||||||
|
|
||||||
|
GLOffscreen::GLOffscreen() : _pixels(NULL), _pixels_inv(NULL) {
|
||||||
|
if(glewInit() != GLEW_OK)
|
||||||
|
abort();
|
||||||
|
|
||||||
|
glGenFramebuffers(1, &_framebuffer);
|
||||||
|
glGenRenderbuffers(1, &_color_renderbuffer);
|
||||||
|
glGenRenderbuffers(1, &_depth_renderbuffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
GLOffscreen::~GLOffscreen() {
|
||||||
|
glDeleteRenderbuffers(1, &_depth_renderbuffer);
|
||||||
|
glDeleteRenderbuffers(1, &_color_renderbuffer);
|
||||||
|
glDeleteFramebuffers(1, &_framebuffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool GLOffscreen::begin(int width, int height) {
|
||||||
|
if(_width != width || _height != height) {
|
||||||
|
delete[] _pixels;
|
||||||
|
delete[] _pixels_inv;
|
||||||
|
|
||||||
|
_pixels = new uint32_t[width * height * 4];
|
||||||
|
_pixels_inv = new uint32_t[width * height * 4];
|
||||||
|
|
||||||
|
_width = width;
|
||||||
|
_height = height;
|
||||||
|
}
|
||||||
|
|
||||||
|
glBindFramebuffer(GL_FRAMEBUFFER, _framebuffer);
|
||||||
|
|
||||||
|
glBindRenderbuffer(GL_RENDERBUFFER, _color_renderbuffer);
|
||||||
|
glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA8, _width, _height);
|
||||||
|
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
|
||||||
|
GL_RENDERBUFFER, _color_renderbuffer);
|
||||||
|
|
||||||
|
glBindRenderbuffer(GL_RENDERBUFFER, _depth_renderbuffer);
|
||||||
|
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24, _width, _height);
|
||||||
|
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT,
|
||||||
|
GL_RENDERBUFFER, _depth_renderbuffer);
|
||||||
|
|
||||||
|
if(glCheckFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t *GLOffscreen::end() {
|
||||||
|
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
|
||||||
|
glReadPixels(0, 0, _width, _height,
|
||||||
|
GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, _pixels_inv);
|
||||||
|
#else
|
||||||
|
glReadPixels(0, 0, _width, _height,
|
||||||
|
GL_BGRA, GL_UNSIGNED_INT_8_8_8_8, _pixels_inv);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* in OpenGL coordinates, bottom is zero Y */
|
||||||
|
for(int i = 0; i < _height; i++)
|
||||||
|
memcpy(&_pixels[_width * i], &_pixels_inv[_width * (_height - i - 1)], _width * 4);
|
||||||
|
|
||||||
|
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||||
|
|
||||||
|
return (uint8_t*) _pixels;
|
||||||
|
}
|
35
src/unix/gloffscreen.h
Normal file
35
src/unix/gloffscreen.h
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// Offscreen rendering in OpenGL using framebuffer objects.
|
||||||
|
//
|
||||||
|
// Copyright 2015 <whitequark@whitequark.org>
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
#ifndef __GLOFFSCREEN_H
|
||||||
|
#define __GLOFFSCREEN_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
class GLOffscreen {
|
||||||
|
public:
|
||||||
|
/* these allocate and deallocate OpenGL resources.
|
||||||
|
an OpenGL context /must/ be current. */
|
||||||
|
GLOffscreen();
|
||||||
|
~GLOffscreen();
|
||||||
|
|
||||||
|
/* prepare for drawing a frame of specified size.
|
||||||
|
returns true if OpenGL likes our configuration, false
|
||||||
|
otherwise. if it returns false, the OpenGL state is restored. */
|
||||||
|
bool begin(int width, int height);
|
||||||
|
|
||||||
|
/* get pixels out of the frame and restore OpenGL state.
|
||||||
|
the pixel format is ARGB32 with top row at index 0.
|
||||||
|
the returned array is valid until the next call to begin() */
|
||||||
|
uint8_t *end();
|
||||||
|
|
||||||
|
private:
|
||||||
|
unsigned int _framebuffer;
|
||||||
|
unsigned int _color_renderbuffer, _depth_renderbuffer;
|
||||||
|
uint32_t *_pixels, *_pixels_inv;
|
||||||
|
int _width, _height;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
Loading…
Reference in New Issue
Block a user