/usr/share/psychtoolbox-3/PsychOpenGL/PsychGLSLShaders/EXPCreateStatic2DConvolutionShader.m is in psychtoolbox-3-common 3.0.11.20140816.dfsg1-1.
This file is owned by root:root, with mode 0o644.
The actual contents of the file can be viewed below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 | function [shader configstring] = EXPCreateStatic2DConvolutionShader(kernel, nrinputchannels, nroutchannels, debug, shadertype)
% [shader configstring] = EXPCreateStatic2DConvolutionShader(kernel [, nrinputchannels=3][, nroutchannels=3][, debug][, shadertype])
%
% Creates a GLSL fragment shader for 2D convolution of textures with
% the 2D or 1D convolution kernel 'kernel' and returns a handle 'shader'
% to it, as well as a blitter config options string as needed by the
% PTB imaging pipeline.
%
% Usually you won't call this routine directly, but use
% Add2DConvolutionToGLOperator() instead. It allows convenient setup of
% convolution operators of arbitrary size and complexity to GLOperators
% for use with Screen('TransformTexture'); and for use with the stimulus
% post-processing pipeline. Only if you want to create small convolution
% kernels for single-pass convolution of textures, you'll use this routine
% directly and use the returned 'shader' as argument to Screen('DrawTexture')
% or Screen('MakeTexture') as 'textureShader' argument.
%
% Parameters:
%
% 'kernel' is a simple 2D m-by-n matrix of floating point numbers with m and
% n being odd numbers, e.g., 1x1, 3x3, 5x5, 7x7, 9x9,..., 1x3, 1x9, 7x1 ...
% Each entry in the kernel is used as a weight factor.
%
% The simplest way to get a 2D kernel is to use the function
% kernel = fspecial(...); fspecial is part of the Matlab image
% processing toolbox, see "help fspecial" for more information.
%
% 'nrinputchannels' = The number of image channels to use as input for the
% convolution. Possible values are: 3 = Red, Green and Blue color channels are
% provided as part of a true-color image, don't use the alpha channel (if
% any) for convolution but just pass it through unmodified. 1 = The image
% only defines a luminance channel for convolution, an (optional) alpha
% channel is passed through unmodified. 4 = Use all four channels (Red,
% green, blue, alpha) for convolution.
%
% 'nroutchannels' = The number of channels to convolve as output: 3 =
% Convolve each of the three color channels red, green and blue separately
% by the same kernel. An (optional) alpha channel is passed through unmodified.
% 1 = Output a filtered luminance channel, and an (optional) unmodified
% alpha channel. If input is a 3 channel RGB image, then the RGB image will
% get converted to luminance before convolution. 4 = Filter all four
% channels independently.
%
% Typical settings:
%
% Filter a RGB(A) image: nrinputchannels = 3, nroutchannels = 3.
% Filter a RGB(A) image into grayscale: nrinputchannels = 3, nroutchannels = 1.
% Filter a luminance(A) image: inputchannels = 1, filteredoutchannels = 1.
% Generic filtering of 4-channel data: nrinputchannels = 4, nroutchannels = 4.
%
% 'debug' Optional debug flag: If set to non-zero, will output some debug
% info about the shader.
%
% 'shadertype' (Optional) The type of internal implementation to choose for
% the operator. This parameter is best left alone, unless you really know
% what you are doing.
%
% CAUTION:
%
% This feature is in early alpha stage. It may fail on your system and its
% future implementation may change significantly! Don't trust its results
% without validation against a known good reference implementation!
%
% NOTES: Filtermode 2 and 3 not fully implemented for the general case!
% ---> No separable 1D kernels, no support for all input->output mappings
% History:
% 27.11.2006 written by Mario Kleiner.
global GL;
persistent initialized;
if nargin < 1
error('CreateStatic2DConvolutionShader: No kernel provided!');
end;
if isempty(kernel)
error('CreateStatic2DConvolutionShader: No kernel provided!');
end
% Query size of kernel:
kernelw = size(kernel,1);
kernelh = size(kernel,2);
% We only want odd sized kernels of at least 3x3, e.g., 3x3, 5x5, 7x7, ...
if kernelw < 1 || kernelh < 1 || mod(kernelw,2)~=1 || mod(kernelh,2)~=1
error('CreateStatic2DConvolutionShader: Only odd-sized kernels of at least size 3x3 supported!');
end;
if nargin < 2 || isempty(nrinputchannels)
% Default to RGB channels as separate input channels:
nrinputchannels = 3;
end
if nargin < 3 || isempty(nroutchannels)
% Default to output of filtered RGB channels:
nroutchannels = 3;
end
if nargin < 4 || isempty(debug);
% No debug mode provided: Disable debug output by default:
debug = 0;
end;
if nargin < 5 || isempty(shadertype)
% No explicit shadertype provided. We use some guess-o-matic here, that
% tries to do its best for different types of hardware, depending on
% kernel-size:
% Query type of graphics hardware (vendor and model):
shadertype = 3;
end
% Preinit configstring to empty - the most common setting:
configstring = [];
% First time invocation?
if isempty(initialized)
% Make sure GLSL and fragmentshaders are supported on first call:
AssertGLSL;
% Query supported extensions:
extensions = glGetString(GL.EXTENSIONS);
if isempty(findstr(extensions, 'GL_ARB_fragment_shader'))
% No fragment shaders: This is a no go!
error('Sorry, this function does not work on your graphics hardware due to lack of sufficient support for fragment shaders.');
end
% Clear any OpenGL error state.
while (glGetError~=GL.NO_ERROR); end;
maxuniforms = glGetIntegerv(GL.MAX_FRAGMENT_UNIFORM_COMPONENTS);
% We are initialized:
initialized = 1;
end % of initialization.
% Compute half-width and linear shader coefficient array:
hwx = (kernelw - 1) / 2;
hwy = (kernelh - 1) / 2;
shaderkernel = reshape(kernel, kernelw * kernelh, 1);
if shadertype == 3
% Generic shader, using a texture bound to 2nd unit as lookup table for
% the convolution kernel. This method is *way* less efficient than
% lookups of the kernel in internal uniform registers or compiled-in
% constants, but it scales up to very large kernels, whereas the former
% methods are limited in kernel size by how many constants can be
% compiled into the shader or stored in uniform registers of a specific
% GPU.
% Load our shader for convolution blit operations:
if nrinputchannels == 1
% 1-channel case: Convolve Red/Luminance channel with kernel,
% replicate result into RGB/Luminance channel. Pass alpha channel
% unmodified:
shader = LoadGLSLProgramFromFiles('Convolve2DRectTexture1ChannelShader.frag.txt', 1);
else
% 4-channel case: Convolve all 4 channels by same kernel:
shader = LoadGLSLProgramFromFiles('Convolve2DRectTextureShader.frag.txt', 1);
end
% Assign proper texture units for input image and clut:
glUseProgram(shader);
shader_image = glGetUniformLocation(shader, 'Image');
shader_clut = glGetUniformLocation(shader, 'Kernel');
shader_kernelsizeX = glGetUniformLocation(shader, 'KernelHalfWidthX');
shader_kernelsizeY = glGetUniformLocation(shader, 'KernelHalfWidthY');
glUniform1i(shader_image, 0);
glUniform1i(shader_clut, 1);
glUniform1f(shader_kernelsizeX, hwx);
glUniform1f(shader_kernelsizeY, hwy);
glUseProgram(0);
% Shader ready. Setup 2nd texture unit as LUT unit for our kernel:
% Select the 2nd texture unit (unit 1) for setup:
glActiveTexture(GL.TEXTURE1);
luttex = glGenTextures(1);
glBindTexture(GL.TEXTURE_RECTANGLE_EXT, luttex);
glTexImage2D(GL.TEXTURE_RECTANGLE_EXT, 0, GL.LUMINANCE_FLOAT32_APPLE, kernelw, kernelh, 0, GL.LUMINANCE, GL.FLOAT, moglsingle(shaderkernel));
% Make sure we use nearest neighbour sampling:
glTexParameteri(GL.TEXTURE_RECTANGLE_EXT, GL.TEXTURE_MIN_FILTER, GL.NEAREST);
glTexParameteri(GL.TEXTURE_RECTANGLE_EXT, GL.TEXTURE_MAG_FILTER, GL.NEAREST);
% And that we clamp to edge:
glTexParameteri(GL.TEXTURE_RECTANGLE_EXT, GL.TEXTURE_WRAP_S, GL.CLAMP);
glTexParameteri(GL.TEXTURE_RECTANGLE_EXT, GL.TEXTURE_WRAP_T, GL.CLAMP);
% Default CLUT setup done: Switch back to texture unit 0:
glBindTexture(GL.TEXTURE_RECTANGLE_EXT, 0);
glActiveTexture(GL.TEXTURE0);
% Create shader config string so this lut texture gets used when
% convolving:
configstring = sprintf('TEXTURERECT2D(%i)=%i', 1, luttex);
end
if shadertype == 2
% Generate a linear shader program, i.e., all loops are completely
% unrolled. This may be a good workaround on old GPU's, but it didn't
% yield any speed improvements on GF-7000 or X1600 ...
% Header section:
src = ['/* Statically compiled 2D convolution fragment shader for 2D rectangle textures.' char(10) ...
'// Kernel Size is ' num2str(kernelw) ' by ' num2str(kernelh) char(10) ...
'// Compiled by Psychtoolbox convolution shader metacompiler.' char(10) ...
'*/' char(10)];
src = [src '' char(10) char(10) ...
'#extension GL_ARB_texture_rectangle : enable' char(10) char(10) ...
'uniform sampler2DRect Image;' char(10) char(10)];
switch(nrinputchannels)
case 1
dtype = ' float sum = float(0.0);';
ifetch = '.r';
douti = ' gl_FragColor.rgb = vec3(sum); gl_FragColor.a = texture2DRect(Image, gl_TexCoord[0].st).a;';
case 2
dtype = ' vec2 sum = vec2(0.0);';
ifetch = '.rg';
douti = ' gl_FragColor.rg = sum; gl_FragColor.a = texture2DRect(Image, gl_TexCoord[0].st).a;';
case 3
dtype = ' vec3 sum = vec3(0.0);';
ifetch = '.rgb';
douti = ' gl_FragColor.rgb = sum; gl_FragColor.a = texture2DRect(Image, gl_TexCoord[0].st).a;';
case 4
dtype = ' vec4 sum = vec4(0.0);';
ifetch = '.rgba';
douti = ' gl_FragColor.rgba = sum;';
end
% Now for the program body:
src = [src ...
'void main()' char(10)...
'{' char(10) ...
dtype char(10) char(10)];
% Generate unrolled loop:
i=0;
for dy=-hwy:hwy
for dx=-hwx:hwx
i= i + 1;
% src = [src ' sum += ' num2str(shaderkernel(i)) ' * texture2DRect(Image, gl_TexCoord[0].st + vec2(' num2str(dx) '.0, ' num2str(dy) '.0))' ifetch ';' char(10)];
src = [src ' sum += ' sprintf('%.16f',shaderkernel(i)) ' * texture2DRect(Image, gl_TexCoord[0].st + vec2(' num2str(dx) '.0, ' num2str(dy) '.0))' ifetch ';' char(10)];
end
end
% Generate tail:
src = [src douti char(10) '}' char(10) char(10)];
% Ok, we have our shader source string:
if debug > 3
src
end
% Create shader object, assign sourcecode and compile it:
shandle = glCreateShader(GL.FRAGMENT_SHADER);
if debug > 5
glShaderSource(shandle, src, debug);
else
glShaderSource(shandle, src);
end;
% This would abort if the shader would be invalid:
glCompileShader(shandle);
% Create new program object and get handle to it:
shader = glCreateProgram;
glAttachShader(shader, shandle);
% Link it: This will abort if linker pass fails.
glLinkProgram(shader);
% Should be ready. Setup texture unit to be always unit zero:
glUseProgram(shader);
shader_image = glGetUniformLocation(shader, 'Image');
glUniform1i(shader_image, 0);
% Unbind program:
glUseProgram(0);
end
if shadertype < 2
% Precompiled shaders, not using textures as lookup tables:
% Ok, now we synthesize a shader program source code string on the fly:
% Header section:
src = ['/* Statically compiled 2D convolution fragment shader for 2D rectangle textures.' char(10) ...
'// Kernel Size is ' num2str(kernelw) ' by ' num2str(kernelh) char(10) ...
'// Auto-Generated by Psychtoolbox convolution shader generator.' char(10) ...
'*/' char(10)];
src = [src '' char(10) char(10) ...
'#extension GL_ARB_texture_rectangle : enable' char(10) char(10) ...
'const int KernelHalfWidthX = ' num2str(hwx) ';' char(10) ...
'const int KernelHalfWidthY = ' num2str(hwy) ';' char(10) ...
'uniform sampler2DRect Image;' char(10)];
if (nrinputchannels==3) && (nroutchannels==1)
src = [src 'const vec3 rgb2grayweights = vec3(0.3, 0.59, 0.11);' char(10) ];
end
if shadertype == 0
src = [src 'float kernel[' num2str(kernelw * kernelh) '];' char(10) char(10)];
else
src = [src 'uniform float kernel[' num2str(kernelw * kernelh) '];' char(10) char(10)];
end
switch(nrinputchannels)
case 1 % Filter luminance channel only, but replicate filtered result to RGB. Alpha is passed through.
dtype = ' float tmp, sum = float(0.0);';
ifetch = ' tmp = texture2DRect(Image, gl_TexCoord[0].st + vec2(float(dx), float(dy))).r;';
iupdate= ' sum += tmp * kernel[i];';
douti = ' gl_FragColor.rgb = vec3(sum); gl_FragColor.a = texture2DRect(Image, gl_TexCoord[0].st).a;';
case 2
dtype = ' vec2 tmp, sum = vec2(0.0);';
ifetch = ' tmp = texture2DRect(Image, gl_TexCoord[0].st + vec2(float(dx), float(dy))).rg;';
iupdate= ' sum += tmp * vec2(kernel[i]);';
douti = ' gl_FragColor.rg = sum; gl_FragColor.a = texture2DRect(Image, gl_TexCoord[0].st).a;';
case 3 % Input is RGB. Alpha is passed through, ...
if (nroutchannels == 3)
% Filter each R,G,B channel separately...
dtype = ' vec3 tmp, sum = vec3(0.0);';
ifetch = ' tmp = texture2DRect(Image, gl_TexCoord[0].st + vec2(float(dx), float(dy))).rgb;';
iupdate= ' sum += tmp * vec3(kernel[i]);';
douti = ' gl_FragColor.rgb = sum; gl_FragColor.a = texture2DRect(Image, gl_TexCoord[0].st).a;';
else
% Convert RGB to Luminance, filter that, replicate result
% into RGB out...
dtype = ' vec3 tmp; float sum = float(0.0);';
ifetch = ' tmp = texture2DRect(Image, gl_TexCoord[0].st + vec2(float(dx), float(dy))).rgb;';
iupdate= ' sum += dot(tmp, rgb2grayweights) * kernel[i];';
douti = ' gl_FragColor.rgb = vec3(sum); gl_FragColor.a = texture2DRect(Image, gl_TexCoord[0].st).a;';
end
case 4 % Filter all RGBA channels. Mostly useful for generic 4-channel data processing, not for imaging.
dtype = ' vec4 tmp, sum = vec4(0.0);';
ifetch = ' tmp = texture2DRect(Image, gl_TexCoord[0].st + vec2(float(dx), float(dy))).rgba;';
iupdate= ' sum += tmp * vec4(kernel[i]);';
douti = ' gl_FragColor.rgba = sum;';
end
% Now for the program body:
src = [src ...
'void main()' char(10)...
'{' char(10) ...
' int dx, dy, i;' char(10) ...
dtype char(10) ...
' i=0;' char(10) ...
' '];
% Type 0: Kernel is compiled into the shader as array of floating
% point constants:
if shadertype == 0
% Output the kernel itself:
for i=1:(kernelw*kernelh)
src = sprintf('%s kernel[%i] = %.16f;', src, i-1, shaderkernel(i));
end
end
src = [src char(10) char(10) ...
' for (dy = -KernelHalfWidthY; dy <= KernelHalfWidthY; dy++) {' char(10) ...
' for (dx = -KernelHalfWidthX; dx <= KernelHalfWidthX; dx++) {' char(10) ...
ifetch char(10) ...
iupdate char(10) ...
' i++;' char(10) ...
' }' char(10) ...
' }' char(10) ...
douti char(10) ...
'}' char(10) char(10)];
% Ok, we have our shader source string:
if debug > 3
fprintf(src);
end
% Create shader object, assign sourcecode and compile it:
shandle = glCreateShader(GL.FRAGMENT_SHADER);
if debug > 4
glShaderSource(shandle, src, debug);
else
glShaderSource(shandle, src);
end;
% This would abort if the shader would be invalid:
glCompileShader(shandle);
% Create new program object and get handle to it:
shader = glCreateProgram;
glAttachShader(shader, shandle);
% Link it: This will abort if linker pass fails.
glLinkProgram(shader);
% Should be ready. Setup texture unit to be always unit zero:
glUseProgram(shader);
shader_image = glGetUniformLocation(shader, 'Image');
glUniform1i(shader_image, 0);
% For shadertype 1, set up uniforms with kernel:
if shadertype == 1
shader_kernel = glGetUniformLocation(shader, 'kernel[0]');
% Output the kernel itself:
for i=1:(kernelw*kernelh)
glUniform1f(shader_kernel + i - 1, shaderkernel(i));
end
end
% Unbind program:
glUseProgram(0);
end
% Ok, shader should be ready for use with e.g., Screen('DrawTexture')...
% Check for errors:
err = glGetError;
if err
fprintf('Error creating static 2D convolution shader! The GL returned: ');
gluErrorString(err);
shader = 0;
end;
% Shader should be ready for use.
return;
|