function SetClutWaitTest(whichScreen)
% SetClutWaitTest(whichScreen)
% Thoroughly tests essentially all possible ways of calling SetClut and
% succinctly reports which modes (if any) wait for blanking. SetClut is
% our name for the Apple driver control call cscSetEntries (when
% pixelSize²8) or cscDirectSetEntries (when pixelSize³16). This routine is
% intended primarily as a subroutine for ScreenTest.m. As a convenience to
% the general user, calling SetClutWaitTest with no arguments will produce
% a complete report on all screens. Also see ScreenTest and SetClutTest.

% 6/1/02  dgp Wrote it, based on SetClutTest.m.
% 6/27/02 dgp Fixed printout bug reported by Keith Schneider <ks@bcs.rochester.edu>.
%             Changed for "d=find(relevant)';" to for "d=find(relevant);"
% 8/1/02  dgp Fixed FOR FIND problems, as in SetGammaWaitTest, before they bite.

if nargin==0
	fprintf('SetClutWaitTest\n');
	DescribeComputer;
	for whichScreen=Screen('Screens')
		DescribeScreen(whichScreen);
		DescribeScreenPrefs(whichScreen);
		SetClutWaitTest(whichScreen);
	end
	DescribeScreen(-1);
	return
end

PrepareScreen(whichScreen);

% setClut.rate(prioritySequence,wholeClut,synchMode,pixelSize)
% setClut.waits(prioritySequence,wholeClut,synchMode,pixelSize)
% relevant(d) Does "waits" depend on parameter d?
% setClut.parameter{d} name of parameter d
% setClut.value{d}{i} i-th value of parameter d
setClut.parameter{1}='prioritySequence';
setClut.parameter{2}='clutEntries';
setClut.parameter{3}='pixelSize';
setClut.parameter{4}='synchMode';
setClut.value{1}={'0/0','7/0','7/7'};
setClut.value{2}={'one','all'};
setClut.value{3}={'8','16','32'};
setClut.value{4}={'sync'};
setClut.ndims=3;
setClut.rate=TimeSetClut(whichScreen);
waits=abs(log10(setClut.rate/framerate(whichScreen)))<0.2;
relevant=RelevantDims(waits);
relevantDimsList=find(relevant);
if ~relevant(1)
	waits=waits(1,:,:,:);
end
if ~relevant(2)
	waits=waits(:,1,:,:);
end
if length(relevant)>=3 & ~relevant(3)
	waits=waits(:,:,1,:);
end
if length(relevant)>=4 & ~relevant(4)
	waits=waits(:,:,:,1);
end
sizeWaits=ones(1,setClut.ndims);
for i=1:ndims(waits)
	sizeWaits(1:ndims(waits))=size(waits);
end
if any(waits(:))
	fprintf('SetClut waits for blanking (i.e. ~%.0f Hz) if parameters have these values:\n',FrameRate(whichScreen));
	fprintf('%10s',' ');
	for d=relevantDimsList
		fprintf('%17s ',setClut.parameter{d});
	end
	fprintf('\n');
	for i=find(waits(:))'
		[s(1) s(2) s(3) s(4)]=ind2sub(sizeWaits,i);
		fprintf('%5.0f Hz: ',setClut.rate(s(1),s(2),s(3),s(4)));
		for d=relevantDimsList
			fprintf('%17s ',setClut.value{d}{s(d)});
		end
		fprintf('\n');
	end
end	
if any(~waits(:))
	fprintf('SetClut doesn''t wait for blanking if parameters have these values:\n');
	fprintf('%10s',' ');
	for d=relevantDimsList
		fprintf('%17s ',setClut.parameter{d});
	end
	fprintf('\n');
	for i=find(~waits(:))'
		[s(1) s(2) s(3) s(4)]=ind2sub(sizeWaits,i);
		fprintf('%5.0f Hz: ',setClut.rate(s(1),s(2),s(3),s(4)));
		for d=relevantDimsList
			fprintf('%17s ',setClut.value{d}{s(d)});
		end
		fprintf('\n');
	end
end	
if any(~relevant)
 	fprintf('The following parameters don''t affect whether SetClut waits for blanking:\n');
	fprintf('%10s',' ');
	for d=find(~relevant)
		fprintf('%17s ',setClut.parameter{d});
	end
	fprintf('\n');
end
return

function relevant=RelevantDims(array)
% relevant=RelevantDim(array)
% Returns a logical vector "relevant" with length ndims(array). Each
% element indicates whether the supplied array has any variation along
% that dimension.
for d=1:ndims(array)
	relevant(d)=0;
	a=shiftdim(array,d-1);
	for j=2:size(a,1)
		if ~all(a(1,:)==a(j,:))
			relevant(d)=1;
			break;
		end
	end
end
return

function rate=TimeSetClut(whichScreen)
saiString={'Sync','Async','Immed'};
for s=whichScreen;
	oldPixelSize=Screen(s,'PixelSize');

	DescribeScreen(-2);

	PrepareScreen(s);

	% Turn off the customization, and save the old settings for restoration below.
	oldSetClutCallsWaitBlanking=Screen(s,'Preference','SetClutCallsWaitBlanking',0);
	oldDipPriorityAfterSetClut=Screen(s,'Preference','DipPriorityAfterSetClut',0);
	oldMinimumSetClutPriority=Screen(s,'Preference','MinimumSetClutPriority',0);
	oldMaximumSetClutPriority=Screen(s,'Preference','MaximumSetClutPriority',7);

	for pixelSize=Screen(s,'PixelSizes')
		Screen(s,'PixelSize',pixelSize);
		Screen(s,'Preference','SetClutCallsWaitBlanking',0);
		cmap=ClutDefault(s);
		for p=[0 7]
			for otherPriority=unique([0 p])
				for entries=[1 size(cmap,1)]
					for sai=0 % 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;
						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);
						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);
						if p==0
							prioritySequence=1;
						else
							if otherPriority==0
								prioritySequence=2;
							else
								prioritySequence=3;
							end
						end
						if entries==1
							clutEntries=1;
						else
							clutEntries=2;
						end
						synchMode=sai+1;
						pixelSizeIndex=log(pixelSize)/log(2)-2;
						rate(prioritySequence,clutEntries,pixelSizeIndex,synchMode)=1/t;
					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','MaximumSetClutPriority',oldMaximumSetClutPriority);
	Screen(s,'Preference','SetClutCallsWaitBlanking',oldSetClutCallsWaitBlanking);
end
return
