PsychError SCREENGetWindowInfo(void) { const char *FieldNames[]={ "Beamposition", "LastVBLTimeOfFlip", "LastVBLTime", "VBLCount", "TimeAtSwapRequest", "TimePostSwapRequest", "RawSwapTimeOfFlip", "VBLTimePostFlip", "OSSwapTimestamp", "GPULastFrameRenderTime", "StereoMode", "ImagingMode", "MultiSampling", "MissedDeadlines", "FlipCount", "StereoDrawBuffer", "GuesstimatedMemoryUsageMB", "VBLStartline", "VBLEndline", "VideoRefreshFromBeamposition", "GLVendor", "GLRenderer", "GLVersion", "GPUCoreId", "GLSupportsFBOUpToBpc", "GLSupportsBlendingUpToBpc", "GLSupportsTexturesUpToBpc", "GLSupportsFilteringUpToBpc", "GLSupportsPrecisionColors", "GLSupportsFP32Shading", "BitsPerColorComponent", "IsFullscreen", "SpecialFlags", "SwapGroup", "SwapBarrier" }; const int fieldCount = 35; PsychGenericScriptType *s; PsychWindowRecordType *windowRecord; double beamposition, lastvbl; int infoType = 0, retIntArg; double auxArg1, auxArg2, auxArg3; CGDirectDisplayID displayId; psych_uint64 postflip_vblcount; double vbl_startline; long scw, sch; psych_bool onscreen; //all subfunctions should have these two lines. PsychPushHelp(useString, synopsisString, seeAlsoString); if(PsychIsGiveHelp()){PsychGiveHelp();return(PsychError_none);}; PsychErrorExit(PsychCapNumInputArgs(5)); //The maximum number of inputs PsychErrorExit(PsychRequireNumInputArgs(1)); //The required number of inputs PsychErrorExit(PsychCapNumOutputArgs(1)); //The maximum number of outputs // Query infoType flag: Defaults to zero. PsychCopyInIntegerArg(2, FALSE, &infoType); if (infoType < 0 || infoType > 5) PsychErrorExitMsg(PsychError_user, "Invalid 'infoType' argument specified! Valid are 0, 1, 2, 3, 4 and 5."); // Windowserver info requested? if (infoType == 2 || infoType == 3) { // Return info about WindowServer: #if PSYCH_SYSTEM == PSYCH_OSX const char *CoreGraphicsFieldNames[]={ "CGSFps", "CGSValue1", "CGSValue2", "CGSValue3", "CGSDebugOptions" }; const int CoreGraphicsFieldCount = 5; float cgsFPS, val1, val2, val3; // This (undocumented) Apple call retrieves information about performance statistics of // the Core graphics server, also known as WindowServer or Quartz compositor: CGSGetPerformanceData(_CGSDefaultConnection(), &cgsFPS, &val1, &val2, &val3); if (CGSGetDebugOptions(&retIntArg)) { if (PsychPrefStateGet_Verbosity() > 1) printf("PTB-WARNING: GetWindowInfo: Call to CGSGetDebugOptions() failed!\n"); } PsychAllocOutStructArray(1, FALSE, 1, CoreGraphicsFieldCount, CoreGraphicsFieldNames, &s); PsychSetStructArrayDoubleElement("CGSFps", 0 , cgsFPS, s); PsychSetStructArrayDoubleElement("CGSValue1", 0, val1, s); PsychSetStructArrayDoubleElement("CGSValue2", 0, val2, s); PsychSetStructArrayDoubleElement("CGSValue3", 0, val3, s); PsychSetStructArrayDoubleElement("CGSDebugOptions", 0, (double) retIntArg, s); if ( (infoType == 3) && PsychCopyInDoubleArg(3, FALSE, &auxArg1) ) { // Type 3 setup request with auxArg1 provided. Apple auxArg1 as debugFlag setting // for the CoreGraphics server: DANGEROUS! if (CGSSetDebugOptions((unsigned int) auxArg1)) { if (PsychPrefStateGet_Verbosity() > 1) printf("PTB-WARNING: GetWindowInfo: Call to CGSSetDebugOptions() failed!\n"); } } #endif #if PSYCH_SYSTEM == PSYCH_WINDOWS psych_uint64 onsetVBLCount, frameId; double onsetVBLTime, compositionRate; psych_uint64 targetVBL; PsychAllocInWindowRecordArg(kPsychUseDefaultArgPosition, TRUE, &windowRecord); // Query all DWM presentation timing info, return full info as struct in optional return argument '1': if (PsychOSGetPresentationTimingInfo(windowRecord, TRUE, 0, &onsetVBLCount, &onsetVBLTime, &frameId, &compositionRate, 1)) { // Query success: Info struct has been created and returned by PsychOSGetPresentationTimingInfo()... auxArg1 = auxArg2 = 0; auxArg3 = 2; // Want us to change settings? if ( (infoType == 3) && PsychCopyInDoubleArg(3, FALSE, &auxArg1) && PsychCopyInDoubleArg(4, FALSE, &auxArg2) && PsychCopyInDoubleArg(5, FALSE, &auxArg3)) { if (auxArg1 < 0) auxArg1 = 0; targetVBL = auxArg1; if (PsychOSSetPresentParameters(windowRecord, targetVBL, (int) auxArg3, auxArg2)) { if (PsychPrefStateGet_Verbosity() > 5) printf("PTB-DEBUG: GetWindowInfo: Call to PsychOSSetPresentParameters(%i, %i, %f) SUCCESS!\n", (int) auxArg1, (int) auxArg3, auxArg2); } else { if (PsychPrefStateGet_Verbosity() > 1) printf("PTB-WARNING: GetWindowInfo: Call to PsychOSSetPresentParameters() failed!\n"); } } } else { // Unsupported / Failed: PsychCopyOutDoubleArg(1, FALSE, -1); } #endif #if PSYCH_SYSTEM == PSYCH_LINUX if (infoType == 2) { // MMIO register Read for screenid "auxArg1", register offset "auxArg2": PsychCopyInDoubleArg(3, TRUE, &auxArg1); PsychCopyInDoubleArg(4, TRUE, &auxArg2); PsychCopyOutDoubleArg(1, FALSE, (double) PsychOSKDReadRegister((int) auxArg1, (unsigned int) auxArg2, NULL)); } if (infoType == 3) { // MMIO register Write for screenid "auxArg1", register offset "auxArg2", to value "auxArg3": PsychCopyInDoubleArg(3, TRUE, &auxArg1); PsychCopyInDoubleArg(4, TRUE, &auxArg2); PsychCopyInDoubleArg(5, TRUE, &auxArg3); PsychOSKDWriteRegister((int) auxArg1, (unsigned int) auxArg2, (unsigned int) auxArg3, NULL); } #endif // Done. return(PsychError_none); } // Get the window record: PsychAllocInWindowRecordArg(kPsychUseDefaultArgPosition, TRUE, &windowRecord); onscreen = PsychIsOnscreenWindow(windowRecord); if (onscreen) { // Query rasterbeam position: Will return -1 if unsupported. PsychGetCGDisplayIDFromScreenNumber(&displayId, windowRecord->screenNumber); beamposition = (double) PsychGetDisplayBeamPosition(displayId, windowRecord->screenNumber); } else { beamposition = -1; } if (infoType == 1) { // Return the measured beamposition: PsychCopyOutDoubleArg(1, FALSE, beamposition); } else if (infoType == 4) { // Return async flip state: 1 = Active, 0 = Inactive. PsychCopyOutDoubleArg(1, FALSE, (((NULL != windowRecord->flipInfo) && (0 != windowRecord->flipInfo->asyncstate)) ? 1 : 0)); } else if (infoType == 5) { // Create a GL_EXT_timer_query object for this window: if (glewIsSupported("GL_EXT_timer_query")) { // Pending queries finished? if (windowRecord->gpuRenderTimeQuery > 0) { PsychErrorExitMsg(PsychError_user, "Tried to create a new GPU rendertime query, but last query not yet finished! Call Screen('Flip') first!"); } // Enable our rendering context by selecting this window as drawing target: PsychSetDrawingTarget(windowRecord); // Generate Query object: glGenQueries(1, &windowRecord->gpuRenderTimeQuery); // Emit Query: GPU will measure elapsed processing time in Nanoseconds, starting // with the first GL command executed after this command: glBeginQuery(GL_TIME_ELAPSED_EXT, windowRecord->gpuRenderTimeQuery); // Reset last measurement: windowRecord->gpuRenderTime = 0; } else { if (PsychPrefStateGet_Verbosity() > 4) printf("PTB-INFO: GetWindowInfo for infoType 5: GPU timer query objects are unsupported on this platform and GPU. Call ignored!\n"); } } else { // Return all information: PsychAllocOutStructArray(1, FALSE, 1, fieldCount, FieldNames, &s); // Rasterbeam position: PsychSetStructArrayDoubleElement("Beamposition", 0, beamposition, s); // Time of last vertical blank when a double-buffer swap occured: if ((windowRecord->flipCount > 0) && (windowRecord->time_at_last_vbl == 0) && (PsychPrefStateGet_VBLTimestampingMode() == 4)) { // If time_at_last_vbl for an already finished or at least pending flip isn't available and // we have support for OS-Builtin timestamping enabled, we try to employ OS-Builtin timestamping // to get a timestamp for the most recent pending or finished flip. If this fails or is unsupported, // it will have no effect: PsychOSGetSwapCompletionTimestamp(windowRecord, 0, &(windowRecord->time_at_last_vbl)); } // Return it - or the value zero if it is (still) undefined/unavailable: PsychSetStructArrayDoubleElement("LastVBLTimeOfFlip", 0, windowRecord->time_at_last_vbl, s); // Uncorrected timestamp of flip swap completion: PsychSetStructArrayDoubleElement("RawSwapTimeOfFlip", 0, windowRecord->rawtime_at_swapcompletion, s); // Timestamp immediately prior to call to PsychOSFlipWindowBuffers(), i.e., time at swap request submission: PsychSetStructArrayDoubleElement("TimeAtSwapRequest", 0, windowRecord->time_at_swaprequest, s); // Timestamp immediately after call to PsychOSFlipWindowBuffers() returns, i.e., time at swap request submission completion: PsychSetStructArrayDoubleElement("TimePostSwapRequest", 0, windowRecord->time_post_swaprequest, s); // Timestamp immediately after call to PsychOSFlipWindowBuffers() returns, i.e., time at swap request submission completion: PsychSetStructArrayDoubleElement("VBLTimePostFlip", 0, windowRecord->postflip_vbltimestamp, s); // Swap completion timestamp for most recently completed swap, according to OS-builtin PsychOSGetSwapCompletionTimestamp() method: PsychSetStructArrayDoubleElement("OSSwapTimestamp", 0, windowRecord->osbuiltin_swaptime, s); // Result from last GPU rendertime query as triggered by infoType 5: Zero if undefined. PsychSetStructArrayDoubleElement("GPULastFrameRenderTime", 0, windowRecord->gpuRenderTime, s); // Try to determine system time of last VBL on display, independent of any // flips / bufferswaps. lastvbl = -1; postflip_vblcount = 0; // On supported systems, we can query the OS for the system time of last VBL, so we can // use the most recent VBL timestamp as baseline for timing calculations, // instead of one far in the past. if (onscreen) { lastvbl = PsychOSGetVBLTimeAndCount(windowRecord, &postflip_vblcount); } // If we couldn't determine this information we just set lastvbl to the last known // vbl timestamp of last flip -- better than nothing... if (lastvbl < 0) lastvbl = windowRecord->time_at_last_vbl; PsychSetStructArrayDoubleElement("LastVBLTime", 0, lastvbl, s); PsychSetStructArrayDoubleElement("VBLCount", 0, (double) (psych_int64) postflip_vblcount, s); // Misc. window parameters: PsychSetStructArrayDoubleElement("StereoMode", 0, windowRecord->stereomode, s); PsychSetStructArrayDoubleElement("ImagingMode", 0, windowRecord->imagingMode, s); PsychSetStructArrayDoubleElement("SpecialFlags", 0, windowRecord->specialflags, s); PsychSetStructArrayDoubleElement("IsFullscreen", 0, (windowRecord->specialflags & kPsychIsFullscreenWindow) ? 1 : 0, s); PsychSetStructArrayDoubleElement("MultiSampling", 0, windowRecord->multiSample, s); PsychSetStructArrayDoubleElement("MissedDeadlines", 0, windowRecord->nr_missed_deadlines, s); PsychSetStructArrayDoubleElement("FlipCount", 0, windowRecord->flipCount, s); PsychSetStructArrayDoubleElement("StereoDrawBuffer", 0, windowRecord->stereodrawbuffer, s); PsychSetStructArrayDoubleElement("GuesstimatedMemoryUsageMB", 0, (double) windowRecord->surfaceSizeBytes / 1024 / 1024, s); PsychSetStructArrayDoubleElement("BitsPerColorComponent", 0, (double) windowRecord->bpc, s); // Query real size of the underlying display in order to define the vbl_startline: PsychGetScreenSize(windowRecord->screenNumber, &scw, &sch); vbl_startline = (double) sch; PsychSetStructArrayDoubleElement("VBLStartline", 0, vbl_startline, s); // And VBL endline: PsychSetStructArrayDoubleElement("VBLEndline", 0, windowRecord->VBL_Endline, s); // Video refresh interval duration from beamposition method: PsychSetStructArrayDoubleElement("VideoRefreshFromBeamposition", 0, windowRecord->ifi_beamestimate, s); // Swap group assignment and swap barrier assignment, if any: PsychSetStructArrayDoubleElement("SwapGroup", 0, windowRecord->swapGroup, s); PsychSetStructArrayDoubleElement("SwapBarrier", 0, windowRecord->swapBarrier, s); // Which basic GPU architecture is this? PsychSetStructArrayStringElement("GPUCoreId", 0, windowRecord->gpuCoreId, s); // FBO's supported, and how deep? if (windowRecord->gfxcaps & kPsychGfxCapFBO) { if (windowRecord->gfxcaps & kPsychGfxCapFPFBO32) { PsychSetStructArrayDoubleElement("GLSupportsFBOUpToBpc", 0, 32, s); } else if (windowRecord->gfxcaps & kPsychGfxCapFPFBO16) { PsychSetStructArrayDoubleElement("GLSupportsFBOUpToBpc", 0, 16, s); } else PsychSetStructArrayDoubleElement("GLSupportsFBOUpToBpc", 0, 8, s); } else { PsychSetStructArrayDoubleElement("GLSupportsFBOUpToBpc", 0, 0, s); } // How deep is alpha blending supported? if (windowRecord->gfxcaps & kPsychGfxCapFPBlend32) { PsychSetStructArrayDoubleElement("GLSupportsBlendingUpToBpc", 0, 32, s); } else if (windowRecord->gfxcaps & kPsychGfxCapFPBlend16) { PsychSetStructArrayDoubleElement("GLSupportsBlendingUpToBpc", 0, 16, s); } else PsychSetStructArrayDoubleElement("GLSupportsBlendingUpToBpc", 0, 8, s); // How deep is texture mapping supported? if (windowRecord->gfxcaps & kPsychGfxCapFPTex32) { PsychSetStructArrayDoubleElement("GLSupportsTexturesUpToBpc", 0, 32, s); } else if (windowRecord->gfxcaps & kPsychGfxCapFPTex16) { PsychSetStructArrayDoubleElement("GLSupportsTexturesUpToBpc", 0, 16, s); } else PsychSetStructArrayDoubleElement("GLSupportsTexturesUpToBpc", 0, 8, s); // How deep is texture filtering supported? if (windowRecord->gfxcaps & kPsychGfxCapFPFilter32) { PsychSetStructArrayDoubleElement("GLSupportsFilteringUpToBpc", 0, 32, s); } else if (windowRecord->gfxcaps & kPsychGfxCapFPFilter16) { PsychSetStructArrayDoubleElement("GLSupportsFilteringUpToBpc", 0, 16, s); } else PsychSetStructArrayDoubleElement("GLSupportsFilteringUpToBpc", 0, 8, s); if (windowRecord->gfxcaps & kPsychGfxCapVCGood) { PsychSetStructArrayDoubleElement("GLSupportsPrecisionColors", 0, 1, s); } else PsychSetStructArrayDoubleElement("GLSupportsPrecisionColors", 0, 0, s); if (windowRecord->gfxcaps & kPsychGfxCapFP32Shading) { PsychSetStructArrayDoubleElement("GLSupportsFP32Shading", 0, 1, s); } else PsychSetStructArrayDoubleElement("GLSupportsFP32Shading", 0, 0, s); // Renderer information: This comes last, and would fail if async flips // are active, because it needs PsychSetDrawingTarget, which in turn needs async // flips to be inactive: PsychSetDrawingTarget(windowRecord); PsychSetStructArrayStringElement("GLVendor", 0, (char*) glGetString(GL_VENDOR), s); PsychSetStructArrayStringElement("GLRenderer", 0, (char*) glGetString(GL_RENDERER), s); PsychSetStructArrayStringElement("GLVersion", 0, (char*) glGetString(GL_VERSION), s); } // Done. 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. */ bool PsychWaitForBufferswapPendingOrFinished(PsychWindowRecordType* windowRecord, double* timestamp, int *beamposition) { #if PSYCH_SYSTEM == PSYCH_OSX || PSYCH_SYSTEM == PSYCH_LINUX 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; // Driver is online. Enter polling loop: while (TRUE) { // Read surface address registers: primarySurface = PsychOSKDReadRegister(screenId, (screenId <=0 ) ? RADEON_D1GRPH_PRIMARY_SURFACE_ADDRESS : RADEON_D2GRPH_PRIMARY_SURFACE_ADDRESS, NULL); secondarySurface = PsychOSKDReadRegister(screenId, (screenId <=0 ) ? RADEON_D1GRPH_SECONDARY_SURFACE_ADDRESS : RADEON_D2GRPH_SECONDARY_SURFACE_ADDRESS, NULL); // Read update status registers: updateStatus = PsychOSKDReadRegister(screenId, (screenId <=0 ) ? 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", ((screenId <=0) ? 0:1), primarySurface, secondarySurface, updateStatus); } // Sleep 200 microseconds, then retry: PsychWaitIntervalSeconds(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 }
PsychError SCREENGetWindowInfo(void) { const char *FieldNames[]={ "Beamposition", "LastVBLTimeOfFlip", "LastVBLTime", "VBLCount", "StereoMode", "ImagingMode", "MultiSampling", "MissedDeadlines", "StereoDrawBuffer", "GuesstimatedMemoryUsageMB", "VBLStartline", "VBLEndline", "VideoRefreshFromBeamposition", "GLVendor", "GLRenderer", "GLVersion", "GLSupportsFBOUpToBpc", "GLSupportsBlendingUpToBpc", "GLSupportsTexturesUpToBpc", "GLSupportsFilteringUpToBpc", "GLSupportsPrecisionColors", "GLSupportsFP32Shading", "BitsPerColorComponent" }; const int fieldCount = 23; PsychGenericScriptType *s; PsychWindowRecordType *windowRecord; double beamposition, lastvbl; int beamposonly = 0; CGDirectDisplayID displayId; psych_uint64 postflip_vblcount; double vbl_startline; long scw, sch; bool onscreen; //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: PsychAllocInWindowRecordArg(kPsychUseDefaultArgPosition, TRUE, &windowRecord); onscreen = PsychIsOnscreenWindow(windowRecord); // Query beamposonly flag: Defaults to zero. PsychCopyInIntegerArg(2, FALSE, &beamposonly); if (onscreen) { // Query rasterbeam position: Will return -1 if unsupported. PsychGetCGDisplayIDFromScreenNumber(&displayId, windowRecord->screenNumber); beamposition = (double) PsychGetDisplayBeamPosition(displayId, windowRecord->screenNumber); } else { beamposition = -1; } if (beamposonly) { // Return the measured beamposition: PsychCopyOutDoubleArg(1, FALSE, beamposition); } else { // Return all information: PsychAllocOutStructArray(1, FALSE, 1, fieldCount, FieldNames, &s); // Rasterbeam position: PsychSetStructArrayDoubleElement("Beamposition", 0, beamposition, s); // Time of last vertical blank when a double-buffer swap occured: PsychSetStructArrayDoubleElement("LastVBLTimeOfFlip", 0, windowRecord->time_at_last_vbl, s); // Try to determine system time of last VBL on display, independent of any // flips / bufferswaps. lastvbl = -1; postflip_vblcount = 0; #if PSYCH_SYSTEM == PSYCH_OSX // On OS/X we can query the OS for the system time of last VBL, so we can // use the most recent VBL timestamp as baseline for timing calculations, // instead of one far in the past. if (onscreen) { lastvbl = PsychOSGetVBLTimeAndCount(windowRecord->screenNumber, &postflip_vblcount); } #endif // If we couldn't determine this information we just set lastvbl to the last known // vbl timestamp of last flip -- better than nothing... if (lastvbl < 0) lastvbl = windowRecord->time_at_last_vbl; PsychSetStructArrayDoubleElement("LastVBLTime", 0, lastvbl, s); PsychSetStructArrayDoubleElement("VBLCount", 0, (double) postflip_vblcount, s); // Misc. window parameters: PsychSetStructArrayDoubleElement("StereoMode", 0, windowRecord->stereomode, s); PsychSetStructArrayDoubleElement("ImagingMode", 0, windowRecord->imagingMode, s); PsychSetStructArrayDoubleElement("MultiSampling", 0, windowRecord->multiSample, s); PsychSetStructArrayDoubleElement("MissedDeadlines", 0, windowRecord->nr_missed_deadlines, s); PsychSetStructArrayDoubleElement("StereoDrawBuffer", 0, windowRecord->stereodrawbuffer, s); PsychSetStructArrayDoubleElement("GuesstimatedMemoryUsageMB", 0, (double) windowRecord->surfaceSizeBytes / 1024 / 1024, s); PsychSetStructArrayDoubleElement("BitsPerColorComponent", 0, (double) windowRecord->bpc, s); // Query real size of the underlying display in order to define the vbl_startline: PsychGetScreenSize(windowRecord->screenNumber, &scw, &sch); vbl_startline = (double) sch; PsychSetStructArrayDoubleElement("VBLStartline", 0, vbl_startline, s); // And VBL endline: PsychSetStructArrayDoubleElement("VBLEndline", 0, windowRecord->VBL_Endline, s); // Video refresh interval duration from beamposition method: PsychSetStructArrayDoubleElement("VideoRefreshFromBeamposition", 0, windowRecord->ifi_beamestimate, s); // Renderer information: PsychSetDrawingTarget(windowRecord); PsychSetStructArrayStringElement("GLVendor", 0, glGetString(GL_VENDOR), s); PsychSetStructArrayStringElement("GLRenderer", 0, glGetString(GL_RENDERER), s); PsychSetStructArrayStringElement("GLVersion", 0, glGetString(GL_VERSION), s); // FBO's supported, and how deep? if (windowRecord->gfxcaps & kPsychGfxCapFBO) { if (windowRecord->gfxcaps & kPsychGfxCapFPFBO32) { PsychSetStructArrayDoubleElement("GLSupportsFBOUpToBpc", 0, 32, s); } else if (windowRecord->gfxcaps & kPsychGfxCapFPFBO16) { PsychSetStructArrayDoubleElement("GLSupportsFBOUpToBpc", 0, 16, s); } else PsychSetStructArrayDoubleElement("GLSupportsFBOUpToBpc", 0, 8, s); } else { PsychSetStructArrayDoubleElement("GLSupportsFBOUpToBpc", 0, 0, s); } // How deep is alpha blending supported? if (windowRecord->gfxcaps & kPsychGfxCapFPBlend32) { PsychSetStructArrayDoubleElement("GLSupportsBlendingUpToBpc", 0, 32, s); } else if (windowRecord->gfxcaps & kPsychGfxCapFPBlend16) { PsychSetStructArrayDoubleElement("GLSupportsBlendingUpToBpc", 0, 16, s); } else PsychSetStructArrayDoubleElement("GLSupportsBlendingUpToBpc", 0, 8, s); // How deep is texture mapping supported? if (windowRecord->gfxcaps & kPsychGfxCapFPTex32) { PsychSetStructArrayDoubleElement("GLSupportsTexturesUpToBpc", 0, 32, s); } else if (windowRecord->gfxcaps & kPsychGfxCapFPTex16) { PsychSetStructArrayDoubleElement("GLSupportsTexturesUpToBpc", 0, 16, s); } else PsychSetStructArrayDoubleElement("GLSupportsTexturesUpToBpc", 0, 8, s); // How deep is texture filtering supported? if (windowRecord->gfxcaps & kPsychGfxCapFPFilter32) { PsychSetStructArrayDoubleElement("GLSupportsFilteringUpToBpc", 0, 32, s); } else if (windowRecord->gfxcaps & kPsychGfxCapFPFilter16) { PsychSetStructArrayDoubleElement("GLSupportsFilteringUpToBpc", 0, 16, s); } else PsychSetStructArrayDoubleElement("GLSupportsFilteringUpToBpc", 0, 8, s); if (windowRecord->gfxcaps & kPsychGfxCapVCGood) { PsychSetStructArrayDoubleElement("GLSupportsPrecisionColors", 0, 1, s); } else PsychSetStructArrayDoubleElement("GLSupportsPrecisionColors", 0, 0, s); if (windowRecord->gfxcaps & kPsychGfxCapFP32Shading) { PsychSetStructArrayDoubleElement("GLSupportsFP32Shading", 0, 1, s); } else PsychSetStructArrayDoubleElement("GLSupportsFP32Shading", 0, 0, s); } // Done. return(PsychError_none); }