function VulkanInteropDebug(flags) % VulkanInteropDebug([flags=0]) % close all; Screen('Preference', 'Verbosity', 5); PsychVulkan('Verbosity', 5); fs = 0; % EDIT ME! n = 1000; numifis = 0; loadjitter = 0; stereo = 0; flushpipe = 0; synchronous = 0; usedpixx = 0; usevulkan = 1; screenNumber = []; clearmode = 2; if nargin < 1 || isempty(flags) flags = 0; end PsychVulkan('OverrideFlags', flags); overrideFlags = PsychVulkan('OverrideFlags') try PsychDefaultSetup(1); RestrictKeysForKbCheck(KbName('ESCAPE')); % Get the list of Screens and choose the one with the highest screen number. % Screen 0 is, by definition, the display with the menu bar. Often when % two monitors are connected the one without the menu bar is used as % the stimulus display. Chosing the display with the highest display number is % a best guess about where you want the stimulus displayed. screens=Screen('Screens'); if isempty(screenNumber) screenNumber=max(screens); end screensize=Screen('Rect', screenNumber); % Query size of screen: screenheight=screensize(4); PsychImaging('PrepareConfiguration'); if usevulkan % Use PsychVulkan display backend instead of standard OpenGL: PsychImaging('AddTask', 'General', 'UseVulkanDisplay'); end if fs rect = []; else rect = InsetRect(screensize, 64, 0); end w=PsychImaging('OpenWindow',screenNumber, [0 255 0], rect,[],[], stereo); % Query effective stereo mode, as Screen() could have changed it behind our % back, e.g., if we asked for mode 1 but Screen() had to fallback to % mode 11: winfo = Screen('GetWindowInfo', w); stereo = winfo.StereoMode; % Query nominal framerate as returned by Operating system: % If OS returns 0, then we assume that we run on a flat-panel with % fixed 60 Hz refresh interval. framerate=Screen('NominalFramerate', w); if (framerate==0) framerate=60; end ifinominal=1 / framerate; fprintf('The refresh interval reported by the operating system is %2.5f ms.\n', ifinominal*1000); if (stereo>0) % Show something for the right eye as well in stereo mode: Screen('SelectStereoDrawBuffer', w, 1); Screen('FillRect', w, 0); Screen('DrawText', w, 'Stereo yeah!!!', 10, 40, 255); end % Measure monitor refresh interval again, just for fun... % This will trigger a calibration loop of minimum 100 valid samples and return the % estimated ifi in 'ifi': We require an accuracy of 0.1 ms == 0.0001 % secs. If this level of accuracy can't be reached, we time out after % 20 seconds... %[ ifi nvalid stddev ]= Screen('GetFlipInterval', w, 100, 0.0001, 5); [ ifi, nvalid, stddev ]= Screen('GetFlipInterval', w); fprintf('Measured refresh interval, as reported by "GetFlipInterval" is %2.5f ms. (nsamples = %i, stddev = %2.5f ms)\n', ifi*1000, nvalid, stddev*1000); % Init data-collection arrays for collection of n samples: ts=zeros(1,n); beampos=ts; missest=ts; flipfin=ts; dpixxdelay=ts; td=ts; so=ts; tSecondary = ts; sodpixx = ts; boxTime = ts; % Compute random load distribution for provided loadjitter value: wt=rand(1,n)*(loadjitter*ifi); % Perform some initial Flip to get us in sync with retrace: % tvbl is the timestamp (system time in seconds) when the retrace % started. We need it as a reference value for our WaitBlanking % emulation: tvbl=Screen('Flip', w, [], clearmode); % Test-loop: Collects n samples. for i=1:n % Presentation time calculation for waiting 'numifis' monitor refresh % intervals before flipping front- and backbuffer: % This formula emulates the old PTB-MacOS9 Screen('WaitBlanking', numifis) % behaviour as closely as possible. % The 'Flip' command takes a presentation timestamp 'tdeadline' as % optional argument: If tdeadline == 0 or is left out, Flip will % flip at the next possible retrace (PTB 1.0.5 and earlier behaviour). % If tdeadline is > 0, then Flip will wait until the system time % 'tdeadline' is reached and then flip the buffers % at the next possible VBL. This allows to specify absolute points in time % at which flip should occur. If you want the old behaviour of % Screen('WaitBlanking', w, numifis) back, then just calculate a % proper presentation timestamp "tdeadline" relative to the time of last flip % 'tvbl', as demonstrated here: tdeadline=tvbl + numifis * ifi - 0.5 * ifi; % If numifis == 0, flip on next retrace. This should be the same % as numifis == 1, but might make a difference in robustness if % the stimulus is **very** complex and the load for the system is at % the limit that it can handle in a single video-refresh interval. if numifis==0 % If user supplied numifis=0, we force tdeadline=0, so Flip % will actually ignore the deadline and just Flip at the next % possible retrace... tdeadline=0; end if usedpixx % Ask for a Datapixx onset timestamp for next 'Flip': PsychDataPixx('LogOnsetTimestamps', 1); end % Flip: The clearmode argument specifies if flip should clear the % drawing buffer after flip (=0 - default), keep it "as is" % for incremental drawing/updating of stims (=1) or don't do % anything to the framebuffer at all (=2). % We return the timestamp, when VBL starts in tvbl: This is when % the front- and back drawing surfaces get exchanged and it is the % crucial reference value for computing the 'tdeadline' % presentation deadline for the next 'Flip' command. % The rasterbeam-position (scanline) when the measurement was taken is returned in beampos(i), % the time when flip returned to Matlab is returned in flipfin(i), % estimated stimulus onset time aka end of VBL is returned in so(i). % % The first value "tvbl" is needed for tdeadline calculation if % one wants to emulate WaitBlanking of old PTB - see formula above. % beampos > screen height means that flip returned during the VBL % interval. Small values << screen height are also ok, % they just indicate either a slower machine or some types of flat-panels... pos=mod(i, screenheight); xm = pos + 50; ym = pos + 50; mouseposrgb = Screen('GetImage', w, OffsetRect([0 0 1 1], xm, ym), 'drawBuffer'); fprintf('PRE-RGB at cursor position (%f, %f): (%f, %f, %f)\n', xm, ym, mouseposrgb); [ tvbl, so(i), flipfin(i), missest(i), beampos(i)]=Screen('Flip', w, tdeadline, clearmode); mouseposrgb = Screen('GetImage', w, OffsetRect([0 0 1 1], xm, ym), 'drawBuffer'); fprintf('POST-RGB at cursor position (%f, %f): (%f, %f, %f)\n', xm, ym, mouseposrgb); if usedpixx % Ask for a Datapixx onset timestamp from last 'Flip': [boxTime(i), sodpixx(i)] = PsychDataPixx('GetLastOnsetTimestamp'); %#ok dpixxdelay(i) = GetSecs; end % Special code for DWM debugging: Disabled by default - Not for pure % mortals! tSecondary(i) = 0; if IsWin && 0 while 1 WaitSecs('YieldSecs', 0.001); wdminfo = Screen('GetWindowInfo', w, 2); if ~isstruct(wdminfo) break; end if wdminfo.cDXPresentConfirmed == wdminfo.cDXPresentSubmitted tSecondary(i) = wdminfo.qpcVBlank - ((wdminfo.cDXRefresh - wdminfo.cDXRefreshConfirmed) * wdminfo.qpcRefreshPeriod); tvbl = tSecondary(i); so(i) = tSecondary(i); break; end end end % Record timestamp for later use: ts(i) = tvbl; % Draw some simple stim for next frame of animation: We draw a % simple flashing rectangle that moves over the screen. The same % rectangle is drawn with some offset for the right-eye if stereo % display is requested: Screen('SelectStereoDrawBuffer', w, 0); Screen('FillRect', w, mod(i, 255), [pos+20 pos+20 pos+400 pos+400]); % Screen('FillRect', w, mod(i, 2)*255); if (stereo>0) % Show something for the right eye as well in stereo mode: Screen('SelectStereoDrawBuffer', w, 1); Screen('FillRect', w, mod(i, 255), [pos+40 pos+20 pos+420 pos+400]); end if flushpipe==1 % Give a hint to PTB that no further drawing commands will % follow before the next Flip-command. This can be used by PTB % to optimize drawing of very demanding stimuli in order to decrease the % chance of deadline misses due to overload. The "clearmode" % argument should be the same as the one passed to Flip. It is % another hint. If synchronous is set == 1, then % DrawingFinished will return an estimate of the time needed by % the graphics hardware to draw your stimulus. This is useful % for finding the cause of skipped frames. If the value is % close to the video refresh interval of your monitor, then you % are drawing too much/too complex stims for your graphics % hardware -> Reduce complexity, reduce monitor refresh rate or % buy faster graphics hardware. Don't set synchronous == 1 for % real experiments as it will significantly degrade % performance and can *cause* deadline misses. td(i)=Screen('DrawingFinished', w, clearmode, synchronous); end % Sleep a random amount of time, just to simulate some work being % done in the Matlab loop: WaitSecs(wt(i)); % And give user a chance to abort the test by pressing any key: if KbCheck break; end end % Draw next frame... % calculate clock skew corrected Datapixx onset timestamps if usedpixx>1 sodpixx = PsychDataPixx('BoxsecsToGetsecs', boxTime); end RestrictKeysForKbCheck([]); % Shutdown realtime scheduling: Priority(0) % Close display: If we skipped/missed any presentation deadline during % Flip, Psychtoolbox will automatically display some warning message on the Matlab % console: sca; PsychVulkan('OverrideFlags', []); % Restrict to actual number of collected samples: n = i; ts = ts(1:n); so = so(1:n); flipfin = flipfin(1:n); missest = missest(1:n); beampos = beampos(1:n); td = td(1:n); dpixxdelay = dpixxdelay(1:n); tSecondary = tSecondary(1:n); sodpixx = sodpixx(1:n); boxTime = boxTime(1:n); % Count and output number of missed flip on VBL deadlines: numbermisses=0; numberearly=0; if numifis > 0 if (stereo == 11) && (numifis == 1) % Special case: Stereomode 11 can't do better than one swap % every two refresh cycles, so take this into account: ifi = ifi * 2; end for i=2:n if (ts(i)-ts(i-1) > ifi*(numifis+0.5)) numbermisses=numbermisses+1; end if (ts(i)-ts(i-1) < ifi*(numifis-0.5)) numberearly=numberearly+1; end end else if stereo == 11 % Special case: Stereomode 11 can't do better than one swap % every two refresh cycles at best, so take this into account: ifi = ifi * 2; end for i=2:n if (ts(i)-ts(i-1) > ifi*1.5) numbermisses=numbermisses+1; end if (ts(i)-ts(i-1) < ifi*(numifis-0.5)) numberearly=numberearly+1; end end end % Plot all our measurement results: % Figure 1 shows time deltas between successive flips in milliseconds: % This should equal the product numifis * ifi: figure hold on plot((ts(2:n) - ts(1:n-1)) * 1000); ni = numifis; if numifis < 1 ni = 1; end if (ni < 2 ) && (stereo == 11) % Special case: Stereomode 11 can't do better than one swap % every two refresh cycles, so take this into account: ni = 2; end plot(ones(1,n)*ifi*ni*1000); title('Delta between successive Flips in milliseconds:'); hold off % Figure 2 shows the recorded beam positions: if winfo.VBLEndline > -1 hasbeampos = 1; else hasbeampos = 0; end if hasbeampos figure plot(beampos); title('Rasterbeam position when timestamp was taken (in scanlines):'); end if isequal(ts, so) % Same info in vbltime and stimulus onset time. Only % do one plot and label it in a less confusing manner: figure plot((flipfin - so)*1000); if IsLinux && (Screen('Preference', 'VBLTimestampingmode') == 4) % Linux mode 4: OpenML or Wayland presentation feedback, so is stimulus onset: title('Time delta between stimulus onset and return of Flip in milliseconds:'); elseif (IsLinux || IsOSX) && ismember(Screen('Preference', 'VBLTimestampingmode'), [1, 3]) && (winfo.VBLCount > 0) % Linux or OSX, vbl timestamping requested and apparently working. so is vblank time: title('Time delta between start of VBL and return of Flip in milliseconds:'); else % Windows or other os'es without working high precision timestamping: so is raw timestamp: title('Time delta between return from swap completion and return of Flip in milliseconds:'); end else % Figure 4 shows difference in ms between finish of Flip and estimated % start of VBL time: figure plot((flipfin - ts)*1000); title('Time delta between start of VBL and return of Flip in milliseconds:'); % Figure 5 shows difference in ms between finish of Flip and estimated % stimulus-onset: figure plot((flipfin - so)*1000); title('Time delta between stimulus onset and return of Flip in milliseconds:'); end % Figure 6 shows duration of drawing commands when calling % "DrawingFinished" in synchronous mode. if synchronous==1 figure plot(td*1000); title('Total duration of all drawing commands in milliseconds:'); end if IsWin && (tSecondary(1)>0 && tSecondary(2)>0) figure; plot((tSecondary - so) * 1000); title('Time delta in milliseconds between stimulus onset according to DWM and stimulus onset according to Flip:'); fprintf('Average discrepancy between DWM and beamposition timestamping is %f msecs, stddev = %f msecs.\n', mean((tSecondary - so) * 1000), std((tSecondary - so) * 1000)); end if usedpixx figure; plot((so - sodpixx) * 1000); title('Time delta in msecs onset according to Flip - onset according to DataPixx:'); fprintf('Average discrepancy between Flip timestamping and DataPixx is %f msecs, stddev = %f msecs.\n', mean((so - sodpixx) * 1000), std((so - sodpixx) * 1000)); figure; plot((dpixxdelay - so) * 1000); title('Time delta between stimulus onset and return of Datapixx timestamping in milliseconds:'); end % Output some summary and say goodbye... fprintf('PTB missed %i out of %i stimulus presentation deadlines.\n', numbermisses, n); fprintf('One missed deadline is ok and an artifact of the measurement.\n'); fprintf('PTB completed %i stimulus presentations before the requested target time.\n', numberearly); if numberearly > 0 fprintf('CAUTION: Completing flips too early should *never ever happen*! Your system has\n'); fprintf('CAUTION: a serious bug or misconfiguration in its graphics driver!!!\n'); end fprintf('Have a look at the plots for more details...\n'); % Done. catch %#ok<*CTCH> % This "catch" section executes in case of an error in the "try" section % above. Importantly, it closes the onscreen window if its open and % shuts down realtime-scheduling of Matlab: RestrictKeysForKbCheck([]); sca; PsychVulkan('OverrideFlags', []); % Disable realtime-priority in case of errors. Priority(0); psychrethrow(psychlasterror); end %try..catch.. return