akarisub 0.1.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/LICENSE +23 -0
- package/README.md +388 -0
- package/dist/COPYRIGHT +951 -0
- package/dist/akarisub-worker.js +39 -0
- package/dist/akarisub-worker.wasm +0 -0
- package/dist/akarisub.umd.js +159 -0
- package/dist/default.woff2 +0 -0
- package/dist/index.js +147 -0
- package/package.json +63 -0
- package/src/ts/akarisub.ts +1159 -0
- package/src/ts/types.ts +391 -0
- package/src/ts/utils.ts +512 -0
- package/src/ts/webgl2-renderer.ts +415 -0
- package/src/ts/webgpu-renderer.ts +728 -0
- package/src/ts/worker.ts +1866 -0
|
@@ -0,0 +1,415 @@
|
|
|
1
|
+
import type { RenderImage } from './types'
|
|
2
|
+
|
|
3
|
+
const MAX_IMAGES_PER_BATCH = 256
|
|
4
|
+
const MAX_TEXTURE_ARRAY_LAYERS = 256
|
|
5
|
+
|
|
6
|
+
// GLSL Vertex Shader (GLSL ES 3.00)
|
|
7
|
+
const VERTEX_SHADER = /* glsl */ `#version 300 es
|
|
8
|
+
precision highp float;
|
|
9
|
+
|
|
10
|
+
in vec4 a_destRect;
|
|
11
|
+
in vec4 a_texInfo;
|
|
12
|
+
|
|
13
|
+
uniform vec2 u_resolution;
|
|
14
|
+
|
|
15
|
+
out vec2 v_uv;
|
|
16
|
+
flat out int v_texIndex;
|
|
17
|
+
flat out vec2 v_texSize;
|
|
18
|
+
|
|
19
|
+
vec2 quadPos(int id) {
|
|
20
|
+
if (id == 0) return vec2(0.0, 0.0);
|
|
21
|
+
if (id == 1) return vec2(1.0, 0.0);
|
|
22
|
+
if (id == 2) return vec2(0.0, 1.0);
|
|
23
|
+
if (id == 3) return vec2(1.0, 0.0);
|
|
24
|
+
if (id == 4) return vec2(1.0, 1.0);
|
|
25
|
+
return vec2(0.0, 1.0);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
void main() {
|
|
29
|
+
vec2 qp = quadPos(gl_VertexID);
|
|
30
|
+
vec2 pixelPos = a_destRect.xy + qp * a_destRect.zw;
|
|
31
|
+
|
|
32
|
+
// Convert CSS pixel coords (y=0 top) to GL clip space (y=1 top)
|
|
33
|
+
vec2 clip = (pixelPos / u_resolution) * 2.0 - 1.0;
|
|
34
|
+
clip.y = -clip.y;
|
|
35
|
+
|
|
36
|
+
gl_Position = vec4(clip, 0.0, 1.0);
|
|
37
|
+
v_uv = qp;
|
|
38
|
+
v_texIndex = int(a_texInfo.z);
|
|
39
|
+
v_texSize = a_texInfo.xy;
|
|
40
|
+
}
|
|
41
|
+
`
|
|
42
|
+
|
|
43
|
+
// GLSL Fragment Shader (GLSL ES 3.00)
|
|
44
|
+
const FRAGMENT_SHADER = /* glsl */ `#version 300 es
|
|
45
|
+
precision highp float;
|
|
46
|
+
precision highp sampler2DArray;
|
|
47
|
+
|
|
48
|
+
uniform sampler2DArray u_texArray;
|
|
49
|
+
uniform ivec2 u_texArraySize;
|
|
50
|
+
|
|
51
|
+
in vec2 v_uv;
|
|
52
|
+
flat in int v_texIndex;
|
|
53
|
+
flat in vec2 v_texSize;
|
|
54
|
+
|
|
55
|
+
out vec4 fragColor;
|
|
56
|
+
|
|
57
|
+
void main() {
|
|
58
|
+
vec2 normalizedCoord = v_uv * v_texSize / vec2(u_texArraySize);
|
|
59
|
+
vec4 color = texture(u_texArray, vec3(normalizedCoord, float(v_texIndex)));
|
|
60
|
+
// Premultiplied alpha output (matches WebGPU renderer behaviour)
|
|
61
|
+
fragColor = vec4(color.rgb * color.a, color.a);
|
|
62
|
+
}
|
|
63
|
+
`
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Check if WebGL2 is supported in the current browser.
|
|
67
|
+
*/
|
|
68
|
+
export function isWebGL2Supported(): boolean {
|
|
69
|
+
if (typeof document === 'undefined') return false
|
|
70
|
+
try {
|
|
71
|
+
const canvas = document.createElement('canvas')
|
|
72
|
+
return canvas.getContext('webgl2') !== null
|
|
73
|
+
} catch {
|
|
74
|
+
return false
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function compileShader(gl: WebGL2RenderingContext, type: number, source: string): WebGLShader {
|
|
79
|
+
const shader = gl.createShader(type)!
|
|
80
|
+
gl.shaderSource(shader, source)
|
|
81
|
+
gl.compileShader(shader)
|
|
82
|
+
if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
|
|
83
|
+
const info = gl.getShaderInfoLog(shader)
|
|
84
|
+
gl.deleteShader(shader)
|
|
85
|
+
throw new Error(`WebGL2 shader compilation failed: ${info}`)
|
|
86
|
+
}
|
|
87
|
+
return shader
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* High-performance WebGL2 subtitle renderer for AkariSub.
|
|
92
|
+
*/
|
|
93
|
+
export class WebGL2Renderer {
|
|
94
|
+
private _gl: WebGL2RenderingContext | null = null
|
|
95
|
+
private _canvas: HTMLCanvasElement | null = null
|
|
96
|
+
private _program: WebGLProgram | null = null
|
|
97
|
+
private _vao: WebGLVertexArrayObject | null = null
|
|
98
|
+
private _instanceBuffer: WebGLBuffer | null = null
|
|
99
|
+
private _texArray: WebGLTexture | null = null
|
|
100
|
+
|
|
101
|
+
private _texWidth = 0
|
|
102
|
+
private _texHeight = 0
|
|
103
|
+
private _texLayers = 0
|
|
104
|
+
|
|
105
|
+
private _resolutionLoc: WebGLUniformLocation | null = null
|
|
106
|
+
private _texArraySizeLoc: WebGLUniformLocation | null = null
|
|
107
|
+
private readonly _instanceData: Float32Array
|
|
108
|
+
|
|
109
|
+
private _lastCanvasWidth = 0
|
|
110
|
+
private _lastCanvasHeight = 0
|
|
111
|
+
private _initialized = false
|
|
112
|
+
private _initPromise: Promise<void> | null = null
|
|
113
|
+
|
|
114
|
+
constructor() {
|
|
115
|
+
this._instanceData = new Float32Array(MAX_IMAGES_PER_BATCH * 8)
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
async init(): Promise<void> {
|
|
119
|
+
if (this._initPromise) return this._initPromise
|
|
120
|
+
this._initPromise = this._checkSupport()
|
|
121
|
+
return this._initPromise
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
private async _checkSupport(): Promise<void> {
|
|
125
|
+
if (typeof document === 'undefined') throw new Error('WebGL2 requires a DOM environment')
|
|
126
|
+
const canvas = document.createElement('canvas')
|
|
127
|
+
if (!canvas.getContext('webgl2')) throw new Error('WebGL2 not supported')
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
private _initGL(): void {
|
|
131
|
+
if (!this._canvas) throw new Error('Canvas not set before _initGL')
|
|
132
|
+
if (this._gl) return // already initialised
|
|
133
|
+
|
|
134
|
+
const gl = this._canvas.getContext('webgl2', { alpha: true, premultipliedAlpha: true, antialias: false })
|
|
135
|
+
if (!gl) throw new Error('Failed to create WebGL2 context')
|
|
136
|
+
this._gl = gl
|
|
137
|
+
|
|
138
|
+
// Compile and link program
|
|
139
|
+
const vert = compileShader(gl, gl.VERTEX_SHADER, VERTEX_SHADER)
|
|
140
|
+
const frag = compileShader(gl, gl.FRAGMENT_SHADER, FRAGMENT_SHADER)
|
|
141
|
+
const program = gl.createProgram()!
|
|
142
|
+
gl.attachShader(program, vert)
|
|
143
|
+
gl.attachShader(program, frag)
|
|
144
|
+
gl.linkProgram(program)
|
|
145
|
+
if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
|
|
146
|
+
throw new Error(`WebGL2 program link failed: ${gl.getProgramInfoLog(program)}`)
|
|
147
|
+
}
|
|
148
|
+
gl.deleteShader(vert)
|
|
149
|
+
gl.deleteShader(frag)
|
|
150
|
+
this._program = program
|
|
151
|
+
|
|
152
|
+
this._resolutionLoc = gl.getUniformLocation(program, 'u_resolution')
|
|
153
|
+
this._texArraySizeLoc = gl.getUniformLocation(program, 'u_texArraySize')
|
|
154
|
+
|
|
155
|
+
// VAO + instance VBO
|
|
156
|
+
this._vao = gl.createVertexArray()!
|
|
157
|
+
gl.bindVertexArray(this._vao)
|
|
158
|
+
|
|
159
|
+
this._instanceBuffer = gl.createBuffer()!
|
|
160
|
+
gl.bindBuffer(gl.ARRAY_BUFFER, this._instanceBuffer)
|
|
161
|
+
// Size: MAX_IMAGES * 8 floats * 4 bytes
|
|
162
|
+
gl.bufferData(gl.ARRAY_BUFFER, MAX_IMAGES_PER_BATCH * 32, gl.DYNAMIC_DRAW)
|
|
163
|
+
|
|
164
|
+
// stride = 32 bytes (8 × float)
|
|
165
|
+
const aDestRect = gl.getAttribLocation(program, 'a_destRect')
|
|
166
|
+
gl.enableVertexAttribArray(aDestRect)
|
|
167
|
+
gl.vertexAttribPointer(aDestRect, 4, gl.FLOAT, false, 32, 0)
|
|
168
|
+
gl.vertexAttribDivisor(aDestRect, 1)
|
|
169
|
+
|
|
170
|
+
const aTexInfo = gl.getAttribLocation(program, 'a_texInfo')
|
|
171
|
+
gl.enableVertexAttribArray(aTexInfo)
|
|
172
|
+
gl.vertexAttribPointer(aTexInfo, 4, gl.FLOAT, false, 32, 16)
|
|
173
|
+
gl.vertexAttribDivisor(aTexInfo, 1)
|
|
174
|
+
|
|
175
|
+
gl.bindVertexArray(null)
|
|
176
|
+
|
|
177
|
+
// Texture array
|
|
178
|
+
this._texArray = gl.createTexture()!
|
|
179
|
+
this._allocateTextureArray(256, 256, 32)
|
|
180
|
+
|
|
181
|
+
// Premultiplied-alpha blending
|
|
182
|
+
gl.enable(gl.BLEND)
|
|
183
|
+
gl.blendEquation(gl.FUNC_ADD)
|
|
184
|
+
gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA)
|
|
185
|
+
|
|
186
|
+
this._initialized = true
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
// ==========================================================================
|
|
190
|
+
// Texture management
|
|
191
|
+
// ==========================================================================
|
|
192
|
+
|
|
193
|
+
private _nextPow2(n: number): number {
|
|
194
|
+
n--; n |= n >> 1; n |= n >> 2; n |= n >> 4; n |= n >> 8; n |= n >> 16; return n + 1
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
private _allocateTextureArray(width: number, height: number, layers: number): void {
|
|
198
|
+
const gl = this._gl!
|
|
199
|
+
const w = this._nextPow2(Math.max(width, 64))
|
|
200
|
+
const h = this._nextPow2(Math.max(height, 64))
|
|
201
|
+
const l = Math.min(this._nextPow2(Math.max(layers, 16)), MAX_TEXTURE_ARRAY_LAYERS)
|
|
202
|
+
|
|
203
|
+
gl.bindTexture(gl.TEXTURE_2D_ARRAY, this._texArray)
|
|
204
|
+
gl.texImage3D(gl.TEXTURE_2D_ARRAY, 0, gl.RGBA8, w, h, l, 0, gl.RGBA, gl.UNSIGNED_BYTE, null)
|
|
205
|
+
gl.texParameteri(gl.TEXTURE_2D_ARRAY, gl.TEXTURE_MIN_FILTER, gl.NEAREST)
|
|
206
|
+
gl.texParameteri(gl.TEXTURE_2D_ARRAY, gl.TEXTURE_MAG_FILTER, gl.NEAREST)
|
|
207
|
+
gl.texParameteri(gl.TEXTURE_2D_ARRAY, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE)
|
|
208
|
+
gl.texParameteri(gl.TEXTURE_2D_ARRAY, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE)
|
|
209
|
+
|
|
210
|
+
this._texWidth = w
|
|
211
|
+
this._texHeight = h
|
|
212
|
+
this._texLayers = l
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
private _ensureTextureArray(maxW: number, maxH: number, count: number): void {
|
|
216
|
+
const c = Math.min(count, MAX_TEXTURE_ARRAY_LAYERS)
|
|
217
|
+
if (maxW <= this._texWidth && maxH <= this._texHeight && c <= this._texLayers) return
|
|
218
|
+
const newW = this._nextPow2(Math.max(this._texWidth, maxW))
|
|
219
|
+
const newH = this._nextPow2(Math.max(this._texHeight, maxH))
|
|
220
|
+
const newL = Math.min(
|
|
221
|
+
this._nextPow2(Math.max(this._texLayers, c, c + 16)),
|
|
222
|
+
MAX_TEXTURE_ARRAY_LAYERS
|
|
223
|
+
)
|
|
224
|
+
this._allocateTextureArray(newW, newH, newL)
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
// ==========================================================================
|
|
228
|
+
// Public interface
|
|
229
|
+
// ==========================================================================
|
|
230
|
+
|
|
231
|
+
async setCanvas(canvas: HTMLCanvasElement, width: number, height: number): Promise<void> {
|
|
232
|
+
await this.init()
|
|
233
|
+
if (width <= 0 || height <= 0) return
|
|
234
|
+
this._canvas = canvas
|
|
235
|
+
canvas.width = width
|
|
236
|
+
canvas.height = height
|
|
237
|
+
this._initGL()
|
|
238
|
+
this._gl!.viewport(0, 0, width, height)
|
|
239
|
+
this._lastCanvasWidth = width
|
|
240
|
+
this._lastCanvasHeight = height
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
updateSize(width: number, height: number): void {
|
|
244
|
+
if (!this._gl || !this._canvas || width <= 0 || height <= 0) return
|
|
245
|
+
if (width === this._lastCanvasWidth && height === this._lastCanvasHeight) return
|
|
246
|
+
this._canvas.width = width
|
|
247
|
+
this._canvas.height = height
|
|
248
|
+
this._gl.viewport(0, 0, width, height)
|
|
249
|
+
this._lastCanvasWidth = width
|
|
250
|
+
this._lastCanvasHeight = height
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
/**
|
|
254
|
+
* Render from ImageBitmaps (async render mode)
|
|
255
|
+
*/
|
|
256
|
+
renderBitmaps(
|
|
257
|
+
images: { image: ImageBitmap; x: number; y: number }[],
|
|
258
|
+
_canvasWidth: number,
|
|
259
|
+
_canvasHeight: number
|
|
260
|
+
): void {
|
|
261
|
+
if (!this._gl || !this._initialized) return
|
|
262
|
+
|
|
263
|
+
const len = images.length
|
|
264
|
+
if (len === 0) {
|
|
265
|
+
this.clear()
|
|
266
|
+
return
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
let maxW = 0, maxH = 0
|
|
270
|
+
for (let i = 0; i < len; i++) {
|
|
271
|
+
const { image } = images[i]
|
|
272
|
+
if (image.width > maxW) maxW = image.width
|
|
273
|
+
if (image.height > maxH) maxH = image.height
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
this._ensureTextureArray(maxW, maxH, Math.min(len, MAX_TEXTURE_ARRAY_LAYERS))
|
|
277
|
+
|
|
278
|
+
const gl = this._gl
|
|
279
|
+
gl.clearColor(0, 0, 0, 0)
|
|
280
|
+
gl.clear(gl.COLOR_BUFFER_BIT)
|
|
281
|
+
gl.useProgram(this._program)
|
|
282
|
+
gl.uniform2f(this._resolutionLoc, this._lastCanvasWidth, this._lastCanvasHeight)
|
|
283
|
+
gl.uniform2i(this._texArraySizeLoc, this._texWidth, this._texHeight)
|
|
284
|
+
gl.activeTexture(gl.TEXTURE0)
|
|
285
|
+
gl.bindTexture(gl.TEXTURE_2D_ARRAY, this._texArray)
|
|
286
|
+
gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, false)
|
|
287
|
+
|
|
288
|
+
const instanceData = this._instanceData
|
|
289
|
+
let imageIndex = 0
|
|
290
|
+
|
|
291
|
+
while (imageIndex < len) {
|
|
292
|
+
let count = 0
|
|
293
|
+
while (imageIndex < len && count < MAX_TEXTURE_ARRAY_LAYERS) {
|
|
294
|
+
const img = images[imageIndex++]
|
|
295
|
+
const w = img.image.width, h = img.image.height
|
|
296
|
+
if (w <= 0 || h <= 0) continue
|
|
297
|
+
gl.texSubImage3D(gl.TEXTURE_2D_ARRAY, 0, 0, 0, count, w, h, 1, gl.RGBA, gl.UNSIGNED_BYTE, img.image)
|
|
298
|
+
const off = count << 3
|
|
299
|
+
instanceData[off] = img.x
|
|
300
|
+
instanceData[off + 1] = img.y
|
|
301
|
+
instanceData[off + 2] = w
|
|
302
|
+
instanceData[off + 3] = h
|
|
303
|
+
instanceData[off + 4] = w
|
|
304
|
+
instanceData[off + 5] = h
|
|
305
|
+
instanceData[off + 6] = count
|
|
306
|
+
instanceData[off + 7] = 0
|
|
307
|
+
count++
|
|
308
|
+
}
|
|
309
|
+
if (count === 0) continue
|
|
310
|
+
gl.bindBuffer(gl.ARRAY_BUFFER, this._instanceBuffer)
|
|
311
|
+
gl.bufferSubData(gl.ARRAY_BUFFER, 0, instanceData, 0, count << 3)
|
|
312
|
+
gl.bindVertexArray(this._vao)
|
|
313
|
+
gl.drawArraysInstanced(gl.TRIANGLES, 0, 6, count)
|
|
314
|
+
gl.bindVertexArray(null)
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
/**
|
|
319
|
+
* Render from raw ArrayBuffer data (non-async render mode)
|
|
320
|
+
*/
|
|
321
|
+
render(
|
|
322
|
+
images: RenderImage[],
|
|
323
|
+
_canvasWidth: number,
|
|
324
|
+
_canvasHeight: number
|
|
325
|
+
): void {
|
|
326
|
+
if (!this._gl || !this._initialized) return
|
|
327
|
+
|
|
328
|
+
const len = images.length
|
|
329
|
+
if (len === 0) {
|
|
330
|
+
this.clear()
|
|
331
|
+
return
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
let maxW = 0, maxH = 0
|
|
335
|
+
for (let i = 0; i < len; i++) {
|
|
336
|
+
const { w, h } = images[i]
|
|
337
|
+
if (w > maxW) maxW = w
|
|
338
|
+
if (h > maxH) maxH = h
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
this._ensureTextureArray(maxW, maxH, Math.min(len, MAX_TEXTURE_ARRAY_LAYERS))
|
|
342
|
+
|
|
343
|
+
const gl = this._gl
|
|
344
|
+
gl.clearColor(0, 0, 0, 0)
|
|
345
|
+
gl.clear(gl.COLOR_BUFFER_BIT)
|
|
346
|
+
gl.useProgram(this._program)
|
|
347
|
+
gl.uniform2f(this._resolutionLoc, this._lastCanvasWidth, this._lastCanvasHeight)
|
|
348
|
+
gl.uniform2i(this._texArraySizeLoc, this._texWidth, this._texHeight)
|
|
349
|
+
gl.activeTexture(gl.TEXTURE0)
|
|
350
|
+
gl.bindTexture(gl.TEXTURE_2D_ARRAY, this._texArray)
|
|
351
|
+
gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, false)
|
|
352
|
+
|
|
353
|
+
const instanceData = this._instanceData
|
|
354
|
+
let imageIndex = 0
|
|
355
|
+
|
|
356
|
+
while (imageIndex < len) {
|
|
357
|
+
let count = 0
|
|
358
|
+
while (imageIndex < len && count < MAX_TEXTURE_ARRAY_LAYERS) {
|
|
359
|
+
const img = images[imageIndex++]
|
|
360
|
+
const w = img.w, h = img.h
|
|
361
|
+
if (w <= 0 || h <= 0) continue
|
|
362
|
+
const imgData = img.image
|
|
363
|
+
if (imgData instanceof ImageBitmap) {
|
|
364
|
+
gl.texSubImage3D(gl.TEXTURE_2D_ARRAY, 0, 0, 0, count, w, h, 1, gl.RGBA, gl.UNSIGNED_BYTE, imgData)
|
|
365
|
+
} else if (imgData instanceof ArrayBuffer) {
|
|
366
|
+
gl.texSubImage3D(gl.TEXTURE_2D_ARRAY, 0, 0, 0, count, w, h, 1, gl.RGBA, gl.UNSIGNED_BYTE, new Uint8Array(imgData))
|
|
367
|
+
}
|
|
368
|
+
const off = count << 3
|
|
369
|
+
instanceData[off] = img.x
|
|
370
|
+
instanceData[off + 1] = img.y
|
|
371
|
+
instanceData[off + 2] = w
|
|
372
|
+
instanceData[off + 3] = h
|
|
373
|
+
instanceData[off + 4] = w
|
|
374
|
+
instanceData[off + 5] = h
|
|
375
|
+
instanceData[off + 6] = count
|
|
376
|
+
instanceData[off + 7] = 0
|
|
377
|
+
count++
|
|
378
|
+
}
|
|
379
|
+
if (count === 0) continue
|
|
380
|
+
gl.bindBuffer(gl.ARRAY_BUFFER, this._instanceBuffer)
|
|
381
|
+
gl.bufferSubData(gl.ARRAY_BUFFER, 0, instanceData, 0, count << 3)
|
|
382
|
+
gl.bindVertexArray(this._vao)
|
|
383
|
+
gl.drawArraysInstanced(gl.TRIANGLES, 0, 6, count)
|
|
384
|
+
gl.bindVertexArray(null)
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
clear(): void {
|
|
389
|
+
if (!this._gl) return
|
|
390
|
+
this._gl.clearColor(0, 0, 0, 0)
|
|
391
|
+
this._gl.clear(this._gl.COLOR_BUFFER_BIT)
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
get initialized(): boolean {
|
|
395
|
+
return this._initialized
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
destroy(): void {
|
|
399
|
+
const gl = this._gl
|
|
400
|
+
if (gl) {
|
|
401
|
+
gl.deleteProgram(this._program)
|
|
402
|
+
gl.deleteVertexArray(this._vao)
|
|
403
|
+
gl.deleteBuffer(this._instanceBuffer)
|
|
404
|
+
gl.deleteTexture(this._texArray)
|
|
405
|
+
}
|
|
406
|
+
this._gl = null
|
|
407
|
+
this._program = null
|
|
408
|
+
this._vao = null
|
|
409
|
+
this._instanceBuffer = null
|
|
410
|
+
this._texArray = null
|
|
411
|
+
this._canvas = null
|
|
412
|
+
this._initialized = false
|
|
413
|
+
this._initPromise = null
|
|
414
|
+
}
|
|
415
|
+
}
|