function tonset = PredictVisualOnsetForTime(window, when, refreshinterval) % tonset = PredictVisualOnsetForTime(window, when [, refreshinterval]) % % Map a specific requested 'when' visual onset time, as you would pass it as % 'when' parameter to Screen('Flip', window, when); to the estimated onset % time of the "flipped" stimulus. % % By default, the refresh interval from Screen('GetFlipInterval', window); % is used for calculation, but you can provide an optional % 'refreshinterval' to override this choice. % % This function predicts the real onset time of your "flipped" stimulus, % taking into account that Screen('Flip') will not show your stimulus at % exactly the requested 'when' time, but it will synchronize stimulus onset % to the display refresh cycle of your monitor, ie, it will wait for onset % of the closest vertical blanking interval equal or later than 'when'. % % You can use the predicted 'tonset' to synchronize other modalities to % visual stimulus onset. E.g., our sound driver PsychPortAudio can accept % such a time as sound onset deadline in order to synch sound onset with % visual stimulus onset... % % Of course if your stimulus is too complex to be finished with drawing % until 'when' then Screen('Flip') will miss the deadline and this % prediction will be wrong. % % Accuracy of prediction: % % On Linux with FOSS graphics drivers on Intel, AMD and NVidia hardware, % and some dedicated SoC's, precision should be accurate to the low usecs % range. % % On other systems that support timing via beamposition queries, the prediction % should be accurate to better than 200 microseconds. On systems without % beamposition queries, the prediction may be off by up to a millisecond % worst case. On such systems we can't determine and correct for the % duration of the vertical blanking interval, so the prediction will be a % bit too early. % History: % 3-Jul-2007 Written. (MK) % 4-Jan-2017 Fixes for OS builtin timestamping. (MK) if nargin < 2 error('You must provide a "window" handle and a "when" target time!'); end % Retrieve last known good VBL timestamp for onscreen window 'window': wininfo = Screen('GetWindowInfo', window); lastvbl = wininfo.LastVBLTime; if lastvbl < 0 error('Sorry, can''t predict onset time, no valid previous flip timestamp.\nCall Screen(''Flip'') at least once before using this function!'); end if nargin < 3 refreshinterval = []; end if isempty(refreshinterval) % Retrieve measured monitor flip interval: ifi = Screen('GetFlipInterval', window); else % Use provided refresh interval instead: ifi = refreshinterval; end % Need to find first VBL after 'when': if when <= 0 % Special case: Swap at next VSYNC: % Convert into something we can handle... when = lastvbl + 0.5 * ifi; end % Compute time delta in units of video refresh intervals between 'when' and % the last known flip: dt = (when - lastvbl) / ifi; % Round 'dt' up to the closest integer greater than 'dt': This will be the % index of the next refresh interval after 'when'. We add a small epsilon % to make sure we don't get stuck at 'dt' itself if it should be too close % to a vbl onset. In that case we would miss that deadline anyway... dt = floor(dt) + 1; % Compute corresponding vbl time for target flip video frame 'dt': vbl = lastvbl + (dt * ifi); % Ok, vbl is our best guess about VBL onset - and therefore bufferswap time % of Screen('Flip', window, when); % If this system supports beamposition queries then we can estimate the % real visual onset time from the 'vbl' time by adding the duration of the % VBL. If the system doesn't support them, then 'vbl' is our best estimate % of onset. On Linux with FOSS drivers, the 'vbl' *is* already the onset % timestamp, so no correction needed. Need for correction can be derived % from SpecialFlags setting 16 (~ No OS builtin vblank timestamping). if (wininfo.VBLEndline > 0) && (bitand(wininfo.SpecialFlags, 16) == 16) % Known VBL endline and vbl is actual vbl start. We can calculate VBL % duration and add it to our 'vbl' estimate. vblduration = (wininfo.VBLEndline - wininfo.VBLStartline) / wininfo.VBLEndline * ifi; tonset = vbl + vblduration; else % No knowledge about duration of VBL. We return 'vbl' as our best % estimate, which is correct onset at least on Linux + FOSS: tonset = vbl; end return;