new Cocoa graphics pipe is now fully functional

This commit is contained in:
rdb 2012-08-03 12:14:04 +00:00
parent dcdc6d9877
commit 1f0304c696
8 changed files with 174 additions and 81 deletions

View File

@ -21,6 +21,7 @@
#import <Foundation/NSAutoreleasePool.h>
#import <AppKit/NSApplication.h>
#import <AppKit/NSRunningApplication.h>
#include <mach-o/arch.h>
@ -31,9 +32,15 @@ static void init_app() {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
[NSApplication sharedApplication];
[NSApp setActivationPolicy:nil];
[NSApp setActivationPolicy:NSApplicationActivationPolicyRegular];
[NSApp finishLaunching];
[NSApp activateIgnoringOtherApps:YES];
// Put Cocoa into thread-safe mode
// by spawning a thread which immediately exits.
NSThread* thread = [[NSThread alloc] init];
[thread start];
[thread autorelease];
}
}
@ -264,7 +271,7 @@ pipe_constructor() {
////////////////////////////////////////////////////////////////////
GraphicsPipe::PreferredWindowThread
CocoaGraphicsPipe::get_preferred_window_thread() const {
// The NSView and NSWindow classes are not thread-safe,
// The NSView and NSWindow classes are not completely thread-safe,
// they can only be called from the main thread!
return PWT_app;
}
@ -309,8 +316,8 @@ make_output(const string &name,
return new CocoaGraphicsWindow(engine, this, name, fb_prop, win_prop,
flags, gsg, host);
}
/*
// Second thing to try: a GLES(2)GraphicsBuffer
// Second thing to try: a GLGraphicsBuffer
if (retry == 1) {
if ((host==0)||
// (!gl_support_fbo)||
@ -342,7 +349,7 @@ make_output(const string &name,
return new GLGraphicsBuffer(engine, this, name, fb_prop, win_prop,
flags, gsg, host);
}
/*
// Third thing to try: a CocoaGraphicsBuffer
if (retry == 2) {
if (((flags&BF_require_parasite)!=0)||

View File

@ -33,7 +33,7 @@ public:
NSOpenGLPixelFormat *pixel_format, int virtual_screen);
void choose_pixel_format(const FrameBufferProperties &properties,
CGDirectDisplayID display,
bool need_window, bool need_pbuffer);
bool need_pbuffer);
CocoaGraphicsStateGuardian(GraphicsEngine *engine, GraphicsPipe *pipe,
CocoaGraphicsStateGuardian *share_with);

View File

@ -17,6 +17,7 @@
#include "lightReMutexHolder.h"
#include <mach-o/dyld.h>
#import <AppKit/AppKit.h>
#import <OpenGL/CGLRenderers.h>
TypeHandle CocoaGraphicsStateGuardian::_type_handle;
@ -97,11 +98,16 @@ get_properties(FrameBufferProperties &properties, NSOpenGLPixelFormat* pixel_for
}
//TODO: add aux buffers
// Extract the renderer ID bits and check if our
// renderer matches the known software renderers.
renderer_id &= kCGLRendererIDMatchingMask;
if (renderer_id == kCGLRendererGenericID ||
renderer_id == kCGLRendererGenericFloatID ||
renderer_id == kCGLRendererAppleSWID) {
properties.set_force_software(1);
}
if (accelerated) {
properties.set_force_hardware(1);
}
@ -117,16 +123,16 @@ get_properties(FrameBufferProperties &properties, NSOpenGLPixelFormat* pixel_for
void CocoaGraphicsStateGuardian::
choose_pixel_format(const FrameBufferProperties &properties,
CGDirectDisplayID display,
bool need_window, bool need_pbuffer) {
bool need_pbuffer) {
_context = nil;
_fbprops.clear();
// Neither Cocoa nor CGL seem to have a mechanism to query the available
// pixel formats, unfortunately, so the only thing we can do is ask for one
// with the properties we have requested.
// pixel formats, unfortunately, so the only thing we can do is ask for
// one with the properties we have requested.
pvector<NSOpenGLPixelFormatAttribute> attribs;
attribs.reserve(13);
attribs.reserve(15);
// Picked this up from the pyglet source - seems
// to be necessary to support RAGE-II, which is not compliant.
@ -135,8 +141,9 @@ choose_pixel_format(const FrameBufferProperties &properties,
// Don't let it fall back to a different renderer.
attribs.push_back(NSOpenGLPFANoRecovery);
// Selection policy.
//attribs.push_back(NSOpenGLPFAMinimumPolicy);
// Consider pixel formats with properties equal
// to or better than we requested.
attribs.push_back(NSOpenGLPFAMinimumPolicy);
if (!properties.is_single_buffered()) {
attribs.push_back(NSOpenGLPFADoubleBuffer);
@ -151,13 +158,22 @@ choose_pixel_format(const FrameBufferProperties &properties,
attribs.push_back(aux_buffers);
attribs.push_back(NSOpenGLPFAColorSize);
attribs.push_back(properties.get_color_bits());
attribs.push_back(NSOpenGLPFAAlphaSize);
attribs.push_back(properties.get_alpha_bits());
attribs.push_back(NSOpenGLPFADepthSize);
attribs.push_back(properties.get_depth_bits());
attribs.push_back(NSOpenGLPFAStencilSize);
attribs.push_back(properties.get_stencil_bits());
// Curious case - if we request anything less than 8 alpha bits,
// then on some ATI cards, it will grab a pixel format with just
// 2 alpha bits, which just shows a white window and nothing else.
// Might have something to do with the compositing window manager.
// Omitting it altogether seems to make it grab one with 8 bits, though.
// Dirty hack. Needs more research.
if (properties.get_alpha_bits() > 0) {
attribs.push_back(NSOpenGLPFAAlphaSize);
attribs.push_back(max(8, properties.get_alpha_bits()));
}
if (properties.get_multisamples() > 0) {
attribs.push_back(NSOpenGLPFASampleBuffers);
attribs.push_back(1);
@ -167,19 +183,16 @@ choose_pixel_format(const FrameBufferProperties &properties,
}
if (properties.get_force_software()) {
// Request the generic software renderer.
attribs.push_back(NSOpenGLPFARendererID);
attribs.push_back(kCGLRendererAppleSWID);
attribs.push_back(kCGLRendererGenericFloatID);
}
if (properties.get_force_hardware()) {
attribs.push_back(NSOpenGLPFAAccelerated);
}
if (need_window) {
//TODO: when to request fullscreen on Cocoa?
attribs.push_back(NSOpenGLPFAWindow);
//attribs.push_back(NSOpenGLPFAFullScreen);
}
if (need_pbuffer) {
attribs.push_back(NSOpenGLPFAPixelBuffer);
@ -189,6 +202,7 @@ choose_pixel_format(const FrameBufferProperties &properties,
attribs.push_back(NSOpenGLPFAScreenMask);
attribs.push_back(CGDisplayIDToOpenGLDisplayMask(display));
// End of the array
attribs.push_back((NSOpenGLPixelFormatAttribute) nil);
// Create the format.
@ -199,8 +213,13 @@ choose_pixel_format(const FrameBufferProperties &properties,
return;
}
//XXX not sure what to do with virtual_screen, let's just set it to 0.
// For now, I'm just using the first virtual screen.
cocoadisplay_cat.debug() <<
"Pixel format has " << [format numberOfVirtualScreens] << " virtual screens.\n";
get_properties(_fbprops, format, 0);
//TODO: print out renderer
_context = [[NSOpenGLContext alloc] initWithFormat:format shareContext:_share_context];
[format release];
if (_context == nil) {
@ -230,6 +249,9 @@ query_gl_version() {
// where the GL version has been output, and it's nice to see the
// two of these together.
if (glgsg_cat.is_debug()) {
//XXX this is supposed to work, but the NSOpenGLGetVersion
// symbol cannot be found when I do this
//GLint major, minor;
//NSOpenGLGetVersion(&major, &minor);
@ -264,7 +286,7 @@ do_get_extension_func(const char *prefix, const char *name) {
return (void *) NSAddressOfSymbol(symbol);
}
cocoadisplay_cat.error() <<
cocoadisplay_cat.warning() <<
"do_get_extension_func failed for " << prefix << name << "!\n";
free(fullname);

View File

@ -56,7 +56,7 @@ public:
void handle_close_event();
void handle_key_event(NSEvent *event);
void handle_mouse_button_event(int button, bool down);
void handle_mouse_moved_event(bool in_window, int x, int y, bool absolute);
void handle_mouse_moved_event(bool in_window, double x, double y, bool absolute);
void handle_wheel_event(double x, double y);
INLINE NSWindow *get_nswindow() const;

View File

@ -39,7 +39,7 @@
#import <AppKit/NSImage.h>
#import <AppKit/NSScreen.h>
#import <OpenGL/OpenGL.h>
#import <Carbon/Carbon.h>
//#import <Carbon/Carbon.h>
TypeHandle CocoaGraphicsWindow::_type_handle;
@ -97,20 +97,35 @@ CocoaGraphicsWindow::
////////////////////////////////////////////////////////////////////
bool CocoaGraphicsWindow::
move_pointer(int device, int x, int y) {
//Hack! Will go away when we have floating-point mouse pos.
MouseData md = get_pointer(device);
if (md.get_x() == x && md.get_y() == y) {
return true;
}
if (device == 0) {
CGPoint point;
if (_properties.get_fullscreen()) {
point = CGPointMake(x, y);
point = CGPointMake(x, y + 1);
} else {
point = CGPointMake(x + _properties.get_x_origin(),
y + _properties.get_y_origin());
y + _properties.get_y_origin() + 1);
}
return (CGDisplayMoveCursorToPoint(_display, point) == kCGErrorSuccess);
// I don't know what the difference between these two methods is.
//if (CGWarpMouseCursorPosition(point) == kCGErrorSuccess) {
if (CGDisplayMoveCursorToPoint(_display, point) == kCGErrorSuccess) {
// Generate a mouse event.
NSPoint pos = [_window mouseLocationOutsideOfEventStream];
NSPoint loc = [_view convertPoint:pos fromView:nil];
BOOL inside = [_view mouse:loc inRect:[_view bounds]];
handle_mouse_moved_event(inside, loc.x, loc.y, true);
return true;
}
} else {
// No support for raw mice at the moment.
return false;
}
return false;
}
@ -145,13 +160,16 @@ begin_frame(FrameMode mode, Thread *current_thread) {
// Fullscreen.
[cocoagsg->_context setFullScreen];
} else {
nassertr([_view lockFocusIfCanDraw], false);
// Although not recommended, it is technically possible to
// use the same context with multiple different-sized windows.
// If that happens, the context needs to be updated accordingly.
if ([cocoagsg->_context view] != _view) {
//XXX I'm not 100% sure that changing the view requires it to update.
_context_needs_update = true;
[cocoagsg->_context setView:_view];
cocoadisplay_cat.spam()
<< "Switching context to view " << _view << "\n";
}
}
@ -161,6 +179,11 @@ begin_frame(FrameMode mode, Thread *current_thread) {
_context_needs_update = false;
}
// Lock the view for drawing.
if (!_properties.get_fullscreen()) {
nassertr([_view lockFocusIfCanDraw], false);
}
// Make the context current.
[cocoagsg->_context makeCurrentContext];
@ -191,6 +214,15 @@ end_frame(FrameMode mode, Thread *current_thread) {
end_frame_spam(mode);
nassertv(_gsg != (GraphicsStateGuardian *)NULL);
if (!_properties.get_fullscreen()) {
[_view unlockFocus];
}
// Release the context.
CocoaGraphicsStateGuardian *cocoagsg;
DCAST_INTO_V(cocoagsg, _gsg);
CGLUnlockContext((CGLContextObj) [cocoagsg->_context CGLContextObj]);
if (mode == FM_render) {
// end_render_texture();
copy_to_textures();
@ -221,12 +253,14 @@ end_flip() {
CocoaGraphicsStateGuardian *cocoagsg;
DCAST_INTO_V(cocoagsg, _gsg);
[cocoagsg->_context flushBuffer];
if (!_properties.get_fullscreen()) {
[_view unlockFocus];
}
CGLLockContext((CGLContextObj) [cocoagsg->_context CGLContextObj]);
// Swap the front and back buffer.
[cocoagsg->_context flushBuffer];
// Flush the window
[[_view window] flushWindow];
// Release the context.
CGLUnlockContext((CGLContextObj) [cocoagsg->_context CGLContextObj]);
}
GraphicsWindow::end_flip();
@ -247,7 +281,6 @@ process_events() {
GraphicsWindow::process_events();
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSEvent *event = nil;
while (true) {
@ -288,7 +321,7 @@ open_window() {
if (_gsg == 0) {
// There is no old gsg. Create a new one.
cocoagsg = new CocoaGraphicsStateGuardian(_engine, _pipe, NULL);
cocoagsg->choose_pixel_format(_fb_properties, cocoa_pipe->_display, true, false);
cocoagsg->choose_pixel_format(_fb_properties, cocoa_pipe->_display, false);
_gsg = cocoagsg;
} else {
// If the old gsg has the wrong pixel format, create a
@ -296,7 +329,7 @@ open_window() {
DCAST_INTO_R(cocoagsg, _gsg, false);
if (!cocoagsg->get_fb_properties().subsumes(_fb_properties)) {
cocoagsg = new CocoaGraphicsStateGuardian(_engine, _pipe, cocoagsg);
cocoagsg->choose_pixel_format(_fb_properties, cocoa_pipe->_display, true, false);
cocoagsg->choose_pixel_format(_fb_properties, cocoa_pipe->_display, false);
_gsg = cocoagsg;
}
}
@ -348,7 +381,7 @@ open_window() {
// Depending on whether the window handle comes from a Carbon or a Cocoa
// application, it could be either a HIViewRef or an NSView or NSWindow.
// Currently, we only support a Cocoa NSView, but we may in the future
// Currently, we only support a Cocoa NSView, but we could in the future
// add support for Carbon parents using HICocoaView.
if (os_handle->is_of_type(NativeWindowHandle::IntHandle::get_class_type())) {
@ -373,6 +406,8 @@ open_window() {
}
// Center the window if coordinates were set to -1 or -2
//TODO: perhaps in future, in the case of -1, it should use the origin
// used in a previous run of Panda
NSRect container;
if (parent_nsview != NULL) {
container = [parent_nsview bounds];
@ -547,8 +582,9 @@ open_window() {
// Make the context current.
_context_needs_update = false;
[cocoagsg->_context update];
[cocoagsg->_context makeCurrentContext];
[cocoagsg->_context setView:_view];
[cocoagsg->_context update];
cocoagsg->reset_if_new();
@ -821,8 +857,6 @@ set_properties_now(WindowProperties &properties) {
properties.clear_origin();
}
//TODO: mouse mode
if (properties.has_title() && _window != nil) {
_properties.set_title(properties.get_title());
[_window setTitle:[NSString stringWithUTF8String:properties.get_title().c_str()]];
@ -1224,10 +1258,6 @@ handle_minimize_event(bool minimized) {
return;
}
WindowProperties properties;
properties.set_minimized(minimized);
system_changed_properties(properties);
if (cocoadisplay_cat.is_debug()) {
if (minimized) {
cocoadisplay_cat.debug() << "Window was miniaturized\n";
@ -1235,6 +1265,10 @@ handle_minimize_event(bool minimized) {
cocoadisplay_cat.debug() << "Window was deminiaturized\n";
}
}
WindowProperties properties;
properties.set_minimized(minimized);
system_changed_properties(properties);
}
////////////////////////////////////////////////////////////////////
@ -1245,10 +1279,6 @@ handle_minimize_event(bool minimized) {
////////////////////////////////////////////////////////////////////
void CocoaGraphicsWindow::
handle_foreground_event(bool foreground) {
WindowProperties properties;
properties.set_foreground(foreground);
system_changed_properties(properties);
if (cocoadisplay_cat.is_debug()) {
if (foreground) {
cocoadisplay_cat.debug() << "Window became key\n";
@ -1257,6 +1287,10 @@ handle_foreground_event(bool foreground) {
}
}
WindowProperties properties;
properties.set_foreground(foreground);
system_changed_properties(properties);
if (foreground && _properties.get_mouse_mode() != WindowProperties::M_relative) {
// The mouse position may have changed during
// the time that we were not the key window.
@ -1500,7 +1534,7 @@ handle_mouse_button_event(int button, bool down) {
// Should only be called by CocoaPandaView.
////////////////////////////////////////////////////////////////////
void CocoaGraphicsWindow::
handle_mouse_moved_event(bool in_window, int x, int y, bool absolute) {
handle_mouse_moved_event(bool in_window, double x, double y, bool absolute) {
if (absolute) {
if (cocoadisplay_cat.is_spam()) {
if (in_window != _input_devices[0].get_pointer().get_in_window()) {
@ -1553,11 +1587,11 @@ handle_wheel_event(double x, double y) {
_input_devices[0].button_up(MouseButton::wheel_down());
}
//TODO: check if this is correct
//TODO: check if this is correct, I don't own a MacBook
if (x > 0.0) {
_input_devices[0].button_down(MouseButton::wheel_right());
_input_devices[0].button_up(MouseButton::wheel_right());
} else if (y < 0.0) {
} else if (x < 0.0) {
_input_devices[0].button_down(MouseButton::wheel_left());
_input_devices[0].button_up(MouseButton::wheel_left());
}
@ -1659,6 +1693,7 @@ map_function_key(unsigned short keycode) {
case NSUserFunctionKey:
case NSSystemFunctionKey:
case NSPrintFunctionKey:
break;
case NSClearLineFunctionKey:
return KeyboardButton::num_lock();
case NSClearDisplayFunctionKey:
@ -1673,6 +1708,7 @@ map_function_key(unsigned short keycode) {
case NSUndoFunctionKey:
case NSRedoFunctionKey:
case NSFindFunctionKey:
break;
case NSHelpFunctionKey:
return KeyboardButton::help();
case NSModeSwitchFunctionKey:

View File

@ -23,8 +23,13 @@ class CocoaGraphicsWindow;
}
- (id) initWithFrame:(NSRect)frameRect context:(NSOpenGLContext*)context window:(CocoaGraphicsWindow*)window;
- (NSOpenGLContext*) openGLContext;
- (GraphicsWindow*) graphicsWindow;
- (void) drawRect:(NSRect)dirtyRect;
- (void) finalize;
- (BOOL) isFlipped;
- (BOOL) needsDisplay;
- (BOOL) acceptsFirstResponder;
- (BOOL) becomeFirstResponder;
- (BOOL) resignFirstResponder;

View File

@ -21,6 +21,9 @@
- (id) initWithFrame:(NSRect)frameRect context:(NSOpenGLContext*)context window:(CocoaGraphicsWindow*)window {
self = [super initWithFrame: frameRect];
_context = context;
[self setCanDrawConcurrently:YES];
cocoadisplay_cat.debug()
<< "Created CocoaPandaView " << self << " for GraphicsWindow " << window << "\n";
_graphicsWindow = window;
@ -28,6 +31,21 @@
return self;
}
- (NSOpenGLContext*) openGLContext {
return _context;
}
- (GraphicsWindow*) graphicsWindow {
return _graphicsWindow;
}
- (void) drawRect:(NSRect)dirtyRect {
// Do nothing. We draw from another thread.
cocoadisplay_cat.spam()
<< "drawRect was called.\n";
}
- (void) finalize {
_graphicsWindow->handle_close_event();
[super finalize];
@ -39,6 +57,11 @@
return YES;
}
- (BOOL) needsDisplay {
// Never, we don't use drawRect. We draw from elsewhere.
return NO;
}
- (BOOL) acceptsFirstResponder {
return YES;
}
@ -59,7 +82,6 @@
- (void) setFrame: (NSRect) frame {
[super setFrame: frame];
//[_context update];
//_graphicsWindow->handle_resize_event();
}
@ -111,7 +133,7 @@
}
- (void) otherMouseDown: (NSEvent *) event {
// 2 and 3 are swapped, for consistency with X11 implementation
// 1 and 2 are swapped, for consistency with X11 implementation
if ([event buttonNumber] == 2) {
_graphicsWindow->handle_mouse_button_event(1, true);
} else {
@ -124,7 +146,7 @@
}
- (void) otherMouseUp: (NSEvent *) event {
// 2 and 3 are swapped, for consistency with X11 implementation
// 1 and 2 are swapped, for consistency with X11 implementation
if ([event buttonNumber] == 2) {
_graphicsWindow->handle_mouse_button_event(1, false);
} else {

View File

@ -29,6 +29,7 @@
[self setDelegate:delegate];
[self setOpaque:YES];
[self setReleasedWhenClosed:YES];
[self setAllowsConcurrentViewDrawing:YES];
// Necessary to be able to accept mouseMoved in the NSView
[self setAcceptsMouseMovedEvents:YES];