X-Git-Url: http://git.hungrycats.org/cgi-bin/gitweb.cgi?p=xscreensaver;a=blobdiff_plain;f=OSX%2FXScreenSaverGLView.m;h=f7a58c481bd4f04d2c287fde7092d5ac2e6d9ac1;hp=f0632bc9552f5a5775c151a4fb136c239b66d64b;hb=f8cf5ac7b2f53510f80a0eaf286a25298be17bfe;hpb=ec8d2b32b63649e6d32bdfb306eda062769af823 diff --git a/OSX/XScreenSaverGLView.m b/OSX/XScreenSaverGLView.m index f0632bc9..f7a58c48 100644 --- a/OSX/XScreenSaverGLView.m +++ b/OSX/XScreenSaverGLView.m @@ -1,4 +1,4 @@ -/* xscreensaver, Copyright (c) 2006-2011 Jamie Zawinski +/* xscreensaver, Copyright (c) 2006-2012 Jamie Zawinski * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided that @@ -20,7 +20,11 @@ #import "screenhackI.h" #import "xlockmoreI.h" -#import +#ifdef USE_IPHONE +# include "jwzgles.h" +#else +# import +#endif /* used by the OpenGL screen savers */ @@ -33,15 +37,6 @@ extern void check_gl_error (const char *type); @implementation XScreenSaverGLView -#if 0 -- (void) dealloc -{ - /* #### Do we have to destroy ogl_ctx? */ - [super dealloc]; -} -#endif - - - (void)stopAnimation { [super stopAnimation]; @@ -49,7 +44,9 @@ extern void check_gl_error (const char *type); // Without this, the GL frame stays on screen when switching tabs // in System Preferences. // +# ifndef USE_IPHONE [NSOpenGLContext clearCurrentContext]; +# endif // !USE_IPHONE } @@ -57,24 +54,23 @@ extern void check_gl_error (const char *type); - (void) prepareContext { if (ogl_ctx) { +#ifdef USE_IPHONE + [EAGLContext setCurrentContext:ogl_ctx]; +#else // !USE_IPHONE [ogl_ctx makeCurrentContext]; // check_gl_error ("makeCurrentContext"); [ogl_ctx update]; +#endif // !USE_IPHONE } } - (void) resizeContext { +# ifndef USE_IPHONE if (ogl_ctx) [ogl_ctx setView:self]; -} - - -- (void)drawRect:(NSRect)rect -{ - if (! ogl_ctx) - [super drawRect:rect]; +# endif // !USE_IPHONE } @@ -87,9 +83,156 @@ extern void check_gl_error (const char *type); - (void) setOglContext: (NSOpenGLContext *) ctx { ogl_ctx = ctx; + +# ifdef USE_IPHONE + [EAGLContext setCurrentContext: ogl_ctx]; + + double s = self.contentScaleFactor; + int w = s * [self frame].size.width; + int h = s * [self frame].size.height; + + if (gl_framebuffer) glDeleteFramebuffersOES (1, &gl_framebuffer); + if (gl_renderbuffer) glDeleteRenderbuffersOES (1, &gl_renderbuffer); + if (gl_depthbuffer) glDeleteRenderbuffersOES (1, &gl_depthbuffer); + + glGenFramebuffersOES (1, &gl_framebuffer); + glBindFramebufferOES (GL_FRAMEBUFFER_OES, gl_framebuffer); + + glGenRenderbuffersOES (1, &gl_renderbuffer); + glBindRenderbufferOES (GL_RENDERBUFFER_OES, gl_renderbuffer); + +// redundant? +// glRenderbufferStorageOES (GL_RENDERBUFFER_OES, GL_RGBA8_OES, w, h); + [ogl_ctx renderbufferStorage:GL_RENDERBUFFER_OES + fromDrawable:(CAEAGLLayer*)self.layer]; + + glFramebufferRenderbufferOES (GL_FRAMEBUFFER_OES, GL_COLOR_ATTACHMENT0_OES, + GL_RENDERBUFFER_OES, gl_renderbuffer); + + glGenRenderbuffersOES (1, &gl_depthbuffer); + // renderbufferStorage: must be called before this. + glBindRenderbufferOES (GL_RENDERBUFFER_OES, gl_depthbuffer); + glRenderbufferStorageOES (GL_RENDERBUFFER_OES, GL_DEPTH_COMPONENT16_OES, + w, h); + glFramebufferRenderbufferOES (GL_FRAMEBUFFER_OES, GL_DEPTH_ATTACHMENT_OES, + GL_RENDERBUFFER_OES, gl_depthbuffer); + + int err = glCheckFramebufferStatusOES (GL_FRAMEBUFFER_OES); + switch (err) { + case GL_FRAMEBUFFER_COMPLETE_OES: + break; + case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_OES: + NSAssert (0, @"framebuffer incomplete attachment"); + break; + case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT_OES: + NSAssert (0, @"framebuffer incomplete missing attachment"); + break; + case GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_OES: + NSAssert (0, @"framebuffer incomplete dimensions"); + break; + case GL_FRAMEBUFFER_INCOMPLETE_FORMATS_OES: + NSAssert (0, @"framebuffer incomplete formats"); + break; + case GL_FRAMEBUFFER_UNSUPPORTED_OES: + NSAssert (0, @"framebuffer unsupported"); + break; +/* + case GL_FRAMEBUFFER_UNDEFINED: + NSAssert (0, @"framebuffer undefined"); + break; + case GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER: + NSAssert (0, @"framebuffer incomplete draw buffer"); + break; + case GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER: + NSAssert (0, @"framebuffer incomplete read buffer"); + break; + case GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE: + NSAssert (0, @"framebuffer incomplete multisample"); + break; + case GL_FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS: + NSAssert (0, @"framebuffer incomplete layer targets"); + break; + */ + default: + NSAssert (0, @"framebuffer incomplete, unknown error 0x%04X", err); + break; + } + + check_gl_error ("setOglContext"); + +# endif // USE_IPHONE + [self resizeContext]; } +#ifdef USE_IPHONE ++ (Class) layerClass +{ + return [CAEAGLLayer class]; +} + + +/* On MacOS: drawRect does nothing, and animateOneFrame renders. + On iOS GL: drawRect does nothing, and animateOneFrame renders. + On iOS X11: drawRect renders, and animateOneFrame marks the view dirty. + */ +- (void)drawRect:(NSRect)rect +{ +} + + +- (void) animateOneFrame +{ + UIGraphicsPushContext (backbuffer); + [self render_x11]; + UIGraphicsPopContext(); +} + + +/* The backbuffer isn't actually used for GL programs, but it needs to + be there for X11 calls to not error out. However, nothing done with + X11 calls will ever show up! It all gets written into the backbuffer + and discarded. That's ok, though, because mostly it's just calls to + XClearWindow and housekeeping stuff like that. So we make a tiny one. + */ +- (void) createBackbuffer +{ + // Don't resize the X11 window to match rotation. + // Rotation and scaling are handled in GL. + // + NSRect f = [self frame]; + double s = self.contentScaleFactor; + backbuffer_size.width = (int) (s * f.size.width); + backbuffer_size.height = (int) (s * f.size.height); + + if (! backbuffer) { + CGColorSpaceRef cs = CGColorSpaceCreateDeviceRGB(); + int w = 8; + int h = 8; + backbuffer = CGBitmapContextCreate (NULL, w, h, + 8, w*4, cs, + kCGImageAlphaPremultipliedLast); + CGColorSpaceRelease (cs); + } +} + + +- (void) swapBuffers +{ + glBindRenderbufferOES (GL_RENDERBUFFER_OES, gl_renderbuffer); + [ogl_ctx presentRenderbuffer:GL_RENDERBUFFER_OES]; +} +# endif // USE_IPHONE + + +- (void)dealloc { + // ogl_ctx + // gl_framebuffer + // gl_renderbuffer + // gl_depthbuffer + [super dealloc]; +} + @end @@ -97,6 +240,16 @@ extern void check_gl_error (const char *type); */ +// redefine these now since they don't work when not inside an ObjC method. + +#undef NSAssert +#undef NSAssert1 +#undef NSAssert2 +#define NSAssert(CC,S) do { if (!(CC)) { NSLog(S); abort();}} while(0) +#define NSAssert1(CC,S,A) do { if (!(CC)) { NSLog(S,A); abort();}} while(0) +#define NSAssert2(CC,S,A,B) do { if (!(CC)) { NSLog(S,A,B);abort();}} while(0) + + /* Called by OpenGL savers using the XLockmore API. */ GLXContext * @@ -104,8 +257,12 @@ init_GL (ModeInfo *mi) { Window win = mi->window; XScreenSaverGLView *view = (XScreenSaverGLView *) jwxyz_window_view (win); + NSAssert1 ([view isKindOfClass:[XScreenSaverGLView class]], + @"wrong view class: %@", view); NSOpenGLContext *ctx = [view oglContext]; +# ifndef USE_IPHONE + if (!ctx) { NSOpenGLPixelFormatAttribute attrs[40]; @@ -149,10 +306,7 @@ init_GL (ModeInfo *mi) pixfmt = [[NSOpenGLPixelFormat alloc] initWithAttributes:attrs]; } - if (! pixfmt) { - NSLog (@"unable to create NSOpenGLPixelFormat"); - exit (1); - } + NSAssert (pixfmt, @"unable to create NSOpenGLPixelFormat"); ctx = [[NSOpenGLContext alloc] initWithFormat:pixfmt @@ -196,6 +350,38 @@ init_GL (ModeInfo *mi) check_gl_error ("init_GL"); +# else // USE_IPHONE + + EAGLContext *ogl_ctx = ctx; + if (!ogl_ctx) { + + Bool dbuf_p = + get_boolean_resource (mi->dpy, "doubleBuffer", "DoubleBuffer"); + + /* There seems to be no way to actually turn off double-buffering in + EAGLContext (e.g., no way to draw to the front buffer directly) + but if we turn on "retained backing" for non-buffering apps like + "pipes", at least the back buffer isn't auto-cleared on them. + */ + CAEAGLLayer *eagl_layer = (CAEAGLLayer *) view.layer; + eagl_layer.opaque = TRUE; + eagl_layer.drawableProperties = + [NSDictionary dictionaryWithObjectsAndKeys: + kEAGLColorFormatRGBA8, kEAGLDrawablePropertyColorFormat, + [NSNumber numberWithBool:!dbuf_p], kEAGLDrawablePropertyRetainedBacking, + nil]; + + ogl_ctx = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES1]; + } + + if (!ogl_ctx) + return 0; + [view setOglContext:ogl_ctx]; + + check_gl_error ("OES_init"); + +# endif // USE_IPHONE + // Caller expects a pointer to an opaque struct... which it dereferences. // Don't ask me, it's historical... static int blort = -1; @@ -209,8 +395,14 @@ void glXSwapBuffers (Display *dpy, Window window) { XScreenSaverGLView *view = (XScreenSaverGLView *) jwxyz_window_view (window); + NSAssert1 ([view isKindOfClass:[XScreenSaverGLView class]], + @"wrong view class: %@", view); +#ifndef USE_IPHONE NSOpenGLContext *ctx = [view oglContext]; if (ctx) [ctx flushBuffer]; // despite name, this actually swaps +#else /* USE_IPHONE */ + [view swapBuffers]; +#endif /* USE_IPHONE */ } /* Does nothing - prepareContext already did the work. @@ -254,6 +446,5 @@ check_gl_error (const char *type) default: e = buf; sprintf (buf, "unknown GL error %d", (int) i); break; } - NSLog (@"%s GL error: %s", type, e); - exit (1); + NSAssert2 (0, @"%s GL error: %s", type, e); }