PistonDevelopers / piston

A modular game engine written in Rust
https://www.piston.rs
MIT License
4.63k stars 235 forks source link

Feature request: port to web #1131

Closed lucklove closed 6 months ago

lucklove commented 7 years ago

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 commented 7 years ago

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

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

lucklove commented 7 years ago

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 commented 7 years ago

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

bvssvni commented 7 years ago

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 commented 7 years ago

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 commented 7 years ago

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 commented 7 years ago

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 commented 7 years ago

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

gifnksm commented 7 years ago

reddit post is here https://www.reddit.com/r/rust_gamedev/comments/5lplno/piston_project_that_can_be_compiled_into_both/

agausmann commented 5 years ago

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 commented 5 years ago

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 commented 5 years ago

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:

zicklag commented 4 years ago

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 commented 2 years ago

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 commented 6 months ago

I opened https://github.com/PistonDevelopers/piston/issues/1398 for web event loop.

bvssvni commented 6 months ago

Closing.