function MultiWindowLockStepTest(nrwins, separateScreens) % MultiWindowLockStepTest([nrwins=2][, separateScreens=0]); % % Test if and how many parallel asynchronous window flips % Psychtoolbox can handle on multiple onscreen windows. % % This test exercises the asynchronous flip scheduling and % timestamping, aka the Screen('AsyncFlipBegin'); and % Screen('AsyncFlipCheckEnd'); functions for parallel scheduling % of multiple independent bufferswaps on multiple open onscreen % windows. % % 'nrwins' selects how many onscreen windows to drive in parallel. % If set to [], 2 windows will be used. The i'th window is flipped % at a target rate of one flip every i'th video refresh interval. % % Timestamps are collected for all flips and in the end one plot % per window shows how well the flips met their requested deadlines. % % If 'nrwins' is omitted, a simple test with only two windows is % conducted. One window flips after 10 seconds, the other after 20 % seconds. % % If the optional parameter 'separateScreens' is set to a non-zero % setting, then the different onscreen windows are not opened % on the same display screen 0, but each one on a separate screen. % Performance can differ significantly between one screen and % multi-screen, as the operating system will use very different % underlying algorithms and methods to handle flips on windows, % depending on their placement on different displays or not. Each % operating system will also behave differently. % % History: % 07.09.2011 mk Written. AssertOpenGL; if nargin == 0 w1 = Screen('Openwindow', 0, [255 0 0], [0 0 200 200]); w2 = Screen('Openwindow', 0, [0 255 0], [210 0 410 200]); DrawFormattedText(w1, 'I swap in 20 seconds.', 'center', 'center'); DrawFormattedText(w2, 'I swap in 10 seconds.', 'center', 'center'); tic; t1 = Screen('Flip', w1); t2 = Screen('Flip', w2); Screen('AsyncFlipBegin', w1, t1 + 20, [], 0); w1Engaged = 1 toc Screen('AsyncFlipBegin', w2, t2 + 10, [], 0); w2Engaged = 1 toc td2 = Screen('AsyncFlipEnd', w2) - t2 td1 = Screen('AsyncFlipEnd', w1) - t1 allfinished = 1 toc KbStrokeWait; sca; return; end % Assign defaults: if isempty(nrwins) || nrwins < 0 || nrwins > 20 nrwins = 2; end if nargin < 2 || isempty(separateScreens) separateScreens = 0; end % Init timestamps and counts: c = zeros(nrwins, 1); ifi = zeros(nrwins, 1); t = nan(nrwins, 5000); screens = Screen('Screens'); if separateScreens % One screen per fullscreen window: if length(screens) < nrwins error('You asked to display each window on a separate screen, but there are less screens than windows!'); end for i = 1:nrwins w(i) = Screen('Openwindow', screens(i), 0, []); ifi(i) = Screen('GetFlipInterval', w(i)); end else % All tiny windows on first screen: for i = 1:nrwins w(i) = Screen('Openwindow', 0, 0, [(i-1)*50, 0, i*50, 50]); ifi(i) = Screen('GetFlipInterval', w(i)); end end % Wait for go from user: KbReleaseWait; % Get baseline timestamp of vsync: tvbl = Screen('Flip', w(1)); for i = 1:nrwins t(i, 1) = tvbl; end % Run until keypress or 5000 frames elapsed: while ~KbCheck && (c(1) < 5000) % Check all windows: for i = nrwins:-1:1 % Check/Process i'th window: if Screen('GetWindowInfo', w(i), 4) == 0 % w(i) doesn't have an async flip scheduled. % Render something into it, schedule one: c(i) = c(i) + 1; x = mod(c(i), 50); Screen('DrawLine', w(i), 255, 0, x, 50, x, 1); Screen('AsyncFlipBegin', w(i), t(i, c(i)) + (i - 0.5) * ifi(i)); else % w(i) has an async flip pending. Check if completed: tvbl = Screen('AsyncFlipCheckEnd', w(i)); if tvbl > 0 % Completed - and finalized. Store its timestamp: t(i, c(i) + 1) = tvbl; end end end WaitSecs('YieldSecs', 0.002); end % Close displays: sca; % Do the stats and plotting of fancy graphs: close all; for i=1:nrwins figure; tvec = t(i, 1:c(i)-1); tvec = diff(tvec) / ifi(i); plot(tvec); title(sprintf('Should be %i frames delta:', i)); fprintf('Window %i should have mean %i, has %f with range %f.\n', i, i, mean(tvec), psychrange(tvec)); end return;