Esempio n. 1
0
/*
    PsychCopyInScreenNumberArg()
    
    Automaticially derive the screen number from a window index if provided.
    Otherwise return the screen number if that is what is provided.
        
*/
psych_bool PsychCopyInScreenNumberArg(int position, psych_bool required, int *screenNumber)
{
	PsychNumdexType numdex;
	PsychWindowRecordType *winRec;
	double arg;
        psych_bool isThere;

	if(position==kPsychUseDefaultArgPosition)
		position = kPsychDefaultNumdexArgPosition; 	
	isThere=PsychCopyInDoubleArg(position,required,&arg);
        if(!isThere)
            return(FALSE);
	numdex = (PsychNumdexType)arg;
	if(IsWindowIndex(numdex)){ 
		//it's a window index, so get the window record and from that get the screen number.  
		FindWindowRecord((PsychWindowIndexType)numdex, &winRec);
		*screenNumber=winRec->screenNumber;
                return(TRUE);
	}else if(IsValidScreenNumber(numdex)){
		//it's a screen number, so just return it.
		*screenNumber=(int)numdex;
                return(TRUE);
	}else{
		//we were passed something that is neither a window index nor a screen number so issue an error.
		PsychErrorExitMsg(PsychError_invalidNumdex,NULL);
		return(FALSE);
	}
}
Esempio n. 2
0
psych_bool PsychIsWindowIndexArg(int position)
{
	PsychNumdexType numdex;
	double arg;

	if(position==kPsychUseDefaultArgPosition)
		position =kPsychDefaultNumdexArgPosition; 	
	if(!PsychCopyInDoubleArg(position,FALSE,&arg))
            return(FALSE);
	numdex = (PsychNumdexType)arg;
	return(IsWindowIndex(numdex));
	
}
Esempio n. 3
0
psych_bool PsychCopyInWindowIndexArg(int position, psych_bool required, PsychWindowIndexType *windowIndex)
{

	double *arg;
        psych_bool isThere;
	
	if(position==kPsychUseDefaultArgPosition)
		position = kPsychDefaultNumdexArgPosition; 
        isThere=PsychAllocInDoubleArg(position,required,&arg);
        if(!isThere)
            return(FALSE);
	*windowIndex = (PsychWindowIndexType)*arg;
	if(IsWindowIndex(*windowIndex))
		return(TRUE);
	else{
		PsychErrorExitMsg(PsychError_invalidWindex,NULL);
		return(FALSE);  //only to satisfy the compiler with a return statement.
	}
}
PsychError SCREENPreloadTextures(void)  
{	
	PsychWindowRecordType                   *windowRecord, *texwin;
	psych_bool                                 isArgThere;
        int                                     *texhandles;
        PsychWindowRecordType                   **windowRecordArray;        
        int                                     i, n, numWindows, myhandle; 
        double                                  *success;
        psych_bool*                                residency;
        GLuint*                                 texids;
        GLboolean*                              texresident;
        psych_bool                                 failed = false;
        GLclampf                                maxprio = 1.0f;
        GLenum                                  target;

	//all sub functions should have these two lines
	PsychPushHelp(useString, synopsisString,seeAlsoString);
	if(PsychIsGiveHelp()){PsychGiveHelp();return(PsychError_none);};
	
	//check for superfluous arguments
	PsychErrorExit(PsychCapNumInputArgs(2));        //The maximum number of inputs
	PsychErrorExit(PsychRequireNumInputArgs(1));    //The minimum number of inputs
	PsychErrorExit(PsychCapNumOutputArgs(2));       //The maximum number of outputs
	
	//get the window record from the window record argument and get info from the window record
	PsychAllocInWindowRecordArg(1, kPsychArgRequired, &windowRecord);
		
	// Get optional texids vector:
	isArgThere = PsychIsArgPresent(PsychArgIn, 2);
        PsychAllocInIntegerListArg(2, FALSE, &n, &texhandles);
        if (n < 1) isArgThere=FALSE;
        
        // Enable this windowRecords framebuffer as current drawingtarget:
        PsychSetDrawingTarget(windowRecord);

		// Disable shader:
		PsychSetShader(windowRecord, 0);
	

        glDisable(GL_TEXTURE_2D);

	// Fetch global texturing mode:
	target=PsychGetTextureTarget(windowRecord);

        glEnable(target);
        glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
        glColor4f(0, 0, 0, 0);
	// Setup identity modelview matrix:
        glMatrixMode(GL_MODELVIEW);
        glPushMatrix();
        glLoadIdentity();

        PsychCreateVolatileWindowRecordPointerList(&numWindows, &windowRecordArray);            

        // Process vector of all texids for all requested textures:
        if (!isArgThere) {
            // No handles provided: In this case, we preload all textures:
            n=0;
            for(i=0; i<numWindows; i++) {                
                if (windowRecordArray[i]->windowType==kPsychTexture) {
                    n++;
                    // Prioritize this texture:
                    glPrioritizeTextures(1, (GLuint*) &(windowRecordArray[i]->textureNumber), &maxprio);
                    // Bind this texture:
                    glBindTexture(target, windowRecordArray[i]->textureNumber);
                    // Render a single textured point, thereby enforcing a texture upload:
                    glBegin(GL_QUADS);
                    glTexCoord2f(0,0); glVertex2i(10,10);
                    glTexCoord2f(0,1); glVertex2i(10,11);
                    glTexCoord2f(1,1); glVertex2i(11,11);
                    glTexCoord2f(1,0); glVertex2i(11,10);                    
                    glEnd();
                }
            }
            
            texids = (GLuint*) PsychMallocTemp(sizeof(GLuint) * n);
            texresident = (GLboolean*) PsychMallocTemp(sizeof(GLboolean) * n);

            n=0;
            for(i=0; i<numWindows; i++) {                
                if (windowRecordArray[i]->windowType==kPsychTexture) {
                    texids[n] = (GLuint) windowRecordArray[i]->textureNumber;
                    n++;
                }
            }
        }
        else {
            // Vector with texture handles provided: Just preload them.
            texids = (GLuint*) PsychMallocTemp(sizeof(GLuint) * n);
            texresident = (GLboolean*) PsychMallocTemp(sizeof(GLboolean) * n);
            myhandle=0;
            for (i=0; i<n; i++) {
                myhandle = texhandles[i];
                texwin = NULL;
                if (IsWindowIndex(myhandle)) FindWindowRecord(myhandle, &texwin);
                if (texwin && texwin->windowType==kPsychTexture) {
                    // Prioritize this texture:
                    glPrioritizeTextures(1, (GLuint*) &(texwin->textureNumber), &maxprio);
                    // Bind this texture:
                    glBindTexture(target, texwin->textureNumber);
                    // Render a single textured point, thereby enforcing a texture upload:
                    glBegin(GL_QUADS);
                    glTexCoord2f(0,0); glVertex2i(10,10);
                    glTexCoord2f(0,1); glVertex2i(10,11);
                    glTexCoord2f(1,1); glVertex2i(11,11);
                    glTexCoord2f(1,0); glVertex2i(11,10);                    
                    glEnd();
                    texids[i] = (GLuint) texwin->textureNumber;
                }
                else {
                    // This handle is invalid or at least no texture handle:
                    printf("PTB-ERROR! Screen('PreloadTextures'): Entry %i of texture handle vector (handle %i) is not a texture handle!\n",
                           i, myhandle);
                    failed = true;
                }
            }
        }
        
        // Restore old matrix from backup copy, undoing the global translation:
        glPopMatrix();
        // Disable texture engine:
        glDisable(GL_TEXTURE_2D);
        glDisable(target);

        // Wait for prefetch completion:
        glFinish();
        
        // We don't need these anymore:
        PsychDestroyVolatileWindowRecordPointerList(windowRecordArray);
        
        if (failed) {
            PsychErrorExitMsg(PsychError_user, "At least one texture handle in texids-vector was invalid! Aborted.");
        }
        
        // Query residency state of all preloaded textures:
        success = NULL;
        PsychAllocOutDoubleArg(1, FALSE, &success);
        *success = (double) glAreTexturesResident(n, texids, texresident);
        
        // Sync pipe again, just to be safe...
        glFinish();
        
        // Count them and copy them into output vector:
        PsychAllocOutBooleanMatArg(2, FALSE, n, 1, 1, &residency);
        
        for (i=0; i<n; i++) {
            residency[i] = (psych_bool) ((*success) ? TRUE : texresident[i]);
        }
        
        PsychTestForGLErrors();
        
 	// Done. Our PsychMallocTemp'ed arrays will be auto-released...
	return(PsychError_none);
}
// Batch-drawing version of DrawTexture:
PsychError SCREENDrawTextures(void) 
{
	// If you change useString then also change the corresponding synopsis string in ScreenSynopsis.c 1 2 3 4 5 6 7 8
	static char useString[] = "Screen('DrawTextures', windowPointer, texturePointer(s) [, sourceRect(s)] [, destinationRect(s)] [, rotationAngle(s)] [, filterMode(s)] [, globalAlpha(s)] [, modulateColor(s)] [, textureShader] [, specialFlags] [, auxParameters]);";
	//                                               1              2                    3                 4                      5                    6                 7				    8					9				 10					11
	
	static char synopsisString[] = "Draw many textures at once, either one texture to many locations or many textures.\n"
	"This function accepts the same parameters as Screen('DrawTexture'), but it is optimized for drawing many textures. "
	"You can leave out each argument, a default setting will be used in that case, provide it once to apply it to all "
	"drawn items, or provide a vector or matrix with a individual setting for each drawn item. If you provide multiple "
	"settings per argument, then the number must match between all arguments.\n\n"
	"Examples:\n"
	"a) One texture drawn to different locations at different orientations: Provide one texture handle for the texturePointer, "
	"a 4 row by n columns matrix for 'destinationRect' to provide target rectangles for n locations, provide a n component "
	"vector of 'rotationAngles' for the n different orientations of the n drawn texture patches.\n"
	"b) n textures drawn to n different locations: Same as a) but provide a n component vector of 'texturePointers' one for "
	"each texture to be drawn to one of n locations at n angles.\n";

	PsychWindowRecordType			*source, *target;
	PsychRectType					sourceRect, targetRect, tempRect;
	PsychColorType	color;
	GLdouble						dVals[4]; 
    double							*dstRects, *srcRects, *colors, *penSizes, *globalAlphas, *filterModes, *rotationAngles;
	unsigned char					*bytecolors;
	int								numTexs, numdstRects, numsrcRects, i, j, nc, mc, nrsize, m, n, p, numAngles, numFilterModes, numAlphas, numRef;
	double*							texids;
	double							rotationAngle, globalAlpha, filterMode;
	double*							auxParameters;
	int								numAuxParams, numAuxComponents;

	int textureShader, backupShader;
	int specialFlags = 0;

    //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 onscreen GL context which we will need in the
    //final step when we copy the texture from system RAM onto the screen.
    PsychErrorExit(PsychCapNumInputArgs(11));   	
    PsychErrorExit(PsychRequireNumInputArgs(2)); 	
    PsychErrorExit(PsychCapNumOutputArgs(0)); 
	
	// The target window is a fixed parameter:
    PsychAllocInWindowRecordArg(1, kPsychArgRequired, &target);
	
	// First get all source texture handles:
	PsychAllocInDoubleMatArg(2, kPsychArgRequired, &m, &n, &p, &texids);
	if ((p!=1) || (m>1 && n!=1) || (n>1 && m!=1)) PsychErrorExitMsg(PsychError_user, "The second argument must be either a row- or columnvector of valid texture handles.");
	// This is the number of texture handles:
	numTexs = m * n;

	// Only one texture?
	if (numTexs == 1) {
		// Yes. Allocate it in the conventional way:
		PsychAllocInWindowRecordArg(2, kPsychArgRequired, &source);
		if(source->windowType!=kPsychTexture) {
			PsychErrorExitMsg(PsychError_user, "The second argument supplied was not a texture handle!");
		}
	}

	// Query, allocate and copy in all vectors...
	numdstRects = 4;
	nrsize = 0;
	colors = NULL;
	bytecolors = NULL;
	penSizes = NULL;
	
	// The negative position -4 means: dstRects coords are expected at position 4, but they are optional.
	// NULL means - don't want a size's vector.
	PsychPrepareRenderBatch(target, -4, &numdstRects, &dstRects, 8, &nc, &mc, &colors, &bytecolors, 5, &nrsize, &penSizes);

	// At this point, target is set up as target window, i.e. its GL-Context is active, it is set as drawing target,
	// alpha blending is set up according to Screen('BlendFunction'), and the drawing color is set if it is a singular one.
	if (nc <= 1) {
		// Only one - or no - color provided. One or none?
		if(PsychCopyInColorArg(8, kPsychArgOptional, &color)) {
			// One global modulate color provided:

			// Setup global vertex color as modulate color for texture drawing:
			PsychCoerceColorMode(&color);
			PsychSetGLColor(&color, target);
		}
		else {
			// No modulateColor provided: Don't use this parameter:
			nc = 0;
		}
	}

	// Try to get source rects:
	m=n=p=0;
	if (PsychAllocInDoubleMatArg(3, kPsychArgOptional, &m, &n, &p, &srcRects)) {
		if ((p!=1) || (m!=1 && m!=4)) PsychErrorExitMsg(PsychError_user, "The third argument must be either empty, or a single srcRect 4 component row vector, or a 4 row by n column matrix with srcRects for all objects to draw, not a 3D matrix!");
		// Ok, its a one row or four row matrix:
		if (m==4) {
			// Potentially multiple source rects provided:
			numsrcRects = n;
		}
		else {
			// Its a one row vector: This is either a single srcRect for all textures, or something invalid:
			if (n!=4) PsychErrorExitMsg(PsychError_user, "The third argument must be either empty, or a single srcRect 4 component row vector, or a 4 row by n column matrix with srcRects for all objects to draw!");
			// Single srcRect provided:
			numsrcRects = 1;
		}
	}
	else {
		// No srcRects provided:
		numsrcRects = 0;
	}
	
	// Optional rotation angles:
	m=n=p=0;
	if (PsychAllocInDoubleMatArg(5, kPsychArgOptional, &m, &n, &p, &rotationAngles)) {
		if ((p!=1) || (m>1 && n!=1) || (n>1 && m!=1)) PsychErrorExitMsg(PsychError_user, "The fifth argument must be either a row- or columnvector of rotation angles.");
	}
	numAngles = m * n;

	// Default to 0 degree rotation -- upright drawing:
	rotationAngle = (numAngles == 1) ? rotationAngles[0] : 0.0;
	
	// Optional filter modes:
	m=n=p=0;
	if (PsychAllocInDoubleMatArg(6, kPsychArgOptional, &m, &n, &p, &filterModes)) {
		if ((p!=1) || (m>1 && n!=1) || (n>1 && m!=1)) PsychErrorExitMsg(PsychError_user, "The sixth argument must be either a row- or columnvector of filterModes.");
	}
	numFilterModes = m * n;

	// Default to bilinear filtering:
	filterMode = (numFilterModes == 1) ? filterModes[0] : 1;
	
	// Optional globalAlphas:
	m=n=p=0;
	if (PsychAllocInDoubleMatArg(7, kPsychArgOptional, &m, &n, &p, &globalAlphas)) {
		if ((p!=1) || (m>1 && n!=1) || (n>1 && m!=1)) PsychErrorExitMsg(PsychError_user, "The seventh argument must be either a row- or columnvector of globalAlpha values.");
	}
	numAlphas = m * n;
	globalAlpha = (numAlphas == 1) ? globalAlphas[0] : 1.0;
	
	// Optional auxParameters:
	auxParameters = NULL;
	m=n=p=0;
	if (PsychAllocInDoubleMatArg(11, kPsychArgOptional, &m, &n, &p, &auxParameters)) {
		if ((p!=1) || (m < 4) || ((m % 4) !=0)|| (n < 1)) PsychErrorExitMsg(PsychError_user, "The 11th argument must be a column vector or matrix of 'auxParameter' values with at least 4 components and component count a multiple of four.");
	}
	numAuxParams = n;
	numAuxComponents = m;
	
	// Check for consistency: Each parameter must be either not present, present once,
	// or present as many times as all other multi-parameters:
	numRef = (numsrcRects > numdstRects) ? numsrcRects : numdstRects;
	numRef = (numRef > numTexs) ? numRef : numTexs;
	numRef = (numRef > nc) ? numRef : nc;
	numRef = (numRef > numAlphas) ? numRef : numAlphas;
	numRef = (numRef > numFilterModes) ? numRef : numFilterModes;
	numRef = (numRef > numAngles) ? numRef : numAngles;
	numRef = (numRef > numAuxParams) ? numRef : numAuxParams;

	if (numTexs > 1 && numTexs != numRef) {
		printf("PTB-ERROR: Number of provided texture handles %i doesn't match number of other primitives %i!\n", numTexs, numRef);
		PsychErrorExitMsg(PsychError_user, "Inconsistent number of arguments provided to Screen('DrawTextures').");
	}

	if (numsrcRects > 1 && numsrcRects != numRef) {
		printf("PTB-ERROR: Number of provided source rectangles %i doesn't match number of other primitives %i!\n", numsrcRects, numRef);
		PsychErrorExitMsg(PsychError_user, "Inconsistent number of arguments provided to Screen('DrawTextures').");
	}
	
	if (numdstRects > 1 && numdstRects != numRef) {
		printf("PTB-ERROR: Number of provided destination rectangles %i doesn't match number of other primitives %i!\n", numdstRects, numRef);
		PsychErrorExitMsg(PsychError_user, "Inconsistent number of arguments provided to Screen('DrawTextures').");
	}
		
	if (numAngles > 1 && numAngles != numRef) {
		printf("PTB-ERROR: Number of provided rotation angles %i doesn't match number of other primitives %i!\n", numAngles, numRef);
		PsychErrorExitMsg(PsychError_user, "Inconsistent number of arguments provided to Screen('DrawTextures').");
	}

	if (numAlphas > 1 && numAlphas != numRef) {
		printf("PTB-ERROR: Number of provided global alpha values %i doesn't match number of other primitives %i!\n", numAlphas, numRef);
		PsychErrorExitMsg(PsychError_user, "Inconsistent number of arguments provided to Screen('DrawTextures').");
	}

	if (numFilterModes > 1 && numFilterModes != numRef) {
		printf("PTB-ERROR: Number of provided filtermode values %i doesn't match number of other primitives %i!\n", numFilterModes, numRef);
		PsychErrorExitMsg(PsychError_user, "Inconsistent number of arguments provided to Screen('DrawTextures').");
	}

	if (nc > 1 && nc != numRef) {
		printf("PTB-ERROR: Number of provided modulateColors %i doesn't match number of other primitives %i!\n", nc, numRef);
		PsychErrorExitMsg(PsychError_user, "Inconsistent number of arguments provided to Screen('DrawTextures').");
	}

	if (numAuxParams > 1 && numAuxParams != numRef) {
		printf("PTB-ERROR: Number of provided 'auxParameter' column vectors %i doesn't match number of other primitives %i!\n", numAuxParams, numRef);
		PsychErrorExitMsg(PsychError_user, "Inconsistent number of arguments provided to Screen('DrawTextures').");
	}

	// Assign optional override texture shader, if any provided:
	textureShader = -1;
    PsychCopyInIntegerArg(9, kPsychArgOptional, &textureShader);

	// Assign any other optional special flags:
    PsychCopyInIntegerArg(10, kPsychArgOptional, &specialFlags);

	// Ok, everything consistent so far.
	
	// Texture blitting loop:
	for (i=0; i < numRef; i++) {
		// Draw i'th texture:
		
		// Check if more than one texture provided. If not then the one single texture has been
		// setup already above:
		if (numTexs > 1) {
			// More than one texture handle provided: Need to allocate i'th one in:
			if(!IsWindowIndex((PsychWindowIndexType) texids[i])) {
				printf("PTB-ERROR: %i th entry in texture handle vector is not a valid handle!\n");
				PsychErrorExitMsg(PsychError_user, "Invalid texture handle provided to Screen('DrawTextures').");
			}

			// Get it:
			FindWindowRecord((PsychWindowIndexType) texids[i], &source);
			if(source->windowType!=kPsychTexture) {
				printf("PTB-ERROR: %i th entry in texture handle vector is not a valid handle!\n");
				PsychErrorExitMsg(PsychError_user, "The second argument supplied was not a texture handle!");
			}

			// Ok, we have our texture record in source:
		}
		
		// Source rectangle provided?
		if (numsrcRects > 1) {
			// Get i'th source rectangle:
			PsychCopyRect(sourceRect, &(srcRects[i*4]));
		} else if (numsrcRects == 1) {
			// Single source rect provided - get it:
			PsychCopyRect(sourceRect, &(srcRects[0]));
		} else {
			// No source rect provided: Take rectangle of current texture as srcRect:
			PsychCopyRect(sourceRect,source->clientrect);
		}
		
		// Skip this texture if sourceRect is an empty rect:
		if (IsPsychRectEmpty(sourceRect)) continue;
		
		// Destination rectangle provided?
		if (numdstRects > 1) {
			// Get i'th destination rectangle:
			PsychCopyRect(targetRect, &(dstRects[i*4]));
		} else if (numdstRects == 1) {
			// Single destination rect provided - get it:
			PsychCopyRect(targetRect, &(dstRects[0]));
		} else {
			// No destination rect provided: Center the current sourceRect in the current
			// target window and use that as destination:
            PsychCopyRect(tempRect, target->clientrect);
			PsychCenterRectInRect(sourceRect, tempRect, targetRect);
		}
		
		// Skip this texture if targetRect is an empty rect:
		if (IsPsychRectEmpty(targetRect)) continue;
		
		if (numAngles > 1) rotationAngle = rotationAngles[i];
		if (numFilterModes > 1) filterMode = filterModes[i];
		if (numAlphas > 1) globalAlpha = globalAlphas[i];
		
		// Disable alpha if modulateColor active:
		if (nc > 0) globalAlpha = DBL_MAX;

		// Pass auxParameters for current primitive in the auxShaderParams field.
		target->auxShaderParamsCount = numAuxComponents;
		if (numAuxParams > 0) {
			if (numAuxParams == 1) {
				target->auxShaderParams = auxParameters;
			}
			else {
				target->auxShaderParams = &(auxParameters[i * numAuxComponents]);
			}
		}
		else {
			target->auxShaderParams = NULL;
		}
		
		// Multiple modulateColors provided?
		if (nc > 1) {
			// Yes. Set it up as current vertex color: We submit to internal currentColor for
			// shader based color processing and via glColorXXX() for fixed pipe processing:
			if (mc==3) {
				if (colors) {
					// RGB double:
					glColor3dv(&(colors[i*3]));
					target->currentColor[0]=colors[i*3 + 0];
					target->currentColor[1]=colors[i*3 + 1];
					target->currentColor[2]=colors[i*3 + 2];
					target->currentColor[3]=1.0;
				}
				else {
					// RGB uint8:
					glColor3ubv(&(bytecolors[i*3]));
					target->currentColor[0]=((double) bytecolors[i*3 + 0] / 255.0);
					target->currentColor[1]=((double) bytecolors[i*3 + 1] / 255.0);
					target->currentColor[2]=((double) bytecolors[i*3 + 2] / 255.0);
					target->currentColor[3]=1.0;
				}
			}
			else {
				if (colors) {
					// RGBA double:
					glColor4dv(&(colors[i*4]));
					target->currentColor[0]=colors[i*4 + 0];
					target->currentColor[1]=colors[i*4 + 1];
					target->currentColor[2]=colors[i*4 + 2];
					target->currentColor[3]=colors[i*4 + 3];
				}
				else {
					// RGBA uint8:
					glColor4ubv(&(bytecolors[i*4]));
					target->currentColor[0]=((double) bytecolors[i*4 + 0] / 255.0);
					target->currentColor[1]=((double) bytecolors[i*4 + 1] / 255.0);
					target->currentColor[2]=((double) bytecolors[i*4 + 2] / 255.0);
					target->currentColor[3]=((double) bytecolors[i*4 + 3] / 255.0);
				}					
			}			
		}
		
		// Ok, everything assigned. Check parameters:
		if (filterMode<0 || filterMode>3) {
			PsychErrorExitMsg(PsychError_user, "filterMode needs to be 0 for nearest neighbour filter, or 1 for bilinear filter, or 2 for mipmapped filter or 3 for mipmapped-linear filter.");    
		}

		// Set rotation mode flag for texture matrix rotation if secialFlags is set accordingly:
		if (specialFlags & kPsychUseTextureMatrixForRotation) source->specialflags|=kPsychUseTextureMatrixForRotation;
		if (specialFlags & kPsychDontDoRotation) source->specialflags|=kPsychDontDoRotation;

		// Perform blit operation for i'th texture, either with or without an override texture shader applied:
		if (textureShader > -1) {
			backupShader = source->textureFilterShader;
			source->textureFilterShader = -1 * textureShader;
			PsychBlitTextureToDisplay(source, target, sourceRect, targetRect, rotationAngle, filterMode, globalAlpha);	
			source->textureFilterShader = backupShader;
		}
		else {
			PsychBlitTextureToDisplay(source, target, sourceRect, targetRect, rotationAngle, filterMode, globalAlpha);	
		}

		// Reset rotation mode flag:
		source->specialflags &= ~(kPsychUseTextureMatrixForRotation | kPsychDontDoRotation);

		// Next one...
	}

	target->auxShaderParams = NULL;
	target->auxShaderParamsCount = 0;

    // Mark end of drawing op. This is needed for single buffered drawing:
    PsychFlushGL(target);

    return(PsychError_none);
}