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 install -q -y \
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

View File

@ -196,7 +196,6 @@ else() # Linux and compatible systems
pkg_check_modules(PNG REQUIRED libpng)
pkg_check_modules(FONTCONFIG REQUIRED fontconfig)
pkg_check_modules(JSONC REQUIRED json-c)
pkg_check_modules(GLEW REQUIRED glew)
pkg_check_modules(FREETYPE REQUIRED freetype2)
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 \
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:

2
debian/control vendored
View File

@ -4,7 +4,7 @@ Priority: optional
Maintainer: whitequark <whitequark@whitequark.org>
Build-Depends: debhelper (>= 9), cmake, libpng-dev, zlib1g-dev, libjson-c-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
Homepage: http://solvespace.com
Vcs-Git: git://github.com/whitequark/solvespace

View File

@ -89,7 +89,7 @@ elseif(APPLE)
set(platform_SOURCES
platform/cocoamain.mm
platform/gloffscreen.cpp)
render/rendergl.cpp)
set(platform_BUNDLED_LIBS
${PNG_LIBRARIES}
@ -101,30 +101,26 @@ elseif(HAVE_GTK)
include_directories(
${GTKMM_INCLUDE_DIRS}
${JSONC_INCLUDE_DIRS}
${FONTCONFIG_INCLUDE_DIRS}
${GLEW_INCLUDE_DIRS})
${FONTCONFIG_INCLUDE_DIRS})
link_directories(
${GTKMM_LIBRARY_DIRS}
${JSONC_LIBRARY_DIRS}
${FONTCONFIG_LIBRARY_DIRS}
${GLEW_LIBRARY_DIRS})
${FONTCONFIG_LIBRARY_DIRS})
add_definitions(
${GTKMM_CFLAGS_OTHER}
${JSONC_CFLAGS_OTHER}
${FONTCONFIG_CFLAGS_OTHER}
${GLEW_CFLAGS_OTHER})
${FONTCONFIG_CFLAGS_OTHER})
set(platform_SOURCES
platform/gtkmain.cpp
platform/gloffscreen.cpp)
render/rendergl.cpp)
set(platform_LIBRARIES
${GTKMM_LIBRARIES}
${JSONC_LIBRARIES}
${FONTCONFIG_LIBRARIES}
${GLEW_LIBRARIES})
${FONTCONFIG_LIBRARIES})
endif()
# solvespace executable

View File

@ -7,9 +7,6 @@
// Copyright 2008-2013 Jonathan Westhues.
//-----------------------------------------------------------------------------
#include "solvespace.h"
#ifndef WIN32
#include <platform/gloffscreen.h>
#endif
void SolveSpaceUI::ExportSectionTo(const std::string &filename) {
Vector gn = (SS.GW.projRight).Cross(SS.GW.projUp);
@ -1084,10 +1081,11 @@ void SolveSpaceUI::ExportAsPngTo(const std::string &filename) {
bool prevShowToolbar = SS.showToolbar;
SS.showToolbar = false;
#ifndef WIN32
std::unique_ptr<GLOffscreen> gloffscreen(new GLOffscreen);
gloffscreen->begin((int)SS.GW.width, (int)SS.GW.height);
#endif
GlOffscreen offscreen;
offscreen.Render((int)SS.GW.width, (int)SS.GW.height, [&] {
SS.GW.Paint();
});
#endif
SS.showToolbar = prevShowToolbar;
// 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());
}
if(f) fclose(f);
#ifndef WIN32
offscreen.Clear();
#endif
return;
}

View File

@ -14,7 +14,6 @@
#include <map>
#include "solvespace.h"
#include "gloffscreen.h"
#include <config.h>
using SolveSpace::dbp;
@ -126,7 +125,7 @@ const bool SolveSpace::FLIP_FRAMEBUFFER = false;
@implementation GLViewWithEditor
{
GLOffscreen *offscreen;
SolveSpace::GlOffscreen offscreen;
NSOpenGLContext *glContext;
@protected
NSTextField *editor;
@ -156,7 +155,7 @@ const bool SolveSpace::FLIP_FRAMEBUFFER = false;
}
- (void)dealloc {
delete offscreen;
offscreen.Clear();
}
#define CONVERT1(name, to_from) \
@ -187,19 +186,13 @@ CONVERT(Rect)
- (void)drawRect:(NSRect)aRect {
[glContext makeCurrentContext];
if(!offscreen)
offscreen = new GLOffscreen;
NSSize size = [self convertSizeToBacking:[self bounds].size];
int width = (int)size.width,
height = (int)size.height;
offscreen->begin(width, height);
offscreen.Render(width, height, [&] { [self drawGL]; });
[self drawGL];
uint8_t *pixels = offscreen->end();
CGDataProviderRef provider = CGDataProviderCreateWithData(
NULL, pixels, width * height * 4, NULL);
NULL, &offscreen.data[0], width * height * 4, NULL);
CGColorSpaceRef colorspace = CGColorSpaceCreateDeviceRGB();
CGImageRef image = CGImageCreate(width, height, 8, 32,
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 "config.h"
#include "gloffscreen.h"
#ifdef HAVE_SPACEWARE
# include <spnav.h>
@ -228,7 +227,7 @@ const bool FLIP_FRAMEBUFFER = true;
class GlWidget : public Gtk::DrawingArea {
public:
GlWidget() : _offscreen(NULL) {
GlWidget() {
_xdisplay = gdk_x11_get_default_xdisplay();
int glxmajor, glxminor;
@ -297,7 +296,7 @@ public:
XDestroyWindow(_xdisplay, _xwindow);
delete _offscreen;
_offscreen.Clear();
glXDestroyContext(_xdisplay, _glcontext);
}
@ -314,18 +313,16 @@ protected:
ssassert(glXMakeCurrent(_xdisplay, _xwindow, _glcontext),
"Cannot make OpenGL context current");
if(!_offscreen)
_offscreen = new GLOffscreen;
Gdk::Rectangle allocation = get_allocation();
ssassert(_offscreen->begin(allocation.get_width(), allocation.get_height()),
"Cannot allocate offscreen rendering buffer");
bool success = _offscreen.Render(
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->end(), Cairo::FORMAT_RGB24,
allocation.get_width(), allocation.get_height(), allocation.get_width() * 4);
Cairo::RefPtr<Cairo::ImageSurface> surface =
Cairo::ImageSurface::create(&_offscreen.data[0], Cairo::FORMAT_RGB24,
allocation.get_width(), allocation.get_height(),
allocation.get_width() * 4);
cr->set_source(surface, 0, 0);
cr->paint();
surface->finish();
@ -338,7 +335,7 @@ protected:
private:
Display *_xdisplay;
GLXContext _glcontext;
GLOffscreen *_offscreen;
GlOffscreen _offscreen;
::Window _xwindow;
};

View File

@ -204,6 +204,17 @@ public:
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.
class OpenGl1Renderer : public Canvas {
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;
}