@bigmistqke/view.gl 0.2.0 → 0.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.
Files changed (43) hide show
  1. package/README.md +82 -27
  2. package/dev/dist/404.html +19 -0
  3. package/dev/dist/assets/bezier-DrA4cU_H.js +292 -0
  4. package/dev/dist/assets/game-of-life-BYlzWhEL.js +186 -0
  5. package/dev/dist/assets/glsl-composition-D_BaFFij.js +97 -0
  6. package/dev/dist/assets/grid-DfXHNLBm.js +181 -0
  7. package/dev/dist/assets/index-C2N3u8wj.css +12 -0
  8. package/dev/dist/assets/index-D-N0FDYs.js +110 -0
  9. package/dev/dist/assets/main-CSE2fki3.js +32 -0
  10. package/dev/dist/assets/overview-IeUhUflE.js +80 -0
  11. package/dev/dist/assets/pix-sim-DMJJA_WO.js +500 -0
  12. package/dev/dist/assets/ray-casting-DTQetDPd.js +411 -0
  13. package/dev/dist/assets/ray-casting-modules-Dkfw4tdc.js +765 -0
  14. package/dev/dist/assets/sdf-CS0uBonx.js +66 -0
  15. package/dev/dist/assets/symbol-BL_7GJ0M.js +67 -0
  16. package/dev/dist/assets/tag-C0Vskpp8.js +833 -0
  17. package/dev/dist/assets/tetris-D8mokodr.js +344 -0
  18. package/dev/dist/assets/utils-2dzuv_bW.js +62 -0
  19. package/dev/dist/index.html +51 -0
  20. package/dist/chunk-BZDGEIOT.mjs +278 -0
  21. package/dist/chunk-BZDGEIOT.mjs.map +1 -0
  22. package/dist/{chunk-EYXOSM7B.mjs → chunk-X4M3LV4R.mjs} +60 -133
  23. package/dist/chunk-X4M3LV4R.mjs.map +1 -0
  24. package/dist/gl.d.mts +2 -2
  25. package/dist/gl.mjs +1 -1
  26. package/dist/index.d.mts +13 -4
  27. package/dist/index.mjs +7 -2
  28. package/dist/tag.d.mts +6 -1
  29. package/dist/tag.mjs +13 -5
  30. package/dist/tag.mjs.map +1 -1
  31. package/dist/{types-CyGyGi18.d.mts → types-CNdqh7tB.d.mts} +34 -4
  32. package/package.json +1 -1
  33. package/src/gl.ts +13 -2
  34. package/src/index.ts +55 -39
  35. package/src/tag.ts +18 -2
  36. package/src/types.ts +33 -3
  37. package/src/utils.ts +45 -10
  38. package/test/index.test.ts +111 -29
  39. package/test/mocks/webgl.ts +14 -0
  40. package/test/tag.test.ts +41 -0
  41. package/dist/chunk-CJTT7WPV.mjs +0 -75
  42. package/dist/chunk-CJTT7WPV.mjs.map +0 -1
  43. package/dist/chunk-EYXOSM7B.mjs.map +0 -1
package/src/types.ts CHANGED
@@ -365,6 +365,15 @@ export interface AttributeMethods<T extends AttributeDefinition = AttributeDefin
365
365
  */
366
366
  bind(): () => void
367
367
  set(data: FormatToArray[ResolvedFormat<T>], usage?: GLUsage): { bind(): () => void }
368
+ /**
369
+ * Delete the buffer, if this attribute is the one that made it.
370
+ *
371
+ * A buffer named by the schema came from somewhere else and is deleted by
372
+ * whoever made it — a shared buffer is the point of passing one in, and one
373
+ * view cannot know whether the others are done. So this is safe to call
374
+ * twice and safe to call on a borrowed buffer, and it is not a way to free
375
+ * one: dispose the view that created it.
376
+ */
368
377
  dispose(): void
369
378
  }
370
379
 
@@ -502,6 +511,12 @@ export interface TextureDefinition {
502
511
  export interface Framebuffer {
503
512
  texture: WebGLTexture
504
513
  framebuffer: WebGLFramebuffer
514
+ /**
515
+ * Delete the framebuffer, and the texture only if `createFramebuffer` made
516
+ * it. A texture handed in belongs to whoever made it. Idempotent, and exactly
517
+ * what aborting the signal does.
518
+ */
519
+ dispose(): void
505
520
  }
506
521
 
507
522
  /**********************************************************************************/
@@ -617,11 +632,16 @@ export interface ViewOptions {
617
632
  signal?: AbortSignal
618
633
  }
619
634
 
635
+ /**
636
+ * What a program has names for. Buffers are absent on purpose: a buffer has no
637
+ * name in the source, so it cannot be resolved against a program the way a
638
+ * uniform or an attribute is, and its lifetime is rarely a program's. Build one
639
+ * with `bufferView` and pass it to `vao()` alongside the attributes.
640
+ */
620
641
  export interface ViewSchema {
621
642
  uniforms?: UniformSchema
622
643
  attributes?: AttributeSchema
623
644
  interleavedAttributes?: InterleavedAttributeSchema
624
- buffers?: BufferSchema
625
645
  }
626
646
 
627
647
  type PartialSchema<T> =
@@ -631,7 +651,6 @@ export interface ViewSchemaPartial {
631
651
  uniforms?: PartialSchema<UniformSchema>
632
652
  attributes?: PartialSchema<AttributeSchema>
633
653
  interleavedAttributes?: PartialSchema<InterleavedAttributeSchema>
634
- buffers?: PartialSchema<BufferSchema>
635
654
  }
636
655
 
637
656
  export type View<T extends ViewSchema = ViewSchema> = {
@@ -641,7 +660,6 @@ export type View<T extends ViewSchema = ViewSchema> = {
641
660
  interleavedAttributes: T['interleavedAttributes'] extends InterleavedAttributeSchema
642
661
  ? Prettify<InterleavedAttributeView<T['interleavedAttributes']>>
643
662
  : {}
644
- buffers: T['buffers'] extends BufferSchema ? Prettify<BufferView<T['buffers']>> : {}
645
663
  uniforms: T['uniforms'] extends UniformSchema ? Prettify<UniformView<T['uniforms']>> : {}
646
664
  /**
647
665
  * Build a vertex array from the participants one draw needs — its attributes,
@@ -653,6 +671,13 @@ export type View<T extends ViewSchema = ViewSchema> = {
653
671
  * something you say rather than something you inherit from call order.
654
672
  */
655
673
  vao(participants: VertexArrayParticipant[]): VertexArrayMethods
674
+ /**
675
+ * Delete everything the view made: the buffers behind its attributes and
676
+ * layouts, and any vertex array still held from `vao()`. Buffers named by the
677
+ * schema are left alone — those belong to whoever made them. Idempotent, and
678
+ * exactly what aborting the signal does.
679
+ */
680
+ dispose(): void
656
681
  }
657
682
 
658
683
  /**********************************************************************************/
@@ -881,5 +906,10 @@ export type CompileResult<
881
906
  view: Prettify<View<TSchema>>
882
907
  vertex: string
883
908
  fragment: string
909
+ /**
910
+ * Delete the program and everything its view made. Idempotent, and
911
+ * exactly what aborting the signal does.
912
+ */
913
+ dispose(): void
884
914
  }
885
915
  : never
package/src/utils.ts CHANGED
@@ -6,6 +6,7 @@ import type {
6
6
  KIND_TO_UNIFORM_FN_NAME_MAP,
7
7
  TextureDefinition,
8
8
  UniformKind,
9
+ ViewOptions,
9
10
  } from './types'
10
11
 
11
12
  export function assertedNotNullish<T>(value: T, message?: string): NonNullable<T> {
@@ -13,6 +14,24 @@ export function assertedNotNullish<T>(value: T, message?: string): NonNullable<T
13
14
  return value
14
15
  }
15
16
 
17
+ /**
18
+ * Wraps a teardown so it runs once.
19
+ *
20
+ * Two reasons, and both bite. A disposer is per `bind()`, and undoing twice
21
+ * would put back state a later `bind()` is relying on. And a `dispose()` that
22
+ * deletes twice invalidates a handle someone else may still be holding — the
23
+ * object survives while it is bound, so the fault surfaces later, somewhere
24
+ * else. Idempotent is the only safe shape for either.
25
+ */
26
+ export function once(teardown: () => void): () => void {
27
+ let done = false
28
+ return () => {
29
+ if (done) return
30
+ done = true
31
+ teardown()
32
+ }
33
+ }
34
+
16
35
  /**********************************************************************************/
17
36
  /* */
18
37
  /* Kind To Uniform Name */
@@ -114,8 +133,9 @@ export function createTexture(
114
133
  height,
115
134
  }: TextureDefinition,
116
135
  data?: ArrayBufferView | null,
136
+ { signal }: ViewOptions = {},
117
137
  ): WebGLTexture {
118
- const texture = gl.createTexture()
138
+ const texture = assertedNotNullish(gl.createTexture(), 'Failed to create texture')
119
139
 
120
140
  function getTextureConstant(name: string) {
121
141
  if (!(name in gl)) {
@@ -137,13 +157,17 @@ export function createTexture(
137
157
  data ?? null,
138
158
  )
139
159
 
140
- console.log(minFilter)
141
160
  // Set texture parameters
142
161
  gl.texParameteri(gl[target], gl.TEXTURE_MIN_FILTER, gl[minFilter])
143
162
  gl.texParameteri(gl[target], gl.TEXTURE_MAG_FILTER, gl[magFilter])
144
163
  gl.texParameteri(gl[target], gl.TEXTURE_WRAP_S, gl[wrapS])
145
164
  gl.texParameteri(gl[target], gl.TEXTURE_WRAP_T, gl[wrapT])
146
165
 
166
+ signal?.addEventListener(
167
+ 'abort',
168
+ once(() => gl.deleteTexture(texture)),
169
+ )
170
+
147
171
  return texture
148
172
  }
149
173
 
@@ -162,7 +186,7 @@ const FRAMEBUFFER_ATTACHMENT_MAP = {
162
186
 
163
187
  class FramebufferError extends Error {
164
188
  constructor(gl: GL, status: number) {
165
- let errorMessage = `Framebuffer '${name}' not complete. Status: `
189
+ let errorMessage = 'Framebuffer not complete. Status: '
166
190
  switch (status) {
167
191
  case gl.FRAMEBUFFER_INCOMPLETE_ATTACHMENT:
168
192
  errorMessage += 'FRAMEBUFFER_INCOMPLETE_ATTACHMENT'
@@ -185,16 +209,15 @@ class FramebufferError extends Error {
185
209
 
186
210
  export function createFramebuffer(
187
211
  gl: GL,
188
- { attachment, texture, ...options }: FramebufferDefinition,
212
+ { attachment, texture: providedTexture, ...definition }: FramebufferDefinition,
213
+ { signal }: ViewOptions = {},
189
214
  ) {
190
- // Create texture for the framebuffer
191
- texture ??= createTexture(gl, options)
215
+ // Create texture for the framebuffer, unless one was handed in. Which one it
216
+ // is decides who deletes it below — the same rule the attribute buffers follow.
217
+ const texture = providedTexture ?? createTexture(gl, definition)
192
218
 
193
219
  // Create framebuffer
194
- const framebuffer = assertedNotNullish(
195
- gl.createFramebuffer(),
196
- `Failed to create framebuffer: ${name}`,
197
- )
220
+ const framebuffer = assertedNotNullish(gl.createFramebuffer(), 'Failed to create framebuffer')
198
221
 
199
222
  gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffer)
200
223
 
@@ -213,9 +236,21 @@ export function createFramebuffer(
213
236
  throw new FramebufferError(gl, status)
214
237
  }
215
238
 
239
+ const dispose = once(() => {
240
+ gl.deleteFramebuffer(framebuffer)
241
+ // A texture handed in belongs to whoever made it — it commonly outlives the
242
+ // framebuffer, ping-ponged between two of them.
243
+ if (!providedTexture) {
244
+ gl.deleteTexture(texture)
245
+ }
246
+ })
247
+
248
+ signal?.addEventListener('abort', dispose)
249
+
216
250
  return {
217
251
  texture,
218
252
  framebuffer,
253
+ dispose,
219
254
  }
220
255
  }
221
256
 
@@ -2,6 +2,8 @@ import { beforeEach, describe, expect, it, vi } from 'vitest'
2
2
  import {
3
3
  attributeView,
4
4
  bufferView,
5
+ createFramebuffer,
6
+ createTexture,
5
7
  interleavedAttributeView,
6
8
  uniformView,
7
9
  vaoView,
@@ -45,7 +47,6 @@ describe('view', () => {
45
47
  expect(result.uniforms!.u_texture.set).toBeInstanceOf(Function)
46
48
  expect(result.attributes).toBeUndefined()
47
49
  expect(result.interleavedAttributes).toBeUndefined()
48
- expect(result.buffers).toBeUndefined()
49
50
  })
50
51
 
51
52
  it('should create a view with attributes', () => {
@@ -91,23 +92,6 @@ describe('view', () => {
91
92
  expect(result.interleavedAttributes!.data.buffer.set).toBeInstanceOf(Function)
92
93
  })
93
94
 
94
- it('should create a view with buffers', () => {
95
- const schema = {
96
- buffers: {
97
- vertices: { target: 'ARRAY_BUFFER', usage: 'STATIC_DRAW' },
98
- indices: { target: 'ELEMENT_ARRAY_BUFFER', usage: 'DYNAMIC_DRAW' },
99
- },
100
- } satisfies ViewSchema
101
-
102
- const result = view(gl, program, schema)
103
-
104
- expect(result.buffers).toBeDefined()
105
- expect(result.buffers!.vertices).toBeDefined()
106
- expect(result.buffers!.vertices.bind).toBeInstanceOf(Function)
107
- expect(result.buffers!.vertices.dispose).toBeInstanceOf(Function)
108
- expect(result.buffers!.vertices.set).toBeInstanceOf(Function)
109
- })
110
-
111
95
  it('should create a view with all components', () => {
112
96
  const schema = {
113
97
  uniforms: {
@@ -122,9 +106,6 @@ describe('view', () => {
122
106
  instanced: false,
123
107
  },
124
108
  },
125
- buffers: {
126
- vertices: {},
127
- },
128
109
  } satisfies ViewSchema
129
110
 
130
111
  const result = view(gl, program, schema)
@@ -132,14 +113,12 @@ describe('view', () => {
132
113
  expect(result.uniforms).toBeDefined()
133
114
  expect(result.attributes).toBeDefined()
134
115
  expect(result.interleavedAttributes).toBeDefined()
135
- expect(result.buffers).toBeDefined()
136
116
  })
137
117
 
138
118
  it('should create a view with symbols for all component types', () => {
139
119
  const u_time_symbol = Symbol('u_time')
140
120
  const a_position_symbol = Symbol('a_position')
141
121
  const data_symbol = Symbol('data')
142
- const vertices_symbol = Symbol('vertices')
143
122
 
144
123
  const schema = {
145
124
  uniforms: {
@@ -154,9 +133,6 @@ describe('view', () => {
154
133
  instanced: false,
155
134
  },
156
135
  },
157
- buffers: {
158
- [vertices_symbol]: {},
159
- },
160
136
  } satisfies ViewSchema
161
137
 
162
138
  const result = view(gl, program, schema)
@@ -169,9 +145,6 @@ describe('view', () => {
169
145
 
170
146
  expect(result.interleavedAttributes![data_symbol]).toBeDefined()
171
147
  expect(result.interleavedAttributes![data_symbol].buffer.bind).toBeInstanceOf(Function)
172
-
173
- expect(result.buffers![vertices_symbol]).toBeDefined()
174
- expect(result.buffers![vertices_symbol].bind).toBeInstanceOf(Function)
175
148
  })
176
149
 
177
150
  it('should create a view with mixed string and symbol keys', () => {
@@ -201,6 +174,60 @@ describe('view', () => {
201
174
  })
202
175
  })
203
176
 
177
+ describe('disposing a view', () => {
178
+ let gl: WebGL2RenderingContext
179
+ let program: WebGLProgram
180
+ beforeEach(() => {
181
+ const mock = createMockCanvas()
182
+ gl = mock.gl
183
+ program = gl.createProgram()!
184
+ })
185
+
186
+ const schema = {
187
+ attributes: { a_position: { kind: 'vec2' } },
188
+ interleavedAttributes: { data: { layout: [{ key: 'a_uv', kind: 'vec2' }] } },
189
+ } satisfies ViewSchema
190
+
191
+ it('deletes the buffers it made, and the arrays it handed out', () => {
192
+ const result = view(gl, program, schema)
193
+ const vertexArray = result.vao([result.attributes.a_position])
194
+
195
+ result.dispose()
196
+
197
+ expect(gl.deleteBuffer).toHaveBeenCalledTimes(2)
198
+ expect(gl.deleteVertexArray).toHaveBeenCalledTimes(1)
199
+ void vertexArray
200
+ })
201
+
202
+ it('does the same thing on abort as it does on dispose', () => {
203
+ const controller = new AbortController()
204
+ const result = view(gl, program, schema, { signal: controller.signal })
205
+ result.vao([result.attributes.a_position])
206
+
207
+ controller.abort()
208
+ const afterAbort = (gl.deleteBuffer as any).mock.calls.length
209
+
210
+ // Both routes lead to the same teardown, so taking both must not delete
211
+ // twice: the second delete would invalidate a handle someone else holds.
212
+ result.dispose()
213
+ expect((gl.deleteBuffer as any).mock.calls).toHaveLength(afterAbort)
214
+ expect(gl.deleteVertexArray).toHaveBeenCalledTimes(1)
215
+ })
216
+
217
+ it('forgets a vertex array the caller disposed itself', () => {
218
+ const result = view(gl, program, schema)
219
+ const vertexArray = result.vao([result.attributes.a_position])
220
+
221
+ vertexArray.dispose()
222
+ vertexArray.dispose()
223
+ result.dispose()
224
+
225
+ // Held only until disposed, so a view handing out an array per frame does
226
+ // not accumulate them — and disposing one twice deletes it once.
227
+ expect(gl.deleteVertexArray).toHaveBeenCalledTimes(1)
228
+ })
229
+ })
230
+
204
231
  describe('uniformView', () => {
205
232
  let gl: ReturnType<typeof createMockGL>
206
233
  let program: WebGLProgram
@@ -482,6 +509,28 @@ describe('attributeView', () => {
482
509
  expect(gl.createBuffer).toHaveBeenCalledTimes(1) // Only for the custom buffer
483
510
  })
484
511
 
512
+ it('does not delete a buffer it did not create', () => {
513
+ const customBuffer = gl.createBuffer()!
514
+ const controller = new AbortController()
515
+ const attributes = attributeView(
516
+ gl,
517
+ program,
518
+ {
519
+ a_position: { kind: 'vec2', buffer: customBuffer },
520
+ a_normal: { kind: 'vec2' },
521
+ } satisfies AttributeSchema,
522
+ { signal: controller.signal },
523
+ )
524
+ const own = attributes.a_normal.buffer
525
+
526
+ controller.abort()
527
+
528
+ // A schema can name a buffer another view owns — compile.toQuad shares one
529
+ // across every program on a context — and disposing here must not take it.
530
+ expect(gl.deleteBuffer).not.toHaveBeenCalledWith(customBuffer)
531
+ expect(gl.deleteBuffer).toHaveBeenCalledWith(own)
532
+ })
533
+
485
534
  it('should handle float attributes', () => {
486
535
  const schema = {
487
536
  a_scalar: { kind: 'float' },
@@ -1445,3 +1494,36 @@ describe('the constant source', () => {
1445
1494
  expect(() => a_instance.constant).toThrow(/cannot be a constant attribute/)
1446
1495
  })
1447
1496
  })
1497
+
1498
+ describe('createFramebuffer', () => {
1499
+ let gl: ReturnType<typeof createMockGL>
1500
+
1501
+ beforeEach(() => {
1502
+ gl = createMockGL()
1503
+ gl.checkFramebufferStatus = vi.fn(() => gl.FRAMEBUFFER_COMPLETE) as any
1504
+ })
1505
+
1506
+ const definition = { attachment: 'color', width: 4, height: 4 } as const
1507
+
1508
+ it('deletes the texture it made', () => {
1509
+ const controller = new AbortController()
1510
+ const { texture } = createFramebuffer(gl, definition, { signal: controller.signal })
1511
+
1512
+ controller.abort()
1513
+
1514
+ expect(gl.deleteTexture).toHaveBeenCalledWith(texture)
1515
+ })
1516
+
1517
+ it('leaves a texture it was handed', () => {
1518
+ // Ping-ponged textures outlive the framebuffers pointing at them, so the
1519
+ // one case that matters is the one where the texture came from outside.
1520
+ const texture = createTexture(gl, { width: 4, height: 4 })
1521
+ const target = createFramebuffer(gl, { ...definition, texture })
1522
+
1523
+ target.dispose()
1524
+ target.dispose()
1525
+
1526
+ expect(gl.deleteFramebuffer).toHaveBeenCalledTimes(1)
1527
+ expect(gl.deleteTexture).not.toHaveBeenCalled()
1528
+ })
1529
+ })
@@ -64,6 +64,20 @@ export function createMockGL() {
64
64
  LINEAR: 0x2601,
65
65
  TEXTURE_MAG_FILTER: 0x2800,
66
66
  TEXTURE_MIN_FILTER: 0x2801,
67
+ TEXTURE_WRAP_S: 0x2802,
68
+ TEXTURE_WRAP_T: 0x2803,
69
+ CLAMP_TO_EDGE: 0x812f,
70
+ REPEAT: 0x2901,
71
+ RGBA: 0x1908,
72
+ RGB: 0x1907,
73
+ DEPTH_ATTACHMENT: 0x8d00,
74
+ STENCIL_ATTACHMENT: 0x8d20,
75
+ DEPTH_STENCIL_ATTACHMENT: 0x821a,
76
+ FRAMEBUFFER_COMPLETE: 0x8cd5,
77
+ FRAMEBUFFER_INCOMPLETE_ATTACHMENT: 0x8cd6,
78
+ FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT: 0x8cd7,
79
+ FRAMEBUFFER_INCOMPLETE_DIMENSIONS: 0x8cd9,
80
+ FRAMEBUFFER_UNSUPPORTED: 0x8cdd,
67
81
 
68
82
  // Buffer methods
69
83
  createBuffer: vi.fn(() => {
package/test/tag.test.ts CHANGED
@@ -1205,6 +1205,47 @@ describe('compile', () => {
1205
1205
  gl.createProgram = vi.fn(() => mockProgram)
1206
1206
  })
1207
1207
 
1208
+ it('disposes the same way whether by hand or by signal', () => {
1209
+ const vertex = glsl`${attribute.vec2('a_position')}
1210
+ void main() { gl_Position = vec4(a_position, 0.0, 1.0); }`
1211
+ const fragment = glsl`void main() {}`
1212
+ gl.deleteProgram = vi.fn()
1213
+ gl.deleteBuffer = vi.fn()
1214
+
1215
+ const controller = new AbortController()
1216
+ const result = compile(gl as WebGLRenderingContext, vertex, fragment, {
1217
+ signal: controller.signal,
1218
+ })
1219
+
1220
+ controller.abort()
1221
+ result.dispose()
1222
+
1223
+ // Idempotent across both routes: the program and the view's own buffer go
1224
+ // exactly once, however many times teardown is asked for.
1225
+ expect(gl.deleteProgram).toHaveBeenCalledTimes(1)
1226
+ expect(gl.deleteBuffer).toHaveBeenCalledTimes(1)
1227
+ })
1228
+
1229
+ it('deletes the program it created, and nothing it borrowed', () => {
1230
+ const vertex = glsl`${attribute.vec2('a_position')}
1231
+ void main() { gl_Position = vec4(a_position, 0.0, 1.0); }`
1232
+ const fragment = glsl`void main() {}`
1233
+ const borrowed = gl.createBuffer()!
1234
+ gl.deleteProgram = vi.fn()
1235
+ gl.deleteBuffer = vi.fn()
1236
+
1237
+ const controller = new AbortController()
1238
+ compile(gl as WebGLRenderingContext, vertex, fragment, {
1239
+ signal: controller.signal,
1240
+ schema: { attributes: { a_position: { buffer: borrowed } } },
1241
+ })
1242
+
1243
+ controller.abort()
1244
+
1245
+ expect(gl.deleteProgram).toHaveBeenCalledWith(mockProgram)
1246
+ expect(gl.deleteBuffer).not.toHaveBeenCalledWith(borrowed)
1247
+ })
1248
+
1208
1249
  it('should compile shaders and return program with schema', () => {
1209
1250
  const vertex = glsl`
1210
1251
  ${attribute.vec2('a_position')}
@@ -1,75 +0,0 @@
1
- // src/gl.ts
2
- function isWebGL2RenderingContext(gl) {
3
- return typeof WebGL2RenderingContext !== "undefined" && gl instanceof WebGL2RenderingContext;
4
- }
5
- function createShader(gl, type, source) {
6
- const shader = gl.createShader(type);
7
- if (!shader) throw new Error("Failed to create shader");
8
- gl.shaderSource(shader, source);
9
- gl.compileShader(shader);
10
- if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
11
- const info = gl.getShaderInfoLog(shader);
12
- gl.deleteShader(shader);
13
- throw new Error(
14
- `Failed to compile ${type === gl.VERTEX_SHADER ? "vertex" : "fragment"} shader: ${info}`
15
- );
16
- }
17
- return shader;
18
- }
19
- function createProgram(gl, vertexSource, fragmentSource) {
20
- const program = gl.createProgram();
21
- if (!program) throw new Error("Failed to create WebGL program");
22
- const vertexShader = createShader(gl, gl.VERTEX_SHADER, vertexSource);
23
- const fragmentShader = createShader(gl, gl.FRAGMENT_SHADER, fragmentSource);
24
- gl.attachShader(program, vertexShader);
25
- gl.attachShader(program, fragmentShader);
26
- gl.linkProgram(program);
27
- if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
28
- const info = gl.getProgramInfoLog(program);
29
- gl.deleteProgram(program);
30
- gl.deleteShader(vertexShader);
31
- gl.deleteShader(fragmentShader);
32
- throw new Error(`Failed to link program: ${info}`);
33
- }
34
- gl.deleteShader(vertexShader);
35
- gl.deleteShader(fragmentShader);
36
- return program;
37
- }
38
- var INSTANCED_ARRAYS_WRAPPER_MAP = /* @__PURE__ */ new WeakMap();
39
- function getInstancedArrays(gl) {
40
- if (isWebGL2RenderingContext(gl)) return gl;
41
- const cached = INSTANCED_ARRAYS_WRAPPER_MAP.get(gl);
42
- if (cached) return cached;
43
- const ext = gl.getExtension("ANGLE_instanced_arrays");
44
- if (!ext) return void 0;
45
- const wrapper = {
46
- drawArraysInstanced: ext.drawArraysInstancedANGLE.bind(ext),
47
- drawElementsInstanced: ext.drawElementsInstancedANGLE.bind(ext),
48
- vertexAttribDivisor: ext.vertexAttribDivisorANGLE.bind(ext)
49
- };
50
- INSTANCED_ARRAYS_WRAPPER_MAP.set(gl, wrapper);
51
- return wrapper;
52
- }
53
- var VERTEX_ARRAY_OBJECT_WRAPPER_MAP = /* @__PURE__ */ new WeakMap();
54
- function getVertexArrayObject(gl) {
55
- if (isWebGL2RenderingContext(gl)) return gl;
56
- const cached = VERTEX_ARRAY_OBJECT_WRAPPER_MAP.get(gl);
57
- if (cached) return cached;
58
- const ext = gl.getExtension("OES_vertex_array_object");
59
- if (!ext) return null;
60
- const wrapper = {
61
- bindVertexArray: ext.bindVertexArrayOES.bind(ext),
62
- createVertexArray: ext.createVertexArrayOES.bind(ext),
63
- deleteVertexArray: ext.deleteVertexArrayOES.bind(ext)
64
- };
65
- VERTEX_ARRAY_OBJECT_WRAPPER_MAP.set(gl, wrapper);
66
- return wrapper;
67
- }
68
-
69
- export {
70
- createShader,
71
- createProgram,
72
- getInstancedArrays,
73
- getVertexArrayObject
74
- };
75
- //# sourceMappingURL=chunk-CJTT7WPV.mjs.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/gl.ts"],"sourcesContent":["import { RemoveSuffix } from './type-utils'\nimport type { GL } from './types'\n\nfunction isWebGL2RenderingContext(\n gl: WebGLRenderingContext | WebGL2RenderingContext,\n): gl is WebGL2RenderingContext {\n return typeof WebGL2RenderingContext !== 'undefined' && gl instanceof WebGL2RenderingContext\n}\n\n/**********************************************************************************/\n/* */\n/* Create Shader */\n/* */\n/**********************************************************************************/\n\nexport function createShader(gl: GL, type: number, source: string): WebGLShader {\n const shader = gl.createShader(type)\n if (!shader) throw new Error('Failed to create shader')\n\n gl.shaderSource(shader, source)\n gl.compileShader(shader)\n\n if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {\n const info = gl.getShaderInfoLog(shader)\n gl.deleteShader(shader)\n throw new Error(\n `Failed to compile ${type === gl.VERTEX_SHADER ? 'vertex' : 'fragment'} shader: ${info}`,\n )\n }\n\n return shader\n}\n\n/**********************************************************************************/\n/* */\n/* Create Program */\n/* */\n/**********************************************************************************/\n\nexport function createProgram(gl: GL, vertexSource: string, fragmentSource: string): WebGLProgram {\n const program = gl.createProgram()\n if (!program) throw new Error('Failed to create WebGL program')\n\n const vertexShader = createShader(gl, gl.VERTEX_SHADER, vertexSource)\n const fragmentShader = createShader(gl, gl.FRAGMENT_SHADER, fragmentSource)\n\n gl.attachShader(program, vertexShader)\n gl.attachShader(program, fragmentShader)\n gl.linkProgram(program)\n\n if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {\n const info = gl.getProgramInfoLog(program)\n gl.deleteProgram(program)\n gl.deleteShader(vertexShader)\n gl.deleteShader(fragmentShader)\n throw new Error(`Failed to link program: ${info}`)\n }\n\n // Clean up shaders after linking\n gl.deleteShader(vertexShader)\n gl.deleteShader(fragmentShader)\n\n return program\n}\n\n/**********************************************************************************/\n/* */\n/* Get Instanced Arrays */\n/* */\n/**********************************************************************************/\n\nconst INSTANCED_ARRAYS_WRAPPER_MAP = new WeakMap<\n GL,\n RemoveSuffix<\n Pick<\n ANGLE_instanced_arrays,\n 'drawArraysInstancedANGLE' | 'drawElementsInstancedANGLE' | 'vertexAttribDivisorANGLE'\n >,\n 'ANGLE'\n > | null\n>()\nexport function getInstancedArrays(gl: GL) {\n if (isWebGL2RenderingContext(gl)) return gl\n\n const cached = INSTANCED_ARRAYS_WRAPPER_MAP.get(gl)\n if (cached) return cached\n\n const ext = gl.getExtension('ANGLE_instanced_arrays')\n if (!ext) return undefined\n\n const wrapper = {\n drawArraysInstanced: ext.drawArraysInstancedANGLE.bind(ext),\n drawElementsInstanced: ext.drawElementsInstancedANGLE.bind(ext),\n vertexAttribDivisor: ext.vertexAttribDivisorANGLE.bind(ext),\n }\n INSTANCED_ARRAYS_WRAPPER_MAP.set(gl, wrapper)\n\n return wrapper\n}\n\n/**********************************************************************************/\n/* */\n/* Get Vertex Array Object */\n/* */\n/**********************************************************************************/\n\nconst VERTEX_ARRAY_OBJECT_WRAPPER_MAP = new WeakMap<\n GL,\n RemoveSuffix<\n Pick<\n OES_vertex_array_object,\n 'bindVertexArrayOES' | 'createVertexArrayOES' | 'deleteVertexArrayOES'\n >,\n 'OES'\n > | null\n>()\nexport function getVertexArrayObject(gl: GL) {\n if (isWebGL2RenderingContext(gl)) return gl\n\n const cached = VERTEX_ARRAY_OBJECT_WRAPPER_MAP.get(gl)\n if (cached) return cached\n\n const ext = gl.getExtension('OES_vertex_array_object')\n if (!ext) return null\n\n const wrapper = {\n bindVertexArray: ext.bindVertexArrayOES.bind(ext),\n createVertexArray: ext.createVertexArrayOES.bind(ext),\n deleteVertexArray: ext.deleteVertexArrayOES.bind(ext),\n }\n VERTEX_ARRAY_OBJECT_WRAPPER_MAP.set(gl, wrapper)\n\n return wrapper\n}\n"],"mappings":";AAGA,SAAS,yBACP,IAC8B;AAC9B,SAAO,OAAO,2BAA2B,eAAe,cAAc;AACxE;AAQO,SAAS,aAAa,IAAQ,MAAc,QAA6B;AAC9E,QAAM,SAAS,GAAG,aAAa,IAAI;AACnC,MAAI,CAAC,OAAQ,OAAM,IAAI,MAAM,yBAAyB;AAEtD,KAAG,aAAa,QAAQ,MAAM;AAC9B,KAAG,cAAc,MAAM;AAEvB,MAAI,CAAC,GAAG,mBAAmB,QAAQ,GAAG,cAAc,GAAG;AACrD,UAAM,OAAO,GAAG,iBAAiB,MAAM;AACvC,OAAG,aAAa,MAAM;AACtB,UAAM,IAAI;AAAA,MACR,qBAAqB,SAAS,GAAG,gBAAgB,WAAW,UAAU,YAAY,IAAI;AAAA,IACxF;AAAA,EACF;AAEA,SAAO;AACT;AAQO,SAAS,cAAc,IAAQ,cAAsB,gBAAsC;AAChG,QAAM,UAAU,GAAG,cAAc;AACjC,MAAI,CAAC,QAAS,OAAM,IAAI,MAAM,gCAAgC;AAE9D,QAAM,eAAe,aAAa,IAAI,GAAG,eAAe,YAAY;AACpE,QAAM,iBAAiB,aAAa,IAAI,GAAG,iBAAiB,cAAc;AAE1E,KAAG,aAAa,SAAS,YAAY;AACrC,KAAG,aAAa,SAAS,cAAc;AACvC,KAAG,YAAY,OAAO;AAEtB,MAAI,CAAC,GAAG,oBAAoB,SAAS,GAAG,WAAW,GAAG;AACpD,UAAM,OAAO,GAAG,kBAAkB,OAAO;AACzC,OAAG,cAAc,OAAO;AACxB,OAAG,aAAa,YAAY;AAC5B,OAAG,aAAa,cAAc;AAC9B,UAAM,IAAI,MAAM,2BAA2B,IAAI,EAAE;AAAA,EACnD;AAGA,KAAG,aAAa,YAAY;AAC5B,KAAG,aAAa,cAAc;AAE9B,SAAO;AACT;AAQA,IAAM,+BAA+B,oBAAI,QASvC;AACK,SAAS,mBAAmB,IAAQ;AACzC,MAAI,yBAAyB,EAAE,EAAG,QAAO;AAEzC,QAAM,SAAS,6BAA6B,IAAI,EAAE;AAClD,MAAI,OAAQ,QAAO;AAEnB,QAAM,MAAM,GAAG,aAAa,wBAAwB;AACpD,MAAI,CAAC,IAAK,QAAO;AAEjB,QAAM,UAAU;AAAA,IACd,qBAAqB,IAAI,yBAAyB,KAAK,GAAG;AAAA,IAC1D,uBAAuB,IAAI,2BAA2B,KAAK,GAAG;AAAA,IAC9D,qBAAqB,IAAI,yBAAyB,KAAK,GAAG;AAAA,EAC5D;AACA,+BAA6B,IAAI,IAAI,OAAO;AAE5C,SAAO;AACT;AAQA,IAAM,kCAAkC,oBAAI,QAS1C;AACK,SAAS,qBAAqB,IAAQ;AAC3C,MAAI,yBAAyB,EAAE,EAAG,QAAO;AAEzC,QAAM,SAAS,gCAAgC,IAAI,EAAE;AACrD,MAAI,OAAQ,QAAO;AAEnB,QAAM,MAAM,GAAG,aAAa,yBAAyB;AACrD,MAAI,CAAC,IAAK,QAAO;AAEjB,QAAM,UAAU;AAAA,IACd,iBAAiB,IAAI,mBAAmB,KAAK,GAAG;AAAA,IAChD,mBAAmB,IAAI,qBAAqB,KAAK,GAAG;AAAA,IACpD,mBAAmB,IAAI,qBAAqB,KAAK,GAAG;AAAA,EACtD;AACA,kCAAgC,IAAI,IAAI,OAAO;AAE/C,SAAO;AACT;","names":[]}