snow buffer (and minor luxe) API changes

sign up sign in

Since these changes are fairly specific I wanted to mention them in more than a commit log.

luxe changes

  • renamed luxe.AppConfig to luxe.GameConfig for consistency and clarity
  • renamed xrel and yrel on MouseEvent to x_rel/y_rel
  • fixed oninputdown and oninputup callbacks. It now only passes an InputEvent which has the name property on it. This fixes a lot of weird inconsistency on those.
  • changed how events are handed into the app, which removes a lot of allocations
  • a lot of core and Game refactoring
  • window properties from the project flow file not respected. see here

You'll see a lot of core changes and cleaning going on. I'm also busy working on finalizing the luxe.Game structure so anything you see in the process can be considered volatile (very unlikely people would be spelunking down here before stable API anyway - but it's worth noting!).

If you run into issues do let me know on the repo.

snow changes

Originally, when I wrote the buffers, they were made with the ES specification in mind. I wanted to have parity with the js/c++ API (or at least as close as possible). I got the parity I wanted which was great, haxe let's you do a lot of stuff with syntax.

The JS API has things that I didn't really like but accepted - like a variadic constructor - meaning there are four or more flavours of the new Uint8Array call. This works well in JS where types don't matter and they look up types anyway, but in the strong typed version without constructor overloads, it ends up making the function signature rather arcane looking.

The JS constructors:

new TypedArray(length);  
new TypedArray(typedArray);  
new TypedArray(object);  
new TypedArray(buffer [, byteOffset [, length]]);  

While this does map 1:1 with the function signatures on the usage side, it isn't exactly clear what the options are if you aren't already familiar with the API. It also adds a lot of weight to the constructor making it less efficient, and adds a lot of checking and pondering before it does what you hoped. This doesn't make a lot of sense when using these since you already know what you want up front.

The second thing I didn't like as much is how the API also adds a bunch of API parity with the regular JS Array. In JS, that makes total sense... but in a cross platform implementation (that aims to be performant and minimal) these would end up adding a lot of unrelated API endpoints to the classes - so I opted to keep the basic API at first. This means that there isn't parity with the spec anyway... There are other places where the parity started to fall away, like the object constructor, and passing in a different typed array which does a conversion... and the similarity starts to make me wonder. While I wanted to implement some of these things for the benefit of the library, the entire point of my initial implementation was for snow.

Haxe lets us make our own API parity over the raw JS API's, which means we can have the API we want (without losing portability) as well as the performance.

I still intend to update, maintain and improve hxtypedarray as it stands, but the snow API now longer aligns with it and will be changing. snow has some other overarching requirements that don't really fit with the goals of that lib specifically, and since the buffers are a core snow API they should align with snow instead.

They'll have all the same benefits, just be smaller and faster.

snow API changes

These changes are made in the snow buffers API only. They are part of an ongoing clean up and optimization of the API, so don't get too comfortable just yet. I will note, except for the API changes to the constructors, unless you were reaching past the public API - most of the the changes are just minor syntax changes. The API remains fully cross platform, and leverages the native performance of each target even more so now!

PLEASE NOTE: There is one behavioural change
In the constructor to create a view over an existing ArrayBuffer:
fromBuffer(buffer:ArrayBuffer, byteOffset:Int, byteLength:Int)

This function changed the meaning of the last argument (if you consider it the equivalent of the JS constructor). The previous API and JS spec treat the last argument as "number of elements" rather than "number of bytes". In practice, using this API frequently, I made the mistake of passing a byte length into the third argument all the time. This is typically the information you have in your hand when you're creating a view in this manner, not the number of floats, but their size in bytes. For consistency with the other arguments and for clarity, all the fromBuffer arguments are in bytes now. If you want to keep it as elements, use elements * TheType.BYTES_PER_ELEMENT and calculate it.

note since the previous usage is a compiler error - it is easy to find and make the change. Take note and update your usage (if you had any specifying the unclear length arg)!

general changes

  • The constructor has changed to only one form:
    • new TypedArray(number of elements)
  • Options to construct are now static members:
    • fromArray, fromView, fromBuffer, fromBytes
  • fromBuffer arguments are all in bytes, and no longer optional.
    • note this is easy since the buffer has a length, instead of fromBuffer(buffer) it becomes fromBuffer(buffer, 0, buffer.byteLength)
  • Most arguments made explicit (i.e not optional) in performance related code (like constructors)
  • DataView continues to separate the big endian code out from the little endian code (since it wasn't complete, and shouldn't slow down the 99.9% use case). Since it wasn't working for big endian yet on C++ target anyway, there's not much difference yet.

c++ target specifics

  • Changed the typeArrayBuffer to BytesData instead of Bytes
    • Obviously treating it directly as this type (or previously, as bytes) would result in the code losing parity and thus not portable across targets, so this shouldn't matter much. If you were reaching past the public API and making the assumption you'll have to use Bytes.ofData if you must have bytes, or toBytes if you have a view over the buffer.
    • This change significantly cleans up a lot of native/extern interop, since the data is already in the format that makes sense to pass around. Bytes is a wrapper around BytesData, so in a number of places it was either being unwrapped. It makes more sense to swap this, the frequency and code paths where you want it unwrapped are more than the ones where you want it wrapped, so no need to pay the costs disproportionately.


That's it for now.

The new code is much faster in a number of ways, and I'll continue to clean up the API and performance characteristics of the buffer API - since a lot of the data transport in the engine is handled by this code. The new renderer uses the buffers extensively so it's important that they're not weighing things down.

Since we're not spec bound anymore, it also opens the opportunity of adding API endpoints that offer far superior performance on native targets when dealing with blocks of memory, things that our web target shouldn't limit randomly (it's harder for JS to allow certain things due to security sandboxing in the browser).

For the other API changes on the buffers, it will mostly be making the API code style consistent with the rest of snow and a few features. I'll do all the changes incrementally like I always do, and will make sure it's clear what changed and when and how to update your code as needed.

As above, If you run into issues do let me know on the repo!