ntk 8.8.1 → 8.8.2
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/lib/app.js +21 -1
- package/lib/fontconfig.js +60 -1
- package/lib/gl.js +72 -0
- package/lib/glx.js +9 -5
- package/lib/renderingcontext_cgl.js +14 -1
- package/lib/renderingcontext_gles.js +15 -1
- package/package.json +1 -1
package/lib/app.js
CHANGED
|
@@ -5,12 +5,15 @@ import {
|
|
|
5
5
|
DIRECT_SAMPLES,
|
|
6
6
|
GLError,
|
|
7
7
|
backendFor,
|
|
8
|
+
directStencilSize,
|
|
8
9
|
glCapabilities,
|
|
9
10
|
glError,
|
|
10
11
|
nativeRefreshRate,
|
|
11
12
|
requestedSamples,
|
|
13
|
+
requestedStencil,
|
|
12
14
|
resolveGLPolicy,
|
|
13
|
-
warnDirectSamples
|
|
15
|
+
warnDirectSamples,
|
|
16
|
+
warnDirectStencil
|
|
14
17
|
} from './gl.js';
|
|
15
18
|
import { chooseGLXConfig } from './glx.js';
|
|
16
19
|
import Picture from './picture.js';
|
|
@@ -637,6 +640,14 @@ export default class App {
|
|
|
637
640
|
* request that cannot be met also warns once per connection, because an
|
|
638
641
|
* aliased edge is otherwise found months later in a screenshot.
|
|
639
642
|
*
|
|
643
|
+
* `stencilSize` is answered the same way, and matters more: a stencil test
|
|
644
|
+
* against a framebuffer with no stencil buffer passes everywhere, so
|
|
645
|
+
* stencil-based drawing renders wrong instead of failing. `STENCIL_SIZE`
|
|
646
|
+
* becomes the CGL context's stencil size on macOS (8 when the spec names
|
|
647
|
+
* none, as the Cocoa backend's surfaces have); the dri3 flavor has nothing
|
|
648
|
+
* to ask EGL with, so it answers 0 and a request for more warns once
|
|
649
|
+
* (`directStencilSize` in lib/gl.js).
|
|
650
|
+
*
|
|
640
651
|
* @param {object} [spec] GLX attributes, e.g. `{ DEPTH_SIZE: 24 }`
|
|
641
652
|
*/
|
|
642
653
|
async chooseGLConfig(spec = {}) {
|
|
@@ -659,6 +670,12 @@ export default class App {
|
|
|
659
670
|
// answer honestly below rather than dropping the attribute (issue #341)
|
|
660
671
|
const wantSamples = requestedSamples(spec);
|
|
661
672
|
if (wantSamples > DIRECT_SAMPLES) warnDirectSamples(this, caps.flavor, wantSamples);
|
|
673
|
+
// the same for stencil, where it matters more: a flavor that cannot ask
|
|
674
|
+
// for a stencil buffer answers 0, because a stencil test against no
|
|
675
|
+
// buffer passes everywhere and the draw code would never find out
|
|
676
|
+
const wantStencil = requestedStencil(spec);
|
|
677
|
+
const stencilSize = directStencilSize(caps.flavor, wantStencil);
|
|
678
|
+
if (wantStencil > stencilSize) warnDirectStencil(this, caps.flavor, wantStencil);
|
|
662
679
|
|
|
663
680
|
return {
|
|
664
681
|
backend: 'direct',
|
|
@@ -671,6 +688,9 @@ export default class App {
|
|
|
671
688
|
class: 4, // TrueColor; the only class these buffers can be read as
|
|
672
689
|
doubleBuffer: true, // a swap chain, always
|
|
673
690
|
depthSize: spec.DEPTH_SIZE ?? 16,
|
|
691
|
+
// what the context will ask for (8 on appledri unless the spec says),
|
|
692
|
+
// carried here so the spec reaches getContext with the rest of it
|
|
693
|
+
stencilSize,
|
|
674
694
|
// no flavor can allocate a multisampled window buffer yet; said out
|
|
675
695
|
// loud so a caller can supersample instead of assuming MSAA
|
|
676
696
|
samples: DIRECT_SAMPLES,
|
package/lib/fontconfig.js
CHANGED
|
@@ -133,8 +133,67 @@ function fcMatchError(err) {
|
|
|
133
133
|
return err;
|
|
134
134
|
}
|
|
135
135
|
|
|
136
|
+
/**
|
|
137
|
+
* macOS's own face for a CSS generic family that fontconfig answers there
|
|
138
|
+
* with the wrong script's face, keyed by the generic.
|
|
139
|
+
*
|
|
140
|
+
* Only `sans-serif` is here, because it is the only generic fontconfig
|
|
141
|
+
* resolves to a CJK face on a Mac (see `platformFamilies`). `serif` and
|
|
142
|
+
* `monospace` land on PT Serif and Andale Mono, both of which set Latin,
|
|
143
|
+
* Cyrillic and Greek at their own widths.
|
|
144
|
+
*/
|
|
145
|
+
const DARWIN_GENERICS = new Map([['sans-serif', 'Helvetica']]);
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* A family list with macOS's own face for a generic named ahead of it — on
|
|
149
|
+
* macOS only, and only for the generics in `DARWIN_GENERICS`. Everywhere
|
|
150
|
+
* else the list goes to fc-match exactly as it came.
|
|
151
|
+
*
|
|
152
|
+
* Homebrew's fontconfig (2.18) ships 48-guessfamily.conf, which tags a query
|
|
153
|
+
* naming a generic with that generic, and under it a `sans-serif` query
|
|
154
|
+
* ranks every face with "Sans" in its name ahead of all of 60-latin.conf's
|
|
155
|
+
* preferences — Verdana, Arial and Helvetica come in past the hundredth
|
|
156
|
+
* candidate. So `fc-match sans-serif` answers with whichever "…Sans…" face
|
|
157
|
+
* ranks first, and on a Mac that is Hiragino Sans: a Japanese face whose
|
|
158
|
+
* Cyrillic, Greek, `…` and `—` are full-width. "Мост" set in it is four
|
|
159
|
+
* em-wide cells, and CoreText sets it the same way — the advances are the
|
|
160
|
+
* font's own, so what is wrong is the face, not the shaping. Without that
|
|
161
|
+
* one file (XQuartz's fontconfig ships without it) the same query answers
|
|
162
|
+
* Verdana.
|
|
163
|
+
*
|
|
164
|
+
* Browsers never ask fontconfig on macOS, and all of them give `sans-serif`
|
|
165
|
+
* Helvetica. Naming it first does the same here, and the generic stays
|
|
166
|
+
* after it, so a codepoint Helvetica lacks still falls back through
|
|
167
|
+
* fontconfig's sans-serif list — CJK to Hiragino, as before.
|
|
168
|
+
*
|
|
169
|
+
* @param {string} family CSS-style family list, as FontManager hands it over
|
|
170
|
+
* @param {string} [platform] `process.platform`; a parameter so that a test
|
|
171
|
+
* can ask for either answer on any machine
|
|
172
|
+
* @returns {string} the family list to hand fc-match
|
|
173
|
+
*/
|
|
174
|
+
export function platformFamilies(family, platform = globalThis.process?.platform) {
|
|
175
|
+
if (platform !== 'darwin') return family;
|
|
176
|
+
const names = String(family)
|
|
177
|
+
.split(',')
|
|
178
|
+
.map((name) => name.trim())
|
|
179
|
+
.filter(Boolean);
|
|
180
|
+
const lower = names.map((name) => name.replace(/^["']|["']$/g, '').toLowerCase());
|
|
181
|
+
const out = [];
|
|
182
|
+
let changed = false;
|
|
183
|
+
for (let i = 0; i < names.length; i++) {
|
|
184
|
+
const face = DARWIN_GENERICS.get(lower[i]);
|
|
185
|
+
// a list that already names the face ahead of the generic needs nothing
|
|
186
|
+
if (face && !lower.slice(0, i).includes(face.toLowerCase())) {
|
|
187
|
+
out.push(face);
|
|
188
|
+
changed = true;
|
|
189
|
+
}
|
|
190
|
+
out.push(names[i]);
|
|
191
|
+
}
|
|
192
|
+
return changed ? out.join(',') : family;
|
|
193
|
+
}
|
|
194
|
+
|
|
136
195
|
function patternFor({ family, weight, style }) {
|
|
137
|
-
let fc = family || 'sans-serif';
|
|
196
|
+
let fc = platformFamilies(family || 'sans-serif');
|
|
138
197
|
const fcWeight = normalizeWeight(weight);
|
|
139
198
|
if (fcWeight !== undefined) fc += `:weight=${fcWeight}`;
|
|
140
199
|
if (style && style.includes('italic')) fc += ':slant=italic';
|
package/lib/gl.js
CHANGED
|
@@ -535,6 +535,78 @@ export function warnDirectSamples(app, flavor, wanted) {
|
|
|
535
535
|
);
|
|
536
536
|
}
|
|
537
537
|
|
|
538
|
+
/**
|
|
539
|
+
* The stencil bits a CGL context asks for when the config names no size:
|
|
540
|
+
* eight, what the Cocoa backend's GL surfaces carry (DEPTH24_STENCIL8), so
|
|
541
|
+
* a window on the `appledri` flavor has the same buffers whichever of the
|
|
542
|
+
* two macOS paths draws it. A stencil buffer costs next to nothing, and the
|
|
543
|
+
* failure without one is silent: a stencil test with no buffer behind it
|
|
544
|
+
* passes everywhere, so a stencil-then-cover fill does not fail — it fills
|
|
545
|
+
* its whole cover quad.
|
|
546
|
+
*/
|
|
547
|
+
export const DEFAULT_STENCIL_SIZE = 8;
|
|
548
|
+
|
|
549
|
+
/**
|
|
550
|
+
* The stencil size a spec or config asks for, read the way the depth size
|
|
551
|
+
* is: `stencilSize` (a config `chooseGLConfig` answered) before
|
|
552
|
+
* `STENCIL_SIZE` (GLX's vocabulary). `undefined` when it names neither,
|
|
553
|
+
* which means "the flavor's default" rather than 0 — an explicit 0 asks for
|
|
554
|
+
* no stencil buffer and is honoured as such. `null` is GLX's "don't care"
|
|
555
|
+
* and reads as unnamed too.
|
|
556
|
+
*/
|
|
557
|
+
export function requestedStencil(spec = {}) {
|
|
558
|
+
const bits = spec.stencilSize ?? spec.STENCIL_SIZE;
|
|
559
|
+
if (bits === undefined || bits === null) return undefined;
|
|
560
|
+
return Number(bits) || 0;
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
/**
|
|
564
|
+
* The stencil bits a direct-backend window gets for a request, per flavor —
|
|
565
|
+
* the one place that decides it, as `DIRECT_SAMPLES` is for samples.
|
|
566
|
+
*
|
|
567
|
+
* - `appledri`: what was asked, `DEFAULT_STENCIL_SIZE` when nothing was. It
|
|
568
|
+
* travels to `kCGLPFAStencilSize` on the CGL pixel format, which CGL
|
|
569
|
+
* treats as a minimum.
|
|
570
|
+
* - `dri3`: 0, whatever was asked. x11-dri's `Gpu` builds its EGLConfig from
|
|
571
|
+
* the colour format and a depth size and takes no stencil size, so none is
|
|
572
|
+
* asked for — and EGL sorts configs smallest stencil first, so assume
|
|
573
|
+
* none. There is nothing here to ask with; what can be done is say so
|
|
574
|
+
* (`warnDirectStencil`). When the addon grows the option, this becomes the
|
|
575
|
+
* request.
|
|
576
|
+
*/
|
|
577
|
+
export function directStencilSize(flavor, wanted) {
|
|
578
|
+
if (flavor === 'appledri') return wanted ?? DEFAULT_STENCIL_SIZE;
|
|
579
|
+
return 0;
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
/**
|
|
583
|
+
* Say — once per connection, on the console — that a stencil request cannot
|
|
584
|
+
* be met on this flavor.
|
|
585
|
+
*
|
|
586
|
+
* Unlike a missing sample buffer this is not a downgrade that merely looks
|
|
587
|
+
* worse: a stencil test against a framebuffer with no stencil buffer passes
|
|
588
|
+
* everywhere, so draw code built on it renders wrong instead of failing, and
|
|
589
|
+
* this warning and the `stencilSize` field are the only signals it gets.
|
|
590
|
+
*/
|
|
591
|
+
export function warnDirectStencil(app, flavor, wanted) {
|
|
592
|
+
if (app._warnedDirectStencil) return;
|
|
593
|
+
app._warnedDirectStencil = true;
|
|
594
|
+
console.warn(
|
|
595
|
+
`ntk: this GL request asks for STENCIL_SIZE=${wanted}, and the direct backend` +
|
|
596
|
+
`${flavor ? ` (${flavor} flavor)` : ''} cannot give its window a stencil buffer, so it has ` +
|
|
597
|
+
'stencilSize: 0 — and a stencil test with no buffer behind it passes everywhere.\n' +
|
|
598
|
+
'\n' +
|
|
599
|
+
' The stencil size belongs to the EGLConfig x11-dri builds one layer below\n' +
|
|
600
|
+
' ntk, and its Gpu takes no such option yet — there is nothing here to ask with.\n' +
|
|
601
|
+
'\n' +
|
|
602
|
+
' Branch on config.stencilSize (or gl.stencilSize) rather than on having asked.\n' +
|
|
603
|
+
' For a stencil buffer today: draw into a framebuffer of your own with a\n' +
|
|
604
|
+
' DEPTH24_STENCIL8 renderbuffer attached, or use indirect GLX, where\n' +
|
|
605
|
+
" STENCIL_SIZE picks an fbconfig that has one (glPolicy: 'indirect').\n" +
|
|
606
|
+
' See docs/context-gles.md#stencil.'
|
|
607
|
+
);
|
|
608
|
+
}
|
|
609
|
+
|
|
538
610
|
/**
|
|
539
611
|
* The backend `getContext('opengl')` should use right now, synchronously.
|
|
540
612
|
*
|
package/lib/glx.js
CHANGED
|
@@ -227,11 +227,12 @@ function matchLegacy(configs, spec) {
|
|
|
227
227
|
* `null` means "don't care". `screen` picks the X screen (default 0),
|
|
228
228
|
* `visual` pins a specific visual id and skips the search.
|
|
229
229
|
* @returns {Promise<{visual: number, depth: number, class: number,
|
|
230
|
-
* doubleBuffer: boolean, depthSize: number,
|
|
231
|
-
* fbconfig: number|null, config: object}>}
|
|
232
|
-
* samples per pixel the chosen config has — 0 for
|
|
233
|
-
*
|
|
234
|
-
* at to
|
|
230
|
+
* doubleBuffer: boolean, depthSize: number, stencilSize: number|null,
|
|
231
|
+
* samples: number|null, fbconfig: number|null, config: object}>}
|
|
232
|
+
* `samples` is the colour samples per pixel the chosen config has — 0 for
|
|
233
|
+
* no multisampling — and `stencilSize` its stencil bits; both are `null`
|
|
234
|
+
* only in the pinned-`visual` case, where no fbconfig was looked at to
|
|
235
|
+
* know.
|
|
235
236
|
*/
|
|
236
237
|
export async function chooseGLXConfig(app, spec = {}) {
|
|
237
238
|
const GLX = app.display.GLX;
|
|
@@ -268,6 +269,7 @@ export async function chooseGLXConfig(app, spec = {}) {
|
|
|
268
269
|
depthSize: toNumber(want.DEPTH_SIZE) || 0,
|
|
269
270
|
// the caller pinned the visual, so nothing was chosen and no fbconfig
|
|
270
271
|
// was read: unknown, which is not the same claim as "none"
|
|
272
|
+
stencilSize: null,
|
|
271
273
|
samples: null,
|
|
272
274
|
screen: screenNum,
|
|
273
275
|
fbconfig: null,
|
|
@@ -286,6 +288,7 @@ export async function chooseGLXConfig(app, spec = {}) {
|
|
|
286
288
|
class: info.visual.class,
|
|
287
289
|
doubleBuffer: !!cfg.DOUBLEBUFFER,
|
|
288
290
|
depthSize: cfg.DEPTH_SIZE || 0,
|
|
291
|
+
stencilSize: cfg.STENCIL_SIZE || 0,
|
|
289
292
|
samples: cfg.SAMPLES || 0,
|
|
290
293
|
screen: screenNum,
|
|
291
294
|
fbconfig: cfg.FBCONFIG_ID,
|
|
@@ -307,6 +310,7 @@ export async function chooseGLXConfig(app, spec = {}) {
|
|
|
307
310
|
class: info.visual.class,
|
|
308
311
|
doubleBuffer: !!cfg.doubleBufferMode,
|
|
309
312
|
depthSize: cfg.depthBits || 0,
|
|
313
|
+
stencilSize: cfg.stencilBits || 0,
|
|
310
314
|
samples: cfg.SAMPLES || 0,
|
|
311
315
|
screen: screenNum,
|
|
312
316
|
fbconfig: null,
|
|
@@ -47,9 +47,11 @@ import {
|
|
|
47
47
|
DIRECT_SAMPLES,
|
|
48
48
|
GLError,
|
|
49
49
|
backendFor,
|
|
50
|
+
directStencilSize,
|
|
50
51
|
glError,
|
|
51
52
|
loadDriAddon,
|
|
52
53
|
requestedSamples,
|
|
54
|
+
requestedStencil,
|
|
53
55
|
warnDirectSamples
|
|
54
56
|
} from './gl.js';
|
|
55
57
|
|
|
@@ -134,6 +136,13 @@ does not make it retroactive. Either:
|
|
|
134
136
|
const wantSamples = Math.max(requestedSamples(config), Number(config.samples) || 0);
|
|
135
137
|
if (wantSamples > DIRECT_SAMPLES) warnDirectSamples(app, 'appledri', wantSamples);
|
|
136
138
|
|
|
139
|
+
/**
|
|
140
|
+
* Stencil bits the context asked CGL for: the config's `stencilSize` /
|
|
141
|
+
* `STENCIL_SIZE`, and 8 when it names none (`directStencilSize`). CGL
|
|
142
|
+
* takes the size as a minimum, so the buffer has at least this many.
|
|
143
|
+
*/
|
|
144
|
+
this.stencilSize = directStencilSize('appledri', requestedStencil(config));
|
|
145
|
+
|
|
137
146
|
this._screen = config.screen ?? 0;
|
|
138
147
|
this._clientId = caps.appleClientId ?? dri.apple.clientId();
|
|
139
148
|
|
|
@@ -145,7 +154,11 @@ does not make it retroactive. Either:
|
|
|
145
154
|
// covered (docs/context-gles.md#macos).
|
|
146
155
|
try {
|
|
147
156
|
this.ctx = new dri.apple.Context({
|
|
148
|
-
depthSize: config.depthSize ?? config.DEPTH_SIZE ?? 16
|
|
157
|
+
depthSize: config.depthSize ?? config.DEPTH_SIZE ?? 16,
|
|
158
|
+
// Without this the pixel format has no stencil buffer at all, and GL
|
|
159
|
+
// raises no error over it: a stencil test against a missing buffer
|
|
160
|
+
// passes everywhere, so a stencil-then-cover fill covers its whole quad.
|
|
161
|
+
stencilSize: this.stencilSize
|
|
149
162
|
});
|
|
150
163
|
} catch (err) {
|
|
151
164
|
throw glError(
|
|
@@ -21,10 +21,13 @@ import {
|
|
|
21
21
|
DIRECT_SAMPLES,
|
|
22
22
|
GLError,
|
|
23
23
|
backendFor,
|
|
24
|
+
directStencilSize,
|
|
24
25
|
glError,
|
|
25
26
|
loadDriAddon,
|
|
26
27
|
requestedSamples,
|
|
27
|
-
|
|
28
|
+
requestedStencil,
|
|
29
|
+
warnDirectSamples,
|
|
30
|
+
warnDirectStencil
|
|
28
31
|
} from './gl.js';
|
|
29
32
|
import { GLSwapchain } from './glswapchain.js';
|
|
30
33
|
|
|
@@ -116,6 +119,17 @@ does not make it retroactive. Either:
|
|
|
116
119
|
const wantSamples = Math.max(requestedSamples(config), Number(config.samples) || 0);
|
|
117
120
|
if (wantSamples > DIRECT_SAMPLES) warnDirectSamples(app, caps.flavor, wantSamples);
|
|
118
121
|
|
|
122
|
+
/**
|
|
123
|
+
* Stencil bits the window's buffers have: 0 on this flavor, whatever the
|
|
124
|
+
* config asks for, because x11-dri's `Gpu` takes no stencil size to pass
|
|
125
|
+
* on (`directStencilSize`). A request for one is answered the way a
|
|
126
|
+
* sample count is — once, out loud — because a stencil test with no
|
|
127
|
+
* buffer behind it passes everywhere rather than failing.
|
|
128
|
+
*/
|
|
129
|
+
const wantStencil = requestedStencil(config);
|
|
130
|
+
this.stencilSize = directStencilSize('dri3', wantStencil);
|
|
131
|
+
if (wantStencil > this.stencilSize) warnDirectStencil(app, 'dri3', wantStencil);
|
|
132
|
+
|
|
119
133
|
const policy = app.glPolicy;
|
|
120
134
|
this.gpu = sharedGpu(app, dri, {
|
|
121
135
|
format: depth === 32 ? dri.FORMAT.ARGB8888 : dri.FORMAT.XRGB8888,
|