summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRobin Gareus <robin@gareus.org>2017-03-19 22:40:58 +0100
committerRobin Gareus <robin@gareus.org>2017-03-19 22:49:17 +0100
commitc371fc511500acada422cdcf93658fc25e5106de (patch)
tree519d15df59ab94c579beceac57aac348c08de329
parent7d41e542fe54feaa24fc87004df2b0276b9941c3 (diff)
Prepare NSView/OpenGL Canvas (to speed up rendering on [mac]OS[X]
This avoids Coregraphics (cairo_quartz_surface..) competely. The openGL texture bypasses CG's slow argb_image and CGSColorMask methods.
-rw-r--r--libs/canvas/canvas.cc40
-rw-r--r--libs/canvas/canvas/canvas.h4
-rw-r--r--libs/canvas/canvas/nsglview.h15
-rw-r--r--libs/canvas/nsglview.mm269
-rw-r--r--libs/canvas/wscript4
5 files changed, 331 insertions, 1 deletions
diff --git a/libs/canvas/canvas.cc b/libs/canvas/canvas.cc
index a72700b94a..70fd36e8b5 100644
--- a/libs/canvas/canvas.cc
+++ b/libs/canvas/canvas.cc
@@ -44,6 +44,10 @@
#include "canvas/scroll_group.h"
#include "canvas/utils.h"
+#ifdef __APPLE__
+#include "canvas/nsglview.h"
+#endif
+
using namespace std;
using namespace ArdourCanvas;
@@ -390,11 +394,18 @@ GtkCanvas::GtkCanvas ()
, current_tooltip_item (0)
, tooltip_window (0)
, _in_dtor (false)
+ , _nsglview (0)
{
/* these are the events we want to know about */
add_events (Gdk::BUTTON_PRESS_MASK | Gdk::BUTTON_RELEASE_MASK | Gdk::POINTER_MOTION_MASK |
Gdk::SCROLL_MASK | Gdk::ENTER_NOTIFY_MASK | Gdk::LEAVE_NOTIFY_MASK |
Gdk::KEY_PRESS_MASK | Gdk::KEY_RELEASE_MASK);
+
+#ifdef __APPLE__NotYetToDueGdkForeignViewMousePatch // XXX
+# ifndef __ppc__ // would need to flip RGBA <> RGBA
+ _nsglview = nsglview_create (this);
+# endif
+#endif
}
void
@@ -756,6 +767,15 @@ GtkCanvas::item_going_away (Item* item, Rect bounding_box)
}
void
+GtkCanvas::on_realize ()
+{
+ Gtk::EventBox::on_realize();
+#ifdef __APPLE__
+ nsglview_overlay (_nsglview, get_window()->gobj());
+#endif
+}
+
+void
GtkCanvas::on_size_allocate (Gtk::Allocation& a)
{
EventBox::on_size_allocate (a);
@@ -771,6 +791,18 @@ GtkCanvas::on_size_allocate (Gtk::Allocation& a)
#ifdef OPTIONAL_CAIRO_IMAGE_SURFACE
}
#endif
+
+#ifdef __APPLE__
+ if (_nsglview) {
+ gint xx, yy;
+ gtk_widget_translate_coordinates(
+ GTK_WIDGET(gobj()),
+ GTK_WIDGET(get_toplevel()->gobj()),
+ 0, 0, &xx, &yy);
+ nsglview_resize (_nsglview, xx, yy, a.get_width(), a.get_height());
+ }
+#endif
+
}
/** Handler for GDK expose events.
@@ -783,6 +815,12 @@ GtkCanvas::on_expose_event (GdkEventExpose* ev)
if (_in_dtor) {
return true;
}
+#ifdef __APPLE__
+ if (_nsglview) {
+ nsglview_queue_draw (_nsglview, ev->area.x, ev->area.y, ev->area.width, ev->area.height);
+ return true;
+ }
+#endif
#ifdef CANVAS_PROFILE
const int64_t start = g_get_monotonic_time ();
@@ -1154,7 +1192,7 @@ GtkCanvas::unfocus (Item* item)
}
/** @return The visible area of the canvas, in window coordinates */
-Rect
+ArdourCanvas::Rect
GtkCanvas::visible_area () const
{
return Rect (0, 0, get_allocation().get_width (), get_allocation().get_height ());
diff --git a/libs/canvas/canvas/canvas.h b/libs/canvas/canvas/canvas.h
index 7932705483..20adfa4c45 100644
--- a/libs/canvas/canvas/canvas.h
+++ b/libs/canvas/canvas/canvas.h
@@ -215,6 +215,8 @@ public:
bool on_enter_notify_event (GdkEventCrossing*);
bool on_leave_notify_event (GdkEventCrossing*);
+ void on_realize ();
+
bool button_handler (GdkEventButton *);
bool motion_notify_handler (GdkEventMotion *);
bool deliver_event (GdkEvent *);
@@ -249,6 +251,8 @@ private:
bool really_start_tooltip_timeout ();
bool _in_dtor;
+
+ void* _nsglview;
};
/** A GTK::Alignment with a GtkCanvas inside it plus some Gtk::Adjustments for
diff --git a/libs/canvas/canvas/nsglview.h b/libs/canvas/canvas/nsglview.h
new file mode 100644
index 0000000000..e18a0f68ab
--- /dev/null
+++ b/libs/canvas/canvas/nsglview.h
@@ -0,0 +1,15 @@
+#ifndef __CANVAS_NSGLVIEW_H__
+#define __CANVAS_NSGLVIEW_H__
+
+#include <gdk/gdk.h>
+
+namespace ArdourCanvas
+{
+ class GtkCanvas;
+
+ void* nsglview_create (GtkCanvas*);
+ void nsglview_overlay (void*, GdkWindow*);
+ void nsglview_resize (void*, int x, int y, int w, int h);
+ void nsglview_queue_draw (void*, int x, int y, int w, int h);
+}
+#endif
diff --git a/libs/canvas/nsglview.mm b/libs/canvas/nsglview.mm
new file mode 100644
index 0000000000..09d0ae1342
--- /dev/null
+++ b/libs/canvas/nsglview.mm
@@ -0,0 +1,269 @@
+/*
+ Copyright (C) 2011 Paul Davis
+ Copyright (C) 2012 David Robillard <http://drobilla.net>
+ Copyright (C) 2017 Robin Gareus <robin@gareus.org>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+/* include order matter due to apple defines */
+#include <gtkmm/window.h>
+
+#include "canvas/canvas.h"
+#include "canvas/utils.h"
+#include "canvas/nsglview.h"
+
+#include <gdk/gdkquartz.h>
+
+#include <OpenGL/gl.h>
+#import <Cocoa/Cocoa.h>
+
+__attribute__ ((visibility ("hidden")))
+@interface ArdourCanvasOpenGLView : NSOpenGLView
+{
+@private
+ unsigned int _texture_id;
+ int _width;
+ int _height;
+ Cairo::RefPtr<Cairo::ImageSurface> surf;
+ ArdourCanvas::GtkCanvas *gtkcanvas;
+}
+
+- (id) initWithFrame:(NSRect)frame;
+- (void) dealloc;
+- (void) set_ardour_canvas:(ArdourCanvas::GtkCanvas*)c;
+- (void) reshape;
+- (void) drawRect:(NSRect)rect;
+- (BOOL) canBecomeKeyWindow:(id)sender;
+- (BOOL) acceptsFirstResponder:(id)sender;
+
+@end
+
+@implementation ArdourCanvasOpenGLView
+
+- (id) initWithFrame:(NSRect)frame
+{
+ NSOpenGLPixelFormatAttribute pixelAttribs[16] = {
+ NSOpenGLPFADoubleBuffer,
+ NSOpenGLPFAAccelerated,
+ NSOpenGLPFAColorSize, 32,
+ NSOpenGLPFADepthSize, 32,
+ NSOpenGLPFAMultisample,
+ NSOpenGLPFASampleBuffers, 1,
+ NSOpenGLPFASamples, 4,
+ 0
+ };
+
+ NSOpenGLPixelFormat* pixelFormat =
+ [[NSOpenGLPixelFormat alloc] initWithAttributes:pixelAttribs];
+
+ if (pixelFormat) {
+ self = [super initWithFrame:frame pixelFormat:pixelFormat];
+ [pixelFormat release];
+ } else {
+ self = [super initWithFrame:frame];
+ }
+
+ _texture_id = 0;
+ _width = 0;
+ _height = 0;
+
+ if (self) {
+
+ [[self openGLContext] makeCurrentContext];
+ glClearColor (0.0f, 0.0f, 0.0f, 0.0f);
+ glDisable (GL_DEPTH_TEST);
+ glEnable (GL_BLEND);
+ glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+ glEnable (GL_TEXTURE_RECTANGLE_ARB);
+ [NSOpenGLContext clearCurrentContext];
+
+ [self reshape];
+ }
+
+ return self;
+}
+
+- (void) dealloc {
+ [[self openGLContext] makeCurrentContext];
+ glDeleteTextures (1, &_texture_id);
+ [NSOpenGLContext clearCurrentContext];
+
+ [super dealloc];
+}
+
+- (void) set_ardour_canvas:(ArdourCanvas::GtkCanvas*)c
+{
+ gtkcanvas = c;
+}
+
+- (BOOL) canBecomeKeyWindow:(id)sender{
+ return NO;
+}
+
+- (BOOL) acceptsFirstResponder:(id)sender{
+ return NO;
+}
+
+- (void) reshape
+{
+ [[self openGLContext] update];
+
+ NSRect bounds = [self bounds];
+ int width = bounds.size.width;
+ int height = bounds.size.height;
+
+ if (_width == width && _height == height) {
+ return;
+ }
+
+ [[self openGLContext] makeCurrentContext];
+
+ glViewport (0, 0, width, height);
+ glMatrixMode (GL_PROJECTION);
+ glLoadIdentity ();
+ glOrtho (-1.0f, 1.0f, -1.0f, 1.0f, -1.0f, 1.0f);
+
+ glClear (GL_COLOR_BUFFER_BIT);
+
+ glDeleteTextures (1, &_texture_id);
+ glGenTextures (1, &_texture_id);
+ glBindTexture (GL_TEXTURE_RECTANGLE_ARB, _texture_id);
+ glTexImage2D (GL_TEXTURE_RECTANGLE_ARB, 0, GL_RGBA8,
+ width, height, 0,
+ GL_BGRA, GL_UNSIGNED_BYTE, NULL);
+ glTexEnvi (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL);
+
+ glMatrixMode(GL_MODELVIEW);
+ glLoadIdentity();
+
+ [NSOpenGLContext clearCurrentContext];
+
+ _width = width;
+ _height = height;
+}
+
+- (void) drawRect:(NSRect)rect
+{
+ [[self openGLContext] makeCurrentContext];
+
+ glMatrixMode(GL_MODELVIEW);
+ glLoadIdentity();
+ glClear(GL_COLOR_BUFFER_BIT);
+
+ /* call back into GtkCanvas */
+
+ ArdourCanvas::Rect crect (rect.origin.x, rect.origin.y,
+ rect.size.width + rect.origin.x,
+ rect.size.height + rect.origin.y);
+
+ if (!surf || surf->get_width () != _width || surf->get_height() != _height) {
+ surf = Cairo::ImageSurface::create (Cairo::FORMAT_ARGB32, _width, _height);
+
+ crect.x0 = crect.y0 = 0;
+ crect.x1 = _width;
+ crect.y1 = _height;
+ }
+
+ Cairo::RefPtr<Cairo::Context> ctx = Cairo::Context::create (surf);
+
+ // TODO: check retina screen, scaling factor.
+ // cairo_surface_get_device_scale () or explicit scale
+
+ ctx->rectangle (crect.x0, crect.y0, crect.width(), crect.height());
+ ctx->clip_preserve ();
+ /* draw background color */
+ ArdourCanvas::set_source_rgba (ctx, gtkcanvas->background_color ());
+ ctx->fill ();
+
+ gtkcanvas->render (crect, ctx);
+
+ surf->flush ();
+ uint8_t* imgdata = surf->get_data ();
+
+ /* NOTE for big-endian (PPC), we'd need to flip byte-order
+ * RGBA <> RGBA for the texture.
+ * GtkCanvas does not use this nsview for PPC builds, yet
+ */
+
+ /* continue OpenGL */
+ glPushMatrix ();
+
+ glEnable(GL_TEXTURE_2D);
+ glBindTexture(GL_TEXTURE_RECTANGLE_ARB, _texture_id);
+ glTexImage2D(GL_TEXTURE_RECTANGLE_ARB, 0, GL_RGBA8,
+ _width, _height, /*border*/ 0,
+ GL_BGRA, GL_UNSIGNED_BYTE, imgdata);
+
+ glBegin(GL_QUADS);
+ glTexCoord2f( 0.0f, (GLfloat) _height);
+ glVertex2f(-1.0f, -1.0f);
+
+ glTexCoord2f((GLfloat) _width, (GLfloat) _height);
+ glVertex2f( 1.0f, -1.0f);
+
+ glTexCoord2f((GLfloat) _width, 0.0f);
+ glVertex2f( 1.0f, 1.0f);
+
+ glTexCoord2f( 0.0f, 0.0f);
+ glVertex2f(-1.0f, 1.0f);
+ glEnd();
+
+ glDisable(GL_TEXTURE_2D);
+ glPopMatrix();
+
+ ///
+
+ glFlush();
+ glSwapAPPLE();
+ [NSOpenGLContext clearCurrentContext];
+}
+
+@end
+
+
+void*
+ArdourCanvas::nsglview_create (GtkCanvas* canvas)
+{
+ ArdourCanvasOpenGLView* gl_view = [ArdourCanvasOpenGLView new];
+ if (!gl_view) {
+ return 0;
+ }
+ [gl_view set_ardour_canvas:canvas];
+ return gl_view;
+}
+
+void
+ArdourCanvas::nsglview_overlay (void* glv, GdkWindow* window)
+{
+ ArdourCanvasOpenGLView* gl_view = (ArdourCanvasOpenGLView*) glv;
+ NSView* view = gdk_quartz_window_get_nsview (window);
+ [view addSubview:gl_view];
+}
+
+void
+ArdourCanvas::nsglview_resize (void* glv, int x, int y, int w, int h)
+{
+ ArdourCanvasOpenGLView* gl_view = (ArdourCanvasOpenGLView*) glv;
+ [gl_view setFrame:NSMakeRect(x, y, w, h)];
+}
+
+void
+ArdourCanvas::nsglview_queue_draw (void* glv, int x, int y, int w, int h)
+{
+ ArdourCanvasOpenGLView* gl_view = (ArdourCanvasOpenGLView*) glv;
+ [gl_view setNeedsDisplayInRect:NSMakeRect(x, y, w, h)];
+}
diff --git a/libs/canvas/wscript b/libs/canvas/wscript
index 6294fe0d05..cdb6156f15 100644
--- a/libs/canvas/wscript
+++ b/libs/canvas/wscript
@@ -3,6 +3,7 @@ from waflib.extras import autowaf as autowaf
from waflib import Options
from waflib import TaskGen
import os
+import sys
# Version of this package (even if built as a child)
MAJOR = '0'
@@ -96,6 +97,9 @@ def build(bld):
obj.install_path = bld.env['LIBDIR']
obj.defines += [ 'PACKAGE="' + I18N_PACKAGE + '"' ]
+ if sys.platform == 'darwin':
+ obj.source += [ 'nsglview.mm']
+
# canvas unit-tests are outdated
if False and bld.env['BUILD_TESTS'] and bld.is_defined('HAVE_CPPUNIT'):
unit_testobj = bld(features = 'cxx cxxprogram')