jassub 2.2.3 → 2.2.5
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/README.md +4 -4
- package/dist/debug.js +1 -2
- package/dist/debug.js.map +1 -1
- package/dist/wasm/jassub-worker.js +1 -1
- package/dist/wasm/jassub-worker.js.map +1 -1
- package/dist/worker/webgl-renderer.d.ts +7 -7
- package/dist/worker/webgl-renderer.js +132 -55
- package/dist/worker/webgl-renderer.js.map +1 -1
- package/dist/worker/worker.js +4 -1
- package/dist/worker/worker.js.map +1 -1
- package/package.json +4 -4
- package/src/debug.ts +13 -15
- package/src/worker/webgl-renderer.ts +169 -74
- package/src/worker/worker.ts +5 -1
|
@@ -6,9 +6,19 @@ declare const self: DedicatedWorkerGlobalScope &
|
|
|
6
6
|
WASMMEMORY: WebAssembly.Memory
|
|
7
7
|
}
|
|
8
8
|
|
|
9
|
+
const IS_FIREFOX = navigator.userAgent.toLowerCase().includes('firefox')
|
|
10
|
+
|
|
11
|
+
const THREAD_COUNT = !IS_FIREFOX && self.crossOriginIsolated ? Math.min(Math.max(1, navigator.hardwareConcurrency - 2), 8) : 1
|
|
12
|
+
|
|
9
13
|
// @ts-expect-error new experimental API
|
|
10
14
|
const SUPPORTS_GROWTH = !!WebAssembly.Memory.prototype.toResizableBuffer
|
|
11
15
|
|
|
16
|
+
// HACK: 3 memory hacks to support here:
|
|
17
|
+
// 1. Chrome WASM Growable memory which can use a reference to the buffer to fix visual artifacts, which happen both with multithreading or without [fastest]
|
|
18
|
+
// 2. Chrome WASM non-growable, but mult-threaded only memory which needs to re-create the HEAPU8 on growth because of race conditions [medium]
|
|
19
|
+
// 3. Firefox non-growable memory which needs a copy of the data into a non-resizable buffer and can't use a reference [fastest single threaded, but only on Firefox, on Chrome this is slowest]
|
|
20
|
+
const SHOULD_REFERENCE_MEMORY = !IS_FIREFOX && (SUPPORTS_GROWTH || THREAD_COUNT > 1)
|
|
21
|
+
|
|
12
22
|
const IDENTITY_MATRIX = new Float32Array([
|
|
13
23
|
1, 0, 0,
|
|
14
24
|
0, 1, 0,
|
|
@@ -62,9 +72,9 @@ export const colorMatrixConversionMap = {
|
|
|
62
72
|
|
|
63
73
|
export type ColorSpace = keyof typeof colorMatrixConversionMap
|
|
64
74
|
|
|
65
|
-
// GLSL ES 3.0 Vertex Shader
|
|
75
|
+
// GLSL ES 3.0 Vertex Shader with Instancing
|
|
66
76
|
const VERTEX_SHADER = /* glsl */`#version 300 es
|
|
67
|
-
precision
|
|
77
|
+
precision mediump float;
|
|
68
78
|
|
|
69
79
|
const vec2 QUAD_POSITIONS[6] = vec2[6](
|
|
70
80
|
vec2(0.0, 0.0),
|
|
@@ -76,9 +86,11 @@ const vec2 QUAD_POSITIONS[6] = vec2[6](
|
|
|
76
86
|
);
|
|
77
87
|
|
|
78
88
|
uniform vec2 u_resolution;
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
89
|
+
|
|
90
|
+
// Instance attributes
|
|
91
|
+
in vec4 a_destRect; // x, y, w, h
|
|
92
|
+
in vec4 a_color; // r, g, b, a
|
|
93
|
+
in float a_texLayer;
|
|
82
94
|
|
|
83
95
|
flat out vec2 v_destXY;
|
|
84
96
|
flat out vec4 v_color;
|
|
@@ -87,22 +99,22 @@ flat out float v_texLayer;
|
|
|
87
99
|
|
|
88
100
|
void main() {
|
|
89
101
|
vec2 quadPos = QUAD_POSITIONS[gl_VertexID];
|
|
90
|
-
vec2 pixelPos =
|
|
102
|
+
vec2 pixelPos = a_destRect.xy + quadPos * a_destRect.zw;
|
|
91
103
|
vec2 clipPos = (pixelPos / u_resolution) * 2.0 - 1.0;
|
|
92
104
|
clipPos.y = -clipPos.y;
|
|
93
105
|
|
|
94
106
|
gl_Position = vec4(clipPos, 0.0, 1.0);
|
|
95
|
-
v_destXY =
|
|
96
|
-
v_color =
|
|
97
|
-
v_texSize =
|
|
98
|
-
v_texLayer =
|
|
107
|
+
v_destXY = a_destRect.xy;
|
|
108
|
+
v_color = a_color;
|
|
109
|
+
v_texSize = a_destRect.zw;
|
|
110
|
+
v_texLayer = a_texLayer;
|
|
99
111
|
}
|
|
100
112
|
`
|
|
101
113
|
|
|
102
114
|
// GLSL ES 3.0 Fragment Shader - use texelFetch for pixel-perfect sampling
|
|
103
115
|
const FRAGMENT_SHADER = /* glsl */`#version 300 es
|
|
104
|
-
precision
|
|
105
|
-
precision
|
|
116
|
+
precision mediump float;
|
|
117
|
+
precision mediump sampler2DArray;
|
|
106
118
|
|
|
107
119
|
uniform sampler2DArray u_texArray;
|
|
108
120
|
uniform mat3 u_colorMatrix;
|
|
@@ -128,8 +140,7 @@ void main() {
|
|
|
128
140
|
// Bounds check (prevents out-of-bounds access)
|
|
129
141
|
ivec2 texSizeI = ivec2(v_texSize);
|
|
130
142
|
if (texCoord.x < 0 || texCoord.y < 0 || texCoord.x >= texSizeI.x || texCoord.y >= texSizeI.y) {
|
|
131
|
-
|
|
132
|
-
return;
|
|
143
|
+
discard;
|
|
133
144
|
}
|
|
134
145
|
|
|
135
146
|
// texelFetch: integer coords, no interpolation, no precision issues
|
|
@@ -152,6 +163,7 @@ void main() {
|
|
|
152
163
|
// Texture array configuration
|
|
153
164
|
const TEX_ARRAY_SIZE = 64 // Fixed layer count
|
|
154
165
|
const TEX_INITIAL_SIZE = 256 // Initial width/height
|
|
166
|
+
const MAX_INSTANCES = 256 // Maximum instances per draw call
|
|
155
167
|
|
|
156
168
|
export class WebGL2Renderer {
|
|
157
169
|
gl: WebGL2RenderingContext | null = null
|
|
@@ -160,19 +172,31 @@ export class WebGL2Renderer {
|
|
|
160
172
|
|
|
161
173
|
// Uniform locations
|
|
162
174
|
u_resolution: WebGLUniformLocation | null = null
|
|
163
|
-
u_destRect: WebGLUniformLocation | null = null
|
|
164
|
-
u_color: WebGLUniformLocation | null = null
|
|
165
175
|
u_texArray: WebGLUniformLocation | null = null
|
|
166
176
|
u_colorMatrix: WebGLUniformLocation | null = null
|
|
167
|
-
u_texLayer: WebGLUniformLocation | null = null
|
|
168
177
|
|
|
169
|
-
//
|
|
178
|
+
// Instance attribute buffers
|
|
179
|
+
instanceDestRectBuffer: WebGLBuffer | null = null
|
|
180
|
+
instanceColorBuffer: WebGLBuffer | null = null
|
|
181
|
+
instanceTexLayerBuffer: WebGLBuffer | null = null
|
|
182
|
+
|
|
183
|
+
// Instance data arrays
|
|
184
|
+
instanceDestRectData: Float32Array
|
|
185
|
+
instanceColorData: Float32Array
|
|
186
|
+
instanceTexLayerData: Float32Array
|
|
187
|
+
|
|
170
188
|
texArray: WebGLTexture | null = null
|
|
171
189
|
texArrayWidth = 0
|
|
172
190
|
texArrayHeight = 0
|
|
173
191
|
|
|
174
192
|
colorMatrix: Float32Array = IDENTITY_MATRIX
|
|
175
193
|
|
|
194
|
+
constructor () {
|
|
195
|
+
this.instanceDestRectData = new Float32Array(MAX_INSTANCES * 4)
|
|
196
|
+
this.instanceColorData = new Float32Array(MAX_INSTANCES * 4)
|
|
197
|
+
this.instanceTexLayerData = new Float32Array(MAX_INSTANCES)
|
|
198
|
+
}
|
|
199
|
+
|
|
176
200
|
setCanvas (canvas: OffscreenCanvas, width: number, height: number) {
|
|
177
201
|
// WebGL2 doesn't allow 0-sized canvases
|
|
178
202
|
if (width <= 0 || height <= 0) return
|
|
@@ -181,16 +205,15 @@ export class WebGL2Renderer {
|
|
|
181
205
|
canvas.height = height
|
|
182
206
|
|
|
183
207
|
if (!this.gl) {
|
|
184
|
-
// Get canvas context
|
|
185
|
-
// Note: preserveDrawingBuffer is false (default) - the browser handles
|
|
186
|
-
// buffer swaps for OffscreenCanvas, avoiding flicker
|
|
187
208
|
this.gl = canvas.getContext('webgl2', {
|
|
188
209
|
alpha: true,
|
|
189
210
|
premultipliedAlpha: true,
|
|
190
211
|
antialias: false,
|
|
191
212
|
depth: false,
|
|
213
|
+
preserveDrawingBuffer: false,
|
|
192
214
|
stencil: false,
|
|
193
|
-
desynchronized: true
|
|
215
|
+
desynchronized: true,
|
|
216
|
+
powerPreference: 'high-performance'
|
|
194
217
|
})
|
|
195
218
|
|
|
196
219
|
if (!this.gl) {
|
|
@@ -221,16 +244,37 @@ export class WebGL2Renderer {
|
|
|
221
244
|
|
|
222
245
|
// Get uniform locations
|
|
223
246
|
this.u_resolution = this.gl.getUniformLocation(this.program, 'u_resolution')
|
|
224
|
-
this.u_destRect = this.gl.getUniformLocation(this.program, 'u_destRect')
|
|
225
|
-
this.u_color = this.gl.getUniformLocation(this.program, 'u_color')
|
|
226
247
|
this.u_texArray = this.gl.getUniformLocation(this.program, 'u_texArray')
|
|
227
248
|
this.u_colorMatrix = this.gl.getUniformLocation(this.program, 'u_colorMatrix')
|
|
228
|
-
|
|
249
|
+
|
|
250
|
+
// Create instance attribute buffers
|
|
251
|
+
this.instanceDestRectBuffer = this.gl.createBuffer()
|
|
252
|
+
this.instanceColorBuffer = this.gl.createBuffer()
|
|
253
|
+
this.instanceTexLayerBuffer = this.gl.createBuffer()
|
|
229
254
|
|
|
230
255
|
// Create a VAO (required for WebGL2)
|
|
231
256
|
this.vao = this.gl.createVertexArray()
|
|
232
257
|
this.gl.bindVertexArray(this.vao)
|
|
233
258
|
|
|
259
|
+
// Setup instance attributes
|
|
260
|
+
const destRectLoc = this.gl.getAttribLocation(this.program, 'a_destRect')
|
|
261
|
+
this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.instanceDestRectBuffer)
|
|
262
|
+
this.gl.enableVertexAttribArray(destRectLoc)
|
|
263
|
+
this.gl.vertexAttribPointer(destRectLoc, 4, this.gl.FLOAT, false, 0, 0)
|
|
264
|
+
this.gl.vertexAttribDivisor(destRectLoc, 1)
|
|
265
|
+
|
|
266
|
+
const colorLoc = this.gl.getAttribLocation(this.program, 'a_color')
|
|
267
|
+
this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.instanceColorBuffer)
|
|
268
|
+
this.gl.enableVertexAttribArray(colorLoc)
|
|
269
|
+
this.gl.vertexAttribPointer(colorLoc, 4, this.gl.FLOAT, false, 0, 0)
|
|
270
|
+
this.gl.vertexAttribDivisor(colorLoc, 1)
|
|
271
|
+
|
|
272
|
+
const texLayerLoc = this.gl.getAttribLocation(this.program, 'a_texLayer')
|
|
273
|
+
this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.instanceTexLayerBuffer)
|
|
274
|
+
this.gl.enableVertexAttribArray(texLayerLoc)
|
|
275
|
+
this.gl.vertexAttribPointer(texLayerLoc, 1, this.gl.FLOAT, false, 0, 0)
|
|
276
|
+
this.gl.vertexAttribDivisor(texLayerLoc, 1)
|
|
277
|
+
|
|
234
278
|
// Set up blending for premultiplied alpha
|
|
235
279
|
this.gl.enable(this.gl.BLEND)
|
|
236
280
|
this.gl.blendFunc(this.gl.ONE, this.gl.ONE_MINUS_SRC_ALPHA)
|
|
@@ -273,10 +317,8 @@ export class WebGL2Renderer {
|
|
|
273
317
|
return shader
|
|
274
318
|
}
|
|
275
319
|
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
* Pass null or undefined to use identity (no conversion).
|
|
279
|
-
*/
|
|
320
|
+
// Set the color matrix for color space conversion.
|
|
321
|
+
// Pass null or undefined to use identity (no conversion).
|
|
280
322
|
setColorMatrix (subtitleColorSpace?: 'BT601' | 'BT709' | 'SMPTE240M' | 'FCC', videoColorSpace?: 'BT601' | 'BT709') {
|
|
281
323
|
this.colorMatrix = (subtitleColorSpace && videoColorSpace && colorMatrixConversionMap[subtitleColorSpace]?.[videoColorSpace]) ?? IDENTITY_MATRIX
|
|
282
324
|
if (this.gl && this.u_colorMatrix && this.program) {
|
|
@@ -304,7 +346,7 @@ export class WebGL2Renderer {
|
|
|
304
346
|
0,
|
|
305
347
|
this.gl!.RED,
|
|
306
348
|
this.gl!.UNSIGNED_BYTE,
|
|
307
|
-
null
|
|
349
|
+
null // Firefox cries about uninitialized data, but is slower with zero initialized data...
|
|
308
350
|
)
|
|
309
351
|
|
|
310
352
|
// Set texture parameters (no filtering needed for texelFetch, but set anyway)
|
|
@@ -320,72 +362,110 @@ export class WebGL2Renderer {
|
|
|
320
362
|
render (images: ASSImage[], heap: Uint8Array): void {
|
|
321
363
|
if (!this.gl || !this.program || !this.vao || !this.texArray) return
|
|
322
364
|
|
|
323
|
-
//
|
|
324
|
-
if ((self.HEAPU8RAW.buffer !== self.WASMMEMORY.buffer) ||
|
|
365
|
+
// HACK 1 and 2 [see above for explanation]
|
|
366
|
+
if ((self.HEAPU8RAW.buffer !== self.WASMMEMORY.buffer) || SHOULD_REFERENCE_MEMORY) {
|
|
325
367
|
heap = self.HEAPU8RAW = new Uint8Array(self.WASMMEMORY.buffer)
|
|
326
368
|
}
|
|
327
369
|
|
|
328
370
|
// Clear canvas
|
|
329
371
|
this.gl.clear(this.gl.COLOR_BUFFER_BIT)
|
|
330
372
|
|
|
331
|
-
// Find max dimensions needed
|
|
373
|
+
// Find max dimensions needed and filter valid images
|
|
332
374
|
let maxW = this.texArrayWidth
|
|
333
375
|
let maxH = this.texArrayHeight
|
|
376
|
+
const validImages: ASSImage[] = []
|
|
377
|
+
|
|
334
378
|
for (const img of images) {
|
|
379
|
+
if (img.w <= 0 || img.h <= 0) continue
|
|
380
|
+
validImages.push(img)
|
|
335
381
|
if (img.w > maxW) maxW = img.w
|
|
336
382
|
if (img.h > maxH) maxH = img.h
|
|
337
383
|
}
|
|
338
384
|
|
|
385
|
+
if (validImages.length === 0) return
|
|
386
|
+
|
|
339
387
|
// Resize texture array if needed
|
|
340
388
|
if (maxW > this.texArrayWidth || maxH > this.texArrayHeight) {
|
|
341
389
|
this.createTexArray(maxW, maxH)
|
|
342
|
-
this.gl.bindTexture(this.gl.TEXTURE_2D_ARRAY, this.texArray)
|
|
343
390
|
}
|
|
344
391
|
|
|
345
|
-
//
|
|
346
|
-
|
|
347
|
-
|
|
392
|
+
// Process images in chunks that fit within texture array size
|
|
393
|
+
const batchSize = Math.min(TEX_ARRAY_SIZE, MAX_INSTANCES)
|
|
394
|
+
|
|
395
|
+
for (let batchStart = 0; batchStart < validImages.length; batchStart += batchSize) {
|
|
396
|
+
const batchEnd = Math.min(batchStart + batchSize, validImages.length)
|
|
397
|
+
let instanceCount = 0
|
|
398
|
+
|
|
399
|
+
// Upload textures for this batch
|
|
400
|
+
for (let i = batchStart; i < batchEnd; i++) {
|
|
401
|
+
const img = validImages[i]!
|
|
402
|
+
const layer = instanceCount
|
|
403
|
+
|
|
404
|
+
// Upload bitmap data to texture array layer
|
|
405
|
+
this.gl.pixelStorei(this.gl.UNPACK_ROW_LENGTH, img.stride)
|
|
406
|
+
|
|
407
|
+
if (IS_FIREFOX) {
|
|
408
|
+
// HACK 3 [see above for explanation]
|
|
409
|
+
const sourceView = new Uint8Array(heap.buffer, img.bitmap, img.stride * img.h)
|
|
410
|
+
const bitmapData = new Uint8Array(sourceView)
|
|
411
|
+
|
|
412
|
+
this.gl.texSubImage3D(
|
|
413
|
+
this.gl.TEXTURE_2D_ARRAY,
|
|
414
|
+
0,
|
|
415
|
+
0, 0, layer, // x, y, z offset
|
|
416
|
+
img.w,
|
|
417
|
+
img.h,
|
|
418
|
+
1, // depth (1 layer)
|
|
419
|
+
this.gl.RED,
|
|
420
|
+
this.gl.UNSIGNED_BYTE,
|
|
421
|
+
bitmapData
|
|
422
|
+
)
|
|
423
|
+
} else {
|
|
424
|
+
this.gl.texSubImage3D(
|
|
425
|
+
this.gl.TEXTURE_2D_ARRAY,
|
|
426
|
+
0,
|
|
427
|
+
0, 0, layer, // x, y, z offset
|
|
428
|
+
img.w,
|
|
429
|
+
img.h,
|
|
430
|
+
1, // depth (1 layer)
|
|
431
|
+
this.gl.RED,
|
|
432
|
+
this.gl.UNSIGNED_BYTE,
|
|
433
|
+
heap,
|
|
434
|
+
img.bitmap
|
|
435
|
+
)
|
|
436
|
+
}
|
|
437
|
+
// Fill instance data
|
|
438
|
+
const idx = instanceCount * 4
|
|
439
|
+
this.instanceDestRectData[idx] = img.dst_x
|
|
440
|
+
this.instanceDestRectData[idx + 1] = img.dst_y
|
|
441
|
+
this.instanceDestRectData[idx + 2] = img.w
|
|
442
|
+
this.instanceDestRectData[idx + 3] = img.h
|
|
443
|
+
|
|
444
|
+
this.instanceColorData[idx] = ((img.color >>> 24) & 0xFF) / 255
|
|
445
|
+
this.instanceColorData[idx + 1] = ((img.color >>> 16) & 0xFF) / 255
|
|
446
|
+
this.instanceColorData[idx + 2] = ((img.color >>> 8) & 0xFF) / 255
|
|
447
|
+
this.instanceColorData[idx + 3] = (img.color & 0xFF) / 255
|
|
448
|
+
|
|
449
|
+
this.instanceTexLayerData[instanceCount] = layer
|
|
450
|
+
|
|
451
|
+
instanceCount++
|
|
452
|
+
}
|
|
348
453
|
|
|
349
|
-
|
|
350
|
-
if (img.w <= 0 || img.h <= 0) continue
|
|
454
|
+
this.gl.pixelStorei(this.gl.UNPACK_ROW_LENGTH, 0)
|
|
351
455
|
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
// Upload bitmap data to texture array layer
|
|
357
|
-
this.gl.pixelStorei(this.gl.UNPACK_ROW_LENGTH, img.stride)
|
|
358
|
-
|
|
359
|
-
this.gl.texSubImage3D(
|
|
360
|
-
this.gl.TEXTURE_2D_ARRAY,
|
|
361
|
-
0,
|
|
362
|
-
0, 0, layer, // x, y, z offset
|
|
363
|
-
img.w,
|
|
364
|
-
img.h,
|
|
365
|
-
1, // depth (1 layer)
|
|
366
|
-
this.gl.RED,
|
|
367
|
-
this.gl.UNSIGNED_BYTE,
|
|
368
|
-
heap,
|
|
369
|
-
img.bitmap
|
|
370
|
-
)
|
|
456
|
+
if (instanceCount === 0) continue
|
|
457
|
+
// Upload instance data to buffers
|
|
458
|
+
this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.instanceDestRectBuffer)
|
|
459
|
+
this.gl.bufferData(this.gl.ARRAY_BUFFER, this.instanceDestRectData.subarray(0, instanceCount * 4), this.gl.DYNAMIC_DRAW)
|
|
371
460
|
|
|
372
|
-
this.gl.
|
|
461
|
+
this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.instanceColorBuffer)
|
|
462
|
+
this.gl.bufferData(this.gl.ARRAY_BUFFER, this.instanceColorData.subarray(0, instanceCount * 4), this.gl.DYNAMIC_DRAW)
|
|
463
|
+
|
|
464
|
+
this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.instanceTexLayerBuffer)
|
|
465
|
+
this.gl.bufferData(this.gl.ARRAY_BUFFER, this.instanceTexLayerData.subarray(0, instanceCount), this.gl.DYNAMIC_DRAW)
|
|
373
466
|
|
|
374
|
-
//
|
|
375
|
-
this.gl.
|
|
376
|
-
this.gl.uniform1f(this.u_texLayer, layer)
|
|
377
|
-
|
|
378
|
-
// color (RGBA from 0xRRGGBBAA)
|
|
379
|
-
this.gl.uniform4f(
|
|
380
|
-
this.u_color,
|
|
381
|
-
((img.color >>> 24) & 0xFF) / 255,
|
|
382
|
-
((img.color >>> 16) & 0xFF) / 255,
|
|
383
|
-
((img.color >>> 8) & 0xFF) / 255,
|
|
384
|
-
(img.color & 0xFF) / 255
|
|
385
|
-
)
|
|
386
|
-
|
|
387
|
-
// 6 vertices for quad
|
|
388
|
-
this.gl.drawArrays(this.gl.TRIANGLES, 0, 6)
|
|
467
|
+
// Single instanced draw call
|
|
468
|
+
this.gl.drawArraysInstanced(this.gl.TRIANGLES, 0, 6, instanceCount)
|
|
389
469
|
}
|
|
390
470
|
}
|
|
391
471
|
|
|
@@ -396,6 +476,21 @@ export class WebGL2Renderer {
|
|
|
396
476
|
this.texArray = null
|
|
397
477
|
}
|
|
398
478
|
|
|
479
|
+
if (this.instanceDestRectBuffer) {
|
|
480
|
+
this.gl.deleteBuffer(this.instanceDestRectBuffer)
|
|
481
|
+
this.instanceDestRectBuffer = null
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
if (this.instanceColorBuffer) {
|
|
485
|
+
this.gl.deleteBuffer(this.instanceColorBuffer)
|
|
486
|
+
this.instanceColorBuffer = null
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
if (this.instanceTexLayerBuffer) {
|
|
490
|
+
this.gl.deleteBuffer(this.instanceTexLayerBuffer)
|
|
491
|
+
this.instanceTexLayerBuffer = null
|
|
492
|
+
}
|
|
493
|
+
|
|
399
494
|
if (this.vao) {
|
|
400
495
|
this.gl.deleteVertexArray(this.vao)
|
|
401
496
|
this.vao = null
|
package/src/worker/worker.ts
CHANGED
|
@@ -11,6 +11,8 @@ import { WebGL2Renderer } from './webgl-renderer'
|
|
|
11
11
|
import type { ASSEvent, ASSImage, ASSStyle } from '../jassub'
|
|
12
12
|
import type { JASSUB, MainModule } from '../wasm/types.js'
|
|
13
13
|
|
|
14
|
+
const IS_FIREFOX = navigator.userAgent.toLowerCase().includes('firefox')
|
|
15
|
+
|
|
14
16
|
declare const self: DedicatedWorkerGlobalScope &
|
|
15
17
|
typeof globalThis & {
|
|
16
18
|
HEAPU8RAW: Uint8Array<ArrayBuffer>
|
|
@@ -80,7 +82,9 @@ export class ASSRenderer {
|
|
|
80
82
|
|
|
81
83
|
const fallbackFont = data.fallbackFont.toLowerCase()
|
|
82
84
|
this._wasm = new Module.JASSUB(data.width, data.height, fallbackFont)
|
|
83
|
-
|
|
85
|
+
// Firefox seems to have issues with multithreading in workers
|
|
86
|
+
// a worker inside a worker does not recive messages properly
|
|
87
|
+
this._wasm.setThreads(!IS_FIREFOX && self.crossOriginIsolated ? Math.min(Math.max(1, navigator.hardwareConcurrency - 2), 8) : 1)
|
|
84
88
|
|
|
85
89
|
if (fallbackFont) this._findAvailableFonts(fallbackFont)
|
|
86
90
|
|