function SyncedCLUTUpdateTest(synced) % SyncedCLUTUpdateTest([synced=1]) % % Perceptual test to test synchronization of hardware gamma table % updates to the vertical refresh and to Screen('Flip'). % % The command Screen('LoadNormalizedGammaTable') provides the optional parameter % 'loadOnNextFlip'. If set to 1, Psychtoolbox is requested to defer the upload % of the specified gamma table until next execution of the Screen('Flip') command, % i.e. swapping the back- and front buffers and update of the gamma table should % happen synchronously with each other and with vertical retrace. % % This test allows to perceptually check correctness of this mechanism. In a loop % it shows two alternating stimuli: A gray-level ramp of range 0-255, displayed % with a hardware lut of *half* intensity range 0-0.5. Then, after next retrace, a % gray-level ramp of *half* range 0-128 with a hardware lut of *full* intensity range % 0-1.0. If update of the visual stimulus (the graylevel ramp) and of the corresponding % clut happens synchronously at the same vertical retrace during the 'Flip' command, % as expected, then the half intensity stim + full intensity lut or full intensity stim + % half intensity lut should cancel each other out, resulting in the perception of a % half-intensity gray-ramp with no or only minimal flicker. If, on the other hand, % synchronisation of clut update and flip doesn't work properly, then one should see % a strong flicker of the gray-ramp due to non-matching stimulus and clut. % % You can easily produce the visual impression of failed sync by providing the optional % parameter synced = 0 to enforce non-synced updates. % % Even if synced updates work, you may see some artifacts at the top of the display, % due to the inability of slow gfx-hardware to update the lut quickly enough. Either % avoid that display area for your stim or buy a faster graphics card. Enabling realtime % scheduling via Priority() command may also help. % % The accuracy of stimulus onset timestamps may be reduced on M$-Windows when using % this mode of operation. % % History: % 24.07.2006 Written (MK). % This script calls Psychtoolbox commands available only in OpenGL-based % versions of the Psychtoolbox. The Psychtoolbox command AssertPsychOpenGL will issue % an error message if someone tries to execute this script on a computer without % an OpenGL Psychtoolbox AssertOpenGL; if nargin < 1 synced = 1; % Default to synchronized updates. end synced try % 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 dislay number is % a best guess about where you want the stimulus displayed. screens=Screen('Screens'); screenNumber=max(screens); % Open a double buffered fullscreen window: w=Screen('OpenWindow',screenNumber); % Store a backup copy of original LUT into origLUT. origLUT=Screen('ReadNormalizedGammaTable', screenNumber); % Build a simple gray-level ramp as a single texture. [width, height]=Screen('WindowSize', w); s=floor(min(width, height)/2)-1; x=meshgrid(-s:s, -s:s); fintex=ones(2*s+1,2*s+1); fintex(:,:)=mod(x,255)+1; % tex1 is a full intensity ramp... tex1=Screen('MakeTexture', w, fintex); % ...with a half intensity clut. lut1=origLUT * 0.5; % tex2 is a half intensity ramp... tex2=Screen('MakeTexture', w, fintex * 0.5); % ...with a full intensity clut. lut2=origLUT * 1; % Switch to realtime-mode for smooth animation: priorityLevel=MaxPriority(w); Priority(priorityLevel); % Query monitor refresh interval: ifi = Screen('GetFlipInterval', w); % Initial Flip to sync us to vbl and get a timestamp: vbl = Screen('Flip', w); i=0; t1=GetSecs; % Test loop: Runs until keypress or 1000 iterations have passed: while (1) % Update the hardware CLUT with our lut 1: Defer update % until Screen('Flip'), if synced == 1 Screen('LoadNormalizedGammaTable', w, lut1, synced); % Draw corresponding stimulus: Screen('DrawTexture', w, tex1); % Show at next retrace after given deadline and update clut: vbl = Screen('Flip', w, vbl + 1.5 * ifi); % Update the hardware CLUT with our lut 2: Defer update % until Screen('Flip'), if synced == 1 Screen('LoadNormalizedGammaTable', w, lut2, synced); % Draw corresponding stimulus: Screen('DrawTexture', w, tex2); % Show at next retrace after given deadline and update clut: vbl = Screen('Flip', w, vbl + 1.5 * ifi); % Increment framecount: i=i+1; % Abort after 1000 iterations or on a key-press: if KbCheck || (i>1000) break; end end % Some stats... avgfps = i / (GetSecs - t1) % Disable realtime scheduling: Priority(0); % Restore the original origLUT CLUT lookup table: Screen('LoadNormalizedGammaTable', screenNumber, origLUT); % Close window, we're done: Screen('CloseAll'); catch %this "catch" section executes in case of an error in the "try" section %above. Importantly, it closes the onscreen window if its open. Priority(0); if exist('origLUT', 'var') Screen('LoadNormalizedGammaTable', screenNumber, origLUT); end Screen('CloseAll'); psychrethrow(lasterror); end %try..catch..