Skip to content

Update EMSDK and add Emmaloc #1

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 9 commits into from
Feb 3, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,24 @@ Add zemscripten's "root" module to your wasm compile target., then create an `em
b.getInstallStep().dependOn(emcc_step);
```

To use a custom html file emccStep() accepts a shell_file_path option:
```zig
const emcc_step = @import("zemscripten").emccStep(
b,
wasm,
.{
.optimize = optimize,
.flags = emcc_flags,
.settings = emcc_settings,
.use_preload_plugins = true,
.embed_paths = &.{},
.preload_paths = &.{},
.install_dir = .{ .custom = "web" },
.shell_file_path = "path/to/file"
},
);
```

Now you can use the provided Zig panic and log overrides in your wasm's root module and define the entry point that invoked by the js output of `emcc` (by default it looks for a symbol named `main`). For example:
```zig
const std = @import("std");
Expand Down Expand Up @@ -85,3 +103,4 @@ You can also define a run step that invokes `emrun`. This will serve the html lo
b.step("emrun", "Build and open the web app locally using emrun").dependOn(emrun_step);
```
See the [emrun documentation](https://emscripten.org/docs/compiling/Running-html-files-with-emrun.html) for the difference args that can be used.

20 changes: 16 additions & 4 deletions build.zig
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ const std = @import("std");

pub const emsdk_ver_major = "3";
pub const emsdk_ver_minor = "1";
pub const emsdk_ver_tiny = "52";
pub const emsdk_ver_tiny = "73";
pub const emsdk_version = emsdk_ver_major ++ "." ++ emsdk_ver_minor ++ "." ++ emsdk_ver_tiny;

pub fn build(b: *std.Build) void {
Expand Down Expand Up @@ -32,6 +32,13 @@ pub fn emrunPath(b: *std.Build) []const u8 {
}) catch unreachable;
}

pub fn htmlPath(b: *std.Build) []const u8 {
return std.fs.path.join(b.allocator, &.{
b.dependency("emsdk", .{}).path("").getPath(b),
"upstream/emscripten/src/shell.html",
}) catch unreachable;
}

pub fn activateEmsdkStep(b: *std.Build) *std.Build.Step {
const emsdk_script_path = std.fs.path.join(b.allocator, &.{
b.dependency("emsdk", .{}).path("").getPath(b),
Expand Down Expand Up @@ -77,9 +84,14 @@ pub const EmccFlags = std.StringHashMap(void);

pub fn emccDefaultFlags(allocator: std.mem.Allocator, optimize: std.builtin.OptimizeMode) EmccFlags {
var args = EmccFlags.init(allocator);
if (optimize == .Debug) {
args.put("-Og", {}) catch unreachable;
args.put("-gsource-map", {}) catch unreachable;
switch (optimize) {
.Debug => {
args.put("-gsource-map", {}) catch unreachable;
},
.ReleaseSmall, .ReleaseFast => {
args.put("-O3", {}) catch unreachable;
},
else => {},
}
return args;
}
Expand Down
122 changes: 118 additions & 4 deletions src/zemscripten.zig
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,7 @@ comptime {
_ = std.testing.refAllDeclsRecursive(@This());
}

extern fn emscripten_err([*c]const u8) void;
extern fn emscripten_console_error([*c]const u8) void;
extern fn emscripten_console_warn([*c]const u8) void;
extern fn emscripten_console_log([*c]const u8) void;
pub extern fn emscripten_sleep(ms: u32) void;

pub const MainLoopCallback = *const fn () callconv(.C) void;
extern fn emscripten_set_main_loop(MainLoopCallback, c_int, c_int) void;
Expand All @@ -21,6 +18,123 @@ pub const AnimationFrameCallback = *const fn (f64, ?*anyopaque) callconv(.C) c_i
extern fn emscripten_request_animation_frame_loop(AnimationFrameCallback, ?*anyopaque) void;
pub const requestAnimationFrameLoop = emscripten_request_animation_frame_loop;

pub const EmscriptenResult = enum(i16) {
success = 0,
deferred = 1,
not_supported = -1,
failed_not_deferred = -2,
invalid_target = -3,
unknown_target = -4,
invalid_param = -5,
failed = -6,
no_data = -7,
timed_out = -8,
};
pub const CanvasSizeChangedCallback = *const fn (
i16,
*anyopaque,
?*anyopaque,
) callconv(.C) c_int;
pub fn setResizeCallback(
cb: CanvasSizeChangedCallback,
use_capture: bool,
user_data: ?*anyopaque,
) EmscriptenResult {
const result = emscripten_set_resize_callback_on_thread(
"2",
user_data,
@intFromBool(use_capture),
cb,
2,
);
return @enumFromInt(result);
}
extern fn emscripten_set_resize_callback_on_thread(
[*:0]const u8,
?*anyopaque,
c_int,
CanvasSizeChangedCallback,
c_int,
) c_int;

pub fn getElementCssSize(
target_id: [:0]const u8,
width: *f64,
height: *f64,
) EmscriptenResult {
return @enumFromInt(emscripten_get_element_css_size(
target_id,
width,
height,
));
}
extern fn emscripten_get_element_css_size([*:0]const u8, *f64, *f64) c_int;

// EmmalocAllocator allocator
// use with linker flag -sMALLOC=emmalloc
// for details see docs: https://github.com/emscripten-core/emscripten/blob/main/system/lib/emmalloc.c
extern fn emmalloc_memalign(u32, u32) ?*anyopaque;
extern fn emmalloc_realloc_try(?*anyopaque, u32) ?*anyopaque;
extern fn emmalloc_free(?*anyopaque) void;
pub const EmmalocAllocator = struct {
const Self = @This();
dummy: u32 = undefined,

pub fn allocator(self: *Self) std.mem.Allocator {
return .{
.ptr = self,
.vtable = &.{
.alloc = &alloc,
.resize = &resize,
.free = &free,
},
};
}

fn alloc(
ctx: *anyopaque,
len: usize,
ptr_align_log2: u8,
return_address: usize,
) ?[*]u8 {
_ = ctx;
_ = return_address;
const ptr_align: u32 = @as(u32, 1) << @as(u5, @intCast(ptr_align_log2));
if (!std.math.isPowerOfTwo(ptr_align)) unreachable;
const ptr = emmalloc_memalign(ptr_align, len) orelse return null;
return @ptrCast(ptr);
}

fn resize(
ctx: *anyopaque,
buf: []u8,
buf_align_log2: u8,
new_len: usize,
return_address: usize,
) bool {
_ = ctx;
_ = return_address;
_ = buf_align_log2;
return emmalloc_realloc_try(buf.ptr, new_len) != null;
}

fn free(
ctx: *anyopaque,
buf: []u8,
buf_align_log2: u8,
return_address: usize,
) void {
_ = ctx;
_ = buf_align_log2;
_ = return_address;
return emmalloc_free(buf.ptr);
}
};

extern fn emscripten_err([*c]const u8) void;
extern fn emscripten_console_error([*c]const u8) void;
extern fn emscripten_console_warn([*c]const u8) void;
extern fn emscripten_console_log([*c]const u8) void;
/// std.panic impl
pub fn panic(msg: []const u8, error_return_trace: ?*std.builtin.StackTrace, ret_addr: ?usize) noreturn {
_ = error_return_trace;
Expand Down
Loading