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.
This commit is contained in:
parent
7d41e542fe
commit
c371fc5115
|
@ -44,6 +44,10 @@
|
||||||
#include "canvas/scroll_group.h"
|
#include "canvas/scroll_group.h"
|
||||||
#include "canvas/utils.h"
|
#include "canvas/utils.h"
|
||||||
|
|
||||||
|
#ifdef __APPLE__
|
||||||
|
#include "canvas/nsglview.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
using namespace ArdourCanvas;
|
using namespace ArdourCanvas;
|
||||||
|
|
||||||
|
@ -390,11 +394,18 @@ GtkCanvas::GtkCanvas ()
|
||||||
, current_tooltip_item (0)
|
, current_tooltip_item (0)
|
||||||
, tooltip_window (0)
|
, tooltip_window (0)
|
||||||
, _in_dtor (false)
|
, _in_dtor (false)
|
||||||
|
, _nsglview (0)
|
||||||
{
|
{
|
||||||
/* these are the events we want to know about */
|
/* these are the events we want to know about */
|
||||||
add_events (Gdk::BUTTON_PRESS_MASK | Gdk::BUTTON_RELEASE_MASK | Gdk::POINTER_MOTION_MASK |
|
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::SCROLL_MASK | Gdk::ENTER_NOTIFY_MASK | Gdk::LEAVE_NOTIFY_MASK |
|
||||||
Gdk::KEY_PRESS_MASK | Gdk::KEY_RELEASE_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
|
void
|
||||||
|
@ -755,6 +766,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
|
void
|
||||||
GtkCanvas::on_size_allocate (Gtk::Allocation& a)
|
GtkCanvas::on_size_allocate (Gtk::Allocation& a)
|
||||||
{
|
{
|
||||||
|
@ -771,6 +791,18 @@ GtkCanvas::on_size_allocate (Gtk::Allocation& a)
|
||||||
#ifdef OPTIONAL_CAIRO_IMAGE_SURFACE
|
#ifdef OPTIONAL_CAIRO_IMAGE_SURFACE
|
||||||
}
|
}
|
||||||
#endif
|
#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.
|
/** Handler for GDK expose events.
|
||||||
|
@ -783,6 +815,12 @@ GtkCanvas::on_expose_event (GdkEventExpose* ev)
|
||||||
if (_in_dtor) {
|
if (_in_dtor) {
|
||||||
return true;
|
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
|
#ifdef CANVAS_PROFILE
|
||||||
const int64_t start = g_get_monotonic_time ();
|
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 */
|
/** @return The visible area of the canvas, in window coordinates */
|
||||||
Rect
|
ArdourCanvas::Rect
|
||||||
GtkCanvas::visible_area () const
|
GtkCanvas::visible_area () const
|
||||||
{
|
{
|
||||||
return Rect (0, 0, get_allocation().get_width (), get_allocation().get_height ());
|
return Rect (0, 0, get_allocation().get_width (), get_allocation().get_height ());
|
||||||
|
|
|
@ -215,6 +215,8 @@ public:
|
||||||
bool on_enter_notify_event (GdkEventCrossing*);
|
bool on_enter_notify_event (GdkEventCrossing*);
|
||||||
bool on_leave_notify_event (GdkEventCrossing*);
|
bool on_leave_notify_event (GdkEventCrossing*);
|
||||||
|
|
||||||
|
void on_realize ();
|
||||||
|
|
||||||
bool button_handler (GdkEventButton *);
|
bool button_handler (GdkEventButton *);
|
||||||
bool motion_notify_handler (GdkEventMotion *);
|
bool motion_notify_handler (GdkEventMotion *);
|
||||||
bool deliver_event (GdkEvent *);
|
bool deliver_event (GdkEvent *);
|
||||||
|
@ -249,6 +251,8 @@ private:
|
||||||
bool really_start_tooltip_timeout ();
|
bool really_start_tooltip_timeout ();
|
||||||
|
|
||||||
bool _in_dtor;
|
bool _in_dtor;
|
||||||
|
|
||||||
|
void* _nsglview;
|
||||||
};
|
};
|
||||||
|
|
||||||
/** A GTK::Alignment with a GtkCanvas inside it plus some Gtk::Adjustments for
|
/** A GTK::Alignment with a GtkCanvas inside it plus some Gtk::Adjustments for
|
||||||
|
|
15
libs/canvas/canvas/nsglview.h
Normal file
15
libs/canvas/canvas/nsglview.h
Normal file
|
@ -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
|
269
libs/canvas/nsglview.mm
Normal file
269
libs/canvas/nsglview.mm
Normal file
|
@ -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)];
|
||||||
|
}
|
|
@ -3,6 +3,7 @@ from waflib.extras import autowaf as autowaf
|
||||||
from waflib import Options
|
from waflib import Options
|
||||||
from waflib import TaskGen
|
from waflib import TaskGen
|
||||||
import os
|
import os
|
||||||
|
import sys
|
||||||
|
|
||||||
# Version of this package (even if built as a child)
|
# Version of this package (even if built as a child)
|
||||||
MAJOR = '0'
|
MAJOR = '0'
|
||||||
|
@ -96,6 +97,9 @@ def build(bld):
|
||||||
obj.install_path = bld.env['LIBDIR']
|
obj.install_path = bld.env['LIBDIR']
|
||||||
obj.defines += [ 'PACKAGE="' + I18N_PACKAGE + '"' ]
|
obj.defines += [ 'PACKAGE="' + I18N_PACKAGE + '"' ]
|
||||||
|
|
||||||
|
if sys.platform == 'darwin':
|
||||||
|
obj.source += [ 'nsglview.mm']
|
||||||
|
|
||||||
# canvas unit-tests are outdated
|
# canvas unit-tests are outdated
|
||||||
if False and bld.env['BUILD_TESTS'] and bld.is_defined('HAVE_CPPUNIT'):
|
if False and bld.env['BUILD_TESTS'] and bld.is_defined('HAVE_CPPUNIT'):
|
||||||
unit_testobj = bld(features = 'cxx cxxprogram')
|
unit_testobj = bld(features = 'cxx cxxprogram')
|
||||||
|
|
Loading…
Reference in New Issue
Block a user