Can you pass a matrix by reference in a GLSL shader? - glsl

How do you pass by reference in a GLSL shader?

You can mark an attribute as inout in the function signature, and that will make the attribute effectively "pass by reference"
For example,
void doSomething( vec3 trans, inout mat4 mat )
Here mat is "passed by reference", trans is passed by value.
mat must be writeable (ie not a uniform attribute)

All parameters are “pass by value” by default. You can change this behavior using these “parameter qualifiers”:
in: “pass by value”; if the parameter’s value is changed in the function, the actual parameter from the calling statement is unchanged.
out: “pass by reference”; the parameter is not initialized when the function is called; any changes in the parameter’s value changes the actual parameter from the calling statement.
inout: the parameter’s value is initialized by the calling statement and any changes made by the function change the actual parameter from the calling statement.
So if you don't want to make a copy, you should use out

Related

Why my class keeps reseting and doesnt keep my changes [duplicate]

A simple question for which I couldn't find the answer here.
What I understand is that while passing an argument to a function during call, e.g.
void myFunction(type myVariable)
{
}
void main()
{
myFunction(myVariable);
}
For simple datatypes like int, float, etc. the function is called by value.
But if myVariable is an array, only the starting address is passed (even though our function is a call by value function).
If myVariable is an object, also only the address of the object is passed rather than creating a copy and passing it.
So back to the question. Does C++ pass a object by reference or value?
Arguments are passed by value, unless the function signature specifies otherwise:
in void foo(type arg), arg is passed by value regardless of whether type is a simple type, a pointer type or a class type,
in void foo(type& arg), arg is passed by reference.
In case of arrays, the value that is passed is a pointer to the first element of the array. If you know the size of the array at compile time, you can pass an array by reference as well: void foo(type (&arg)[10]).
C++ always gives you the choice: All types T (except arrays, see below) can be passed by value by making the parameter type T, and passed by reference by making the parameter type T &, reference-to-T.
When the parameter type is not explicitly annotated to be a reference (type &myVariable), it is always passed by value regardless of the specific type. For user-defined types too (that's what the copy constructor is for). Also for pointers, even though copying a pointer does not copy what's pointed at.
Arrays are a bit more complicated. Arrays cannot be passed by value, parameter types like int arr[] are really just different syntax for int *arr. It's not the act of passing to a function which produces a pointer from an array, virtually every possible operation (excluding only a few ones like sizeof) does that. One can pass a reference-to-an-array, but this explicitly annotated as reference: int (&myArray)[100] (note the ampersand).
C++ makes both pass by value and pass by reference paradigms possible.
You can find two example usages below.
http://www.learncpp.com/cpp-tutorial/72-passing-arguments-by-value/
http://www.learncpp.com/cpp-tutorial/73-passing-arguments-by-reference/
Arrays are special constructs, when you pass an array as parameter, a pointer to the address of the first element is passed as value with the type of element in the array.
When you pass a pointer as parameter, you actually implement the pass by reference paradigm yourself, as in C. Because when you modify the data in the specified address, you exactly modify the object in the caller function.
In C++, types declared as a class, struct, or union are considered "of class type". These are passed by value or you can say a copy using copy constructor is passed to the functions. This is pretty evident when we implement binary trees wherein you almost always have a Node * type of param in the recursive function acting on the binary tree. This is so as to facilitate modification of that node. If the node were to be passed as is (i.e not being a pointer type), the modifications to the nodes would have been to the local copy. Even in the case of vectors, while passing a copy of vectors is passed to the functions, to avoid which we use a reference &.
C++ passes arguments that are no pointers (int*) or references (int&) by value. You cannot modify the var of the calling block in the called function. Arrays are pointers.

Clarification of GLSL Function Calling Conventions

I recently encountered some confusion while using a GLSL function which modified (and copied out) one of its input parameters. Let's suppose this is the function:
float v(inout uint idx) {
return 3.14 * idx++;
}
Now let's use that function in a potentially ambiguous way:
uint idx = 0;
const vec4 values = vec4(v(idx), v(idx), v(idx), v(idx));
We might reasonably assume that after the call to the vec4 constructor returns, our vector values should equal {0.00, 3.14, 6.28, 9.42} and idx should equal 4. However, it occured to me to wonder if the order of evaluation of function arguments in GLSL is well defined, and if so whether the above assumed ordering is correct. Alternatively, could this result in (implementation dependent) undefined behavior?
So of course I consulted the GLSL spec (v4.6, rev3, §6.1.1, p116, "Function Calling Conventions"), which has the following to say:
All arguments are evaluated at call time, exactly once, in order, from left to right.
So far so good. But then farther down the page:
The order in which output parameters are copied back to the caller is undefined.
I'm not entirely clear on the significance of this second statement.
Does it mean that for the function float doWork(inout uint v1, inout uint v2) {...} that the order in which v1 and v2 are copied back is undefined? This would matter if you did something like passing the same local variable in place of both parameters.
Alternatively, does it instead mean that in the earlier example, the order in which the variable idx is updated is undefined, and as such the ordering of values is also undefined?
Or perhaps both of these cases are undefined? That is, perhaps all copy-back operations on the entire line of code happen in an unordered manner?
It goes without saying that using multiple variables to hold the values prior to the vec4 constructor call would trivially avoid this question entirely, but that's not the point. Rather, I'd like to know how this part of the standard was meant to be interpreted and whether or not my first example would result in idx containing an undefined value.

Eigen::Ref in pass-by-pointer

Similar to question Pointer vs Reference difference when passing Eigen objects as arguments
Let's say we have foo1 and matrix mat2by2:
void foo1(MatrixXd& container){
//...container matrix is modified here
}
and
Matrix33d mat2by2;
mat2by2 << 1,2,
3,4;
After reading http://eigen.tuxfamily.org/dox/TopicFunctionTakingEigenTypes.html, it seems like a better alternative to foo1 may be:
void foo2(Ref<MatrixXd> container){
//...container matrix is modified here
}
If foo2's parameter is being passed as a reference,
what would be the equivalent to pass-by-pointer using the Eigen::Ref class??
void foo(Eigen::MatrixXd* container){
//...container matrix is modified here
}
I think the basic idea is not to use pointers or references directly. Eigen uses template expressions to represent calculations. This means the type changes depending on the expression used to calculate the matrix, and expressions are potentially carried around unevaluated.
If necessary Ref will evaluate the template expression into a temporary object matching the memory layout you requested to pass as an argument. If the memory layout of your argument matches the memory layout required by your parameter, Ref will act as a transparent reference.
Borrowing directly from the documentation: Your input parameters should be declared constant, while non-const parameters can be used as output parameters.
void cov(const Ref<const MatrixXf> x, Ref<MatrixXf> C)
{
...
C = ...; // Your return value here
}
If you read from and write to a matrix, the parameter should also obviously be non-const.
For optional parameters you could use a pointer to a Ref.
Edit: The documentation does note that you can use constant references directly to pass parameters. This only works because the compiler is happy to convert temporary objects to const-references. It will not work for pointers.

Shader's function parameters performance

I'm trying to understand how passing parameters is implemented in shader languages.
I've read several articles and documentation, but still I have some doubts. In particular I'm trying to understand the differences with a C++ function call, with a particular emphasis on performances.
There are slightly differences between HLSL,Cg and GLSL but I guess the underline implementation is quite similar.
What I've understood so far:
Unless otherwise specified a function parameter is always passed by value (is this true even for matrix?)
Passing by value in this context hasn't the same implications as with C++. No recursion is supported, so the stack isn't used and most function are inlined and arguments directly put into registers.
functions are often inlined by default (HLSL) or at least inline keyword is always respected by the compiler (Cg)
Are the considerations above right?
Now 2 specific question:
Passing a matrix as function parameter
inline float4 DoSomething(in Mat4x4 mat, in float3 vec)
{
...
}
Considering the function above, in C++ that would be nasty and would be definitely better to use references : const Mat4x4&.
What about shaders? Is this a bad approach? I read that for example inout qualifier could be used to pass a matrix by reference, but actually it implicates that matrix be modified by the called function..
Does the number (and type of arguments) have any implication? For example is better use functions with a limited set of arguments?Or avoid passing matrices?
Is inout modifier a valid way to improve performance here? If so, anyone does know how a typical compiler implement this?
Are there any difference between HLSL an GLSL on this?
Does anyone have hints on this?
According to the spec, values are always copied. For in parameters, the are copied at call time, for out parameters at return time, and for inout parameters at both call and return time.
In the language of the spec (GLSL 4.50, section 6.1.1 "Function Calling Conventions"):
All arguments are evaluated at call time, exactly once, in order, from left to right. Evaluation of an in parameter results in a value that is copied to the formal parameter. Evaluation of an out parameter results in an l-value that is used to copy out a value when the function returns. Evaluation of an inout parameter results in both a value and an l-value; the value is copied to the formal parameter at call time and the lvalue is used to copy out a value when the function returns.
An implementation is of course free to optimize anything it wants as long as the result is the same as it would be with the documented behavior. But I don't think you can expect it to work in any specify way.
For example, it wouldn't be save to pass all inout parameters by reference. Say if you had this code:
vec4 Foo(inout mat4 mat1, inout mat4 mat2) {
mat1 = mat4(0.0);
mat2 = mat4(1.0);
return mat1 * vec4(1.0);
}
mat4 myMat;
vec4 res = Foo(myMat, myMat);
The correct result for this is a vector containing all 0.0 components.
If the arguments were passed by reference, mat1 and mat2 inside Foo() would alias the same matrix. This means that the assignment to mat2 also changes the value of mat1, and the result is a vector with all 1.0 components. Which would be wrong.
This is of course a very artificial example, but the optimization has to be selective to work correctly in all cases.
Your first bullet point does not work when you consider arguments qualified using inout.
The real issue is what you do with the parameter inside the function, if you modify a parameter qualified with in then it cannot be "passed by reference" and a copy will have to be made. On modern hardware this probably is not a big deal, but Shader Model 2.0 was pretty limited in terms of number of temp registers and I ran into these kinds of issues more than once when GLSL and Cg first came out.
For reference, consider the following GLSL code:
vec4 DoSomething (mat4 mat, vec3 vec)
{
// Pretty straight forward, no temporary registers are required to pass arguments.
return vec4 (mat [0] + vec4 (vec, 0.0));
}
vec4 DoSomethingCopy (mat4 mat, vec3 vec)
{
mat [0][0] = 0.0; // This requires the compiler to make a local copy of mat
return vec4 (mat [0] + vec4 (vec, 0.0));
}
vec4 DoSomethingInOut (inout mat4 mat, in vec3 vec)
{
mat [0][0] = 0.0; // No copy required, but the original mat is modified
return vec4 (mat [0] + vec4 (vec, 0.0));
}
I cannot really comment on performance, my only bad experiences had to do with hitting actual hardware limits on older GPUs. Of course you should assume that any time something has to be copied it is going to negatively impact performance.
All shader functions are inlined (recursive function are forbidden). The concept of reference/pointer is invalid here too. The only case when some code will be generated is when you write on an input parameter. However, if the original registers aren't used anymore the compiler will probably use the same registers, and the copy (mov operation) won't be needed.
Bottom line: function invocation is free.

Does C++ pass objects by value or reference?

A simple question for which I couldn't find the answer here.
What I understand is that while passing an argument to a function during call, e.g.
void myFunction(type myVariable)
{
}
void main()
{
myFunction(myVariable);
}
For simple datatypes like int, float, etc. the function is called by value.
But if myVariable is an array, only the starting address is passed (even though our function is a call by value function).
If myVariable is an object, also only the address of the object is passed rather than creating a copy and passing it.
So back to the question. Does C++ pass a object by reference or value?
Arguments are passed by value, unless the function signature specifies otherwise:
in void foo(type arg), arg is passed by value regardless of whether type is a simple type, a pointer type or a class type,
in void foo(type& arg), arg is passed by reference.
In case of arrays, the value that is passed is a pointer to the first element of the array. If you know the size of the array at compile time, you can pass an array by reference as well: void foo(type (&arg)[10]).
C++ always gives you the choice: All types T (except arrays, see below) can be passed by value by making the parameter type T, and passed by reference by making the parameter type T &, reference-to-T.
When the parameter type is not explicitly annotated to be a reference (type &myVariable), it is always passed by value regardless of the specific type. For user-defined types too (that's what the copy constructor is for). Also for pointers, even though copying a pointer does not copy what's pointed at.
Arrays are a bit more complicated. Arrays cannot be passed by value, parameter types like int arr[] are really just different syntax for int *arr. It's not the act of passing to a function which produces a pointer from an array, virtually every possible operation (excluding only a few ones like sizeof) does that. One can pass a reference-to-an-array, but this explicitly annotated as reference: int (&myArray)[100] (note the ampersand).
C++ makes both pass by value and pass by reference paradigms possible.
You can find two example usages below.
http://www.learncpp.com/cpp-tutorial/72-passing-arguments-by-value/
http://www.learncpp.com/cpp-tutorial/73-passing-arguments-by-reference/
Arrays are special constructs, when you pass an array as parameter, a pointer to the address of the first element is passed as value with the type of element in the array.
When you pass a pointer as parameter, you actually implement the pass by reference paradigm yourself, as in C. Because when you modify the data in the specified address, you exactly modify the object in the caller function.
In C++, types declared as a class, struct, or union are considered "of class type". These are passed by value or you can say a copy using copy constructor is passed to the functions. This is pretty evident when we implement binary trees wherein you almost always have a Node * type of param in the recursive function acting on the binary tree. This is so as to facilitate modification of that node. If the node were to be passed as is (i.e not being a pointer type), the modifications to the nodes would have been to the local copy. Even in the case of vectors, while passing a copy of vectors is passed to the functions, to avoid which we use a reference &.
C++ passes arguments that are no pointers (int*) or references (int&) by value. You cannot modify the var of the calling block in the called function. Arrays are pointers.