GTK: remove GlWidget, use standard Gtk::GLArea.

This removes a large number of hacks from the codebase, speeds up
the rendering, and removes tearing when dragging entities.
pull/106/head
whitequark 2017-01-02 16:21:05 +00:00
parent 7dbbd75969
commit 6bb73a162c
9 changed files with 33 additions and 157 deletions

View File

@ -104,8 +104,6 @@ void SolveSpace::ScheduleLater() {
/* OpenGL view */ /* OpenGL view */
const bool SolveSpace::FLIP_FRAMEBUFFER = false;
@interface GLViewWithEditor : NSView @interface GLViewWithEditor : NSView
- (void)drawGL; - (void)drawGL;

View File

@ -20,6 +20,7 @@
#include <giomm/file.h> #include <giomm/file.h>
#include <gdkmm/cursor.h> #include <gdkmm/cursor.h>
#include <gtkmm/drawingarea.h> #include <gtkmm/drawingarea.h>
#include <gtkmm/glarea.h>
#include <gtkmm/scrollbar.h> #include <gtkmm/scrollbar.h>
#include <gtkmm/entry.h> #include <gtkmm/entry.h>
#include <gtkmm/eventbox.h> #include <gtkmm/eventbox.h>
@ -33,7 +34,6 @@
#include <gtkmm/radiobuttongroup.h> #include <gtkmm/radiobuttongroup.h>
#include <gtkmm/menu.h> #include <gtkmm/menu.h>
#include <gtkmm/menubar.h> #include <gtkmm/menubar.h>
#include <gtkmm/scrolledwindow.h>
#include <gtkmm/filechooserdialog.h> #include <gtkmm/filechooserdialog.h>
#include <gtkmm/messagedialog.h> #include <gtkmm/messagedialog.h>
#include <gtkmm/main.h> #include <gtkmm/main.h>
@ -213,121 +213,6 @@ void ScheduleLater() {
Glib::signal_idle().connect(&LaterCallback); Glib::signal_idle().connect(&LaterCallback);
} }
/* GL wrapper */
const bool FLIP_FRAMEBUFFER = true;
class GlWidget : public Gtk::DrawingArea {
public:
GlWidget() : _offscreen() {
_xdisplay = gdk_x11_get_default_xdisplay();
int glxmajor, glxminor;
ssassert(glXQueryVersion(_xdisplay, &glxmajor, &glxminor),
"Expected OpenGL to be available");
ssassert(glxmajor > 1 || (glxmajor == 1 && glxminor >= 3),
"Expected GLX >= 1.3");
static int fbconfig_attrs[] = {
GLX_RENDER_TYPE, GLX_RGBA_BIT,
GLX_RED_SIZE, 8,
GLX_GREEN_SIZE, 8,
GLX_BLUE_SIZE, 8,
GLX_DEPTH_SIZE, 24,
None
};
int fbconfig_num = 0;
GLXFBConfig *fbconfigs = glXChooseFBConfig(_xdisplay, DefaultScreen(_xdisplay),
fbconfig_attrs, &fbconfig_num);
ssassert(fbconfigs && fbconfig_num > 0,
"Expected an available framebuffer configuration");
/* prefer FBConfigs with depth of 32;
* Mesa software rasterizer explodes with a BadMatch without this;
* without this, Intel on Mesa flickers horribly for some reason.
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];
for(int i = 0; i < fbconfig_num; i++) {
XVisualInfo *visual_info = glXGetVisualFromFBConfig(_xdisplay, fbconfigs[i]);
/* some GL visuals, notably on Chromium GL, do not have an associated
X visual; this is not an obstacle as we always render offscreen. */
if(!visual_info) continue;
int depth = visual_info->depth;
XFree(visual_info);
if(depth == 32) {
fbconfig = fbconfigs[i];
break;
}
}
_glcontext = glXCreateNewContext(_xdisplay,
fbconfig, GLX_RGBA_TYPE, 0, True);
ssassert(_glcontext != NULL, "Cannot create an OpenGL context");
XFree(fbconfigs);
/* create a dummy X window to create a rendering context against.
we could use a Pbuffer, but some implementations (Chromium GL)
don't support these. we could use an existing window, but
some implementations (Chromium GL... do you see a pattern?)
do really strange things, i.e. draw a black rectangle on
the very front of the desktop if you do this. */
_xwindow = XCreateSimpleWindow(_xdisplay,
XRootWindow(_xdisplay, gdk_x11_get_default_screen()),
/*x*/ 0, /*y*/ 0, /*width*/ 1, /*height*/ 1,
/*border_width*/ 0, /*border*/ 0, /*background*/ 0);
}
~GlWidget() {
glXMakeCurrent(_xdisplay, None, NULL);
XDestroyWindow(_xdisplay, _xwindow);
_offscreen.Clear();
glXDestroyContext(_xdisplay, _glcontext);
}
protected:
/* 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. */
bool on_draw(const Cairo::RefPtr<Cairo::Context> &cr) override {
ssassert(glXMakeCurrent(_xdisplay, _xwindow, _glcontext),
"Cannot make OpenGL context current");
Gdk::Rectangle allocation = get_allocation();
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");
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();
glXSwapBuffers(_xdisplay, _xwindow);
return true;
}
virtual void on_gl_draw() = 0;
private:
Display *_xdisplay;
GLXContext _glcontext;
GlOffscreen _offscreen;
::Window _xwindow;
};
/* Editor overlay */ /* Editor overlay */
class EditorOverlay : public Gtk::Fixed { class EditorOverlay : public Gtk::Fixed {
@ -441,26 +326,27 @@ double DeltaYOfScrollEvent(GdkEventScroll *event) {
return delta_y; return delta_y;
} }
class GraphicsWidget : public GlWidget { class GraphicsWidget : public Gtk::GLArea {
public: public:
GraphicsWidget() { GraphicsWidget() {
set_events(Gdk::POINTER_MOTION_MASK | set_events(Gdk::POINTER_MOTION_MASK |
Gdk::BUTTON_PRESS_MASK | Gdk::BUTTON_RELEASE_MASK | Gdk::BUTTON_MOTION_MASK | Gdk::BUTTON_PRESS_MASK | Gdk::BUTTON_RELEASE_MASK | Gdk::BUTTON_MOTION_MASK |
Gdk::SCROLL_MASK | Gdk::SCROLL_MASK |
Gdk::LEAVE_NOTIFY_MASK); Gdk::LEAVE_NOTIFY_MASK);
set_double_buffered(true); set_has_alpha(true);
set_has_depth_buffer(true);
set_use_es(true);
} }
protected: protected:
bool on_configure_event(GdkEventConfigure *event) override { void on_resize(int width, int height) override {
_w = event->width; _w = width;
_h = event->height; _h = height;
return GlWidget::on_configure_event(event);;
} }
void on_gl_draw() override { bool on_render(const Glib::RefPtr<Gdk::GLContext> &context) override {
SS.GW.Paint(); SS.GW.Paint();
return true;
} }
bool on_motion_notify_event(GdkEventMotion *event) override { bool on_motion_notify_event(GdkEventMotion *event) override {
@ -1196,11 +1082,14 @@ DialogChoice LocateImportedFileYesNoCancel(const std::string &filename,
/* Text window */ /* Text window */
class TextWidget : public GlWidget { class TextWidget : public Gtk::GLArea {
public: public:
TextWidget(Glib::RefPtr<Gtk::Adjustment> adjustment) : _adjustment(adjustment) { TextWidget(Glib::RefPtr<Gtk::Adjustment> adjustment) : _adjustment(adjustment) {
set_events(Gdk::POINTER_MOTION_MASK | Gdk::BUTTON_PRESS_MASK | Gdk::SCROLL_MASK | set_events(Gdk::POINTER_MOTION_MASK | Gdk::BUTTON_PRESS_MASK | Gdk::SCROLL_MASK |
Gdk::LEAVE_NOTIFY_MASK); Gdk::LEAVE_NOTIFY_MASK);
set_has_alpha(true);
set_has_depth_buffer(true);
set_use_es(true);
} }
void set_cursor_hand(bool is_hand) { void set_cursor_hand(bool is_hand) {
@ -1212,8 +1101,9 @@ public:
} }
protected: protected:
void on_gl_draw() override { bool on_render(const Glib::RefPtr<Gdk::GLContext> &context) override {
SS.TW.Paint(); SS.TW.Paint();
return true;
} }
bool on_motion_notify_event(GdkEventMotion *event) override { bool on_motion_notify_event(GdkEventMotion *event) override {

View File

@ -93,8 +93,6 @@ void ScheduleLater() {
// Rendering // Rendering
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
const bool FLIP_FRAMEBUFFER = false;
std::shared_ptr<ViewportCanvas> CreateRenderer() { std::shared_ptr<ViewportCanvas> CreateRenderer() {
return NULL; return NULL;
} }

View File

@ -757,8 +757,6 @@ void SolveSpace::ShowTextWindow(bool visible)
ShowWindow(TextWnd, visible ? SW_SHOWNOACTIVATE : SW_HIDE); ShowWindow(TextWnd, visible ? SW_SHOWNOACTIVATE : SW_HIDE);
} }
const bool SolveSpace::FLIP_FRAMEBUFFER = false;
#if HAVE_OPENGL == 2 #if HAVE_OPENGL == 2
static void CreateGlContext(HWND hwnd, EGLDisplay *eglDisplay, EGLSurface *eglSurface, static void CreateGlContext(HWND hwnd, EGLDisplay *eglDisplay, EGLSurface *eglSurface,
EGLContext *eglContext) { EGLContext *eglContext) {

View File

@ -172,7 +172,7 @@ public:
// An interface for view-dependent visualization. // An interface for view-dependent visualization.
class ViewportCanvas : public Canvas { class ViewportCanvas : public Canvas {
public: public:
virtual void SetCamera(const Camera &camera, bool filp = FLIP_FRAMEBUFFER) = 0; virtual void SetCamera(const Camera &camera) = 0;
virtual void SetLighting(const Lighting &lighting) = 0; virtual void SetLighting(const Lighting &lighting) = 0;
virtual void NewFrame() = 0; virtual void NewFrame() = 0;

View File

@ -215,8 +215,8 @@ public:
void DoPoint(Vector p, double radius); void DoPoint(Vector p, double radius);
void DoStippledLine(const Vector &a, const Vector &b, hStroke hcs, double phase = 0.0); void DoStippledLine(const Vector &a, const Vector &b, hStroke hcs, double phase = 0.0);
void UpdateProjection(bool flip = FLIP_FRAMEBUFFER); void UpdateProjection();
void SetCamera(const Camera &camera, bool filp = FLIP_FRAMEBUFFER) override; void SetCamera(const Camera &camera) override;
void SetLighting(const Lighting &lighting) override; void SetLighting(const Lighting &lighting) override;
void NewFrame() override; void NewFrame() override;
@ -702,7 +702,7 @@ void OpenGl1Renderer::InvalidatePixmap(std::shared_ptr<const Pixmap> pm) {
} }
} }
void OpenGl1Renderer::UpdateProjection(bool flip) { void OpenGl1Renderer::UpdateProjection() {
UnSelectPrimitive(); UnSelectPrimitive();
glViewport(0, 0, camera.width, camera.height); glViewport(0, 0, camera.width, camera.height);
@ -717,17 +717,12 @@ void OpenGl1Renderer::UpdateProjection(bool flip) {
double mat[16]; double mat[16];
// Last thing before display is to apply the perspective // Last thing before display is to apply the perspective
double clp = camera.tangent * camera.scale; double clp = camera.tangent * camera.scale;
double sy = flip ? -1.0 : 1.0;
MakeMatrix(mat, 1, 0, 0, 0, MakeMatrix(mat, 1, 0, 0, 0,
0, sy, 0, 0, 0, 1, 0, 0,
0, 0, 1, 0, 0, 0, 1, 0,
0, 0, clp, 1); 0, 0, clp, 1);
glMultMatrixd(mat); glMultMatrixd(mat);
// If we flip the framebuffer, then we also flip the handedness
// of the coordinate system, and so the face winding order.
glFrontFace(flip ? GL_CW : GL_CCW);
// Before that, we apply the rotation // Before that, we apply the rotation
Vector projRight = camera.projRight, Vector projRight = camera.projRight,
projUp = camera.projUp, projUp = camera.projUp,
@ -829,9 +824,9 @@ void OpenGl1Renderer::GetIdent(const char **vendor, const char **renderer, const
*version = (const char *)glGetString(GL_VERSION); *version = (const char *)glGetString(GL_VERSION);
} }
void OpenGl1Renderer::SetCamera(const Camera &c, bool flip) { void OpenGl1Renderer::SetCamera(const Camera &c) {
camera = c; camera = c;
UpdateProjection(flip); UpdateProjection();
} }
void OpenGl1Renderer::SetLighting(const Lighting &l) { void OpenGl1Renderer::SetLighting(const Lighting &l) {

View File

@ -129,8 +129,8 @@ public:
void DoPoint(Vector p, hStroke hs); void DoPoint(Vector p, hStroke hs);
void DoStippledLine(const Vector &a, const Vector &b, hStroke hcs); void DoStippledLine(const Vector &a, const Vector &b, hStroke hcs);
void UpdateProjection(bool flip = FLIP_FRAMEBUFFER); void UpdateProjection();
void SetCamera(const Camera &c, bool flip) override; void SetCamera(const Camera &c) override;
void SetLighting(const Lighting &l) override; void SetLighting(const Lighting &l) override;
void NewFrame() override; void NewFrame() override;
@ -528,7 +528,7 @@ void OpenGl2Renderer::DrawPixmap(std::shared_ptr<const Pixmap> pm,
mli->mesh.AddPixmap(o, u, v, ta, tb); mli->mesh.AddPixmap(o, u, v, ta, tb);
} }
void OpenGl2Renderer::UpdateProjection(bool flip) { void OpenGl2Renderer::UpdateProjection() {
glViewport(0, 0, camera.width, camera.height); glViewport(0, 0, camera.width, camera.height);
double mat1[16]; double mat1[16];
@ -547,18 +547,13 @@ void OpenGl2Renderer::UpdateProjection(bool flip) {
// Last thing before display is to apply the perspective // Last thing before display is to apply the perspective
double clp = camera.tangent * camera.scale; double clp = camera.tangent * camera.scale;
double fy = flip ? -1.0 : 1.0;
MakeMatrix(mat2, MakeMatrix(mat2,
1, 0, 0, 0, 1, 0, 0, 0,
0, fy, 0, 0, 0, 1, 0, 0,
0, 0, 1, 0, 0, 0, 1, 0,
0, 0, clp, 1 0, 0, clp, 1
); );
// If we flip the framebuffer, then we also flip the handedness
// of the coordinate system, and so the face winding order.
glFrontFace(flip ? GL_CW : GL_CCW);
double projection[16]; double projection[16];
MultMatrix(mat1, mat2, projection); MultMatrix(mat1, mat2, projection);
@ -668,9 +663,9 @@ void OpenGl2Renderer::GetIdent(const char **vendor, const char **renderer, const
*version = (const char *)glGetString(GL_VERSION); *version = (const char *)glGetString(GL_VERSION);
} }
void OpenGl2Renderer::SetCamera(const Camera &c, bool flip) { void OpenGl2Renderer::SetCamera(const Camera &c) {
camera = c; camera = c;
UpdateProjection(flip); UpdateProjection();
} }
void OpenGl2Renderer::SetLighting(const Lighting &l) { void OpenGl2Renderer::SetLighting(const Lighting &l) {

View File

@ -149,8 +149,6 @@ enum class ContextCommand : uint32_t;
#define PATH_SEP "/" #define PATH_SEP "/"
#endif #endif
extern const bool FLIP_FRAMEBUFFER;
bool PathEqual(const std::string &a, const std::string &b); bool PathEqual(const std::string &a, const std::string &b);
std::string PathSepPlatformToUnix(const std::string &filename); std::string PathSepPlatformToUnix(const std::string &filename);
std::string PathSepUnixToPlatform(const std::string &filename); std::string PathSepUnixToPlatform(const std::string &filename);

View File

@ -865,8 +865,12 @@ void TextWindow::Paint() {
camera.offset.x = -(double)camera.width / 2.0; camera.offset.x = -(double)camera.width / 2.0;
camera.offset.y = -(double)camera.height / 2.0; camera.offset.y = -(double)camera.height / 2.0;
Lighting lighting = {};
lighting.backgroundColor = RGBi(0, 0, 0);
canvas->NewFrame(); canvas->NewFrame();
canvas->SetCamera(camera); canvas->SetCamera(camera);
canvas->SetLighting(lighting);
UiCanvas uiCanvas = {}; UiCanvas uiCanvas = {};
uiCanvas.canvas = canvas; uiCanvas.canvas = canvas;