function SetClutTest(s)
% SetClutTest([screenNumber])
% 
% Usually, you'll call this with no arguments, to test all screens.
% 
% SetClutTest times SetClut for all your screens at all pixelSizes, at
% various priorities, with a brief stint, on each iteration, at another
% priority, using Sync, Async, and Immed versions of the PBControl call,
% to discover what factors, if any, affect whether SetClut waits for
% blanking.
% 
% "60 Hz:" is the result, the measured iteration rate of the loop.
%     If SetClut waits for blanking then this will equal the FrameRate.
% "priority 7/0" specifies two processor priorities:
%     "7" is the priority at which we call SetClut,
%     "0" is another priority that we use momentarily in the loop.
% "256 entries" says how many of the clut's entries we are loading,
%     either 1 or all.
% "Sync","Async", and "Immed" are flavors of the PBControl call for
%     cscSetEntries or cscDirectSetEntries.
% "8 bits" specifies the pixelSize.
% 
% Written to help unravel mysterious behavior of the Rage 128 driver
% discovered by Bob Dougherty on his blue G3 in 1999. His discovery seems to
% be true of all Rage 128 drivers, e.g. including the driver of the built-in
% screen of the Titanium G4/500 PowerBook under Mac OS 9.2.2. Bob's
% discovery is that the Rage 128 driver waits for blanking if the priority
% is low "0/0" or dips "7/0", but doesn't wait if the priority is high
% "7/7". The fix, installed automatically by Screen, but disabled during
% this test, is to always momentarily dip the priority to 0 after every
% call to SetClut. We don't know why this works, but it does work,
% perfectly, with no undesired side effect that we are aware of. If you
% have a Rage 128 driver, then you'll see a ridiculously high loop rate
% (e.g. 300 Hz) in the SetClutTest results when priority is high "7/7" and
% the proper frame rate (e.g. 60 Hz) when the priority is low "0/0", or
% dips "7/0".
% 
% See also WaitBlankingTest, PeekBlankingTest, FlickerTest, ScreenTest,
% and RushTest.

% 9/9/99  dgp Wrote it.
% 9/10/99 dgp Turn off SetClutCallsWaitBlanking during test.
% 9/21/99 dgp Test Sync, Async, and Immed versions of the PBControl call.
%             Load all variables and funs before timing loop.
%             Cleaned up comments and printout.
% 9/22/99 dgp Test at all pixelSizes.
% 9/25/99 dgp Test loading one/all clut entries.
% 9/30/99 dgp Renamed from "SetClutPriorityTest.m" to SetClutTest.m
% 10/2/99 dgp Explain the printout.
% 11/1/99 dgp Report the new Preferences added to work around the bug in the Rage128 video driver.
% 4/18/02 dgp "See also ..."
%             Disable the customization for Rage128, to get basic data.
% 4/24/02 awi Exit on PC with message.
% 5/5/02  dgp Clean up the report. Reduced n from 100 to 30, to run 3x faster, with slightly reduced precision.

if strcmp('PCWIN',computer)
    error('Win: SetClutTest not yet supported.');
end

if nargin==0
	fprintf('SetClutTest\n');
	fprintf('Does SetClut wait for blanking? Assess various parameters that may affect the answer.\n');
	DescribeComputer;
	for s=Screen('Screens');
		SetClutTest(s);
	end
	DescribeScreen(-1);
	return
end

saiString={'Sync','Async','Immed'};
PrepareScreen(s);
oldPixelSize=Screen(s,'PixelSize');
DescribeScreen(s);
DescribeScreenPrefs(s);

oldWarning=warning;
warning off;
Screen(s,'Preference','AskSetClutDriverToWaitForBlanking',1);
warning(oldWarning);

% Turn off the customization, and save the old settings for restoration below.
oldSetClutCallsWaitBlanking=Screen(s,'Preference','SetClutCallsWaitBlanking',0);
if oldSetClutCallsWaitBlanking
	fprintf('NOTE: Disabling SetClutCallsWaitBlanking while testing driver.\n');
end
oldDipPriorityAfterSetClut=Screen(s,'Preference','DipPriorityAfterSetClut',0);
oldMinimumSetClutPriority=Screen(s,'Preference','MinimumSetClutPriority',0);
if oldDipPriorityAfterSetClut
	fprintf('NOTE: Disabling automatic priority dipping while testing driver.\n');
end
DescribeScreen(-2);
fprintf('Directly calling SetClut to synch with %.0f Hz display:\n',FrameRate(s));

for pixelSize=Screen(s,'PixelSizes')
	Screen(s,'PixelSize',pixelSize);
	Screen(s,'Preference','SetClutCallsWaitBlanking',0);
	cmap=ClutDefault(s);
	for p=[0 1 2 7]
		for otherPriority=unique([0 p])
			for entries=[1 size(cmap,1)]
				for sai=0:2 % select Sync, Async, and Immed. Doesn't seem to make any difference.
					Screen(s,'Preference','SetClutSAI',sai);
					clear cmap2
					cmap2=cmap(1:entries,:);
					GetSecs;WaitSecs(0);Priority(0);i=0;t0=0;t1=0;t2=0; % load funs and vars
					tSetClut=0;tDeferred=0;
					n=30;
					Priority(p);
					loop={
					't=GetSecs;'
					'for i=1:n;'
						't0=GetSecs;'
						'Screen(s,''SetClut'',cmap2);'
						't1=GetSecs;'
						'Priority(otherPriority);'
						'Priority(p);'
						't2=GetSecs;'
						'tSetClut=tSetClut+t1-t0;'
						'tDeferred=tDeferred+t2-t1;'
						'WaitSecs(0.002);'
					'end;'
					't=(GetSecs-t)/n;'
					};
					Rush(loop,p);
					Priority(0);
					tSetClut=tSetClut/n;
					tDeferred=tDeferred/n;
					fprintf('%3.0f Hz: priority %d/%d, %3d entries, %-5s, %2d bits\n',1/t,p,otherPriority,entries,saiString{sai+1},pixelSize);
				end
			end
		end
	end
	Screen(s,'Preference','SetClutSAI',0);
end
Screen(s,'PixelSize',oldPixelSize);
% Restore the customization.
Screen(s,'Preference','DipPriorityAfterSetClut',oldDipPriorityAfterSetClut);
Screen(s,'Preference','MinimumSetClutPriority',oldMinimumSetClutPriority);
Screen(s,'Preference','SetClutCallsWaitBlanking',oldSetClutCallsWaitBlanking);
