PsychError WAITSECSYieldSecs(void) { static char useString[] = "[realWakeupTimeSecs] = WaitSecs('YieldSecs', waitPeriodSecs);"; // 1 static char synopsisString[] = "Wait for at least \"waitPeriodSecs\", don't care if it takes a few milliseconds longer. " "Optionally, return the real wakeup time \"realWakeupTimeSecs\".\n" "This call is useful if you want your code to release the cpu for a few milliseconds, " "e.g., to avoid overloading the cpu in a spinning loop, and you don't care if the " "wait takes a few msecs longer than specified. If you do care, use one of the other " "WaitSecs() variants! The other variants emphasize accuracy of timed waits, even if " "this causes a high load on the processor.\n"; static char seeAlsoString[] = ""; double waitPeriodSecs; double now; //all sub functions should have these two lines PsychPushHelp(useString, synopsisString,seeAlsoString); if(PsychIsGiveHelp()){PsychGiveHelp();return(PsychError_none);}; //check to see if the user supplied superfluous arguments PsychErrorExit(PsychCapNumOutputArgs(1)); PsychErrorExit(PsychCapNumInputArgs(1)); PsychCopyInDoubleArg(1,TRUE,&waitPeriodSecs); PsychYieldIntervalSeconds(waitPeriodSecs); // Return current system time at end of sleep: PsychGetAdjustedPrecisionTimerSeconds(&now); PsychCopyOutDoubleArg(1, FALSE, now); return(PsychError_none); }
void PsychOSCloseWindow(PsychWindowRecordType *windowRecord) { CGDirectDisplayID cgDisplayID; // Disable rendering context: CGLSetCurrentContext(NULL); // Destroy pixelformat object: CGLDestroyPixelFormat(windowRecord->targetSpecific.pixelFormatObject); // Destroy rendering context: CGLReleaseContext(windowRecord->targetSpecific.contextObject); if (windowRecord->targetSpecific.glusercontextObject) CGLReleaseContext(windowRecord->targetSpecific.glusercontextObject); if (windowRecord->targetSpecific.glswapcontextObject) CGLReleaseContext(windowRecord->targetSpecific.glswapcontextObject); // Last reference to this screen? In that case we have to shutdown the fallback // vbl timestamping and vblank counting facilities for this screen: if (screenRefCount[windowRecord->screenNumber] == 1) { // Last one on this screen will be gone in a second. // Shutdown and release CVDisplayLink for this windows screen, if any: if (cvDisplayLink[windowRecord->screenNumber]) { if (PsychPrefStateGet_Verbosity() > 3) printf("PTB-INFO: Releasing CVDisplayLink for screen %i.\n", windowRecord->screenNumber); if (CVDisplayLinkIsRunning(cvDisplayLink[windowRecord->screenNumber])) CVDisplayLinkStop(cvDisplayLink[windowRecord->screenNumber]); PsychYieldIntervalSeconds(0.1); CVDisplayLinkRelease(cvDisplayLink[windowRecord->screenNumber]); cvDisplayLink[windowRecord->screenNumber] = NULL; PsychYieldIntervalSeconds(0.1); // Teardown shared data structure and mutex: PsychDestroyMutex(&(cvDisplayLinkData[windowRecord->screenNumber].mutex)); } } // Release reference of this window to its screen: screenRefCount[windowRecord->screenNumber]--; // Destroy Cocoa onscreen window, if any: if (windowRecord->targetSpecific.windowHandle) PsychCocoaDisposeWindow(windowRecord); windowRecord->targetSpecific.windowHandle = NULL; return; }
/* Perform one context loop iteration (for bus message handling) if doWait == false, * or two seconds worth of iterations if doWait == true. This drives the message-bus * callback, so needs to be performed to get any error reporting etc. */ int PsychGSProcessMovieContext(GMainLoop *loop, psych_bool doWait) { double tdeadline, tnow; PsychGetAdjustedPrecisionTimerSeconds(&tdeadline); tnow = tdeadline; tdeadline+=2.0; if (NULL == loop) return(0); while (doWait && (tnow < tdeadline)) { // Perform non-blocking work iteration: if (!g_main_context_iteration(g_main_loop_get_context(loop), false)) PsychYieldIntervalSeconds(0.010); // Update time: PsychGetAdjustedPrecisionTimerSeconds(&tnow); } // Perform one more work iteration of the event context, but don't block: return(g_main_context_iteration(g_main_loop_get_context(loop), false)); }
/* Perform context loop iterations (for bus message handling) if doWait == false, * as long as there is work to do, or at least two seconds worth of iterations * if doWait == true. This drives the message-bus callback, so needs to be * performed to get any error reporting etc. */ static int PsychGSProcessMovieContext(GMainLoop *loop, psych_bool doWait) { psych_bool workdone; double tdeadline, tnow; PsychGetAdjustedPrecisionTimerSeconds(&tdeadline); tnow = tdeadline; tdeadline+=2.0; if (NULL == loop) return(0); while (doWait && (tnow < tdeadline)) { // Perform non-blocking work iteration: if (!g_main_context_iteration(g_main_loop_get_context(loop), false)) PsychYieldIntervalSeconds(0.010); // Update time: PsychGetAdjustedPrecisionTimerSeconds(&tnow); } // Perform work iterations of the event context as long as events are available, but don't block: while ((workdone = g_main_context_iteration(g_main_loop_get_context(loop), false)) == TRUE); return(workdone); }
PsychError PsychHIDOSKbCheck(int deviceIndex, double* scanList) { psych_uint8 keys[1024]; LPDIRECTINPUTDEVICE8 kb; unsigned int i, j; double* buttonStates; int keysdown; double timestamp; DWORD cbSize; if (deviceIndex == INT_MAX) { deviceIndex = PsychHIDGetDefaultKbQueueDevice(); // Ok, deviceIndex now contains our default keyboard to use - The first suitable keyboard. } if ((deviceIndex < 0) || (deviceIndex >= ndevices)) { // Out of range index: PsychErrorExitMsg(PsychError_user, "Invalid 'deviceIndex' specified. No such device!"); } // Get DirectInput keyboard device: kb = GetXDevice(deviceIndex); // Keyboard queue for this deviceIndex already exists? if (NULL == psychHIDKbQueueFirstPress[deviceIndex]) { // No. Create one which accepts all keys: PsychHIDOSKbQueueCreate(deviceIndex, 0, NULL); } // Keyboard queue for this device active? If not, we need // to start it: if (!psychHIDKbQueueActive[deviceIndex]) { // Keyboard idle: Need to start it: PsychHIDOSKbQueueStart(deviceIndex); // Startup to first key delivery takes time. Wait for // 50 msecs to be on the safe side: PsychYieldIntervalSeconds(0.050); } // Size of state structure is device dependent: switch (info[deviceIndex].dwDevType & 0xff) { case DI8DEVTYPE_KEYBOARD: cbSize = 256; break; case DI8DEVTYPE_MOUSE: case DI8DEVTYPE_SCREENPOINTER: cbSize = sizeof(DIMOUSESTATE2); break; case DI8DEVTYPE_JOYSTICK: cbSize = sizeof(DIJOYSTATE2); break; default: // Unkown device. Fail. cbSize = 0; } // Query current state snapshot of keyboard: memset(keys, 0, sizeof(keys)); if (DI_OK != kb->GetDeviceState(cbSize, (LPVOID) &keys[0])) { printf("PsychHID-ERROR: KbCheck for deviceIndex %i failed, because query of device failed!\n", deviceIndex); PsychErrorExitMsg(PsychError_user, "KbCheck failed!"); } // Request current time of query: PsychGetAdjustedPrecisionTimerSeconds(×tamp); // Reset overall key state to "none pressed": keysdown = 0; // Copy out timestamp: PsychCopyOutDoubleArg(2, kPsychArgOptional, timestamp); // Copy keyboard state: PsychAllocOutDoubleMatArg(3, kPsychArgOptional, 1, 256, 1, &buttonStates); for (i = 0; i < 256; i++) buttonStates[i] = 0; // Keyboard? if (cbSize == 256) { // Copy button state to output vector, apply scanlist mask, compute // resulting overall keysdown state. We ignore keyboard scancode zero and // start with 1 instead. We also ignore code 255. These are borderline codes // which may do weird things... for (i = 1; i < 255; i++) { // Compute target key slot for this scancode i: j = PsychHIDOSMapKey(i); // This key down? buttonStates[j] += (keys[i] > 0) ? 1 : 0; // Apply scanList mask, if any provided: if (scanList && (scanList[j] <= 0)) buttonStates[j] = 0; keysdown += (unsigned int) buttonStates[j]; } } // Joystick? if (cbSize == sizeof(DIJOYSTATE2)) { // Copy button state to output vector, apply scanlist mask, compute // resulting overall keysdown state. There are 128 buttons at an offset: for (i = (8 * sizeof(LONG) + 4 * sizeof(DWORD)); i < (8 * sizeof(LONG) + 4 * sizeof(DWORD)) + 128; i++) { // Compute target key slot for this scancode i: j = i - (8 * sizeof(LONG) + 4 * sizeof(DWORD)); // This key down? buttonStates[j] += (keys[i] > 0) ? 1 : 0; // Apply scanList mask, if any provided: if (scanList && (scanList[j] <= 0)) buttonStates[j] = 0; keysdown += (unsigned int) buttonStates[j]; } } // Mouse? if (cbSize == sizeof(DIMOUSESTATE2)) { // Copy button state to output vector, apply scanlist mask, compute // resulting overall keysdown state. There are 8 buttons at an offset: for (i = (3 * sizeof(LONG)); i < (3 * sizeof(LONG)) + 8; i++) { // Compute target key slot for this scancode i: j = i - (3 * sizeof(LONG)); // This key down? buttonStates[j] += (keys[i] > 0) ? 1 : 0; // Apply scanList mask, if any provided: if (scanList && (scanList[j] <= 0)) buttonStates[j] = 0; keysdown += (unsigned int) buttonStates[j]; } } // Copy out overall keystate: PsychCopyOutDoubleArg(1, kPsychArgOptional, (keysdown > 0) ? 1 : 0); return(PsychError_none); }
PsychError SCREENGetCapturedImage(void) { PsychWindowRecordType *windowRecord; PsychWindowRecordType *textureRecord; PsychRectType rect; double summed_intensity; int capturehandle = -1; int waitForImage = TRUE; int specialmode = 0; double timeout, tnow; double presentation_timestamp = 0; int rc=-1; double targetmemptr = 0; double* tsummed = NULL; psych_uint8 *targetmatrixptr = NULL; static rawcapimgdata rawCaptureBuffer = {0, 0, 0, NULL}; // All sub functions should have these two lines PsychPushHelp(useString, synopsisString, seeAlsoString); if(PsychIsGiveHelp()) {PsychGiveHelp(); return(PsychError_none);}; PsychErrorExit(PsychCapNumInputArgs(6)); // Max. 6 input args. PsychErrorExit(PsychRequireNumInputArgs(2)); // Min. 2 input args required. PsychErrorExit(PsychCapNumOutputArgs(4)); // Max. 4 output args. // Get the window record from the window record argument and get info from the window record PsychAllocInWindowRecordArg(kPsychUseDefaultArgPosition, TRUE, &windowRecord); // Only onscreen windows allowed: if(!PsychIsOnscreenWindow(windowRecord) && !PsychIsOffscreenWindow(windowRecord)) { PsychErrorExitMsg(PsychError_user, "GetCapturedImage called on something else than an onscreen window or offscreen window."); } // Get the handle: PsychCopyInIntegerArg(2, TRUE, &capturehandle); if (capturehandle==-1) { PsychErrorExitMsg(PsychError_user, "GetCapturedImage called without valid handle to a capture object."); } // Get the 'waitForImage' flag: If waitForImage == true == 1, we'll do a blocking wait for // arrival of a new image. Otherwise we will return with a 0-Handle if there // isn't any new image available. PsychCopyInIntegerArg(3, FALSE, &waitForImage); // Special case waitForImage == 4? This would ask to call into the capture driver, but // not wait for any image to arrive and not return any information. This is only useful // on OS/X and Windows when using the capture engine for video recording to harddisk. In // that case we are not interested at all in the captured live video, we just want it to // get written to harddisk in the background. To keep the video encoder going, we need to // call its SGIdle() routine and waitForImage==4 does just that, call SGIdle(). if (waitForImage == 4) { // Perform the null-call to the capture engine, ie a SGIdle() on OS/X and Windows: PsychGetTextureFromCapture(windowRecord, capturehandle, 4, 0.0, NULL, NULL, NULL, NULL); // Done. Nothing to return... return(PsychError_none); } // Get the optional textureRecord for the optional texture handle. If the calling script // provides the texture handle of an existing Psychtoolbox texture that has a matching // format, then that texture is recycled by overwriting its previous content with the // image data from the new captured image. This can save some overhead for texture destruction // and recreation. While this is probably not noticeable on mid- to high-end gfx cards with // rectangle texture support, it can provide a significant speedup on low-end gfx cards with // only power-of-two texture support. textureRecord = NULL; if ((PsychGetNumInputArgs()>=4) && PsychIsWindowIndexArg(4)) PsychAllocInWindowRecordArg(4, FALSE, &textureRecord); // Get the optional specialmode flag: PsychCopyInIntegerArg(5, FALSE, &specialmode); // Set a 10 second maximum timeout for waiting for new frames: PsychGetAdjustedPrecisionTimerSeconds(&timeout); timeout+=10; while (rc==-1) { // We pass a checkForImage value of 2 if waitForImage>0. This way we can signal if we are in polling or blocking mode. // With the libdc1394 engine this allows to do a real blocking wait in the driver -- much more efficient than the spin-waiting approach! rc = PsychGetTextureFromCapture(windowRecord, capturehandle, ((waitForImage>0 && waitForImage<3) ? 2 : 1), 0.0, NULL, &presentation_timestamp, NULL, &rawCaptureBuffer); PsychGetAdjustedPrecisionTimerSeconds(&tnow); if (rc==-2 || (tnow > timeout)) { // No image available and there won't be any in the future, because capture has been stopped or there is a timeout: if (tnow > timeout) printf("PTB-WARNING: In Screen('GetCapturedImage') timed out waiting for a new frame. No video data in over 10 seconds!\n"); // No new texture available: Return a negative handle: PsychCopyOutDoubleArg(1, TRUE, -1); // ...and an invalid timestamp: PsychCopyOutDoubleArg(2, FALSE, -1); PsychCopyOutDoubleArg(3, FALSE, 0); PsychCopyOutDoubleArg(4, FALSE, 0); // Ready! return(PsychError_none); } else if (rc==-1 && (waitForImage == 0 || waitForImage == 3)) { // We should just poll once and no new texture available: Return a null-handle: PsychCopyOutDoubleArg(1, TRUE, 0); // ...and the current timestamp: PsychCopyOutDoubleArg(2, FALSE, presentation_timestamp); PsychCopyOutDoubleArg(3, FALSE, 0); PsychCopyOutDoubleArg(4, FALSE, 0); // Ready! return(PsychError_none); } else if (rc==-1 && waitForImage != 0) { // No new texture available yet. Just sleep a bit and then retry... PsychYieldIntervalSeconds(0.002); } } // rc == 0 --> New image available: Go ahead... if (waitForImage!=2 && waitForImage!=3) { // Ok, we need a texture for the image. Did script provide an old one for recycling? if (textureRecord) { // Old texture provided for reuse? Some basic sanity check: Everything else is // up to the lower level PsychGetTextureFromCapture() routine. if(!PsychIsOffscreenWindow(textureRecord)) { PsychErrorExitMsg(PsychError_user, "GetCapturedImage provided with something else than a texture as fourth call parameter."); } } else { // No old texture provided: Create a new texture record: PsychCreateWindowRecord(&textureRecord); // Set mode to 'Texture': textureRecord->windowType=kPsychTexture; // We need to assign the screen number of the onscreen-window. textureRecord->screenNumber=windowRecord->screenNumber; // It defaults to a 32 bit texture for captured images. On Linux, this will be overriden, // if optimized formats exist for our purpose: textureRecord->depth=32; textureRecord->nrchannels = 4; // Create default rectangle which describes the dimensions of the image. Will be overwritten // later on. PsychMakeRect(rect, 0, 0, 10, 10); PsychCopyRect(textureRecord->rect, rect); // Other setup stuff: textureRecord->textureMemorySizeBytes= 0; textureRecord->textureMemory=NULL; // Assign parent window and copy its inheritable properties: PsychAssignParentWindow(textureRecord, windowRecord); // Set textureNumber to zero, which means "Not cached, do not recycle" // Todo: Texture recycling like in PsychMovieSupport for higher efficiency! textureRecord->textureNumber = 0; } // Power-of-two texture requested? if (specialmode & 0x01) { // Yes. Spec it: textureRecord->texturetarget = GL_TEXTURE_2D; } } else { // Just want to return summed_intensity and timestamp, not real texture... textureRecord = NULL; } // Default to no calculation of summed image intensity: tsummed = NULL; if ((PsychGetNumOutputArgs() > 3) && !(specialmode & 0x2)) { // Return sum of pixel intensities for all channels of this image: Need to // assign the output pointer for this to happen: tsummed = &summed_intensity; } // Try to fetch an image from the capture object and return it as texture: targetmatrixptr = NULL; // Shall we return a Matlab matrix? if ((PsychGetNumOutputArgs() > 3) && (specialmode & 0x2)) { // We shall return a matrix with raw image data. Allocate a uint8 matrix // of sufficient size: PsychAllocOutUnsignedByteMatArg(4, TRUE, rawCaptureBuffer.depth, rawCaptureBuffer.w, rawCaptureBuffer.h, &targetmatrixptr); tsummed = NULL; } // Shall we return data into preallocated memory buffer? if (specialmode & 0x4) { // Copy in memory address (which itself is encoded in a double value): PsychCopyInDoubleArg(6, TRUE, &targetmemptr); targetmatrixptr = (psych_uint8*) PsychDoubleToPtr(targetmemptr); } if (targetmatrixptr == NULL) { // Standard fetch of a texture and its timestamp: rc = PsychGetTextureFromCapture(windowRecord, capturehandle, 0, 0.0, textureRecord, &presentation_timestamp, tsummed, NULL); } else { // Fetch of a memory raw image buffer + timestamp + possibly a texture: rawCaptureBuffer.data = (void*) targetmatrixptr; rc = PsychGetTextureFromCapture(windowRecord, capturehandle, 0, 0.0, textureRecord, &presentation_timestamp, tsummed, &rawCaptureBuffer); } if (tsummed) { // Return sum of pixel intensities for all channels of this image: PsychCopyOutDoubleArg(4, FALSE, summed_intensity); } // Real texture requested? if (textureRecord) { // Texture ready for consumption. // Assign GLSL filter-/lookup-shaders if needed: usefloatformat is always == 0 as // our current capture engine implementations only return 8 bpc fixed textures. // The 'userRequest' flag is set if specialmode flag is set to 8. PsychAssignHighPrecisionTextureShaders(textureRecord, windowRecord, 0, (specialmode & 8) ? 1 : 0); // specialmode setting 16? Disable auto-mipmap generation: if (specialmode & 16) textureRecord->specialflags |= kPsychDontAutoGenMipMaps; // Mark it valid and return handle to userspace: PsychSetWindowRecordValid(textureRecord); PsychCopyOutDoubleArg(1, TRUE, textureRecord->windowIndex); } else { PsychCopyOutDoubleArg(1, TRUE, 0); } // Return presentation timestamp for this image: PsychCopyOutDoubleArg(2, FALSE, presentation_timestamp); // Return count of pending frames in buffers or of dropped frames: PsychCopyOutDoubleArg(3, FALSE, (double) rc); // Ready! return(PsychError_none); }
PsychError SCREENWaitBlanking(void) { PsychWindowRecordType *windowRecord; int waitFrames, framesWaited; double tvbl, ifi; long screenwidth, screenheight; int vbl_startline, beampos, lastline; psych_uint64 vblCount, vblRefCount; CGDirectDisplayID cgDisplayID; GLint read_buffer, draw_buffer; // All subfunctions should have these two lines. PsychPushHelp(useString, synopsisString, seeAlsoString); if(PsychIsGiveHelp()){PsychGiveHelp();return(PsychError_none);}; PsychErrorExit(PsychCapNumInputArgs(2)); //The maximum number of inputs PsychErrorExit(PsychRequireNumInputArgs(1)); //The required number of inputs PsychErrorExit(PsychCapNumOutputArgs(1)); //The maximum number of outputs // Get the window record from the window record argument and get info from the window record PsychAllocInWindowRecordArg(kPsychUseDefaultArgPosition, TRUE, &windowRecord); if(!PsychIsOnscreenWindow(windowRecord)) PsychErrorExitMsg(PsychError_user, "Tried to call 'WaitBlanking' on something else than an onscreen window!"); // Get the number of frames to wait: waitFrames = 0; PsychCopyInIntegerArg(2, FALSE, &waitFrames); // We default to wait at least one interval if no argument supplied: waitFrames = (waitFrames < 1) ? 1 : waitFrames; // Enable this windowRecords framebuffer as current drawingtarget: // This is needed to make sure that Offscreen windows work propely. PsychSetDrawingTarget(windowRecord); // Retrieve display handle for beamposition queries: PsychGetCGDisplayIDFromScreenNumber(&cgDisplayID, windowRecord->screenNumber); // Retrieve final vbl_startline, aka physical height of the display in pixels: PsychGetScreenSize(windowRecord->screenNumber, &screenwidth, &screenheight); vbl_startline = (int) screenheight; // Query duration of a monitor refresh interval: We try to use the measured interval, // but fallback of the nominal value reported by the operating system if necessary: if ((ifi = windowRecord->VideoRefreshInterval)<=0) { if (PsychGetNominalFramerate(windowRecord->screenNumber) > 0) { // Valid nominal framerate returned by OS: Calculate nominal IFI from it. ifi = 1.0 / ((double) PsychGetNominalFramerate(windowRecord->screenNumber)); } else { // No reasonable value available! We fallback to an assumed 60 Hz refresh... ifi = 1.0 / 60.0; } } // Query vblcount to test if this method works correctly: PsychOSGetVBLTimeAndCount(windowRecord, &vblRefCount); // Check if beamposition queries are supported by this OS and working properly: if (-1 != PsychGetDisplayBeamPosition(cgDisplayID, windowRecord->screenNumber) && windowRecord->VBL_Endline >= 0) { // Beamposition queries supported and fine. We can wait for VBL without bufferswap-tricks: // This is the OS-X way of doing things. We query the rasterbeamposition and compare it // to the known values for the VBL area. If we enter VBL, we take a timestamp and return - // or wait for the next VBL if waitFrames>0 // Query current beamposition when entering WaitBlanking: beampos = PsychGetDisplayBeamPosition(cgDisplayID, windowRecord->screenNumber); // Are we in VBL when entering WaitBlanking? If so, we should wait for one additional frame, // because by definition, WaitBlanking should always wait for at least one monitor refresh // interval... if ((beampos<=windowRecord->VBL_Endline) && (beampos>=vbl_startline)) waitFrames++; while(waitFrames > 0) { // Enough time for a sleep? If the beam is far away from VBL area, we try to sleep to // yield some CPU time to other processes in the system -- we are nice citizens ;) beampos = PsychGetDisplayBeamPosition(cgDisplayID, windowRecord->screenNumber); while (( ((float)(vbl_startline - beampos)) / (float) windowRecord->VBL_Endline * ifi) > 0.003) { // At least 3 milliseconds left until retrace. We sleep for 1 millisecond. PsychWaitIntervalSeconds(0.001); beampos = PsychGetDisplayBeamPosition(cgDisplayID, windowRecord->screenNumber); } // Less than 3 ms away from retrace. Busy-Wait for retrace... lastline = PsychGetDisplayBeamPosition(cgDisplayID, windowRecord->screenNumber); beampos = lastline; while ((beampos < vbl_startline) && (beampos >= lastline)) { lastline = beampos; beampos = (long) PsychGetDisplayBeamPosition(cgDisplayID, windowRecord->screenNumber); } // Retrace! Take system timestamp of VBL onset: PsychGetAdjustedPrecisionTimerSeconds(&tvbl); // If this wasn't the last frame to wait, we need to wait for end of retrace before // repeating the loop, because otherwise we would detect the same VBL and skip frames. // If it is the last frame, we skip it and return as quickly as possible to save the // Matlab script some extra Millisecond for drawing... if (waitFrames>1) { beampos = vbl_startline; while ((beampos<=windowRecord->VBL_Endline) && (beampos>=vbl_startline)) { beampos = PsychGetDisplayBeamPosition(cgDisplayID, windowRecord->screenNumber); }; } // Done with this refresh interval... // Decrement remaining number of frames to wait: waitFrames--; } } else if (vblRefCount > 0) { // Display beamposition queries unsupported, but vblank count queries seem to work. Try those. // Should work on Linux and OS/X: while(waitFrames > 0) { vblCount = vblRefCount; // Wait for next vblank counter increment - aka start of next frame (its vblank): while (vblCount == vblRefCount) { // Requery: PsychOSGetVBLTimeAndCount(windowRecord, &vblCount); // Yield at least 100 usecs. This is accurate as this code-path // only executes on OS/X and Linux, never on Windows (as of 01/06/2011): PsychYieldIntervalSeconds(0.000100); } vblRefCount = vblCount; // Done with this refresh interval... // Decrement remaining number of frames to wait: waitFrames--; } } else { // Other methods unsupported. We use the doublebuffer swap method of waiting for retrace. // // Working principle: On each frame, we first copy the content of the (user visible) frontbuffer into the backbuffer. // Then we ask the OS to perform a front-backbuffer swap on next vertical retrace and go to sleep via glFinish() et al. // until the OS signals swap-completion. This way PTB's/Matlabs execution will stall until VBL, when swap happens and // we get woken up. We repeat this procedure 'waitFrames' times, then we take a high precision timestamp and exit the // Waitblanking loop. As backbuffer and frontbuffer are identical (due to the copy) at swap time, the visual display // won't change at all for the subject. // This method should work reliably, but it has one drawback: A random wakeup delay (scheduling jitter) is added after // retrace has been entered, so Waitblanking returns only after the beam has left retrace state on older hardware. // This means a bit less time (1 ms?) for stimulus drawing on Windows than on OS-X where Waitblanking returns faster. // Child protection: if (windowRecord->windowType != kPsychDoubleBufferOnscreen) { PsychErrorExitMsg(PsychError_internal, "WaitBlanking tried to perform swap-waiting on a single buffered window!"); } // Setup buffers for front->back copy op: // Backup old read- writebuffer assignments: glGetIntegerv(GL_READ_BUFFER, &read_buffer); glGetIntegerv(GL_DRAW_BUFFER, &draw_buffer); // Set read- and writebuffer properly: glReadBuffer(GL_FRONT); glDrawBuffer(GL_BACK); // Reset viewport to full-screen default: glViewport(0, 0, screenwidth, screenheight); glScissor(0, 0, screenwidth, screenheight); // Reset color buffer writemask to "All enabled": glColorMask(TRUE, TRUE, TRUE, TRUE); glDisable(GL_BLEND); glPixelZoom(1,1); // Disable draw shader: PsychSetShader(windowRecord, 0); // Swap-Waiting loop for 'waitFrames' frames: while(waitFrames > 0) { // Copy current content of front buffer into backbuffer: glRasterPos2i(0, screenheight); glCopyPixels(0, 0, screenwidth, screenheight, GL_COLOR); // Ok, front- and backbuffer are now identical, so a bufferswap // will be a visual no-op. // Enable beamsyncing of bufferswaps to VBL: PsychOSSetVBLSyncLevel(windowRecord, 1); // Trigger bufferswap in sync with retrace: PsychOSFlipWindowBuffers(windowRecord); // Wait for swap-completion, aka beginning of VBL: PsychWaitPixelSyncToken(windowRecord); // VBL happened - Take system timestamp: PsychGetAdjustedPrecisionTimerSeconds(&tvbl); // This code-chunk is an alternate way of syncing, only used for debugging: if (false) { // Disable beamsyncing of bufferswaps to VBL: PsychOSSetVBLSyncLevel(windowRecord, 0); // Swap buffers immediately without vsync: PsychOSFlipWindowBuffers(windowRecord); } // Decrement remaining number of frames to wait: waitFrames--; } // Do it again... // Enable beamsyncing of bufferswaps to VBL: PsychOSSetVBLSyncLevel(windowRecord, 1); // Restore assignment of read- writebuffers and such: glEnable(GL_BLEND); glReadBuffer(read_buffer); glDrawBuffer(draw_buffer); // Done with Windows waitblanking... } // Compute number of frames waited: It is timestamp of return of this waitblanking minus // timestamp of return of last waitblanking, divided by duration of a monitor refresh // interval, mathematically rounded to an integral number: framesWaited = (int) (((tvbl - windowRecord->time_at_last_vbl) / ifi) + 0.5f); // Update timestamp for next invocation of Waitblanking: windowRecord->time_at_last_vbl = tvbl; // Return optional number of frames waited: PsychCopyOutDoubleArg(1, FALSE, (double) framesWaited); // Done. return(PsychError_none); }
/* PsychWaitForBufferswapPendingOrFinished() * Waits until a bufferswap for window windowRecord has either already happened or * bufferswap is certain. * Input values: * windowRecord struct of onscreen window to monitor. * timestamp = Deadline for abortion of flip detection at input. * * Return values: * timestamp = System time at polling loop exit. * beamposition = Beamposition at polling loop exit. * * Return value: FALSE if swap happened already, TRUE if swap is imminent. */ psych_bool PsychWaitForBufferswapPendingOrFinished(PsychWindowRecordType* windowRecord, double* timestamp, int *beamposition) { #if PSYCH_SYSTEM == PSYCH_OSX || PSYCH_SYSTEM == PSYCH_LINUX int gpuMaintype, gpuMinortype; CGDirectDisplayID displayID; unsigned int primarySurface, secondarySurface; unsigned int updateStatus; double deadline = *timestamp; // If we are called, we know that 'windowRecord' is an onscreen window. int screenId = windowRecord->screenNumber; // Retrieve display id and screen size spec that is needed later... PsychGetCGDisplayIDFromScreenNumber(&displayID, screenId); #define RADEON_D1GRPH_UPDATE 0x6144 #define RADEON_D2GRPH_UPDATE 0x6944 #define RADEON_SURFACE_UPDATE_PENDING 4 #define RADEON_SURFACE_UPDATE_TAKEN 8 // Just need to check if GPU low-level access is supported: if (!PsychOSIsKernelDriverAvailable(screenId)) return(FALSE); // We only support Radeon GPU's with pre DCE-4 display engine, nothing more recent: if (!PsychGetGPUSpecs(screenId, &gpuMaintype, &gpuMinortype, NULL, NULL) || (gpuMaintype != kPsychRadeon) || (gpuMinortype >= 0x40)) { return(FALSE); } // Driver is online. Enter polling loop: while (TRUE) { // Read surface address registers: primarySurface = PsychOSKDReadRegister(screenId, (PsychScreenToCrtcId(screenId, 0) < 1) ? RADEON_D1GRPH_PRIMARY_SURFACE_ADDRESS : RADEON_D2GRPH_PRIMARY_SURFACE_ADDRESS, NULL); secondarySurface = PsychOSKDReadRegister(screenId, (PsychScreenToCrtcId(screenId, 0) < 1) ? RADEON_D1GRPH_SECONDARY_SURFACE_ADDRESS : RADEON_D2GRPH_SECONDARY_SURFACE_ADDRESS, NULL); // Read update status registers: updateStatus = PsychOSKDReadRegister(screenId, (PsychScreenToCrtcId(screenId, 0) < 1) ? RADEON_D1GRPH_UPDATE : RADEON_D2GRPH_UPDATE, NULL); PsychGetAdjustedPrecisionTimerSeconds(timestamp); if (primarySurface!=windowRecord->gpu_preflip_Surfaces[0] || secondarySurface!=windowRecord->gpu_preflip_Surfaces[1] || (updateStatus & (RADEON_SURFACE_UPDATE_PENDING | RADEON_SURFACE_UPDATE_TAKEN)) || (*timestamp > deadline)) { // Abort condition: Exit loop. break; } if (PsychPrefStateGet_Verbosity() > 9) { printf("PTB-DEBUG: Head %i: primarySurface=%p : secondarySurface=%p : updateStatus=%i\n", PsychScreenToCrtcId(screenId, 0), primarySurface, secondarySurface, updateStatus); } // Sleep slacky at least 200 microseconds, then retry: PsychYieldIntervalSeconds(0.0002); }; // Take timestamp and beamposition: *beamposition = PsychGetDisplayBeamPosition(displayID, screenId); PsychGetAdjustedPrecisionTimerSeconds(timestamp); // Exit due to timeout? if (*timestamp > deadline) { // Mark timestamp as invalid due to timeout: *timestamp = -1; } // Return FALSE if bufferswap happened already, TRUE if swap is still pending: return((updateStatus & RADEON_SURFACE_UPDATE_PENDING) ? TRUE : FALSE); #else // On Windows, we always return "swap happened": return(FALSE); #endif }
/* PsychSetGPUIdentityPassthrough() - Control identity passthrough of framebuffer 8 bpc pixel values to encoders/connectors: * * This function enables or disables bit depths truncation or dithering of digital display output ports of supported * graphics hardware, and optionally loads a identity LUT into the hardware and configures other parts of the GPU's * color management for untampered passthrough of framebuffer pixels. * Currently the ATI Radeon X1000/HD2000/HD3000/HD4000/HD5000/HD6000 and later cards should allow this. * * This needs support from the Psychtoolbox kernel level support driver for low-level register reads * and writes to the GPU registers. * * * 'windowRecord' Is used to find the Id of the screen for which mode should be changed. If set to NULL then... * 'screenId' ... is used to determine the screenId for the screen. Otherwise 'screenId' is ignored. * 'passthroughEnable' Zero = Disable passthrough: Currently only reenables dithering, otherwise a no-op. * 1 = Enable passthrough, if possible. * 'changeDithering' FALSE = Don't touch dither control, TRUE = Control dithering enable/disable if possible. * * Returns: * * 0xffffffff if feature unsupported by given OS/Driver/GPU combo. * 0 = On failure to establish passthrough. * 1 = On partial success: Dithering disabled and identityt LUT loaded, but other GPU color transformation * features may not be configured optimally for passthrough. * 2 = On full success, as far as can be determined by software. * */ unsigned int PsychSetGPUIdentityPassthrough(PsychWindowRecordType* windowRecord, int screenId, psych_bool passthroughEnable, psych_bool changeDithering) { #if PSYCH_SYSTEM == PSYCH_OSX || PSYCH_SYSTEM == PSYCH_LINUX unsigned int rc, rcret; int head, iter; // Init return code to "unsupported": rcret = 0xffffffff; // Child protection: if (windowRecord && !PsychIsOnscreenWindow(windowRecord)) PsychErrorExitMsg(PsychError_internal, "Invalid non-onscreen windowRecord provided!!!"); // Either screenid from windowRecord or as passed in: if (windowRecord) screenId = windowRecord->screenNumber; // Check if kernel driver is enabled, otherwise this won't work: if (!PsychOSIsKernelDriverAvailable(screenId)) { if(PsychPrefStateGet_Verbosity() > 3) printf("PTB-INFO: GPU framebuffer passthrough setup requested, but this is not supported without kernel driver.\n"); return(0xffffffff); } // Try to enable or disable dithering on display: if (changeDithering) PsychSetOutputDithering(windowRecord, screenId, (passthroughEnable) ? 0 : 1); // We're done if this an actual passthrough disable, as a full disable isn't yet implemented: if (!passthroughEnable) return(0); // Start with head undefined: head = -1; for (iter = 0; iter < kPsychMaxPossibleCrtcs; iter++) { if (screenId >= 0) { // Positive screenId: Apply to all crtc's for this screenId: // Is there an iter'th crtc assigned to this screen? head = PsychScreenToCrtcId(screenId, iter); // If end of list of associated crtc's for this screenId reached, then we're done: if (head < 0) break; } else { // Negative screenId -> Only affect one head defined by screenId: if (head < 0) { // Setup single target head in this iteration: head = -screenId; } else { // Single target head already set up: We're done: break; } } // Check if remaining GPU is already configured for untampered identity passthrough: rc = PsychOSKDGetLUTState(screenId, head, (PsychPrefStateGet_Verbosity() > 4) ? 1 : 0); if(PsychPrefStateGet_Verbosity() > 3) printf("PTB-INFO: [screen %i, head %i] 1st LUT query rc = %i.\n", screenId, head, rc); if (rc == 0xffffffff) { // Unsupported for this GPU. We're done: if(PsychPrefStateGet_Verbosity() > 3) printf("PTB-INFO: GPU framebuffer passthrough setup on screenid %i, head %i requested, but this is not supported on this GPU.\n", screenId, head); rcret = 0xffffffff; continue; } // Perfect identity passthrough already configured? if (rc == 2) { // Yes. We're successfully done! if(PsychPrefStateGet_Verbosity() > 3) printf("PTB-INFO: GPU framebuffer passthrough setup I completed on screenid %i, head %i. Perfect passthrough should work now.\n", screenId, head); rcret = 2; continue; } // No. Try to setup GPU for passthrough: if (!PsychOSKDLoadIdentityLUT(screenId, head)) { // Failed. if(PsychPrefStateGet_Verbosity() > 3) printf("PTB-INFO: GPU framebuffer passthrough setup requested on screenid %i, head %i, but setup failed.\n", screenId, head); rcret = 0xffffffff; continue; } // Make sure, GPU's gamma table can settle by waiting 250 msecs: PsychYieldIntervalSeconds(0.250); // Setup supposedly successfully finished. Re-Query state: rc = PsychOSKDGetLUTState(screenId, head, (PsychPrefStateGet_Verbosity() > 4) ? 1 : 0); if(PsychPrefStateGet_Verbosity() > 3) printf("PTB-INFO: [screen %i, head %i] 2nd LUT query rc = %i.\n", screenId, head, rc); // Perfect identity passthrough now configured? if (rc == 2) { // Yes. We're successfully done! if(PsychPrefStateGet_Verbosity() > 3) printf("PTB-INFO: GPU framebuffer passthrough setup II completed on screenid %i, head %i. Perfect passthrough should work now.\n", screenId, head); rcret = 2; continue; } if (rc == 3) { // Not quite. We've done what we could. A perfect identity LUT is setup, but the rest of the hw // isn't in that a great shape. This may or may not be good enough... if(PsychPrefStateGet_Verbosity() > 3) { printf("PTB-INFO: GPU framebuffer passthrough setup II completed on screenid %i, head %i. Sort of ok passthrough achieved. May or may not work.\n", screenId, head); printf("PTB-INFO: A perfect identity gamma table is loaded, but the other GPU color transformation settings are still suboptimal.\n"); } rcret = 1; continue; } // Ok, we failed. if(PsychPrefStateGet_Verbosity() > 3) { printf("PTB-INFO: GPU framebuffer passthrough setup II completed on screenid %i, head %i. Failed to establish identity passthrough!\n", screenId, head); printf("PTB-INFO: Could not upload a perfect identity LUT. May still work due to hopefully disabled dithering, who knows?\n"); } rcret = 0; } return(rcret); #else // This cool stuff not supported on the uncool Windows OS: if(PsychPrefStateGet_Verbosity() > 4) printf("PTB-INFO: GPU framebuffer passthrough setup requested, but this is not supported on MS-Windows.\n"); return(0xffffffff); #endif }
/* PsychOSOpenOnscreenWindow() Creates the CGL pixel format and the CGL context objects and then instantiates the context onto the screen. -The pixel format and the context are stored in the target specific field of the window recored. Close should clean up by destroying both the pixel format and the context. -We mantain the context because it must be be made the current context by drawing functions to draw into the specified window. -We maintain the pixel format object because there seems to be now way to retrieve that from the context. -To tell the caller to clean up PsychOSOpenOnscreenWindow returns FALSE if we fail to open the window. It would be better to just issue an PsychErrorExit() and have that clean up everything allocated outside of PsychOpenOnscreenWindow(). MK: The new option 'stereomode' allows selection of stereo display instead of mono display: 0 (default) == Old behaviour -> Monoscopic rendering context. >0 == Stereo display, where the number defines the type of stereo algorithm to use. =1 == Use OpenGL built-in stereo by creating a context/window with left- and right backbuffer. This is the only mode of interest here, as it requires use of a stereo capable OpenGL pixelformat. All other stereo modes are implemented by PTB itself in a platform independent manner on top of a standard mono context. */ psych_bool PsychOSOpenOnscreenWindow(PsychScreenSettingsType *screenSettings, PsychWindowRecordType *windowRecord, int numBuffers, int stereomode, int conserveVRAM) { CGOpenGLDisplayMask displayMask; CGLError error; CGDirectDisplayID cgDisplayID; CGLPixelFormatAttribute attribs[40]; int attribcount; GLint numVirtualScreens; GLenum glerr; PsychRectType screenrect; int i; int windowLevel; long scw, sch; void* cocoaWindow = NULL; // Map screen number to physical display handle cgDisplayID: PsychGetCGDisplayIDFromScreenNumber(&cgDisplayID, screenSettings->screenNumber); displayMask = CGDisplayIDToOpenGLDisplayMask(cgDisplayID); // NULL-out Cocoa window handle, so this is well-defined in case of error: windowRecord->targetSpecific.windowHandle = NULL; // Retrieve windowLevel, an indicator of where non-CGL / non-fullscreen windows should // be located wrt. to other windows. -2 = Allow regular window manager control of stacking // order, visibility etc., -1 = Invisible/hidden, 0 = Behind everything else, occluded by // everything else. 1 - 999 = At layer 'windowLevel' -> Occludes stuff on layers "below" it. // 1000 - 1999 = At highest level, but partially translucent / alpha channel allows to make // regions transparent. Range 1000 - 1499 = transparent for mouse and keyboard, alpha 0-99.99%, // 1500-1599 = opaque for mouse and keyboard, alpha 0-99.99%, 2000 or higher: Above everything, // fully opaque, occludes everything, a typical fullscreen onscreen window. 2000 is the default. windowLevel = PsychPrefStateGet_WindowShieldingLevel(); // Window rect provided which has same size as screen? // We do not use windowed mode if the provided window rectangle either // matches the target screens rectangle (and therefore its exact size) // or its screens global rectangle. In such cases we use CGL for better // low level control and to exclude the desktop compositor from interfering: PsychGetScreenRect(screenSettings->screenNumber, screenrect); if (PsychMatchRect(screenrect, windowRecord->rect)) windowRecord->specialflags |= kPsychIsFullscreenWindow; PsychGetGlobalScreenRect(screenSettings->screenNumber, screenrect); if (PsychMatchRect(screenrect, windowRecord->rect)) windowRecord->specialflags |= kPsychIsFullscreenWindow; if ((windowRecord->specialflags & kPsychIsFullscreenWindow) && (PsychPrefStateGet_Verbosity() > 3)) { printf("PTB-INFO: Always using Cocoa for fullscreen windows to work around graphics driver bugs in OSX.\n"); printf("PTB-INFO: Presentation timing precision is not yet known for this configuration on most machines. Check your results.\n"); } // Display for fullscreen window not captured? Timing precision is unclear in this mode. In theory the compositor should disable // itself for fullscreen windows on modern OSX versions. If it really does that, who knows? if ((windowRecord->specialflags & kPsychIsFullscreenWindow) && (PsychPrefStateGet_ConserveVRAM() & kPsychUseAGLCompositorForFullscreenWindows)) { // Force a window rectangle that matches the global screen rectangle for that windows screen: PsychCopyRect(windowRecord->rect, screenrect); // Warn user about what's going on: if (PsychPrefStateGet_Verbosity()>1) printf("PTB-INFO: No display capture / compositor lockout for fullscreen window. Timing precision is unknown.\n"); } if ((windowRecord->specialflags & kPsychGUIWindow) && (PsychPrefStateGet_Verbosity() > 3)) { printf("PTB-INFO: Onscreen window is configured as regular GUI window.\n"); } // Create onscreen Cocoa window of requested position and size: if (PsychCocoaCreateWindow(windowRecord, windowLevel, &cocoaWindow)) { printf("\nPTB-ERROR[CreateNewWindow failed]: Failed to open Cocoa onscreen window\n\n"); return(FALSE); } // Transparent window requested? if ((windowLevel >= 1000) && (windowLevel < 2000)) { // Setup of global window alpha value for transparency. This is premultiplied to // the individual per-pixel alpha values if transparency is enabled by Cocoa code. // // Levels 1000 - 1499 and 1500 to 1999 map to a master opacity level of 0.0 - 1.0: PsychCocoaSetWindowAlpha(cocoaWindow, ((float) (windowLevel % 500)) / 499.0); } // Show it! Unless a windowLevel of -1 requests hiding the window: if (windowLevel != -1) PsychCocoaShowWindow(cocoaWindow); // If usercode wants a black startup screen then we add a pause of 0.5 seconds here // before proceeding. This will avoid a white flash at window open time, which might // be something the user wanted to avoid. Why does this help or is needed at all? // Nobody knows, but this is Apples ridiculous toy OS, so why even ask such questions? if (PsychPrefStateGet_VisualDebugLevel() < 4) PsychYieldIntervalSeconds(0.5); // Level zero means: Place behind all other windows: if (windowLevel == 0) PsychCocoaSendBehind(cocoaWindow); // Levels 1 to 999 define window levels for the group of the window. // A level of -2 would leave this to the system: if (windowLevel > 0 && windowLevel < 1000) PsychCocoaSetWindowLevel(cocoaWindow, windowLevel); // Is the target display captured for a fullscreen window? if (PsychIsScreenCaptured(screenSettings->screenNumber)) { // Yes. Make sure our window is above the shielding window level: PsychCocoaSetWindowLevel(cocoaWindow, (int) CGShieldingWindowLevel()); } // Store window handle in windowRecord: windowRecord->targetSpecific.windowHandle = cocoaWindow; // Store vblank startline aka true height of physical display screen in pixels: PsychGetScreenPixelSize(screenSettings->screenNumber, &scw, &sch); windowRecord->VBL_Startline = (int) sch; // Define pixelformat attributes for OpenGL contexts: // No pixelformat attribs to start with: attribcount = 0; attribs[attribcount++]=kCGLPFADisplayMask; attribs[attribcount++]=displayMask; // 10 bit per component integer framebuffer requested (10-10-10-2)? if (windowRecord->depth == 30) { // Request a 10 bit per color component framebuffer with 2 bit alpha channel: printf("PTB-INFO: Trying to enable 10 bpc, 30 bit integer framebuffer...\n"); attribs[attribcount++]=kCGLPFANoRecovery; attribs[attribcount++]=kCGLPFAMinimumPolicy; attribs[attribcount++]=kCGLPFAColorSize; attribs[attribcount++]=10*3; attribs[attribcount++]=kCGLPFAAlphaSize; attribs[attribcount++]=2; } // 11 bit per component integer framebuffer requested (11-11-10-0)? if (windowRecord->depth == 33) { // Request a ~ 11 bit per color component framebuffer without alpha channel: printf("PTB-INFO: Trying to enable 11 bpc, 32 bit integer framebuffer...\n"); attribs[attribcount++]=kCGLPFANoRecovery; attribs[attribcount++]=kCGLPFAMinimumPolicy; attribs[attribcount++]=kCGLPFAColorSize; attribs[attribcount++]=32; attribs[attribcount++]=kCGLPFAAlphaSize; attribs[attribcount++]=0; } // 16 bit per component integer framebuffer requested (16-16-16-16)? if (windowRecord->depth == 48) { // Request a 16 bit per color component framebuffer: printf("PTB-INFO: Trying to enable 16 bpc, 64 bit integer framebuffer...\n"); attribs[attribcount++]=kCGLPFANoRecovery; attribs[attribcount++]=kCGLPFAMinimumPolicy; attribs[attribcount++]=kCGLPFAColorSize; attribs[attribcount++]=16*3; attribs[attribcount++]=kCGLPFAAlphaSize; attribs[attribcount++]=16; } // 16 bit per component, 64 bit framebuffer requested (16-16-16-16)? if (windowRecord->depth == 64) { // Request a floating point framebuffer in 16-bit half-float format, i.e., RGBA = 16 bits per component. printf("PTB-INFO: Trying to enable 16 bpc float framebuffer...\n"); attribs[attribcount++]=kCGLPFAColorFloat; attribs[attribcount++]=kCGLPFAMinimumPolicy; attribs[attribcount++]=kCGLPFAColorSize; attribs[attribcount++]=16*3; attribs[attribcount++]=kCGLPFAAlphaSize; attribs[attribcount++]=16; } // 32 bit per component, 128 bit framebuffer requested (32-32-32-32)? if (windowRecord->depth == 128) { // Request a floating point framebuffer in 32-bit float format, i.e., RGBA = 32 bits per component. printf("PTB-INFO: Trying to enable 32 bpc float framebuffer...\n"); attribs[attribcount++]=kCGLPFAColorFloat; attribs[attribcount++]=kCGLPFAMinimumPolicy; attribs[attribcount++]=kCGLPFAColorSize; attribs[attribcount++]=32*3; attribs[attribcount++]=kCGLPFAAlphaSize; attribs[attribcount++]=32; } // Possible to request use of the Apple floating point software renderer: if (conserveVRAM & kPsychUseSoftwareRenderer) { #ifndef kCGLRendererGenericFloatID #define kCGLRendererGenericFloatID 0x00020400 #endif attribs[attribcount++]=AGL_RENDERER_ID; attribs[attribcount++]=kCGLRendererGenericFloatID; } // Support for 3D rendering requested? if (PsychPrefStateGet_3DGfx()) { // Yes. Allocate a 24-Bit depth and 8-Bit stencilbuffer for this purpose: attribs[attribcount++]=kCGLPFADepthSize; attribs[attribcount++]=24; attribs[attribcount++]=kCGLPFAStencilSize; attribs[attribcount++]=8; // Alloc an accumulation buffer as well? if (PsychPrefStateGet_3DGfx() & 2) { // Yes: Alloc accum buffer, request 64 bpp, aka 16 bits integer per color component if possible: attribs[attribcount++]=kCGLPFAAccumSize; attribs[attribcount++]=64; } } if(numBuffers>=2){ // Enable double-buffering: attribs[attribcount++]=kCGLPFADoubleBuffer; if ((conserveVRAM & kPsychDisableAUXBuffers) == 0) { // Allocate one or two (for mono vs. stereo display) AUX buffers for "don't clear" mode of Screen('Flip'): // Not clearing the framebuffer after "Flip" is implemented by storing a backup-copy of // the backbuffer to AUXs before flip and restoring the content from AUXs after flip. // Unless the imaging pipeline is active, which doesn't need AUX buffers due to internal // storage of fb content in its drawbufferFBO's: attribs[attribcount++]=kCGLPFAAuxBuffers; attribs[attribcount++]=(stereomode==kPsychOpenGLStereo || stereomode==kPsychCompressedTLBRStereo || stereomode==kPsychCompressedTRBLStereo) ? 2 : 1; } } // If stereo display output is requested with OpenGL native stereo, request a stereo-enabled rendering context. // This is deprecated since 10.11 El Capitan, and in fact does no longer work - OpenGL quad buffered stereo is // dead on 10.11 on all tested GPU's from Intel, NVidia, AMD. if(stereomode==kPsychOpenGLStereo) { attribs[attribcount++] = kCGLPFAStereo; } // Multisampled Anti-Aliasing requested? if (windowRecord->multiSample > 0) { // Request a multisample buffer: attribs[attribcount++]= kCGLPFASampleBuffers; attribs[attribcount++]= 1; // Request at least multiSample samples per pixel: attribs[attribcount++]= kCGLPFASamples; attribs[attribcount++]= windowRecord->multiSample; } // Finalize attribute array with NULL. attribs[attribcount++]=(CGLPixelFormatAttribute)NULL; // Init to zero: windowRecord->targetSpecific.pixelFormatObject = NULL; windowRecord->targetSpecific.glusercontextObject = NULL; windowRecord->targetSpecific.glswapcontextObject = NULL; // Try to find matching pixelformat: error = CGLChoosePixelFormat(attribs, &(windowRecord->targetSpecific.pixelFormatObject), &numVirtualScreens); // No valid pixelformat found and stereo format requested? if ((error || (windowRecord->targetSpecific.pixelFormatObject == NULL)) && (stereomode == kPsychOpenGLStereo)) { // Yep: Stereo may be the culprit. Remove the stereo attribute by overwriting it with something // that is essentially a no-op, specifically kCGLPFAAccelerated which is supported by all real // renderers that might end up in this code-path: for (i = 0; i < attribcount && attribs[i] != kCGLPFAStereo; i++); attribs[i] = kCGLPFAAccelerated; // Retry query of pixelformat without request for native OpenGL quad-buffered stereo. If we succeed, we're // sort of ok, as the higher-level code will fallback to stereomode kPsychFrameSequentialStereo - our own // homegrown frame-sequential stereo support, which may be good enough. error = CGLChoosePixelFormat(attribs, &(windowRecord->targetSpecific.pixelFormatObject), &numVirtualScreens); if (error || (windowRecord->targetSpecific.pixelFormatObject == NULL)) { windowRecord->targetSpecific.pixelFormatObject = NULL; printf("\nPTB-ERROR[ChoosePixelFormat failed: %s]: Disabling OpenGL native quad-buffered stereo did not help. Moving on...\n\n", CGLErrorString(error)); } } // Now try if choosing a matching format for a lower multisample mode helps to get unstuck: if (windowRecord->multiSample > 0) { if (windowRecord->targetSpecific.pixelFormatObject==NULL && windowRecord->multiSample > 0) { // Failed. Probably due to too demanding multisample requirements: Lets lower them... for (i = 0; i < attribcount && attribs[i] != kCGLPFASamples; i++); while (windowRecord->targetSpecific.pixelFormatObject == NULL && windowRecord->multiSample > 0) { attribs[i+1]--; windowRecord->multiSample--; error = CGLChoosePixelFormat(attribs, &(windowRecord->targetSpecific.pixelFormatObject), &numVirtualScreens); } if (windowRecord->multiSample == 0 && windowRecord->targetSpecific.pixelFormatObject == NULL) { // Ok, multisampling is now at zero and we still don't succeed. Disable multisampling completely: for (i=0; i<attribcount && attribs[i]!=kCGLPFASampleBuffers; i++); attribs[i+1] = 0; printf("\nPTB-ERROR[ChoosePixelFormat failed: %s]: Disabling multisample anti-aliasing did not help. Moving on...\n\n", CGLErrorString(error)); } } } // Try choosing a matching display configuration again and create the window and rendering context: // If one of these two fails, then the installed gfx hardware is not good enough to satisfy our // requirements, or we have massive ressource shortage in the system. -> Screwed up anyway, so we abort. if (windowRecord->targetSpecific.pixelFormatObject == NULL) error = CGLChoosePixelFormat(attribs, &(windowRecord->targetSpecific.pixelFormatObject), &numVirtualScreens); if (error) { printf("\nPTB-ERROR[ChoosePixelFormat failed: %s]: Reason unknown. There could be insufficient video memory or a driver malfunction. Giving up.\n\n", CGLErrorString(error)); return(FALSE); } // Create an OpenGL rendering context with the selected pixelformat: Share its ressources with 'slaveWindow's context, if slaveWindow is non-NULL. // If slaveWindow is non-NULL here, then slaveWindow is typically another onscreen window. Therefore this establishes OpenGL resource sharing across // different onscreen windows in a session, e.g., for multi-display operation: error=CGLCreateContext(windowRecord->targetSpecific.pixelFormatObject, ((windowRecord->slaveWindow) ? windowRecord->slaveWindow->targetSpecific.contextObject : NULL), &(windowRecord->targetSpecific.contextObject)); if (error) { printf("\nPTB-ERROR[ContextCreation failed: %s]: Could not create master OpenGL context for new onscreen window. Insufficient video memory?\n\n", CGLErrorString(error)); return(FALSE); } // Enable the OpenGL rendering context associated with our window: error=CGLSetCurrentContext(windowRecord->targetSpecific.contextObject); if (error) { printf("\nPTB-ERROR[SetCurrentContext failed: %s]: Insufficient video memory\n\n", CGLErrorString(error)); return(FALSE); } // NULL-out the AGL context field, just for safety... windowRecord->targetSpecific.deviceContext = NULL; // Ok, the master OpenGL rendering context for this new onscreen window is up and running. // Auto-detect and bind all available OpenGL extensions via GLEW: glerr = glewInit(); if (GLEW_OK != glerr) { /* Problem: glewInit failed, something is seriously wrong. */ printf("\nPTB-ERROR[GLEW init failed: %s]: Please report this to the forum. Will try to continue, but may crash soon!\n\n", glewGetErrorString(glerr)); fflush(NULL); } else { if (PsychPrefStateGet_Verbosity()>3) printf("PTB-INFO: Using GLEW version %s for automatic detection of OpenGL extensions...\n", glewGetString(GLEW_VERSION)); } // Enable multisampling if it was requested: if (windowRecord->multiSample > 0) glEnable(GL_MULTISAMPLE); // External 3D graphics support enabled? if (PsychPrefStateGet_3DGfx()) { // Yes. We need to create an extra OpenGL rendering context for the external // OpenGL code to provide optimal state-isolation. The context shares all // heavyweight ressources likes textures, FBOs, VBOs, PBOs, shader, display lists and // starts off as an identical copy of PTB's context as of here. error=CGLCreateContext(windowRecord->targetSpecific.pixelFormatObject, windowRecord->targetSpecific.contextObject, &(windowRecord->targetSpecific.glusercontextObject)); if (error) { printf("\nPTB-ERROR[UserContextCreation failed: %s]: Creating a private OpenGL context for userspace OpenGL failed.\n\n", CGLErrorString(error)); return(FALSE); } } // Create glswapcontextObject - An OpenGL context for exclusive use by parallel background threads, // e.g., our thread for async flip operations and self-made frame-sequential stereo: error=CGLCreateContext(windowRecord->targetSpecific.pixelFormatObject, windowRecord->targetSpecific.contextObject, &(windowRecord->targetSpecific.glswapcontextObject)); if (error) { printf("\nPTB-ERROR[SwapContextCreation failed: %s]: Creating a private OpenGL context for async-bufferswaps failed.\n\n", CGLErrorString(error)); CGLSetCurrentContext(NULL); return(FALSE); } // Store Cocoa onscreen window handle: windowRecord->targetSpecific.windowHandle = cocoaWindow; // Objective-C setup path, using Cocoa + NSOpenGLContext wrapped around already // existing and setup CGLContext: if (PsychCocoaSetupAndAssignOpenGLContextsFromCGLContexts(cocoaWindow, windowRecord)) { printf("\nPTB-ERROR[Cocoa OpenGL setup failed]: Setup failed for unknown reasons.\n\n"); PsychCocoaDisposeWindow(windowRecord); return(FALSE); } // Check for output display rotation enabled. Will impair timing/timestamping because // it uses the desktop compositor for a rotated copy blit, instead of via rotated crtc // scanout, as most crtc's don't support this in hardware: if ((((int) CGDisplayRotation(cgDisplayID)) != 0) && (PsychPrefStateGet_Verbosity() > 1)) { printf("PTB-WARNING: Your onscreen windows output display has rotation enabled. It is not displaying in upright orientation.\n"); printf("PTB-WARNING: On most graphics cards this will cause unreliable stimulus presentation timing and timestamping.\n"); printf("PTB-WARNING: If you want non-upright stimulus presentation, look at 'help PsychImaging' on how to achieve this in\n"); printf("PTB-WARNING: a way that doesn't impair timing. The subfunctions 'FlipHorizontal' and 'FlipVertical' are what you probably need.\n"); } // First reference to this screen by a window? if (screenRefCount[screenSettings->screenNumber] == 0) { // High precision timestamping enabled? If so, we need to setup the fallback // timestamping methods in case beamposition timestamping doesn't work: if (PsychPrefStateGet_VBLTimestampingMode() > 0) { // Use of CoreVideo is needed on 10.7 and later due to brokeness of the old method (thanks Apple!): if (PsychPrefStateGet_Verbosity() > 2) { printf("PTB-INFO: Will use fragile CoreVideo timestamping as fallback if beamposition timestamping doesn't work.\n"); // Recommend use of kernel driver if it isn't installed already for all but Intel GPU's: if (!PsychOSIsKernelDriverAvailable(screenSettings->screenNumber) && !strstr((char*) glGetString(GL_VENDOR), "Intel")) { printf("PTB-INFO: Installation of the PsychtoolboxKernelDriver is strongly recommended if you care about precise visual\n"); printf("PTB-INFO: onset timestamping or timing. See 'help PsychtoolboxKernelDriver' for installation instructions.\n"); } } if (NULL == cvDisplayLink[screenSettings->screenNumber]) { // CoreVideo timestamping: // // Create and start a CVDisplayLink for this screen. if (kCVReturnSuccess != CVDisplayLinkCreateWithCGDisplay(cgDisplayID, &cvDisplayLink[screenSettings->screenNumber])) { if (PsychPrefStateGet_Verbosity()>1) printf("PTB-WARNING: Failed to create CVDisplayLink for screenId %i. This may impair VBL timestamping.\n", screenSettings->screenNumber); } else { // Assign dummy output callback, as this is mandatory to get the link up and running: CVDisplayLinkSetOutputCallback(cvDisplayLink[screenSettings->screenNumber], &PsychCVDisplayLinkOutputCallback, (void*) (long int) screenSettings->screenNumber); // Setup shared data structure and mutex: memset(&cvDisplayLinkData[screenSettings->screenNumber], 0, sizeof(cvDisplayLinkData[screenSettings->screenNumber])); PsychInitMutex(&(cvDisplayLinkData[screenSettings->screenNumber].mutex)); // Start the link: if (kCVReturnSuccess != CVDisplayLinkStart(cvDisplayLink[screenSettings->screenNumber])) { // Failed to start: Release it again and report error: CVDisplayLinkRelease(cvDisplayLink[screenSettings->screenNumber]); cvDisplayLink[screenSettings->screenNumber] = NULL; // Teardown shared data structure and mutex: PsychDestroyMutex(&(cvDisplayLinkData[screenSettings->screenNumber].mutex)); if (PsychPrefStateGet_Verbosity()>1) printf("PTB-WARNING: Failed to start CVDisplayLink for screenId %i. This may impair VBL timestamping.\n", screenSettings->screenNumber); } else { // Display link started: Report some stuff for the fun of it... if (PsychPrefStateGet_Verbosity() > 3) { // Wait for 50 msecs before query of video refresh from display link to give it a chance to start up: PsychWaitIntervalSeconds(0.050); printf("PTB-INFO: CVDisplayLink for screen %i created to work around the brokenness of Apple Mac OS/X 10.7 and later:\n", screenSettings->screenNumber); printf("PTB-INFO: Video refresh interval as measured by CoreVideo display link: %f msecs.\n", (float) CVDisplayLinkGetActualOutputVideoRefreshPeriod(cvDisplayLink[screenSettings->screenNumber]) * 1000.0); CVTime outLatency = CVDisplayLinkGetOutputVideoLatency(cvDisplayLink[screenSettings->screenNumber]); printf("PTB-INFO: Video display output delay as reported by CoreVideo display link: %f msecs.\n", screenSettings->screenNumber, (float) (((double) outLatency.timeValue / (double) outLatency.timeScale) * 1000.0)); } } } } } else { // VBLtimestampingmode 0 or -1 -- No CoreVideo fallback for timestamping if beamposition timestamping is unavailable: // This is the new default as of Psychtoolbox 3.0.12 to avoid the buggy, crashy, unreliably CoreVideo fallback. // Recommend use of kernel driver if it isn't installed already for all but Intel GPU's: if (!PsychOSIsKernelDriverAvailable(screenSettings->screenNumber) && !strstr((char*) glGetString(GL_VENDOR), "Intel")) { printf("PTB-INFO: Installation of the PsychtoolboxKernelDriver is strongly recommended if you care about precise visual\n"); printf("PTB-INFO: onset timestamping or timing. See 'help PsychtoolboxKernelDriver' for installation instructions.\n"); } } } // Retain reference of this window to its screen: screenRefCount[screenSettings->screenNumber]++; // Done. return(TRUE); }
int PsychFinalizeNewMovieFile(int movieHandle) { int myErr = 0; GError *ret = NULL; PsychMovieWriterRecordType* pwriterRec = PsychGetMovieWriter(movieHandle, FALSE); if (NULL == pwriterRec->ptbvideoappsrc) return(0); // Release any pending buffers: if (pwriterRec->PixMap) gst_buffer_unref(pwriterRec->PixMap); pwriterRec->PixMap = NULL; PsychGSProcessMovieContext(pwriterRec->Context, FALSE); // Send EOS signal downstream: g_signal_emit_by_name(pwriterRec->ptbvideoappsrc, "end-of-stream", &ret); if (pwriterRec->ptbaudioappsrc) g_signal_emit_by_name(pwriterRec->ptbaudioappsrc, "end-of-stream", &ret); // Wait for eos flag to turn TRUE due to bus callback receiving the // downstream EOS event that we just sent out: while (!pwriterRec->eos) { PsychGSProcessMovieContext(pwriterRec->Context, FALSE); PsychYieldIntervalSeconds(0.010); } // Yield another 10 msecs after EOS signalled, just to be safe: PsychYieldIntervalSeconds(0.010); PsychGSProcessMovieContext(pwriterRec->Context, FALSE); // Pause the encoding pipeline: if (!PsychMoviePipelineSetState(pwriterRec->Movie, GST_STATE_PAUSED, 10)) { if (PsychPrefStateGet_Verbosity() > 0) printf("PTB-ERROR: Failed to pause movie encoding pipeline at close time!!\n"); } PsychGSProcessMovieContext(pwriterRec->Context, FALSE); // Stop the encoding pipeline: if (!PsychMoviePipelineSetState(pwriterRec->Movie, GST_STATE_READY, 10)) { if (PsychPrefStateGet_Verbosity() > 0) printf("PTB-ERROR: Failed to stop movie encoding pipeline at close time!!\n"); } PsychGSProcessMovieContext(pwriterRec->Context, FALSE); // Shutdown and release encoding pipeline: if (!PsychMoviePipelineSetState(pwriterRec->Movie, GST_STATE_NULL, 10)) { if (PsychPrefStateGet_Verbosity() > 0) printf("PTB-ERROR: Failed to shutdown movie encoding pipeline at close time!!\n"); } PsychGSProcessMovieContext(pwriterRec->Context, FALSE); gst_object_unref(GST_OBJECT(pwriterRec->Movie)); pwriterRec->Movie = NULL; if (pwriterRec->ptbvideoappsrc) gst_object_unref(GST_OBJECT(pwriterRec->ptbvideoappsrc)); pwriterRec->ptbvideoappsrc = NULL; if (pwriterRec->ptbaudioappsrc) gst_object_unref(GST_OBJECT(pwriterRec->ptbaudioappsrc)); pwriterRec->ptbaudioappsrc = NULL; // Delete video context: if (pwriterRec->Context) g_main_loop_unref(pwriterRec->Context); pwriterRec->Context = NULL; // Decrement count of active writers: moviewritercount--; // Return success/fail status: if ((myErr == 0) && (PsychPrefStateGet_Verbosity() > 3)) printf("PTB-INFO: Moviehandle %i successfully closed and movie written to filesystem.\n", movieHandle); return(myErr == 0); }