File: SCREENTestTexture.c

package info (click to toggle)
psychtoolbox-3 3.0.9%2Bsvn2579.dfsg1-1
  • links: PTS, VCS
  • area: main
  • in suites: wheezy
  • size: 63,408 kB
  • sloc: ansic: 73,310; cpp: 11,139; objc: 3,129; sh: 1,669; python: 382; php: 272; makefile: 172; java: 113
file content (236 lines) | stat: -rwxr-xr-x 14,310 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
/*
  SCREENTestTexture.c	
  
  AUTHORS:
  Allen.Ingling@nyu.edu		awi 
  
  PLATFORMS:	All
    

  HISTORY:
  10/13/03  awi		Created.    
 
  DESCRIPTION:

  10/15/03
  
  Experimental SCREEN subfunction for testing out texture blitting using apple's AGP texture extension to get DMA transfers 
  from system RAM to VRAME over the AGP bus.  This should supplant the technique used by 'CopyWindow' now which is inelegant and slow.    
  
  Previously offscreen windows were created by passing CGLCreateContext() a block of memory and a pixel format.  The problem with
  CGLCreateContext() is that that there no fast way to transfer the contents of the context wich it creates to VRAM quickly because the packing
  format and alignment of the GL context created by CGLCreateContext() is opaque.  Using CGLCreateContext it not possible to create a GL 
  context in system RAM which was aligned with the packing format and alinment of VRAM.  The only way to access the images stored in the offscreen 
  context through glReadPixels wich copies from the the glContext memory to another block of applicatin memory where images whould are held in a 
  known format.  The method for copying from offscreen psychtoolbox windows to onscreen psychtoolbox windows was to allocated a temporary memory
  buffer the size of the copy block, use glReadPixels to copy the image from the offscreen context to the temporary buffer, then use glDrawPixels
  to copy from the temporary buffer to an onscreen glContext.  Well this is bad for several reasons:  it requires two copy operations, 
  it requires that the buffer be allocated, glReadPixels and glDrawPixels are slow because source and destinations are packed and aligned
  differently.  
  
  Geoff Stahl <gstahl@apple.com> suggested that I could used the combination of Apple GL  extensions  demonstrated in Apple's TextRange demo
  to get block DMA transfers directly from system RAM to video RAM.   We test that out here.  
  
  Now Psychtoolbox offscreen windows will be copied as GL textures instead of as offscreen gl contexts created using GLCreateContext().
  Usually when defining a texture using glTexImage2D() an application passes a pointer to a block of memory holding an image
  and GL copies the contents into its own block of texture memory, typically VRAM.  But with Appe's GL texture extensions,  an application
  passes glTexImage2D a pointer to a block of memory holding an image and GL instead of copinging the contents into a separate texture memory  
  GL retains the pointer and defines the texture to be the contents of that block of application memory specified to GLCreateContext().  The memory
  holding the texture thus has dual status, it is both a GL texture and it is a block of application memory.  
  
  Something unclear from Apple's documentation is how to declare that block of applciation memory to be a GL context if it will be given to
  glTexImage2D() to create a texture.  The TextureRange example does not demonstrate this.  GLCreateContext() will not work for this purpose because
  the pixel packing formats known to glTexImage2D() and GLCreateContext() functions must agree, yet the  GLCreateContext() pixel packing format is
  opaque.  
  
  It seems that CGBitmapContextCreate() unlike GLCreateContext() allows for specifying the packing format when creating the 
  GL rendering context, so 'TestTexture' uses CGBitmapContextCreate().  Note that Quartz will render text into a CGBitmapContext
  which probably how to implement Psychtoolbox's 'DrawText'.
  
  There are three levels of optimization necessary to obtain the DMA transfers and not all video boards support all three levels.
  This demo assumes all levels are available without checking.   It  should work with Radeaon 9000 on OS X 10.2.8. 
  
  10/16/03
  
  It turns out that there is no way to set the glContext to be a cgContext.  There are CGContextRef surfaces created with CGBitmapContextCreate() and there are
  CGLContextObj surfaces created with CGLCreateContext().  There is a way to set the gl context to a CGLContext object, CGLSetCurrentContext().  There is apparently 
  no way to set the gl context to a cgContextRef.  cgContextRef are for Quartz, CGLContextObj are for OpenGL.  I made the mistake and confusing the two.  The consequences
   are 1) This demo will only blit text and will not blit anything rendered with gl. This is still useful. 2) We either need to find a way to set assign a block of client 
   memory double status as both a glContext and a texture or else create two blocks of memory for offscreen windows, a CGContextRef which is the texture and CGLContextObj
   which is the GL rendering surface and we render into the CGLContextObj and copy it to the CGContextRef, which is the gl texture.  This is ugly and slower than ideal
   but only impacts the speed of creating and drawing into Psychtoolbox offscree windows, which is not time critical.  Finish the SCREENTestTexture demo, once that
   works post to the Mac gl mailing list and ask about this.   
  
  
  RESOURCES:
  Apple's TextureRange example.
  http://developer.apple.com/samplecode/Sample_Code/Graphics_3D/TextureRange.htm
  
  Core Graphcs API used to crete Core Graphics bitmap contexts.
    http://developer.apple.com/documentation/GraphicsImaging/Reference/Quartz_2D_Ref/index.html?http://developer.apple.com/documentation/GraphicsImaging/Reference/Quartz_2D_Ref/qref_main/function_group_1.html#//apple_ref/c/func/CGPDFContextCreate  
  

*/


#include "Screen.h"


static char useString[] = "copySpeed=SCREEN('TestTexture', windowPntr);";
static char synopsisString[] = 
	"Test out textures for implementing  offscreen windows.";
static char seeAlsoString[] = "";

//try to make the surface to double duty as a CG surface for quartz rendering surface and also as a GL surface. Otherwise poke the surface bits directly
//with specified fill values
#define useQuartz			1
#define redFill				0xFF
#define greenFill			0x00
#define blueFill			0X00
#define alphaFill			0XFF

//Specify the dimensions of the texture in units of texels.   GL's  power  rule (dimensions of a texture must be 2^n x 2^m, where n 
//and m are both positive integers) does not apply because the texture is GL_TEXTURE_RECTANGLE_EXTENSION and not GL_TEXTURE_2D.    
//#define textureSizeX			512
//#define textureSizeY			512
#define textureSizeX			600
#define textureSizeY			600


//Specify the string want to display and its position within the offscreen window/texture.
#define textPositionX			200
#define textPositionY			200  
#define textString			"Hello"

//Specify constants for creating the texture and for creating the CG context.  Conceptuall these are separate entities but 
//by use of Apple's GL extensions are embodied by the same block of RAM.  The pixel packing format of VRAM is fixed and from that follows 
//choices of packing formats for textures held in application memory and also the CG context emobied by the identical block of RAM.  

//Specify arguments to glTexImage2D when creating a texture to be held in client RAM. The choices are dictated  by our use of Apple's 
//GL_UNPACK_CLIENT_STORAGE_APPLE constant, an argument to glPixelStorei() which specifies the packing format of pixel the data passed to 
//glTexImage2D().  
#define texImage_target			GL_TEXTURE_2D
#define texImage_level			0
#define	texImage_internalFormat		GL_RGBA
#define texImage_sourceFormat		GL_BGRA
#define texImage_sourceType		GL_UNSIGNED_INT_8_8_8_8_REV

//Specify arguments to CGBitmapContextCreate() when creating a CG context which matches the pixel packing of the texture stored in client memory.
//The choice of values is dictated by our use arguments to    
#define cg_RGBA_32_BitsPerPixel		32
#define cg_RGBA_32_BitsPerComponent	8
#define cg_RGBA_32_AlphaOption		kCGImageAlphaPremultipliedLast


	 
PsychError SCREENTestTexture(void) 
{
#if PSYCH_SYSTEM == PSYCH_OSX

    PsychWindowRecordType 	*winRec;
    CGContextRef			cgContext;
    unsigned int			memoryTotalSizeBytes, memoryRowSizeBytes;
    UInt32					*textureMemory;
    int						stringLength, totalTexels, i;
    GLuint					myTexture;
    CGColorSpaceRef			cgColorSpace;
    
    			
    //all subfunctions should have these two lines.  
    PsychPushHelp(useString, synopsisString, seeAlsoString);
    if(PsychIsGiveHelp()){PsychGiveHelp();return(PsychError_none);};
    
    //Get the window structure for the onscreen window.  It holds the onscreein GL context which we will need in the
    //final step when we copy the texture from system RAM onto the screen.
    PsychErrorExit(PsychCapNumInputArgs(1));   	
    PsychErrorExit(PsychRequireNumInputArgs(1)); 	
    PsychErrorExit(PsychCapNumOutputArgs(1));  
    PsychAllocInWindowRecordArg(1, TRUE, &winRec);
    if(!PsychIsOnscreenWindow(winRec))
        PsychErrorExitMsg(PsychError_user, "Onscreen window pointer required");
        
    //allocate memory for the surface
    memoryRowSizeBytes=sizeof(UInt32) * textureSizeX;
    memoryTotalSizeBytes= memoryRowSizeBytes * textureSizeY;
    textureMemory=(UInt32 *)malloc(memoryTotalSizeBytes);
    if(!textureMemory)
            PsychErrorExitMsg(PsychError_internal, "Failed to allocate surface memory\n");
    
    if(useQuartz){
        //Create the Core Graphics bitmap graphics context.  We have to be careful to specify arguments which will allow us to store the texture as an OpenGL texture. 
        //The choice of color space needs to be checked.  
        cgColorSpace=CGColorSpaceCreateDeviceRGB();
        cgContext= CGBitmapContextCreate(textureMemory, textureSizeX, textureSizeY, cg_RGBA_32_BitsPerComponent, memoryRowSizeBytes, cgColorSpace, cg_RGBA_32_AlphaOption);
        if(!cgContext){
            free((void *)textureMemory);
            PsychErrorExitMsg(PsychError_internal, "Failed to allocate CG Bimap Context\n");
        }
            
        //	Draw some text into the bitmap context.  We need to set font, size, pen (drawing mode), color, alpha, text position.
        
        //	There are two ways to select the font in a Core Graphics Quartz context depending on the type of font.
        //	1) CGContextSetFont() for Apple Type Services (ATS) font aka "The Right Way"
        //		A) call CGFontCreateWithPlatformFont() which returns a CGFontRef
        //		B) call CGContextSetFont() to set the font to be the drawing font within a context.
        //  2) CGContextSelectFont() for MacRoman aka "How We Do It"
        //
        //  Using MacRoman seems to mean that we just change the coding, though CGContextSelectFont().  For info on using ATS fonts see:
        // 	http://developer.apple.com/documentation/Carbon/Reference/ATS/
        CGContextSelectFont(cgContext, "Helvetica", (float)24, kCGEncodingMacRoman);		//set the font and its size.
        CGContextSetTextDrawingMode(cgContext, kCGTextFill);					//set the pen to be a filled pen
        CGContextSetRGBStrokeColor(cgContext, (float)0.5, (float)0.5, (float)0.0, (float)1.0);	//set the stroke color and alpha
        CGContextSetRGBFillColor(cgContext, (float)0.5, (float)0.5, (float)0.0, (float)1.0);	//set the fill color and alpha
        stringLength=strlen(textString);
        CGContextShowTextAtPoint(cgContext, (float)textPositionX, (float)textPositionY, textString, stringLength);	//draw at specified location.
        CGContextFlush(cgContext); 	//this might not be necessary but do it just in case.
    }else{
        //fill the texture memory by poking bits in the array which will be turned into a texture.
        totalTexels=textureSizeX * textureSizeY;
        for(i=0;i<totalTexels;i++)
            textureMemory[i]=	redFill << 24 | greenFill | 16 << blueFill << 8 | alphaFill;
     }       

    //Convert the CG graphics bitmap (Quartz surface) into a CG texture.  GL thinks we are loading the texture from memory we indicate to glTexImage2D, but really
    //we are just setting the texture to share the same memory as the Quartz surface.
    PsychSetGLContext(winRec); 
    glEnable(GL_TEXTURE_RECTANGLE_EXT);
    glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
    glGenTextures(1, &myTexture);						//create an index "name" for our texture
    glBindTexture(GL_TEXTURE_RECTANGLE_EXT, myTexture);			//instantiate a texture of type associated with the index and set it to be the target for subsequent gl texture operators.
    glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, 1);			//tell gl how to unpack from our memory when creating a surface, namely don't really unpack it but use it for texture storage.
    glTexParameteri(GL_TEXTURE_RECTANGLE_EXT, GL_TEXTURE_MIN_FILTER, GL_NEAREST);	//specify interpolation scaling rule for copying from texture.  
    glTexParameteri(GL_TEXTURE_RECTANGLE_EXT, GL_TEXTURE_MAG_FILTER, GL_NEAREST);  //specify interpolation scaling rule from copying from texture.
    glTexParameteri(GL_TEXTURE_RECTANGLE_EXT, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_RECTANGLE_EXT, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
    glTexImage2D(GL_TEXTURE_RECTANGLE_EXT, 0, GL_RGBA,  textureSizeX, textureSizeY, 0, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, textureMemory);
    
    //Copy the texture to the display.  What are the s and  t indices  of the first pixel of the texture ? 0 or 1 ?
    //set the GL context to be the onscreen window
    glBegin(GL_QUADS);
        glTexCoord2d(0.0, 0.0);					glVertex2d(0.0, 0.0);
        glTexCoord2d(textureSizeX, 0.0 );			glVertex2d(textureSizeX, 0.0);
        glTexCoord2d(textureSizeX, textureSizeY);		glVertex2d(textureSizeX, textureSizeY);
        glTexCoord2d(0.0, textureSizeY);			glVertex2d(0.0, textureSizeY);
    glEnd();
    glFlush();	
    glDisable(GL_TEXTURE_RECTANGLE_EXT);

    //Close  up shop.  Unlike with normal textures is important to release the context before deallocating the memory which glTexImage2D() was given. 
    //First release the GL context, then the CG context, then free the memory.
    glDeleteTextures(1, &myTexture);	//Remove references from gl to the texture memory  & free gl's associated resources   
    if(useQuartz) CGContextRelease(cgContext);	//Remove references from Core Graphics to the texture memory & free Core Graphics' associated resources.
    free((void *)textureMemory);	//Free the memory
#endif
    return(PsychError_none);

}