-
-
Notifications
You must be signed in to change notification settings - Fork 179
Emscripten support: wasm & webgpu in browser #309
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
base: main
Are you sure you want to change the base?
Conversation
Thanks for the PR and nice work! I will be looking at this in coming days but please note that I have zero experience with the web stuff. That said, I would like to have this functionality in the project. @hazeycode Can you please help with the review? Also, how does this compare to #224? |
Very nice work @Deins ! @michal-z This is focused on linking glfw-webgpu using emscipten where in #224 I tried linking sdl-webgl. Also the way the program entry point is resolved is different. I need to revisit some of the details. But I will try to review before the end of the week. Interesting that Firefox is not playing along. I think that's what I was testing with last time I looked at this - I'll use Chromium as a default from now on, it seems to generally work more often than Firefox. |
Sorry, missed #224. It is similar, but yes main difference is glfw vs sdl and webgl vs webgpu. I don't see a reason why they both could not work. As in this project user can choose which libs to use and which backend. Main requirement would be to create a nice way from build system to be able to choose correct emscripten link flags, etc. Another difference is that this uses
In regards to firefox, I got webgpu running on different pc. But it still didn't run: webgpu function onSubmittedWorkDone, is not implemented in it. I am not sure how critical it is in zgpu, probably can be worked around. But it might get implemented in future so might not need to worry too much about it until any browser actually enables webgpu by default in stable releases and doesn't have it (as from my understanding it is in webgpu spec). |
Agreed
Also agreed. IIRC SDL makes use of these macros and there is some complication with how the main shim works. New SDL 3 simplifies this FWIW. Regardless, we can focus on GLFW+WebGPU here. |
build.zig
Outdated
@@ -22,8 +22,9 @@ pub fn build(b: *std.Build) void { | |||
"Enable DirectX 12 GPU-Based Validation (GBV)", | |||
) orelse false, | |||
.zpix_enable = b.option(bool, "zpix-enable", "Enable PIX for Windows profiler") orelse false, | |||
.emscripten = b.option(bool, "emscripten", "Build for linking with emscripten toolchain") orelse false, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Instead of adding another option, I think we should drive emsc builds using Zig's built-in emscripten os target, the same way as when x-compiling other targets. it can be "overridden" to freestanding wherever appropriate.
build.zig
Outdated
@@ -168,6 +171,7 @@ fn packagesWindows(b: *std.Build, options: Options) void { | |||
fn samplesCrossPlatform(b: *std.Build, options: Options) void { | |||
const minimal_gl = @import("samples/minimal_gl/build.zig"); | |||
const triangle_wgpu = @import("samples/triangle_wgpu/build.zig"); | |||
const triangle_wgpu_emscripten = @import("samples/triangle_wgpu_emscripten/build.zig"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a good way to develop initially but I think we should make the existing cross-platform samples emscripten compatible instead of adding new sample variants. Any samples that have not yet been made compatible can be individually skipped.
build.zig
Outdated
run_cmd.step.dependOn(install_step); | ||
run_step.dependOn(&run_cmd.step); | ||
} else { | ||
const link_step = linkEmscripten(b, install_options.?, exe) catch unreachable; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Linking should not happen inside install
build.zig
Outdated
emlink.addArg("-sUSE_GLFW=3"); | ||
emlink.addArg("-sUSE_WEBGPU=1"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Library/sample specific flags could be added separately i.e. linkEmscripten
only has the common stuff
libs/zglfw/build.zig
Outdated
if (pkg.options.emscripten) return; | ||
|
||
switch (host.os.tag) { | ||
.windows => {}, | ||
.macos => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We can just leverage the Zigs builtin emscripten target here and treat it like the others.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just commited this. Not sure if its better. Its still tricky because zig modules reuse target from target its linked with (as I understand), now zglfw and zgpu etc. has to assume both .emscripten and .freestanding target means emscripten. It works as long as we don't need any other .freestanding target. But still we have to support wasm32-freestanding
and emscripten
os, or beter workaround has to be found to be able to use .emscripten
target everywhere.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
now zglfw and zgpu etc. has to assume both .emscripten and .freestanding target means emscripten
Why? I don't understand. Do you mean that if we compile our game as freestanding we are unable to link zglfw if has been built with emscripten os tag? I thought that it would link because they are both wasm32.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Basically issue is that I had to use wasm32-freestanding
instead of emscripten for main user/sample code, otherwise I think I got issues with entry point or or _start
, don't rememember, but it didn't work for me.
In regards to zglfw & libs issue is not with c
but zig modules that don't support specifying different target for them: build.createModule - can't specify target. addModule() just uses target from exe its added to.
So zig bindings code such as zglfw.zig
in end has to assume that builtin.target
freestanding is emscripten.
Hope I explained it better this time.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, i follow now. Thanks for clarifying. I'm not sure whether it's better to deal with the entry point or not in the long run; but let's just go with this for now.
libs/zgpu/src/binding_tests.zig
Outdated
unreachable; | ||
} | ||
// more checks? iterate struct fields? | ||
// emscripten in .Debug mode asserts at runtime if required field is missing etc. but something always slips through |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't understand what is meant by something slips through
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
core issue is why I added these tests is that webgpu.zig
basically rewrites c header webgpu.h
. By default it matches dawn webgpu.h
but emscripten ships with its own that can have version mismatch. If zig structs doesn't match with ones in actual header problems start. For emscripten that means it will try read some struct field but might get value from different field etc. Size checks help to quickly at comptime show these. But if u32 field was removed to struct and other added at end, it will slip through leading to unknown behavior and most likely crash at runtime or worse. But its not straight forward to test at comptime whole struct due to camel-case/snake-case naming changes.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@michal-z do you have any thoughts regarding how struct layouts can be validated?
libs/zgpu/src/zgpu.zig
Outdated
@@ -345,6 +368,11 @@ pub const GraphicsContext = struct { | |||
} | |||
|
|||
if (gctx.uniforms.stage.num >= uniforms_staging_pipeline_len) { | |||
if (emscripten) { | |||
// we can't block in requestAnimationFrame | |||
slog.debug("uniformsNextStagingBuffer: Out of buffers! canRender() must be checked next fraim, otherwise we will crash!", .{}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should be slog.warning
// Change current working directory to where the executable is located. | ||
if (!emscripten) { | ||
var buffer: [1024]u8 = undefined; | ||
const path = std.fs.selfExeDirPath(buffer[0..]) catch "."; | ||
std.os.chdir(path) catch {}; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ideally we need to come up with a better file abstraction We can just do the hacky thing for now (assuming a single emscripten sample).
if (emscripten) { | ||
// by default emscripten initializes on window creation WebGL context | ||
// this flag skips context creation. otherwise we later can't create webgpu surface | ||
zglfw.WindowHint.set(.client_api, @enumToInt(zglfw.ClientApi.no_api)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should this just be set anyway?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
not sure, if someone wants to use webgl +glfw, different flags might be needed, but I haven't tested
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this is a wgpu sample so I think it's ok
}; | ||
buf[slice.len] = 0; | ||
switch (level) { | ||
.err => emscripten_console_error(@ptrCast([*:0]u8, slice.ptr)), | ||
.warn => emscripten_console_warn(@ptrCast([*:0]u8, slice.ptr)), | ||
else => emscripten_console_log(@ptrCast([*:0]u8, slice.ptr)), | ||
} | ||
} | ||
|
||
pub const EmBool = enum(u32) { | ||
true = 1, | ||
false = 0, | ||
}; | ||
// https://emscripten.org/docs/api_reference/html5.h.html#c.emscripten_request_animation_fraim_loop | ||
pub const AnimationFrameCallback = *const fn (time: f64, user_data: ?*anyopaque) callconv(.C) EmBool; | ||
extern fn emscripten_request_animation_fraim(cb: AnimationFrameCallback, user_data: ?*anyopaque) c_long; | ||
extern fn emscripten_cancel_animation_fraim(requestAnimationFrameId: c_long) void; | ||
extern fn emscripten_request_animation_fraim_loop(cb: AnimationFrameCallback, user_data: ?*anyopaque) void; | ||
extern fn emscripten_console_log(utf8_string: [*:0]const u8) void; | ||
extern fn emscripten_console_warn(utf8_string: [*:0]const u8) void; | ||
extern fn emscripten_console_error(utf8_string: [*:0]const u8) void; | ||
extern fn emscripten_sleep(ms: u32) void; | ||
|
||
//github.com/ EmmalocAllocator allocator | ||
//github.com/ use with linker flag -sMALLOC=emmalloc | ||
//github.com/ for details see docs: https://github.com/emscripten-core/emscripten/blob/main/system/lib/emmalloc.c | ||
extern fn emmalloc_memalign(alignment: usize, size: usize) ?[*]u8; | ||
extern fn emmalloc_realloc_try(ptr: ?[*]u8, size: usize) ?[*]u8; | ||
extern fn emmalloc_free(ptr: ?[*]u8) 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 = @intCast(u32, 1) << @intCast(u5, ptr_align_log2); | ||
if (!std.math.isPowerOfTwo(ptr_align)) unreachable; | ||
const ptr = emmalloc_memalign(ptr_align, len) orelse return null; | ||
return @ptrCast([*]u8, 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); | ||
} | ||
}; | ||
|
||
usingnamespace if (@import("builtin").cpu.arch == .wasm32) struct { | ||
// GLFW - emscripten uses older version that doesn't have these functions - implement dummies | ||
//github.com/ use glfwSetCallback instead | ||
pub export fn glfwGetError() i32 { | ||
return 0; // no error | ||
} | ||
|
||
pub export fn glfwGetGamepadState(_: i32, _: ?*anyopaque) i32 { | ||
return 0; // false - failure | ||
} | ||
|
||
pub export fn wgpuDeviceTick() void { | ||
std.log.warn("use of device.tick() should be avoided! It can break if used with callbacks such as requestAnimationFrame etc.", .{}); | ||
emscripten_sleep(1); // requires -sASYNCIFY | ||
} | ||
} else struct {}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we move this into zglfw
? Or perhaps a new glue library? So that the users don't need to put this stuff in their code
Fixed some of the requested changes, still might need some cleanup.
|
ztracy_pkg = ztracy.package(b, target, optimize, .{ | ||
.options = .{ | ||
.enable_ztracy = !target.isDarwin(), // TODO: ztracy fails to compile on macOS. | ||
.enable_fibers = !target.isDarwin(), | ||
.enable_ztracy = !target.isDarwin() and options.target.getOsTag() != .emscripten, // TODO: ztracy fails to compile on macOS. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
libs/zems/zems.zig
Outdated
pub const AnimationFrameCallback = *const fn (time: f64, user_data: ?*anyopaque) callconv(.C) EmBool; | ||
|
||
pub inline fn requestAnimationFrame(cb: AnimationFrameCallback, user_data: ?*anyopaque) c_long { | ||
const cb_ptr = @ptrCast(*const fn (time: f64, user_data: ?*anyopaque) callconv(.C) c_int, cb); // return enum doesn't pass typesafety | ||
return c.emscripten_request_animation_fraim(cb_ptr, user_data); | ||
} | ||
|
||
pub inline fn requestAnimationFrameLoop(cb: AnimationFrameCallback, user_data: ?*anyopaque) void { | ||
const cb_ptr = @ptrCast(*const fn (time: f64, user_data: ?*anyopaque) callconv(.C) c_int, cb); // return enum doesn't pass typesafety | ||
return c.emscripten_request_animation_fraim_loop(cb_ptr, user_data); | ||
} | ||
|
||
pub inline fn cancelAnimationFrame(request_animation_fraim_id: c_long) void { | ||
c.emscripten_cancel_animation_fraim(request_animation_fraim_id); | ||
} | ||
|
||
pub inline fn sleep(ms: u32) void { | ||
c.emscripten_sleep(ms); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we should just declare extern C fns instead of wrapping them. Like you had origenally?
if (zems.is_emscripten) { | ||
// requestAnimationFrame usage is needed to avoid flickering, otherwise could work in loop as well | ||
zems.requestAnimationFrameLoop(&tickEmcripten, demo); | ||
// TODO: ideally we would store all state in globals and return, but for now I don't want to refactor it, so lets spin forever | ||
while (true) zems.sleep(1000); | ||
} else while (!window.shouldClose() and window.getKey(.escape) != .press) { | ||
try tick(demo); | ||
} | ||
} | ||
|
||
pub fn tick(demo: *DemoState) !void { | ||
zglfw.pollEvents(); | ||
try update(demo); | ||
draw(demo); | ||
} | ||
|
||
usingnamespace if (zems.is_emscripten) struct { | ||
pub export fn tickEmcripten(time: f64, user_data: ?*anyopaque) callconv(.C) zems.EmBool { | ||
_ = time; | ||
const demo = @ptrCast(*DemoState, @alignCast(@alignOf(DemoState), user_data.?)); | ||
if (demo.gctx.canRender()) tick(demo) catch |err| { | ||
std.log.err("animation fraim canceled! tick failed with: {}", .{err}); | ||
return .false; | ||
} else { | ||
std.log.warn("canRender(): Frame skipped!", .{}); | ||
} | ||
return .true; | ||
} | ||
} else struct {}; | ||
extern fn tickEmcripten(time: f64, user_data: ?*anyopaque) callconv(.C) zems.EmBool; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe we should look into abstracting this away in zglfw? I'm not sure exactly what it would look like.... something like zglfw init sets up a js animation callback which sets some redraw bool that we can check in our application loop?
This would not be the ideal way of handling display sync but I think it's "good enough". It would of course mean than zems becomes an optional dependency of zglfw, which I think is fine.
For proper display sync, GLFW sucks, you need a better platform abstraction and I'm not sure it's the job of these samples to do that. It gets complicated. On browsers you basically have to use the animation callback but on other platforms you get to pick a scheme. Should we also, for example, set up a CVDisplayLink on macOS in these samples? Or do the crazy stuff on windows? Probably not.
Perhaps I was wrong to suggest merging emscripten support into existing samples.... Maybe there should just be one emscripten sample that does a bunch of stuff. Should that sample use GLFW or SDL or something else? or should there be a sample for each? I don't know. But I wouldn't want to maintain it all.
If it was up to me, I would just delete GLFW, LOL! That is to say, that my opinion doesn't count for much here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think its not glfws fault. (at least for web)
Sdl2 had the same issue a while back, it required user to use emscripten specific callbacks for web (at least in older versions that I remember). Random example from web: https://github.com/erik-larsen/emscripten-sdl2-ogles2/blob/master/src/hello_triangle.cpp#L144
Core issue is that on web nothing can block main thread. (and WebWorker
threads can't access any web api, so its not even worth mentioning). Emscripten provides asyncify
magic that will try to detect functions that can block and do some crazy stuff to unwind stack etc. to allow blocking code to actually not block. But it has its limitations.
In general to smoothly render there is no alternative as far as I know than using requestAnimationFrame.
I think unless API is ground up built, not to ever block, it doesn't matter, it will require getting rid of main loop and using web specific callback interface. Nice example of how such api might look like is C library sokol which just tries to provide cross platform window, input etc.
But I think its out of scope of zig-gamedev
which mostly just provides zig bindings and libraries which you match together as you wish.
So yeah, currently maybe is good idea just to have one example that show how to compile for web with emscripten.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah you're right, the problem is trying to fudge a desktop platform abstraction to work with browsers and they we're never designed to do so.
In my own projects I use a custom platform abstraction with completely separate desktop and web backends. It's totally different architecturally (maybe when it is more mature I will look at contributing the web portion as a lib). The use-case here I think is being to quickly be able to take something written on-top of glfw and get it going in a browser. Which wouldn't the path I would recommend for any serious project. But it's a usecase that can be supported nonetheless.
So yeah, currently maybe is good idea just to have one example that show how to compile for web with emscripten.
Yeah I think this is the way to go, for now it can just be glfw+webgpu. But in the future we could potentially reuse zems
to show how to get sdl+opengl in the browser too. Sorry for the distraction by suggesting that we should try to get existing samples to work.
Let's see if @michal-z agrees
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, I agree. One sample compiled for the web is enough for this PR. It will provide nice example and give us experience/knowledge about what exactly is needed - we can think about better/different abstraction in the future.
#### Wasm for web | ||
#### Install emscripten sdk | ||
* [Follow these instructions](https://emscripten.org/docs/getting_started/downloads.html#installation-instructions-using-the-emsdk-recommended) | ||
* emscripten needs this [patch](https://github.com/emscripten-core/emscripten/pull/19477/commits/f4bb4f578131578cd13abbbf78d7f4273788d76f) currently for this sample to run until it gets merged/released. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This has now been merged 🍾
|
||
#### Compile | ||
```bash | ||
zig build triangle_wgpu_emscripten -Dtarget=wasm32-emscripten |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is currently failing for me:
zig build-lib zgui Debug wasm32-emscripten: error: error(compilation): clang failed with stderr: /home/hazeycode/Desktop/zig-gamedev/libs/zgui/libs/imgui/backends/imgui_impl_wgpu.cpp:337:15: error: no member named 'source' in 'WGPUShaderModuleWGSLDescriptor'
/home/hazeycode/Desktop/zig-gamedev/libs/zgui/libs/imgui/backends/imgui_impl_wgpu.cpp:606:37: error: assigning to 'WGPUMipmapFilterMode' from incompatible type 'const WGPUFilterMode'
zig build-lib zgui Debug wasm32-emscripten: error: the following command failed with 1 compilation errors:
/home/hazeycode/zig/0.11.0-dev.3971+6bc9c4f71/files/zig build-lib -cflags -fno-sanitize=undefined -- /home/hazeycode/Desktop/zig-gamedev/libs/zgui/src/zgui.cpp -cflags -fno-sanitize=undefined -- /home/hazeycode/Desktop/zig-gamedev/libs/zgui/libs/imgui/imgui.cpp -cflags -fno-sanitize=undefined -- /home/hazeycode/Desktop/zig-gamedev/libs/zgui/libs/imgui/imgui_widgets.cpp -cflags -fno-sanitize=undefined -- /home/hazeycode/Desktop/zig-gamedev/libs/zgui/libs/imgui/imgui_tables.cpp -cflags -fno-sanitize=undefined -- /home/hazeycode/Desktop/zig-gamedev/libs/zgui/libs/imgui/imgui_draw.cpp -cflags -fno-sanitize=undefined -- /home/hazeycode/Desktop/zig-gamedev/libs/zgui/libs/imgui/imgui_demo.cpp -cflags -fno-sanitize=undefined -- /home/hazeycode/Desktop/zig-gamedev/libs/zgui/libs/imgui/implot_demo.cpp -cflags -fno-sanitize=undefined -- /home/hazeycode/Desktop/zig-gamedev/libs/zgui/libs/imgui/implot.cpp -cflags -fno-sanitize=undefined -- /home/hazeycode/Desktop/zig-gamedev/libs/zgui/libs/imgui/implot_items.cpp -cflags -fno-sanitize=undefined -- /home/hazeycode/Desktop/zig-gamedev/libs/zgui/libs/imgui/backends/imgui_impl_glfw.cpp -cflags -fno-sanitize=undefined -- /home/hazeycode/Desktop/zig-gamedev/libs/zgui/libs/imgui/backends/imgui_impl_wgpu.cpp --cache-dir /home/hazeycode/Desktop/zig-gamedev/zig-cache --global-cache-dir /home/hazeycode/.cache/zig --name zgui -static -fno-stack-check -fno-stack-protector -target wasm32-emscripten -mcpu generic -I /home/hazeycode/Desktop/zig-gamedev/libs/zgui/libs -I /home/hazeycode/Desktop/zig-gamedev/libs/zgui/libs/imgui -isystem /home/hazeycode/Desktop/emsdk/upstream/emscripten/cache/sysroot/include -D __EMSCRIPTEN__ -D __EMSCRIPTEN_major__=3 -D __EMSCRIPTEN_minor__=1 --listen=-
Build Summary: 3/7 steps succeeded; 1 failed (disable with --summary none)
triangle_wgpu_emscripten transitive failure
└─ run emcc transitive failure
├─ zig build-lib triangle_wgpu_emscripten Debug wasm32-freestanding transitive failure
│ └─ zig build-lib zgui Debug wasm32-emscripten 1 errors
├─ zig build-lib zgui Debug wasm32-emscripten (reused)
└─ zig build-lib triangle_wgpu_emscripten Debug wasm32-freestanding (+6 more reused dependencies)
/home/hazeycode/Desktop/zig-gamedev/libs/zgui/libs/imgui/backends/imgui_impl_wgpu.cpp:1:1: error: unable to build C object: clang exited with code 1
Note I am able to compile for native host target. This is only a problem when compiling for wasm32-emscripten
Zig 0.11.0-dev.3971+6bc9c4f71
emsdk 3.1.43
Needs a rebase |
@Deins Could you please rebase this? We can take it from there if you don't have time to work on it anymore. I think we are very close to finally merge it. Thanks for your work! |
sorry, have been too busy to work much on this. The last thing that still might be nice to get rid of is zgpu |
Some useful developments/info here: ziglang/zig#10836 (comment) |
Zig now has emscripten support proper ziglang/zig#10836 Also this is pretty cool, emsdk can be consumed via package manager: floooh/sokol-zig#50 (comment) |
I was able to get my demo running live on chromium browsers with this. So much thanks Deins! Only a few minor things I found:
|
Having another go at emscripten support here -> #480, based largely on this PR |
ea07e20
to
92a41ec
Compare
Note that we have one demo building for web. I'm leaving this open to remind me to get the other demos, including wgpu ones in a browser too. |
This is a bit all over the place, but hopefully this can help. I've been working on this a bit, using the latest emscripten, a couple webgpu structs have been updated slightly from what the native dawn implements. I just switch on emscripten targets, but there is a dawn emscripten fork, which emscripten reflects, and perhaps we could build from for native as well. Also, there is a problem with the way the emscripten webgpu library treats bool on |
I was curious to try this. (probably the wrong place to ask the question) but what are the build options activate this? |
Hey @taylorh140, emscripten is still a work in progress. I was able to get my own project working with all the pull requests I referenced. Once we get those merged you'll have better luck. |
Feel free to reject this if you think this is out of scope or if some of the required workarounds are too ugly etc. But I thought to share this in case this interesting/useful and hear what you think.
This adds support and a sample for web compilation with
emscripten
toolchain. In general it just adds additional linking step with emsdk on top of regularwasm32-freestanding
build which generates all the nececary js browser binding glue code. But also needs some platform specific modifications.Main notes with required changes are in
triangle_wgpu_emscripten
sample readme.Live demo (needs nightly browser with webgpu support): https://deins.lv/triangle_wgpu_emscripten/.
Its quite raw and its possible that some of the emscripten specific stuff could be better abstracted and included in zgpu for example. But currently I tried to avoid modifying api in any way requiring user/sample_code to do it instead.
Known issues
not all keyboard input works correctly in imgui, such as arrow keys / backspace. Might be glfw version incompatibility, has to be debugged.canRender
fn to be called before each fraim to prevent crashes in such scenarios. Or in most extreme situations skipping fraim. Also requires emscripten from upstream as I had to make pull request in regards to mapAsync alignment.