wasm: use shared, imported memory

This switches our wasm build to use "shared" memory. Shared memory can
be shared across multiple web workers, which is something we'll want to
support for our multi-threaded behaviors later.

Shared memory has a number of different restrictions so this updates
zig-js to support it as well as updates some of our functions that need
to be aware of it.
This commit is contained in:
Mitchell Hashimoto
2022-12-24 16:20:59 -08:00
parent 61450fce25
commit 241bfee7d4
5 changed files with 52 additions and 9 deletions

View File

@ -112,16 +112,44 @@ pub fn build(b: *std.build.Builder) !void {
// wasm
{
// Build our Wasm target.
const wasm_target: std.zig.CrossTarget = .{
.cpu_arch = .wasm32,
.os_tag = .freestanding,
.cpu_model = .{ .explicit = &std.Target.wasm.cpu.mvp },
.cpu_features_add = std.Target.wasm.featureSet(&.{
// We use this to explicitly request shared memory.
.atomics,
// Not explicitly used but compiler could use them if they want.
.bulk_memory,
.reference_types,
.sign_ext,
}),
};
// Whether we're using wasm shared memory. Some behaviors change.
// For now we require this but I wanted to make the code handle both
// up front.
const wasm_shared: bool = true;
exe_options.addOption(bool, "wasm_shared", wasm_shared);
const wasm = b.addSharedLibrary(
"ghostty-wasm",
"src/main_wasm.zig",
.{ .unversioned = {} },
);
wasm.setTarget(.{ .cpu_arch = .wasm32, .os_tag = .freestanding });
wasm.setTarget(wasm_target);
wasm.setBuildMode(mode);
wasm.setOutputDir("zig-out");
wasm.addOptions("build_options", exe_options);
// So that we can use web workers with our wasm binary
wasm.import_memory = true;
wasm.initial_memory = 65536 * 25;
wasm.max_memory = 65536 * 65536; // Maximum number of pages in wasm32
wasm.shared_memory = wasm_shared;
// Stack protector adds extern requirements that we don't satisfy.
wasm.stack_protector = false;
@ -136,7 +164,7 @@ pub fn build(b: *std.build.Builder) !void {
// it lets us test some basic functionality.
const test_step = b.step("test-wasm", "Run all tests for wasm");
const main_test = b.addTest("src/main_wasm.zig");
main_test.setTarget(.{ .cpu_arch = .wasm32, .os_tag = .wasi });
main_test.setTarget(wasm_target);
main_test.addOptions("build_options", exe_options);
try addDeps(b, main_test, true);
test_step.dependOn(&main_test.step);

View File

@ -4,9 +4,11 @@ const zjs = new ZigJS();
const importObject = {
module: {},
env: {
memory: new WebAssembly.Memory({ initial: 25, maximum: 65536, shared: true }),
log: (ptr: number, len: number) => {
const view = new DataView(zjs.memory.buffer, ptr, Number(len));
const str = new TextDecoder('utf-8').decode(view);
const arr = new Uint8ClampedArray(zjs.memory.buffer, ptr, len);
const data = arr.slice();
const str = new TextDecoder('utf-8').decode(data);
console.log(str);
},
},
@ -20,8 +22,8 @@ fetch(url.href).then(response =>
).then(bytes =>
WebAssembly.instantiate(bytes, importObject)
).then(results => {
const memory = importObject.env.memory;
const {
memory,
malloc,
free,
face_new,

View File

@ -423,9 +423,17 @@ pub const Wasm = struct {
defer mem_buf.deinit();
// Create an array that points to our buffer
const Uint8ClampedArray = try js.global.get(js.Object, "Uint8ClampedArray");
defer Uint8ClampedArray.deinit();
const arr = try Uint8ClampedArray.new(.{ mem_buf, buf.ptr, buf.len });
const arr = arr: {
const Uint8ClampedArray = try js.global.get(js.Object, "Uint8ClampedArray");
defer Uint8ClampedArray.deinit();
const arr = try Uint8ClampedArray.new(.{ mem_buf, buf.ptr, buf.len });
if (!wasm.shared_mem) break :arr arr;
// If we're sharing memory then we have to copy the data since
// we can't set ImageData directly using a SharedArrayBuffer.
defer arr.deinit();
break :arr try arr.call(js.Object, "slice", .{});
};
defer arr.deinit();
// Create the image data from our array

View File

@ -1,6 +1,7 @@
//! This file contains helpers for wasm compilation.
const std = @import("std");
const builtin = @import("builtin");
const options = @import("build_options");
comptime {
if (!builtin.target.isWasm()) {
@ -8,6 +9,10 @@ comptime {
}
}
/// True if we're in shared memory mode. If true, then the memory buffer
/// in JS will be backed by a SharedArrayBuffer and some behaviors change.
pub const shared_mem = options.wasm_shared;
/// The allocator to use in wasm environments.
///
/// The return values of this should NOT be sent to the host environment

2
vendor/zig-js vendored

@ -1 +1 @@
Subproject commit 5e3a5ce776f7b424022494a830f66ace224fe7ff
Subproject commit c89c1965cc6bf6ede97c1b891b624ce5282853d1