@driftengine/core 4.2.1 → 4.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.json +11 -0
- package/dist/render/backend/webgl2/renderer.d.ts +19 -0
- package/dist/render/backend/webgl2/renderer.js +22 -0
- package/dist/render/backend/webgpu/renderer.d.ts +2 -0
- package/dist/render/backend/webgpu/renderer.js +5 -1
- package/package.json +6 -6
- package/src/render/backend/webgl2/renderer.ts +23 -0
- package/src/render/backend/webgpu/renderer.ts +6 -1
package/CHANGELOG.json
CHANGED
|
@@ -1,4 +1,15 @@
|
|
|
1
1
|
[
|
|
2
|
+
{
|
|
3
|
+
"version": "4.3.0",
|
|
4
|
+
"date": "2026-09-21",
|
|
5
|
+
"entries": [
|
|
6
|
+
{
|
|
7
|
+
"kind": "added",
|
|
8
|
+
"short": "RendererApi.snapTextCellSize, so a caller can draw a bitmap glyph on whole device pixels and measure the size it will actually get.",
|
|
9
|
+
"text": "**The snap 4.1.4 imposed and 4.2.0 made opt-in, now reachable from where text is actually laid out.**\n\n`deviceSnappedCellSize` has been exported since 4.2.0, but it takes the ratio as two widths and the second of them is the renderer's own drawing buffer. `RendererApi` carries `cssWidth` and `cssHeight` and nothing about device pixels, and a game's text layout is a pure function over CSS pixels — so a consumer that wanted whole-pixel strokes had to thread a canvas down through its whole interface layer to reach a number the renderer was already holding. Every consumer on this engine sizes text that way, so the export alone was not adoptable.\n\n```js\nconst cell = renderer.snapTextCellSize(available / cells, viewportWidth);\nconst width = textWidthPx(label, cell);\nconst x = (boxWidth - width) / 2;\nrenderer.drawText(text, viewportWidth, viewportHeight, x, y, { ...style, cellSize: cell }, now);\n```\n\n`viewportWidth` is the caller's own and it already passes it to `drawText`. Snap once on the line the cell is computed, and the width, the centring, the right edge and the row pitch all follow from the one number — the measurement and the picture cannot disagree, which is the property the imposed version could never have.\n\nThe decision stays in `textLayout.ts` with only the binding per backend: the cell drawn is the largest whole number of device pixels not larger than the one asked for, downward so a line fitted to a box can only leave a gap rather than overflow it, and a cell already whole comes back untouched. **What it gives up** is that a caller who does not ask still gets strokes of two widths on a fractional cell, which is the trade 4.2.0 chose deliberately.\n\nPinned on the shared surface by a test rather than left to the `Omit` derivation, for the reason `api.test.ts` records about the resolution governor: a public member is otherwise on the surface by accident of not being in the omit list, which is a poor way to make a promise."
|
|
10
|
+
}
|
|
11
|
+
]
|
|
12
|
+
},
|
|
2
13
|
{
|
|
3
14
|
"version": "4.2.1",
|
|
4
15
|
"date": "2026-09-21",
|
|
@@ -1839,6 +1839,25 @@ export declare class WebGL2Renderer implements RendererApi {
|
|
|
1839
1839
|
* whatever box the caller lays out in, and the game's own card is not always the canvas.
|
|
1840
1840
|
*/
|
|
1841
1841
|
drawText(text: TextRenderer, viewportWidth: number, viewportHeight: number, originX: number, originY: number, style: TextStyle, timeSec: number): void;
|
|
1842
|
+
/**
|
|
1843
|
+
* The cell size to draw a bitmap glyph at so every cell covers whole device pixels.
|
|
1844
|
+
*
|
|
1845
|
+
* **The caller snaps, and then measures and draws with the one number.** 4.1.4 applied this
|
|
1846
|
+
* inside `drawText` instead, where `textWidthPx` could not see it — it takes no viewport and so
|
|
1847
|
+
* cannot know the ratio — and every consumer went on laying out against the cell it asked for
|
|
1848
|
+
* while the engine drew a smaller one. A centred line, a right-aligned column and a line fitted
|
|
1849
|
+
* to a box broke together. Snapped here, the arithmetic and the picture cannot disagree.
|
|
1850
|
+
*
|
|
1851
|
+
* **It is on the renderer because only the renderer knows the second number.** `viewportWidth`
|
|
1852
|
+
* is the caller's, and it already passes it to `drawText`; the drawing buffer is not on the
|
|
1853
|
+
* shared surface and a game's text layout is a pure function over CSS pixels, so reaching it
|
|
1854
|
+
* meant threading a canvas through an interface layer to a value the renderer was holding.
|
|
1855
|
+
*
|
|
1856
|
+
* The decision itself stays in `textLayout.ts` and only this binding is per backend, which is
|
|
1857
|
+
* the 2026-08-13 rule: a cell already whole comes back untouched, and the snap is downward so a
|
|
1858
|
+
* line fitted to a box can only leave a gap rather than overflow it.
|
|
1859
|
+
*/
|
|
1860
|
+
snapTextCellSize(cellSize: number, viewportWidth: number): number;
|
|
1842
1861
|
/** Width of the string this handle currently holds, in pixels at a given cell size. */
|
|
1843
1862
|
textWidth(text: TextRenderer, cellSize: number): number;
|
|
1844
1863
|
disposeText(text: TextRenderer): void;
|
|
@@ -75,6 +75,7 @@ import { TextRenderer } from '../../textRenderer.js';
|
|
|
75
75
|
import { SdfTextRenderer } from '../../sdfTextRenderer.js';
|
|
76
76
|
import { MOTION_BLUR_MAX_UV, RUSH_REACH_UV } from '../../vertexDefaults.js';
|
|
77
77
|
import { OUTPUT_TRANSFORM_CODE } from '../../vertexDefaults.js';
|
|
78
|
+
import { deviceSnappedCellSize } from '../../textLayout.js';
|
|
78
79
|
import { WindStreakRenderer } from '../../windStreakRenderer.js';
|
|
79
80
|
import { InstancedBatch } from './instanced.js';
|
|
80
81
|
/**
|
|
@@ -2777,6 +2778,27 @@ export class WebGL2Renderer {
|
|
|
2777
2778
|
this.textBudget.ask();
|
|
2778
2779
|
}
|
|
2779
2780
|
}
|
|
2781
|
+
/**
|
|
2782
|
+
* The cell size to draw a bitmap glyph at so every cell covers whole device pixels.
|
|
2783
|
+
*
|
|
2784
|
+
* **The caller snaps, and then measures and draws with the one number.** 4.1.4 applied this
|
|
2785
|
+
* inside `drawText` instead, where `textWidthPx` could not see it — it takes no viewport and so
|
|
2786
|
+
* cannot know the ratio — and every consumer went on laying out against the cell it asked for
|
|
2787
|
+
* while the engine drew a smaller one. A centred line, a right-aligned column and a line fitted
|
|
2788
|
+
* to a box broke together. Snapped here, the arithmetic and the picture cannot disagree.
|
|
2789
|
+
*
|
|
2790
|
+
* **It is on the renderer because only the renderer knows the second number.** `viewportWidth`
|
|
2791
|
+
* is the caller's, and it already passes it to `drawText`; the drawing buffer is not on the
|
|
2792
|
+
* shared surface and a game's text layout is a pure function over CSS pixels, so reaching it
|
|
2793
|
+
* meant threading a canvas through an interface layer to a value the renderer was holding.
|
|
2794
|
+
*
|
|
2795
|
+
* The decision itself stays in `textLayout.ts` and only this binding is per backend, which is
|
|
2796
|
+
* the 2026-08-13 rule: a cell already whole comes back untouched, and the snap is downward so a
|
|
2797
|
+
* line fitted to a box can only leave a gap rather than overflow it.
|
|
2798
|
+
*/
|
|
2799
|
+
snapTextCellSize(cellSize, viewportWidth) {
|
|
2800
|
+
return deviceSnappedCellSize(cellSize, viewportWidth, this.gl.drawingBufferWidth);
|
|
2801
|
+
}
|
|
2780
2802
|
/** Width of the string this handle currently holds, in pixels at a given cell size. */
|
|
2781
2803
|
textWidth(text, cellSize) {
|
|
2782
2804
|
return text.widthPx(cellSize);
|
|
@@ -1126,6 +1126,8 @@ export declare class WebGPURenderer implements RendererApi {
|
|
|
1126
1126
|
createText(): GpuText;
|
|
1127
1127
|
setText(text: GpuText, content: string): void;
|
|
1128
1128
|
setPlate(text: GpuText, widthCells: number, heightCells: number, bottomCell: number): void;
|
|
1129
|
+
/** See `Renderer.snapTextCellSize`; only the drawing buffer differs. */
|
|
1130
|
+
snapTextCellSize(cellSize: number, viewportWidth: number): number;
|
|
1129
1131
|
textWidth(text: GpuText, cellSize: number): number;
|
|
1130
1132
|
disposeText(text: GpuText): void;
|
|
1131
1133
|
/** A text label from an SDF font. See `sdfTextPass.ts`; the layout it holds is shared with WebGL2. */
|
|
@@ -38,7 +38,7 @@ import { POINT_SHADOW_SAMPLER, PointShadowSystem, } from '../../pointShadowSyste
|
|
|
38
38
|
import { GpuPointShadowArray, GpuPointShadowMap } from './pointShadowPass.js';
|
|
39
39
|
import { createPanelBindGroup, createPanelBindGroupLayout, createPanelCorners, panelPipeline, PANEL_FRAG_FIELDS, PANEL_FRAG_SIZE, PANEL_VERTEX_COUNT, PANEL_VERT_FIELDS, PANEL_VERT_SIZE, } from './panelPass.js';
|
|
40
40
|
import { createGpuTextCube, createTextBindGroup, createTextBindGroupLayout, GpuText, textPipeline, TEXT_FRAG_FIELDS, TEXT_FRAG_SIZE, TEXT_VERT_FIELDS, TEXT_VERT_SIZE, } from './textPass.js';
|
|
41
|
-
import { deviceSnappedOrigin } from '../../textLayout.js';
|
|
41
|
+
import { deviceSnappedCellSize, deviceSnappedOrigin } from '../../textLayout.js';
|
|
42
42
|
import { GpuSurfaceTexture } from './surfaceTexturePass.js';
|
|
43
43
|
import { createSdfTextBindGroup, createSdfTextBindGroupLayout, GpuSdfText, sdfTextPipeline, SDF_TEXT_FRAG_FIELDS, SDF_TEXT_FRAG_SIZE, SDF_TEXT_VERT_FIELDS, SDF_TEXT_VERT_SIZE, } from './sdfTextPass.js';
|
|
44
44
|
import { createFilmBindGroup, createFilmBindGroupLayout, filmPipeline, FILM_FRAG_FIELDS, FILM_FRAG_SIZE, FILM_VERT_FIELDS, FILM_VERT_SIZE, } from './filmPass.js';
|
|
@@ -2342,6 +2342,10 @@ export class WebGPURenderer {
|
|
|
2342
2342
|
setPlate(text, widthCells, heightCells, bottomCell) {
|
|
2343
2343
|
text.setPlate(widthCells, heightCells, bottomCell);
|
|
2344
2344
|
}
|
|
2345
|
+
/** See `Renderer.snapTextCellSize`; only the drawing buffer differs. */
|
|
2346
|
+
snapTextCellSize(cellSize, viewportWidth) {
|
|
2347
|
+
return deviceSnappedCellSize(cellSize, viewportWidth, this.surface.canvas.width);
|
|
2348
|
+
}
|
|
2345
2349
|
textWidth(text, cellSize) {
|
|
2346
2350
|
return text.layout.widthPx(cellSize);
|
|
2347
2351
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@driftengine/core",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.3.0",
|
|
4
4
|
"description": "DriftEngine reusable browser 3D runtime",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"type": "module",
|
|
@@ -29,8 +29,8 @@
|
|
|
29
29
|
],
|
|
30
30
|
"sideEffects": false,
|
|
31
31
|
"dependencies": {
|
|
32
|
-
"@driftengine/drft": "4.
|
|
33
|
-
"@driftengine/physics": "4.
|
|
32
|
+
"@driftengine/drft": "4.3.0",
|
|
33
|
+
"@driftengine/physics": "4.3.0",
|
|
34
34
|
"gl-matrix": "^3.4.3"
|
|
35
35
|
},
|
|
36
36
|
"author": "Drift Technologies",
|
|
@@ -63,8 +63,8 @@
|
|
|
63
63
|
"prepack": "node ../../scripts/build.mjs @driftengine/core"
|
|
64
64
|
},
|
|
65
65
|
"devDependencies": {
|
|
66
|
-
"@driftengine/nav": "4.
|
|
67
|
-
"@driftengine/network": "4.
|
|
68
|
-
"@driftengine/texture": "4.
|
|
66
|
+
"@driftengine/nav": "4.3.0",
|
|
67
|
+
"@driftengine/network": "4.3.0",
|
|
68
|
+
"@driftengine/texture": "4.3.0"
|
|
69
69
|
}
|
|
70
70
|
}
|
|
@@ -218,6 +218,7 @@ import type { SdfTextStyle } from '../../sdfTextLayout.ts';
|
|
|
218
218
|
import { MOTION_BLUR_MAX_UV, RUSH_REACH_UV } from '../../vertexDefaults.ts';
|
|
219
219
|
import { OUTPUT_TRANSFORM_CODE } from '../../vertexDefaults.ts';
|
|
220
220
|
import type { RenderBackend } from '../api.ts';
|
|
221
|
+
import { deviceSnappedCellSize } from '../../textLayout.ts';
|
|
221
222
|
import type { TextStyle } from '../../textLayout.ts';
|
|
222
223
|
import { WindStreakRenderer } from '../../windStreakRenderer.ts';
|
|
223
224
|
import type { WindStreakOptions } from '../../windStreakRenderer.ts';
|
|
@@ -3657,6 +3658,28 @@ export class WebGL2Renderer implements RendererApi {
|
|
|
3657
3658
|
}
|
|
3658
3659
|
}
|
|
3659
3660
|
|
|
3661
|
+
/**
|
|
3662
|
+
* The cell size to draw a bitmap glyph at so every cell covers whole device pixels.
|
|
3663
|
+
*
|
|
3664
|
+
* **The caller snaps, and then measures and draws with the one number.** 4.1.4 applied this
|
|
3665
|
+
* inside `drawText` instead, where `textWidthPx` could not see it — it takes no viewport and so
|
|
3666
|
+
* cannot know the ratio — and every consumer went on laying out against the cell it asked for
|
|
3667
|
+
* while the engine drew a smaller one. A centred line, a right-aligned column and a line fitted
|
|
3668
|
+
* to a box broke together. Snapped here, the arithmetic and the picture cannot disagree.
|
|
3669
|
+
*
|
|
3670
|
+
* **It is on the renderer because only the renderer knows the second number.** `viewportWidth`
|
|
3671
|
+
* is the caller's, and it already passes it to `drawText`; the drawing buffer is not on the
|
|
3672
|
+
* shared surface and a game's text layout is a pure function over CSS pixels, so reaching it
|
|
3673
|
+
* meant threading a canvas through an interface layer to a value the renderer was holding.
|
|
3674
|
+
*
|
|
3675
|
+
* The decision itself stays in `textLayout.ts` and only this binding is per backend, which is
|
|
3676
|
+
* the 2026-08-13 rule: a cell already whole comes back untouched, and the snap is downward so a
|
|
3677
|
+
* line fitted to a box can only leave a gap rather than overflow it.
|
|
3678
|
+
*/
|
|
3679
|
+
snapTextCellSize(cellSize: number, viewportWidth: number): number {
|
|
3680
|
+
return deviceSnappedCellSize(cellSize, viewportWidth, this.gl.drawingBufferWidth);
|
|
3681
|
+
}
|
|
3682
|
+
|
|
3660
3683
|
/** Width of the string this handle currently holds, in pixels at a given cell size. */
|
|
3661
3684
|
textWidth(text: TextRenderer, cellSize: number): number {
|
|
3662
3685
|
return text.widthPx(cellSize);
|
|
@@ -150,7 +150,7 @@ import {
|
|
|
150
150
|
type GpuTextCube,
|
|
151
151
|
} from './textPass.ts';
|
|
152
152
|
import type { TextStyle } from '../../textLayout.ts';
|
|
153
|
-
import { deviceSnappedOrigin } from '../../textLayout.ts';
|
|
153
|
+
import { deviceSnappedCellSize, deviceSnappedOrigin } from '../../textLayout.ts';
|
|
154
154
|
import { GpuSurfaceTexture } from './surfaceTexturePass.ts';
|
|
155
155
|
import {
|
|
156
156
|
createSdfTextBindGroup,
|
|
@@ -3000,6 +3000,11 @@ export class WebGPURenderer implements RendererApi {
|
|
|
3000
3000
|
text.setPlate(widthCells, heightCells, bottomCell);
|
|
3001
3001
|
}
|
|
3002
3002
|
|
|
3003
|
+
/** See `Renderer.snapTextCellSize`; only the drawing buffer differs. */
|
|
3004
|
+
snapTextCellSize(cellSize: number, viewportWidth: number): number {
|
|
3005
|
+
return deviceSnappedCellSize(cellSize, viewportWidth, this.surface.canvas.width);
|
|
3006
|
+
}
|
|
3007
|
+
|
|
3003
3008
|
textWidth(text: GpuText, cellSize: number): number {
|
|
3004
3009
|
return text.layout.widthPx(cellSize);
|
|
3005
3010
|
}
|