@bigmistqke/view.gl 0.1.17 → 0.1.19

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 CHANGED
@@ -204,6 +204,18 @@ attributes.instanceOffset.set(instanceData).bind()
204
204
  gl.drawArraysInstanced(gl.TRIANGLES, 0, 3, 100)
205
205
  ```
206
206
 
207
+ `bind()` returns a disposer that restores the state it changed — here the
208
+ instancing divisor, which belongs to the attribute location rather than to the
209
+ program, and so is inherited by whatever binds there next. Ignore it when the
210
+ view owns its locations for the lifetime of the program; call it when they are
211
+ shared:
212
+
213
+ ```typescript
214
+ const unbind = attributes.instanceOffset.set(instanceData).bind()
215
+ gl.drawArraysInstanced(gl.TRIANGLES, 0, 3, 100)
216
+ unbind()
217
+ ```
218
+
207
219
  ##### 📋 AttributeSchema
208
220
 
209
221
  A mapping of attribute names to their configuration.
@@ -258,6 +270,16 @@ const interleavedAttributes = interleavedAttributeView(gl, program, {
258
270
  interleavedAttributes.vertexData.set(interleavedVertexData).bind()
259
271
  ```
260
272
 
273
+ Where vertex array objects are available this binds one, and it stays selected
274
+ for every later draw — which would then write its own attributes into it. The
275
+ disposer returned by `bind()` selects the previous array again:
276
+
277
+ ```typescript
278
+ const unbind = interleavedAttributes.vertexData.bind()
279
+ gl.drawElements(gl.TRIANGLES, count, gl.UNSIGNED_SHORT, 0)
280
+ unbind()
281
+ ```
282
+
261
283
  ##### 📋 InterleavedAttributeSchema
262
284
 
263
285
  A mapping of interleaved buffer names to their layout configuration. Each layout defines multiple attributes packed into a single buffer.
@@ -296,6 +318,10 @@ const buffers = bufferView(gl, {
296
318
 
297
319
  buffers.indices.set(indexData).bind()
298
320
  buffers.data.set(dynamicData).bind()
321
+
322
+ // bind() returns a disposer restoring the previous binding for that target.
323
+ // Worth calling for ELEMENT_ARRAY_BUFFER, whose binding is recorded in the
324
+ // bound vertex array object.
299
325
  ```
300
326
 
301
327
  ##### 📋 BufferSchema
@@ -72,4 +72,4 @@ export {
72
72
  getInstancedArrays,
73
73
  getVertexArrayObject
74
74
  };
75
- //# sourceMappingURL=chunk-WSNE4JVQ.mjs.map
75
+ //# sourceMappingURL=chunk-CJTT7WPV.mjs.map
@@ -0,0 +1 @@
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":[]}
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  getInstancedArrays,
3
3
  getVertexArrayObject
4
- } from "./chunk-WSNE4JVQ.mjs";
4
+ } from "./chunk-CJTT7WPV.mjs";
5
5
 
6
6
  // src/utils.ts
7
7
  function assertedNotNullish(value, message) {
@@ -164,34 +164,85 @@ function uniformView(gl, program, schema) {
164
164
  };
165
165
  });
166
166
  }
167
- function handleAttribute(gl, location, size, stride, offset, type, instanced) {
167
+ var FORMAT_TO_GL_TYPE = {
168
+ float32: "FLOAT",
169
+ int32: "INT",
170
+ uint32: "UNSIGNED_INT",
171
+ int16: "SHORT",
172
+ uint16: "UNSIGNED_SHORT",
173
+ int8: "BYTE",
174
+ uint8: "UNSIGNED_BYTE"
175
+ };
176
+ var FORMAT_BYTE_SIZE = {
177
+ float32: 4,
178
+ int32: 4,
179
+ uint32: 4,
180
+ int16: 2,
181
+ uint16: 2,
182
+ int8: 1,
183
+ uint8: 1
184
+ };
185
+ function defaultFormat(kind) {
186
+ if (kind.startsWith("u")) return "uint32";
187
+ if (kind.startsWith("i")) return "int32";
188
+ return "float32";
189
+ }
190
+ function handleAttribute(gl, location, size, stride, offset, glType, isIntegerKind, normalized, instanced) {
168
191
  gl.enableVertexAttribArray(location);
169
- if (type === "INT" || type === "UNSIGNED_INT") {
192
+ if (isIntegerKind) {
170
193
  ;
171
- gl.vertexAttribIPointer(location, size, gl[type], stride, offset);
194
+ gl.vertexAttribIPointer(location, size, gl[glType], stride, offset);
172
195
  } else {
173
- gl.vertexAttribPointer(location, size, gl[type], false, stride, offset);
196
+ gl.vertexAttribPointer(location, size, gl[glType], normalized, stride, offset);
174
197
  }
198
+ const instancedArrays = getInstancedArrays(gl);
175
199
  if (instanced) {
176
- assertedNotNullish(getInstancedArrays(gl)).vertexAttribDivisor(location, 1);
200
+ assertedNotNullish(instancedArrays).vertexAttribDivisor(location, 1);
201
+ } else if (instancedArrays) {
202
+ instancedArrays.vertexAttribDivisor(location, 0);
177
203
  }
178
204
  }
205
+ var VERTEX_ATTRIB_ARRAY_DIVISOR = 35070;
206
+ var VERTEX_ARRAY_BINDING = 34229;
207
+ function readDivisor(gl, location) {
208
+ return getInstancedArrays(gl) ? gl.getVertexAttrib(location, VERTEX_ATTRIB_ARRAY_DIVISOR) : 0;
209
+ }
210
+ function once(restore) {
211
+ let done = false;
212
+ return () => {
213
+ if (done) return;
214
+ done = true;
215
+ restore();
216
+ };
217
+ }
179
218
  function attributeView(gl, program, schema, { signal } = {}) {
180
219
  const attributes = mapObject(
181
220
  schema,
182
- ({ kind, instanced, buffer = assertedNotNullish(gl.createBuffer()) }, key) => {
221
+ ({
222
+ kind,
223
+ format,
224
+ normalized = false,
225
+ instanced,
226
+ buffer = assertedNotNullish(gl.createBuffer())
227
+ }, key) => {
183
228
  const name2 = toID(key);
184
229
  const location = gl.getAttribLocation(program, name2);
185
230
  if (location < 0) {
186
231
  throw new Error(`Attribute '${name2}' not found`);
187
232
  }
188
233
  const size = kindToSize(kind);
189
- const type = kind.startsWith("u") ? "UNSIGNED_INT" : kind.startsWith("i") ? "INT" : "FLOAT";
234
+ const resolvedFormat = format ?? defaultFormat(kind);
235
+ const glType = FORMAT_TO_GL_TYPE[resolvedFormat];
236
+ const isIntKind = kind.startsWith("i") || kind.startsWith("u");
190
237
  return {
191
238
  buffer,
192
239
  bind() {
240
+ const previousDivisor = readDivisor(gl, location);
193
241
  gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
194
- handleAttribute(gl, location, size, 0, 0, type, instanced);
242
+ handleAttribute(gl, location, size, 0, 0, glType, isIntKind, normalized, instanced);
243
+ return once(() => {
244
+ getInstancedArrays(gl)?.vertexAttribDivisor(location, previousDivisor);
245
+ });
195
246
  },
196
247
  dispose() {
197
248
  gl.deleteBuffer(buffer);
@@ -212,17 +263,32 @@ function attributeView(gl, program, schema, { signal } = {}) {
212
263
  function interleavedAttributeView(gl, program, schema, { signal } = {}) {
213
264
  const interleavedAttributes = mapObject(schema, ({ layout, instanced }) => {
214
265
  let index2 = 0;
266
+ const locations = [];
215
267
  const handles = layout.map((layout2) => {
216
268
  const name2 = toID(layout2.key);
217
269
  const location = gl.getAttribLocation(program, name2);
218
270
  if (location < 0) {
219
271
  throw new Error(`Attribute '${name2}' not found`);
220
272
  }
273
+ locations.push(location);
221
274
  const size = kindToSize(layout2.kind);
222
275
  const offset = index2;
223
- const type = layout2.kind.startsWith("u") ? "UNSIGNED_INT" : layout2.kind.startsWith("i") ? "INT" : "FLOAT";
224
- index2 += size * 4;
225
- return () => handleAttribute(gl, location, size, stride, offset, type, instanced);
276
+ const resolvedFormat = layout2.format ?? defaultFormat(layout2.kind);
277
+ const glType = FORMAT_TO_GL_TYPE[resolvedFormat];
278
+ const isIntKind = layout2.kind.startsWith("i") || layout2.kind.startsWith("u");
279
+ const normalized = layout2.normalized ?? false;
280
+ index2 += size * FORMAT_BYTE_SIZE[resolvedFormat];
281
+ return () => handleAttribute(
282
+ gl,
283
+ location,
284
+ size,
285
+ stride,
286
+ offset,
287
+ glType,
288
+ isIntKind,
289
+ normalized,
290
+ instanced
291
+ );
226
292
  });
227
293
  const stride = index2;
228
294
  const buffer = assertedNotNullish(gl.createBuffer());
@@ -248,8 +314,15 @@ function interleavedAttributeView(gl, program, schema, { signal } = {}) {
248
314
  }
249
315
  };
250
316
  }
317
+ const unbind = () => {
318
+ if (vao) {
319
+ vao.unbind();
320
+ }
321
+ };
251
322
  return {
252
323
  bind() {
324
+ const previousVertexArray = feature ? gl.getParameter(VERTEX_ARRAY_BINDING) : null;
325
+ const previousDivisors = vao ? [] : locations.map((location) => [location, readDivisor(gl, location)]);
253
326
  gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
254
327
  if (vao) {
255
328
  vao.bind();
@@ -258,12 +331,19 @@ function interleavedAttributeView(gl, program, schema, { signal } = {}) {
258
331
  handle();
259
332
  }
260
333
  }
334
+ return once(() => {
335
+ if (feature) {
336
+ feature.bindVertexArray(previousVertexArray);
337
+ }
338
+ if (!vao) {
339
+ const instancedArrays = getInstancedArrays(gl);
340
+ previousDivisors.forEach(([location, divisor]) => {
341
+ instancedArrays?.vertexAttribDivisor(location, divisor);
342
+ });
343
+ }
344
+ });
261
345
  },
262
- unbind() {
263
- if (vao) {
264
- vao.unbind();
265
- }
266
- },
346
+ unbind,
267
347
  dispose() {
268
348
  gl.deleteBuffer(buffer);
269
349
  if (vao) {
@@ -292,7 +372,11 @@ function bufferView(gl, schema, { signal } = {}) {
292
372
  const buffer = assertedNotNullish(gl.createBuffer());
293
373
  return {
294
374
  bind() {
375
+ const previousBuffer = gl.getParameter(gl[`${target}_BINDING`]);
295
376
  gl.bindBuffer(gl[target], buffer);
377
+ return once(() => {
378
+ gl.bindBuffer(gl[target], previousBuffer);
379
+ });
296
380
  },
297
381
  dispose() {
298
382
  gl.deleteBuffer(buffer);
@@ -318,4 +402,4 @@ export {
318
402
  interleavedAttributeView,
319
403
  bufferView
320
404
  };
321
- //# sourceMappingURL=chunk-EBMXKCOV.mjs.map
405
+ //# sourceMappingURL=chunk-ZP4HP5H7.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/utils.ts","../src/index.ts"],"sourcesContent":["import type {\n AttributeKind,\n FramebufferDefinition,\n GL,\n KIND_TO_SIZE_MAP,\n KIND_TO_UNIFORM_FN_NAME_MAP,\n TextureDefinition,\n UniformKind,\n} from './types'\n\nexport function assertedNotNullish<T>(value: T, message?: string): NonNullable<T> {\n if (value === undefined || value === null) throw new Error(message)\n return value\n}\n\n/**********************************************************************************/\n/* */\n/* Kind To Uniform Name */\n/* */\n/**********************************************************************************/\n\nexport const kindToUniformFnName = <T extends UniformKind>(\n kind: T,\n): KIND_TO_UNIFORM_FN_NAME_MAP[T] => {\n switch (kind[0]) {\n // mat\n case 'm':\n return ('Matrix' + kind.slice(3) + 'fv') as KIND_TO_UNIFORM_FN_NAME_MAP[T]\n // sampler/booleans/integer\n case 's':\n case 'b':\n case 'i':\n return ((kind.match(/\\d/)?.[0] ?? '1') + 'i') as KIND_TO_UNIFORM_FN_NAME_MAP[T]\n // unsigned integers\n case 'u':\n return ((kind[4] || '1') + 'ui') as KIND_TO_UNIFORM_FN_NAME_MAP[T]\n // vec\n case 'v':\n return (kind[3] + 'f') as KIND_TO_UNIFORM_FN_NAME_MAP[T]\n default:\n switch (kind) {\n case 'float':\n return '1f' as KIND_TO_UNIFORM_FN_NAME_MAP[T]\n case 'uint':\n return '1ui' as KIND_TO_UNIFORM_FN_NAME_MAP[T]\n default:\n return '1i' as KIND_TO_UNIFORM_FN_NAME_MAP[T]\n }\n }\n}\n\n/**********************************************************************************/\n/* */\n/* Kind To Size */\n/* */\n/**********************************************************************************/\n\nexport const kindToSize = <T extends UniformKind>(kind: T): KIND_TO_SIZE_MAP[T] => {\n switch (kind[0]) {\n case 'm':\n const [a, b] = kind.match(/\\d+/g)!.map(Number) as [number, number?]\n return (a * (b ?? a)) as KIND_TO_SIZE_MAP[T]\n case 'v':\n case 'i':\n case 'b':\n case 'u':\n const match = kind.match(/\\d/) as [string] | undefined\n return (match ? +match[0] : 1) as KIND_TO_SIZE_MAP[T]\n default:\n return 1 as KIND_TO_SIZE_MAP[T]\n }\n}\n\n/**********************************************************************************/\n/* */\n/* Guards */\n/* */\n/**********************************************************************************/\n\nexport const isMatKind = <\n T extends UniformKind | AttributeKind,\n TPrefix extends string,\n TPostfix extends string,\n>(\n kind: T,\n): kind is T & `${TPrefix}mat${TPostfix}` => kind.includes('mat')\n\nexport const isSamplerKind = <TPrefix extends string, TPostfix extends string>(\n kind: UniformKind,\n): kind is UniformKind & `${TPrefix}sampler${TPostfix}` => kind.includes('sampler')\n\nexport const isVecKind = <TPrefix extends string, TPostfix extends string>(\n kind: UniformKind,\n): kind is UniformKind & `${TPrefix}vec${TPostfix}` => kind.includes('vec')\n\n/**********************************************************************************/\n/* */\n/* Create Texture */\n/* */\n/**********************************************************************************/\n\nexport function createTexture(\n gl: GL,\n {\n target = 'TEXTURE_2D',\n internalFormat = 'RGBA',\n format = 'RGBA',\n type = 'UNSIGNED_BYTE',\n minFilter = 'NEAREST',\n magFilter = 'NEAREST',\n wrapS = 'CLAMP_TO_EDGE',\n wrapT = 'CLAMP_TO_EDGE',\n width,\n height,\n }: TextureDefinition,\n data?: ArrayBufferView | null,\n): WebGLTexture {\n const texture = gl.createTexture()\n\n function getTextureConstant(name: string) {\n if (!(name in gl)) {\n throw new Error(`Attempted to create webgl2-only texture (${name}) in webgl1`)\n }\n return gl[name as keyof typeof gl] as any\n }\n\n gl.bindTexture(gl[target], texture)\n gl.texImage2D(\n gl[target],\n 0,\n getTextureConstant(internalFormat),\n width,\n height,\n 0,\n getTextureConstant(format),\n getTextureConstant(type),\n data ?? null,\n )\n\n console.log(minFilter)\n // Set texture parameters\n gl.texParameteri(gl[target], gl.TEXTURE_MIN_FILTER, gl[minFilter])\n gl.texParameteri(gl[target], gl.TEXTURE_MAG_FILTER, gl[magFilter])\n gl.texParameteri(gl[target], gl.TEXTURE_WRAP_S, gl[wrapS])\n gl.texParameteri(gl[target], gl.TEXTURE_WRAP_T, gl[wrapT])\n\n return texture\n}\n\n/**********************************************************************************/\n/* */\n/* Create Framebuffer */\n/* */\n/**********************************************************************************/\n\nconst FRAMEBUFFER_ATTACHMENT_MAP = {\n color: 'COLOR_ATTACHMENT0',\n depth: 'DEPTH_ATTACHMENT',\n stencil: 'STENCIL_ATTACHMENT',\n depthStencil: 'DEPTH_STENCIL_ATTACHMENT',\n} as const\n\nclass FramebufferError extends Error {\n constructor(gl: GL, status: number) {\n let errorMessage = `Framebuffer '${name}' not complete. Status: `\n switch (status) {\n case gl.FRAMEBUFFER_INCOMPLETE_ATTACHMENT:\n errorMessage += 'FRAMEBUFFER_INCOMPLETE_ATTACHMENT'\n break\n case gl.FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT:\n errorMessage += 'FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT'\n break\n case gl.FRAMEBUFFER_INCOMPLETE_DIMENSIONS:\n errorMessage += 'FRAMEBUFFER_INCOMPLETE_DIMENSIONS'\n break\n case gl.FRAMEBUFFER_UNSUPPORTED:\n errorMessage += 'FRAMEBUFFER_UNSUPPORTED'\n break\n default:\n errorMessage += `Unknown (${status})`\n }\n super(errorMessage)\n }\n}\n\nexport function createFramebuffer(\n gl: GL,\n { attachment, texture, ...options }: FramebufferDefinition,\n) {\n // Create texture for the framebuffer\n texture ??= createTexture(gl, options)\n\n // Create framebuffer\n const framebuffer = assertedNotNullish(\n gl.createFramebuffer(),\n `Failed to create framebuffer: ${name}`,\n )\n\n gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffer)\n\n gl.framebufferTexture2D(\n gl.FRAMEBUFFER,\n // Determine attachment point based on attachment kind\n gl[FRAMEBUFFER_ATTACHMENT_MAP[attachment]],\n gl.TEXTURE_2D,\n texture,\n 0,\n )\n\n // Check framebuffer completeness\n const status = gl.checkFramebufferStatus(gl.FRAMEBUFFER)\n if (status !== gl.FRAMEBUFFER_COMPLETE) {\n throw new FramebufferError(gl, status)\n }\n\n return {\n texture,\n framebuffer,\n }\n}\n\n/**********************************************************************************/\n/* */\n/* Create Upsert */\n/* */\n/**********************************************************************************/\n\ntype UpsertMapKind<T, U> = Map<T, U> | (T extends WeakKey ? WeakMap<T, U> : never)\n\nexport function createUpsertMap<T extends UpsertMapKind<any, any> = Map<any, any>>(\n constructor?: new () => T,\n) {\n const map = new (constructor ?? Map)() as T\n return Object.assign(map, {\n getOrInsert(\n key: T extends UpsertMapKind<infer U, any> ? U : never,\n value: T extends UpsertMapKind<any, infer U> ? () => U : never,\n ) {\n let result = map.get(key)\n if (result) {\n return result\n }\n result = value()\n map.set(key, result)\n return result\n },\n })\n}\n\nexport function forEachObject<T extends Record<string, any>>(\n value: T,\n callback: (value: T[keyof T], key: keyof T, index: number) => void,\n) {\n let index = 0\n for (const key in value) {\n callback(value[key], key, index)\n index++\n }\n for (const key of Object.getOwnPropertySymbols(value)) {\n callback(value[key as keyof T], key as keyof T, index)\n index++\n }\n}\n\nexport function mapObject<T extends Record<string, any>, TReturn>(\n value: T,\n callback: (value: T[keyof T], key: keyof T, index: number) => TReturn,\n): { [TKey in keyof T]: TReturn } {\n const result = {} as { [TKey in keyof T]: TReturn }\n\n forEachObject(value, (value, key, index) => {\n result[key] = callback(value, key, index)\n })\n\n return result\n}\n","import { getInstancedArrays, getVertexArrayObject } from './gl'\nimport type {\n AttributeFormat,\n AttributeKind,\n AttributeMethods,\n AttributeSchema,\n AttributeView,\n BufferSchema,\n BufferView,\n GL,\n UniformView as InferUniformView,\n InterleavedAttributeMethods,\n InterleavedAttributeSchema,\n InterleavedAttributeView,\n UniformSchema,\n View,\n ViewOptions,\n ViewSchema,\n} from './types'\nimport {\n isSamplerKind,\n kindToUniformFnName,\n isMatKind,\n assertedNotNullish,\n kindToSize,\n forEachObject,\n} from './utils'\nimport { createUpsertMap, mapObject } from './utils'\nexport * from './types'\n\n/**********************************************************************************/\n/* */\n/* To ID */\n/* */\n/**********************************************************************************/\n\nlet index = 0\nconst PREFIX = 'VIEW_GL_ALIAS'\n// Firefox has currently a bug where it can not handle symbol as WeakRef / key for WeakMap\n// see https://bugzilla.mozilla.org/show_bug.cgi?id=1710433\nconst HAS_WEAK_SYMBOL = (() => {\n try {\n const symbol = Symbol()\n new WeakMap().set(symbol, '')\n return true\n } catch {\n return false\n }\n})()\n// NOTE: fallback to Map will currently cause a minor memory leak.\nconst SYMBOL_MAP = createUpsertMap(HAS_WEAK_SYMBOL ? WeakMap<symbol, string> : Map<symbol, string>)\n\nexport function toID(key: string | number | symbol) {\n if (typeof key === 'string') {\n return key\n }\n if (typeof key === 'symbol') {\n return SYMBOL_MAP.getOrInsert(key, () => `${PREFIX}_${index++}`)\n }\n return key.toString()\n}\n\n/**********************************************************************************/\n/* */\n/* View */\n/* */\n/**********************************************************************************/\n\nexport function view<TSchema extends ViewSchema>(\n gl: GL,\n program: WebGLProgram,\n schema: TSchema,\n options?: ViewOptions,\n): View<TSchema> {\n return {\n uniforms: !schema.uniforms ? undefined : uniformView(gl, program, schema.uniforms),\n attributes: !schema.attributes\n ? undefined\n : attributeView(gl, program, schema.attributes, options),\n interleavedAttributes: !schema.interleavedAttributes\n ? undefined\n : interleavedAttributeView(gl, program, schema.interleavedAttributes, options),\n buffers: !schema.buffers ? undefined : bufferView(gl, schema.buffers),\n } as View<TSchema>\n}\n\n/**********************************************************************************/\n/* */\n/* Uniform View */\n/* */\n/**********************************************************************************/\n\nexport function uniformView<T extends UniformSchema>(\n gl: GL,\n program: WebGLProgram,\n schema: T,\n): InferUniformView<T> {\n // @ts-expect-error - conditional return type based on size is hard to infer\n return mapObject(schema, ({ kind, size }, key) => {\n const name = toID(key)\n\n if (isSamplerKind(kind)) {\n const location = gl.getUniformLocation(program, name)\n return {\n set(arg: number) {\n gl.uniform1i(location, arg)\n },\n }\n }\n\n // Array uniform - support both bulk set and indexed access\n if (size) {\n const bulkFnName = `uniform${kindToUniformFnName(kind)}v`\n // @ts-ignore FIX WEBGL/WEBGL2 TYPES\n const bulkFn = gl[bulkFnName].bind(gl)\n const bulkLocation = gl.getUniformLocation(program, name)\n\n const elementFnName = `uniform${kindToUniformFnName(kind)}`\n // @ts-ignore FIX WEBGL/WEBGL2 TYPES\n const elementFn = gl[elementFnName].bind(gl)\n\n // Build array of element setters\n const elements: Array<{ set(...args: any[]): void }> = []\n for (let i = 0; i < size; i++) {\n const location = gl.getUniformLocation(program, `${name}[${i}]`)\n elements[i] = {\n set(...args: any[]) {\n elementFn(location, ...args)\n },\n }\n }\n\n // Return array-like object with bulk set\n return Object.assign(elements, {\n set(arg: Float32Array) {\n bulkFn(bulkLocation, arg)\n },\n })\n }\n\n // Non-array uniform\n const location = gl.getUniformLocation(program, name)\n const fnName = `uniform${kindToUniformFnName(kind)}`\n\n // @ts-ignore FIX WEBGL/WEBGL2 TYPES\n const fn = gl[fnName].bind(gl)\n\n if (isMatKind(kind)) {\n return {\n set(...args: any[]) {\n fn(location, false, args[0])\n },\n }\n }\n\n return {\n set(...args: any[]) {\n fn(location, ...args)\n },\n }\n })\n}\n\n/**********************************************************************************/\n/* */\n/* Attribute View */\n/* */\n/**********************************************************************************/\n\ntype GLType =\n | 'FLOAT'\n | 'INT'\n | 'UNSIGNED_INT'\n | 'SHORT'\n | 'UNSIGNED_SHORT'\n | 'BYTE'\n | 'UNSIGNED_BYTE'\n\nconst FORMAT_TO_GL_TYPE: Record<AttributeFormat, GLType> = {\n float32: 'FLOAT',\n int32: 'INT',\n uint32: 'UNSIGNED_INT',\n int16: 'SHORT',\n uint16: 'UNSIGNED_SHORT',\n int8: 'BYTE',\n uint8: 'UNSIGNED_BYTE',\n}\n\nconst FORMAT_BYTE_SIZE: Record<AttributeFormat, number> = {\n float32: 4,\n int32: 4,\n uint32: 4,\n int16: 2,\n uint16: 2,\n int8: 1,\n uint8: 1,\n}\n\nfunction defaultFormat(kind: AttributeKind): AttributeFormat {\n if (kind.startsWith('u')) return 'uint32'\n if (kind.startsWith('i')) return 'int32'\n return 'float32'\n}\n\n// Shared attribute helper functions\n// between attributeView and interleavedAttributeView\nfunction handleAttribute(\n gl: GL,\n location: number,\n size: number,\n stride: number,\n offset: number,\n glType: GLType,\n isIntegerKind: boolean,\n normalized: boolean,\n instanced?: boolean,\n) {\n gl.enableVertexAttribArray(location)\n if (isIntegerKind) {\n ;(gl as WebGL2RenderingContext).vertexAttribIPointer(location, size, gl[glType], stride, offset)\n } else {\n gl.vertexAttribPointer(location, size, gl[glType], normalized, stride, offset)\n }\n\n // Always stated, never inherited: the divisor belongs to the location rather\n // than to the program, so a location left at 1 by an earlier instanced\n // attribute would feed one value to every vertex here.\n // Get instanced-arrays-feature: extension if webgl, gl if webgl2\n const instancedArrays = getInstancedArrays(gl)\n if (instanced) {\n assertedNotNullish(instancedArrays).vertexAttribDivisor(location, 1)\n } else if (instancedArrays) {\n instancedArrays.vertexAttribDivisor(location, 0)\n }\n}\n\n// Same enum in webgl2 and in the webgl1 extensions, which expose no constants\nconst VERTEX_ATTRIB_ARRAY_DIVISOR = 0x88fe\nconst VERTEX_ARRAY_BINDING = 0x85b5\n\n// Reads the divisor currently attached to a location, 0 where instancing is\n// unavailable and nothing can have set one\nfunction readDivisor(gl: GL, location: number): number {\n return getInstancedArrays(gl)\n ? (gl.getVertexAttrib(location, VERTEX_ATTRIB_ARRAY_DIVISOR) as number)\n : 0\n}\n\n// Wraps a restore so it runs once: a disposer is per bind() and undoing twice\n// would put back state a later bind() is relying on\nfunction once(restore: () => void): () => void {\n let done = false\n return () => {\n if (done) return\n done = true\n restore()\n }\n}\n\nexport function attributeView<T extends AttributeSchema>(\n gl: GL,\n program: WebGLProgram,\n schema: T,\n { signal }: ViewOptions = {},\n): AttributeView<T> {\n const attributes = mapObject(\n schema,\n (\n {\n kind,\n format,\n normalized = false,\n instanced,\n buffer = assertedNotNullish(gl.createBuffer()),\n },\n key,\n ): AttributeMethods => {\n const name = toID(key)\n\n const location = gl.getAttribLocation(program, name)\n if (location < 0) {\n throw new Error(`Attribute '${name}' not found`)\n }\n\n const size = kindToSize(kind)\n const resolvedFormat = format ?? defaultFormat(kind)\n const glType = FORMAT_TO_GL_TYPE[resolvedFormat]\n const isIntKind = kind.startsWith('i') || kind.startsWith('u')\n\n return {\n buffer,\n bind() {\n // Snapshot before the change, restore in the disposer\n const previousDivisor = readDivisor(gl, location)\n gl.bindBuffer(gl.ARRAY_BUFFER, buffer)\n handleAttribute(gl, location, size, 0, 0, glType, isIntKind, normalized, instanced)\n return once(() => {\n getInstancedArrays(gl)?.vertexAttribDivisor(location, previousDivisor)\n })\n },\n dispose() {\n gl.deleteBuffer(buffer)\n },\n set(data, usage = 'STATIC_DRAW') {\n gl.bindBuffer(gl.ARRAY_BUFFER, buffer)\n gl.bufferData(gl.ARRAY_BUFFER, data, gl[usage])\n return this\n },\n }\n },\n )\n\n signal?.addEventListener('abort', function dispose() {\n forEachObject(attributes, value => value.dispose())\n })\n\n return attributes\n}\n\n/**********************************************************************************/\n/* */\n/* Interleaved Attribute View */\n/* */\n/**********************************************************************************/\n\nexport function interleavedAttributeView<T extends InterleavedAttributeSchema>(\n gl: GL,\n program: WebGLProgram,\n schema: T,\n { signal }: ViewOptions = {},\n): InterleavedAttributeView<T> {\n // Initialize interleaved attributes\n const interleavedAttributes = mapObject(schema, ({ layout, instanced }) => {\n // Increment number to keep track of offset\n let index = 0\n\n // Calculate layout information\n const locations: number[] = []\n const handles = layout.map(layout => {\n const name = toID(layout.key)\n\n const location = gl.getAttribLocation(program, name)\n\n if (location < 0) {\n throw new Error(`Attribute '${name}' not found`)\n }\n\n locations.push(location)\n\n const size = kindToSize(layout.kind)\n const offset = index\n const resolvedFormat = layout.format ?? defaultFormat(layout.kind)\n const glType = FORMAT_TO_GL_TYPE[resolvedFormat]\n const isIntKind = layout.kind.startsWith('i') || layout.kind.startsWith('u')\n const normalized = layout.normalized ?? false\n index += size * FORMAT_BYTE_SIZE[resolvedFormat]\n\n return () =>\n handleAttribute(\n gl,\n location,\n size,\n stride,\n offset,\n glType,\n isIntKind,\n normalized,\n instanced,\n )\n })\n\n // Set stride to final index\n const stride = index\n\n // Create a buffer\n const buffer = assertedNotNullish(gl.createBuffer())\n\n // Create VAO to cache attribute state\n let vao: { unbind(): void; bind(): void; dispose(): void } | undefined = undefined\n\n // Get VAO-feature: extension if webgl1, gl if webgl2\n const feature = getVertexArrayObject(gl)\n if (feature) {\n const vertexArray = feature.createVertexArray()\n feature.bindVertexArray(vertexArray)\n gl.bindBuffer(gl.ARRAY_BUFFER, buffer)\n for (const handle of handles) {\n handle()\n }\n feature.bindVertexArray(null)\n vao = {\n unbind() {\n feature.bindVertexArray(null)\n },\n bind() {\n feature.bindVertexArray(vertexArray)\n },\n dispose() {\n feature.deleteVertexArray(vertexArray)\n },\n }\n }\n\n // A selected vertex array stays selected for every later draw, which would\n // then write its own attributes into this one\n const unbind = () => {\n if (vao) {\n vao.unbind()\n }\n }\n\n return {\n bind() {\n // Snapshot before the change, restore in the disposer\n const previousVertexArray = feature\n ? (gl.getParameter(VERTEX_ARRAY_BINDING) as WebGLVertexArrayObject | null)\n : null\n const previousDivisors = vao\n ? []\n : locations.map(location => [location, readDivisor(gl, location)] as const)\n\n gl.bindBuffer(gl.ARRAY_BUFFER, buffer)\n if (vao) {\n vao.bind()\n } else {\n // Fallback: manual attribute setup\n for (const handle of handles) {\n handle()\n }\n }\n\n return once(() => {\n if (feature) {\n feature.bindVertexArray(previousVertexArray)\n }\n // Without a vertex array the manual setup wrote divisors straight to\n // the locations, so they are ours to put back\n if (!vao) {\n const instancedArrays = getInstancedArrays(gl)\n previousDivisors.forEach(([location, divisor]) => {\n instancedArrays?.vertexAttribDivisor(location, divisor)\n })\n }\n })\n },\n unbind,\n dispose() {\n gl.deleteBuffer(buffer)\n if (vao) {\n vao.dispose()\n }\n },\n set(value, usage = 'STATIC_DRAW') {\n if (vao) {\n vao.bind()\n }\n gl.bindBuffer(gl.ARRAY_BUFFER, buffer)\n gl.bufferData(gl.ARRAY_BUFFER, value, gl[usage])\n if (vao) {\n vao.unbind()\n }\n },\n } satisfies InterleavedAttributeMethods\n })\n\n signal?.addEventListener('abort', function dispose() {\n forEachObject(interleavedAttributes, value => value.dispose())\n })\n\n return interleavedAttributes\n}\n\n/**********************************************************************************/\n/* */\n/* Buffer View */\n/* */\n/**********************************************************************************/\n\nexport function bufferView<T extends BufferSchema>(\n gl: GL,\n schema: T,\n { signal }: ViewOptions = {},\n): BufferView<T> {\n // Initialize buffers\n const buffers = mapObject(schema, ({ target = 'ARRAY_BUFFER', usage = 'STATIC_DRAW' }) => {\n const buffer = assertedNotNullish(gl.createBuffer())\n\n return {\n bind() {\n // Snapshot before the change, restore in the disposer. Restoring the\n // previous buffer rather than null matters for ELEMENT_ARRAY_BUFFER,\n // whose binding is recorded in the bound vertex array: clearing it\n // would strip the index buffer from an array the caller may not own.\n const previousBuffer = gl.getParameter(gl[`${target}_BINDING`]) as WebGLBuffer | null\n gl.bindBuffer(gl[target], buffer)\n return once(() => {\n gl.bindBuffer(gl[target], previousBuffer)\n })\n },\n dispose() {\n gl.deleteBuffer(buffer)\n },\n set(data: Float32Array) {\n gl.bindBuffer(gl[target], buffer)\n gl.bufferData(gl[target], data, gl[usage])\n },\n }\n })\n\n signal?.addEventListener('abort', function dispose() {\n forEachObject(buffers, value => value.dispose())\n })\n\n return buffers\n}\n"],"mappings":"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","names":["index","value","name","location","index","layout"]}
package/dist/gl.d.mts CHANGED
@@ -1,4 +1,4 @@
1
- import { G as GL, R as RemoveSuffix } from './types-lm9fTTy8.mjs';
1
+ import { G as GL, R as RemoveSuffix } from './types-DTSPQclW.mjs';
2
2
 
3
3
  /**********************************************************************************/
4
4
  /**********************************************************************************/
package/dist/gl.mjs CHANGED
@@ -3,7 +3,7 @@ import {
3
3
  createShader,
4
4
  getInstancedArrays,
5
5
  getVertexArrayObject
6
- } from "./chunk-WSNE4JVQ.mjs";
6
+ } from "./chunk-CJTT7WPV.mjs";
7
7
  export {
8
8
  createProgram,
9
9
  createShader,
package/dist/index.d.mts CHANGED
@@ -1,5 +1,5 @@
1
- import { V as ViewSchema, G as GL, a as ViewOptions, b as View, U as UniformSchema, c as UniformView, A as AttributeSchema, d as AttributeView, I as InterleavedAttributeSchema, e as InterleavedAttributeView, B as BufferSchema, f as BufferView } from './types-lm9fTTy8.mjs';
2
- export { y as AttributeDefinition, x as AttributeKind, z as AttributeMethods, w as AttributeShortHand, a5 as AttributeToken, a6 as AttributeTokenFn, F as BufferDefinition, H as BufferMethods, ad as CompileResult, ab as FlattenSlots, Y as Format, L as Framebuffer, J as FramebufferDefinition, a2 as GLSL, a0 as GLSLResult, a1 as GLSLSlot, aa as GLSLSlotsToSchema, a8 as GLSLToSchema, a9 as GLSLToView, g as GLTarget, l as GLTextureFilter, j as GLTextureFormat, i as GLTextureTarget, k as GLTextureType, m as GLTextureWrap, h as GLUsage, a7 as InterleaveToken, D as InterleavedAttributeDefinition, C as InterleavedAttributeLayout, E as InterleavedAttributeMethods, X as InternalFormat, n as KIND_TO_SIZE_MAP, K as KIND_TO_UNIFORM_FN_NAME_MAP, ac as MergeGLSLSchema, Z as PixelType, _ as TexImage2DOptions, T as TextureDefinition, O as TextureMethods, Q as TextureParameterName, S as TextureParameterValue, W as TextureParameters, N as TextureSchema, M as TextureTarget, P as TextureView, t as UniformArrayMethods, r as UniformDefinition, s as UniformElementMethods, o as UniformFnName, p as UniformKind, q as UniformKindMap, u as UniformMethods, a3 as UniformToken, a4 as UniformTokenFn, v as UniformValue, $ as ViewSchemaPartial } from './types-lm9fTTy8.mjs';
1
+ import { V as ViewSchema, G as GL, a as ViewOptions, b as View, U as UniformSchema, c as UniformView, A as AttributeSchema, d as AttributeView, I as InterleavedAttributeSchema, e as InterleavedAttributeView, B as BufferSchema, f as BufferView } from './types-DTSPQclW.mjs';
2
+ export { z as AttributeDefinition, y as AttributeFormat, x as AttributeKind, C as AttributeMethods, w as AttributeShortHand, a6 as AttributeToken, a7 as AttributeTokenFn, H as BufferDefinition, J as BufferMethods, ae as CompileResult, ac as FlattenSlots, Z as Format, M as Framebuffer, L as FramebufferDefinition, a3 as GLSL, a1 as GLSLResult, a2 as GLSLSlot, ab as GLSLSlotsToSchema, a9 as GLSLToSchema, aa as GLSLToView, g as GLTarget, l as GLTextureFilter, j as GLTextureFormat, i as GLTextureTarget, k as GLTextureType, m as GLTextureWrap, h as GLUsage, a8 as InterleaveToken, E as InterleavedAttributeDefinition, D as InterleavedAttributeLayout, F as InterleavedAttributeMethods, Y as InternalFormat, n as KIND_TO_SIZE_MAP, K as KIND_TO_UNIFORM_FN_NAME_MAP, ad as MergeGLSLSchema, _ as PixelType, $ as TexImage2DOptions, T as TextureDefinition, P as TextureMethods, S as TextureParameterName, W as TextureParameterValue, X as TextureParameters, O as TextureSchema, N as TextureTarget, Q as TextureView, t as UniformArrayMethods, r as UniformDefinition, s as UniformElementMethods, o as UniformFnName, p as UniformKind, q as UniformKindMap, u as UniformMethods, a4 as UniformToken, a5 as UniformTokenFn, v as UniformValue, a0 as ViewSchemaPartial } from './types-DTSPQclW.mjs';
3
3
 
4
4
  declare function toID(key: string | number | symbol): any;
5
5
  /**********************************************************************************/
package/dist/index.mjs CHANGED
@@ -5,8 +5,8 @@ import {
5
5
  toID,
6
6
  uniformView,
7
7
  view
8
- } from "./chunk-EBMXKCOV.mjs";
9
- import "./chunk-WSNE4JVQ.mjs";
8
+ } from "./chunk-ZP4HP5H7.mjs";
9
+ import "./chunk-CJTT7WPV.mjs";
10
10
  export {
11
11
  attributeView,
12
12
  bufferView,
package/dist/tag.d.mts CHANGED
@@ -1,4 +1,4 @@
1
- import { a1 as GLSLSlot, a2 as GLSL, a4 as UniformTokenFn, a6 as AttributeTokenFn, a5 as AttributeToken, x as AttributeKind, y as AttributeDefinition, a7 as InterleaveToken, $ as ViewSchemaPartial, ad as CompileResult, ae as Prettify, a8 as GLSLToSchema, G as GL } from './types-lm9fTTy8.mjs';
1
+ import { a2 as GLSLSlot, a3 as GLSL, a5 as UniformTokenFn, a7 as AttributeTokenFn, a6 as AttributeToken, x as AttributeKind, z as AttributeDefinition, a8 as InterleaveToken, a0 as ViewSchemaPartial, ae as CompileResult, af as Prettify, a9 as GLSLToSchema, G as GL } from './types-DTSPQclW.mjs';
2
2
 
3
3
  declare function glsl<TSlot extends GLSLSlot, const TSlots extends TSlot[]>(template: TemplateStringsArray, ...slots: [...TSlots]): GLSL<TSlots>;
4
4
  declare const uniform: {
package/dist/tag.mjs CHANGED
@@ -2,10 +2,10 @@ import {
2
2
  createUpsertMap,
3
3
  toID,
4
4
  view
5
- } from "./chunk-EBMXKCOV.mjs";
5
+ } from "./chunk-ZP4HP5H7.mjs";
6
6
  import {
7
7
  createProgram
8
- } from "./chunk-WSNE4JVQ.mjs";
8
+ } from "./chunk-CJTT7WPV.mjs";
9
9
 
10
10
  // src/tag.ts
11
11
  function glsl(template, ...slots) {
@@ -186,28 +186,49 @@ type UniformValue = UniformKindMap[UniformKind];
186
186
  /**********************************************************************************/
187
187
  type AttributeShortHand = '1f' | '2f' | '3f' | '4f' | '1i' | '2i' | '3i' | '4i' | '1ui' | '2ui' | '3ui' | '4ui';
188
188
  type AttributeKind = 'float' | 'vec2' | 'vec3' | 'vec4' | 'mat2' | 'mat3' | 'mat4' | 'mat2x3' | 'mat2x4' | 'mat3x2' | 'mat3x4' | 'mat4x2' | 'mat4x3' | 'int' | 'ivec2' | 'ivec3' | 'ivec4' | 'uint' | 'uvec2' | 'uvec3' | 'uvec4';
189
+ type AttributeFormat = 'float32' | 'int32' | 'uint32' | 'int16' | 'uint16' | 'int8' | 'uint8';
189
190
  interface AttributeDefinition {
190
191
  kind: AttributeKind;
192
+ format?: AttributeFormat;
193
+ normalized?: boolean;
191
194
  instanced?: boolean;
192
195
  buffer?: WebGLBuffer;
193
196
  }
194
197
  type AttributeSchema = Record<string | symbol, AttributeDefinition>;
195
- interface AttributeMethods<T = AttributeKind> {
198
+ type IntKind = 'int' | 'ivec2' | 'ivec3' | 'ivec4';
199
+ type UintKind = 'uint' | 'uvec2' | 'uvec3' | 'uvec4';
200
+ type DefaultFormat<K extends AttributeKind> = K extends IntKind ? 'int32' : K extends UintKind ? 'uint32' : 'float32';
201
+ type ResolvedFormat<T extends AttributeDefinition> = T extends {
202
+ format: infer F extends AttributeFormat;
203
+ } ? F : DefaultFormat<T['kind']>;
204
+ type FormatToArray = {
205
+ float32: Float32Array;
206
+ int32: Int32Array;
207
+ uint32: Uint32Array;
208
+ int16: Int16Array;
209
+ uint16: Uint16Array;
210
+ int8: Int8Array;
211
+ uint8: Uint8Array;
212
+ };
213
+ interface AttributeMethods<T extends AttributeDefinition = AttributeDefinition> {
196
214
  buffer: WebGLBuffer;
197
- bind(): void;
198
- set(data: Float32Array, usage?: GLUsage): {
199
- bind(): void;
215
+ /** Binds the attribute, returning a disposer that restores the divisor */
216
+ bind(): () => void;
217
+ set(data: FormatToArray[ResolvedFormat<T>], usage?: GLUsage): {
218
+ bind(): () => void;
200
219
  };
201
220
  dispose(): void;
202
221
  }
203
222
  type AttributeView<T extends AttributeSchema> = {
204
- [K in keyof T]: Prettify<AttributeMethods<T[K]['kind']>>;
223
+ [K in keyof T]: Prettify<AttributeMethods<T[K]>>;
205
224
  };
206
225
  /**********************************************************************************/
207
226
  /**********************************************************************************/
208
227
  interface InterleavedAttributeLayout {
209
228
  key: string | symbol;
210
229
  kind: AttributeKind;
230
+ format?: AttributeFormat;
231
+ normalized?: boolean;
211
232
  }
212
233
  interface InterleavedAttributeDefinition {
213
234
  layout: InterleavedAttributeLayout[];
@@ -215,7 +236,8 @@ interface InterleavedAttributeDefinition {
215
236
  }
216
237
  type InterleavedAttributeSchema = Record<string | symbol, InterleavedAttributeDefinition>;
217
238
  interface InterleavedAttributeMethods<T extends InterleavedAttributeLayout[] = InterleavedAttributeLayout[]> {
218
- bind(): void;
239
+ /** Binds the vertex array, returning a disposer that restores the previous one */
240
+ bind(): () => void;
219
241
  unbind(): void;
220
242
  set(data: Float32Array, usage?: GLUsage): void;
221
243
  dispose(): void;
@@ -232,7 +254,8 @@ interface BufferDefinition {
232
254
  type BufferSchema = Record<string | symbol, BufferDefinition>;
233
255
  interface BufferMethods {
234
256
  set(data: Float32Array | Uint16Array | Uint32Array): void;
235
- bind(): void;
257
+ /** Binds the buffer, returning a disposer that restores the previous binding */
258
+ bind(): () => void;
236
259
  dispose(): void;
237
260
  }
238
261
  type BufferView<T extends BufferSchema> = {
@@ -416,4 +439,4 @@ type CompileResult<TVertex extends GLSL, TFragment extends GLSL, TOverrideSchema
416
439
  fragment: string;
417
440
  } : never;
418
441
 
419
- export type { ViewSchemaPartial as $, AttributeSchema as A, BufferSchema as B, InterleavedAttributeLayout as C, InterleavedAttributeDefinition as D, InterleavedAttributeMethods as E, BufferDefinition as F, GL as G, BufferMethods as H, InterleavedAttributeSchema as I, FramebufferDefinition as J, KIND_TO_UNIFORM_FN_NAME_MAP as K, Framebuffer as L, TextureTarget as M, TextureSchema as N, TextureMethods as O, TextureView as P, TextureParameterName as Q, RemoveSuffix as R, TextureParameterValue as S, TextureDefinition as T, UniformSchema as U, ViewSchema as V, TextureParameters as W, InternalFormat as X, Format as Y, PixelType as Z, TexImage2DOptions as _, ViewOptions as a, GLSLResult as a0, GLSLSlot as a1, GLSL as a2, UniformToken as a3, UniformTokenFn as a4, AttributeToken as a5, AttributeTokenFn as a6, InterleaveToken as a7, GLSLToSchema as a8, GLSLToView as a9, GLSLSlotsToSchema as aa, FlattenSlots as ab, MergeGLSLSchema as ac, CompileResult as ad, Prettify as ae, View as b, UniformView as c, AttributeView as d, InterleavedAttributeView as e, BufferView as f, GLTarget as g, GLUsage as h, GLTextureTarget as i, GLTextureFormat as j, GLTextureType as k, GLTextureFilter as l, GLTextureWrap as m, KIND_TO_SIZE_MAP as n, UniformFnName as o, UniformKind as p, UniformKindMap as q, UniformDefinition as r, UniformElementMethods as s, UniformArrayMethods as t, UniformMethods as u, UniformValue as v, AttributeShortHand as w, AttributeKind as x, AttributeDefinition as y, AttributeMethods as z };
442
+ export type { TexImage2DOptions as $, AttributeSchema as A, BufferSchema as B, AttributeMethods as C, InterleavedAttributeLayout as D, InterleavedAttributeDefinition as E, InterleavedAttributeMethods as F, GL as G, BufferDefinition as H, InterleavedAttributeSchema as I, BufferMethods as J, KIND_TO_UNIFORM_FN_NAME_MAP as K, FramebufferDefinition as L, Framebuffer as M, TextureTarget as N, TextureSchema as O, TextureMethods as P, TextureView as Q, RemoveSuffix as R, TextureParameterName as S, TextureDefinition as T, UniformSchema as U, ViewSchema as V, TextureParameterValue as W, TextureParameters as X, InternalFormat as Y, Format as Z, PixelType as _, ViewOptions as a, ViewSchemaPartial as a0, GLSLResult as a1, GLSLSlot as a2, GLSL as a3, UniformToken as a4, UniformTokenFn as a5, AttributeToken as a6, AttributeTokenFn as a7, InterleaveToken as a8, GLSLToSchema as a9, GLSLToView as aa, GLSLSlotsToSchema as ab, FlattenSlots as ac, MergeGLSLSchema as ad, CompileResult as ae, Prettify as af, View as b, UniformView as c, AttributeView as d, InterleavedAttributeView as e, BufferView as f, GLTarget as g, GLUsage as h, GLTextureTarget as i, GLTextureFormat as j, GLTextureType as k, GLTextureFilter as l, GLTextureWrap as m, KIND_TO_SIZE_MAP as n, UniformFnName as o, UniformKind as p, UniformKindMap as q, UniformDefinition as r, UniformElementMethods as s, UniformArrayMethods as t, UniformMethods as u, UniformValue as v, AttributeShortHand as w, AttributeKind as x, AttributeFormat as y, AttributeDefinition as z };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bigmistqke/view.gl",
3
- "version": "0.1.17",
3
+ "version": "0.1.19",
4
4
  "description": "👀 Utilities for managing WebGL resources.",
5
5
  "exports": {
6
6
  ".": {
package/src/gl.ts CHANGED
@@ -1,7 +1,9 @@
1
1
  import { RemoveSuffix } from './type-utils'
2
2
  import type { GL } from './types'
3
3
 
4
- function isWebGL2RenderingContext(gl: WebGLRenderingContext | WebGL2RenderingContext): gl is WebGL2RenderingContext {
4
+ function isWebGL2RenderingContext(
5
+ gl: WebGLRenderingContext | WebGL2RenderingContext,
6
+ ): gl is WebGL2RenderingContext {
5
7
  return typeof WebGL2RenderingContext !== 'undefined' && gl instanceof WebGL2RenderingContext
6
8
  }
7
9
 
package/src/index.ts CHANGED
@@ -1,5 +1,7 @@
1
1
  import { getInstancedArrays, getVertexArrayObject } from './gl'
2
2
  import type {
3
+ AttributeFormat,
4
+ AttributeKind,
3
5
  AttributeMethods,
4
6
  AttributeSchema,
5
7
  AttributeView,
@@ -165,6 +167,41 @@ export function uniformView<T extends UniformSchema>(
165
167
  /* */
166
168
  /**********************************************************************************/
167
169
 
170
+ type GLType =
171
+ | 'FLOAT'
172
+ | 'INT'
173
+ | 'UNSIGNED_INT'
174
+ | 'SHORT'
175
+ | 'UNSIGNED_SHORT'
176
+ | 'BYTE'
177
+ | 'UNSIGNED_BYTE'
178
+
179
+ const FORMAT_TO_GL_TYPE: Record<AttributeFormat, GLType> = {
180
+ float32: 'FLOAT',
181
+ int32: 'INT',
182
+ uint32: 'UNSIGNED_INT',
183
+ int16: 'SHORT',
184
+ uint16: 'UNSIGNED_SHORT',
185
+ int8: 'BYTE',
186
+ uint8: 'UNSIGNED_BYTE',
187
+ }
188
+
189
+ const FORMAT_BYTE_SIZE: Record<AttributeFormat, number> = {
190
+ float32: 4,
191
+ int32: 4,
192
+ uint32: 4,
193
+ int16: 2,
194
+ uint16: 2,
195
+ int8: 1,
196
+ uint8: 1,
197
+ }
198
+
199
+ function defaultFormat(kind: AttributeKind): AttributeFormat {
200
+ if (kind.startsWith('u')) return 'uint32'
201
+ if (kind.startsWith('i')) return 'int32'
202
+ return 'float32'
203
+ }
204
+
168
205
  // Shared attribute helper functions
169
206
  // between attributeView and interleavedAttributeView
170
207
  function handleAttribute(
@@ -173,19 +210,50 @@ function handleAttribute(
173
210
  size: number,
174
211
  stride: number,
175
212
  offset: number,
176
- type: 'FLOAT' | 'INT' | 'UNSIGNED_INT',
213
+ glType: GLType,
214
+ isIntegerKind: boolean,
215
+ normalized: boolean,
177
216
  instanced?: boolean,
178
217
  ) {
179
218
  gl.enableVertexAttribArray(location)
180
- if (type === 'INT' || type === 'UNSIGNED_INT') {
181
- ;(gl as WebGL2RenderingContext).vertexAttribIPointer(location, size, gl[type], stride, offset)
219
+ if (isIntegerKind) {
220
+ ;(gl as WebGL2RenderingContext).vertexAttribIPointer(location, size, gl[glType], stride, offset)
182
221
  } else {
183
- gl.vertexAttribPointer(location, size, gl[type], false, stride, offset)
222
+ gl.vertexAttribPointer(location, size, gl[glType], normalized, stride, offset)
184
223
  }
185
224
 
225
+ // Always stated, never inherited: the divisor belongs to the location rather
226
+ // than to the program, so a location left at 1 by an earlier instanced
227
+ // attribute would feed one value to every vertex here.
228
+ // Get instanced-arrays-feature: extension if webgl, gl if webgl2
229
+ const instancedArrays = getInstancedArrays(gl)
186
230
  if (instanced) {
187
- // Get instanced-arrays-feature: extension if webgl, gl if webgl2
188
- assertedNotNullish(getInstancedArrays(gl)).vertexAttribDivisor(location, 1)
231
+ assertedNotNullish(instancedArrays).vertexAttribDivisor(location, 1)
232
+ } else if (instancedArrays) {
233
+ instancedArrays.vertexAttribDivisor(location, 0)
234
+ }
235
+ }
236
+
237
+ // Same enum in webgl2 and in the webgl1 extensions, which expose no constants
238
+ const VERTEX_ATTRIB_ARRAY_DIVISOR = 0x88fe
239
+ const VERTEX_ARRAY_BINDING = 0x85b5
240
+
241
+ // Reads the divisor currently attached to a location, 0 where instancing is
242
+ // unavailable and nothing can have set one
243
+ function readDivisor(gl: GL, location: number): number {
244
+ return getInstancedArrays(gl)
245
+ ? (gl.getVertexAttrib(location, VERTEX_ATTRIB_ARRAY_DIVISOR) as number)
246
+ : 0
247
+ }
248
+
249
+ // Wraps a restore so it runs once: a disposer is per bind() and undoing twice
250
+ // would put back state a later bind() is relying on
251
+ function once(restore: () => void): () => void {
252
+ let done = false
253
+ return () => {
254
+ if (done) return
255
+ done = true
256
+ restore()
189
257
  }
190
258
  }
191
259
 
@@ -198,7 +266,13 @@ export function attributeView<T extends AttributeSchema>(
198
266
  const attributes = mapObject(
199
267
  schema,
200
268
  (
201
- { kind, instanced, buffer = assertedNotNullish(gl.createBuffer()) },
269
+ {
270
+ kind,
271
+ format,
272
+ normalized = false,
273
+ instanced,
274
+ buffer = assertedNotNullish(gl.createBuffer()),
275
+ },
202
276
  key,
203
277
  ): AttributeMethods => {
204
278
  const name = toID(key)
@@ -209,13 +283,20 @@ export function attributeView<T extends AttributeSchema>(
209
283
  }
210
284
 
211
285
  const size = kindToSize(kind)
212
- const type = kind.startsWith('u') ? 'UNSIGNED_INT' : kind.startsWith('i') ? 'INT' : 'FLOAT'
286
+ const resolvedFormat = format ?? defaultFormat(kind)
287
+ const glType = FORMAT_TO_GL_TYPE[resolvedFormat]
288
+ const isIntKind = kind.startsWith('i') || kind.startsWith('u')
213
289
 
214
290
  return {
215
291
  buffer,
216
292
  bind() {
293
+ // Snapshot before the change, restore in the disposer
294
+ const previousDivisor = readDivisor(gl, location)
217
295
  gl.bindBuffer(gl.ARRAY_BUFFER, buffer)
218
- handleAttribute(gl, location, size, 0, 0, type, instanced)
296
+ handleAttribute(gl, location, size, 0, 0, glType, isIntKind, normalized, instanced)
297
+ return once(() => {
298
+ getInstancedArrays(gl)?.vertexAttribDivisor(location, previousDivisor)
299
+ })
219
300
  },
220
301
  dispose() {
221
302
  gl.deleteBuffer(buffer)
@@ -254,6 +335,7 @@ export function interleavedAttributeView<T extends InterleavedAttributeSchema>(
254
335
  let index = 0
255
336
 
256
337
  // Calculate layout information
338
+ const locations: number[] = []
257
339
  const handles = layout.map(layout => {
258
340
  const name = toID(layout.key)
259
341
 
@@ -263,16 +345,28 @@ export function interleavedAttributeView<T extends InterleavedAttributeSchema>(
263
345
  throw new Error(`Attribute '${name}' not found`)
264
346
  }
265
347
 
348
+ locations.push(location)
349
+
266
350
  const size = kindToSize(layout.kind)
267
351
  const offset = index
268
- const type = layout.kind.startsWith('u')
269
- ? 'UNSIGNED_INT'
270
- : layout.kind.startsWith('i')
271
- ? 'INT'
272
- : 'FLOAT'
273
- index += size * 4
274
-
275
- return () => handleAttribute(gl, location, size, stride, offset, type, instanced)
352
+ const resolvedFormat = layout.format ?? defaultFormat(layout.kind)
353
+ const glType = FORMAT_TO_GL_TYPE[resolvedFormat]
354
+ const isIntKind = layout.kind.startsWith('i') || layout.kind.startsWith('u')
355
+ const normalized = layout.normalized ?? false
356
+ index += size * FORMAT_BYTE_SIZE[resolvedFormat]
357
+
358
+ return () =>
359
+ handleAttribute(
360
+ gl,
361
+ location,
362
+ size,
363
+ stride,
364
+ offset,
365
+ glType,
366
+ isIntKind,
367
+ normalized,
368
+ instanced,
369
+ )
276
370
  })
277
371
 
278
372
  // Set stride to final index
@@ -307,8 +401,24 @@ export function interleavedAttributeView<T extends InterleavedAttributeSchema>(
307
401
  }
308
402
  }
309
403
 
404
+ // A selected vertex array stays selected for every later draw, which would
405
+ // then write its own attributes into this one
406
+ const unbind = () => {
407
+ if (vao) {
408
+ vao.unbind()
409
+ }
410
+ }
411
+
310
412
  return {
311
413
  bind() {
414
+ // Snapshot before the change, restore in the disposer
415
+ const previousVertexArray = feature
416
+ ? (gl.getParameter(VERTEX_ARRAY_BINDING) as WebGLVertexArrayObject | null)
417
+ : null
418
+ const previousDivisors = vao
419
+ ? []
420
+ : locations.map(location => [location, readDivisor(gl, location)] as const)
421
+
312
422
  gl.bindBuffer(gl.ARRAY_BUFFER, buffer)
313
423
  if (vao) {
314
424
  vao.bind()
@@ -318,12 +428,22 @@ export function interleavedAttributeView<T extends InterleavedAttributeSchema>(
318
428
  handle()
319
429
  }
320
430
  }
431
+
432
+ return once(() => {
433
+ if (feature) {
434
+ feature.bindVertexArray(previousVertexArray)
435
+ }
436
+ // Without a vertex array the manual setup wrote divisors straight to
437
+ // the locations, so they are ours to put back
438
+ if (!vao) {
439
+ const instancedArrays = getInstancedArrays(gl)
440
+ previousDivisors.forEach(([location, divisor]) => {
441
+ instancedArrays?.vertexAttribDivisor(location, divisor)
442
+ })
443
+ }
444
+ })
321
445
  },
322
- unbind() {
323
- if (vao) {
324
- vao.unbind()
325
- }
326
- },
446
+ unbind,
327
447
  dispose() {
328
448
  gl.deleteBuffer(buffer)
329
449
  if (vao) {
@@ -364,9 +484,18 @@ export function bufferView<T extends BufferSchema>(
364
484
  // Initialize buffers
365
485
  const buffers = mapObject(schema, ({ target = 'ARRAY_BUFFER', usage = 'STATIC_DRAW' }) => {
366
486
  const buffer = assertedNotNullish(gl.createBuffer())
487
+
367
488
  return {
368
489
  bind() {
490
+ // Snapshot before the change, restore in the disposer. Restoring the
491
+ // previous buffer rather than null matters for ELEMENT_ARRAY_BUFFER,
492
+ // whose binding is recorded in the bound vertex array: clearing it
493
+ // would strip the index buffer from an array the caller may not own.
494
+ const previousBuffer = gl.getParameter(gl[`${target}_BINDING`]) as WebGLBuffer | null
369
495
  gl.bindBuffer(gl[target], buffer)
496
+ return once(() => {
497
+ gl.bindBuffer(gl[target], previousBuffer)
498
+ })
370
499
  },
371
500
  dispose() {
372
501
  gl.deleteBuffer(buffer)
package/src/types.ts CHANGED
@@ -285,23 +285,53 @@ export type AttributeKind =
285
285
  | 'uvec3'
286
286
  | 'uvec4'
287
287
 
288
+ export type AttributeFormat = 'float32' | 'int32' | 'uint32' | 'int16' | 'uint16' | 'int8' | 'uint8'
289
+
288
290
  export interface AttributeDefinition {
289
291
  kind: AttributeKind
292
+ format?: AttributeFormat
293
+ normalized?: boolean
290
294
  instanced?: boolean
291
295
  buffer?: WebGLBuffer
292
296
  }
293
297
 
294
298
  export type AttributeSchema = Record<string | symbol, AttributeDefinition>
295
299
 
296
- export interface AttributeMethods<T = AttributeKind> {
300
+ type IntKind = 'int' | 'ivec2' | 'ivec3' | 'ivec4'
301
+ type UintKind = 'uint' | 'uvec2' | 'uvec3' | 'uvec4'
302
+
303
+ type DefaultFormat<K extends AttributeKind> = K extends IntKind
304
+ ? 'int32'
305
+ : K extends UintKind
306
+ ? 'uint32'
307
+ : 'float32'
308
+
309
+ type ResolvedFormat<T extends AttributeDefinition> = T extends {
310
+ format: infer F extends AttributeFormat
311
+ }
312
+ ? F
313
+ : DefaultFormat<T['kind']>
314
+
315
+ type FormatToArray = {
316
+ float32: Float32Array
317
+ int32: Int32Array
318
+ uint32: Uint32Array
319
+ int16: Int16Array
320
+ uint16: Uint16Array
321
+ int8: Int8Array
322
+ uint8: Uint8Array
323
+ }
324
+
325
+ export interface AttributeMethods<T extends AttributeDefinition = AttributeDefinition> {
297
326
  buffer: WebGLBuffer
298
- bind(): void
299
- set(data: Float32Array, usage?: GLUsage): { bind(): void }
327
+ /** Binds the attribute, returning a disposer that restores the divisor */
328
+ bind(): () => void
329
+ set(data: FormatToArray[ResolvedFormat<T>], usage?: GLUsage): { bind(): () => void }
300
330
  dispose(): void
301
331
  }
302
332
 
303
333
  export type AttributeView<T extends AttributeSchema> = {
304
- [K in keyof T]: Prettify<AttributeMethods<T[K]['kind']>>
334
+ [K in keyof T]: Prettify<AttributeMethods<T[K]>>
305
335
  }
306
336
 
307
337
  /**********************************************************************************/
@@ -313,6 +343,8 @@ export type AttributeView<T extends AttributeSchema> = {
313
343
  export interface InterleavedAttributeLayout {
314
344
  key: string | symbol
315
345
  kind: AttributeKind
346
+ format?: AttributeFormat
347
+ normalized?: boolean
316
348
  }
317
349
 
318
350
  export interface InterleavedAttributeDefinition {
@@ -325,7 +357,8 @@ export type InterleavedAttributeSchema = Record<string | symbol, InterleavedAttr
325
357
  export interface InterleavedAttributeMethods<
326
358
  T extends InterleavedAttributeLayout[] = InterleavedAttributeLayout[],
327
359
  > {
328
- bind(): void
360
+ /** Binds the vertex array, returning a disposer that restores the previous one */
361
+ bind(): () => void
329
362
  unbind(): void
330
363
  set(data: Float32Array, usage?: GLUsage): void
331
364
  dispose(): void
@@ -350,7 +383,8 @@ export type BufferSchema = Record<string | symbol, BufferDefinition>
350
383
 
351
384
  export interface BufferMethods {
352
385
  set(data: Float32Array | Uint16Array | Uint32Array): void
353
- bind(): void
386
+ /** Binds the buffer, returning a disposer that restores the previous binding */
387
+ bind(): () => void
354
388
  dispose(): void
355
389
  }
356
390
 
@@ -409,6 +409,64 @@ describe('attributeView', () => {
409
409
  expect(gl.vertexAttribDivisor).toHaveBeenCalledWith(expect.any(Number), 1)
410
410
  })
411
411
 
412
+ it('should return a disposer from bind that clears the instancing divisor', () => {
413
+ const schema = {
414
+ a_instancePosition: { kind: 'vec3', instanced: true },
415
+ } satisfies AttributeSchema
416
+
417
+ const attributes = attributeView(gl, program, schema)
418
+
419
+ // The divisor is global to the location rather than to the program, so
420
+ // leaving it at 1 makes the next non-instanced attribute bound there read
421
+ // a single value for every vertex.
422
+ const unbind = attributes.a_instancePosition.bind()
423
+ expect(gl.vertexAttribDivisor).toHaveBeenCalledWith(expect.any(Number), 1)
424
+
425
+ unbind()
426
+ expect(gl.vertexAttribDivisor).toHaveBeenLastCalledWith(expect.any(Number), 0)
427
+ })
428
+
429
+ it('should state a zero divisor for non-instanced attributes rather than inherit one', () => {
430
+ const schema = {
431
+ a_position: { kind: 'vec2' },
432
+ } satisfies AttributeSchema
433
+
434
+ const attributes = attributeView(gl, program, schema)
435
+
436
+ // A location left at 1 by an earlier instanced attribute would otherwise
437
+ // feed a single value to every vertex here.
438
+ attributes.a_position.bind()
439
+ expect(gl.vertexAttribDivisor).toHaveBeenCalledWith(expect.any(Number), 0)
440
+ })
441
+
442
+ it('should restore the previous divisor rather than assume it was zero', () => {
443
+ const schema = {
444
+ a_instancePosition: { kind: 'vec3', instanced: true },
445
+ } satisfies AttributeSchema
446
+
447
+ const attributes = attributeView(gl, program, schema)
448
+ ;(gl.getVertexAttrib as any).mockReturnValue(2)
449
+
450
+ const unbind = attributes.a_instancePosition.bind()
451
+ unbind()
452
+ expect(gl.vertexAttribDivisor).toHaveBeenLastCalledWith(expect.any(Number), 2)
453
+ })
454
+
455
+ it('should return a disposer that only restores once', () => {
456
+ const schema = {
457
+ a_instancePosition: { kind: 'vec3', instanced: true },
458
+ } satisfies AttributeSchema
459
+
460
+ const attributes = attributeView(gl, program, schema)
461
+
462
+ const unbind = attributes.a_instancePosition.bind()
463
+ unbind()
464
+ const callsAfterFirst = (gl.vertexAttribDivisor as any).mock.calls.length
465
+ // Undoing twice would put back state a later bind() is relying on.
466
+ unbind()
467
+ expect((gl.vertexAttribDivisor as any).mock.calls).toHaveLength(callsAfterFirst)
468
+ })
469
+
412
470
  it('should use custom buffer if provided', () => {
413
471
  const customBuffer = gl.createBuffer()!
414
472
  const schema = {
@@ -434,7 +492,74 @@ describe('attributeView', () => {
434
492
  expect.any(Number),
435
493
  1, // size for float
436
494
  gl.FLOAT,
437
- false,
495
+ false, // normalized
496
+ 0,
497
+ 0,
498
+ )
499
+ })
500
+
501
+ it('should use format to override the GL buffer type', () => {
502
+ const schema = {
503
+ a_color: { kind: 'vec4', format: 'uint8', normalized: true },
504
+ a_normal: { kind: 'vec3', format: 'int8', normalized: true },
505
+ a_uv: { kind: 'vec2', format: 'uint16', normalized: true },
506
+ } satisfies AttributeSchema
507
+
508
+ const attributes = attributeView(gl, program, schema)
509
+
510
+ attributes.a_color.bind()
511
+ expect(gl.vertexAttribPointer).toHaveBeenCalledWith(
512
+ expect.any(Number),
513
+ 4,
514
+ gl.UNSIGNED_BYTE,
515
+ true,
516
+ 0,
517
+ 0,
518
+ )
519
+
520
+ attributes.a_normal.bind()
521
+ expect(gl.vertexAttribPointer).toHaveBeenCalledWith(
522
+ expect.any(Number),
523
+ 3,
524
+ gl.BYTE,
525
+ true,
526
+ 0,
527
+ 0,
528
+ )
529
+
530
+ attributes.a_uv.bind()
531
+ expect(gl.vertexAttribPointer).toHaveBeenCalledWith(
532
+ expect.any(Number),
533
+ 2,
534
+ gl.UNSIGNED_SHORT,
535
+ true,
536
+ 0,
537
+ 0,
538
+ )
539
+ })
540
+
541
+ it('should use int16/uint16 format for integer attribute kinds', () => {
542
+ const schema = {
543
+ a_index: { kind: 'int', format: 'int16' },
544
+ a_flags: { kind: 'uint', format: 'uint8' },
545
+ } satisfies AttributeSchema
546
+
547
+ const attributes = attributeView(gl, program, schema)
548
+
549
+ attributes.a_index.bind()
550
+ expect(gl.vertexAttribIPointer).toHaveBeenCalledWith(
551
+ expect.any(Number),
552
+ 1,
553
+ gl.SHORT,
554
+ 0,
555
+ 0,
556
+ )
557
+
558
+ attributes.a_flags.bind()
559
+ expect(gl.vertexAttribIPointer).toHaveBeenCalledWith(
560
+ expect.any(Number),
561
+ 1,
562
+ gl.UNSIGNED_BYTE,
438
563
  0,
439
564
  0,
440
565
  )
@@ -659,6 +784,34 @@ describe('interleavedAttributeView', () => {
659
784
  expect(interleavedAttributes.vertexData.set).toBeInstanceOf(Function)
660
785
  })
661
786
 
787
+ it('should return a disposer from bind that restores the default vertex array', () => {
788
+ const schema = {
789
+ vertexData: {
790
+ layout: [
791
+ { key: 'a_position', kind: 'vec3' },
792
+ { key: 'a_color', kind: 'vec4' },
793
+ ],
794
+ instanced: true,
795
+ },
796
+ } satisfies InterleavedAttributeSchema
797
+
798
+ const interleavedAttributes = interleavedAttributeView(gl, program, schema)
799
+
800
+ // A bound vertex array stays selected for everything drawn afterwards, so
801
+ // the next draw would write its own attributes into THIS array and corrupt
802
+ // the layer that owns it. The disposer hands the default back.
803
+ // Restoring the previous array rather than null keeps nested binds working.
804
+ const previous = { id: 'previously-bound-vao' }
805
+ ;(gl.getParameter as any).mockReturnValue(previous)
806
+
807
+ const unbind = interleavedAttributes.vertexData.bind()
808
+ expect(unbind).toBeInstanceOf(Function)
809
+
810
+ ;(gl.bindVertexArray as any).mockClear()
811
+ unbind()
812
+ expect(gl.bindVertexArray).toHaveBeenCalledWith(previous)
813
+ })
814
+
662
815
  it('should calculate correct stride and offsets', () => {
663
816
  const schema = {
664
817
  vertexData: {
@@ -742,6 +895,43 @@ describe('interleavedAttributeView', () => {
742
895
  expect(intCalls[1][2]).toBe(gl.INT) // ivec2
743
896
  })
744
897
 
898
+ it('should use format byte size for stride and use normalized flag', () => {
899
+ const schema = {
900
+ data: {
901
+ layout: [
902
+ { key: 'a_position', kind: 'vec2' }, // 2 * 4 = 8 bytes
903
+ { key: 'a_color', kind: 'vec4', format: 'uint8', normalized: true }, // 4 * 1 = 4 bytes
904
+ { key: 'a_normal', kind: 'vec2', format: 'int16', normalized: true }, // 2 * 2 = 4 bytes
905
+ ],
906
+ instanced: false,
907
+ },
908
+ } satisfies InterleavedAttributeSchema
909
+
910
+ const interleavedAttributes = interleavedAttributeView(gl, program, schema)
911
+ interleavedAttributes.data.bind()
912
+
913
+ const calls = (gl.vertexAttribPointer as any).mock.calls
914
+ const stride = 16 // 8 + 4 + 4
915
+
916
+ // a_position: float32, not normalized
917
+ expect(calls[0][2]).toBe(gl.FLOAT)
918
+ expect(calls[0][3]).toBe(false)
919
+ expect(calls[0][4]).toBe(stride)
920
+ expect(calls[0][5]).toBe(0)
921
+
922
+ // a_color: uint8, normalized
923
+ expect(calls[1][2]).toBe(gl.UNSIGNED_BYTE)
924
+ expect(calls[1][3]).toBe(true)
925
+ expect(calls[1][4]).toBe(stride)
926
+ expect(calls[1][5]).toBe(8)
927
+
928
+ // a_normal: int16, normalized
929
+ expect(calls[2][2]).toBe(gl.SHORT)
930
+ expect(calls[2][3]).toBe(true)
931
+ expect(calls[2][4]).toBe(stride)
932
+ expect(calls[2][5]).toBe(12)
933
+ })
934
+
745
935
  it('should set buffer data', () => {
746
936
  const schema = {
747
937
  vertexData: {
@@ -976,6 +1166,26 @@ describe('bufferView', () => {
976
1166
  expect(gl.bindBuffer).toHaveBeenCalledWith(gl.ELEMENT_ARRAY_BUFFER, expect.any(Object))
977
1167
  })
978
1168
 
1169
+ it('should return a disposer from bind that releases the target', () => {
1170
+ const schema = {
1171
+ indices: { target: 'ELEMENT_ARRAY_BUFFER' },
1172
+ } satisfies BufferSchema
1173
+
1174
+ const buffers = bufferView(gl, schema)
1175
+
1176
+ // An ELEMENT_ARRAY_BUFFER binding is recorded in whichever vertex array is
1177
+ // bound, so restoring the previous buffer rather than null keeps the
1178
+ // disposer from stripping the index buffer off an array we do not own.
1179
+ const previous = { id: 'previously-bound' }
1180
+ ;(gl.getParameter as any).mockReturnValue(previous)
1181
+
1182
+ const unbind = buffers.indices.bind()
1183
+ expect(unbind).toBeInstanceOf(Function)
1184
+
1185
+ unbind()
1186
+ expect(gl.bindBuffer).toHaveBeenLastCalledWith(gl.ELEMENT_ARRAY_BUFFER, previous)
1187
+ })
1188
+
979
1189
  it('should set buffer data with specified usage', () => {
980
1190
  const schema = {
981
1191
  vertices: { target: 'ARRAY_BUFFER', usage: 'STATIC_DRAW' },
@@ -38,11 +38,16 @@ export function createMockGL() {
38
38
  // Constants
39
39
  ARRAY_BUFFER: 0x8892,
40
40
  ELEMENT_ARRAY_BUFFER: 0x8893,
41
+ ARRAY_BUFFER_BINDING: 0x8894,
42
+ ELEMENT_ARRAY_BUFFER_BINDING: 0x8895,
41
43
  STATIC_DRAW: 0x88e4,
42
44
  DYNAMIC_DRAW: 0x88e8,
43
45
  FLOAT: 0x1406,
44
46
  INT: 0x1404,
45
47
  UNSIGNED_INT: 0x1405,
48
+ BYTE: 0x1400,
49
+ SHORT: 0x1402,
50
+ UNSIGNED_SHORT: 0x1403,
46
51
  UNSIGNED_BYTE: 0x1401,
47
52
  TRIANGLES: 0x0004,
48
53
  LINES: 0x0001,
@@ -124,6 +129,12 @@ export function createMockGL() {
124
129
  vertexAttribIPointer: vi.fn(),
125
130
  vertexAttribDivisor: vi.fn(),
126
131
 
132
+ // State queries. Real contexts always have these; the disposers returned by
133
+ // bind() snapshot through them. Default to "nothing bound / no divisor",
134
+ // which tests override where the previous value is what is under test.
135
+ getParameter: vi.fn(() => null),
136
+ getVertexAttrib: vi.fn(() => 0),
137
+
127
138
  // Uniform methods
128
139
  getUniformLocation: vi.fn((program: MockWebGLProgram, name: string) => {
129
140
  return { id: nextId++, name } as WebGLUniformLocation
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/utils.ts","../src/index.ts"],"sourcesContent":["import type {\n AttributeKind,\n FramebufferDefinition,\n GL,\n KIND_TO_SIZE_MAP,\n KIND_TO_UNIFORM_FN_NAME_MAP,\n TextureDefinition,\n UniformKind,\n} from './types'\n\nexport function assertedNotNullish<T>(value: T, message?: string): NonNullable<T> {\n if (value === undefined || value === null) throw new Error(message)\n return value\n}\n\n/**********************************************************************************/\n/* */\n/* Kind To Uniform Name */\n/* */\n/**********************************************************************************/\n\nexport const kindToUniformFnName = <T extends UniformKind>(\n kind: T,\n): KIND_TO_UNIFORM_FN_NAME_MAP[T] => {\n switch (kind[0]) {\n // mat\n case 'm':\n return ('Matrix' + kind.slice(3) + 'fv') as KIND_TO_UNIFORM_FN_NAME_MAP[T]\n // sampler/booleans/integer\n case 's':\n case 'b':\n case 'i':\n return ((kind.match(/\\d/)?.[0] ?? '1') + 'i') as KIND_TO_UNIFORM_FN_NAME_MAP[T]\n // unsigned integers\n case 'u':\n return ((kind[4] || '1') + 'ui') as KIND_TO_UNIFORM_FN_NAME_MAP[T]\n // vec\n case 'v':\n return (kind[3] + 'f') as KIND_TO_UNIFORM_FN_NAME_MAP[T]\n default:\n switch (kind) {\n case 'float':\n return '1f' as KIND_TO_UNIFORM_FN_NAME_MAP[T]\n case 'uint':\n return '1ui' as KIND_TO_UNIFORM_FN_NAME_MAP[T]\n default:\n return '1i' as KIND_TO_UNIFORM_FN_NAME_MAP[T]\n }\n }\n}\n\n/**********************************************************************************/\n/* */\n/* Kind To Size */\n/* */\n/**********************************************************************************/\n\nexport const kindToSize = <T extends UniformKind>(kind: T): KIND_TO_SIZE_MAP[T] => {\n switch (kind[0]) {\n case 'm':\n const [a, b] = kind.match(/\\d+/g)!.map(Number) as [number, number?]\n return (a * (b ?? a)) as KIND_TO_SIZE_MAP[T]\n case 'v':\n case 'i':\n case 'b':\n case 'u':\n const match = kind.match(/\\d/) as [string] | undefined\n return (match ? +match[0] : 1) as KIND_TO_SIZE_MAP[T]\n default:\n return 1 as KIND_TO_SIZE_MAP[T]\n }\n}\n\n/**********************************************************************************/\n/* */\n/* Guards */\n/* */\n/**********************************************************************************/\n\nexport const isMatKind = <\n T extends UniformKind | AttributeKind,\n TPrefix extends string,\n TPostfix extends string,\n>(\n kind: T,\n): kind is T & `${TPrefix}mat${TPostfix}` => kind.includes('mat')\n\nexport const isSamplerKind = <TPrefix extends string, TPostfix extends string>(\n kind: UniformKind,\n): kind is UniformKind & `${TPrefix}sampler${TPostfix}` => kind.includes('sampler')\n\nexport const isVecKind = <TPrefix extends string, TPostfix extends string>(\n kind: UniformKind,\n): kind is UniformKind & `${TPrefix}vec${TPostfix}` => kind.includes('vec')\n\n/**********************************************************************************/\n/* */\n/* Create Texture */\n/* */\n/**********************************************************************************/\n\nexport function createTexture(\n gl: GL,\n {\n target = 'TEXTURE_2D',\n internalFormat = 'RGBA',\n format = 'RGBA',\n type = 'UNSIGNED_BYTE',\n minFilter = 'NEAREST',\n magFilter = 'NEAREST',\n wrapS = 'CLAMP_TO_EDGE',\n wrapT = 'CLAMP_TO_EDGE',\n width,\n height,\n }: TextureDefinition,\n data?: ArrayBufferView | null,\n): WebGLTexture {\n const texture = gl.createTexture()\n\n function getTextureConstant(name: string) {\n if (!(name in gl)) {\n throw new Error(`Attempted to create webgl2-only texture (${name}) in webgl1`)\n }\n return gl[name as keyof typeof gl] as any\n }\n\n gl.bindTexture(gl[target], texture)\n gl.texImage2D(\n gl[target],\n 0,\n getTextureConstant(internalFormat),\n width,\n height,\n 0,\n getTextureConstant(format),\n getTextureConstant(type),\n data ?? null,\n )\n\n console.log(minFilter)\n // Set texture parameters\n gl.texParameteri(gl[target], gl.TEXTURE_MIN_FILTER, gl[minFilter])\n gl.texParameteri(gl[target], gl.TEXTURE_MAG_FILTER, gl[magFilter])\n gl.texParameteri(gl[target], gl.TEXTURE_WRAP_S, gl[wrapS])\n gl.texParameteri(gl[target], gl.TEXTURE_WRAP_T, gl[wrapT])\n\n return texture\n}\n\n/**********************************************************************************/\n/* */\n/* Create Framebuffer */\n/* */\n/**********************************************************************************/\n\nconst FRAMEBUFFER_ATTACHMENT_MAP = {\n color: 'COLOR_ATTACHMENT0',\n depth: 'DEPTH_ATTACHMENT',\n stencil: 'STENCIL_ATTACHMENT',\n depthStencil: 'DEPTH_STENCIL_ATTACHMENT',\n} as const\n\nclass FramebufferError extends Error {\n constructor(gl: GL, status: number) {\n let errorMessage = `Framebuffer '${name}' not complete. Status: `\n switch (status) {\n case gl.FRAMEBUFFER_INCOMPLETE_ATTACHMENT:\n errorMessage += 'FRAMEBUFFER_INCOMPLETE_ATTACHMENT'\n break\n case gl.FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT:\n errorMessage += 'FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT'\n break\n case gl.FRAMEBUFFER_INCOMPLETE_DIMENSIONS:\n errorMessage += 'FRAMEBUFFER_INCOMPLETE_DIMENSIONS'\n break\n case gl.FRAMEBUFFER_UNSUPPORTED:\n errorMessage += 'FRAMEBUFFER_UNSUPPORTED'\n break\n default:\n errorMessage += `Unknown (${status})`\n }\n super(errorMessage)\n }\n}\n\nexport function createFramebuffer(\n gl: GL,\n { attachment, texture, ...options }: FramebufferDefinition,\n) {\n // Create texture for the framebuffer\n texture ??= createTexture(gl, options)\n\n // Create framebuffer\n const framebuffer = assertedNotNullish(\n gl.createFramebuffer(),\n `Failed to create framebuffer: ${name}`,\n )\n\n gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffer)\n\n gl.framebufferTexture2D(\n gl.FRAMEBUFFER,\n // Determine attachment point based on attachment kind\n gl[FRAMEBUFFER_ATTACHMENT_MAP[attachment]],\n gl.TEXTURE_2D,\n texture,\n 0,\n )\n\n // Check framebuffer completeness\n const status = gl.checkFramebufferStatus(gl.FRAMEBUFFER)\n if (status !== gl.FRAMEBUFFER_COMPLETE) {\n throw new FramebufferError(gl, status)\n }\n\n return {\n texture,\n framebuffer,\n }\n}\n\n/**********************************************************************************/\n/* */\n/* Create Upsert */\n/* */\n/**********************************************************************************/\n\ntype UpsertMapKind<T, U> = Map<T, U> | (T extends WeakKey ? WeakMap<T, U> : never)\n\nexport function createUpsertMap<T extends UpsertMapKind<any, any> = Map<any, any>>(\n constructor?: new () => T,\n) {\n const map = new (constructor ?? Map)() as T\n return Object.assign(map, {\n getOrInsert(\n key: T extends UpsertMapKind<infer U, any> ? U : never,\n value: T extends UpsertMapKind<any, infer U> ? () => U : never,\n ) {\n let result = map.get(key)\n if (result) {\n return result\n }\n result = value()\n map.set(key, result)\n return result\n },\n })\n}\n\nexport function forEachObject<T extends Record<string, any>>(\n value: T,\n callback: (value: T[keyof T], key: keyof T, index: number) => void,\n) {\n let index = 0\n for (const key in value) {\n callback(value[key], key, index)\n index++\n }\n for (const key of Object.getOwnPropertySymbols(value)) {\n callback(value[key as keyof T], key as keyof T, index)\n index++\n }\n}\n\nexport function mapObject<T extends Record<string, any>, TReturn>(\n value: T,\n callback: (value: T[keyof T], key: keyof T, index: number) => TReturn,\n): { [TKey in keyof T]: TReturn } {\n const result = {} as { [TKey in keyof T]: TReturn }\n\n forEachObject(value, (value, key, index) => {\n result[key] = callback(value, key, index)\n })\n\n return result\n}\n","import { getInstancedArrays, getVertexArrayObject } from './gl'\nimport type {\n AttributeMethods,\n AttributeSchema,\n AttributeView,\n BufferSchema,\n BufferView,\n GL,\n UniformView as InferUniformView,\n InterleavedAttributeMethods,\n InterleavedAttributeSchema,\n InterleavedAttributeView,\n UniformSchema,\n View,\n ViewOptions,\n ViewSchema,\n} from './types'\nimport {\n isSamplerKind,\n kindToUniformFnName,\n isMatKind,\n assertedNotNullish,\n kindToSize,\n forEachObject,\n} from './utils'\nimport { createUpsertMap, mapObject } from './utils'\nexport * from './types'\n\n/**********************************************************************************/\n/* */\n/* To ID */\n/* */\n/**********************************************************************************/\n\nlet index = 0\nconst PREFIX = 'VIEW_GL_ALIAS'\n// Firefox has currently a bug where it can not handle symbol as WeakRef / key for WeakMap\n// see https://bugzilla.mozilla.org/show_bug.cgi?id=1710433\nconst HAS_WEAK_SYMBOL = (() => {\n try {\n const symbol = Symbol()\n new WeakMap().set(symbol, '')\n return true\n } catch {\n return false\n }\n})()\n// NOTE: fallback to Map will currently cause a minor memory leak.\nconst SYMBOL_MAP = createUpsertMap(HAS_WEAK_SYMBOL ? WeakMap<symbol, string> : Map<symbol, string>)\n\nexport function toID(key: string | number | symbol) {\n if (typeof key === 'string') {\n return key\n }\n if (typeof key === 'symbol') {\n return SYMBOL_MAP.getOrInsert(key, () => `${PREFIX}_${index++}`)\n }\n return key.toString()\n}\n\n/**********************************************************************************/\n/* */\n/* View */\n/* */\n/**********************************************************************************/\n\nexport function view<TSchema extends ViewSchema>(\n gl: GL,\n program: WebGLProgram,\n schema: TSchema,\n options?: ViewOptions,\n): View<TSchema> {\n return {\n uniforms: !schema.uniforms ? undefined : uniformView(gl, program, schema.uniforms),\n attributes: !schema.attributes\n ? undefined\n : attributeView(gl, program, schema.attributes, options),\n interleavedAttributes: !schema.interleavedAttributes\n ? undefined\n : interleavedAttributeView(gl, program, schema.interleavedAttributes, options),\n buffers: !schema.buffers ? undefined : bufferView(gl, schema.buffers),\n } as View<TSchema>\n}\n\n/**********************************************************************************/\n/* */\n/* Uniform View */\n/* */\n/**********************************************************************************/\n\nexport function uniformView<T extends UniformSchema>(\n gl: GL,\n program: WebGLProgram,\n schema: T,\n): InferUniformView<T> {\n // @ts-expect-error - conditional return type based on size is hard to infer\n return mapObject(schema, ({ kind, size }, key) => {\n const name = toID(key)\n\n if (isSamplerKind(kind)) {\n const location = gl.getUniformLocation(program, name)\n return {\n set(arg: number) {\n gl.uniform1i(location, arg)\n },\n }\n }\n\n // Array uniform - support both bulk set and indexed access\n if (size) {\n const bulkFnName = `uniform${kindToUniformFnName(kind)}v`\n // @ts-ignore FIX WEBGL/WEBGL2 TYPES\n const bulkFn = gl[bulkFnName].bind(gl)\n const bulkLocation = gl.getUniformLocation(program, name)\n\n const elementFnName = `uniform${kindToUniformFnName(kind)}`\n // @ts-ignore FIX WEBGL/WEBGL2 TYPES\n const elementFn = gl[elementFnName].bind(gl)\n\n // Build array of element setters\n const elements: Array<{ set(...args: any[]): void }> = []\n for (let i = 0; i < size; i++) {\n const location = gl.getUniformLocation(program, `${name}[${i}]`)\n elements[i] = {\n set(...args: any[]) {\n elementFn(location, ...args)\n },\n }\n }\n\n // Return array-like object with bulk set\n return Object.assign(elements, {\n set(arg: Float32Array) {\n bulkFn(bulkLocation, arg)\n },\n })\n }\n\n // Non-array uniform\n const location = gl.getUniformLocation(program, name)\n const fnName = `uniform${kindToUniformFnName(kind)}`\n\n // @ts-ignore FIX WEBGL/WEBGL2 TYPES\n const fn = gl[fnName].bind(gl)\n\n if (isMatKind(kind)) {\n return {\n set(...args: any[]) {\n fn(location, false, args[0])\n },\n }\n }\n\n return {\n set(...args: any[]) {\n fn(location, ...args)\n },\n }\n })\n}\n\n/**********************************************************************************/\n/* */\n/* Attribute View */\n/* */\n/**********************************************************************************/\n\n// Shared attribute helper functions\n// between attributeView and interleavedAttributeView\nfunction handleAttribute(\n gl: GL,\n location: number,\n size: number,\n stride: number,\n offset: number,\n type: 'FLOAT' | 'INT' | 'UNSIGNED_INT',\n instanced?: boolean,\n) {\n gl.enableVertexAttribArray(location)\n if (type === 'INT' || type === 'UNSIGNED_INT') {\n ;(gl as WebGL2RenderingContext).vertexAttribIPointer(location, size, gl[type], stride, offset)\n } else {\n gl.vertexAttribPointer(location, size, gl[type], false, stride, offset)\n }\n\n if (instanced) {\n // Get instanced-arrays-feature: extension if webgl, gl if webgl2\n assertedNotNullish(getInstancedArrays(gl)).vertexAttribDivisor(location, 1)\n }\n}\n\nexport function attributeView<T extends AttributeSchema>(\n gl: GL,\n program: WebGLProgram,\n schema: T,\n { signal }: ViewOptions = {},\n): AttributeView<T> {\n const attributes = mapObject(\n schema,\n (\n { kind, instanced, buffer = assertedNotNullish(gl.createBuffer()) },\n key,\n ): AttributeMethods => {\n const name = toID(key)\n\n const location = gl.getAttribLocation(program, name)\n if (location < 0) {\n throw new Error(`Attribute '${name}' not found`)\n }\n\n const size = kindToSize(kind)\n const type = kind.startsWith('u') ? 'UNSIGNED_INT' : kind.startsWith('i') ? 'INT' : 'FLOAT'\n\n return {\n buffer,\n bind() {\n gl.bindBuffer(gl.ARRAY_BUFFER, buffer)\n handleAttribute(gl, location, size, 0, 0, type, instanced)\n },\n dispose() {\n gl.deleteBuffer(buffer)\n },\n set(data, usage = 'STATIC_DRAW') {\n gl.bindBuffer(gl.ARRAY_BUFFER, buffer)\n gl.bufferData(gl.ARRAY_BUFFER, data, gl[usage])\n return this\n },\n }\n },\n )\n\n signal?.addEventListener('abort', function dispose() {\n forEachObject(attributes, value => value.dispose())\n })\n\n return attributes\n}\n\n/**********************************************************************************/\n/* */\n/* Interleaved Attribute View */\n/* */\n/**********************************************************************************/\n\nexport function interleavedAttributeView<T extends InterleavedAttributeSchema>(\n gl: GL,\n program: WebGLProgram,\n schema: T,\n { signal }: ViewOptions = {},\n): InterleavedAttributeView<T> {\n // Initialize interleaved attributes\n const interleavedAttributes = mapObject(schema, ({ layout, instanced }) => {\n // Increment number to keep track of offset\n let index = 0\n\n // Calculate layout information\n const handles = layout.map(layout => {\n const name = toID(layout.key)\n\n const location = gl.getAttribLocation(program, name)\n\n if (location < 0) {\n throw new Error(`Attribute '${name}' not found`)\n }\n\n const size = kindToSize(layout.kind)\n const offset = index\n const type = layout.kind.startsWith('u')\n ? 'UNSIGNED_INT'\n : layout.kind.startsWith('i')\n ? 'INT'\n : 'FLOAT'\n index += size * 4\n\n return () => handleAttribute(gl, location, size, stride, offset, type, instanced)\n })\n\n // Set stride to final index\n const stride = index\n\n // Create a buffer\n const buffer = assertedNotNullish(gl.createBuffer())\n\n // Create VAO to cache attribute state\n let vao: { unbind(): void; bind(): void; dispose(): void } | undefined = undefined\n\n // Get VAO-feature: extension if webgl1, gl if webgl2\n const feature = getVertexArrayObject(gl)\n if (feature) {\n const vertexArray = feature.createVertexArray()\n feature.bindVertexArray(vertexArray)\n gl.bindBuffer(gl.ARRAY_BUFFER, buffer)\n for (const handle of handles) {\n handle()\n }\n feature.bindVertexArray(null)\n vao = {\n unbind() {\n feature.bindVertexArray(null)\n },\n bind() {\n feature.bindVertexArray(vertexArray)\n },\n dispose() {\n feature.deleteVertexArray(vertexArray)\n },\n }\n }\n\n return {\n bind() {\n gl.bindBuffer(gl.ARRAY_BUFFER, buffer)\n if (vao) {\n vao.bind()\n } else {\n // Fallback: manual attribute setup\n for (const handle of handles) {\n handle()\n }\n }\n },\n unbind() {\n if (vao) {\n vao.unbind()\n }\n },\n dispose() {\n gl.deleteBuffer(buffer)\n if (vao) {\n vao.dispose()\n }\n },\n set(value, usage = 'STATIC_DRAW') {\n if (vao) {\n vao.bind()\n }\n gl.bindBuffer(gl.ARRAY_BUFFER, buffer)\n gl.bufferData(gl.ARRAY_BUFFER, value, gl[usage])\n if (vao) {\n vao.unbind()\n }\n },\n } satisfies InterleavedAttributeMethods\n })\n\n signal?.addEventListener('abort', function dispose() {\n forEachObject(interleavedAttributes, value => value.dispose())\n })\n\n return interleavedAttributes\n}\n\n/**********************************************************************************/\n/* */\n/* Buffer View */\n/* */\n/**********************************************************************************/\n\nexport function bufferView<T extends BufferSchema>(\n gl: GL,\n schema: T,\n { signal }: ViewOptions = {},\n): BufferView<T> {\n // Initialize buffers\n const buffers = mapObject(schema, ({ target = 'ARRAY_BUFFER', usage = 'STATIC_DRAW' }) => {\n const buffer = assertedNotNullish(gl.createBuffer())\n return {\n bind() {\n gl.bindBuffer(gl[target], buffer)\n },\n dispose() {\n gl.deleteBuffer(buffer)\n },\n set(data: Float32Array) {\n gl.bindBuffer(gl[target], buffer)\n gl.bufferData(gl[target], data, gl[usage])\n },\n }\n })\n\n signal?.addEventListener('abort', function dispose() {\n forEachObject(buffers, value => value.dispose())\n })\n\n return buffers\n}\n"],"mappings":"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","names":["index","value","name","location","index","layout"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/gl.ts"],"sourcesContent":["import { RemoveSuffix } from './type-utils'\nimport type { GL } from './types'\n\nfunction isWebGL2RenderingContext(gl: WebGLRenderingContext | WebGL2RenderingContext): 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,yBAAyB,IAAkF;AAClH,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":[]}