jassub 2.0.12 → 2.0.15
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 +2 -2
- package/dist/jassub.d.ts +2 -1
- package/dist/jassub.js +6 -3
- package/dist/jassub.js.map +1 -1
- package/dist/wasm/jassub-worker.js +2 -3
- package/dist/wasm/jassub-worker.js.map +1 -1
- package/dist/worker/webgpu-renderer.d.ts +2 -4
- package/dist/worker/webgpu-renderer.js +108 -120
- package/dist/worker/webgpu-renderer.js.map +1 -1
- package/dist/worker/worker.d.ts +0 -1
- package/dist/worker/worker.js +1 -1
- package/dist/worker/worker.js.map +1 -1
- package/package.json +3 -2
- package/src/debug.ts +72 -0
- package/src/jassub.ts +7 -3
- package/src/worker/webgpu-renderer.ts +109 -126
- package/src/worker/worker.ts +1 -2
- package/dist/wasm/old.js +0 -1034
|
@@ -177,123 +177,122 @@ export class WebGPURenderer {
|
|
|
177
177
|
|
|
178
178
|
// eslint-disable-next-line no-undef
|
|
179
179
|
format: GPUTextureFormat = 'bgra8unorm'
|
|
180
|
-
|
|
180
|
+
_ready
|
|
181
181
|
|
|
182
182
|
constructor () {
|
|
183
183
|
// Start async initialization immediately
|
|
184
|
-
this.
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
async _initDevice (): Promise<void> {
|
|
184
|
+
this._ready = (async () => {
|
|
188
185
|
// Check WebGPU support
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
186
|
+
if (!navigator.gpu) {
|
|
187
|
+
throw new Error('WebGPU not supported')
|
|
188
|
+
}
|
|
192
189
|
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
190
|
+
const adapter = await navigator.gpu.requestAdapter({
|
|
191
|
+
powerPreference: 'high-performance'
|
|
192
|
+
})
|
|
196
193
|
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
194
|
+
if (!adapter) {
|
|
195
|
+
throw new Error('No WebGPU adapter found')
|
|
196
|
+
}
|
|
200
197
|
|
|
201
|
-
|
|
202
|
-
|
|
198
|
+
this.device = await adapter.requestDevice()
|
|
199
|
+
this.format = navigator.gpu.getPreferredCanvasFormat()
|
|
203
200
|
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
201
|
+
// Create shader modules
|
|
202
|
+
const vertexModule = this.device.createShaderModule({
|
|
203
|
+
code: VERTEX_SHADER
|
|
204
|
+
})
|
|
208
205
|
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
206
|
+
const fragmentModule = this.device.createShaderModule({
|
|
207
|
+
code: FRAGMENT_SHADER
|
|
208
|
+
})
|
|
212
209
|
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
210
|
+
// Create uniform buffer
|
|
211
|
+
this.uniformBuffer = this.device.createBuffer({
|
|
212
|
+
size: 16, // vec2f resolution + padding
|
|
213
|
+
usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST
|
|
214
|
+
})
|
|
218
215
|
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
216
|
+
// Create color matrix buffer (mat3x3f requires 48 bytes: 3 vec3f padded to vec4f each)
|
|
217
|
+
this.colorMatrixBuffer = this.device.createBuffer({
|
|
218
|
+
size: 48, // 3 x vec4f (each column is vec3f padded to 16 bytes)
|
|
219
|
+
usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST
|
|
220
|
+
})
|
|
221
|
+
// Initialize with identity matrix
|
|
222
|
+
this.device.queue.writeBuffer(this.colorMatrixBuffer, 0, IDENTITY_MATRIX)
|
|
226
223
|
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
224
|
+
// Create bind group layout (no sampler needed - using textureLoad for pixel-perfect sampling)
|
|
225
|
+
this.bindGroupLayout = this.device.createBindGroupLayout({
|
|
226
|
+
entries: [
|
|
227
|
+
{
|
|
228
|
+
binding: 0,
|
|
229
|
+
visibility: GPUShaderStage.VERTEX,
|
|
230
|
+
buffer: { type: 'uniform' }
|
|
231
|
+
},
|
|
232
|
+
{
|
|
233
|
+
binding: 1,
|
|
234
|
+
visibility: GPUShaderStage.VERTEX,
|
|
235
|
+
buffer: { type: 'read-only-storage' }
|
|
236
|
+
},
|
|
237
|
+
{
|
|
238
|
+
binding: 3,
|
|
239
|
+
visibility: GPUShaderStage.FRAGMENT,
|
|
240
|
+
texture: { sampleType: 'unfilterable-float' } // textureLoad requires unfilterable
|
|
241
|
+
},
|
|
242
|
+
{
|
|
243
|
+
binding: 4,
|
|
244
|
+
visibility: GPUShaderStage.FRAGMENT,
|
|
245
|
+
buffer: { type: 'uniform' }
|
|
246
|
+
}
|
|
247
|
+
]
|
|
248
|
+
})
|
|
252
249
|
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
250
|
+
// Create pipeline layout
|
|
251
|
+
const pipelineLayout = this.device.createPipelineLayout({
|
|
252
|
+
bindGroupLayouts: [this.bindGroupLayout]
|
|
253
|
+
})
|
|
257
254
|
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
255
|
+
// Create render pipeline
|
|
256
|
+
this.pipeline = this.device.createRenderPipeline({
|
|
257
|
+
layout: pipelineLayout,
|
|
258
|
+
vertex: {
|
|
259
|
+
module: vertexModule,
|
|
260
|
+
entryPoint: 'vertexMain'
|
|
261
|
+
},
|
|
262
|
+
fragment: {
|
|
263
|
+
module: fragmentModule,
|
|
264
|
+
entryPoint: 'fragmentMain',
|
|
265
|
+
targets: [
|
|
266
|
+
{
|
|
267
|
+
format: this.format,
|
|
268
|
+
blend: {
|
|
269
|
+
color: {
|
|
270
|
+
srcFactor: 'one',
|
|
271
|
+
dstFactor: 'one-minus-src-alpha',
|
|
272
|
+
operation: 'add'
|
|
273
|
+
},
|
|
274
|
+
alpha: {
|
|
275
|
+
srcFactor: 'one',
|
|
276
|
+
dstFactor: 'one-minus-src-alpha',
|
|
277
|
+
operation: 'add'
|
|
278
|
+
}
|
|
281
279
|
}
|
|
282
280
|
}
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
}
|
|
289
|
-
})
|
|
281
|
+
]
|
|
282
|
+
},
|
|
283
|
+
primitive: {
|
|
284
|
+
topology: 'triangle-list'
|
|
285
|
+
}
|
|
286
|
+
})
|
|
287
|
+
})()
|
|
290
288
|
}
|
|
291
289
|
|
|
292
290
|
async setCanvas (canvas: OffscreenCanvas, width: number, height: number) {
|
|
293
|
-
await this.
|
|
294
|
-
if (!this.device)
|
|
295
|
-
|
|
296
|
-
|
|
291
|
+
await this._ready
|
|
292
|
+
if (!this.device) return
|
|
293
|
+
|
|
294
|
+
// WebGPU doesn't allow 0-sized textures/swapchains
|
|
295
|
+
if (width <= 0 || height <= 0) return
|
|
297
296
|
|
|
298
297
|
canvas.width = width
|
|
299
298
|
canvas.height = height
|
|
@@ -325,9 +324,10 @@ export class WebGPURenderer {
|
|
|
325
324
|
* Pass null or undefined to use identity (no conversion).
|
|
326
325
|
* Matrix should be a pre-padded Float32Array with 12 values (3 columns × 4 floats each).
|
|
327
326
|
*/
|
|
328
|
-
setColorMatrix (matrix?: Float32Array<ArrayBuffer>)
|
|
329
|
-
|
|
330
|
-
this.device
|
|
327
|
+
async setColorMatrix (matrix?: Float32Array<ArrayBuffer>) {
|
|
328
|
+
await this._ready
|
|
329
|
+
if (!this.device) return
|
|
330
|
+
this.device.queue.writeBuffer(this.colorMatrixBuffer!, 0, matrix ?? IDENTITY_MATRIX)
|
|
331
331
|
}
|
|
332
332
|
|
|
333
333
|
private createTextureInfo (width: number, height: number): TextureInfo {
|
|
@@ -348,9 +348,13 @@ export class WebGPURenderer {
|
|
|
348
348
|
render (images: ASSImage[], heap: Uint8Array): void {
|
|
349
349
|
if (!this.device || !this.context || !this.pipeline) return
|
|
350
350
|
|
|
351
|
+
// getCurrentTexture fails if canvas has 0 dimensions
|
|
352
|
+
const currentTexture = this.context.getCurrentTexture()
|
|
353
|
+
if (currentTexture.width === 0 || currentTexture.height === 0) return
|
|
354
|
+
|
|
351
355
|
const commandEncoder = this.device.createCommandEncoder()
|
|
352
356
|
|
|
353
|
-
const textureView =
|
|
357
|
+
const textureView = currentTexture.createView()
|
|
354
358
|
|
|
355
359
|
// Begin render pass
|
|
356
360
|
const renderPass = commandEncoder.beginRenderPass({
|
|
@@ -378,13 +382,13 @@ export class WebGPURenderer {
|
|
|
378
382
|
}
|
|
379
383
|
|
|
380
384
|
// Render each image
|
|
381
|
-
for (let i = 0, texIndex =
|
|
385
|
+
for (let i = 0, texIndex = -1; i < images.length; i++) {
|
|
382
386
|
const img = images[i]!
|
|
383
387
|
|
|
384
388
|
// Skip images with invalid dimensions (WebGPU doesn't allow 0-sized textures)
|
|
385
389
|
if (img.w <= 0 || img.h <= 0) continue
|
|
386
390
|
|
|
387
|
-
let texInfo = this.textures[texIndex]!
|
|
391
|
+
let texInfo = this.textures[++texIndex]!
|
|
388
392
|
|
|
389
393
|
// Recreate texture if size changed (use actual w, not stride)
|
|
390
394
|
if (texInfo.width !== img.w || texInfo.height !== img.h) {
|
|
@@ -456,28 +460,7 @@ export class WebGPURenderer {
|
|
|
456
460
|
this.pendingDestroyTextures = []
|
|
457
461
|
}
|
|
458
462
|
|
|
459
|
-
|
|
460
|
-
if (!this.device || !this.context) return
|
|
461
|
-
|
|
462
|
-
const commandEncoder = this.device.createCommandEncoder()
|
|
463
|
-
const textureView = this.context.getCurrentTexture().createView()
|
|
464
|
-
|
|
465
|
-
const renderPass = commandEncoder.beginRenderPass({
|
|
466
|
-
colorAttachments: [
|
|
467
|
-
{
|
|
468
|
-
view: textureView,
|
|
469
|
-
clearValue: { r: 0, g: 0, b: 0, a: 0 },
|
|
470
|
-
loadOp: 'clear',
|
|
471
|
-
storeOp: 'store'
|
|
472
|
-
}
|
|
473
|
-
]
|
|
474
|
-
})
|
|
475
|
-
|
|
476
|
-
renderPass.end()
|
|
477
|
-
this.device.queue.submit([commandEncoder.finish()])
|
|
478
|
-
}
|
|
479
|
-
|
|
480
|
-
destroy (): void {
|
|
463
|
+
destroy () {
|
|
481
464
|
for (const tex of this.textures) {
|
|
482
465
|
tex.texture.destroy()
|
|
483
466
|
}
|
package/src/worker/worker.ts
CHANGED
|
@@ -28,7 +28,6 @@ interface opts {
|
|
|
28
28
|
libassMemoryLimit: number
|
|
29
29
|
libassGlyphLimit: number
|
|
30
30
|
useLocalFonts: boolean
|
|
31
|
-
supportsSIMD: boolean
|
|
32
31
|
}
|
|
33
32
|
|
|
34
33
|
export class ASSRenderer {
|
|
@@ -68,7 +67,7 @@ export class ASSRenderer {
|
|
|
68
67
|
}
|
|
69
68
|
addEventListener('message', handleMessage)
|
|
70
69
|
|
|
71
|
-
this._ready = (WASM({
|
|
70
|
+
this._ready = (WASM({ __url: data.wasmUrl }) as Promise<MainModule>).then(Module => {
|
|
72
71
|
// eslint-disable-next-line @typescript-eslint/unbound-method
|
|
73
72
|
this._malloc = Module._malloc
|
|
74
73
|
|