function MakeTextureTimingTest(screenid, width, height, channels, nSamples, preload, specialFlags, precision)
% MakeTextureTimingTest([screenid=max][,width=1024][,height=1024][,channels=4][,nSamples=100][,preload=1][,specialFlags=0][,precision=0]);
%
% Test creation timing of a texture of specific 'width' x 'height' size with
% 'channels' color channels (1=Luminance, 2=Luminance+Alpha, 3=RGB,
% 4=RGBA). Also measure texture upload speed if 'preload==1' and drawing
% speed if 'preload==2'. Use 'specialFlags' for texture creation, most
% interestingly a value of 4 to use planar texture storage, which can be
% faster under some circumstances. Create textures of 'precision' - 0 = 8
% bit integer, -1 = 8 bit integer, but created from double() input instead
% of uint8 input, 1 = 16 bpc float or 16 bit signed integer as fallback, 2 =
% 32 bpc float. Use 'nSamples' samples to compute mean timing.
%
% All parameters are optional: Defaults are width x height = 1024 x 1024,
% channels = 4 (RGBA), screenid = max id, 100 samples, preload but don't
% draw, standard storage, 8 bit uint input to 8 bit integer precision.
%
% Shows average duration.
%
% Each run creates a new texture via Screen('MakeTexture'), then preloads
% it onto the graphics card to measure that aspect as well, then deletes
% the texture again. This test will give you rather "worst case" or
% upper-bound numbers on texture management time. In many cases, processing
% of textures of similar size will be faster due to all kinds of internal
% optimizations. Your mileage may vary (TM), but it provides a rough
% impression at least.
%
% see also: PsychTests

% History:
% 06/05/08 mk   Wrote it.

AssertOpenGL;

if nargin < 1
    screenid = [];
end

if isempty(screenid)
    screenid = max(Screen('Screens'));
end

if nargin < 2
    width = [];
end

if nargin < 3
    height = [];
end

if isempty(width)
    width = 1024;
end

if isempty(height)
    height = 1024;
end

if nargin < 4
    channels = [];
end

if isempty(channels)
    channels = 4;
end

if nargin < 5
    nSamples = [];
end

if isempty(nSamples)
    nSamples = 100;
end

if nargin < 6
    preload = [];
end

if isempty(preload)
    preload = 1;
end

if nargin < 7 || isempty(specialFlags)
    specialFlags = 0;
end

if nargin < 8 || isempty(precision)
    precision = 0;
end

try
    % Open standard window:
    InitializeMatlabOpenGL(0,0,1);
    w=Screen('OpenWindow', screenid);

    % Create random test pixel matrix:
    img = uint8(rand(height, width, channels) * 255);
    if precision ~= 0 
        img = double(img);
    end
    
    if precision < 1
        precision = 0;
    end
    
    % Preheat: Screen() may need to allocate internal buffers or create
    % shaders - we don't want this one-time setup overhead to spoil the
    % numbers:
    tex = Screen('MakeTexture', w, img, [], specialFlags, precision);
    Screen('Close', tex);
    
    tbase = Screen('Flip', w);

    % Perform nSamples sampling passes:
    for i=1:nSamples
        tex = Screen('MakeTexture', w, img, [], specialFlags, precision);
        if preload == 1
            Screen('PreloadTextures', w, tex);
        end
        
        if preload == 2
            Screen('DrawTexture', w, tex);
            if i == 1
                Screen('Flip', w, [], 2, 1);
            end
        end
        
        Screen('Close', tex);
    end
    
    if preload ~= 2
        % Enforce completion of all GPU operations, take timestamp:
        elapsed = Screen('DrawingFinished', w, 2, 1);
    else
        elapsed = Screen('Flip', w) - tbase;
    end
    
    avgmsecs = (elapsed / nSamples) * 1000;
    
    fprintf('\n\n\nAverage Make -> Upload -> Destroy time for a %i x %i pixels, %i channels texture over %i samples is: %f msecs.\n\n\n', width, height, channels, nSamples, avgmsecs);

    % Only test common case:
    if (channels >= 3) && (specialFlags == 0) && (precision == 0)
        % Ok, same thing again with low-level calls:
        tex = Screen('MakeTexture', w, img);
        [texid textarget] = Screen('GetOpenGLTexture', w, tex);

        Screen('Flip', w);

        glBindTexture(textarget, texid);
        switch channels
            case 4,
                        glTexImage2D(textarget, 0, GL.RGBA8, width, height, 0, GL.BGRA, GL.UNSIGNED_INT_8_8_8_8_REV, img);
            case 3,
                        glTexImage2D(textarget, 0, GL.RGBA8, width, height, 0, GL.RGB, GL.UNSIGNED_BYTE, img);
        end
        
        glBindTexture(textarget, 0);

        % Perform nSamples sampling passes:
        for i=1:nSamples
            glBindTexture(textarget, texid);
            if channels == 4
                glTexSubImage2D(textarget, 0, 0, 0, width, height, GL.BGRA, GL.UNSIGNED_INT_8_8_8_8_REV, img);
            else
                glTexSubImage2D(textarget, 0, 0, 0, width, height, GL.RGB, GL.UNSIGNED_BYTE, img);
            end
            glBindTexture(textarget, 0);
            if preload
                Screen('PreloadTextures', w, tex);
            end
        end

        % Enforce completion of all GPU operations, take timestamp:
        elapsed = Screen('DrawingFinished', w, 2, 1);
        Screen('Close', tex);

        avgmsecs = (elapsed / nSamples) * 1000;

        fprintf('\n\n\nAverage glTexSubImage2D() -> Upload time for a %i x %i pixels, %i channels texture over %i samples is: %f msecs.\n\n\n', width, height, channels, nSamples, avgmsecs);
    end
    
    Screen('CloseAll');
catch 
    Screen('CloseAll');
    psychrethrow(psychlasterror);
end

return;
