-
Notifications
You must be signed in to change notification settings - Fork 30
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
Implement async using workers #46
Open
AntonPieper
wants to merge
7
commits into
tsoding:main
Choose a base branch
from
AntonPieper:workers
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
7 commits
Select commit
Hold shift + click to select a range
379ff66
Implement async using workers
AntonPieper 26ad19d
Add additional cross origin isolation message
AntonPieper 480b691
Add COI hack for GitHub pages
AntonPieper cf05fa8
Add `SharedData` to share data more easily
AntonPieper 312195e
Remove `EventWorker`
AntonPieper c43c4ff
Format glfw key mapping
AntonPieper f4f7967
Simplify by inlining functions
AntonPieper File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,95 @@ | ||
/** | ||
* @typedef {keyof typeof SchemaType} SchemaKey | ||
* @typedef {(typeof SchemaType)[SchemaKey]} SchemaType | ||
*/ | ||
export const SchemaType = { | ||
u8: Uint8Array, | ||
i32: Int32Array, | ||
u32: Uint32Array, | ||
f32: Float32Array, | ||
f64: Float64Array, | ||
}; | ||
|
||
/** | ||
* @typedef {Record<string, { type: SchemaKey, count: number}} Schema | ||
*//** | ||
* @typedef {{schema: T, buffer: SharedArrayBuffer}} SharedBufferData | ||
* @template {Schema} T | ||
*/ | ||
|
||
/** | ||
* Schema builder. | ||
*/ | ||
export class DataSchema { | ||
/** @param {Schema} schema */ | ||
static byteSize(schema) { | ||
return Object.values(schema).reduce((acc, el) => { | ||
const bytes = SchemaType[el.type].BYTES_PER_ELEMENT; | ||
const mod = acc % bytes; | ||
if (mod !== 0) { | ||
acc += bytes - mod; | ||
} | ||
acc += bytes * el.count; | ||
return acc; | ||
}, 0); | ||
} | ||
|
||
/** | ||
* @returns {SharedBufferData} The data to be sahred across workers. | ||
*/ | ||
build() { | ||
return { | ||
schema: this.schema, | ||
buffer: new SharedArrayBuffer(this.byteSize()), | ||
}; | ||
} | ||
|
||
/** | ||
* Build a schema. | ||
* @param {T} schema | ||
* @template {Record<string, [SchemaKey, number] | SchemaKey>} T | ||
* @returns {{[P in keyof T]: T[P] extends string ? {type: T[P], count: 1} : {type: T[P][0], count: T[P][1]}} | ||
*/ | ||
static buildSchema(schema) { | ||
const result = {}; | ||
for (const key in schema) { | ||
if (typeof schema[key] === "string") { | ||
result[key] = { type: schema[key], count: 1 }; | ||
} else { | ||
result[key] = { type: schema[key][0], count: schema[key][1] }; | ||
} | ||
} | ||
return result; | ||
} | ||
/** | ||
* @param {T} schema | ||
* @template {Parameters<typeof DataSchema.buildSchema>[0]} T | ||
* @returns {SharedBufferData<ReturnType<typeof DataSchema.buildSchema<T>>} | ||
*/ | ||
static build(schema) { | ||
const s = this.buildSchema(schema); | ||
return { buffer: new SharedArrayBuffer(this.byteSize(s)), schema: s }; | ||
} | ||
|
||
/** | ||
* Build views around a schema. | ||
* @param {{schema: T, buffer: SharedArrayBuffer}} data | ||
* @template {Schema} T | ||
* @returns {{[P in keyof T]: InstanceType<(typeof SchemaType)[T[P]["type"]]>}} | ||
*/ | ||
static view(data) { | ||
let offset = 0; | ||
const views = {}; | ||
for (const [name, el] of Object.entries(data.schema)) { | ||
const constructor = SchemaType[el.type]; | ||
const bytes = constructor.BYTES_PER_ELEMENT; | ||
const mod = offset % bytes; | ||
if (mod !== 0) { | ||
offset += bytes - mod; | ||
} | ||
views[name] = new constructor(data.buffer, offset, el.count); | ||
offset += views[name].byteLength; | ||
} | ||
return views; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,75 @@ | ||
// NOTE: This file creates a service worker that cross-origin-isolates the page (read more here: https://web.dev/coop-coep/) which allows us to use wasm threads. | ||
// Normally you would set the COOP and COEP headers on the server to do this, but Github Pages doesn't allow this, so this is a hack to do that. | ||
|
||
/* Edited version of: coi-serviceworker v0.1.6 - Guido Zuidhof, licensed under MIT */ | ||
// From here: https://github.com/gzuidhof/coi-serviceworker | ||
if(typeof window === 'undefined') { | ||
self.addEventListener("install", () => self.skipWaiting()); | ||
self.addEventListener("activate", e => e.waitUntil(self.clients.claim())); | ||
|
||
async function handleFetch(request) { | ||
if(request.cache === "only-if-cached" && request.mode !== "same-origin") { | ||
return; | ||
} | ||
|
||
if(request.mode === "no-cors") { // We need to set `credentials` to "omit" for no-cors requests, per this comment: https://bugs.chromium.org/p/chromium/issues/detail?id=1309901#c7 | ||
request = new Request(request.url, { | ||
cache: request.cache, | ||
credentials: "omit", | ||
headers: request.headers, | ||
integrity: request.integrity, | ||
destination: request.destination, | ||
keepalive: request.keepalive, | ||
method: request.method, | ||
mode: request.mode, | ||
redirect: request.redirect, | ||
referrer: request.referrer, | ||
referrerPolicy: request.referrerPolicy, | ||
signal: request.signal, | ||
}); | ||
} | ||
|
||
let r = await fetch(request).catch(e => console.error(e)); | ||
|
||
if(r.status === 0) { | ||
return r; | ||
} | ||
|
||
const headers = new Headers(r.headers); | ||
headers.set("Cross-Origin-Embedder-Policy", "credentialless"); // or: require-corp | ||
headers.set("Cross-Origin-Opener-Policy", "same-origin"); | ||
|
||
return new Response(r.body, { status: r.status, statusText: r.statusText, headers }); | ||
} | ||
|
||
self.addEventListener("fetch", function(e) { | ||
e.respondWith(handleFetch(e.request)); // respondWith must be executed synchonously (but can be passed a Promise) | ||
}); | ||
|
||
} else { | ||
(async function() { | ||
if(window.crossOriginIsolated !== false) return; | ||
|
||
let registration = await navigator.serviceWorker.register(window.document.currentScript.src).catch(e => console.error("COOP/COEP Service Worker failed to register:", e)); | ||
if(registration) { | ||
console.log("COOP/COEP Service Worker registered", registration.scope); | ||
|
||
registration.addEventListener("updatefound", () => { | ||
console.log("Reloading page to make use of updated COOP/COEP Service Worker."); | ||
window.location.reload(); | ||
}); | ||
|
||
// If the registration is active, but it's not controlling the page | ||
if(registration.active && !navigator.serviceWorker.controller) { | ||
console.log("Reloading page to make use of COOP/COEP Service Worker."); | ||
window.location.reload(); | ||
} | ||
} | ||
})(); | ||
} | ||
|
||
// Code to deregister: | ||
// let registrations = await navigator.serviceWorker.getRegistrations(); | ||
// for(let registration of registrations) { | ||
// await registration.unregister(); | ||
// } |
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Instead of modifying all the examples, I instead opted for backwards compatibility and added another example that does not use the |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,104 @@ | ||
/******************************************************************************************* | ||
* | ||
* raylib [shapes] example - Colors palette | ||
* | ||
* Example originally created with raylib 1.0, last time updated with raylib 2.5 | ||
* | ||
* Example licensed under an unmodified zlib/libpng license, which is an OSI-certified, | ||
* BSD-like license that allows static linking with closed source software | ||
* | ||
* Copyright (c) 2014-2024 Ramon Santamaria (@raysan5) | ||
* | ||
********************************************************************************************/ | ||
|
||
#include "raylib.h" | ||
|
||
#define MAX_COLORS_COUNT 21 // Number of colors available | ||
|
||
//------------------------------------------------------------------------------------ | ||
// Program main entry point | ||
//------------------------------------------------------------------------------------ | ||
int main(void) | ||
{ | ||
// Initialization | ||
//-------------------------------------------------------------------------------------- | ||
const int screenWidth = 800; | ||
const int screenHeight = 450; | ||
|
||
InitWindow(screenWidth, screenHeight, "raylib [shapes] example - colors palette"); | ||
|
||
Color colors[MAX_COLORS_COUNT] = { | ||
DARKGRAY, MAROON, ORANGE, DARKGREEN, DARKBLUE, DARKPURPLE, DARKBROWN, | ||
GRAY, RED, GOLD, LIME, BLUE, VIOLET, BROWN, LIGHTGRAY, PINK, YELLOW, | ||
GREEN, SKYBLUE, PURPLE, BEIGE }; | ||
|
||
const char *colorNames[MAX_COLORS_COUNT] = { | ||
"DARKGRAY", "MAROON", "ORANGE", "DARKGREEN", "DARKBLUE", "DARKPURPLE", | ||
"DARKBROWN", "GRAY", "RED", "GOLD", "LIME", "BLUE", "VIOLET", "BROWN", | ||
"LIGHTGRAY", "PINK", "YELLOW", "GREEN", "SKYBLUE", "PURPLE", "BEIGE" }; | ||
|
||
Rectangle colorsRecs[MAX_COLORS_COUNT] = { 0 }; // Rectangles array | ||
|
||
// Fills colorsRecs data (for every rectangle) | ||
for (int i = 0; i < MAX_COLORS_COUNT; i++) | ||
{ | ||
colorsRecs[i].x = 20.0f + 100.0f *(i%7) + 10.0f *(i%7); | ||
colorsRecs[i].y = 80.0f + 100.0f *(i/7) + 10.0f *(i/7); | ||
colorsRecs[i].width = 100.0f; | ||
colorsRecs[i].height = 100.0f; | ||
} | ||
|
||
int colorState[MAX_COLORS_COUNT] = { 0 }; // Color state: 0-DEFAULT, 1-MOUSE_HOVER | ||
|
||
Vector2 mousePoint = { 0.0f, 0.0f }; | ||
|
||
SetTargetFPS(60); // Set our game to run at 60 frames-per-second | ||
//-------------------------------------------------------------------------------------- | ||
|
||
// Main game loop | ||
while (!WindowShouldClose()) // Detect window close button or ESC key | ||
{ | ||
// Update | ||
//---------------------------------------------------------------------------------- | ||
mousePoint = GetMousePosition(); | ||
|
||
for (int i = 0; i < MAX_COLORS_COUNT; i++) | ||
{ | ||
if (CheckCollisionPointRec(mousePoint, colorsRecs[i])) colorState[i] = 1; | ||
else colorState[i] = 0; | ||
} | ||
//---------------------------------------------------------------------------------- | ||
|
||
// Draw | ||
//---------------------------------------------------------------------------------- | ||
BeginDrawing(); | ||
|
||
ClearBackground(RAYWHITE); | ||
|
||
DrawText("raylib colors palette", 28, 42, 20, BLACK); | ||
DrawText("press SPACE to see all colors", GetScreenWidth() - 180, GetScreenHeight() - 40, 10, GRAY); | ||
|
||
for (int i = 0; i < MAX_COLORS_COUNT; i++) // Draw all rectangles | ||
{ | ||
DrawRectangleRec(colorsRecs[i], Fade(colors[i], colorState[i]? 0.6f : 1.0f)); | ||
|
||
if (IsKeyDown(KEY_SPACE) || colorState[i]) | ||
{ | ||
DrawRectangle((int)colorsRecs[i].x, (int)(colorsRecs[i].y + colorsRecs[i].height - 26), (int)colorsRecs[i].width, 20, BLACK); | ||
DrawRectangleLinesEx(colorsRecs[i], 6, Fade(BLACK, 0.3f)); | ||
DrawText(colorNames[i], (int)(colorsRecs[i].x + colorsRecs[i].width - MeasureText(colorNames[i], 10) - 12), | ||
(int)(colorsRecs[i].y + colorsRecs[i].height - 20), 10, colors[i]); | ||
} | ||
} | ||
|
||
EndDrawing(); | ||
//---------------------------------------------------------------------------------- | ||
} | ||
|
||
// De-Initialization | ||
//-------------------------------------------------------------------------------------- | ||
CloseWindow(); // Close window and OpenGL context | ||
//-------------------------------------------------------------------------------------- | ||
|
||
return 0; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
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 needed for GitHub pages, due to modern requirements for
SharedArrayBuffer
(after Spectre) and is a bit tacky by using a service worker that reloads the page with the needed headers set.Ideally, GitHub pages would just set this header (like the the CORS headers), but that's not the case.