You probably have heard about short vector types in C++ AMP from the introduction of the concurrency::graphics namespace, and I will also assume you have read the post on the norm and unorm scalar types.
In this post, I’m going to provide more details on short vector types, which emulate the behavior of short numerical vectors which are available in shader languages like HLSL and widely used in computer graphics programming. First I’ll talk about the concept, then about construction, then about the swizzling accessors, then about other operations, and I will end with an example of using metadata and reflection.
Short Vector Types Basics
To start using short vector types, you need to do the following:
C++ AMP short vector types are named as: ScalarType_N, where ScalarType is one of int, uint, float, double, norm, and unorm, and N is one of 2, 3, or 4. Note that uint is a typedef of unsigned int, available in the concurrency::graphics namespace. A ScalarType_N represents a short vector of N ScalarType components. So in total, there are 18 short vector types. For example, uint_2 represents a short vector of 2 unsigned integers. Short vector types can be used in both restrict(amp) and restrict(cpu) functions.
For each short vector type, there is a corresponding typedef without the underscore, e.g. uint2, which is the syntax typically expected by graphics programmers. These typedef’s reside in the concurrency::graphics::direct3d namespace.
Creating Short Vector Types
There are multiple ways to construct a short vector type object. With default construction, all the components are initialized to 0 values. For example,
Both components of “a” are initialized to 0. The copy construction and assignment are component-wise copy. For example,
You can also construct a ScalarType_N object by specifying the value for each of the N components via a constructor that takes N ScalarType parameters. For example,
You can also initialize all components with the same value. For example,
All four components of “d1” will be initialized to the double value of 3.14. This constructor also allows the implicit conversion from a ScalarType to ScalarType_N. The scalar value is copied into each component of the short vector object. For example,
One can construct a ScalarType_N object with an AnotherScalarType_N object. Each scalar component of the latter is converted to ScalarType using C++ conversion rule for primitive types or the conversions defined for norm/unorm type. The constructor is annotated with “explicit”, so the conversion from a ScalarType2_N to a ScalarType1_N has to be explicit. For example,
- construction (only set) as explained in previous section, and
- using a swizzling format accessor.
C++ AMP short vector types offer the syntax for accessing the component(s) as
ScalarType_N_Obj is a short vector of type ScalarType_N. The ident is an identifier specifying which components out of the ScalarType_N_Obj are selected for accessing. The above expression can be an l-value or an r-value. The ident is a combination of no more than N characters. The individual character in ident may be x, y, z, w, r, g, b, and a. Note that x, y, z, and w means the first, the second, the third, and the fourth component of the short vector respectively. As you may have guessed, r, g, b, a are synonymous for x, y, z, w. In ident, characters from group x, y, z, w, cannot be combined with characters from group r, g, b, a. Single component accessor, such as x or y, is type of ScalarType. Multi-component accessor is type of ScalarType_M, where M is the number of components in ident. For example, the type of e3.xy is uint_2.
The ident can use swizzling format to rearrange the components from ScalarType_N_Obj. For example, one can write,
The above code is equivalent to
For the initial release, C++ AMP does not offer all the possible swizzling formats. For a ScalarType_N, the swizzling format can have 1 to N components, each from the character group x, y, z, and w, and in any order. Repetition of a character is not allowed. The same set of swizzling formats are available using the character group r, g, b, and a. Below are examples of supported usages:
Here are some unsupported usages:
For unsupported swizzling format, you will have to explicitly use constructors.
With Visual Studio 2012, you can always use Intellisense to discover the available usages of swizzling formats, for example:
A variety of operations are supported for short vector types. Binary operators, compound assignment operators, and relational operators, are defined between two short vector types with the same ScalarType and N. Below are the supported operators:
- Arithmetic operators: +, -, *, /
- Bitwise operators (only for int_N and uint_N): %, ^, |, &, <<, >>, and ~
- Compound assignment operators:
- arithmetic operations: +=, -=, *=, /=
- bitwise operations: only for int_N, and uint_N: %=, ^=, |=, &=, <<=, >>=
- Equality operators: ==, !=
- Increment and decrement operators: ++, — both prefix and postfix
- Unary “-“ operator (Not available for unorm_N, and uint_N)
For all above operators, the operation is carried component-wise except for equality/inequality operators. For equality operator, “==”, it returns true if all components are equal. For inequality operator, “!=”, it returns true if any pair of components are not equal. Here are some examples:
For any operation that is defined between two short vectors, the same operation can be performed between a short vector and a scalar. The scalar’s type should either be the same as the short vector’s ScalarType, or be a type that could be implicitly converted to ScalarType_N with only one user-defined conversion. The operation is carried component-wise between each component of the short vector and the scalar. Here are some examples:
Metadata and Reflection
C++ AMP also provides some C++ metaprogramming facilities for short vector types.
Every short vector type exposes its number of components as a static member called size of type int, e.g., double_4 contains the following:
Every short vector type exposes its scalar type as a typedef, consistent with STL containers, e.g., double_4 has:
Another generic way to retrieve the size and value_type of a short vector type is to use a class called short_vector_traits, which allows you to write code like:
short_vector_traits also support scalar types, for example:
We also provide a class called short_vector, which is useful for programming short vectors generically, as
short_vector only has a typedef member called type. For the six ScalarType’s, when N is 1, short_vector::type is ScalarType; when N is 2, 3, or 4, short_vector::type is ScalarType_N. For all other instantiation of short_vector, a static assertion will be triggered. For example,
Following is an example using the above reflection classes. In this release, we don’t provide a stream operator for short vector types. However, you can implement it using short_vector, and short_vector_traits.
Then you can write code like,
This concludes my introduction to short vector types. As always, your feedback is welcome below or in our MSDN Forum.