diff --git a/build/assets/include/linalg.hpp b/build/assets/include/linalg.hpp index de26bd8..2a6e7ec 100644 --- a/build/assets/include/linalg.hpp +++ b/build/assets/include/linalg.hpp @@ -49,10 +49,13 @@ #ifndef LINALG_H #define LINALG_H -#include -#include -#include -#include +#include // For various unary math functions, such as std::sqrt +#include // To resolve std::abs ambiguity on clang +#include // For implementing namespace linalg::aliases +#include // For std::array +#include // For forward definitions of std::ostream +#include // For std::enable_if, std::is_same, std::declval +#include // For std::hash declaration namespace linalg { @@ -109,8 +112,8 @@ namespace linalg template struct make_seq_impl { using type=seq; }; template struct make_seq_impl { using type=seq; }; template using make_seq = typename make_seq_impl::type; - template vec constexpr swizzle(const vec & v, seq i [[maybe_unused]]) { return {getter{}(v)...}; } - template mat constexpr swizzle(const mat & m, seq i, seq j [[maybe_unused]]) { return {swizzle(getter{}(m),i)...}; } + template vec constexpr swizzle(const vec & v, seq) { return {getter{}(v)...}; } + template mat constexpr swizzle(const mat & m, seq i, seq) { return {swizzle(getter{}(m),i)...}; } // SFINAE helpers to determine result of function application template using ret_t = decltype(std::declval()(std::declval()...)); @@ -193,6 +196,7 @@ namespace linalg struct std_fmod { template auto operator() (A a, B b) const -> decltype(std::fmod (a, b)) { return std::fmod (a, b); } }; struct std_pow { template auto operator() (A a, B b) const -> decltype(std::pow (a, b)) { return std::pow (a, b); } }; struct std_atan2 { template auto operator() (A a, B b) const -> decltype(std::atan2 (a, b)) { return std::atan2 (a, b); } }; + struct std_copysign { template 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 does NOT have a constructor from pointer, this can conflict with initializing its single element from zero template constexpr explicit vec(const vec & v) : vec(static_cast(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> constexpr vec(const U & u) : vec(converter{}(u)) {} template> constexpr operator U () const { return converter{}(*this); } @@ -220,7 +224,7 @@ namespace linalg template constexpr explicit vec(const vec & v) : vec(static_cast(v.x), static_cast(v.y)) {} constexpr const T & operator[] (int i) const { return i==0?x:y; } - constexpr T & operator[] (int i) { return i==0?x:y; } + constexpr T & operator[] (int i) { return i==0?x:y; } template> constexpr vec(const U & u) : vec(converter{}(u)) {} template> constexpr operator U () const { return converter{}(*this); } @@ -238,7 +242,7 @@ namespace linalg template constexpr explicit vec(const vec & v) : vec(static_cast(v.x), static_cast(v.y), static_cast(v.z)) {} constexpr const T & operator[] (int i) const { return i==0?x:i==1?y:z; } - constexpr T & operator[] (int i) { return i==0?x:i==1?y:z; } + constexpr T & operator[] (int i) { return i==0?x:i==1?y:z; } constexpr const vec & xy() const { return *reinterpret_cast *>(this); } vec & xy() { return *reinterpret_cast *>(this); } @@ -260,7 +264,7 @@ namespace linalg template constexpr explicit vec(const vec & v) : vec(static_cast(v.x), static_cast(v.y), static_cast(v.z), static_cast(v.w)) {} constexpr const T & operator[] (int i) const { return i==0?x:i==1?y:i==2?z:w; } - constexpr T & operator[] (int i) { return i==0?x:i==1?y:i==2?z:w; } + constexpr T & operator[] (int i) { return i==0?x:i==1?y:i==2?z:w; } constexpr const vec & xy() const { return *reinterpret_cast *>(this); } constexpr const vec & xyz() const { return *reinterpret_cast *>(this); } vec & xy() { return *reinterpret_cast *>(this); } @@ -282,8 +286,8 @@ namespace linalg template constexpr explicit mat(const mat & m) : mat(V(m.x)) {} constexpr vec 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> constexpr mat(const U & u) : mat(converter{}(u)) {} template> constexpr operator U () const { return converter{}(*this); } @@ -465,6 +469,7 @@ namespace linalg template apply_t fmod (const A & a, const B & b) { return apply(detail::std_fmod{}, a, b); } template apply_t pow (const A & a, const B & b) { return apply(detail::std_pow{}, a, b); } template apply_t atan2 (const A & a, const B & b) { return apply(detail::std_atan2{}, a, b); } + template apply_t copysign(const A & a, const B & b) { return apply(detail::std_copysign{}, a, b); } // Component-wise relational functions on vectors template constexpr apply_t equal (const A & a, const B & b) { return apply(detail::op_eq{}, a, b); } @@ -546,7 +551,7 @@ namespace linalg template constexpr mat transpose(const mat & m) { return {m.row(0), m.row(1), m.row(2)}; } template constexpr mat transpose(const mat & m) { return {m.row(0), m.row(1), m.row(2), m.row(3)}; } template constexpr mat transpose(const vec & m) { return transpose(mat(m)); } - template constexpr mat adjugate(const mat & a [[maybe_unused]]) { return {vec{1}}; } + template constexpr mat adjugate(const mat &) { return {vec{1}}; } template constexpr mat adjugate(const mat & a) { return {{a.y.y, -a.x.y}, {-a.y.x, a.x.x}}; } template constexpr mat adjugate(const mat & a); template constexpr mat adjugate(const mat & a); @@ -580,6 +585,17 @@ namespace linalg template mat 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 mat 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 and std::array + template struct converter, std::array> { vec operator() (const std::array & a) const { return {a[0]}; } }; + template struct converter, std::array> { vec operator() (const std::array & a) const { return {a[0], a[1]}; } }; + template struct converter, std::array> { vec operator() (const std::array & a) const { return {a[0], a[1], a[2]}; } }; + template struct converter, std::array> { vec operator() (const std::array & a) const { return {a[0], a[1], a[2], a[3]}; } }; + + template struct converter, vec> { std::array operator() (const vec & a) const { return {a[0]}; } }; + template struct converter, vec> { std::array operator() (const vec & a) const { return {a[0], a[1]}; } }; + template struct converter, vec> { std::array operator() (const vec & a) const { return {a[0], a[1], a[2]}; } }; + template struct converter, vec> { std::array operator() (const vec & 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 bool4x3; typedef mat int4x3; typedef mat float4x3; typedef mat double4x3; typedef mat bool4x4; typedef mat int4x4; typedef mat float4x4; typedef mat 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 std::basic_ostream & operator << (std::basic_ostream & out, const vec & v) { return out << '{' << v[0] << '}'; } + template std::basic_ostream & operator << (std::basic_ostream & out, const vec & v) { return out << '{' << v[0] << ',' << v[1] << '}'; } + template std::basic_ostream & operator << (std::basic_ostream & out, const vec & v) { return out << '{' << v[0] << ',' << v[1] << ',' << v[2] << '}'; } + template std::basic_ostream & operator << (std::basic_ostream & out, const vec & v) { return out << '{' << v[0] << ',' << v[1] << ',' << v[2] << ',' << v[3] << '}'; } + + template std::basic_ostream & operator << (std::basic_ostream & out, const mat & m) { return out << '{' << m[0] << '}'; } + template std::basic_ostream & operator << (std::basic_ostream & out, const mat & m) { return out << '{' << m[0] << ',' << m[1] << '}'; } + template std::basic_ostream & operator << (std::basic_ostream & out, const mat & m) { return out << '{' << m[0] << ',' << m[1] << ',' << m[2] << '}'; } + template std::basic_ostream & operator << (std::basic_ostream & out, const mat & m) { return out << '{' << m[0] << ',' << m[1] << ',' << m[2] << ',' << m[3] << '}'; } + } +} + +namespace std +{ + // Provide specializations for std::hash<...> with linalg types + template struct hash> { std::size_t operator()(const linalg::vec & v) const { std::hash h; return h(v.x); } }; + template struct hash> { std::size_t operator()(const linalg::vec & v) const { std::hash h; return h(v.x) ^ (h(v.y) << 1); } }; + template struct hash> { std::size_t operator()(const linalg::vec & v) const { std::hash h; return h(v.x) ^ (h(v.y) << 1) ^ (h(v.z) << 2); } }; + template struct hash> { std::size_t operator()(const linalg::vec & v) const { std::hash h; return h(v.x) ^ (h(v.y) << 1) ^ (h(v.z) << 2) ^ (h(v.w) << 3); } }; + + template struct hash> { std::size_t operator()(const linalg::mat & v) const { std::hash> h; return h(v.x); } }; + template struct hash> { std::size_t operator()(const linalg::mat & v) const { std::hash> h; return h(v.x) ^ (h(v.y) << M); } }; + template struct hash> { std::size_t operator()(const linalg::mat & v) const { std::hash> h; return h(v.x) ^ (h(v.y) << M) ^ (h(v.z) << (M*2)); } }; + template struct hash> { std::size_t operator()(const linalg::mat & v) const { std::hash> 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 diff --git a/build/wasmPlugin.ts b/build/wasmPlugin.ts index e20572a..32316e4 100644 --- a/build/wasmPlugin.ts +++ b/build/wasmPlugin.ts @@ -59,93 +59,75 @@ const getCompiler = async (): Promise => { return cc; } -const wasmPlugin = ({ production, portable }: WasmLoaderConfig = {}): BunPlugin => { +async function instantiate(url: string) { + const memory = new WebAssembly.Memory({ + initial: 32, + }); + let data = new DataView(memory.buffer); + const decoder = new TextDecoder(); + let buf = ''; + let errBuf = ''; + const { instance } = await WebAssembly.instantiateStreaming(fetch(url), { + env: { memory }, + wasi_snapshot_preview1: { + random_get: (ptr: number, length: number) => { + for (let i = 0; i < length; i++) { + data.setUint8(ptr + i, Math.random() * 256); + } + }, + 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; }, + } + }); + + return { + ...instance.exports, + memory, + 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 = ` - async function instantiate(url) { - 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; - 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, - wasi_snapshot_preview1: { - random_get: (ptr, length) => { - for (let i=0; i < length; i++) { - data.setUint8(ptr + i, Math.random() * 256); - } - }, - environ_sizes_get(){ return 0; }, - environ_get() { return 0; }, - } - }); - - return { - ...instance.exports, - memory, - get data() { return data; }, - }; - } + ${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'); diff --git a/src/games/playground/awoo.cpp b/src/games/playground/awoo.cpp index 0c37c57..9b462e2 100644 --- a/src/games/playground/awoo.cpp +++ b/src/games/playground/awoo.cpp @@ -1,12 +1,12 @@ #include -#include +#include -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; } diff --git a/src/games/playground/index.tsx b/src/games/playground/index.tsx index 97b3885..b9cedd8 100644 --- a/src/games/playground/index.tsx +++ b/src/games/playground/index.tsx @@ -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()); } \ No newline at end of file