0

I am using DemoDrift2 from Psychtool box to create a moving grating. The grating moves perfectly and works great. However, i need to use it with some other devices connected and working with other softwares. In these situations the grating is displayed slower and jiggles. How can i pre-render the frames before the grating is displayed?

function DriftDemo2(angle, cyclespersecond, f, drawmask, gratingsize)
% function DriftDemo2([angle=30][, cyclespersecond=1][, f=0.05][, drawmask=1],[gratingsize=400])


if nargin < 5
gratingsize = [];
end

if isempty(gratingsize)
% By default the visible grating is 400 pixels by 400 pixels in size:
gratingsize = 400;
end

if nargin < 4
drawmask = [];
end

if isempty(drawmask)
% By default, we mask the grating by a gaussian transparency mask:
drawmask=1;
end;

if nargin < 3
f = [];
end

if isempty(f)
% Grating cycles/pixel: By default 0.05 cycles per pixel.
f=0.05;
end;

if nargin < 2
cyclespersecond = [];
end

if isempty(cyclespersecond)
% Speed of grating in cycles per second: 1 cycle per second by default.
cyclespersecond=1;
end;

if nargin < 1
angle = [];
end

if isempty(angle)
% Angle of the grating: We default to 30 degrees.
angle=30;
end;

movieDurationSecs=20;   % Abort demo after 20 seconds.

% Define Half-Size of the grating image.
texsize=gratingsize / 2;

% Screen('Preference', 'SkipSyncTests', 1);

try
% This script calls Psychtoolbox commands available only in OpenGL-based 
% versions of the Psychtoolbox. 
AssertOpenGL;

% Get the list of screens and choose the one with the highest screen number.
screens=Screen('Screens');
screenNumber=max(screens);

% Find the color values which correspond to white and black:
% functions WhiteIndex and BlackIndex:
white=WhiteIndex(screenNumber);
black=BlackIndex(screenNumber);

% Round gray to integral number, to avoid roundoff artifacts with some
% graphics cards:
gray=round((white+black)/2);

% This makes sure that on floating point framebuffers we still get a
% well defined gray:
if gray == white
    gray=white / 2;
end

% Contrast 'inc'rement range for given white and gray values:
inc=white-gray;

% Open a double buffered fullscreen window and set default background
% color to gray:
[w screenRect]=Screen('OpenWindow',screenNumber, gray);

if drawmask
    % Enable alpha blending for proper combination of the gaussian aperture
    % with the drifting sine grating:
    Screen('BlendFunction', w, GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
end

% Calculate parameters of the grating:


p=ceil(1/f);

% frequency in radians:
fr=f*2*pi;

% This is the visible size of the grating. It is twice the half-width
% of the texture plus one pixel to make sure it has an odd number of
% pixels and is therefore symmetric around the center of the texture:
visiblesize=2*texsize+1;

% Create one single static grating image:
%
% We only need a texture with a single row of pixels(i.e. 1 pixel in height) to
% define the whole grating! If the 'srcRect' in the 'Drawtexture' call
% below is "higher" than that (i.e. visibleSize >> 1), the GPU will
% automatically replicate pixel rows. This 1 pixel height saves memory
% and memory bandwith, ie. it is potentially faster on some GPUs.
%
% However it does need 2 * texsize + p columns, i.e. the visible size
% of the grating extended by the length of 1 period (repetition) of the
% sine-wave in pixels 'p':
x = meshgrid(-texsize:texsize + p, 1);

% Compute actual cosine grating:
grating=gray + inc*cos(fr*x);

% Store 1-D single row grating in texture:
gratingtex=Screen('MakeTexture', w, grating);

% Create a single gaussian transparency mask and store it to a texture:
% The mask must have the same size as the visible size of the grating
% to fully cover it. Here we must define it in 2 dimensions and can't
% get easily away with one single row of pixels.
%
% We create a  two-layer texture: One unused luminance channel which we
% just fill with the same color as the background color of the screen
% 'gray'. The transparency (aka alpha) channel is filled with a
% gaussian (exp()) aperture mask:
mask=ones(2*texsize+1, 2*texsize+1, 2) * gray;
[x,y]=meshgrid(-1*texsize:1*texsize,-1*texsize:1*texsize);
mask(:, :, 2)=white * (1 - exp(-((x/90).^2)-((y/90).^2)));
masktex=Screen('MakeTexture', w, mask);

% Query maximum useable priorityLevel on this system:
priorityLevel=MaxPriority(w); %#ok<NASGU>

% We don't use Priority() in order to not accidentally overload older
% machines that can't handle a redraw every 40 ms. If your machine is
% fast enough, uncomment this to get more accurate timing.
%Priority(priorityLevel);

% Definition of the drawn rectangle on the screen:
% Compute it to  be the visible size of the grating, centered on the
% screen:
dstRect=[0 0 visiblesize visiblesize];
dstRect=CenterRect(dstRect, screenRect);

% Query duration of one monitor refresh interval:
ifi=Screen('GetFlipInterval', w);

% Translate that into the amount of seconds to wait between screen
% redraws/updates:

% waitframes = 1 means: Redraw every monitor refresh. If your GPU is
% not fast enough to do this, you can increment this to only redraw
% every n'th refresh. All animation paramters will adapt to still
% provide the proper grating. However, if you have a fine grating
% drifting at a high speed, the refresh rate must exceed that
% "effective" grating speed to avoid aliasing artifacts in time, i.e.,
% to make sure to satisfy the constraints of the sampling theorem
% (See Wikipedia: "Nyquist?Shannon sampling theorem" for a starter, if
% you don't know what this means):
waitframes = 1;

% Translate frames into seconds for screen update interval:
waitduration = waitframes * ifi;

% Recompute p, this time without the ceil() operation from above.
% Otherwise we will get wrong drift speed due to rounding errors!
p=1/f;  % pixels/cycle    

% Translate requested speed of the grating (in cycles per second) into
% a shift value in "pixels per frame", for given waitduration: This is
% the amount of pixels to shift our srcRect "aperture" in horizontal
% directionat each redraw:
shiftperframe= cyclespersecond * p * waitduration;

% Perform initial Flip to sync us to the VBL and for getting an initial
% VBL-Timestamp as timing baseline for our redraw loop:
vbl=Screen('Flip', w);

% We run at most 'movieDurationSecs' seconds if user doesn't abort via keypress.
vblendtime = vbl + movieDurationSecs;
i=0;

% Animationloop:
while(vbl < vblendtime)

    % Shift the grating by "shiftperframe" pixels per frame:
    % the mod'ulo operation makes sure that our "aperture" will snap
    % back to the beginning of the grating, once the border is reached.
    % Fractional values of 'xoffset' are fine here. The GPU will
    % perform proper interpolation of color values in the grating
    % texture image to draw a grating that corresponds as closely as
    % technical possible to that fractional 'xoffset'. GPU's use
    % bilinear interpolation whose accuracy depends on the GPU at hand.
    % Consumer ATI hardware usually resolves 1/64 of a pixel, whereas
    % consumer NVidia hardware usually resolves 1/256 of a pixel. You
    % can run the script "DriftTexturePrecisionTest" to test your
    % hardware...
    xoffset = mod(i*shiftperframe,p);
    i=i+1;

    % Define shifted srcRect that cuts out the properly shifted rectangular
    % area from the texture: We cut out the range 0 to visiblesize in
    % the vertical direction although the texture is only 1 pixel in
    % height! This works because the hardware will automatically
    % replicate pixels in one dimension if we exceed the real borders
    % of the stored texture. This allows us to save storage space here,
    % as our 2-D grating is essentially only defined in 1-D:
    srcRect=[xoffset 0 xoffset + visiblesize visiblesize];

    % Draw grating texture, rotated by "angle":
    Screen('DrawTexture', w, gratingtex, srcRect, dstRect, angle);

    if drawmask==1
        % Draw gaussian mask over grating:
        Screen('DrawTexture', w, masktex, [0 0 visiblesize visiblesize], dstRect, angle);
    end;

    % Flip 'waitframes' monitor refresh intervals after last redraw.
    % Providing this 'when' timestamp allows for optimal timing
    % precision in stimulus onset, a stable animation framerate and at
    % the same time allows the built-in "skipped frames" detector to
    % work optimally and report skipped frames due to hardware
    % overload:
    vbl = Screen('Flip', w, vbl + (waitframes - 0.5) * ifi);

    % Abort demo if any key is pressed:
    if KbCheck
        break;
    end;
end;

% Restore normal priority scheduling in case something else was set
% before:
Priority(0);

%The same commands wich close onscreen and offscreen windows also close
%textures.
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.
    Screen('CloseAll');
    Priority(0);
    psychrethrow(psychlasterror);
end %try..catch..enter code here

Any help is appreciated!

Flowers
  • 59
  • 1
  • 2
  • 12
  • Your code does not appear to show interaction with any devices (in fact, it appears to be the unedited code of the demo that comes with PsychToolBox). As such, we can't comment on your code. The problems you describe are likely due to problems in your code. ITs better to fix these than to switch to a hack like prerendering – Diederick C. Niehorster Jan 05 '14 at 16:27

1 Answers1

1

Pre-rendering is done by calling owpnt = Screen('OpenOffscreenWindow',...) and then issuing your regular drawing commands, but passing owpnt as the destination. These are then written to a texture, which you can display with Screen('DrawTexture').

You'd need one offscreen window per frame. This will quickly exhaust your video card's memory. So:

  1. Make sure to keep the dimensions of the offscreen window as small as possible: make it just fit your grating
  2. Screen('Close') unneeded offscreen windows asap