Join GitHub today
GitHub is home to over 50 million developers working together to host and review code, manage projects, and build software together.
Sign upWe should switch to column-major notation with column-vector #331
Comments
|
An example of this biting us... servo/rust-webvr@2b016cc This commit took a day to work out, because the specs are pretty clear that they want column-major matrices, and it is very odd that |
|
Worth considering forking euclid ( |
|
If we're going to fork incompatibly we might consider just switching to |
|
We cooooould make this part of the phantom type, so in |
I don't like how this reads as an ultimatum. When people express intent to fork an actively maintained project it usually means that either:
I don't think that either of these two apply here. People on the gecko and WebRender side would also prefer matrices to follow the most popular convention, my only ask being that we do it consistently accross gecko as well to avoid subtle bugs. I think that there is no shortage of good will to try to accommodate and make things work for both sides which shouldn't be hard since there doesn't seem to be disagreement about what everyone wants. This doesn't mean we are good at it but at least we try. I'm open to feedback if things are not working out. I understand that the prospect of going thourgh Gecko's infamous C++ code isn't appealing, but the last year has been rather intense for people involved with WebRender, and it's been a bit hard for me and others to justify spending time on something non-critical, if that thing makes uplifting patches harder. That's not to say I would have prevented anyone from doing it, I'm happy to assist with reviews. If you want someone else to do the work, there is only so much you can demand from people under pressure of shipping something, even when everyone want the same thing. So it comes as a surprise to me that the problem is considered important enough to fork euclid and have to juggle with both euclid and its fork in servo, but not enough to put effort into propagating the changes to both servo and gecko. |
I mean, it's a project we maintain. We're forking our own project. I want a copy free from row matrices to use for the VR stuff. This isn't a hostile fork. Also, forking frees us of having to worry about non-servo euclid users; we can design the new API without trying to smooth the transition. This isn't supposed to be an ultimatum, this is just that this bug is causing trouble for us, and a fork is a simple way to solve this problem for the area where it's causing us trouble (VR) without causing too much strife for y'all (fork can avoid touching rendering code).
We're already regularly hitting subtle bugs on the servo side because of this. We're working a lot with matrices for WebXR, and this bug makes it really easy to slip up since device docs and the WebXR spec assume column vector notation.
There were signals from Gecko folks (you? I forget) that this task would be incredibly nontrivial, which is why we've held off on this.
AIUI we don't actually use euclid transforms much in Servo, aside from VR. But we can totally do this fork for the VR stuff without coming close to touching webrender -- i.e. it wouldn't be much work to maintain at all. |
This is beside the point. Even then, look at who submitted and reviewed changes in the last couple of years. Do you consider WebRender to be your project as well?
Okay, I apologize for having taken a rather defensive stance here.
Could be me, the truth is I don't know most of the code that interacts with matrices so it's probably not incredibly nontrivial but I can't assume I'd be able to expedite it in a trivial amount of time either. Now, if the situation is that dire on servo's side and you can't afford to spend time on Gecko's code, go ahead and change the matrix convention in euclid now. I would have liked to do gecko and servo in lockstep, but if that's not an option I don't mean to be uncompromising. In counterpart I have a branch with a number of euclid breaking changes that I held off because of how time consuming it is to bump euclid in all of servo (at least for me). If these make it into euclid master and I let you or someone else deal with updating servo, does that sound fair? |
I don't think I've done that at all. I only bring up the "we maintain it" because of you painting this fork as a hostile fork. I don't think the fork is incompatible with working together, indeed I thought it would be the easiest way to work together and make this happen incrementally. Maintaining multiple versions of a single crate gets annoying and is prone to issues, whereas it's much easier for everyone involved if we use a new crate for all the matrices that don't touch webrender, which gives us a stopgap solution until we all actually have time for the coordinated updated.
I mean, I personally also want to avoid doing a euclid update that's not lockstep: it's not good for anyone involved. That's why I suggested the fork in the first place, it's a good bandaid, and it shouldn't affect y'all at all because our VR code is totally separate. It also means that we can make a breaking change without having to worry about how best to present that to consumers which aren't us. At the moment a major blocker is that I don't know how to change the euclid API in a way that makes it clear that this stuff has changed. We'd have to rename some methods, but it's not clear how.
Sure |
|
Trying to catch up on all the things here (with @jrmuizel , who helps a lot), it's not easy. Sorry if I'm missing something important!
Current euclid is row-major with row-vector. @Manishearth suggests to switch it to column-major column-vector. This wouldn't affect most of the logic, i.e. things like There is one external force that can help us make the right decision on these conventions: SIMD compatibility. Euclid today would require SIMD broadcast before doing a matrix-vector multiplication, since each SIMD vector is not continuous in memory. Fixing that would require changing exactly one of the conventions (but not both, like @Manishearth suggests) as well as a lot of internal logic, obviously... This is what @pcwalton suggests, and I sympathize with this approach. I.e. if we are to disrupt the workflow that much, better make it so we are faster as well, for SIMD usage (today or in the future). That is to say, I just rounded up and explained the choices as I see them. No objectified preference is expressed here. Subjectively, column vectors with row-major representation would be ideal for me. |
Actually, I think we should switch to column-vector, I don't care about column/row-major. Pick whichever is more efficient, I recall things are easier to vectorize in one way. If that way is column-vector row-major, so be it, we should do that |
|
It's easier to vectorize if you use column major matrices. See
`pathfinder_geometry`.
…On Fri, Jun 28, 2019, 8:40 AM Manish Goregaokar ***@***.***> wrote:
@Manishearth <https://github.com/Manishearth> suggests to switch it to
column-major column-vector.
Actually, I think we should switch to column-vector, I don't care about
column/row-major. Pick whichever is more efficient, I recall things are
easier to vectorize in one way. If that way is column-vector row-major, so
be it, we should do that
|
|
@nical we should merge your breaking changes first, so that any experimentation can be done on top of that |
I was wrong, Euclid has internal bugs due to this too, see #354 The core problem is that column vectors are so ubiquitous that nobody bothers to specify that they're using them, so it's going to be very easy to make such mistakes when working on euclid, let alone when using euclid. |
|
Looks like our |
|
As someone who isn't terribly good at math and just wants a lightweight vector math library for graphics... is there anything I can do to help this? |
|
Not really, you'd need to make a coordinated change across Gecko and Servo and that's really tricky to get right. |
|
So for the record I'm fine with gecko not being updated now since it's too big a blocker to get things moving. Updating webrender would be nice though (I suspect it wouldn't be too much work), though if servo is not ready to follow with an update soon after, that would be inconvenient (for the servo folks). |
|
I'm going to make some breaking changes soon, if there is still motivation around this, now is a good time to change the matrix conventions. |
|
I have motivation, I just don't have the time to handle any Firefox-side changes |
|
It's OK let's forget about firefox side changes. |
|
Quick recap of various existing memory layouts and notations to make sure we are on the same page. The following assumes the members are laid out in the same order as the members are provided (so row by row). I highlighted the members that describe the translation (that's how I orient myself in the different conventions out there), and the terminology used by the project for that layout/notation. The thing that makes efficient simd possible is the layout where the translation is contiguous in memory (euclid and pathfinder's current conventions). It's the only real argument that isn't based on someone's habits and tastes so let's start from there. Another detail is whether the notation starts with zero or one (m00 or m11 being the first member). Pathfinder seems to be the odd one here starting with zero. I can probably deal with having to juggle between mXY/mYX in gecko's C++ and rust code, but honnestly I just don't want to have to offset the indices on top of that, so I'll pretend it's not a thing. euclid, geckorow-major:
CSScolumn-major
pathfinder_geometry(edited) column-major
euclid next@Manishearth IIUC what you want is to use the same notation as pathfinder (swap mAB to mBA names and swap column_major to row_major terminology in method names). Is that right? Also if we go through with this, are you OK with doing the servo update (I can deal with WR)? |
|
The representation of pahfinder_geometry is backwards in your comment: it has the same model as CSS, but zero indexed and column major We need to:
I'd be happy to do the servo side of these changes if you handle webrender mXY can stay the same |
|
Indeed I was looking at its row major constructor. I fixed representation in the original comment.
Deal.
Ah right I remember some debates around the names. So current post_mul/pre_mul names were meant to signify "apply a transformation before (pre) this one" or "after (post) this one". The unfortunate presence of "mul" makes it sounds like a left/right multiplication in mathematical-notation sense which led to to confusion. I vaguely remember a discussion about this where you proposed In any case I want the API to be worded in terms of the semantics of the transformation rather than in terms of mathematical notations for abstract matrix objects whenever possible, and be worded in a way that makes that clear). |
|
I love Then the only thing that neds to be swapped is |
Previously: #146
In #329 we discovered that euclid uses row-vector notation, i.e. if you wish to transform a vector
vyou usev * T. Transformations compose asT1 * T2, whereT1is applied first. This is also what Gecko does. Column-vector notation works the other way, with transformations goingT * vand composing asT2 * T1.This is different from what OpenGL and most web specs (geometry, transforms, webxr) do. Overall while documents tend to explicitly specify row-major vs column-major since it varies a lot, documents do not specify row-vector vs column-vector, because column-vector notation is the norm.
Euclid is perfectly self-consistent with its usage of this notation, however, this breaks down at the border. If someone runs
Transform3D::create_translation(...).to_row_major_arrays(), the translation values will be in the wrong spot if they expected it to be a column-vector based representation.An example of this messing things up is here:
https://github.com/servo/webrender/blob/01046c6430992840c3a10e15762dbfb07a4b1039/webrender/src/device/gl.rs#L2323
uniform_matrix_4fv(_, false, _)takes a column-major array, however we call.to_row_major_array(). This works because a row-major row-vector matrix has the same representation as a column-major column-vector matrix.There are cases in Gecko where similar things happen. Overall due to using a very unpopular convention (this is the first time I've come across this convention in physics, graphics, web specs, or browser impls) users have to deliberately use
.row_majormethods when they mean.column_majorand vice versa, as well as swapping the order of.mXYaccessors sometimes.We should fix both. @nical has stated that they're open to this changing if Gecko is changed in lockstep. A bit of work, but doable.
This is a breaking change, however, and kinda annoying to do. I think it's worth it: This isn't the first time I've stumbled across this when implementing web specs, and last time was much worse since I never realized what the discrepancy was and just fixed it by swapping function calls until it worked (which largely seems to be what every other client of this library has been doing.)
cc @nox @pcwalton