fsleyes.gl.shaders.arbp.parse
¶
This module provides functions for use with OpenGL ARB_vertex_program
and ARB_fragment_program
assembly source code. It defines a simple
templating system, allowing ARB assembly programs to be written in such a way
that input parameters, vertex attributes, texture locations, and texture
coordinates and do not have to be hard coded in the source.
Note
This module is used by the ARBPShader
class - if you use
the ARBShader
class, you will not need to use this module
at all.
Instead, place holder expressions can be used in the source code. These
expressions may be parsed (using jinja2
) by the parseARBP()
function. Values can then be assigned to the place holders using the
fillARBP()
function.
An example¶
As an example, consider the following vertex program, for drawing a slice from a 3D image texture:
!!ARBvp1.0
PARAM imageShape = program.local[0];
# Transform the vertex position into display coordinates
DP4 result.position.x, state.matrix.mvp.row[0], vertex.position;
DP4 result.position.y, state.matrix.mvp.row[1], vertex.position;
DP4 result.position.z, state.matrix.mvp.row[2], vertex.position;
DP4 result.position.w, state.matrix.mvp.row[3], vertex.position;
# Transform the texture coordinates (which are
# between 0 and 1) into voxel coordinates (which
# are within the image voxel dimensions).
MOV voxCoord, vertex.texcoord[0];
MUL voxCoord, voxCoord, imageShape;
# Pass the texture coordinates and
# corresponding voxel coordinates
# through to the fragment program.
MOV result.texcoord[0], vertex.texcoord[0];
MOV result.texcoord[1], voxCoord;
END
And the corresponding fragment program, which looks up the voxel value and colours the fragment accordingly:
!!ARBfp1.0
TEMP voxValue;
# A transformation matrix (encoding a linear
# offset/scale) which transforms a voxel value
# from the image texture data range to the
# colour map texture input coordinate range.
PARAM voxValXform[4] = { program.local[0],
program.local[1],
program.local[2],
program.local[3] };
# Get the voxel value
TEX voxValue.x, fragment.texcoord[0], texture[0], 3D;
# Transform the voxel value
MAD voxValue, voxValue, voxValXform[0].x, voxValXform[3].x;
# Get the colour that corresponds to the voxel value
TEX result.color, voxValue.x, texture[1], 1D;
This program requires:
The image shape to be specified as a program parameter at index 0.
Image texture coordinates to be passed as coordinates on texture unit 0.
Both the vertex and fragment programs to know which texture units the texture and voxel coordinates are passed through on.
The image texture to be bound to texture unit 0.
The colour map texture to be bound to texture unit 1.
By using this module, all of these requirements can be removed by re-writing the vertex program as follows:
!!ARBvp1.0
PARAM imageShape = {{ param_imageShape }};
TEMP voxCoord;
# Transform the vertex position into display coordinates
DP4 result.position.x, state.matrix.mvp.row[0], vertex.position;
DP4 result.position.y, state.matrix.mvp.row[1], vertex.position;
DP4 result.position.z, state.matrix.mvp.row[2], vertex.position;
DP4 result.position.w, state.matrix.mvp.row[3], vertex.position;
# Transform the texture coordinates (which are
# between 0 and 1) into voxel coordinates (which
# are within the image voxel dimensions).
MOV voxCoord, {{ attr_texCoord }};
MUL voxCoord, voxCoord, imageShape;
# Pass the texture coordinates and
# corresponding voxel coordinates
# through to the fragment program.
MOV {{ varying_texCoord }}, {{ attr_texCoord }};
MOV {{ varying_voxCoord }}, voxCoord;
END
And the fragment program:
!!ARBfp1.0
TEMP voxValue;
# A transformation matrix (encoding a linear
# offset/scale) which transforms a voxel value
# from the image texture data range to the
# colour map texture input coordinate range.
PARAM voxValXform[4] = {{ param4_voxValXform }};
# Get the voxel value
TEX voxValue.x, {{ varying_texCoord }}, {{ texture_imageTexture }}, 3D;
# Transform the voxel value
MAD voxValue, voxValue, voxValXform[0].x, voxValXform[3].x;
# Get the colour that corresponds to the voxel value
TEX result.color, voxValue.x, {{ texture_colourMapTexture }}, 1D;
The parseARBP()
function parses the source code and returns information
about all declared items. The fillARBP()
function can then be used to
assign explicit values to each of the items:
vertSrc = '!!ARBvp1.0 vertex shader source'
fragSrc = '!!ARBfp1.0 fragment shader source'
# Get information about all parameters,
# attributes, textures, varyings, and
# constants.
items = parse.parseARBP(vertSrc, fragSrc)
# ...
# You have to calculate positions for
# parameters, attributes and textures.
# Positions for varying items are
# automatically calculated for you.
# ...
vertParams = {'imageShape' : 0}
vertParamLens = {'imageShape' : 1}
fragParams = {'voxValXform' : 0}
fragParamLens = {'voxValXform' : 4}
textures = {'imageTexture' : 0,
'colourMapTexture' : 1}
attrs = {'texCoord' : 0}
constants = {}
# Fill in the template
vertSrc, fragSrc = parse.fillARBP(vertSrc,
fragSrc,
vertParams,
vertParamLens,
fragParams,
fragParamLens,
constants,
textures,
attrs)
# Now you can compile the source
# code and run your program!
Template expressions¶
The following items may be specified as template expressions. As depicted in the example above, an expression is specified in the following manner (with the exception of constant values, which are described below):
{{ tokenPrefix_itemName }}
Prefixes for each item type are as follows:
Item
Expression prefix
Parameters:
param
Vertex attributes
attr
Textures
texture
Varying attributes
varying
Parameters¶
Parameters are constant values which are passed to every instantiation of a
shader program - they are equivalent to uniform
values in a GLSL program.
In a normal ARB assembly program, parameters are accessed as follows:
PARAM imageShape = program.local[0];
When using this module, you may instead access parameters in this way:
PARAM imageShape = {{ param_imageShape }};
Parameters with a length greater than 1 (e.g. matrix parameters) are traditionally accessed in this way:
PARAM xform[4] = { program.local[0],
program.local[1],
program.local[2],
program.local[3] };
When using this module, you may access matrix parameters in this way:
PARAM xform[4] = {{ param4_xform }};
Vertex attributes¶
Vertex attributes are values which are associated with every rendered
vertex. They are equivalent to attribute
values in a GLSL program.
In a normal ARB assembly program, one would typically pass vertex
attributes as texture coordinates bound to a specified texture unit:
PARAM texCoord = vertex.texcoord[0];
When using this module, you may access vertex attributes as follows:
PARAM texCoord = {{ attr_texCoord }};
Textures¶
In a typical ARB assembly program, the texture unit to which each texture is bound must be hard coded:
TEX voxelValue, texCoord, texture[0], 3D;
This can be avoided by using texture expressions:
TEX voxelValue, texCoord, {{ texture_imageTexture }}, 3D;
Varying attributes¶
Varying attributes are attributes which are generated in the vertex program,
and passed through to the fragment program. They are equivalent to varying
values in a GLSL program. In an ARB assembly program, they are typically
passed and accessed as texture coordinates:
!!ARBvp1.0
# In the vertex program, we pass varying
# attribute through as texture coordinates:
MOV result.texcoord[0], texCoord;
MOV result.texcoord[1], voxCoord;
# ...
!!ARBfp1.0
# ...
# In the fragment program, we access varying
# attrbutes as texture coordinates
TEMP texCoord;
TEMP voxCoord;
MOV texCoord, fragment.texcoord[0];
MOV voxCoord, fragment.texcoord[1];
# ...
This can be avoided by using the fillARBP()
function, which will
automatically assign texture coordinate positions to each varying attribute.
The assembly code can thus be re-written as follows:
!!ARBvp1.0
# ...
MOV {{ varying_texCoord }}, texCoord;
MOV {{ varying_voxCoord }}, voxCoord;
# ...
!!ARBfp1.0
# ...
TEMP texCoord;
TEMP voxCoord;
MOV texCoord, {{ varying_texCoord }};
MOV voxCoord, {{ varying_voxCoord }};
# ...
Constants¶
All expressions in the source which do not fit into any of the above categories are treated as “constant” values. These can be used to specify any values which will not change across multiple executions of the program. As a silly example, let’s say you want to apply a fixed offset to some texture coordinates. You could do this:
!!ARBfp1.0
# ...
TEMP texCoord;
MOV texCoord, {{ varying_texCoord }};
ADD texCoord, texCoord, {{ my_fixed_offset }};
Then, when calling fillARBP()
to generate the source code, add
my_fixed_offset
as a constant:
vertSrc = '!!ARBvp1.0 vertex shader source'
fragSrc = '!!ARBfp1.0 fragment shader source'
items = parse.parseARBP(vertSrc, fragSrc)
vertParams = {}
vertParamLens = {}
fragParams = {}
fragParamLens = {}
textures = {}
attrs = {'texCoord' : 0}
constants = {'my_fixed_offset' : '{0.1, 0.2, 0.3, 0}'}
# Fill in the template
vertSrc, fragSrc = parse.fillARBP(vertSrc,
fragSrc,
vertParams,
vertParamLens,
fragParams,
fragParamLens,
constants,
textures,
attrs)
Constant values can also be used in jinja2
if` and ``for
statements.
For example, to unroll a for
loop, you could do this:
!!ARBfp1.0
# ...
{% for i in range(num_iters) %}
# ... do stuff repeatedly
{% endfor %}
When generating the source, simply add a constant value called num_iters
,
specifying the desired number of iterations.
Including other files¶
By using this module, you are able to split your application logic across multiple files and emulate function calls between them. As an example, let’s say that we want to test whether some texture coordinates are valid:
!!ARBfp1.0
TEMP textest;
# do some stuff
# ...
# Check that the texture coordinates are in bounds
MOV texCoord, {{ varying_texCoord }};
# Test whether any coordinates are < 0.
# Set textest.x to:
# - -1 if any coordinates are < 0
# - +1 if they are all >= 0
CMP textest, texCoord, -1, 1;
MIN textest.x, textest.x, textest.y;
MIN textest.x, textest.x, textest.z;
# Test whether any coordinates are < 0.
# Set textest.y to:
# - -1 if any coordinates are > 1
# - +1 if they are all <= 1
MUL textest.yzw, texCoord, -1;
SLT textest.yzw, textest.yzww, -1;
MAD textest.yzw, textest.yzww 2, -1;
MUL textest.yzw, textest.yzww, -1;
MIN textest.y, textest.y, textest.z;
MIN textest.y, textest.y, textest.w;
# Set textest.x to:
# - -1 if any component of texCoord is < 0 or > 1
# - +1 otherwise
MIN textest.x, textest.x, textest.y;
# Kill the fragment if the texture
# coordinates are out of bounds
KIL textest.x;
# Othewrwise, carry on
# processing the fragment.
# ...
This may be a common operation which we would like to re-use in other fragment
programs. We can do this by using expressions in place of the inputs and
outputs of the routine. First, create a new file called, for example,
textest.prog
, containing the texture coordinate test routine:
#
# textest.prog - test whether texture coordinates are in bounds.
#
# Inputs:
# - texCoord - texture coordinates to test
# Outputs:
# - out_textest - The x component will be +1 if the texture coordinates
# are in bounds, -1 otherwise.
# Test whether any coordinates are < 0.
# Set textest.x to:
# - -1 if any coordinates are < 0
# - +1 if they are all >= 0
CMP {{ out_textest }}, {{ texCoord }} , -1, 1;
MIN {{ out_textest }}.x, {{ out_textest }}.x, {{ out_textest }}.y;
MIN {{ out_textest }}.x, {{ out_textest }}.x, {{ out_textest }}.z;
# Test whether any coordinates are < 0.
# Set textest.y to:
# - -1 if any coordinates are > 1
# - +1 if they are all <= 1
MUL {{ out_textest }}.yzw, {{ texCoord }}, -1;
SLT {{ out_textest }}.yzw, {{ out_textest }}.yzww, -1;
MAD {{ out_textest }}.yzw, {{ out_textest }}.yzww 2, -1;
MUL {{ out_textest }}.yzw, {{ out_textest }}.yzww, -1;
MIN {{ out_textest }}.y, {{ out_textest }}.y, {{ out_textest }}.z;
MIN {{ out_textest }}.y, {{ out_textest }}.y, {{ out_textest }}.w;
# Set textest.x to:
# - -1 if any component of texCoord is < 0 or > 1
# - +1 otherwise
MIN {{ out_textest ]}.x, {{ out_textest }}.x, {{ out_textest }}.y;
You can then use this routine in any fragment program like so:
!!ARBfp1.0
# Make the textest routine
# available to this program. This
# (and other includes) must occur
# at the top of your program.
{{ arb_include('textest.prog') }}
TEMP textest;
# do some stuff
# ...
# Check that {{ varying_texCoord }} is in
# bounds (see below for information about
# the use of the template expression in
# this comment).
{{ arb_call('textest.prog',
texCoord='{{ varying_texCoord }}',
out_result='textest') }}
# Kill the fragment if the texture
# coordinates are out of bounds
KIL textest.x;
# Othewrwise, carry on
# processing the fragment.
# ...
There are two requirements that must be met:
In the expression names that you use, that the routine’s output variables must start with
'out_'
.If, as in the example above, you use a template expression as an argument in the
arb_call
function, you must make sure that that expression is used elsewhere in the file, as otherwise it will not be detected by the parser. This can easily be accomplished simply by using the expression somewhere in a comment, as in the example above.The only expression types that you cannot pass direcrly into a routine are Parameters with a length greater than 1 (e.g. matrix parameters). For example, this will result in a compiler error:
{{ arb_call('my_routine.prog', xform='{{ param4_xform }}') }}In this case, you will need to declare the parameter as a
PARAM
or copy it to aTEMP
variable before passing it to the routine.
The arb_include
function requires you to pass the name of the routine file
that you wish to use - this must be specified relative to the includePath
argument to the parseARBP()
function.
The arb_call
function requires:
The name of the routine file (must be identical to that passed to
arb_include
)Mappings between the variables in your code, and the routine’s input and output parameters. These must all be passed as named (keyword) arguments. You cannot use swizzle masks in these mappings.
Temporary variables in included files¶
When your shader logic is split across several files, it can become difficult
to ensure that you don’t introduce any variable name conflicts. To avoid this,
the arb_temp
macro allows you to safely define temporary variables in
included files. For example:
{{ arb_temp('mytemp') }}
MOV {{ mytemp }}, 1;
The arb_temp
macro will assign a unique variable name, ensuring that there
are no conflicts with other files.
-
fsleyes.gl.shaders.arbp.parse.
TEMPLATE_BUILTIN_CONSTANTS
= ['range', 'arb_call', 'arb_include']¶ List of constant variables which may occur in source files, and which are provided by
jinja2
, or provided by this module.As of
jinja2
version 2.9.6, thejinja2.meta.find_undeclared_variables
function will return built-in functions such asrange
, so our_findDeclaredVariables`()
has to filter them out, and it uses this list to do so.
-
fsleyes.gl.shaders.arbp.parse.
parseARBP
(vertSrc, fragSrc)[source]¶ Parses the given
ARB_vertex_program
andARB_fragment_program
code, and returns information about all declared variables.
-
fsleyes.gl.shaders.arbp.parse.
fillARBP
(vertSrc, fragSrc, vertParams, vertParamLens, fragParams, fragParamLens, constants, textures, attrs, includePath)[source]¶ Fills in the given ARB assembly code, replacing all template tokens with the values specified by the various arguments.
- Parameters
vertSrc – Vertex program source.
fragSrc – Fragment program source.
vertParams – Dictionary of
{name : position}
mappings, specifying the position indices of all vertex program parameters.vertParamLens – Dictionary
{name : length}
mappings, specifying the lengths of all vertex program parameters.fragParams – Dictionary of
{name : position}
mappings, specifying the position indices of all fragment program parameters.fragParamLens – Dictionary
{name : length}
mappings, specifying the lengths of all fragment program parameters.constants – Dictionary of
{name : value}
mappings, specifying any variables used in the ARB template that are not vertex or fragment program parameters (e.g. vars used in if blocks or for loops).textures – Dictionary of {name : textureUnit}` mappings, specifying the texture unit to use for each texture.
attrs – Dictionary of {name : textureUnit}` mappings, specifying the texture unit to use for each vertex attribute.
includePath – Path to a directory which contains any additional files that may be included in the given source files.
-
fsleyes.gl.shaders.arbp.parse.
_render
(src, env, includePath)[source]¶ Called by :func:parseARBP`. Renders the given source template using the given environment, managing the logic for
arb_include
andarb_call
expressions.
-
fsleyes.gl.shaders.arbp.parse.
_findDeclaredVariables
(source)[source]¶ Parses the given ARB assembly program source, and returns information about all template tokens defined within. Returns a sequence of lists, which contain the names of:
Parameters
Textures
Vertex attributes
Varying attributes
Constants
-
fsleyes.gl.shaders.arbp.parse.
_checkVariableValidity
(vertVars, fragVars, vertParamMap, fragParamMap, textureMap, attrMap, constantMap)[source]¶ Checks the information about a vertex/fragment program, and raises an error if it looks like something is wrong.
-
fsleyes.gl.shaders.arbp.parse.
_makeVaryingMap
(vertVaryings, fragVaryings)[source]¶ Generates texture unit indices for all varying attributes.