Make wasm instantiation function separate
This commit is contained in:
parent
d8fe2a7922
commit
9ffe98a8c5
|
|
@ -49,10 +49,13 @@
|
|||
#ifndef LINALG_H
|
||||
#define LINALG_H
|
||||
|
||||
#include <cmath>
|
||||
#include <type_traits>
|
||||
#include <cstdint>
|
||||
#include <cstddef>
|
||||
#include <cmath> // For various unary math functions, such as std::sqrt
|
||||
#include <cstdlib> // To resolve std::abs ambiguity on clang
|
||||
#include <cstdint> // For implementing namespace linalg::aliases
|
||||
#include <array> // For std::array
|
||||
#include <iosfwd> // For forward definitions of std::ostream
|
||||
#include <type_traits> // For std::enable_if, std::is_same, std::declval
|
||||
#include <functional> // For std::hash declaration
|
||||
|
||||
namespace linalg
|
||||
{
|
||||
|
|
@ -109,8 +112,8 @@ namespace linalg
|
|||
template<int A> struct make_seq_impl<A,3> { using type=seq<A+0,A+1,A+2>; };
|
||||
template<int A> struct make_seq_impl<A,4> { using type=seq<A+0,A+1,A+2,A+3>; };
|
||||
template<int A, int B> using make_seq = typename make_seq_impl<A,B-A>::type;
|
||||
template<class T, int M, int... I> vec<T,sizeof...(I)> constexpr swizzle(const vec<T,M> & v, seq<I...> i [[maybe_unused]]) { return {getter<I>{}(v)...}; }
|
||||
template<class T, int M, int N, int... I, int... J> mat<T,sizeof...(I),sizeof...(J)> constexpr swizzle(const mat<T,M,N> & m, seq<I...> i, seq<J...> j [[maybe_unused]]) { return {swizzle(getter<J>{}(m),i)...}; }
|
||||
template<class T, int M, int... I> vec<T,sizeof...(I)> constexpr swizzle(const vec<T,M> & v, seq<I...>) { return {getter<I>{}(v)...}; }
|
||||
template<class T, int M, int N, int... I, int... J> mat<T,sizeof...(I),sizeof...(J)> constexpr swizzle(const mat<T,M,N> & m, seq<I...> i, seq<J...>) { return {swizzle(getter<J>{}(m),i)...}; }
|
||||
|
||||
// SFINAE helpers to determine result of function application
|
||||
template<class F, class... T> using ret_t = decltype(std::declval<F>()(std::declval<T>()...));
|
||||
|
|
@ -193,6 +196,7 @@ namespace linalg
|
|||
struct std_fmod { template<class A, class B> auto operator() (A a, B b) const -> decltype(std::fmod (a, b)) { return std::fmod (a, b); } };
|
||||
struct std_pow { template<class A, class B> auto operator() (A a, B b) const -> decltype(std::pow (a, b)) { return std::pow (a, b); } };
|
||||
struct std_atan2 { template<class A, class B> auto operator() (A a, B b) const -> decltype(std::atan2 (a, b)) { return std::atan2 (a, b); } };
|
||||
struct std_copysign { template<class A, class B> auto operator() (A a, B b) const -> decltype(std::copysign(a, b)) { return std::copysign(a, b); } };
|
||||
}
|
||||
|
||||
// Small, fixed-length vector type, consisting of exactly M elements of type T, and presumed to be a column-vector unless otherwise noted
|
||||
|
|
@ -204,8 +208,8 @@ namespace linalg
|
|||
// NOTE: vec<T,1> does NOT have a constructor from pointer, this can conflict with initializing its single element from zero
|
||||
template<class U>
|
||||
constexpr explicit vec(const vec<U,1> & v) : vec(static_cast<T>(v.x)) {}
|
||||
constexpr const T & operator[] (int i [[maybe_unused]]) const { return x; }
|
||||
constexpr T & operator[] (int i [[maybe_unused]]) { return x; }
|
||||
constexpr const T & operator[] (int) const { return x; }
|
||||
constexpr T & operator[] (int) { return x; }
|
||||
|
||||
template<class U, class=detail::conv_t<vec,U>> constexpr vec(const U & u) : vec(converter<vec,U>{}(u)) {}
|
||||
template<class U, class=detail::conv_t<U,vec>> constexpr operator U () const { return converter<U,vec>{}(*this); }
|
||||
|
|
@ -282,8 +286,8 @@ namespace linalg
|
|||
template<class U>
|
||||
constexpr explicit mat(const mat<U,M,1> & m) : mat(V(m.x)) {}
|
||||
constexpr vec<T,1> row(int i) const { return {x[i]}; }
|
||||
constexpr const V & operator[] (int j [[maybe_unused]]) const { return x; }
|
||||
constexpr V & operator[] (int j [[maybe_unused]]) { return x; }
|
||||
constexpr const V & operator[] (int) const { return x; }
|
||||
constexpr V & operator[] (int) { return x; }
|
||||
|
||||
template<class U, class=detail::conv_t<mat,U>> constexpr mat(const U & u) : mat(converter<mat,U>{}(u)) {}
|
||||
template<class U, class=detail::conv_t<U,mat>> constexpr operator U () const { return converter<U,mat>{}(*this); }
|
||||
|
|
@ -465,6 +469,7 @@ namespace linalg
|
|||
template<class A, class B> apply_t<detail::std_fmod, A, B> fmod (const A & a, const B & b) { return apply(detail::std_fmod{}, a, b); }
|
||||
template<class A, class B> apply_t<detail::std_pow, A, B> pow (const A & a, const B & b) { return apply(detail::std_pow{}, a, b); }
|
||||
template<class A, class B> apply_t<detail::std_atan2, A, B> atan2 (const A & a, const B & b) { return apply(detail::std_atan2{}, a, b); }
|
||||
template<class A, class B> apply_t<detail::std_copysign, A, B> copysign(const A & a, const B & b) { return apply(detail::std_copysign{}, a, b); }
|
||||
|
||||
// Component-wise relational functions on vectors
|
||||
template<class A, class B> constexpr apply_t<detail::op_eq, A, B> equal (const A & a, const B & b) { return apply(detail::op_eq{}, a, b); }
|
||||
|
|
@ -546,7 +551,7 @@ namespace linalg
|
|||
template<class T, int M> constexpr mat<T,M,3> transpose(const mat<T,3,M> & m) { return {m.row(0), m.row(1), m.row(2)}; }
|
||||
template<class T, int M> constexpr mat<T,M,4> transpose(const mat<T,4,M> & m) { return {m.row(0), m.row(1), m.row(2), m.row(3)}; }
|
||||
template<class T, int M> constexpr mat<T,1,M> transpose(const vec<T,M> & m) { return transpose(mat<T,M,1>(m)); }
|
||||
template<class T> constexpr mat<T,1,1> adjugate(const mat<T,1,1> & a [[maybe_unused]]) { return {vec<T,1>{1}}; }
|
||||
template<class T> constexpr mat<T,1,1> adjugate(const mat<T,1,1> &) { return {vec<T,1>{1}}; }
|
||||
template<class T> constexpr mat<T,2,2> adjugate(const mat<T,2,2> & a) { return {{a.y.y, -a.x.y}, {-a.y.x, a.x.x}}; }
|
||||
template<class T> constexpr mat<T,3,3> adjugate(const mat<T,3,3> & a);
|
||||
template<class T> constexpr mat<T,4,4> adjugate(const mat<T,4,4> & a);
|
||||
|
|
@ -580,6 +585,17 @@ namespace linalg
|
|||
template<class T> mat<T,4,4> frustum_matrix (T x0, T x1, T y0, T y1, T n, T f, fwd_axis a = neg_z, z_range z = neg_one_to_one);
|
||||
template<class T> mat<T,4,4> perspective_matrix(T fovy, T aspect, T n, T f, fwd_axis a = neg_z, z_range z = neg_one_to_one) { T y = n*std::tan(fovy / 2), x = y*aspect; return frustum_matrix(-x, x, -y, y, n, f, a, z); }
|
||||
|
||||
// Provide implicit conversion between linalg::vec<T,M> and std::array<T,M>
|
||||
template<class T> struct converter<vec<T,1>, std::array<T,1>> { vec<T,1> operator() (const std::array<T,1> & a) const { return {a[0]}; } };
|
||||
template<class T> struct converter<vec<T,2>, std::array<T,2>> { vec<T,2> operator() (const std::array<T,2> & a) const { return {a[0], a[1]}; } };
|
||||
template<class T> struct converter<vec<T,3>, std::array<T,3>> { vec<T,3> operator() (const std::array<T,3> & a) const { return {a[0], a[1], a[2]}; } };
|
||||
template<class T> struct converter<vec<T,4>, std::array<T,4>> { vec<T,4> operator() (const std::array<T,4> & a) const { return {a[0], a[1], a[2], a[3]}; } };
|
||||
|
||||
template<class T> struct converter<std::array<T,1>, vec<T,1>> { std::array<T,1> operator() (const vec<T,1> & a) const { return {a[0]}; } };
|
||||
template<class T> struct converter<std::array<T,2>, vec<T,2>> { std::array<T,2> operator() (const vec<T,2> & a) const { return {a[0], a[1]}; } };
|
||||
template<class T> struct converter<std::array<T,3>, vec<T,3>> { std::array<T,3> operator() (const vec<T,3> & a) const { return {a[0], a[1], a[2]}; } };
|
||||
template<class T> struct converter<std::array<T,4>, vec<T,4>> { std::array<T,4> operator() (const vec<T,4> & a) const { return {a[0], a[1], a[2], a[3]}; } };
|
||||
|
||||
// Provide typedefs for common element types and vector/matrix sizes
|
||||
namespace aliases
|
||||
{
|
||||
|
|
@ -608,6 +624,34 @@ namespace linalg
|
|||
typedef mat<bool,4,3> bool4x3; typedef mat<int,4,3> int4x3; typedef mat<float,4,3> float4x3; typedef mat<double,4,3> double4x3;
|
||||
typedef mat<bool,4,4> bool4x4; typedef mat<int,4,4> int4x4; typedef mat<float,4,4> float4x4; typedef mat<double,4,4> double4x4;
|
||||
}
|
||||
|
||||
// Provide output streaming operators, writing something that resembles an aggregate literal that could be used to construct the specified value
|
||||
namespace ostream_overloads
|
||||
{
|
||||
template<class C, class T> std::basic_ostream<C> & operator << (std::basic_ostream<C> & out, const vec<T,1> & v) { return out << '{' << v[0] << '}'; }
|
||||
template<class C, class T> std::basic_ostream<C> & operator << (std::basic_ostream<C> & out, const vec<T,2> & v) { return out << '{' << v[0] << ',' << v[1] << '}'; }
|
||||
template<class C, class T> std::basic_ostream<C> & operator << (std::basic_ostream<C> & out, const vec<T,3> & v) { return out << '{' << v[0] << ',' << v[1] << ',' << v[2] << '}'; }
|
||||
template<class C, class T> std::basic_ostream<C> & operator << (std::basic_ostream<C> & out, const vec<T,4> & v) { return out << '{' << v[0] << ',' << v[1] << ',' << v[2] << ',' << v[3] << '}'; }
|
||||
|
||||
template<class C, class T, int M> std::basic_ostream<C> & operator << (std::basic_ostream<C> & out, const mat<T,M,1> & m) { return out << '{' << m[0] << '}'; }
|
||||
template<class C, class T, int M> std::basic_ostream<C> & operator << (std::basic_ostream<C> & out, const mat<T,M,2> & m) { return out << '{' << m[0] << ',' << m[1] << '}'; }
|
||||
template<class C, class T, int M> std::basic_ostream<C> & operator << (std::basic_ostream<C> & out, const mat<T,M,3> & m) { return out << '{' << m[0] << ',' << m[1] << ',' << m[2] << '}'; }
|
||||
template<class C, class T, int M> std::basic_ostream<C> & operator << (std::basic_ostream<C> & out, const mat<T,M,4> & m) { return out << '{' << m[0] << ',' << m[1] << ',' << m[2] << ',' << m[3] << '}'; }
|
||||
}
|
||||
}
|
||||
|
||||
namespace std
|
||||
{
|
||||
// Provide specializations for std::hash<...> with linalg types
|
||||
template<class T> struct hash<linalg::vec<T,1>> { std::size_t operator()(const linalg::vec<T,1> & v) const { std::hash<T> h; return h(v.x); } };
|
||||
template<class T> struct hash<linalg::vec<T,2>> { std::size_t operator()(const linalg::vec<T,2> & v) const { std::hash<T> h; return h(v.x) ^ (h(v.y) << 1); } };
|
||||
template<class T> struct hash<linalg::vec<T,3>> { std::size_t operator()(const linalg::vec<T,3> & v) const { std::hash<T> h; return h(v.x) ^ (h(v.y) << 1) ^ (h(v.z) << 2); } };
|
||||
template<class T> struct hash<linalg::vec<T,4>> { std::size_t operator()(const linalg::vec<T,4> & v) const { std::hash<T> h; return h(v.x) ^ (h(v.y) << 1) ^ (h(v.z) << 2) ^ (h(v.w) << 3); } };
|
||||
|
||||
template<class T, int M> struct hash<linalg::mat<T,M,1>> { std::size_t operator()(const linalg::mat<T,M,1> & v) const { std::hash<linalg::vec<T,M>> h; return h(v.x); } };
|
||||
template<class T, int M> struct hash<linalg::mat<T,M,2>> { std::size_t operator()(const linalg::mat<T,M,2> & v) const { std::hash<linalg::vec<T,M>> h; return h(v.x) ^ (h(v.y) << M); } };
|
||||
template<class T, int M> struct hash<linalg::mat<T,M,3>> { std::size_t operator()(const linalg::mat<T,M,3> & v) const { std::hash<linalg::vec<T,M>> h; return h(v.x) ^ (h(v.y) << M) ^ (h(v.z) << (M*2)); } };
|
||||
template<class T, int M> struct hash<linalg::mat<T,M,4>> { std::size_t operator()(const linalg::mat<T,M,4> & v) const { std::hash<linalg::vec<T,M>> h; return h(v.x) ^ (h(v.y) << M) ^ (h(v.z) << (M*2)) ^ (h(v.w) << (M*3)); } };
|
||||
}
|
||||
|
||||
// Definitions of functions too long to be defined inline
|
||||
|
|
|
|||
|
|
@ -59,82 +59,56 @@ const getCompiler = async (): Promise<CompilerWithFlags> => {
|
|||
return cc;
|
||||
}
|
||||
|
||||
const wasmPlugin = ({ production, portable }: WasmLoaderConfig = {}): BunPlugin => {
|
||||
const p: BunPlugin = {
|
||||
name: "WASM loader",
|
||||
async setup(build) {
|
||||
build.onLoad({ filter: /\.(c(pp)?|wasm)$/ }, async (args) => {
|
||||
let wasmPath = path.resolve(import.meta.dir, '..', 'dist', 'tmp.wasm');
|
||||
let jsContent: string = `
|
||||
async function instantiate(url) {
|
||||
async function instantiate(url: string) {
|
||||
const memory = new WebAssembly.Memory({
|
||||
initial: 32,
|
||||
});
|
||||
let data = new DataView(memory.buffer);
|
||||
const decoder = new TextDecoder();
|
||||
const getString = (ptr) => {
|
||||
const view = new Uint8Array(memory.buffer, ptr);
|
||||
const start = ptr;
|
||||
const end = ptr + view.indexOf(0);
|
||||
|
||||
return decoder.decode(memory.buffer.slice(start, end));
|
||||
};
|
||||
let buf = '';
|
||||
Math.fmod = (x, y) => x % y;
|
||||
let errBuf = '';
|
||||
const { instance } = await WebAssembly.instantiateStreaming(fetch(url), {
|
||||
env: {
|
||||
memory,
|
||||
log(format, argsPtr) {
|
||||
let bufLen = buf.length;
|
||||
let count = 0;
|
||||
format = getString(format);
|
||||
let isFormat = false;
|
||||
let w = 4;
|
||||
const align = (a) => argsPtr += (argsPtr % a);
|
||||
for (const c of format) {
|
||||
if (!isFormat) {
|
||||
if (c === '%') {
|
||||
isFormat = true;
|
||||
} else if (c === '\\n') {
|
||||
console.log('[wasm]', buf);
|
||||
count += buf.length - bufLen + 1;
|
||||
bufLen = 0;
|
||||
buf = '';
|
||||
} else {
|
||||
buf += c;
|
||||
}
|
||||
} else switch(c) {
|
||||
case '%': buf += '%'; isFormat = false; break;
|
||||
case 's': align(4); buf += getString(data.getInt32(argsPtr, true)); argsPtr += 4; isFormat = false; break;
|
||||
case 'd': align(w); buf += data[w === 4 ? 'getInt32': 'getBigInt64'](argsPtr, true); argsPtr += w; isFormat = false; break;
|
||||
case 'x': align(w); buf += data[w === 4 ? 'getUint32': 'getBigUint64'](argsPtr, true).toString(16); argsPtr += w; isFormat = false; break;
|
||||
case 'o': align(w); buf += data[w === 4 ? 'getUint32': 'getBigUint64'](argsPtr, true).toString(8); argsPtr += w; isFormat = false; break;
|
||||
case 'u': align(w); buf += data[w === 4 ? 'getUint32': 'getBigUint64'](argsPtr, true); argsPtr += w; isFormat = false; break;
|
||||
case 'f': align(8); buf += data.getFloat64(argsPtr, true); argsPtr += 8; isFormat = false; break;
|
||||
case 'c': align(4); buf += String.fromCharCode(data.getInt32(argsPtr, true)); argsPtr += 4; isFormat = false; break;
|
||||
case 'l': w = 8; break;
|
||||
default: buf += '%' + c; isFormat = false; break;
|
||||
}
|
||||
}
|
||||
count += buf.length - bufLen;
|
||||
return count;
|
||||
},
|
||||
grow(blocks) {
|
||||
if (blocks > 0) {
|
||||
memory.grow(blocks);
|
||||
data = new DataView(memory.buffer);
|
||||
}
|
||||
},
|
||||
},
|
||||
Math: Math,
|
||||
env: { memory },
|
||||
wasi_snapshot_preview1: {
|
||||
random_get: (ptr, length) => {
|
||||
random_get: (ptr: number, length: number) => {
|
||||
for (let i = 0; i < length; i++) {
|
||||
data.setUint8(ptr + i, Math.random() * 256);
|
||||
}
|
||||
},
|
||||
environ_sizes_get(){ return 0; },
|
||||
environ_get() { return 0; },
|
||||
environ_sizes_get: (...args: any[]) => { console.debug(`[environ_sizes_get]`, args); return 0; },
|
||||
environ_get: (...args: any[]) => { console.debug(`[environ_get]`, args); return 0; },
|
||||
proc_exit: (...args: any[]) => { console.debug(`[proc_exit]`, args); return 0; },
|
||||
fd_close: (...args: any[]) => { console.debug(`[fd_close]`, args); return 0; },
|
||||
fd_seek: (...args: any[]) => { console.debug(`[fd_seek]`, args); return 0; },
|
||||
fd_write: (fd: number, iovsPtr: number, iovsLength: number, bytesWrittenPtr: number) => {
|
||||
const iovs = new Uint32Array(memory.buffer, iovsPtr, iovsLength * 2);
|
||||
if (fd === 1 || fd === 2) {
|
||||
let text = "";
|
||||
let totalBytesWritten = 0;
|
||||
for (let i = 0; i < iovsLength * 2; i += 2) {
|
||||
const offset = iovs[i];
|
||||
const length = iovs[i + 1];
|
||||
const textChunk = decoder.decode(new Int8Array(memory.buffer, offset, length));
|
||||
text += textChunk;
|
||||
totalBytesWritten += length;
|
||||
}
|
||||
data.setInt32(bytesWrittenPtr, totalBytesWritten, true);
|
||||
if (fd === 1) {
|
||||
const lines = (buf + text).split('\n');
|
||||
buf = lines.pop() ?? '';
|
||||
lines.forEach(l => console.log(`[wasm] ${l}`));
|
||||
} else {
|
||||
const lines = (errBuf + text).split('\n');
|
||||
errBuf = lines.pop() ?? '';
|
||||
lines.forEach(l => console.error(`[wasm] ${l}`));
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
},
|
||||
fd_read: (...args: any[]) => { console.debug(`[fd_read]`, args); return 0; },
|
||||
fd_fdstat_get: (fd: number, fdstatPtr: number) => { console.debug(`[fd_fdstat_get] fd=${fd}, ptr=${fdstatPtr}`); return 0; },
|
||||
fd_prestat_get: (...args: any[]) => { console.debug(`[fd_prestat_get]`, args); return 0; },
|
||||
fd_prestat_dir_name: (...args: any[]) => { console.debug(`[fd_prestat_dir_name]`, args); return 0; },
|
||||
}
|
||||
});
|
||||
|
||||
|
|
@ -144,8 +118,16 @@ const wasmPlugin = ({ production, portable }: WasmLoaderConfig = {}): BunPlugin
|
|||
get data() { return data; },
|
||||
};
|
||||
}
|
||||
|
||||
const wasmPlugin = ({ production }: WasmLoaderConfig = {}): BunPlugin => {
|
||||
const p: BunPlugin = {
|
||||
name: "WASM loader",
|
||||
async setup(build) {
|
||||
build.onLoad({ filter: /\.(c(pp)?|wasm)$/ }, async (args) => {
|
||||
let wasmPath = path.resolve(import.meta.dir, '..', 'dist', 'tmp.wasm');
|
||||
let jsContent: string = `
|
||||
${instantiate}
|
||||
const module = await instantiate(new URL($WASM$));
|
||||
if (typeof module.__srand === 'function') module.__srand(BigInt(Date.now()));
|
||||
|
||||
export default module;
|
||||
`;
|
||||
|
|
@ -183,9 +165,10 @@ const wasmPlugin = ({ production, portable }: WasmLoaderConfig = {}): BunPlugin
|
|||
'-Werror',
|
||||
'-Wshadow',
|
||||
...features,
|
||||
'-I', include,
|
||||
];
|
||||
const std = args.path.endsWith('.cpp') ? '-std=gnu++23' : '-std=gnu23';
|
||||
const compileResult = await $`${cc.cc} -c ${flags} ${std} -I ${include} -o ${objPath} ${args.path}`;
|
||||
const compileResult = await $`${cc.cc} -c ${flags} ${std} -o ${objPath} ${args.path}`;
|
||||
|
||||
if (compileResult.exitCode !== 0) {
|
||||
throw new Error('Compile failed, check output');
|
||||
|
|
@ -197,7 +180,7 @@ const wasmPlugin = ({ production, portable }: WasmLoaderConfig = {}): BunPlugin
|
|||
'--import-memory',
|
||||
].map(f => `-Wl,${f}`);
|
||||
|
||||
const linkResult = await $`${cc.cc} ${flags} -std=gnu23 -I ${include} ${linkFlags} -lstdc++ -nostartfiles -o ${wasmPath} ${objPath} ${stdlib}`;
|
||||
const linkResult = await $`${cc.cc} ${flags} -std=gnu23 ${linkFlags} -lstdc++ -nostartfiles -o ${wasmPath} ${objPath} ${stdlib}`;
|
||||
|
||||
if (linkResult.exitCode !== 0) {
|
||||
throw new Error('Link failed, check output');
|
||||
|
|
|
|||
|
|
@ -1,12 +1,12 @@
|
|||
#include <js.h>
|
||||
#include <linalg.hpp>
|
||||
#include <stdlib.h>
|
||||
|
||||
using namespace linalg::aliases;
|
||||
struct boob {
|
||||
int x;
|
||||
boob(int x_): x(x_) {};
|
||||
operator int() { return rand() + x; }
|
||||
} boob(rand());
|
||||
|
||||
EXPORT(awoo) auto awoo() {
|
||||
static constexpr double2 vel = {-1, -1};
|
||||
static constexpr double2 left = {1, 0};
|
||||
static constexpr double2 refl = linalg::reflect(vel, left);
|
||||
|
||||
return refl.x;
|
||||
EXPORT(awoo) auto awoo() -> int {
|
||||
return boob;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import awoo from "./awoo.cpp";
|
||||
|
||||
export default function main() {
|
||||
console.log(awoo, awoo.awoo(), awoo.blah());
|
||||
console.log(awoo, awoo.awoo());
|
||||
}
|
||||
Loading…
Reference in New Issue