@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 +26 -0
- package/dist/{chunk-WSNE4JVQ.mjs → chunk-CJTT7WPV.mjs} +1 -1
- package/dist/chunk-CJTT7WPV.mjs.map +1 -0
- package/dist/{chunk-EBMXKCOV.mjs → chunk-ZP4HP5H7.mjs} +102 -18
- package/dist/chunk-ZP4HP5H7.mjs.map +1 -0
- package/dist/gl.d.mts +1 -1
- package/dist/gl.mjs +1 -1
- package/dist/index.d.mts +2 -2
- package/dist/index.mjs +2 -2
- package/dist/tag.d.mts +1 -1
- package/dist/tag.mjs +2 -2
- package/dist/{types-lm9fTTy8.d.mts → types-DTSPQclW.d.mts} +31 -8
- package/package.json +1 -1
- package/src/gl.ts +3 -1
- package/src/index.ts +151 -22
- package/src/types.ts +40 -6
- package/test/index.test.ts +211 -1
- package/test/mocks/webgl.ts +11 -0
- package/dist/chunk-EBMXKCOV.mjs.map +0 -1
- package/dist/chunk-WSNE4JVQ.mjs.map +0 -1
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
|
|
@@ -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-
|
|
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
|
-
|
|
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 (
|
|
192
|
+
if (isIntegerKind) {
|
|
170
193
|
;
|
|
171
|
-
gl.vertexAttribIPointer(location, size, gl[
|
|
194
|
+
gl.vertexAttribIPointer(location, size, gl[glType], stride, offset);
|
|
172
195
|
} else {
|
|
173
|
-
gl.vertexAttribPointer(location, size, gl[
|
|
196
|
+
gl.vertexAttribPointer(location, size, gl[glType], normalized, stride, offset);
|
|
174
197
|
}
|
|
198
|
+
const instancedArrays = getInstancedArrays(gl);
|
|
175
199
|
if (instanced) {
|
|
176
|
-
assertedNotNullish(
|
|
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
|
-
({
|
|
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
|
|
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,
|
|
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
|
|
224
|
-
|
|
225
|
-
|
|
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-
|
|
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-
|
|
1
|
+
import { G as GL, R as RemoveSuffix } from './types-DTSPQclW.mjs';
|
|
2
2
|
|
|
3
3
|
/**********************************************************************************/
|
|
4
4
|
/**********************************************************************************/
|
package/dist/gl.mjs
CHANGED
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-
|
|
2
|
-
export {
|
|
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
package/dist/tag.d.mts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
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-
|
|
5
|
+
} from "./chunk-ZP4HP5H7.mjs";
|
|
6
6
|
import {
|
|
7
7
|
createProgram
|
|
8
|
-
} from "./chunk-
|
|
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
|
-
|
|
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
|
-
|
|
198
|
-
|
|
199
|
-
|
|
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]
|
|
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
|
-
|
|
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
|
-
|
|
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 {
|
|
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
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(
|
|
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
|
-
|
|
213
|
+
glType: GLType,
|
|
214
|
+
isIntegerKind: boolean,
|
|
215
|
+
normalized: boolean,
|
|
177
216
|
instanced?: boolean,
|
|
178
217
|
) {
|
|
179
218
|
gl.enableVertexAttribArray(location)
|
|
180
|
-
if (
|
|
181
|
-
;(gl as WebGL2RenderingContext).vertexAttribIPointer(location, size, gl[
|
|
219
|
+
if (isIntegerKind) {
|
|
220
|
+
;(gl as WebGL2RenderingContext).vertexAttribIPointer(location, size, gl[glType], stride, offset)
|
|
182
221
|
} else {
|
|
183
|
-
gl.vertexAttribPointer(location, size, gl[
|
|
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
|
-
|
|
188
|
-
|
|
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
|
-
{
|
|
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
|
|
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,
|
|
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
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
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
|
-
|
|
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
|
-
|
|
299
|
-
|
|
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]
|
|
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
|
-
|
|
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
|
-
|
|
386
|
+
/** Binds the buffer, returning a disposer that restores the previous binding */
|
|
387
|
+
bind(): () => void
|
|
354
388
|
dispose(): void
|
|
355
389
|
}
|
|
356
390
|
|
package/test/index.test.ts
CHANGED
|
@@ -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' },
|
package/test/mocks/webgl.ts
CHANGED
|
@@ -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":[]}
|