Skip to content
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

Feature request: port to web #1131

Closed
lucklove opened this issue Nov 24, 2016 · 16 comments
Closed

Feature request: port to web #1131

lucklove opened this issue Nov 24, 2016 · 16 comments
Assignees

Comments

@lucklove
Copy link

Since rust nightly has support asmjs and wasm, maybe It's possible that we can use piston to write web game in the soon future.

@bvssvni
Copy link
Member

bvssvni commented Nov 26, 2016

Does anyone have a testing environment set up? What are the requirements?

If needed, we could write a window backend for the "web".

@lucklove
Copy link
Author

lucklove commented Nov 26, 2016

To compile piston to web, we should have emscripten installed. And the work to port to emscripten is simple since emscripten reimplement sdl1 and support sdl2 natively. In fact, I'm already on the way, but I need some time to make clear how to deal with the main loop of sdl.
About how to compile rust to web, this will help.

@lucklove lucklove self-assigned this Nov 27, 2016
@lucklove
Copy link
Author

I made a demo to show how to make rust and sdl2 work on web, hope it can help.

@bvssvni
Copy link
Member

bvssvni commented Nov 30, 2016

I'm getting this error:

$ cargo build --target asmjs-unknown-emscripten
   Compiling web_demo v0.1.0 (file:///Users/sven/rust/web_demo)
error: linking with `emcc` failed: exit code: 1
  |
  = note: "emcc" "-L" "/Users/sven/.multirust/toolchains/nightly-x86_64-apple-darwin/lib/rustlib/asmjs-unknown-emscripten/lib" "/Users/sven/rust/web_demo/target/asmjs-unknown-emscripten/debug/deps/web_demo-ab888d76389d5a0c.0.o" "-o" "/Users/sven/rust/web_demo/target/asmjs-unknown-emscripten/debug/deps/web_demo-ab888d76389d5a0c.js" "-Wl,--gc-sections" "-nodefaultlibs" "-L" "/Users/sven/rust/web_demo/target/asmjs-unknown-emscripten/debug/deps" "-L" "/Users/sven/.multirust/toolchains/nightly-x86_64-apple-darwin/lib/rustlib/asmjs-unknown-emscripten/lib" "-Wl,-Bstatic" "-Wl,-Bdynamic" "/Users/sven/rust/web_demo/target/asmjs-unknown-emscripten/debug/deps/libsdl2-6d4b96e8b9dbc83c.rlib" "/Users/sven/rust/web_demo/target/asmjs-unknown-emscripten/debug/deps/libsdl2_sys-524b015e9982a43f.rlib" "/Users/sven/rust/web_demo/target/asmjs-unknown-emscripten/debug/deps/liblazy_static-5e13a0eed191ed30.rlib" "/Users/sven/rust/web_demo/target/asmjs-unknown-emscripten/debug/deps/librand-35167bebb3b301e7.rlib" "/Users/sven/rust/web_demo/target/asmjs-unknown-emscripten/debug/deps/libnum-351b1cddc85503c1.rlib" "/Users/sven/rust/web_demo/target/asmjs-unknown-emscripten/debug/deps/libnum_iter-618824a5957aaa8f.rlib" "/Users/sven/rust/web_demo/target/asmjs-unknown-emscripten/debug/deps/libnum_integer-9eb11829964f3c6e.rlib" "/Users/sven/rust/web_demo/target/asmjs-unknown-emscripten/debug/deps/libnum_traits-b3505e86c388a8aa.rlib" "/Users/sven/rust/web_demo/target/asmjs-unknown-emscripten/debug/deps/libbitflags-8f04bbe98b78ab83.rlib" "/Users/sven/rust/web_demo/target/asmjs-unknown-emscripten/debug/deps/libemscripten-4877834e79cf9ec4.rlib" "/Users/sven/rust/web_demo/target/asmjs-unknown-emscripten/debug/deps/libemscripten_sys-c4cd19d68d42016c.rlib" "/Users/sven/rust/web_demo/target/asmjs-unknown-emscripten/debug/deps/liblibc-38a11e2c014183dd.rlib" "/Users/sven/.multirust/toolchains/nightly-x86_64-apple-darwin/lib/rustlib/asmjs-unknown-emscripten/lib/libstd-17342542cc541012.rlib" "/Users/sven/.multirust/toolchains/nightly-x86_64-apple-darwin/lib/rustlib/asmjs-unknown-emscripten/lib/librand-46ed9b788a6928f6.rlib" "/Users/sven/.multirust/toolchains/nightly-x86_64-apple-darwin/lib/rustlib/asmjs-unknown-emscripten/lib/libcollections-e32369d7fef31fbf.rlib" "/Users/sven/.multirust/toolchains/nightly-x86_64-apple-darwin/lib/rustlib/asmjs-unknown-emscripten/lib/librustc_unicode-844a33a197b559a5.rlib" "/Users/sven/.multirust/toolchains/nightly-x86_64-apple-darwin/lib/rustlib/asmjs-unknown-emscripten/lib/libpanic_unwind-f78756b576499725.rlib" "/Users/sven/.multirust/toolchains/nightly-x86_64-apple-darwin/lib/rustlib/asmjs-unknown-emscripten/lib/libunwind-11f7709e0c71505b.rlib" "/Users/sven/.multirust/toolchains/nightly-x86_64-apple-darwin/lib/rustlib/asmjs-unknown-emscripten/lib/liballoc-24699c1ddb055eb0.rlib" "/Users/sven/.multirust/toolchains/nightly-x86_64-apple-darwin/lib/rustlib/asmjs-unknown-emscripten/lib/liballoc_system-3e467e865c8fa572.rlib" "/Users/sven/.multirust/toolchains/nightly-x86_64-apple-darwin/lib/rustlib/asmjs-unknown-emscripten/lib/liblibc-60365c932e50e382.rlib" "/Users/sven/.multirust/toolchains/nightly-x86_64-apple-darwin/lib/rustlib/asmjs-unknown-emscripten/lib/libcore-d9873b515905cac5.rlib" "/Users/sven/.multirust/toolchains/nightly-x86_64-apple-darwin/lib/rustlib/asmjs-unknown-emscripten/lib/libcompiler_builtins-e428224f6caf212a.rlib" "-l" "SDL2" "-l" "c" "-s" "USE_SDL=2" "-s" "ERROR_ON_UNDEFINED_SYMBOLS=1"
  = note: WARNING:root:LLVM version appears incorrect (seeing "7.3", expected "3.9")
CRITICAL:root:fastcomp in use, but LLVM has not been built with the JavaScript backend as a target, llc reports:
===========================================================================
(no targets could be identified: [Errno 2] No such file or directory)
===========================================================================
CRITICAL:root:you can fall back to the older (pre-fastcomp) compiler core, although that is not recommended, see http://kripken.github.io/emscripten-site/docs/building_from_source/LLVM-Backend.html
INFO:root:(Emscripten: Running sanity checks)
CRITICAL:root:Cannot find /usr/bin/llvm-link, check the paths in ~/.emscripten

Did you do something special to get LLVM 3.9?

@lucklove
Copy link
Author

lucklove commented Nov 30, 2016

Do you have llvm-link in your PATH? my llvm-link is at ${emsdk_portable}/clang/fastcomp/build_incoming_64//bin/llvm-link, and I have these lines to setup my PATH:

PATH+=:${emsdk_portable}
PATH+=:${emsdk_portable}/clang/fastcomp/build_incoming_64/bin
PATH+=:${emsdk_portable}/node/4.1.1_64bit/bin
PATH+=:${emsdk_portable}/emscripten/incoming

The ${emsdk_portable} is where your emsdk at.
I guess it used your native llvm(by mistake) instead of the one in ${emsdk_portable} directory.

@bvssvni
Copy link
Member

bvssvni commented Dec 1, 2016

It works (OSX)! I copied "emsdk_portable" to "/Applications" and made the following changes:

~/.bash_profile:

PATH="/Applications/emsdk_portable/emscripten/incoming":"$PATH"

~/.emscripten:

# this helps projects using emscripten find it
EMSCRIPTEN_ROOT = os.path.expanduser(os.getenv('EMSCRIPTEN') or '/Applications/emsdk_portable/emscripten/incoming') # directory
LLVM_ROOT = os.path.expanduser(os.getenv('LLVM') or '/Applications/emsdk_portable/clang/fastcomp/build_incoming_64/bin') # directory
BINARYEN_ROOT = os.path.expanduser(os.getenv('BINARYEN') or '/Applications/emsdk_portable/node/4.1.1_64bit/bin') # directory

@gifnksm
Copy link

gifnksm commented Jan 1, 2017

FYI: I've created a piston project that can be compiled into both native and web.
https://github.com/gifnksm/game-of-life-rs

This project uses piston2d-opengl_graphics with small patch and piston-shaders_graphics2d with WebGL shader support.

I initially tried using piston_window with SDL2 backend, but it didn't run because piston_window always sets WindowSettings::srgb is true (SDL2 seems not to work with SRGB).

@bvssvni
Copy link
Member

bvssvni commented Jan 1, 2017

@gifnksm You should post this to https://www.reddit.com/r/rust_gamedev/!

@gifnksm
Copy link

gifnksm commented Jan 3, 2017

@agausmann
Copy link

agausmann commented Aug 18, 2019

I'd like to do some research/testing to see if it is possible to implement window and input using web_sys or stdweb targeting wasm32-unknown-unknown. I've been struggling for over a year now trying to get things compiling/running with Emscripten/SDL2; I've had some successes and the cross-platform compatibility of SDL is nice, but it's way too much effort for me to maintain. A lot of the problems have been caused by compatibility issues with the Emscripten SDK, and I would love to be able to move away from it. It was great for initial support and testing of WebAssembly, but in my personal experience it is very difficult to set up the build environment correctly.

It appears that piston (core crates) and piston2d-opengl_graphics both compile successfully on the wasm32-unknown-unknown target, so that is a good start. AFAICT, the gl crate has WebGL bindings as well. I'll do some more research and see how far I can get.

@agausmann
Copy link

agausmann commented Aug 19, 2019

It's been less than a day, and I've already travelled deeper into the rabbit hole than I ever could have anticipated. There is so much more to it than writing a window library for stdweb; we will also need to do some work to support WebGL directly.

The issue that I ran into immediately is that gl doesn't actually support WebGL, at least not directly. WebGL isn't compatible with the way that OpenGL functions are typically loaded - using raw function pointers returned by the loader's getProcAddress function - as it is a JS API. To get around this, Emscripten defines their own C bindings that delegate to the WebGL API, so they can then return pointers to those functions in their own getProcAddress function. This is the compatibility layer that makes the gl crate work, which is built around this loader pattern.

These are the options I see moving forward:

  1. "Cherry-pick" the compatibility layer and omit the rest of the Emscripten SDK, allowing us to use the emscripten_GetProcAddress binding provided via emscripten-sys. This might work and would be the easiest solution in the short term, but I don't know if I like it.

  2. Write our own layer that essentially does the same thing as above. I'm not very familiar with bindgen-like tools, but it seems possible to write our own generator for webgl_generator to create extern "C" fns and a get_proc_address function.

  3. Write a graphics implementation based on WebGL bindings instead of OpenGL. This will likely take the most effort and will require a bit more work from the end user in the case that cross-compatibility is desired, but it could potentially be the cleanest solution with the fewest layers between piston2d and the calls to GL.

I think I'm going to focus my efforts around number 2 for now, but I do think number 3 could also be promising for the long run, if anybody would be interested in looking into it.


While I was writing this, I also found out that gfx-backend-gl (the HAL-based implementation) recently added support for WebGL using glow, which is a relatively new crate that provides a unified API for GL, GLES and WebGL. I'm not totally familiar with Piston's support for gfx, but I'm pretty sure it is for pre-LL so unfortunately it's not compatible. Maybe in the long term, it would make sense to port opengl_graphics to glow?

@agausmann
Copy link

agausmann commented Aug 28, 2019

I have an update! I spent the last week developing bindings for WebGL, and I think I have a solution that is at least presentable.

At first I tried to generate the bindings using a combination of gl_generator and webgl_generator - it sounded nice since a lot of the GL functions have very similar functions, but there are also several that are unique and require special cases. After a couple of days messing around with this idea, I settled on writing the bindings by hand using webgl-stdweb. It's only a few hundred functions and I got the easy ones done within a day, but there are some more complex ones that took quite a while longer.

I've uploaded it to a repository: webgl_loader so you can take a look at it, but I'm not confident in its correctness/soundness, and I still have to do a couple more passes to clean it up:

  • Moving uses of unsafe into "safe" abstractions, or at least as safe as I can make them in GL. There are some parts of the API that assume the user upholds the contract that might otherwise cause UB. For example, there are parts of the API where the user must provide a sufficiently-sized buffer where the size is well-defined by the OpenGL specifications and is not passed with the pointer itself.
    Even if that is technically still unsafe, I'd prefer to not litter the codebase with unsafe all the way up to the binding implementations themselves as that would make it much harder to audit. Rather, I'd assume that any contract with the user of the API is upheld and document those invariants where unsafe is used internally. That seems most readable to me, but I'm open to hearing other opinions.
    I do think there might be some benefit to keeping some implementation details unsafe - especially the user-provided buffers that I mentioned before. For those, the buffer size has to be specified per-binding; in those cases, unsafe might help us explicitly recognize, document (how it is calculated, why it is as such, and links to standard specifications) and verify them.

  • (Re)documenting the implementation details. There is a significant amount of code that works alongside the function delegations themselves; for example, managing a mapping between integers and JavaScript object references for handles to buffers/programs.

  • Cleaning up the implementations themselves. It's a bit scattered at the moment because I wanted to do a simple, big-picture implementation and I implemented features as-needed. Now that I see everything at the same time, I feel like I can easily go back and make it cleaner.

@zicklag
Copy link

zicklag commented May 17, 2020

I don't know what the status of this is now, but I saw mention of gl not having WebGL bindings and glow ( GL on Whatever ) could be really useful for that ( if I understand the problem correctly ).

@shinmili
Copy link

Another note about Window implementations. If piston is going to support web "natively", we need to overhaul the event loop API just like winit has done in 0.20. See rust-windowing/winit#459 for the detail of their work.

In short, we can't implement Window::poll_event or wait_event for web browsers, because we can't dequeue an event from a browser's event loop. It is meant to be run by the browser itself, not by our user code.

And in fact, the example above (https://github.com/gifnksm/game-of-life-rs) defines two different event_loop::run functions for native and web, wihch can be switched by cfg. The native one simply goes with a familiar dequeuing code while let Some(e) = events.next(&mut window) { ... }, but the web one doesn't use any parts of the piston's event loop system. Instead it tells a browser to run a event handler with help of empscripten_sys::emscripten_set_main_loop_arg. It would be really awkward to write these codes manually, so I hope it can be handled inside the piston library.

@bvssvni
Copy link
Member

bvssvni commented Apr 30, 2024

I opened #1398 for web event loop.

@bvssvni
Copy link
Member

bvssvni commented Apr 30, 2024

Closing.

@bvssvni bvssvni closed this as completed Apr 30, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

6 participants