From 7265121b24c2d9db79a13c42be34903c2a7d78e2 Mon Sep 17 00:00:00 2001 From: whitequark Date: Sun, 24 Jul 2016 14:42:44 +0000 Subject: [PATCH] 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. --- .travis/install-debian.sh | 2 +- CMakeLists.txt | 1 - README.md | 2 +- debian/control | 2 +- src/CMakeLists.txt | 16 +++----- src/export.cpp | 15 +++++--- src/platform/cocoamain.mm | 15 ++------ src/platform/gloffscreen.cpp | 74 ------------------------------------ src/platform/gloffscreen.h | 36 ------------------ src/platform/gtkmain.cpp | 25 ++++++------ src/render/render.h | 11 ++++++ src/render/rendergl.cpp | 57 +++++++++++++++++++++++++++ 12 files changed, 101 insertions(+), 155 deletions(-) delete mode 100644 src/platform/gloffscreen.cpp delete mode 100644 src/platform/gloffscreen.h create mode 100644 src/render/rendergl.cpp diff --git a/.travis/install-debian.sh b/.travis/install-debian.sh index 6c11310..be6f9ba 100755 --- a/.travis/install-debian.sh +++ b/.travis/install-debian.sh @@ -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 diff --git a/CMakeLists.txt b/CMakeLists.txt index 5dd1844..709a4d1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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) diff --git a/README.md b/README.md index f06ca8e..42a1b12 100644 --- a/README.md +++ b/README.md @@ -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: diff --git a/debian/control b/debian/control index faa62d9..4e03aae 100644 --- a/debian/control +++ b/debian/control @@ -4,7 +4,7 @@ Priority: optional Maintainer: whitequark 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 diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 9e794e2..250c105 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -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 diff --git a/src/export.cpp b/src/export.cpp index db4791f..d435a8f 100644 --- a/src/export.cpp +++ b/src/export.cpp @@ -7,9 +7,6 @@ // Copyright 2008-2013 Jonathan Westhues. //----------------------------------------------------------------------------- #include "solvespace.h" -#ifndef WIN32 -#include -#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(new GLOffscreen); - gloffscreen->begin((int)SS.GW.width, (int)SS.GW.height); + GlOffscreen offscreen; + offscreen.Render((int)SS.GW.width, (int)SS.GW.height, [&] { + SS.GW.Paint(); + }); #endif - SS.GW.Paint(); 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; } diff --git a/src/platform/cocoamain.mm b/src/platform/cocoamain.mm index 21208ef..f448441 100644 --- a/src/platform/cocoamain.mm +++ b/src/platform/cocoamain.mm @@ -14,7 +14,6 @@ #include #include "solvespace.h" -#include "gloffscreen.h" #include 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, diff --git a/src/platform/gloffscreen.cpp b/src/platform/gloffscreen.cpp deleted file mode 100644 index b073893..0000000 --- a/src/platform/gloffscreen.cpp +++ /dev/null @@ -1,74 +0,0 @@ -//----------------------------------------------------------------------------- -// Offscreen rendering in OpenGL using framebuffer objects. -// -// Copyright 2015 -//----------------------------------------------------------------------------- -#ifdef __APPLE__ -#include -#else -#include -#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; -} diff --git a/src/platform/gloffscreen.h b/src/platform/gloffscreen.h deleted file mode 100644 index 378e044..0000000 --- a/src/platform/gloffscreen.h +++ /dev/null @@ -1,36 +0,0 @@ -//----------------------------------------------------------------------------- -// Offscreen rendering in OpenGL using framebuffer objects. -// -// Copyright 2015 -//----------------------------------------------------------------------------- -#ifndef __GLOFFSCREEN_H -#define __GLOFFSCREEN_H - -#include - -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 diff --git a/src/platform/gtkmain.cpp b/src/platform/gtkmain.cpp index 474fea2..4f20f50 100644 --- a/src/platform/gtkmain.cpp +++ b/src/platform/gtkmain.cpp @@ -52,7 +52,6 @@ #include "solvespace.h" #include "config.h" -#include "gloffscreen.h" #ifdef HAVE_SPACEWARE # include @@ -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 surface = Cairo::ImageSurface::create( - _offscreen->end(), Cairo::FORMAT_RGB24, - allocation.get_width(), allocation.get_height(), allocation.get_width() * 4); + Cairo::RefPtr 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; }; diff --git a/src/render/render.h b/src/render/render.h index 0212dbd..b0b4b24 100644 --- a/src/render/render.h +++ b/src/render/render.h @@ -204,6 +204,17 @@ public: bool Pick(std::function drawFn); }; +// An offscreen renderer based on OpenGL framebuffers. +class GlOffscreen { +public: + unsigned int framebuffer; + unsigned int colorRenderbuffer, depthRenderbuffer; + std::vector data; + + bool Render(int width, int height, std::function renderFn); + void Clear(); +}; + // A canvas that uses the core OpenGL profile, for desktop systems. class OpenGl1Renderer : public Canvas { public: diff --git a/src/render/rendergl.cpp b/src/render/rendergl.cpp new file mode 100644 index 0000000..1e65e59 --- /dev/null +++ b/src/render/rendergl.cpp @@ -0,0 +1,57 @@ +//----------------------------------------------------------------------------- +// Offscreen rendering in OpenGL using EGL and framebuffer objects. +// +// Copyright 2015-2016 whitequark +//----------------------------------------------------------------------------- +#ifdef __APPLE__ +#include +#else +#define GL_GLEXT_PROTOTYPES +#include +#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 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; +}