I am interested in using Kotlin WASM to make a 3D ...
# webassembly
j
I am interested in using Kotlin WASM to make a 3D game. I managed to get a decently complex Three.js scene working on Kotlin/JS before but WASM sounds much more promising and future-proof, so I have a few questions. 1. Since Kotlin/WASM has interoperability with JavaScript, I am assuming that using Three.js would still be an option paired with the inline JS function blocks. Is there anything that would prevent me from doing that? 2. Does Kotlin/WASM support multithreading? 3. Should I expect a native Kotlin/WASM 3D library to come out in the future similar to how Bevy engine works in Rust WASM?
đź‘€ 1
s
1. Should be doable. But it will have a few more interop restrictions compared to Kotlin/JS. Arrays and non-external class can’t be used directly in JS, so they would need to be copied or wrapped. 2. No multithreading in the near future. Just like in JavaScript or Kotlin/JS you can use Web Workers to send messages and share byte buffers, but you won’t be able to share normal Kotlin objects because Wasm standard doesn’t allow sharing managed data yet. 3. I would really hope so, but at the moment I’m not aware of any serious efforts to implement this.
j
Ok, this sounds like a fine tradeoff of interop for performance. Since a lot of my compute is deserializing data during loading, I can't spin it off into webworkers, so it's a bit unfortunate that we still can't share objects between threads. Do you by chance have a link to an article or something showing the performance gain I can expect from Kotlin/WASM? I am assuming that things like type casting and long numbers are fast unlike kotlin/JS so I am hoping it is a worthwhile upgrade.
s
I don’t think we have performance data published yet. Our initial results are mixed depending on the workload. Longs and type casts are indeed much faster, but passing data to JS and dynamic things, like interface method calls, tend to be slower. Wasm has a lot of performance potential that we haven’t utilised yet. We are still working on it.
j
Alright, interesting, seems like I would have to write my own tests. I have a particularly interesting detail question - is there a faster way to copy data from Kotlin/WASM arrays to JavaScript number arrays than simply running a loop and setting numbers one by one? I think I expect to be doing that a lot to pass 3D model data to Three.js and that seems like it could be slow based on your description of the current performance characteristics.
Also, thanks a lot for taking the time to answer my questions.
I just took another read at the docs and it seems like the only way to move a lot of bytes right now is through Strings or a least that's the only thing mentioned currently. Would go a long way if there was a way to copy number arrays in a similar way
s
is there a faster way to copy data from Kotlin/WASM arrays to Java number arrays than simply running a loop and setting numbers one by one?
Unfortunately no, this is a current Wasm GC limitation, but I expect it to be addressed in the next iteration of the spec. The fastest way currently is to copy data to linear memory (one element at a time) and then use it JavaScript. I found an example of the opposite operation, but the idea is the same.
^^ This linear memory approach uses temporary scoped allocator, thus memory should be used on JS side before leaving the allocator scope.
j
Ok, that is quite cool, I was unaware that this unsafe API exists. I have no experience with unsafe memory management but it seems like for the opposite direction, I would just need to do the same temporary memory allocation, save the bytes from Kotlin array into it, load it into a JS array, and then copy that into another array which is the final result?
Also would it be possible to have something like this in the stdlib or would that become redundant once the wasm GC limitation you mentioned gets resolved?
s
Sounds about right. Note that sometimes you won’t need the extra JS copy, in cases where JS API performs the processing/copy immediately upon receiving the data.
j
Would it be reliable if i try without copying first? or say the API just stores the array itself which has now been deallocated, does that lead to an exception (easy to debug) or undefined behavior?
s
These functions would a useful in stdlib, agree. They could do per-element copy initially and then optimised with newer Wasm features.
Would it be reliable if i try without copying first?
Use it only if you know for sure. For instance, if this behaviour is documented in the API. Otherwise you’ll get undefined behavior.
j
Right, thanks. Honestly copying the array in JS one more time is probably not the end of the world
s
can I make inline value classes with multiple parameters in Kotlin/WASM or is that planned for the future?
Not yet. We don’t have concrete plans. I think this feature would need to be designed in the future together with other Kotlin platforms. At least together with Kotlin/Native which doesn’t have any technical limitations as well.
j
Alright I will keep that in mind. How do value classes work currently? Are they just ignored and treated as a regular class, or are single-value inline classes implemented and work just like java (compiles down to the underlying value)?
I sometimes like to do tricks like using a value class as basically a 64-bit bitfield to improve performance in Java so I'm wondering whether that works in wasm
s
Single-value value classes are implemented, inlined, and auto-boxed when used as a supertype as usual.
j
I just need
@JvmInline
annotation or is there a different multiplatform one?
or do I not need an annotation at all if writing in the common/shared module?
s
Just
value class
should work fine on non-JVM platforms
j
Ah alright, that's really cool. I am assuming that also works in JS and native as well right?
s
Yes
j
Great, thank you so much for taking the time to answer my questions. I will try out the Kotlin/WASM when i have time and share my experience here.
a
is there a faster way to copy data from Kotlin/WASM arrays to JavaScript number arrays than simply running a loop and setting numbers one by one?
I am actually playing around with passing number arrays from K/Wasm to JS right now by doing something similar as the example of the opposite operation @Svyatoslav Kuzmich [JB] mentioned. The idea is the following: • pass a JS function reference to WebAssembly.Instance’s importObject with signature
(address, size, bytesPerElement) -> void
• write K/Wasm Array to linear memory utilising ScopedMemoryAllocator • call external JS function within MemoryAllocator scope passing pointer address, array size & bytesPerElement • use JS’s `Int8Array`/`Int16Array`/`Int32Array`/`BigInt64Array` to create a view on the buffer As reading the allocated buffer is done within the JS callback, I think this should be safe to be used from ScopedMemoryAllocator. Copying from linear memory to a separate JS ArrayBuffer within JS callback should also be possible if needed. Will try to push the example code asap for anyone interested to validate the idea, but I think this would work as an answer to your question @Jason Zhao .
j
Since there doesn't seem to be the final copying step in this method, does that get garbage collected by the JS side properly?
a
Should I expect a native Kotlin/WASM 3D library to come out in the future similar to how Bevy engine works in Rust WASM?
I’ve been mulling over using Raylib for this. I’m mucking about with Kotlin/Native bindings, and it work be relative easy (albeit boring and repetitive) to try writing Node/JS bindings…
a
@Jason Zhao the code here (https://github.com/alxgrk/kotlin-nodejs-wasm-example/blob/main/src/wasmMain/kotlin/primeFactorization.kt) passes the Wasm Array reference to the
onResult
callback - and since the JS implementation of
onResult
(
readJsArrayFromLinearMemory
in https://github.com/alxgrk/kotlin-nodejs-wasm-example/blob/main/src/wasmMain/kotlin/memoryUtil.kt) only creates a view on the buffer and consumes it within the scope of MemoryAllocator, it should be GCed correctly. If it would be copied to an JS array, of course the Node.js GC would take care of it.
342 Views