chore: migrate tests to vitest (#3381)
managed to migrate the engine leak & memory reporters, although it took a bit of rigamarole. Mainly because, as opposed to karma, the reporter runs in node and not the browser. So I have to track the data needed separately in some global hooks _within_ the browser environment, which the reporter then reads and creates the logs if needed.
However, it seems the memory tracking is regularly reporting >1 mb, so I'm not sure if it's tracking properly or if things have changed with the new browsers. My only theory is that because the timing where memory is read is during an `afterEach` _before_ the test's `afterEach`, it's running before any potential cleanup. The timing of this is not something that's easy to control, unfortunately. I did try to prove this theory by doing the memory analysis on the next test's `beforeEach`, but it didnt seem to change the results, so it may not be an issue.
Implementation is done in `src/spec/vitest/__reporters__/memory.ts` and `src/spec/vitest/__reporters/memory.setup.ts`
---
I noticed CouroutineSpec was an offender for >10mb memory, which spins up additional engines. I added a short 100ms wait after the engine.dispose and the memory usage did drop, so it does seem like this is prone to scheduled garbage collecting.
---
I was able to run the garbage collector (if exposed, currently only on chrome) and this makes the reports more accurate
feat: Shader Pipelines (#3828)
This PR adds Shader pipeline capabilities to Materials/PostProcessors!
https://github.com/user-attachments/assets/256bdf2c-c09d-40db-b5cd-0e0e1776729c
1. You can stack fragment shaders on each other and re-use them to mix/match effects
- Built in uniforms are passed through each pass
- We support lists of glsl fragment strings or ShaderPasses, and ShaderPipelines that can wrap up multipass effects into a convient package
3. This enables effects like Blur/Bloom/Glow (built in ShaderPipelines) that are only easily possible with multipass workflows
Example using multiple strings
```typescript
// 3. Custom 2-pass pipeline from bare glsl strings
var redOnly = ex.glsl`
in vec2 v_uv;
uniform sampler2D u_image;
out vec4 fragColor;
void main() {
vec4 color = texture(u_image, v_uv);
fragColor = vec4(color.r + color.g + color.b, 0.0, 0.0, color.a);
}`;
var pulseBlue = ex.glsl`
in vec2 v_uv;
uniform sampler2D u_image;
uniform float u_time_ms;
out vec4 fragColor;
void main() {
vec4 color = texture(u_image, v_uv);
float pulse = (sin(u_time_ms / 300.0) + 1.0) / 2.0;
fragColor = vec4(color.r, 0.0, pulse * color.r, color.a);
}`;
var tinted = new ex.Actor({ pos: ex.vec(480, 150) });
tinted.graphics.use(swordImage.toSprite());
tinted.graphics.material = new ex.Material({
name: 'tint-pipeline',
graphicsContext,
passes: [redOnly, pulseBlue]
});
```
Built in effects!
```typescript
var bloomEffect = new ex.BloomEffect({ graphicsContext, threshold: 0.3, intensity: 1.5 });
var blooming = new ex.Actor({ pos: ex.vec(780, 150) });
blooming.graphics.use(swordImage.toSprite());
blooming.graphics.material = new ex.Material({
name: 'bloom-material',
graphicsContext,
passes: bloomEffect,
padding: 32
});
game.add(blooming);
```
Accidental bug fixes
- The time based uniforms in post processors were counting N times based on the number of postprocessors
This was re-implemented resurrected from my old branch back onto main
* https://github.com/excaliburjs/Excalibur/compare/main...feat/shader-pipelines
Huge thanks to Andrew Adamson's tutorials and code samples that provided the inspiration for the framebuffer technique
* https://youtu.be/bAq9Zk7BAWo?si=f4h0rBBR9T5yaS2W
* https://github.com/scriptfoundry/WebGL2-Videos-Materials/blob/main/25.framebuffer.basics.js