Añadiendo mlx

This commit is contained in:
2025-11-14 15:10:19 +01:00
parent 06267858e8
commit 5c40a00540
61 changed files with 27644 additions and 1 deletions

151
lib/mlx/web/README.md Normal file
View File

@@ -0,0 +1,151 @@
# Web
MLX42 supports compilation towards [WASM](https://webassembly.org/). What this means is you can run any application written in C directly in the browser!
This overcomes a lot of issues with for instance showing projects towards others or have an environment where building natively just won't work.
In this README you will learn how to compile your project towards Webassembly and later deploy it on github!
## Pre-requisites
- [Emscripten](https://emscripten.org/), you can install this via brew or read the instructions they provide for [Windows or Linux](https://emscripten.org/docs/getting_started/downloads.html)
## Building
Once you made sure you have emscripten installed (check if `emcc` and `emcmake` work).
Run:
```bash
emcmake cmake -B build && cmake --build build --parallel
```
## Modifications
You're only required to do a few modifications to your `main.c`.
For this we will use the demo main provided in the root [readme](../README.md).
Add the following headers at the top:
```c
#include <emscripten/html5.h>
#include <emscripten/emscripten.h>
```
Modify your main:
```c
// Invoked instead of mlx_loop directly.
void emscripten_main_loop() {
mlx_loop(mlx);
}
int32_t main(int argc, char **argv)
{
// Gotta error check this stuff
if (!(mlx = mlx_init(WIDTH, HEIGHT, "MLX42", true)))
{
puts(mlx_strerror(mlx_errno));
return(EXIT_FAILURE);
}
if (!(image = mlx_new_image(mlx, 128, 128)))
{
mlx_close_window(mlx);
puts(mlx_strerror(mlx_errno));
return(EXIT_FAILURE);
}
if (mlx_image_to_window(mlx, image, 0, 0) == -1)
{
mlx_close_window(mlx);
puts(mlx_strerror(mlx_errno));
return(EXIT_FAILURE);
}
mlx_loop_hook(mlx, ft_randomize, mlx);
mlx_loop_hook(mlx, ft_hook, mlx);
// This function will set up the main loop
emscripten_set_main_loop(emscripten_main_loop, 0, true);
mlx_terminate(mlx);
return (EXIT_SUCCESS);
}
```
Thats actually it! It may or may not be necessary to modify your own source code depending on what you do but that's most often not the case.
It is that easy to just re-deploy your own app into webassembly.
## Building
```bash
# Compile C into JS/WASM
emcc -O3 -I include -I mlx -pthread main.c \
-o ./web/demo.js \
./build/libmlx42.a \
-s USE_GLFW=3 -s USE_WEBGL2=1 -s FULL_ES3=1 -s WASM=1 \
-s NO_EXIT_RUNTIME=1 -s EXPORTED_RUNTIME_METHODS='["ccall", "cwrap"]' \
-s ALLOW_MEMORY_GROWTH
# Navigate into the web folder (if you're running this directly from this repo)
cd web
# Launch local webserver, this is required to make the service worker function.
python3 -m http.server 8000
```
Once the server is up and running all you need to do now is go to [localhost](http://localhost:8000/index.html).
There you can locally develop your application without having to do any git commits or actions shenanigans.
# Deploying to Github Pages
For a free, quick and easy hosting solution you can realistically deploy this anywhere.
However for now we will only focus on putting this up via github pages.
What you need in your repository is a `.github/workflows/static.yml` file.
It can be named anything `static`, `ci`, whatever. Later on if you learn more about CI Pipelines you can use this to do a lot of useful things.
## Enabling github pages
Follow this step: https://docs.github.com/en/pages/getting-started-with-github-pages/configuring-a-publishing-source-for-your-github-pages-site#publishing-with-a-custom-github-actions-workflow
Once selected, you need to commit an actions file.
For now you can copy paste MLX42's `wasm.yml` file which functionally does the exact same.
```yml
# Simple workflow for deploying static content to GitHub Pages
name: Deploy static content to Pages
on:
push:
branches: ["master"] # Change to main or whatever fancy name
workflow_dispatch:
permissions:
contents: read
pages: write
id-token: write
# Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued.
# However, do NOT cancel in-progress runs as we want to allow these production deployments to complete.
concurrency:
group: "pages"
cancel-in-progress: false
# Single deploy job since we're just deploying
jobs:
deploy:
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
runs-on: ubuntu-latest
#TODO: add a build step to get the wasm file instead of commiting it.
#Doesn't really matter atm since the git history is polluted anyway
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Pages
uses: actions/configure-pages@v5
- name: Upload artifact
uses: actions/upload-pages-artifact@v3
with:
path: './web' # <= Set this variable to the directory relative to the root of the repo.
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v4
```
Once you commit this file github will do it's magic and create a deployment.
You should then get a link to where you can access you program. Now you can access your app anywhere!

View File

@@ -0,0 +1,146 @@
/*! coi-serviceworker v0.1.7 - Guido Zuidhof and contributors, licensed under MIT */
let coepCredentialless = false;
if (typeof window === 'undefined') {
self.addEventListener("install", () => self.skipWaiting());
self.addEventListener("activate", (event) => event.waitUntil(self.clients.claim()));
self.addEventListener("message", (ev) => {
if (!ev.data) {
return;
} else if (ev.data.type === "deregister") {
self.registration
.unregister()
.then(() => {
return self.clients.matchAll();
})
.then(clients => {
clients.forEach((client) => client.navigate(client.url));
});
} else if (ev.data.type === "coepCredentialless") {
coepCredentialless = ev.data.value;
}
});
self.addEventListener("fetch", function (event) {
const r = event.request;
if (r.cache === "only-if-cached" && r.mode !== "same-origin") {
return;
}
const request = (coepCredentialless && r.mode === "no-cors")
? new Request(r, {
credentials: "omit",
})
: r;
event.respondWith(
fetch(request)
.then((response) => {
if (response.status === 0) {
return response;
}
const newHeaders = new Headers(response.headers);
newHeaders.set("Cross-Origin-Embedder-Policy",
coepCredentialless ? "credentialless" : "require-corp"
);
if (!coepCredentialless) {
newHeaders.set("Cross-Origin-Resource-Policy", "cross-origin");
}
newHeaders.set("Cross-Origin-Opener-Policy", "same-origin");
return new Response(response.body, {
status: response.status,
statusText: response.statusText,
headers: newHeaders,
});
})
.catch((e) => console.error(e))
);
});
} else {
(() => {
const reloadedBySelf = window.sessionStorage.getItem("coiReloadedBySelf");
window.sessionStorage.removeItem("coiReloadedBySelf");
const coepDegrading = (reloadedBySelf == "coepdegrade");
// You can customize the behavior of this script through a global `coi` variable.
const coi = {
shouldRegister: () => !reloadedBySelf,
shouldDeregister: () => false,
coepCredentialless: () => true,
coepDegrade: () => true,
doReload: () => window.location.reload(),
quiet: false,
...window.coi
};
const n = navigator;
const controlling = n.serviceWorker && n.serviceWorker.controller;
// Record the failure if the page is served by serviceWorker.
if (controlling && !window.crossOriginIsolated) {
window.sessionStorage.setItem("coiCoepHasFailed", "true");
}
const coepHasFailed = window.sessionStorage.getItem("coiCoepHasFailed");
if (controlling) {
// Reload only on the first failure.
const reloadToDegrade = coi.coepDegrade() && !(
coepDegrading || window.crossOriginIsolated
);
n.serviceWorker.controller.postMessage({
type: "coepCredentialless",
value: (reloadToDegrade || coepHasFailed && coi.coepDegrade())
? false
: coi.coepCredentialless(),
});
if (reloadToDegrade) {
!coi.quiet && console.log("Reloading page to degrade COEP.");
window.sessionStorage.setItem("coiReloadedBySelf", "coepdegrade");
coi.doReload("coepdegrade");
}
if (coi.shouldDeregister()) {
n.serviceWorker.controller.postMessage({ type: "deregister" });
}
}
// If we're already coi: do nothing. Perhaps it's due to this script doing its job, or COOP/COEP are
// already set from the origin server. Also if the browser has no notion of crossOriginIsolated, just give up here.
if (window.crossOriginIsolated !== false || !coi.shouldRegister()) return;
if (!window.isSecureContext) {
!coi.quiet && console.log("COOP/COEP Service Worker not registered, a secure context is required.");
return;
}
// In some environments (e.g. Firefox private mode) this won't be available
if (!n.serviceWorker) {
!coi.quiet && console.error("COOP/COEP Service Worker not registered, perhaps due to private mode.");
return;
}
n.serviceWorker.register(window.document.currentScript.src).then(
(registration) => {
!coi.quiet && console.log("COOP/COEP Service Worker registered", registration.scope);
registration.addEventListener("updatefound", () => {
!coi.quiet && console.log("Reloading page to make use of updated COOP/COEP Service Worker.");
window.sessionStorage.setItem("coiReloadedBySelf", "updatefound");
coi.doReload();
});
// If the registration is active, but it's not controlling the page
if (registration.active && !n.serviceWorker.controller) {
!coi.quiet && console.log("Reloading page to make use of COOP/COEP Service Worker.");
window.sessionStorage.setItem("coiReloadedBySelf", "notcontrolling");
coi.doReload();
}
},
(err) => {
!coi.quiet && console.error("COOP/COEP Service Worker failed to register:", err);
}
);
})();
}

1
lib/mlx/web/demo.js Normal file

File diff suppressed because one or more lines are too long

BIN
lib/mlx/web/demo.wasm Executable file

Binary file not shown.

147
lib/mlx/web/index.html Normal file
View File

@@ -0,0 +1,147 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>WebAssembly Test</title>
<style>
body {
margin: auto;
padding-top: 1rem;
width: 100%;
min-height: 100dvh;
background-color: #121212;
color: #ffffff;
font-family: 'Arial', sans-serif;
max-width: 65rem;
overflow: hidden;
}
header {
text-align: center;
margin-bottom: 40px;
}
h1 {
margin: 0;
font-size: 2.5em;
text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.5);
}
a {
color: #1e90ff;
text-decoration: none;
font-size: 1.1em;
transition: color 0.3s ease;
}
a:hover {
color: #00bfff;
}
.canvas-container {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
position: relative;
}
canvas {
border-radius: 15px;
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.7);
border: 2px solid #1e90ff;
}
.loading {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
font-size: 1.5em;
color: #1e90ff;
font-weight: bold;
display: none;
}
.description {
padding: 1rem;
margin-bottom: 1rem;
background: rgba(255, 255, 255, 0.1);
border-radius: 10px;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.5);
text-align: justify;
}
.description p {
line-height: 1.6;
margin: 1em 0;
}
.instructions {
text-align: center;
margin: 20px;
}
.instructions p {
font-size: 1.2em;
}
kbd {
background-color: #333;
border-radius: 3px;
border: 1px solid #666;
box-shadow: 0 1px 0 #666, 0 1px 0 1px rgba(255, 255, 255, 0.1);
color: #fff;
display: inline-block;
font-size: 1em;
line-height: 1.4;
padding: 2px 6px;
margin: 0 2px;
white-space: nowrap;
}
</style>
</head>
<body>
<header>
<h1>WebAssembly Test</h1>
<a href="https://github.com/codam-coding-college/MLX42" target="_blank">Visit MLX42 GitHub Repo</a>
</header>
<section class="description">
<p>
This little demo demonstrates how you compile MLX42 using emscripten to leverage the power of WebAssembly
and run any graphical project directly in the web!
</p>
</section>
<div class="canvas-container">
<span class="loading">Loading...</span>
<canvas width="1024" height="1024" id="canvas"></canvas>
</div>
<div class="instructions">
<p>Use <kbd></kbd> <kbd></kbd> <kbd></kbd> <kbd></kbd> to move the element in the canvas.</p>
</div>
<script src="coi-serviceworker.js"></script>
<script>
var Module = {
// This is called when the Emscripten module is ready
onRuntimeInitialized: () => {
console.log("Emscripten module initialized");
var loadingText = document.querySelector('.loading');
loadingText.style.display = 'none';
},
canvas: (() => {
var canvas = document.getElementById('canvas');
canvas.addEventListener("webglcontextlost", function (e) {
alert('WebGL context lost. Reload the page.');
e.preventDefault();
}, false);
return canvas;
})()
};
</script>
<script src="demo.js"></script>
</body>
</html>