This document an outdated list  of options for how to to implement high-speed blits on OS X, as with PTB 'CopyWindow'  in OS 9.  In the end, the only thing that worked was to preconfigure one screen-sized segment of system memory for DMA block transfers and to implement Psychtoolbox textures as ordinary malloced system RAM.  To blit, we memcpy from the Psychtoolbox texture into the special DMA-configured region and then trigger the DMA block transfer from the special DMA block in system memory over the video bus to video RAM and the onscreen window.  The relevant Apple OpenGL extensions for setting up a DMA block transfer are shown in Apple's TextureRange demo.  Buried somewhere in that documentation it says that underneath the TextureRange stuff reduces to DMA transfers over the AGP bus.

It would be faster if we could DMA-enable the Psychtoolbox texture memory directly, and avoid the memcopy, but that didn't work because 1. If we try to enable it per texture on the fly immediately before the blit, it takes too long (longer than a blanking interval) 2. If we try to enable all of it, there is a limit of about 2x the video memory.  So we are stuck with a two-stage process, memcopy to  the DMA block then blit the DMA block.  In the future, we could make special provision for new types of blits in the Psychtoolbox, giving the user the choice between direct blits without the memcopy but with limited total texture memory, or slow blits with the mecopy step but texture memory limited only by system RAM.  

We tried hard to stick with offscreen windows calls in the Psychtoolbox by creating shadow textures, but performance sucked and for purposes of rapid blits the PTB supplies textures now.  Unlike QuickDraw which had one class of surface which could both be blitted and rendered on, OpenGL has two classes: The context and the texture.  We can issue drawing commmands to contexts but copying them is very slow. We can copy textures very quickly between surfaces, but we can not issue OpenGL drawing commands to them.  We implemented a system where contexts were shadowed by textures.  Drawing was rendered to the context then, with the Priority of Rush command, copied to the texture prior to movie start.  Flags tracked when the shadow surface was outdated and had to be updated by Rush or Priority.  This gave poor performance because of the (undocumented) memory limitation on textures held in system RAM, and that the copy from contexts to shadow textures by Rush or Priority took way too much time.  

Apple provides a render texture extension, which if they ever get around to documenting it, might be useful here.  It allows one to render into a texture.  Though its not clear if that will ever work in conjunction with the DMA block transfer, or only with textures held in video RAM.    

As matter of general policy trying to fake QuickDraw using OpenGL is  a bad idea.  The best policy (according to Allen) is to migrate all Psychtoolboxes (Win and Mac) to  OpenGL-style commands.

By the way, we tried the carbon copybits calls on OS X for the HIPS viewer.   Useless on OS X.  Slow transfer rates and unreliably and unverifiable video synching.

Allen W. Ingling
28 October 2004

Option A:

1. When we open an onscreen window create a shadow surface which is a glcontext.  Make it the same X & Y size as the onscreen surface and aligned with the onscreen surface.

2. When we open an offscreen window create a texture in system RAM.  If the offscreen window is larger than the onscreen window then replace the shadow surface with one larger. 

3. When we draw to the offscreen window first copy the texture to the shadow surface then issue the drawing command on the shadow surface context then copy it back to the texture. 

4.  When we copy window from offscreen windows to onscreen windows use the fast DMA blit. 

Problems are that this is ugly. 

notes:

to get the image from the texture to the shadow context just draw a texture rect.
to get the image from the shadow surface back to the texture either
	treat the texture as a block of memory and use glCopyPixels  
		or
	treat the texture as a texutre and use glCopyTexSubImage2D or   

Option A2: (the awesome one)

1. Offscreen windows can remain as gl contexts.

2. Provides  Screen commands 'TexturizeOffscreenWindow' window which converts the glcontext to a texture using glCopyTexImage and stores the texture in the window structure.  It sets a flag in the window structure "isTextureUpToDate" to indicate that the texture contents match the glcontext contents

3. If we then draw again to the glcontext we unset the flag

4. Copy window checks the  isTextureUpToDate flag.  If the texture is up to date it just copies the texture onto the designated surface.  If the texture is not up to date then it first texturizes the window and sets the isTextureUpToDate flag.

5. Rush and Priority call 'TexturizeOffscreenWindows'.  You get fast CopyWindow if you rush or prioritize and slow copies if you don't. 

Option A3: (less awesome but feasible)

Like A2, except we use glReadPixels instead of glCopyTexImage to move content into a texture.  This works because the texture has dual status both as client memory and as a texture and can be treated as either.

There is a problem using glCopyTexImage to move the image from the offscreen context to at texture and then using the texture to draw on the onscreen window.  That problem is that the texture has to be shared between contexts, and to share a texture between contexts the format has to be the same and there seems to be no way to do that except to 

Like A3 except use glReadPixels to instead of glCopyTexImage to move contents from offscreen gl context to the texture.  This treats the texture as plain old memory when it is loaded instead of treating it as a texture when it is loaded.  This should be ok.  It is unclear how to reload the texture, but the worst case is that we have to reallocated memory, which is not a problem, given that TexturizeOffscreenWindow can take forever.  

Option B:

Like A except:

3. When we draw to the offscreen window  issue the drawing command on the shadow surface then composite then back onto to the shadow surface when we transfer them instead of first copying the texture to the shadow window.    

Problems are that  glReadPixels seems not to do compositing.   Take a look at ARB_texture_env_combine and ATI_texture_env_combine3 extensions but it looks like more trouble than Option A.  

Option C: 

Grant the same block of memory which holds the texture status as a GL context.  

Problems are that the alignment of GL contexts is opaque.  Check the documentation one more time though.

Option D: 

1. For each offscreen window Create a gl context/texture using apple's shared pbuffer extension .  Draw into the context and render with the texture.  

Problems are that pbuffers are still poorly documented (see the "Carbon Pbuffer Shared" example) and its not clear if they are held in system RAM or video RAM.

References:

APPLE_client_storage:
http://oss.sgi.com/projects/ogl-sample/registry/APPLE/client_storage.txt

glCopyTexSubImage2D:
http://www.3dlabs.com/support/developer/GLmanpages/glcopytexsubimage2d.htm

APPLE_texture_range
http://developer.apple.com/opengl/extensions/apple_texture_range.html

GL_EXT_bgra
http://developer.apple.com/opengl/extensions.html#GL_EXT_bgra

GL_EXT_texture_rectangle
http://developer.apple.com/opengl/extensions.html#GL_EXT_texture_rectangle

To Do:

• Init new fields in the window record structure when we create the window.

• When we open an offscreen window, share the context with the screen context.  Insist (for now) that we open an onscreen context first.  Maybe later we could relax that requirement if the underlying CGL routines will support that.

• When we open an offscreen window, create a texture for it. 

• When we texturize an offscreen window, create the texture if it does not exist.  Copy the contents of the window to the texture.

* When we copy window, just draw a rect with the texture.