Compare commits
5 Commits
7b7b59d66b
...
wip-em
| Author | SHA1 | Date | |
|---|---|---|---|
| 39a5fdc85b | |||
| d7a4b915cf | |||
| 2adf072337 | |||
| ae49550874 | |||
| 709a2ff162 |
112
posts/cpp-without-emscripten.md
Normal file
112
posts/cpp-without-emscripten.md
Normal file
@@ -0,0 +1,112 @@
|
||||
# C++ without emscripten
|
||||
|
||||
If you've ever worked on a C++/C project and wanted to distribute it for browsers, you have probably hear of emscripten. emscripten allows you to compile your existing C++/C codebase without (if you're lucky) any changes and run it in a browser.
|
||||
This is made possible by the use of WebAssembly (which I will call wasm from now on) combined with a javascript interface to browser functionality.
|
||||
|
||||
For my current project I have been doing some research into this process and doing the same but without emscripten. In theory you don't need emscripten as the entire compilation step to wasm is already handled by LLVM with it's wasm backend. emscripten is also a pretty large codebase and I wanted to cut down on dependencies for this project.
|
||||
|
||||
All the source code for this guide will be available [here](https://git.bakje.coffee/guus/cjs-lite)
|
||||
|
||||
## Disclaimer
|
||||
|
||||
If you have a large complicated project with an existing codebase you might want to stick to just using emscripten as it provides a lot of the standard library and APIs such as for WebGL, file access, http, etc. For my own project I have full control over the entire codebase which includes not using any of the C++ standard library, no C++ exceptions and no graphics APIs since I'm working with a pure software renderer.
|
||||
|
||||
The ideas mentioned here don't attempt to fully replace emscripten, rather just provide insights into what it does and how you can possibly live without it for smaller projects.
|
||||
|
||||
## Compilation step
|
||||
|
||||
The first step trying to compile for the browser was to setup clang and the llvm linker to spit out a wasm file. From here on I will be using CMake to setup the toolchain, however you can adapt this to whatever build system.
|
||||
|
||||
I will use the llvm installation that is available on my system. Most linux distributions will have one available or preinstalled. The tools I'm using in this guide are `clang`, `clang++`, `llvm-mc` and `wasm-ld`. `llvm-mc` is the llvm machine code compiler which is used to compile a small snippet for the threading code later on.
|
||||
These should all come with a recent llvm installation. Recent releases can also be found on the llvm-project github page [here](https://github.com/llvm/llvm-project/releases)
|
||||
|
||||
To easily switch to compilation for web I created a small CMake toolchain file that can be referenced during the configuration step using `-DCMAKE_TOOLCHAIN_FILE=Web.cmake`.
|
||||
At this point it looks something like [this](TODO) and can be used to compile a simple test file.
|
||||
|
||||
Certain builtins and attributes are available to interface specifically with the javascript plaform. I already required one such attribute to export a function so it is visible in the compiled wasm module like this:
|
||||
|
||||
```cpp
|
||||
__attribute__((export_name("hello")))
|
||||
int hello() {
|
||||
return 4;
|
||||
}
|
||||
```
|
||||
|
||||
In addition you will need to pass some flags to the linker to describe the memory that your wasm module requires. This can be done like this in CMake:
|
||||
```
|
||||
target_link_options(app PRIVATE
|
||||
-Wl,--no-entry
|
||||
-Wl,--no-growable-memory
|
||||
-Wl,--initial-memory=536870912
|
||||
)
|
||||
```
|
||||
Which will set the initial memory to 512MiB, disable automatic resizing of memory (explained later) and disable the default entry point.
|
||||
Since CMake will use `clang` as the linker executable, we need to use -Wl to pass these to the linker.
|
||||
|
||||
When building this, the output (`app.wasm`) will be inside the build folder. We can inspect this using the WebAssembly Binary Toolkit (wabt), which can be obtained [here](https://github.com/WebAssembly/wabt#). There are releases available for most platforms, they might be available from your package manager or can be built from source.
|
||||
|
||||
Running wasm2wat -- which will convert any wasm binary or object file generated by llvm into a textural representation -- we get something like the following:
|
||||
|
||||
```wat
|
||||
(module $app.wasm
|
||||
(type (;0;) (func (result i32)))
|
||||
(import "env" "memory" (memory (;0;) 8192 8192))
|
||||
(func $hello (type 0) (result i32)
|
||||
i32.const 4
|
||||
return)
|
||||
(table (;0;) 1 1 funcref)
|
||||
(global $__stack_pointer (mut i32) (i32.const 65536))
|
||||
(export "hello" (func $hello))
|
||||
(@custom "name" "\00\09\08app.wasm\01\08\01\00\05hello\07\12\01\00\0f__stack_pointer")
|
||||
(@custom "producers" "\01\0cprocessed-by\01\05clang\0d22.1.7+libcxx")
|
||||
(@custom "target_features" "\0a+\07atomics+\0bbulk-memory+\0fbulk-memory-opt+\16call-indirect-overlong+\0amultivalue+\0fmutable-globals+\13nontrapping-fptoint+\0freference-types+\08sign-ext+\07simd128"))
|
||||
```
|
||||
|
||||
> Note that you might have to enable certian features or run with `wasm2wat --enable-all app.wasm` to enable all the extensions that might also be passed to the compiler.
|
||||
|
||||
Here we can see the module exports "hello" (our function) alongside "memory" which is the memory that is available to the program. We will need the memory later to pass any data which we can not encode into function parameters (which can only be integers)
|
||||
|
||||
The memory size is specified in the binary since we specified it during link time:\
|
||||
```(import "env" "memory" (memory (;0;) 8192 8192))```\
|
||||
This specifies both the minimum and maximum memory size in 64KiB pages, 8192 * 64 * 1024 = 512MiB
|
||||
|
||||
> Most often you will want to set the memory to a fixed size, as it can [currently](https://github.com/WebAssembly/design/issues/1397#issuecomment-1247972859) **never** shrink and only grow when enabled (remove the `--no-growable-memory` linker flag). However for performance, simplicity and to limit browser memory usage I decided to constrain my application design within this fixed memory space.
|
||||
|
||||
The wasm2wat output also shows the `__stack_pointer` global which is used for the program's stack memory. This means that it shares the same memory and lives at the start of your memory block at address `65536` and grows towards `0`
|
||||
|
||||
## Running from JavaScript
|
||||
|
||||
To run the wasm application a little wrapper is provided [here](https://git.bakje.coffee/guus/cjs-lite/TODO) as a little main.js + index.html.
|
||||
|
||||
> As a bonus this also includes a server.js which hosts the files and sends a reload request to the browser whenever any source file changes, which will then reload the page automatically :)\
|
||||
This server refers to `build/app.wasm` by relative path, so make sure your build ends up there.
|
||||
|
||||
Besides being convenient, the server is also required to set the following HTTP headers.
|
||||
These are required later to get access to shared memory when adding web workers:
|
||||
```
|
||||
Cross-Origin-Opener-Policy: 'same-origin'
|
||||
Cross-Origin-Embedder-Policy: 'require-corp'
|
||||
```
|
||||
|
||||
The main part of the browser code (main.js) is the following which fetches and instantiates the module:
|
||||
```js
|
||||
const response = await fetch('./bin/app.wasm');
|
||||
const wasmBytes = await response.arrayBuffer();
|
||||
const wasmCompiled = await WebAssembly.compile(wasmBytes);
|
||||
const wasmModule = await WebAssembly.instantiate(wasmCompiled, { });
|
||||
|
||||
var result = wasmModule.exports.hello();
|
||||
log("Finished", result);
|
||||
```
|
||||
|
||||
We can see the output `Finished 4` if we check the browser console (Ctrl+Shift+i or F12). The 4 came from the `return 4` inside the C function.
|
||||
|
||||
## Exposing functions
|
||||
|
||||
## C Standard library
|
||||
|
||||
## compiler-rt
|
||||
|
||||
## C++ Standard library
|
||||
|
||||
## Shared memory and Threading
|
||||
@@ -1,7 +1,29 @@
|
||||
<footer style="margin-top: 50px; padding-top: 20px; border-top: 1px solid #ddd; color: #666; text-align: center;">
|
||||
<div class="social-links">
|
||||
<a href="https://bsky.app/profile/guusww.bsky.social" class="social-link" target="_blank" aria-label="Follow on Bluesky">
|
||||
<svg viewBox="0 0 568 501" width="24" height="24" aria-hidden="true">
|
||||
<path d="M123.121 33.664C188.241 82.553 258.281 181.68 284 234.873c25.719-53.192 95.759-152.32 160.879-201.21C491.866-1.611 568-28.906 568 57.947c0 17.346-9.945 145.713-15.778 166.555-20.275 72.453-94.155 90.933-159.875 79.748C507.222 323.8 536.444 388.56 473.333 453.32c-119.86 122.992-172.272-30.859-185.702-70.281-2.462-7.227-3.614-10.608-3.631-7.733-.017-2.875-1.169.506-3.631 7.733-13.43 39.422-65.842 193.273-185.702 70.281-63.111-64.76-33.89-129.52 80.986-149.071-65.72 11.185-139.6-7.295-159.875-79.748C9.945 203.659 0 75.291 0 57.946 0-28.906 76.135-1.612 123.121 33.664Z"/>
|
||||
</svg>
|
||||
</a>
|
||||
<a href="https://x.com/WaalsGuus" class="social-link" target="_blank" aria-label="Follow on Twitter">
|
||||
<svg viewBox="0 0 24 24" width="24" height="24" aria-hidden="true">
|
||||
<path d="M18.244 2.25h3.308l-7.227 8.26 8.502 11.24H16.17l-5.214-6.817L4.99 21.75H1.68l7.73-8.835L1.254 2.25H8.08l4.713 6.231zm-1.161 17.52h1.833L7.084 4.126H5.117z"/>
|
||||
</svg>
|
||||
</a>
|
||||
<a href="https://www.linkedin.com/in/guus-waals-4a7640110/" class="social-link" target="_blank" aria-label="Connect on LinkedIn">
|
||||
<svg viewBox="0 0 24 24" width="24" height="24" aria-hidden="true">
|
||||
<path d="M20.5 2h-17A1.5 1.5 0 002 3.5v17A1.5 1.5 0 003.5 22h17a1.5 1.5 0 001.5-1.5v-17A1.5 1.5 0 0020.5 2zM8 19H5v-9h3zM6.5 8.25A1.75 1.75 0 118.3 6.5a1.78 1.78 0 01-1.8 1.75zM19 19h-3v-4.74c0-1.42-.6-1.93-1.38-1.93A1.74 1.74 0 0013 14.19a.66.66 0 000 .14V19h-3v-9h2.9v1.3a3.11 3.11 0 012.7-1.4c1.55 0 3.36.86 3.36 3.66z"/>
|
||||
</svg>
|
||||
</a>
|
||||
<a href="https://github.com/guusw" class="social-link" target="_blank" aria-label="Follow on GitHub">
|
||||
<svg viewBox="0 0 16 16" width="24" height="24" aria-hidden="true">
|
||||
<path d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.013 8.013 0 0016 8c0-4.42-3.58-8-8-8z"/>
|
||||
</svg>
|
||||
</a>
|
||||
</div>
|
||||
<p>
|
||||
Guus Waals - updated on $<updated/> </br>
|
||||
$<version/>
|
||||
<a href="https://git.bakje.coffee/guus/blog">$<version/></a>
|
||||
</p>
|
||||
</footer>
|
||||
</body>
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
padding-bottom: 10px;
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
nav span {
|
||||
nav #title {
|
||||
font-size: 1.2em;
|
||||
font-weight: bold;
|
||||
margin-right: 20px;
|
||||
@@ -75,35 +75,27 @@
|
||||
padding: 0;
|
||||
}
|
||||
.social-link {
|
||||
float: right;
|
||||
display: inline-block;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
vertical-align: middle;
|
||||
margin-left: 0px;
|
||||
margin: 0 8px;
|
||||
}
|
||||
.social-link svg {
|
||||
fill: #333;
|
||||
fill: #666;
|
||||
transition: fill 0.2s;
|
||||
}
|
||||
.social-link:hover svg {
|
||||
fill: #0066cc;
|
||||
}
|
||||
footer .social-links {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<nav>
|
||||
<span>Guus' things</span>
|
||||
<a href="/">Home</a>
|
||||
<a href="/" id="title">Guus' things</a>
|
||||
<a href="/all">All Posts</a>
|
||||
<a href="https://bsky.app/profile/guusww.bsky.social" class="social-link" target="_blank" aria-label="Follow on Bluesky">
|
||||
<svg viewBox="0 0 568 501" width="24" height="24" aria-hidden="true">
|
||||
<path d="M123.121 33.664C188.241 82.553 258.281 181.68 284 234.873c25.719-53.192 95.759-152.32 160.879-201.21C491.866-1.611 568-28.906 568 57.947c0 17.346-9.945 145.713-15.778 166.555-20.275 72.453-94.155 90.933-159.875 79.748C507.222 323.8 536.444 388.56 473.333 453.32c-119.86 122.992-172.272-30.859-185.702-70.281-2.462-7.227-3.614-10.608-3.631-7.733-.017-2.875-1.169.506-3.631 7.733-13.43 39.422-65.842 193.273-185.702 70.281-63.111-64.76-33.89-129.52 80.986-149.071-65.72 11.185-139.6-7.295-159.875-79.748C9.945 203.659 0 75.291 0 57.946 0-28.906 76.135-1.612 123.121 33.664Z"/>
|
||||
</svg>
|
||||
</a>
|
||||
<a href="https://github.com/guusw" class="social-link" target="_blank" aria-label="View source on GitHub">
|
||||
<svg viewBox="0 0 16 16" width="24" height="24" aria-hidden="true">
|
||||
<path d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.013 8.013 0 0016 8c0-4.42-3.58-8-8-8z"/>
|
||||
</svg>
|
||||
</a>
|
||||
<a href="/work">Work</a>
|
||||
</nav>
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
$<include src="header.html"/>
|
||||
<p> A blog about game development and whatever else I'm up to :).</br> If you're looking to work together check out <a href="/work">this page</a>
|
||||
<h1>Recent Posts</h1>
|
||||
$<post-list limit="10"/>
|
||||
$<include src="footer.html"/>
|
||||
|
||||
64
templates/page_work.html
Normal file
64
templates/page_work.html
Normal file
@@ -0,0 +1,64 @@
|
||||
$<include src="header.html"/>
|
||||
<script>document.title = "Guus - Let's work together!";</script>
|
||||
<h1>Work</h1>
|
||||
<p>For more than 10 years I have worked on games, game engines and everything else around the development process.</p>
|
||||
|
||||
<h2>Games</h2>
|
||||
<p>Since I started my game dev career I have worked a lot with both Unity and Unreal.</p>
|
||||
<p>For Unity I have worked on 2 released commercial games, one being a live-service mobile arcade game, the other a console+PC racing game.</p>
|
||||
<p>For Unreal I have worked on 3 released commercial games, all of them being console+PC racing titles.</p>
|
||||
|
||||
<h2>Game Engines</h2>
|
||||
<p>
|
||||
I have worked on the Stride Game Engine (formerly Xenko) - a C# game engine - for a year.
|
||||
</p>
|
||||
<p>
|
||||
During my game development with Unreal Engine, I have written plenty of developer tools, UIs and other extensions to the engine, have gotten familiar with its build system and with debugging various console issues.
|
||||
</p>
|
||||
<p> I have worked on the Formabble game engine, which takes a collaborative editing and user generated content approach to game engines. As part of a 2-person developer team I worked on a lot of the engine, graphics and scripting systems.
|
||||
</p>
|
||||
|
||||
<h2>Others</h2>
|
||||
<p>
|
||||
I have worked with various CI Systems (Jenkins/GitHub actions) and build systems such as Unity's, Unreal's, CMake and .NET.
|
||||
</p>
|
||||
<p>
|
||||
I have worked with various Platforms and SDKs, some of which are:
|
||||
</p>
|
||||
<ul>
|
||||
<li>Android (Google Play, IAPs)</li>
|
||||
<li>iOS (GameCenter)</li>
|
||||
<li>PS5 (Unreal)</li>
|
||||
<li>PS4 (Unity/Unreal)</li>
|
||||
<li>Xbox One (Unity/Unreal)</li>
|
||||
<li>Nintendo Switch (Unity/Unreal)</li>
|
||||
<li>Wii U (low level, game from scratch)</li>
|
||||
</ul>
|
||||
<p>
|
||||
For my personal projects I work a lot with Linux systems, either as a desktop system or for projects requiring a server backend. I run my own website, email and git servers and am comfortable setting up systems on Linux-based servers.
|
||||
</p>
|
||||
|
||||
<h2>Contact</h2>
|
||||
<p>
|
||||
If you have any project that could use my skills, I'd be happy to talk to you.
|
||||
<br/>Contact me at: <span id="email-hidden" style="background: #000; color: #fff; cursor: pointer; padding: 2px 8px; user-select: none;">click to reveal</span>
|
||||
<script>
|
||||
var revealed = false;
|
||||
var span = document.getElementById('email-hidden');
|
||||
span.addEventListener('click', function() {
|
||||
if (!revealed) {
|
||||
var decoded = "thhf@onxwr.pbssrr".replace(/[a-zA-Z]/g, function(c) {
|
||||
return String.fromCharCode((c <= 'Z' ? 90 : 122) >= (c = c.charCodeAt(0) + 13) ? c : c - 26);
|
||||
});
|
||||
var link = document.createElement('a');
|
||||
link.href = 'mailto:' + decoded;
|
||||
link.textContent = decoded;
|
||||
link.style.cssText = 'cursor: pointer; user-select: text;';
|
||||
span.parentNode.replaceChild(link, span);
|
||||
revealed = true;
|
||||
}
|
||||
});
|
||||
</script>
|
||||
</p>
|
||||
|
||||
$<include src="footer.html"/>
|
||||
Reference in New Issue
Block a user