Native Luau Vector3 Beta

Check the source code of the following game: WIP RoKarts [Alpha 1.8.5] - Roblox

The performance heavy code can be located in the “GeometryManager” module. It’s basically different intersection tests for geometric collisions.

The aforementioned CFrame-heavy code can be found in “KartRenderer”, I do quite a bit of CFrame operations to animate the player model with IK (specifically the UpdateAvatar function inside said module, I also tag this region of code in the microprofiler)

Also, if you do find any use at all in my place as some sort of benchmark or analysis, you should probably save a local copy of the place, since I am making daily changes that may or may not break things.

I started noticing this too interestingly!

When I tested, two Vector3 values would result in the same index in the table. For some reason, however, when I tried applying this to my terrain generator, not only did this fail a very large number of times, the performance was terrible. I saw my terrain gen go from taking between 0.9 and 1.9 seconds depending on the map size to 5.8 to 6.9 seconds. For reference, the number of index operations I’m doing is about 10k. Not sure what’s going on, but, Vectors really hate being table keys now and it seems that my previous excitement has wavered since its so much slower and doesn’t seem to work consistently.

Strangely, I got a small few attempts to work, and, the results were really really good, my generation speed got all the way down to 0.7 to 1.4 seconds, and, no issues with caching.

Edit: The caching issue was due to me rarely entering a CFrame into the first component of Vector3.new. Not sure what’s going on with that, I got no error, but, fixing this resolved the issue, though, I am still getting very poor performance. I got it back down to about 2.3 to 4.8 seconds now based on about 20 tests.

image
:flushed:

My solution to this was simple, for testing purposes, multiply your key values by a large number, say 10 million, and then when dealing with the keys, divide by 10 million. Then you will get more unique hashcodes, and the performance should be solid, more so than it was before with your old keying method.

1 Like

Thanks for the feedback, we will look closer at the hash function we use atm and make sure it works better.

1 Like

Very interesting. For now, I believe I can just use the following and calculate my own hash, which, shouldn’t be expensive since I already access the x y and z components:

local MAX_INT = 1e7 -- This is the best value I found without losing any precision from my testing
local MAX_INT_SQ = MAX_INT^2
local hash = x + y * MAX_INT + y * MAX_INT_SQ

This is pretty much what I was doing before, just with extra steps to prevent duplicate vectors, now I can just optimize things a good bit more.

2 Likes

This should be fixed next week.

2 Likes

Legacy code. If you pass something invalid to one of the arguments of Vector3.new it will interpret it as 0. Sadly, way too much code accidentally relies on this in one way or another so we can’t fix it.

3 Likes

Interesting. Maybe this could display a one-time warning in the console, it took me a minute to figure out and it seems kind of obscure. I accidentally found out that I was passing a CFrame and then I realized what was happening, but, that was probably lucky of me. I don’t really remember what the general rule of thumb is for when warnings should be displayed, but, I’m not too sure how else that could be signaled (if at all I suppose)

1 Like

This update is amazing! I have one question out of curiosity:
are Vector3s now stack allocated rather than heap allocated or are they in some other part of memory I haven’t learned about?

With the move from userdata to vector; will this change how Vectors are treated in regards to DataStores?

1 Like

I’m a bit confused. Probably because I don’t understand what any of this means… : ). Can someone please explain what this means in simpler terms? And what exactly does “Native” mean in this sense?

The gist: if you have code in your game that does stuff with Vector3s (such as reading/setting properties like Position/Size, or summing 2 positions together) then you can toggle on the beta feature to make your code run slightly faster, without needing to make any changes to your code.

1 Like

In this context “Native” is just referring to the Vector3s being a built in type (so its supported directly in the engine as its own unique thing). In lua there are value types called userdatas, and, they’re sort of like tables, but, they don’t have any table data. You can give them metatables, and, they have special properties. Pretty much all types are userdata in Roblox. UDim, Instance, Region3, CFrame, etc are all userdatas and all other data types like them currently are. If you do type on something Roblox specific, aside from functions, you’ll usually see userdata, and, if you do typeof you’ll see something like CFrame or Vector3.

With this change, the type of a Vector3 won’t be userdata anymore, it will be its own new type that’s built in to luau named vector. Since its not a userdata it can have lots of new behaviour separate from how userdata objects behave that can make it faster.

Basically, the tl;dr like @buildthomas said is that Vector3s are becoming their own unique type and the results are that doing stuff with Vector3s can be a lot faster and a lot more efficient than it was before.

3 Likes

There should be no difference in the way Vector3 is serialized into/from Data Stores.

1 Like

Right now Vector3.new(x) is actually more performant than Vector3.new(x, 0, 0) (it takes about .8x the time.)

3 Likes

I have a few questions above native Vector3s.

  1. What other behavior changes can we expect for type? This isn’t a backwards compatible change, but I’m okay with it so long as we’re given sufficient warning and it’s clear what will happen to other types like Vector2 and Color3. (IMO it might make sense to have Vector3 or number alternatives to Color3 properties, like part.Color3uint8. I’m guessing there’s a maximum to how many types can be represented in Luau, and Color3 stores basically the same data as Vector3.)

  2. Vector3 values no longer clean up in weak tables. Are there other types that will be affected like this in the future? This is another change that isn’t backwards compatible, but is acceptable so long as we’re given sufficient warning.

  3. Are there any plans to support game worlds with double precision? Of course game clients should use float Vector3s, but perhaps studio would benefit from using doubles. It’s easy to suffer from precision loss when moving models far from the origin in studio. Dividing the world into chunks is what many game engines / game tools do. I would argue that studio tools should use doubles instead of floats to improve the resulting positional accuracy far from the origin. Precision problems also become noticeable on small skinned characters ~5k units from the origin, but that’s a different issue.

4 Likes

Looks like I’ll need to do a ton of performance related testing now.

So, no serializing at all still? (That’s not bad, just from what you’re saying, if it’s as if it was a thing.)

Hey, quick question - I’m currently unavailable to test this new behavior, but how would tostring behave with this new primitive type? I currently have some code to automatically serialize all non-primitive values, which looks like this:

local function SerializeType(Obj)
    local Class = typeof(Obj)
    if type(Obj) ~= Class then
        return string.format('%s.new(%s)', Class, tostring(Obj))
    end
end

I removed the rest of the code in the SerializeType function, only keeping the relevant code.
Nothing was mentioned about tostring behaving differently than before, which is fine, but my question is mostly for the future - should I be worried about this code breaking as more types become native, or start functioning properly (like CFrames, Vector2’s etc)?

That code is certainly on thin ice, but not for the reason that you’re worried about here.

The main issue is that there’s no codified guarantee that for new types we add tostring(type) yields valid arguments to the constructor. For instance, RaycastParams and OverlapParams do not follow that rule, though they aren’t used in any properties, so they won’t break your code.

1 Like