0
votes

Context

I work in cognitive science, so the context for this question is hard to convey. Long-story short, we have an extant library that heavily depends on PyGame; we need to use SDL2 for a specific, experimental task.

Our library extends a base class, Exp when we design experiments; it inits PyGame, has a simple and well-tested preparatory sequence, then passes control to the experimenter to complete a set of abstract methods which constitute the experiment.

Goal: At the handoff, kill PyGame and switch to SDL2.

Problem: The script below (SDL2 script) works when used outside of the Exp environment. But when it's changed just the little but (Experiment Implementation), two things happen:

  1. the images that we're using SDL2 to load don't visibly draw (thought the SDL2 window does)
  2. an exception is raised: CALayer position contains NaN: [nan nan]

Current Suspect Pygame and SDL2 are competing for namespace into two places, as evidenced from this terminal output at the program's launch:

objc[34300]: Class SDLTranslatorResponder is implemented in both /Library/Frameworks/SDL.framework/Versions/A/SDL and /Library/Frameworks/SDL2.framework/SDL2. One of the two will be used. Which one is undefined.
objc[34300]: Class SDLApplication is implemented in both /Library/Frameworks/SDL2.framework/SDL2 and /Library/Python/2.7/site-packages/pygame/sdlmain_osx.so. One of the two will be used. Which one is undefined.

Below are the working external script and the small modifications made to implement it within our Exp context. Finally, the complete error out put is at the bottom.

Any advice on what this problem might be would be hugely appreciated!!!

SDL2 Script

import sdl2 
import sdl2.ext 
import numpy 
from PIL import Image 
import time 
import OpenGL.GL as gl
from scipy import misc
import os

stimDisplayRes = (1366,768) #pixel resolution of the stimDisplay    
imgs = []
img_path = "path/to/jpgs"
# this loop just grabs our images; works fine in both scripts
for x in range(1, 450):
    x = str(int(x * 2.5)).zfill(8)
    path = os.path.join(img_path, "{0}.jpg".format(x))
    imgs.append(numpy.array(Image.open(path)))


sdl2.SDL_Init(sdl2.SDL_INIT_VIDEO)
stimDisplay = sdl2.ext.Window("Experiment", size=stimDisplayRes,position=(0,0),flags=sdl2.SDL_WINDOW_OPENGL|sdl2.SDL_WINDOW_SHOWN| sdl2.SDL_WINDOW_FULLSCREEN_DESKTOP |sdl2.SDL_RENDERER_ACCELERATED | sdl2.SDL_RENDERER_PRESENTVSYNC)
glContext = sdl2.SDL_GL_CreateContext(stimDisplay.window)
gl.glMatrixMode(gl.GL_PROJECTION)
gl.glLoadIdentity()
gl.glOrtho(0, stimDisplayRes[0],stimDisplayRes[1], 0, 0, 1)
gl.glMatrixMode(gl.GL_MODELVIEW)
gl.glDisable(gl.GL_DEPTH_TEST)

sdl2.SDL_PumpEvents() # to show the windows

time.sleep(1)

def blitNumpy(numpyArray,xLoc,yLoc,xCentered=True,yCentered=True):
    gl.glEnable(gl.GL_BLEND)
    gl.glBlendFunc(gl.GL_SRC_ALPHA, gl.GL_ONE_MINUS_SRC_ALPHA)
    ID = gl.glGenTextures(1)
    gl.glBindTexture(gl.GL_TEXTURE_2D, ID)
    gl.glTexEnvi(gl.GL_TEXTURE_ENV, gl.GL_TEXTURE_ENV_MODE, gl.GL_REPLACE);
    gl.glTexParameterf(gl.GL_TEXTURE_2D, gl.GL_TEXTURE_WRAP_S, gl.GL_CLAMP)
    gl.glTexParameterf(gl.GL_TEXTURE_2D, gl.GL_TEXTURE_WRAP_T, gl.GL_CLAMP)
    gl.glTexParameterf(gl.GL_TEXTURE_2D, gl.GL_TEXTURE_MAG_FILTER, gl.GL_LINEAR)
    gl.glTexParameterf(gl.GL_TEXTURE_2D, gl.GL_TEXTURE_MIN_FILTER, gl.GL_LINEAR)
    if len(numpyArray.shape)==3: #no alpha channel
        gl.glTexImage2D( gl.GL_TEXTURE_2D , 0 , gl.GL_RGBA , numpyArray.shape[1] , numpyArray.shape[0] , 0 , gl.GL_RGB , gl.GL_UNSIGNED_BYTE , numpyArray )
    elif len(numpyArray.shape)==4: #alpha channel
        gl.glTexImage2D( gl.GL_TEXTURE_2D , 0 , gl.GL_RGBA , numpyArray.shape[1] , numpyArray.shape[0] , 0 , gl.GL_RGBA , gl.GL_UNSIGNED_BYTE , numpyArray )
    gl.glEnable(gl.GL_TEXTURE_2D)
    gl.glBindTexture(gl.GL_TEXTURE_2D, ID)
    gl.glBegin(gl.GL_QUADS)
    x1 = xLoc + 1.5 - 0.5
    x2 = xLoc + numpyArray.shape[1] - 0.0 + 0.5
    y1 = yLoc + 1.0 - 0.5
    y2 = yLoc + numpyArray.shape[0] - 0.5 + 0.5
    if xCentered:
        x1 = x1 - numpyArray.shape[1]/2.0
        x2 = x2 - numpyArray.shape[1]/2.0
    if yCentered:
        y1 = y1 - numpyArray.shape[0]/2.0
        y2 = y2 - numpyArray.shape[0]/2.0
    gl.glTexCoord2f( 0 , 0 )
    gl.glVertex2f( x1 , y1 )
    gl.glTexCoord2f( 1 , 0 )
    gl.glVertex2f( x2 , y1 )
    gl.glTexCoord2f( 1 , 1)
    gl.glVertex2f( x2 , y2 )
    gl.glTexCoord2f( 0 , 1 )
    gl.glVertex2f( x1, y2 )
    gl.glEnd()
    gl.glBindTexture(gl.GL_TEXTURE_2D, 0)
    gl.glDeleteTextures([ID])
    del ID
    gl.glDisable(gl.GL_TEXTURE_2D)
    return None

# this is a little loop we're using to get our "frame rate" right; if this is made to work, I'm good from here
i = 0
j = 0
start = time.time()
while time.time()-start<10:
    time.sleep(.01) #pretend to do 5ms of other work per frame
    gl.glClearColor(0,0,0,1)
    gl.glClear(gl.GL_COLOR_BUFFER_BIT)
    blitNumpy(imgs[i],0,0,xCentered=False,yCentered=False)
    sdl2.SDL_GL_SwapWindow(stimDisplay.window)
    i+= 1
    if i == len(imgs):
        i=0
    j += 1

print j/(time.time()-start) # we just happen to need this metric

Experiment Implementation

I'm not reposting the entire script; I literally change nothing but what's added here:

class Experiment(ExpLib.Exp):
    first_run = True  # 
    # bunch of methods that don't interact with either PyGame or SDL2—science stuff

    def trial(self, trial_factors, trial_num):
        if self.first_run:
            pygame.quit()
            self.first_run = False

        time.sleep(0.1)  # maybe unnecessary; just giving PyGame a chance to be fully shut down

        # this next call wraps the script above; the only difference is that 
        # blitNumpy becomes self.blitNumpy, and our FPS loop
        self.sdl_trial()  

        # the experiment's code will go here if I can get this to fly

Console Output

2014-10-16 15:15:18.366 Python[34300:d07] An uncaught exception was raised 2014-10-16 15:15:18.366 Python[34300:d07] CALayer position contains NaN: [nan nan] 2014-10-16 15:15:18.366 Python[34300:d07] ( 0 CoreFoundation 0x00007fff8fba325c exceptionPreprocess + 172 1 libobjc.A.dylib 0x00007fff86dc3e75 objc_exception_throw + 43 2 CoreFoundation
0x00007fff8fba310c +[NSException raise:format:] + 204 3 QuartzCore 0x00007fff84cc425e _ZN2CA5Layer12set_positionERKNS_4Vec2IdEEb + 158 4 QuartzCore 0x00007fff84cc41b7 -[CALayer setPosition:] + 44 5 QuartzCore
0x00007fff84cc5e97 -[CALayer setFrame:] + 858 6 AppKit
0x00007fff8e5e837e -[_NSFullScreenTransitionOverlayWindow positionLayers] + 1790 7 AppKit
0x00007fff8e5e8e61 -[_NSFullScreenTransitionOverlayWindow startEnterFullScreenAnimationWithDuration:completionHandler:] + 55 8 AppKit 0x00007fff8e5ead76 -[_NSFullScreenTransition _startFullScreenTransitionForCGWindow:targetFrame:duration:completionHandler:] + 193 9 AppKit 0x00007fff8e5ecca6 __89-[_NSFullScreenTransition _performEnterFullScreenModeAnimating:activating:customWindows:]_block_invoke361 + 1430 10 libxpc.dylib 0x00007fff84eafca2 _xpc_connection_reply_callout + 47 11 libxpc.dylib 0x00007fff84eafc2e _xpc_connection_call_reply + 36 12 libdispatch.dylib 0x00007fff869ab2ad _dispatch_client_callout + 8 13 libdispatch.dylib 0x00007fff869b2f03 _dispatch_main_queue_callback_4CF + 333 14 CoreFoundation 0x00007fff8fb0a679 __CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE
+ 9 15 CoreFoundation 0x00007fff8fac5954 CFRunLoopRun + 1636 16 CoreFoundation 0x00007fff8fac50b5 CFRunLoopRunSpecific + 309 17 HIToolbox
0x00007fff85ab9a0d RunCurrentEventLoopInMode + 226 18 HIToolbox
0x00007fff85ab97b7 ReceiveNextEventCommon + 479 19 HIToolbox
0x00007fff85ab95bc _BlockUntilNextEventMatchingListInModeWithFilter + 65 20 AppKit 0x00007fff8dd293de _DPSNextEvent + 1434 21 AppKit 0x00007fff8dd28a2b -[NSApplication nextEventMatchingMask:untilDate:inMode:dequeue:] + 122 22 SDL
0x000000011023243c SDL_SoftStretch + 7888 23 SDL
0x0000000110211efd SDL_PumpEvents + 38 24 SDL
0x00000001102120dd SDL_EventState + 200 25 SDL
0x00000001102303f6 SDL_JoystickEventState + 82 26 joystick.so
0x000000011040313d joy_autoinit + 42 27 Python
0x000000010ff18f72 PyObject_Call + 101 28 Python
0x000000010ff9501f PyEval_CallObjectWithKeywords + 93 29 base.so
0x0000000110207c47 init + 279 30 Python
0x000000010ff921ef PyEval_EvalFrameEx + 12338 31 Python
0x000000010ff8f093 PyEval_EvalCodeEx + 1641 32 Python
0x000000010ff36796 PyFunction_SetClosure + 809 33 Python
0x000000010ff18f72 PyObject_Call + 101 34 Python
0x000000010ff239a7 PyMethod_New + 1200 35 Python
0x000000010ff18f72 PyObject_Call + 101 36 Python
0x000000010ff92df5 PyEval_EvalFrameEx + 15416 37 Python
0x000000010ff8f093 PyEval_EvalCodeEx + 1641 38 Python
0x000000010ff36796 PyFunction_SetClosure + 809 39 Python
0x000000010ff18f72 PyObject_Call + 101 40 Python
0x000000010ff239a7 PyMethod_New + 1200 41 Python
0x000000010ff18f72 PyObject_Call + 101 42 Python
0x000000010ff5e6ce _PyObject_SlotCompare + 5565 43 Python
0x000000010ff5a184 _PyType_Lookup + 1343 44 Python
0x000000010ff18f72 PyObject_Call + 101 45 Python
0x000000010ff92df5 PyEval_EvalFrameEx + 15416 46 Python
0x000000010ff8f093 PyEval_EvalCodeEx + 1641 47 Python
0x000000010ff958c8 _PyEval_SliceIndex + 929 48 Python
0x000000010ff924d4 PyEval_EvalFrameEx + 13079 49 Python
0x000000010ff8f093 PyEval_EvalCodeEx + 1641 50 Python
0x000000010ff36796 PyFunction_SetClosure + 809 51 Python
0x000000010ff18f72 PyObject_Call + 101 52 Python
0x000000010ff93395 PyEval_EvalFrameEx + 16856 53 Python
0x000000010ff8f093 PyEval_EvalCodeEx + 1641 54 Python
0x000000010ff958c8 _PyEval_SliceIndex + 929 55 Python
0x000000010ff924d4 PyEval_EvalFrameEx + 13079 56 Python
0x000000010ff8f093 PyEval_EvalCodeEx + 1641 57 Python
0x000000010ff36796 PyFunction_SetClosure + 809 58 Python
0x000000010ff18f72 PyObject_Call + 101 59 Python
0x000000010ff93395 PyEval_EvalFrameEx + 16856 60 Python
0x000000010ff8f093 PyEval_EvalCodeEx + 1641 61 Python
0x000000010ff958c8 _PyEval_SliceIndex + 929 62 Python
0x000000010ff924d4 PyEval_EvalFrameEx + 13079 63 Python
0x000000010ff8f093 PyEval_EvalCodeEx + 1641 64 Python
0x000000010ff8ea24 PyEval_EvalCode + 54 65 Python
0x000000010ffadc2c PyParser_ASTFromFile + 306 66 Python
0x000000010ffadcd3 PyRun_FileExFlags + 137 67 Python
0x000000010ffad821 PyRun_SimpleFileExFlags + 718 68 Python
0x000000010ffbe363 Py_Main + 2995 69 libdyld.dylib
0x00007fff89e375fd start + 1 70 ???
0x0000000000000002 0x0 + 2 ) 2014-10-16 15:15:18.367 Python[34300:d07] *** Terminating app due to uncaught exception 'CALayerInvalidGeometry', reason: 'CALayer position contains NaN: [nan nan]' *** First throw call stack: ( 0 CoreFoundation 0x00007fff8fba325c __exceptionPreprocess + 172 1 libobjc.A.dylib
0x00007fff86dc3e75 objc_exception_throw + 43 2 CoreFoundation
0x00007fff8fba310c +[NSException raise:format:] + 204 3 QuartzCore 0x00007fff84cc425e _ZN2CA5Layer12set_positionERKNS_4Vec2IdEEb + 158 4 QuartzCore 0x00007fff84cc41b7 -[CALayer setPosition:] + 44 5 QuartzCore
0x00007fff84cc5e97 -[CALayer setFrame:] + 858 6 AppKit
0x00007fff8e5e837e -[_NSFullScreenTransitionOverlayWindow positionLayers] + 1790 7 AppKit
0x00007fff8e5e8e61 -[_NSFullScreenTransitionOverlayWindow startEnterFullScreenAnimationWithDuration:completionHandler:] + 55 8 AppKit 0x00007fff8e5ead76 -[_NSFullScreenTransition _startFullScreenTransitionForCGWindow:targetFrame:duration:completionHandler:] + 193 9 AppKit 0x00007fff8e5ecca6 __89-[_NSFullScreenTransition _performEnterFullScreenModeAnimating:activating:customWindows:]_block_invoke361 + 1430 10 libxpc.dylib 0x00007fff84eafca2 _xpc_connection_reply_callout + 47 11 libxpc.dylib 0x00007fff84eafc2e _xpc_connection_call_reply + 36 12 libdispatch.dylib 0x00007fff869ab2ad _dispatch_client_callout + 8 13 libdispatch.dylib 0x00007fff869b2f03 _dispatch_main_queue_callback_4CF + 333 14 CoreFoundation 0x00007fff8fb0a679 __CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE
+ 9 15 CoreFoundation 0x00007fff8fac5954 __CFRunLoopRun + 1636 16 CoreFoundation 0x00007fff8fac50b5 CFRunLoopRunSpecific + 309 17 HIToolbox
0x00007fff85ab9a0d RunCurrentEventLoopInMode + 226 18 HIToolbox
0x00007fff85ab97b7 ReceiveNextEventCommon + 479 19 HIToolbox
0x00007fff85ab95bc _BlockUntilNextEventMatchingListInModeWithFilter + 65 20 AppKit 0x00007fff8dd293de _DPSNextEvent + 1434 21 AppKit 0x00007fff8dd28a2b -[NSApplication nextEventMatchingMask:untilDate:inMode:dequeue:] + 122 22 SDL
0x000000011023243c SDL_SoftStretch + 7888 23 SDL
0x0000000110211efd SDL_PumpEvents + 38 24 SDL
0x00000001102120dd SDL_EventState + 200 25 SDL
0x00000001102303f6 SDL_JoystickEventState + 82 26 joystick.so
0x000000011040313d joy_autoinit + 42 27 Python
0x000000010ff18f72 PyObject_Call + 101 28 Python
0x000000010ff9501f PyEval_CallObjectWithKeywords + 93 29 base.so
0x0000000110207c47 init + 279 30 Python
0x000000010ff921ef PyEval_EvalFrameEx + 12338 31 Python
0x000000010ff8f093 PyEval_EvalCodeEx + 1641 32 Python
0x000000010ff36796 PyFunction_SetClosure + 809 33 Python
0x000000010ff18f72 PyObject_Call + 101 34 Python
0x000000010ff239a7 PyMethod_New + 1200 35 Python
0x000000010ff18f72 PyObject_Call + 101 36 Python
0x000000010ff92df5 PyEval_EvalFrameEx + 15416 37 Python
0x000000010ff8f093 PyEval_EvalCodeEx + 1641 38 Python
0x000000010ff36796 PyFunction_SetClosure + 809 39 Python
0x000000010ff18f72 PyObject_Call + 101 40 Python
0x000000010ff239a7 PyMethod_New + 1200 41 Python
0x000000010ff18f72 PyObject_Call + 101 42 Python
0x000000010ff5e6ce _PyObject_SlotCompare + 5565 43 Python
0x000000010ff5a184 _PyType_Lookup + 1343 44 Python
0x000000010ff18f72 PyObject_Call + 101 45 Python
0x000000010ff92df5 PyEval_EvalFrameEx + 15416 46 Python
0x000000010ff8f093 PyEval_EvalCodeEx + 1641 47 Python
0x000000010ff958c8 _PyEval_SliceIndex + 929 48 Python
0x000000010ff924d4 PyEval_EvalFrameEx + 13079 49 Python
0x000000010ff8f093 PyEval_EvalCodeEx + 1641 50 Python
0x000000010ff36796 PyFunction_SetClosure + 809 51 Python
0x000000010ff18f72 PyObject_Call + 101 52 Python
0x000000010ff93395 PyEval_EvalFrameEx + 16856 53 Python
0x000000010ff8f093 PyEval_EvalCodeEx + 1641 54 Python
0x000000010ff958c8 _PyEval_SliceIndex + 929 55 Python
0x000000010ff924d4 PyEval_EvalFrameEx + 13079 56 Python
0x000000010ff8f093 PyEval_EvalCodeEx + 1641 57 Python
0x000000010ff36796 PyFunction_SetClosure + 809 58 Python
0x000000010ff18f72 PyObject_Call + 101 59 Python
0x000000010ff93395 PyEval_EvalFrameEx + 16856 60 Python
0x000000010ff8f093 PyEval_EvalCodeEx + 1641 61 Python
0x000000010ff958c8 _PyEval_SliceIndex + 929 62 Python
0x000000010ff924d4 PyEval_EvalFrameEx + 13079 63 Python
0x000000010ff8f093 PyEval_EvalCodeEx + 1641 64 Python
0x000000010ff8ea24 PyEval_EvalCode + 54 65 Python
0x000000010ffadc2c PyParser_ASTFromFile + 306 66 Python
0x000000010ffadcd3 PyRun_FileExFlags + 137 67 Python
0x000000010ffad821 PyRun_SimpleFileExFlags + 718 68 Python
0x000000010ffbe363 Py_Main + 2995 69 libdyld.dylib
0x00007fff89e375fd start + 1 70 ???
0x0000000000000002 0x0 + 2 ) libc++abi.dylib: terminating with uncaught exception of type NSException

Process finished with exit code 13

1
you are loading both SDL1 and SDL2; you dont want to do that. - Brad Allred
Yeah. I know. I was hoping someone whose ability to nerd outpaces my own (easy enough to do) might drum up a hack or work-around of some sort. Alas. - Jonline
the only "hacks" to get around this UB would require as much work as just updating to pygame2 and ditching SDL1, which is what you really need to do. - Brad Allred
Yeah, I'm conceding that. All the sighs. - Jonline

1 Answers

0
votes

After consulting some in-house nerds, who confirmed Brad Allred's comment to my question, the solution is: using PyGame and SDL2 at the same time will essentially always require more work than replacing PyGame with SDL2. Don't do it.

Leaving my question just in case it ever proves to help.