Refactor GlOffscreen; remove the GLEW dependency.

It was never really needed, since both Linux and OS X, where
GlOffscreen is used, guarantee that the API we need is present,
on all OS versions we're interested in.

Also, reorganize GlOffscreen consistently with the rest of our
codebase, and don't use RAII for OpenGL resource management because
of its requirement for an active context.
This commit is contained in:
whitequark 2016-07-24 14:42:44 +00:00
parent 216091a366
commit 7265121b24
12 changed files with 101 additions and 155 deletions

View File

@ -4,5 +4,5 @@ sudo add-apt-repository -y ppa:ubuntu-toolchain-r/test
sudo apt-get update -qq sudo apt-get update -qq
sudo apt-get install -q -y \ sudo apt-get install -q -y \
cmake cmake-data libpng12-dev zlib1g-dev libjson0-dev libfontconfig1-dev \ cmake cmake-data libpng12-dev zlib1g-dev libjson0-dev libfontconfig1-dev \
libgtkmm-2.4-dev libpangomm-1.4-dev libgl1-mesa-dev libglu-dev libglew-dev \ libgtkmm-2.4-dev libpangomm-1.4-dev libgl1-mesa-dev libglu-dev \
libfreetype6-dev dpkg-dev gcc-5 g++-5 libfreetype6-dev dpkg-dev gcc-5 g++-5

View File

@ -196,7 +196,6 @@ else() # Linux and compatible systems
pkg_check_modules(PNG REQUIRED libpng) pkg_check_modules(PNG REQUIRED libpng)
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)
pkg_check_modules(FREETYPE REQUIRED freetype2) pkg_check_modules(FREETYPE REQUIRED freetype2)
set(HAVE_GTK TRUE) set(HAVE_GTK TRUE)

View File

@ -31,7 +31,7 @@ On a Debian derivative (e.g. Ubuntu) these can be installed with:
apt-get install libpng-dev libjson-c-dev libfreetype6-dev \ apt-get install libpng-dev libjson-c-dev libfreetype6-dev \
libfontconfig1-dev libgtkmm-2.4-dev libpangomm-1.4-dev \ libfontconfig1-dev libgtkmm-2.4-dev libpangomm-1.4-dev \
libgl-dev libglu-dev libglew-dev cmake libgl-dev libglu-dev cmake
Before building, check out the necessary submodules: Before building, check out the necessary submodules:

2
debian/control vendored
View File

@ -4,7 +4,7 @@ Priority: optional
Maintainer: whitequark <whitequark@whitequark.org> Maintainer: whitequark <whitequark@whitequark.org>
Build-Depends: debhelper (>= 9), cmake, libpng-dev, zlib1g-dev, libjson-c-dev, Build-Depends: debhelper (>= 9), cmake, libpng-dev, zlib1g-dev, libjson-c-dev,
libfontconfig1-dev, libgtkmm-2.4-dev, libpangomm-1.4-dev, libfontconfig1-dev, libgtkmm-2.4-dev, libpangomm-1.4-dev,
libgl-dev, libglu-dev, libglew-dev libgl-dev, libglu-dev
Standards-Version: 3.9.5 Standards-Version: 3.9.5
Homepage: http://solvespace.com Homepage: http://solvespace.com
Vcs-Git: git://github.com/whitequark/solvespace Vcs-Git: git://github.com/whitequark/solvespace

View File

@ -89,7 +89,7 @@ elseif(APPLE)
set(platform_SOURCES set(platform_SOURCES
platform/cocoamain.mm platform/cocoamain.mm
platform/gloffscreen.cpp) render/rendergl.cpp)
set(platform_BUNDLED_LIBS set(platform_BUNDLED_LIBS
${PNG_LIBRARIES} ${PNG_LIBRARIES}
@ -101,30 +101,26 @@ 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
platform/gtkmain.cpp platform/gtkmain.cpp
platform/gloffscreen.cpp) render/rendergl.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

View File

@ -7,9 +7,6 @@
// Copyright 2008-2013 Jonathan Westhues. // Copyright 2008-2013 Jonathan Westhues.
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
#include "solvespace.h" #include "solvespace.h"
#ifndef WIN32
#include <platform/gloffscreen.h>
#endif
void SolveSpaceUI::ExportSectionTo(const std::string &filename) { void SolveSpaceUI::ExportSectionTo(const std::string &filename) {
Vector gn = (SS.GW.projRight).Cross(SS.GW.projUp); Vector gn = (SS.GW.projRight).Cross(SS.GW.projUp);
@ -1084,10 +1081,11 @@ void SolveSpaceUI::ExportAsPngTo(const std::string &filename) {
bool prevShowToolbar = SS.showToolbar; bool prevShowToolbar = SS.showToolbar;
SS.showToolbar = false; SS.showToolbar = false;
#ifndef WIN32 #ifndef WIN32
std::unique_ptr<GLOffscreen> gloffscreen(new GLOffscreen); GlOffscreen offscreen;
gloffscreen->begin((int)SS.GW.width, (int)SS.GW.height); offscreen.Render((int)SS.GW.width, (int)SS.GW.height, [&] {
SS.GW.Paint();
});
#endif #endif
SS.GW.Paint();
SS.showToolbar = prevShowToolbar; SS.showToolbar = prevShowToolbar;
// Somewhat hacky way to invoke glReadPixels without dragging in all OpenGL headers. // Somewhat hacky way to invoke glReadPixels without dragging in all OpenGL headers.
@ -1100,6 +1098,11 @@ void SolveSpaceUI::ExportAsPngTo(const std::string &filename) {
Error("Couldn't write to '%s'", filename.c_str()); Error("Couldn't write to '%s'", filename.c_str());
} }
if(f) fclose(f); if(f) fclose(f);
#ifndef WIN32
offscreen.Clear();
#endif
return; return;
} }

View File

@ -14,7 +14,6 @@
#include <map> #include <map>
#include "solvespace.h" #include "solvespace.h"
#include "gloffscreen.h"
#include <config.h> #include <config.h>
using SolveSpace::dbp; using SolveSpace::dbp;
@ -126,7 +125,7 @@ const bool SolveSpace::FLIP_FRAMEBUFFER = false;
@implementation GLViewWithEditor @implementation GLViewWithEditor
{ {
GLOffscreen *offscreen; SolveSpace::GlOffscreen offscreen;
NSOpenGLContext *glContext; NSOpenGLContext *glContext;
@protected @protected
NSTextField *editor; NSTextField *editor;
@ -156,7 +155,7 @@ const bool SolveSpace::FLIP_FRAMEBUFFER = false;
} }
- (void)dealloc { - (void)dealloc {
delete offscreen; offscreen.Clear();
} }
#define CONVERT1(name, to_from) \ #define CONVERT1(name, to_from) \
@ -187,19 +186,13 @@ CONVERT(Rect)
- (void)drawRect:(NSRect)aRect { - (void)drawRect:(NSRect)aRect {
[glContext makeCurrentContext]; [glContext makeCurrentContext];
if(!offscreen)
offscreen = new GLOffscreen;
NSSize size = [self convertSizeToBacking:[self bounds].size]; NSSize size = [self convertSizeToBacking:[self bounds].size];
int width = (int)size.width, int width = (int)size.width,
height = (int)size.height; height = (int)size.height;
offscreen->begin(width, height); offscreen.Render(width, height, [&] { [self drawGL]; });
[self drawGL];
uint8_t *pixels = offscreen->end();
CGDataProviderRef provider = CGDataProviderCreateWithData( CGDataProviderRef provider = CGDataProviderCreateWithData(
NULL, pixels, width * height * 4, NULL); NULL, &offscreen.data[0], width * height * 4, NULL);
CGColorSpaceRef colorspace = CGColorSpaceCreateDeviceRGB(); CGColorSpaceRef colorspace = CGColorSpaceCreateDeviceRGB();
CGImageRef image = CGImageCreate(width, height, 8, 32, CGImageRef image = CGImageCreate(width, height, 8, 32,
width * 4, colorspace, kCGBitmapByteOrder32Little | kCGImageAlphaNoneSkipFirst, width * 4, colorspace, kCGBitmapByteOrder32Little | kCGImageAlphaNoneSkipFirst,

View File

@ -1,74 +0,0 @@
//-----------------------------------------------------------------------------
// Offscreen rendering in OpenGL using framebuffer objects.
//
// Copyright 2015 <whitequark@whitequark.org>
//-----------------------------------------------------------------------------
#ifdef __APPLE__
#include <OpenGL/GL.h>
#else
#include <GL/glew.h>
#endif
#include "gloffscreen.h"
#include "solvespace.h"
GLOffscreen::GLOffscreen() : _pixels(NULL), _width(0), _height(0) {
#ifndef __APPLE__
ssassert(glewInit() == GLEW_OK, "Unexpected GLEW failure");
#endif
ssassert(GL_EXT_framebuffer_object, "Expected an available FBO extension");
glGenFramebuffersEXT(1, &_framebuffer);
glGenRenderbuffersEXT(1, &_color_renderbuffer);
glGenRenderbuffersEXT(1, &_depth_renderbuffer);
}
GLOffscreen::~GLOffscreen() {
delete[] _pixels;
glDeleteRenderbuffersEXT(1, &_depth_renderbuffer);
glDeleteRenderbuffersEXT(1, &_color_renderbuffer);
glDeleteFramebuffersEXT(1, &_framebuffer);
}
bool GLOffscreen::begin(int width, int height) {
if(_width != width || _height != height) {
delete[] _pixels;
_pixels = new uint8_t[width * height * 4];
_width = width;
_height = height;
}
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, _framebuffer);
glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, _color_renderbuffer);
glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_RGBA8, _width, _height);
glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT,
GL_RENDERBUFFER_EXT, _color_renderbuffer);
glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, _depth_renderbuffer);
glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_DEPTH_COMPONENT24, _width, _height);
glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT,
GL_RENDERBUFFER_EXT, _depth_renderbuffer);
if(glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT) == GL_FRAMEBUFFER_COMPLETE_EXT)
return true;
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 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);
#else
glReadPixels(0, 0, _width, _height,
GL_BGRA, GL_UNSIGNED_INT_8_8_8_8, _pixels);
#endif
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
return _pixels;
}

View File

@ -1,36 +0,0 @@
//-----------------------------------------------------------------------------
// 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 if
flip is true and bottom row at index 0 if flip is false.
the returned array is valid until the next call to begin() */
uint8_t *end();
private:
unsigned int _framebuffer;
unsigned int _color_renderbuffer, _depth_renderbuffer;
uint8_t *_pixels;
int _width, _height;
};
#endif

View File

@ -52,7 +52,6 @@
#include "solvespace.h" #include "solvespace.h"
#include "config.h" #include "config.h"
#include "gloffscreen.h"
#ifdef HAVE_SPACEWARE #ifdef HAVE_SPACEWARE
# include <spnav.h> # include <spnav.h>
@ -228,7 +227,7 @@ const bool FLIP_FRAMEBUFFER = true;
class GlWidget : public Gtk::DrawingArea { class GlWidget : public Gtk::DrawingArea {
public: public:
GlWidget() : _offscreen(NULL) { GlWidget() {
_xdisplay = gdk_x11_get_default_xdisplay(); _xdisplay = gdk_x11_get_default_xdisplay();
int glxmajor, glxminor; int glxmajor, glxminor;
@ -297,7 +296,7 @@ public:
XDestroyWindow(_xdisplay, _xwindow); XDestroyWindow(_xdisplay, _xwindow);
delete _offscreen; _offscreen.Clear();
glXDestroyContext(_xdisplay, _glcontext); glXDestroyContext(_xdisplay, _glcontext);
} }
@ -314,18 +313,16 @@ protected:
ssassert(glXMakeCurrent(_xdisplay, _xwindow, _glcontext), ssassert(glXMakeCurrent(_xdisplay, _xwindow, _glcontext),
"Cannot make OpenGL context current"); "Cannot make OpenGL context current");
if(!_offscreen)
_offscreen = new GLOffscreen;
Gdk::Rectangle allocation = get_allocation(); Gdk::Rectangle allocation = get_allocation();
ssassert(_offscreen->begin(allocation.get_width(), allocation.get_height()), bool success = _offscreen.Render(
"Cannot allocate offscreen rendering buffer"); allocation.get_width(), allocation.get_height(),
sigc::mem_fun(this, &GlWidget::on_gl_draw));
ssassert(success, "Cannot allocate offscreen rendering buffer");
on_gl_draw(); Cairo::RefPtr<Cairo::ImageSurface> surface =
Cairo::ImageSurface::create(&_offscreen.data[0], Cairo::FORMAT_RGB24,
Cairo::RefPtr<Cairo::ImageSurface> surface = Cairo::ImageSurface::create( allocation.get_width(), allocation.get_height(),
_offscreen->end(), Cairo::FORMAT_RGB24, allocation.get_width() * 4);
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();
@ -338,7 +335,7 @@ protected:
private: private:
Display *_xdisplay; Display *_xdisplay;
GLXContext _glcontext; GLXContext _glcontext;
GLOffscreen *_offscreen; GlOffscreen _offscreen;
::Window _xwindow; ::Window _xwindow;
}; };

View File

@ -204,6 +204,17 @@ public:
bool Pick(std::function<void()> drawFn); bool Pick(std::function<void()> drawFn);
}; };
// An offscreen renderer based on OpenGL framebuffers.
class GlOffscreen {
public:
unsigned int framebuffer;
unsigned int colorRenderbuffer, depthRenderbuffer;
std::vector<uint8_t> data;
bool Render(int width, int height, std::function<void()> renderFn);
void Clear();
};
// A canvas that uses the core OpenGL profile, for desktop systems. // A canvas that uses the core OpenGL profile, for desktop systems.
class OpenGl1Renderer : public Canvas { class OpenGl1Renderer : public Canvas {
public: public:

57
src/render/rendergl.cpp Normal file
View File

@ -0,0 +1,57 @@
//-----------------------------------------------------------------------------
// Offscreen rendering in OpenGL using EGL and framebuffer objects.
//
// Copyright 2015-2016 whitequark
//-----------------------------------------------------------------------------
#ifdef __APPLE__
#include <OpenGL/GL.h>
#else
#define GL_GLEXT_PROTOTYPES
#include <GL/gl.h>
#endif
#include "solvespace.h"
void GlOffscreen::Clear() {
glDeleteRenderbuffersEXT(1, &depthRenderbuffer);
glDeleteRenderbuffersEXT(1, &colorRenderbuffer);
glDeleteFramebuffersEXT(1, &framebuffer);
*this = {};
}
bool GlOffscreen::Render(int width, int height, std::function<void()> renderFn) {
data.resize(width * height * 4);
if(framebuffer == 0) {
glGenFramebuffersEXT(1, &framebuffer);
glGenRenderbuffersEXT(1, &colorRenderbuffer);
glGenRenderbuffersEXT(1, &depthRenderbuffer);
}
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, framebuffer);
glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, colorRenderbuffer);
glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_RGBA8, width, height);
glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT,
GL_RENDERBUFFER_EXT, colorRenderbuffer);
glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, depthRenderbuffer);
glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_DEPTH_COMPONENT24, width, height);
glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT,
GL_RENDERBUFFER_EXT, depthRenderbuffer);
bool framebufferComplete =
glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT) == GL_FRAMEBUFFER_COMPLETE_EXT;
if(framebufferComplete) {
renderFn();
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
glReadPixels(0, 0, width, height, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, &data[0]);
#else
glReadPixels(0, 0, width, height, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8, &data[0]);
#endif
}
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
return framebufferComplete;
}