golang.org/x/image/math/{f32,f64} packages

450 views
Skip to first unread message

Nigel Tao

unread,
Feb 26, 2015, 1:23:10 AM2/26/15
to golang-dev, David Crawshaw, Stephen Gutekanst, Jeff Juozapaitis, Dmitri Shuralyov, Robert Griesemer
There's a bunch of graphics / OpenGL Go packages out there now [0].
They each have their own definitions of Vec2, Mat3, Mat4x4, etc. I'd
like to standardize on a uniform vector and math implementation, to
live in golang.org/x/image/math/{f32,f64}, so we'd have f32.Vec2,
f64.Mat3, etc.

The package will just be the type definitions, and maybe some dumb
methods (e.g. adding two vectors) that we can all agree on. Smart
code, such as calculating a projection matrix based on viewpoint,
angle, etc. are out of scope, and belongs in packages that *use* f32,
instead of being *in* f32.

There was a previous CL at
https://go-review.googlesource.com/#/c/2734/ that also links to a
shared document that discusses what's in and out of scope but it seems
to have stalled, and I don't think that doc was ever circulated with
golang-dev. From that CL's description:

----
Effectively, these packages will enable the creation of a rich set of
interoperable graphics math packages by the Go community ranging from
standard math, to collision detection, physics engines, etc.

See the proposal for more information:

https://docs.google.com/document/d/1BdQMB7OWzqFyn4VpwkvCVRFZ5KJMQCgyAA5KRMdMId8/edit?usp=sharing
----

I have sent out a new CL at https://go-review.googlesource.com/6076

[0] Stephen Gutekanst gave me this list earlier:
https://github.com/go-gl/mathgl
https://github.com/golang/mobile/tree/master/f32
https://github.com/azul3d/lmath
https://github.com/ungerik/go3d
https://github.com/Jragonmiris/mathgl
https://github.com/skelterjohn/geom
https://github.com/spate/vectormath

Christoph Hack

unread,
Feb 26, 2015, 1:50:59 AM2/26/15
to golan...@googlegroups.com, craw...@golang.org, stephen....@gmail.com, jrago...@gmail.com, shur...@gmail.com, g...@golang.org
Wow, that are great news! I am currently working on a toy physic engine / OpenGL game and I have encountered the problem myself.

My only complain about the current proposal is the missing Quaternion type. I think quaternions are quite common in 3D graphic programming and my code relies heavily on them. Therefore, a standardized quaternion definition would be useful as well!

-christoph

Egon Elbre

unread,
Feb 26, 2015, 2:48:48 AM2/26/15
to golan...@googlegroups.com, craw...@golang.org, stephen....@gmail.com, jrago...@gmail.com, shur...@gmail.com, g...@golang.org
On Thursday, 26 February 2015 08:23:10 UTC+2, Nigel Tao wrote:
There's a bunch of graphics / OpenGL Go packages out there now [0].
They each have their own definitions of Vec2, Mat3, Mat4x4, etc. I'd
like to standardize on a uniform vector and math implementation, to
live in golang.org/x/image/math/{f32,f64}, so we'd have f32.Vec2,
f64.Mat3, etc.

I like the idea, but I'm not convinced that x/image/math is the best place for it. 
Maybe x/math/, x/matrix/, x/geom or something like that would be better?

+ Egon

Nigel Tao

unread,
Feb 26, 2015, 5:02:56 AM2/26/15
to Egon Elbre, golang-dev, David Crawshaw, Stephen Gutekanst, Jeff Juozapaitis, Dmitri Shuralyov, Robert Griesemer
On Thu, Feb 26, 2015 at 6:48 PM, Egon Elbre <egon...@gmail.com> wrote:
> I like the idea, but I'm not convinced that x/image/math is the best place
> for it.
> Maybe x/math/, x/matrix/, x/geom or something like that would be better?

Reasons for the x/image bikeshed color include that this is limited to
2- 3- and 4-sized vectors and matrices, as used by 2D and 3D graphics,
and not general-purpose, arbitrary-sized matrices for numerical
algorithms. These are arrays, not slices.

Also, the packages are tiny: tens of lines of code each. Maybe it'll
eventually grow to 100. But it doesn't seem worth starting a whole new
x/repo for them.

Nigel Tao

unread,
Feb 26, 2015, 5:04:00 AM2/26/15
to Christoph Hack, golang-dev, David Crawshaw, Stephen Gutekanst, Jeff Juozapaitis, Dmitri Shuralyov, Robert Griesemer
On Thu, Feb 26, 2015 at 5:50 PM, Christoph Hack <tux...@gmail.com> wrote:
> My only complain about the current proposal is the missing Quaternion type.

I'm not ruling out quaternions, but they're not part of the initial
check-in, and I haven't seen as many quaternion implementations as
I've seen 3x3 matrix implementations.

snes...@gmail.com

unread,
Feb 26, 2015, 6:30:21 AM2/26/15
to golan...@googlegroups.com, craw...@golang.org, stephen....@gmail.com, jrago...@gmail.com, shur...@gmail.com, g...@golang.org
With the very strict Go type system such a standardization of basic math types is indeed almost a must. Go code often becomes inefficient, ugly and possibly unsafe by the conversions that interop between datatypes defined in unrelated packages requires. It is very tempting to use package unsafe for efficient type conversions, and i have done so, but of course the ideal is to need no conversions at all.

How to arrive at a consensus about the type definitions? I have been torn between [4]float32 and X, Y, Z, W variants for instance, and what about R, G, B, A selectors, etc.. I wished Go would allow a union of those like C/C++, or GLSL like flexible field syntax. From a distance i would say that the array variant is the most flexible and generic, but i find code written using it less clear to read. Maybe the use of constants for indexing can alleviate this, a la vec[X], where: const X = 0, etc. 

If basic methods are included, would this be a good time to consider SIMD as well?    

Robert Griesemer

unread,
Feb 26, 2015, 12:21:29 PM2/26/15
to Jeff Juozapaitis, snes...@gmail.com, golang-dev, David Crawshaw, Stephen Gutekanst, Dmitri Shuralyov
The spec does not provide guarantees of struct field ordering or packing.

That said, I believe all (?) compilers follow the source order when representing structs in memory, and I wouldn't be surprised if code was dependent on this property.

- gri

On Thu, Feb 26, 2015 at 9:18 AM, Jeff Juozapaitis <jrago...@gmail.com> wrote:
Are there guarantees on struct field ordering and/or packing? We were always hesitant to use structs because as we understood it there was no guarantee that the fields would be stored unpacked, in order like they would with arrays.

Jeff Juozapaitis

unread,
Feb 26, 2015, 12:31:24 PM2/26/15
to snes...@gmail.com, golan...@googlegroups.com, David Crawshaw, Stephen Gutekanst, Dmitri Shuralyov, Robert Griesemer
Are there guarantees on struct field ordering and/or packing? We were always hesitant to use structs because as we understood it there was no guarantee that the fields would be stored unpacked, in order like they would with arrays.
On Thu, Feb 26, 2015 at 3:30 AM, <snes...@gmail.com> wrote:

Robert Griesemer

unread,
Feb 26, 2015, 12:38:02 PM2/26/15
to Jeff Juozapaitis, snes...@gmail.com, golang-dev, David Crawshaw, Stephen Gutekanst, Dmitri Shuralyov

Stephen Gutekanst

unread,
Feb 26, 2015, 5:14:16 PM2/26/15
to golan...@googlegroups.com, craw...@golang.org, stephen....@gmail.com, jrago...@gmail.com, shur...@gmail.com, g...@golang.org
Nigel,

Thank you for picking this up. I've abandoned my old one in favor of yours. Due to the long (~6mo) process (and overall confusion) I fell behind on the CL, my apologies.

The fact that struct fields are not in a guarantee'd memory layout sounds like a good reason to stick with arrays -- as these will be passed unsafely to API's like OpenGL and Direct3D.

I'm highly in favor of this and will try to keep up.

Cheers,
Stephen

Christoph Hack

unread,
Feb 26, 2015, 5:49:10 PM2/26/15
to golan...@googlegroups.com, tux...@gmail.com, craw...@golang.org, stephen....@gmail.com, jrago...@gmail.com, shur...@gmail.com, g...@golang.org
From the list of packages you have posted earlier, 5 (out of 7) packages also implement quaternions :)

-christoph

Jeff Juozapaitis

unread,
Feb 26, 2015, 8:31:02 PM2/26/15
to Erwin Driessens, golan...@googlegroups.com, David Crawshaw, Stephen Gutekanst, Dmitri Shuralyov, Robert Griesemer
If basic methods are included, would this be a good time to consider SIMD as well?    

SIMD is good, but I'd like to note that I've done a ton of benchmarks and come to some conclusions:

1. There's almost no benefit for anything other smaller than Vec4/Mat4. I can't recall the specifics, but doing a SIMD determinant on a 3x3 matrix resulted in something like a 1% speed increase. (It's also hard to work with SIMD code for odd-sized vectors so there's that); for Mat4 and Vec4 it can be extremely significant though.

2. It's best with pointers. We're talking 5x-10x speed increases on most operations instead of 1.5x.
3. This only applies if you're calling the SIMD function directly, any indirection kills the performance and negates the SIMD.

Let me elaborate on 3. In Go, we can't implement assembly on pointer receivers, like we can values, e.g.:

TEXT ·Mat4·Mul(SB)

The equivalent

TEXT ·*Mat4·Mul(SB)

Will NOT work.

The obvious workaround is this:

func (m1 *Mat4) Mul(m2 Mat4) {
    mul4(m1,m2)
}
TEXT ·mul4(SB)

At SIMD-levels of optimization, this single passthrough pretty much negates the SIMD performance benefit. Which limits our options to either writing a SIMD extension to the Go compiler ourselves that optimizes our code, and hope it gets completed and accepted for a nearby release, asking for a compiler extension to support custom assembly on pointer receivers, or making all methods of the form Op(m1, m2, m3[...]) instead of the "infix" m1.Op(m2,m3,[...])

I understand that at some point there was a pull request to allow pointer-receiver assembly implementations but it got rejected because ???.

Nigel Tao

unread,
Feb 26, 2015, 10:20:36 PM2/26/15
to snes...@gmail.com, golang-dev, David Crawshaw, Stephen Gutekanst, Jeff Juozapaitis, Dmitri Shuralyov, Robert Griesemer
On Thu, Feb 26, 2015 at 10:30 PM, <snes...@gmail.com> wrote:
> How to arrive at a consensus about the type definitions? I have been torn
> between [4]float32 and X, Y, Z, W variants for instance, and what about R,
> G, B, A selectors, etc.. I wished Go would allow a union of those like
> C/C++, or GLSL like flexible field syntax. From a distance i would say that
> the array variant is the most flexible and generic, but i find code written
> using it less clear to read. Maybe the use of constants for indexing can
> alleviate this, a la vec[X], where: const X = 0, etc.

We arrive at a consensus by discussing it in this mail thread and on
the code review. Note that there are valid arguments for a variety of
approaches, e.g. whether a Mat3 is [3][3]float32 or [9]float32, but at
some point we have to pick one and stick with it.

As for saying v.X instead of v[X], I don't think that's going to
happen in Go per se. I could imagine a separate 'compiler' that took
something similar to GLSL shaders and translated "a.X = b.G" to be
"a[0] = b[1]", or skipped the middleman and output (SIMD) assembly
that was compatible with the Go calling convention, but I don't see it
part of Go the language.

You are, of course, free to define your own X, Y, Z, W, R, G, B, A, S,
T, P and Q constants in your own package.

Jeff Juozapaitis

unread,
Feb 26, 2015, 10:29:25 PM2/26/15
to Nigel Tao, Erwin Driessens, golang-dev, David Crawshaw, Stephen Gutekanst, Dmitri Shuralyov, Robert Griesemer
The only good way I can think of the define coordinate systems is small subpackages (because some systems overlap, e.g. rstv and rgba)

package rstv

const (
    R = iota
    S
    T
    V
)

package xyzw

const (
    X = iota
    Y
    Z
    W
)

And I'm not sure it's worth polluting the namespace with that.

Erwin

unread,
Feb 27, 2015, 7:13:10 AM2/27/15
to Jeff Juozapaitis, golang-dev, David Crawshaw, Stephen Gutekanst, Dmitri Shuralyov, Robert Griesemer
1. There's almost no benefit for anything other smaller than Vec4/Mat4. I can't recall the specifics, but doing a SIMD determinant on a 3x3 matrix resulted in something like a 1% speed increase. (It's also hard to work with SIMD code for odd-sized vectors so there's that); for Mat4 and Vec4 it can be extremely significant though.

Similar experiences here. There might be more gain with non-multiple of 4 types when processing them in larger batches though? 
 
 
2. It's best with pointers. We're talking 5x-10x speed increases on most operations instead of 1.5x.
3. This only applies if you're calling the SIMD function directly, any indirection kills the performance and negates the SIMD.

Let me elaborate on 3. In Go, we can't implement assembly on pointer receivers, like we can values, e.g.:

TEXT ·Mat4·Mul(SB)

The equivalent

TEXT ·*Mat4·Mul(SB)

Will NOT work.

The obvious workaround is this:

func (m1 *Mat4) Mul(m2 Mat4) {
    mul4(m1,m2)
}
TEXT ·mul4(SB)

At SIMD-levels of optimization, this single passthrough pretty much negates the SIMD performance benefit. Which limits our options to either writing a SIMD extension to the Go compiler ourselves that optimizes our code, and hope it gets completed and accepted for a nearby release, asking for a compiler extension to support custom assembly on pointer receivers, or making all methods of the form Op(m1, m2, m3[...]) instead of the "infix" m1.Op(m2,m3,[...])

Ideally we would get both the possibility to write methods in assembler and SIMD optimization in the compiler :) I don't like the Op(m1, m2, m3[...]) approach so much because we loose concise function names. 
 
I understand that at some point there was a pull request to allow pointer-receiver assembly implementations but it got rejected because ???.

I remember reading that as well. Micheal Jones has been running a patch that enables this for a while now? Maybe it will come once the transition to a Go Go compiler is a fact.

Erwin

unread,
Feb 27, 2015, 7:21:43 AM2/27/15
to Jeff Juozapaitis, Nigel Tao, golang-dev, David Crawshaw, Stephen Gutekanst, Dmitri Shuralyov, Robert Griesemer

I don't see that polluting anything... I mean, there is a full package path somewhere, where do you imagine the conflicts?
Reply all
Reply to author
Forward
0 new messages