Small Vectors Proposal
Mikola Lysenko
mclysenk at mtu.edu
Tue Jan 30 09:48:26 PST 2007
Coordinate geometry is a fundamental part of mathematics. Many
important problems in computer science are easily expressed in terms of
vectors and products. There are many domains which could benefit from
enhanced vector performance, especially in scientific and graphical
applications.
Despite their importance, very few languages provide support for vector
types. The prevailing notion is that it should be up to the compiler to
optimize and somehow automatically determine where SIMD code can be
applied. The problem with this approach is that it is often very
difficult for the compiler to spot where a SIMD operation can be
applied. Code must be carefully tweaked to make the compiler recognize
the correct possible optimization. Even then, many programmers still
write their own low-dimensional vector classes for convenience, and
perform all SIMD optimizations by hand in assembler.
It is important to make the distinction between low dimension vectors
and other higher order arrays. This is crucial since the higher
dimensional arrays are often more tenuously connected with any sort of
geometric or physical interpretation. Moreover, many architectures are
specially optimized for the lower dimensional cases, and offer special
registers which are virtually impossible to properly exploit using
libraries alone. The situation is analogous to floating point
arithmetic, proper hardware support requires language level integration.
Shader languages (Cg/GLSL) already provide extremely robust vector
arithmetic support, and give some direction to this proposal. Here are
the proposed language extensions:
1. Additional primitive types
All numerical primitive types in the language will be supplemented with
3 additional vector extensions. Each vector extension will contain 2, 3
or 4 components since these are best supported by hardware, and the most
relevant geometrically. The initial value for each component in each
type will be the same as that of its base type. Each the size of each
vector component will be equal to the size of its base type times its
dimension. Here is a complete list of the new types to be added:
Integer types:
byte2, byte3, byte4, short2, short3, short4, int2, int3, int4, long2,
long3, long4, cent2, cent3, cent4, ubyte2, ubyte3, ubyte4, ushort2,
ushort3, ushort4, uint2, uint3, uint4, ulong2, ulong3, ulong4, ucent2,
ucent3, ucent4
Floating types:
float2, float3, float4, double2, double3, double4, real2, real3, real4
Imaginary types:
ifloat2, ifloat3, ifloat4, idouble2, idouble3, idouble4, ireal2, ireal3,
ireal4
Complex types:
cfloat2, cfloat3, cfloat4, cdouble2, cdouble3, cdouble4, creal2, creal3,
creal4
All new vector types are pass-by-value, and may be used in combination
with any number of other types. The following expressions would all be
valid:
int2[] a;
float4* b;
char[][ucent4] c;
2. Vector literals
It must be possible to declare a vector literal with unambiguously as an
argument to a function, or any number of other situations. This syntax
must be concise since it will be used often, and it must be flexible,
allowing vectors to be constructed via concatenation. In this light,
the following is proposed:
float4(1, 2, 3, 4);
int2(100, 200);
creal3(1 + 2i, 3+4i, 5-6i);
Additionally, this syntax will also support the construction of vectors
from other sub vectors. This can be useful for augmenting a 3-vector
with a fourth homogenous component for transformation amongst other
things. Here are some examples:
float4(float3(x, y, z), 1); // returns (x, y, z, 1)
int3(0, int2(a, b)); // returns (0, a, b)
creal4(ireal2(r, z), real2(p, q)); // returns (r, z, p, q)
3. Casting rules
Vector types will obey the same casting rules as their base type within
a single dimension. It is not possible to use a cast to change the
dimension of a vector.
cast(float2)(int2(1, 2)); //ok
cast(int)(float2(3, 4)); //error, int is not the same dimension as float2
real3(a, b) + ireal(c, d); //ok, type of expression is creal3
4. Component-Wise Arithmetic
All of the arithmetic operators on primitive types will be extended such
that when applied to vectors they operate on each component
independently. The size and types of the vectors must be compatible
with the operator.
float4(1, 2, 3, 4) + float4(3, 5, 6, 7); // Vector-Vector operation,
returns (4, 7, 9, 11)
float3(1, 5, 6) * float3(1, 0, 2); // returns (1, 0, 12)
uint2(0xFF00FF, 0x00FF88) & uint2(0xFF, 0xFF); // returns (0xFF, 0x88)
int3(x, y, z) * int2(a, b); // error, size of vectors does not match
Additionally, scalar vector operations will be supported. Each
scalar-vector operator is applied per-component within the vector.
int2(0, 3) * 5; // Vector-Scalar operation, returns (0, 15)
byte2(2, 5) + 6; // returns (8, 11)
Note that the only two comparison operators allowed on vectors are ==
and !=. The problem with <, >, etc. is that there is no commonly agreed
upon definition for such things. Therefore the operators are omitted to
avoid confusion.
int2(1, 2) == int2(1, 2); //ok, evaluates to true
float3(1, 0, 0) < float3(0, 0, 1); // error, < is undefined for vectors
5. Swizzle operations
In many vector applications, it is often necessary to reorder the
components. In shader languages this is accomplished via 'swizzle'
properties in each vector type. Each swizzle selects a set of 1-4
vector components and places them into a new vector of the given
dimension. Conventionally these components have the following names:
x - first component
y - second component
z - third component
w - fourth component
A swizzle is then given as a property consisting of 1-4 components in a
given order. Here are some examples:
float2(a, b).yx; // evaluates to (b, a)
float3(1, 2, 3).zzyx; // evaluates to (3, 3, 2, 1)
byte4('a', 'b', 'c', 'd').w; // evaluates to 'd'
creal3(1 + 2i, 0, 4i).zzzz; // evaluates to (4i, 4i, 4i, 4i)
int2(1, 2).z; // error, vector does not have 3 components
These can be useful when projecting homogenous vectors into a lower
dimension subspace, or when computing various products. Here is an
example of a cross product implemented using swizzles:
float3 cross(float3 a, float3 b)
{
return a.zxy * b.yzx - a.yzx * b.zxy;
}
And here is a simple homogenous projection:
float3 project(float4 hg)
{
return hg.xyz / hg.w;
}
6. Standard Library Additions
The basic vector type additions have been kept as spartan as possible to
avoid cluttering the basic language, while still allowing the maximum
expressiveness. Instead of defining various vector products as
properties, they are implemented within a standard library extension.
The reason for this is simple; many users may wish to redefine these
products based on their own coordinate system. Within the standard
library, all vector products are performed in right-handed euclidean
space.
Vector specific functions will be placed in a new library std.vmath, and
all functions in std.math will be extended to work on vectors as well
primitive types. Here is a list of functions std.vmath will support
along with a brief description:
dot(a, b) - Computes dot product of two vectors:
a.x * b.x + a.y * b.y + a.z * b.z + a.w * b.w
cross(a, b) - Computes the cross product as defined above. Only defined
for 3d vectors.
perp(a, b) - Computes the perp product of two vectors as (a.x * b.y -
a.y * b.x). Only defined for 2d vectors.
mag(a) - Computes the magnitude of a vector, given as: sqrt(x * x + y *
y + z * z + w * w);
mag2(a) - Compute the square of the magnitude of a vector.
norm(a) - Returns a / mag(a)
lerp(a, b, t) - Compute an interpolated position between two vectors. a
* (1 - t) + b * t
Additionally, it would be convenient to have quaternion functions
supported in the standard library. A quaternion q would be a 4 vector
with the following form: q = q.w + q.x * i + q.y * j + q.z * k
qconj(a) - Computes the quaternion conjugate of a. Given as
float4(-a.xyz, a.w)
qmul(a, b) - Computes the quaternion product of two 4d vectors.
qrot(a, b) - Performs a quaternion rotation/dilation on the 3d vector b
by the 4d quaternion a.
Finally, there are range manipulation functions:
clamp(a, min, max) - Clamps each component of a to the range [min, max]
minv(a) - Returns the smallest component in the vector a
maxv(a) - Returns the largest component in the vector a
More information about the Digitalmars-d
mailing list