• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

excaliburjs / Excalibur / 7284323037

21 Dec 2023 05:19AM UTC coverage: 91.829% (-0.001%) from 91.83%
7284323037

push

github

web-flow
feat: Materials can now use the screen texture (#2849)


https://github.com/excaliburjs/Excalibur/assets/612071/ade92ec3-caff-4835-8410-74f4ebbbe421

This PR adds a new feature for materials that allows them to reference the screen texture. They way it works is it's the screen's framebuffer RIGHT before the material draw call (if you include the complete drawing you get the infinite mirror artifact which is pretty unusable)

2 new uniforms

* `u_screen_texture` - This is the texture of the screen right before the material draw call
* `u_time_ms` - This is the milliseconds since page navigation (`performance.now()` under the hood)

2 new attribute/varyings

* `a_screenuv` - The vertex attribute corresponding to the screen uv relative to the current graphic
* `v_screenuv` - The fragment varying corresponding to the screen uv relative to the current graphic

Finally there is a new convenience api for updating shader values in materials. `.update(shader => {...})`

```typescript
  game.input.pointers.primary.on('move', evt => {
    heartActor.pos = evt.worldPos;
    swirlMaterial.update(shader => {
      shader.trySetUniformFloatVector('iMouse', evt.worldPos);
    });
  });
```

4593 of 5750 branches covered (0.0%)

35 of 35 new or added lines in 3 files covered. (100.0%)

10 existing lines in 2 files now uncovered.

10676 of 11626 relevant lines covered (91.83%)

27097.65 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

88.89
/src/engine/Graphics/FontCache.ts
1
import { BoundingBox } from '../Collision/BoundingBox';
2
import { Color } from '../Color';
3
import { Logger } from '../Util/Log';
4
import { Font } from './Font';
5
import { FontTextInstance } from './FontTextInstance';
6

7
export class FontCache {
8
  private static _LOGGER = Logger.getInstance();
1✔
9
  private static _TEXT_USAGE = new Map<FontTextInstance, number>();
1✔
10
  private static _TEXT_CACHE = new Map<string, FontTextInstance>();
1✔
11
  private static _MEASURE_CACHE = new Map<string, BoundingBox>();
1✔
12

13
  static measureText(text: string, font: Font, maxWidth?: number) {
14
    const hash = FontTextInstance.getHashCode(font, text);
1,074✔
15
    if (FontCache._MEASURE_CACHE.has(hash)) {
1,074✔
16
      return FontCache._MEASURE_CACHE.get(hash);
45✔
17
    }
18
    FontCache._LOGGER.debug('Font text measurement cache miss');
1,029✔
19
    const measurement = font.measureTextWithoutCache(text, maxWidth);
1,029✔
20
    FontCache._MEASURE_CACHE.set(hash, measurement);
1,029✔
21
    return measurement;
1,029✔
22
  }
23

24
  static getTextInstance(text: string, font: Font, color: Color) {
25
    const hash = FontTextInstance.getHashCode(font, text, color);
24✔
26
    let textInstance = FontCache._TEXT_CACHE.get(hash);
24✔
27
    if (!textInstance) {
24✔
28
      textInstance = new FontTextInstance(font, text, color);
21✔
29
      FontCache._TEXT_CACHE.set(hash, textInstance);
21✔
30
      FontCache._LOGGER.debug('Font text instance cache miss');
21✔
31
    }
32

33
    // Cache the bitmap for certain amount of time
34
    FontCache._TEXT_USAGE.set(textInstance, performance.now());
24✔
35

36
    return textInstance;
24✔
37
  }
38

39
  static checkAndClearCache() {
40
    const deferred: FontTextInstance[] = [];
754✔
41
    const currentHashCodes = new Set<string>();
754✔
42
    for (const [textInstance, time] of FontCache._TEXT_USAGE.entries()) {
754✔
43
      // if bitmap hasn't been used in 100 ms clear it
44
      if (time + 100 < performance.now()) {
6!
45
        FontCache._LOGGER.debug(`Text cache entry timed out ${textInstance.text}`);
6✔
46
        deferred.push(textInstance);
6✔
47
        textInstance.dispose();
6✔
48
      } else {
UNCOV
49
        const hash = textInstance.getHashCode(false);
×
UNCOV
50
        currentHashCodes.add(hash);
×
51
      }
52
    }
53
    // Deferred removal of text instances
54
    deferred.forEach((t) => {
754✔
55
      FontCache._TEXT_USAGE.delete(t);
6✔
56
    });
57

58
    // Regenerate text instance cache
59
    this._TEXT_CACHE.clear();
754✔
60
    for (const [textInstance] of this._TEXT_USAGE.entries()) {
754✔
UNCOV
61
      this._TEXT_CACHE.set(textInstance.getHashCode(), textInstance);
×
62
    }
63

64
    // Regenerated measurement cache
65
    const newTextMeasurementCache = new Map<string, BoundingBox>();
754✔
66
    for (const current of currentHashCodes) {
754✔
UNCOV
67
      if (FontCache._MEASURE_CACHE.has(current)) {
×
UNCOV
68
        newTextMeasurementCache.set(current, FontCache._MEASURE_CACHE.get(current));
×
69
      }
70
    }
71
    this._MEASURE_CACHE.clear();
754✔
72
    this._MEASURE_CACHE = newTextMeasurementCache;
754✔
73
  }
74

75
  public static get cacheSize() {
76
    return FontCache._TEXT_USAGE.size;
7✔
77
  }
78

79
  /**
80
   * Force clear all cached text bitmaps
81
   */
82
  public static clearCache() {
83
    for (const [textInstance] of FontCache._TEXT_USAGE.entries()) {
5✔
84
      textInstance.dispose();
15✔
85
    }
86
    FontCache._TEXT_USAGE.clear();
5✔
87
    FontCache._TEXT_CACHE.clear();
5✔
88
    FontCache._MEASURE_CACHE.clear();
5✔
89
  }
90
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc