@bigmistqke/view.gl 0.8.0 → 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -6,19 +6,21 @@ import {
6
6
  uniformView,
7
7
  vaoView,
8
8
  view
9
- } from "./chunk-7Z5UULAB.mjs";
9
+ } from "./chunk-NRCBGOZB.mjs";
10
10
  import {
11
11
  AttributeNotFoundError,
12
12
  GLCreateError,
13
+ InstancingUnavailableError,
13
14
  ProgramLinkError,
14
15
  ShaderCompileError,
15
16
  createFramebuffer,
16
17
  createTexture,
17
18
  created
18
- } from "./chunk-TQWPUDQI.mjs";
19
+ } from "./chunk-IAM2SWJK.mjs";
19
20
  export {
20
21
  AttributeNotFoundError,
21
22
  GLCreateError,
23
+ InstancingUnavailableError,
22
24
  ProgramLinkError,
23
25
  ShaderCompileError,
24
26
  attributeView,
package/dist/tag.d.mts CHANGED
@@ -1,4 +1,4 @@
1
- import { a8 as GLSLSlot, a9 as GLSL, ab as UniformTokenFn, ad as AttributeTokenFn, ac as AttributeToken, z as AttributeKind, D as AttributeDefinition, ae as InterleaveToken, a6 as ViewSchemaPartial, ak as CompileResult, al as Prettify, af as GLSLToSchema, G as GL } from './types-DZbMcty2.mjs';
1
+ import { a9 as GLSLSlot, aa as GLSL, ac as UniformTokenFn, ae as AttributeTokenFn, ad as AttributeToken, D as AttributeKind, H as AttributeDefinition, af as InterleaveToken, a7 as ViewSchemaPartial, G as GL, al as CompileResult, am as Prettify, ag as GLSLToSchema } from './types-Z2s1zsZz.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: {
@@ -81,7 +81,7 @@ interface CompileOptions<TSchema extends ViewSchemaPartial> {
81
81
  */
82
82
  signal?: AbortSignal;
83
83
  }
84
- declare function compile<TVertex extends GLSL, TFragment extends GLSL, TSchema extends ViewSchemaPartial>(gl: WebGLRenderingContext, vertex: TVertex, fragment: TFragment, options?: CompileOptions<TSchema> | undefined): CompileResult<TVertex, TFragment, TSchema> extends infer T ? { [K in keyof T]: CompileResult<TVertex, TFragment, TSchema>[K]; } : never;
84
+ declare function compile<TVertex extends GLSL, TFragment extends GLSL, TSchema extends ViewSchemaPartial>(gl: GL, vertex: TVertex, fragment: TFragment, options?: CompileOptions<TSchema> | undefined): CompileResult<TVertex, TFragment, TSchema> extends infer T ? { [K in keyof T]: CompileResult<TVertex, TFragment, TSchema>[K]; } : never;
85
85
  declare namespace compile {
86
86
  var toSchema: <TTag extends GLSL>(tag: TTag) => Prettify<GLSLToSchema<TTag>>;
87
87
  var toString: <TTag extends GLSL>({ template: [initial, ...rest], slots }: TTag, config?: {
package/dist/tag.mjs CHANGED
@@ -1,12 +1,13 @@
1
+ import {
2
+ createProgram
3
+ } from "./chunk-UVDTF5P3.mjs";
1
4
  import {
2
5
  toID,
3
6
  view
4
- } from "./chunk-7Z5UULAB.mjs";
7
+ } from "./chunk-NRCBGOZB.mjs";
5
8
  import {
6
- createProgram,
7
- createUpsertMap,
8
9
  once
9
- } from "./chunk-TQWPUDQI.mjs";
10
+ } from "./chunk-IAM2SWJK.mjs";
10
11
 
11
12
  // src/tag.ts
12
13
  function glsl(template, ...slots) {
@@ -185,14 +186,12 @@ function resolveGlslSlotToString(slot, v300) {
185
186
  throw new Error(`Unexpected slot: ${JSON.stringify(slot)}`);
186
187
  }
187
188
  var QUAD_FLOAT_ARRAY = new Float32Array([-1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, 1]);
188
- var QUAD_BUFFER_MAP = createUpsertMap(WeakMap);
189
189
  compile.toQuad = function(gl, fragment, options) {
190
- const buffer = QUAD_BUFFER_MAP.getOrInsert(gl, gl.createBuffer.bind(gl));
191
190
  const webgl2 = options?.webgl2 ?? fragment.template[0]?.startsWith("#version 300 es");
192
191
  const vertex = webgl2 ? glsl`#version 300 es
193
192
  precision mediump float;
194
193
 
195
- ${attribute.vec2("a_quad", { buffer })}
194
+ ${attribute.vec2("a_quad")}
196
195
 
197
196
  out vec2 v_uv;
198
197
 
@@ -201,7 +200,7 @@ void main() {
201
200
  gl_Position = vec4(v_uv, 0.0, 1.0);
202
201
  }` : glsl`precision mediump float;
203
202
 
204
- ${attribute.vec2("a_quad", { buffer })}
203
+ ${attribute.vec2("a_quad")}
205
204
 
206
205
  varying vec2 v_uv;
207
206
 
package/dist/tag.mjs.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/tag.ts"],"sourcesContent":["import { toID, view } from '.'\nimport { createProgram } from './gl'\nimport type { Prettify } from './type-utils'\nimport type {\n AttributeDefinition,\n AttributeKind,\n AttributeTokenFn as AttributeTagMethod,\n AttributeToken,\n CompileResult,\n GL,\n GLSL,\n GLSLSlot,\n GLSLToSchema,\n InterleaveToken,\n UniformDefinition,\n UniformKind,\n UniformTokenFn as UniformTagMethod,\n ViewSchema,\n ViewSchemaPartial,\n} from './types'\nimport { createUpsertMap, once } from './utils'\n\nexport function glsl<TSlot extends GLSLSlot, const TSlots extends TSlot[]>(\n template: TemplateStringsArray,\n ...slots: [...TSlots]\n): GLSL<TSlots> {\n return { template, slots, type: 'glsl' }\n}\n\nexport const uniform = new Proxy(\n {} as {\n [TKey in UniformKind]: UniformTagMethod<TKey>\n },\n {\n get(target, property) {\n // @ts-expect-error\n if (typeof property === 'symbol') return target[property]\n return (key: string, options?: Omit<UniformDefinition, 'kind'>) => ({\n type: 'uniform',\n key,\n kind: property,\n ...options,\n })\n },\n },\n)\n\nexport const attribute = new Proxy(\n {} as {\n [TKey in AttributeKind]: AttributeTagMethod<TKey>\n },\n {\n get(target, property) {\n // @ts-expect-error\n if (typeof property === 'symbol') return target[property]\n return (key: string, options?: Omit<AttributeDefinition, 'kind'>) => ({\n type: 'attribute',\n key,\n kind: property,\n ...options,\n })\n },\n },\n)\n\nexport function interleave<\n const TKey extends string | symbol,\n const TLayout extends Array<AttributeToken<string | symbol, AttributeKind, undefined>>,\n const TOptions extends Omit<AttributeDefinition, 'kind'>,\n>(key: TKey, layout: TLayout, instanced: TOptions): InterleaveToken<TKey, TLayout, TOptions>\nexport function interleave<\n const TKey extends string | symbol,\n const TLayout extends Array<AttributeToken<string | symbol, AttributeKind, undefined>>,\n>(key: TKey, layout: TLayout): InterleaveToken<TKey, TLayout, { instanced: false }>\nexport function interleave<\n const TKey extends string | symbol,\n const TLayout extends Array<AttributeToken<string | symbol, AttributeKind, undefined>>,\n const TOptions extends Omit<AttributeDefinition, 'kind'>,\n>(key: TKey, layout: TLayout, { instanced, buffer }: Partial<TOptions> = {}) {\n return {\n type: 'interleavedAttribute',\n key,\n // remove instanced- and type-property\n layout: layout.map(({ key, kind }) => ({\n key,\n kind,\n })),\n instanced: !!instanced,\n buffer,\n } as unknown as InterleaveToken<TKey, TLayout, TOptions>\n}\n\n/**********************************************************************************/\n/* */\n/* Compile */\n/* */\n/**********************************************************************************/\n\nexport interface CompileOptions<TSchema extends ViewSchemaPartial> {\n schema?: TSchema\n webgl2?: boolean\n /**\n * Disposes what `compile` made: the program, and the buffers the view created\n * for itself. Buffers named by the schema belong to whoever made them.\n */\n signal?: AbortSignal\n}\n\nexport function compile<\n TVertex extends GLSL,\n TFragment extends GLSL,\n TSchema extends ViewSchemaPartial,\n>(\n gl: WebGLRenderingContext,\n vertex: TVertex,\n fragment: TFragment,\n options?: CompileOptions<TSchema> | undefined,\n) {\n const _vertex = resolveGLSLTag(vertex, options) as { template: string; schema: ViewSchema }\n const _fragment = resolveGLSLTag(fragment, options) as { template: string; schema: ViewSchema }\n\n const schema = {\n uniforms: {\n ..._vertex.schema.uniforms,\n ..._fragment.schema.uniforms,\n },\n attributes: {\n ..._vertex.schema.attributes,\n ..._fragment.schema.attributes,\n },\n interleavedAttributes: {\n ..._vertex.schema.interleavedAttributes,\n ..._fragment.schema.interleavedAttributes,\n },\n // Never produced by a tag — a buffer has no name in the source to be\n // parsed out of one — but `options.schema` may name buffers for the view to\n // own, and the deep merge below indexes this object by every key it finds\n // there. Missing, that threw.\n buffers: {} as NonNullable<ViewSchema['buffers']>,\n }\n\n // Deep merge options.schema with generated schema\n for (const kind in options?.schema) {\n const schemaKind = schema[kind as keyof typeof schema]\n const configSchemaKind = options?.schema[kind]\n\n for (const key in configSchemaKind) {\n schemaKind[key] = {\n ...schemaKind[key],\n ...configSchemaKind[key],\n }\n }\n }\n\n try {\n const program = createProgram(gl, _vertex.template, _fragment.template)\n\n // The same rule the views follow: `compile` made this program, so `compile`\n // is what deletes it. A program handed in from outside would not be ours to\n // delete, and there is no way to hand one in.\n const resolvedView = view(gl, program, schema)\n const dispose = once(() => {\n resolvedView.dispose()\n gl.deleteProgram(program)\n })\n options?.signal?.addEventListener('abort', dispose)\n\n return {\n program,\n schema,\n view: resolvedView,\n dispose,\n vertex: _vertex.template,\n fragment: _fragment.template,\n } as unknown as Prettify<CompileResult<TVertex, TFragment, TSchema>>\n } catch (error) {\n console.error('Error while creating WebGLProgram - vertex\\n\\n', _vertex.template)\n console.error('Error while creating WebGLProgram - fragment\\n\\n', _fragment.template)\n\n throw error\n }\n}\n\nfunction resolveGLSLTag<TTag extends GLSL>(tag: TTag, options?: { webgl2?: boolean }) {\n return {\n template: compile.toString(tag, options),\n schema: compile.toSchema(tag),\n }\n}\n\ncompile.toSchema = function <TTag extends GLSL>(tag: TTag) {\n const result = {\n uniforms: {},\n attributes: {},\n interleavedAttributes: {},\n }\n\n tag.slots.forEach(function handleSlot(slot: GLSLSlot) {\n if (typeof slot !== 'object') {\n return\n }\n\n if (Array.isArray(slot)) {\n slot.forEach(handleSlot)\n return\n }\n\n if (slot.type === 'glsl') {\n const { uniforms, attributes, interleavedAttributes } = compile.toSchema(slot)\n\n result.uniforms = {\n ...result.uniforms,\n ...uniforms,\n }\n result.attributes = {\n ...result.attributes,\n ...attributes,\n }\n result.interleavedAttributes = {\n ...result.interleavedAttributes,\n ...interleavedAttributes,\n }\n\n return\n }\n\n const { key: name, type, ...rest } = slot\n\n // @ts-expect-error\n result[`${type}s`][name] = rest\n })\n\n return result as unknown as Prettify<GLSLToSchema<TTag>>\n}\n\ncompile.toString = function <TTag extends GLSL>(\n { template: [initial, ...rest], slots }: TTag,\n config?: { webgl2?: boolean },\n) {\n const v300 = config?.webgl2 ?? !!initial?.startsWith('#version 300 es')\n\n let template = initial ?? ''\n\n for (let i = 0; i < rest.length; i++) {\n template += `${resolveGlslSlotToString(slots[i]!, v300)}${rest[i]}`\n }\n\n return template\n}\n\nfunction resolveGlslSlotToString(slot: GLSLSlot, v300: boolean): string {\n if (typeof slot !== 'object') {\n return toID(slot)\n }\n\n if (Array.isArray(slot)) {\n return slot.map(slot => resolveGlslSlotToString(slot, v300)).join('')\n }\n\n switch (slot.type) {\n case 'glsl':\n return compile.toString(slot)\n case 'interleavedAttribute':\n return slot.layout.reduce(\n (a, v) =>\n v300 ? `${a}in ${v.kind} ${toID(v.key)};\\n` : `${a}attribute ${v.kind} ${toID(v.key)};\\n`,\n '',\n )\n case 'uniform':\n if ('size' in slot) {\n return `${slot.type} ${slot.kind} ${toID(slot.key)}[${slot.size}];`\n }\n return `${slot.type} ${slot.kind} ${toID(slot.key)};`\n }\n\n if (slot.type === 'attribute') {\n return `${v300 ? 'in' : slot.type} ${slot.kind} ${toID(slot.key)};`\n }\n\n throw new Error(`Unexpected slot: ${JSON.stringify(slot)}`)\n}\n\nconst QUAD_FLOAT_ARRAY = new Float32Array([-1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, 1])\nconst QUAD_BUFFER_MAP = createUpsertMap(WeakMap<GL, WebGLBuffer>)\n\ncompile.toQuad = function <TFragment extends GLSL, TSchema extends ViewSchemaPartial>(\n gl: GL,\n fragment: TFragment,\n options?: CompileOptions<TSchema>,\n) {\n const buffer = QUAD_BUFFER_MAP.getOrInsert(gl, gl.createBuffer.bind(gl))\n\n const webgl2 = options?.webgl2 ?? fragment.template[0]?.startsWith('#version 300 es')\n\n const vertex = webgl2\n ? glsl`#version 300 es\nprecision mediump float;\n\n${attribute.vec2('a_quad', { buffer })}\n\nout vec2 v_uv;\n\nvoid main() {\n v_uv = a_quad;\n gl_Position = vec4(v_uv, 0.0, 1.0);\n}`\n : glsl`precision mediump float;\n\n${attribute.vec2('a_quad', { buffer })}\n\nvarying vec2 v_uv;\n\nvoid main() {\n v_uv = a_quad;\n gl_Position = vec4(v_uv, 0.0, 1.0);\n}`\n\n const result = compile(gl, vertex, fragment, options)\n\n // @ts-expect-error\n result.view.attributes.a_quad.set(QUAD_FLOAT_ARRAY)\n\n return result\n}\n"],"mappings":";;;;;;;;;;;AAsBO,SAAS,KACd,aACG,OACW;AACd,SAAO,EAAE,UAAU,OAAO,MAAM,OAAO;AACzC;AAEO,IAAM,UAAU,IAAI;AAAA,EACzB,CAAC;AAAA,EAGD;AAAA,IACE,IAAI,QAAQ,UAAU;AAEpB,UAAI,OAAO,aAAa,SAAU,QAAO,OAAO,QAAQ;AACxD,aAAO,CAAC,KAAa,aAA+C;AAAA,QAClE,MAAM;AAAA,QACN;AAAA,QACA,MAAM;AAAA,QACN,GAAG;AAAA,MACL;AAAA,IACF;AAAA,EACF;AACF;AAEO,IAAM,YAAY,IAAI;AAAA,EAC3B,CAAC;AAAA,EAGD;AAAA,IACE,IAAI,QAAQ,UAAU;AAEpB,UAAI,OAAO,aAAa,SAAU,QAAO,OAAO,QAAQ;AACxD,aAAO,CAAC,KAAa,aAAiD;AAAA,QACpE,MAAM;AAAA,QACN;AAAA,QACA,MAAM;AAAA,QACN,GAAG;AAAA,MACL;AAAA,IACF;AAAA,EACF;AACF;AAWO,SAAS,WAId,KAAW,QAAiB,EAAE,WAAW,OAAO,IAAuB,CAAC,GAAG;AAC3E,SAAO;AAAA,IACL,MAAM;AAAA,IACN;AAAA;AAAA,IAEA,QAAQ,OAAO,IAAI,CAAC,EAAE,KAAAA,MAAK,KAAK,OAAO;AAAA,MACrC,KAAAA;AAAA,MACA;AAAA,IACF,EAAE;AAAA,IACF,WAAW,CAAC,CAAC;AAAA,IACb;AAAA,EACF;AACF;AAkBO,SAAS,QAKd,IACA,QACA,UACA,SACA;AACA,QAAM,UAAU,eAAe,QAAQ,OAAO;AAC9C,QAAM,YAAY,eAAe,UAAU,OAAO;AAElD,QAAM,SAAS;AAAA,IACb,UAAU;AAAA,MACR,GAAG,QAAQ,OAAO;AAAA,MAClB,GAAG,UAAU,OAAO;AAAA,IACtB;AAAA,IACA,YAAY;AAAA,MACV,GAAG,QAAQ,OAAO;AAAA,MAClB,GAAG,UAAU,OAAO;AAAA,IACtB;AAAA,IACA,uBAAuB;AAAA,MACrB,GAAG,QAAQ,OAAO;AAAA,MAClB,GAAG,UAAU,OAAO;AAAA,IACtB;AAAA;AAAA;AAAA;AAAA;AAAA,IAKA,SAAS,CAAC;AAAA,EACZ;AAGA,aAAW,QAAQ,SAAS,QAAQ;AAClC,UAAM,aAAa,OAAO,IAA2B;AACrD,UAAM,mBAAmB,SAAS,OAAO,IAAI;AAE7C,eAAW,OAAO,kBAAkB;AAClC,iBAAW,GAAG,IAAI;AAAA,QAChB,GAAG,WAAW,GAAG;AAAA,QACjB,GAAG,iBAAiB,GAAG;AAAA,MACzB;AAAA,IACF;AAAA,EACF;AAEA,MAAI;AACF,UAAM,UAAU,cAAc,IAAI,QAAQ,UAAU,UAAU,QAAQ;AAKtE,UAAM,eAAe,KAAK,IAAI,SAAS,MAAM;AAC7C,UAAM,UAAU,KAAK,MAAM;AACzB,mBAAa,QAAQ;AACrB,SAAG,cAAc,OAAO;AAAA,IAC1B,CAAC;AACD,aAAS,QAAQ,iBAAiB,SAAS,OAAO;AAElD,WAAO;AAAA,MACL;AAAA,MACA;AAAA,MACA,MAAM;AAAA,MACN;AAAA,MACA,QAAQ,QAAQ;AAAA,MAChB,UAAU,UAAU;AAAA,IACtB;AAAA,EACF,SAAS,OAAO;AACd,YAAQ,MAAM,kDAAkD,QAAQ,QAAQ;AAChF,YAAQ,MAAM,oDAAoD,UAAU,QAAQ;AAEpF,UAAM;AAAA,EACR;AACF;AAEA,SAAS,eAAkC,KAAW,SAAgC;AACpF,SAAO;AAAA,IACL,UAAU,QAAQ,SAAS,KAAK,OAAO;AAAA,IACvC,QAAQ,QAAQ,SAAS,GAAG;AAAA,EAC9B;AACF;AAEA,QAAQ,WAAW,SAA6B,KAAW;AACzD,QAAM,SAAS;AAAA,IACb,UAAU,CAAC;AAAA,IACX,YAAY,CAAC;AAAA,IACb,uBAAuB,CAAC;AAAA,EAC1B;AAEA,MAAI,MAAM,QAAQ,SAAS,WAAW,MAAgB;AACpD,QAAI,OAAO,SAAS,UAAU;AAC5B;AAAA,IACF;AAEA,QAAI,MAAM,QAAQ,IAAI,GAAG;AACvB,WAAK,QAAQ,UAAU;AACvB;AAAA,IACF;AAEA,QAAI,KAAK,SAAS,QAAQ;AACxB,YAAM,EAAE,UAAU,YAAY,sBAAsB,IAAI,QAAQ,SAAS,IAAI;AAE7E,aAAO,WAAW;AAAA,QAChB,GAAG,OAAO;AAAA,QACV,GAAG;AAAA,MACL;AACA,aAAO,aAAa;AAAA,QAClB,GAAG,OAAO;AAAA,QACV,GAAG;AAAA,MACL;AACA,aAAO,wBAAwB;AAAA,QAC7B,GAAG,OAAO;AAAA,QACV,GAAG;AAAA,MACL;AAEA;AAAA,IACF;AAEA,UAAM,EAAE,KAAK,MAAM,MAAM,GAAG,KAAK,IAAI;AAGrC,WAAO,GAAG,IAAI,GAAG,EAAE,IAAI,IAAI;AAAA,EAC7B,CAAC;AAED,SAAO;AACT;AAEA,QAAQ,WAAW,SACjB,EAAE,UAAU,CAAC,SAAS,GAAG,IAAI,GAAG,MAAM,GACtC,QACA;AACA,QAAM,OAAO,QAAQ,UAAU,CAAC,CAAC,SAAS,WAAW,iBAAiB;AAEtE,MAAI,WAAW,WAAW;AAE1B,WAAS,IAAI,GAAG,IAAI,KAAK,QAAQ,KAAK;AACpC,gBAAY,GAAG,wBAAwB,MAAM,CAAC,GAAI,IAAI,CAAC,GAAG,KAAK,CAAC,CAAC;AAAA,EACnE;AAEA,SAAO;AACT;AAEA,SAAS,wBAAwB,MAAgB,MAAuB;AACtE,MAAI,OAAO,SAAS,UAAU;AAC5B,WAAO,KAAK,IAAI;AAAA,EAClB;AAEA,MAAI,MAAM,QAAQ,IAAI,GAAG;AACvB,WAAO,KAAK,IAAI,CAAAC,UAAQ,wBAAwBA,OAAM,IAAI,CAAC,EAAE,KAAK,EAAE;AAAA,EACtE;AAEA,UAAQ,KAAK,MAAM;AAAA,IACjB,KAAK;AACH,aAAO,QAAQ,SAAS,IAAI;AAAA,IAC9B,KAAK;AACH,aAAO,KAAK,OAAO;AAAA,QACjB,CAAC,GAAG,MACF,OAAO,GAAG,CAAC,MAAM,EAAE,IAAI,IAAI,KAAK,EAAE,GAAG,CAAC;AAAA,IAAQ,GAAG,CAAC,aAAa,EAAE,IAAI,IAAI,KAAK,EAAE,GAAG,CAAC;AAAA;AAAA,QACtF;AAAA,MACF;AAAA,IACF,KAAK;AACH,UAAI,UAAU,MAAM;AAClB,eAAO,GAAG,KAAK,IAAI,IAAI,KAAK,IAAI,IAAI,KAAK,KAAK,GAAG,CAAC,IAAI,KAAK,IAAI;AAAA,MACjE;AACA,aAAO,GAAG,KAAK,IAAI,IAAI,KAAK,IAAI,IAAI,KAAK,KAAK,GAAG,CAAC;AAAA,EACtD;AAEA,MAAI,KAAK,SAAS,aAAa;AAC7B,WAAO,GAAG,OAAO,OAAO,KAAK,IAAI,IAAI,KAAK,IAAI,IAAI,KAAK,KAAK,GAAG,CAAC;AAAA,EAClE;AAEA,QAAM,IAAI,MAAM,oBAAoB,KAAK,UAAU,IAAI,CAAC,EAAE;AAC5D;AAEA,IAAM,mBAAmB,IAAI,aAAa,CAAC,IAAI,IAAI,GAAG,IAAI,IAAI,GAAG,IAAI,GAAG,GAAG,IAAI,GAAG,CAAC,CAAC;AACpF,IAAM,kBAAkB,gBAAgB,OAAwB;AAEhE,QAAQ,SAAS,SACf,IACA,UACA,SACA;AACA,QAAM,SAAS,gBAAgB,YAAY,IAAI,GAAG,aAAa,KAAK,EAAE,CAAC;AAEvE,QAAM,SAAS,SAAS,UAAU,SAAS,SAAS,CAAC,GAAG,WAAW,iBAAiB;AAEpF,QAAM,SAAS,SACX;AAAA;AAAA;AAAA,EAGJ,UAAU,KAAK,UAAU,EAAE,OAAO,CAAC,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,KAQhC;AAAA;AAAA,EAEJ,UAAU,KAAK,UAAU,EAAE,OAAO,CAAC,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AASpC,QAAM,SAAS,QAAQ,IAAI,QAAQ,UAAU,OAAO;AAGpD,SAAO,KAAK,WAAW,OAAO,IAAI,gBAAgB;AAElD,SAAO;AACT;","names":["key","slot"]}
1
+ {"version":3,"sources":["../src/tag.ts"],"sourcesContent":["import { toID, view } from '.'\nimport { createProgram } from './gl'\nimport type { Prettify } from './type-utils'\nimport type {\n AttributeDefinition,\n AttributeKind,\n AttributeTokenFn as AttributeTagMethod,\n AttributeToken,\n CompileResult,\n GL,\n GLSL,\n GLSLSlot,\n GLSLToSchema,\n InterleaveToken,\n UniformDefinition,\n UniformKind,\n UniformTokenFn as UniformTagMethod,\n ViewSchema,\n ViewSchemaPartial,\n} from './types'\nimport { once } from './utils'\n\nexport function glsl<TSlot extends GLSLSlot, const TSlots extends TSlot[]>(\n template: TemplateStringsArray,\n ...slots: [...TSlots]\n): GLSL<TSlots> {\n return { template, slots, type: 'glsl' }\n}\n\nexport const uniform = new Proxy(\n {} as {\n [TKey in UniformKind]: UniformTagMethod<TKey>\n },\n {\n get(target, property) {\n // @ts-expect-error\n if (typeof property === 'symbol') return target[property]\n return (key: string, options?: Omit<UniformDefinition, 'kind'>) => ({\n type: 'uniform',\n key,\n kind: property,\n ...options,\n })\n },\n },\n)\n\nexport const attribute = new Proxy(\n {} as {\n [TKey in AttributeKind]: AttributeTagMethod<TKey>\n },\n {\n get(target, property) {\n // @ts-expect-error\n if (typeof property === 'symbol') return target[property]\n return (key: string, options?: Omit<AttributeDefinition, 'kind'>) => ({\n type: 'attribute',\n key,\n kind: property,\n ...options,\n })\n },\n },\n)\n\nexport function interleave<\n const TKey extends string | symbol,\n const TLayout extends Array<AttributeToken<string | symbol, AttributeKind, undefined>>,\n const TOptions extends Omit<AttributeDefinition, 'kind'>,\n>(key: TKey, layout: TLayout, instanced: TOptions): InterleaveToken<TKey, TLayout, TOptions>\nexport function interleave<\n const TKey extends string | symbol,\n const TLayout extends Array<AttributeToken<string | symbol, AttributeKind, undefined>>,\n>(key: TKey, layout: TLayout): InterleaveToken<TKey, TLayout, { instanced: false }>\nexport function interleave<\n const TKey extends string | symbol,\n const TLayout extends Array<AttributeToken<string | symbol, AttributeKind, undefined>>,\n const TOptions extends Omit<AttributeDefinition, 'kind'>,\n>(key: TKey, layout: TLayout, { instanced, buffer }: Partial<TOptions> = {}) {\n return {\n type: 'interleavedAttribute',\n key,\n // remove instanced- and type-property\n layout: layout.map(({ key, kind }) => ({\n key,\n kind,\n })),\n instanced: !!instanced,\n buffer,\n } as unknown as InterleaveToken<TKey, TLayout, TOptions>\n}\n\n/**********************************************************************************/\n/* */\n/* Compile */\n/* */\n/**********************************************************************************/\n\nexport interface CompileOptions<TSchema extends ViewSchemaPartial> {\n schema?: TSchema\n webgl2?: boolean\n /**\n * Disposes what `compile` made: the program, and the buffers the view created\n * for itself. Buffers named by the schema belong to whoever made them.\n */\n signal?: AbortSignal\n}\n\nexport function compile<\n TVertex extends GLSL,\n TFragment extends GLSL,\n TSchema extends ViewSchemaPartial,\n>(gl: GL, vertex: TVertex, fragment: TFragment, options?: CompileOptions<TSchema> | undefined) {\n const _vertex = resolveGLSLTag(vertex, options) as { template: string; schema: ViewSchema }\n const _fragment = resolveGLSLTag(fragment, options) as { template: string; schema: ViewSchema }\n\n const schema = {\n uniforms: {\n ..._vertex.schema.uniforms,\n ..._fragment.schema.uniforms,\n },\n attributes: {\n ..._vertex.schema.attributes,\n ..._fragment.schema.attributes,\n },\n interleavedAttributes: {\n ..._vertex.schema.interleavedAttributes,\n ..._fragment.schema.interleavedAttributes,\n },\n // Never produced by a tag — a buffer has no name in the source to be\n // parsed out of one — but `options.schema` may name buffers for the view to\n // own, and the deep merge below indexes this object by every key it finds\n // there. Missing, that threw.\n buffers: {} as NonNullable<ViewSchema['buffers']>,\n }\n\n // Deep merge options.schema with generated schema\n for (const kind in options?.schema) {\n const schemaKind = schema[kind as keyof typeof schema]\n const configSchemaKind = options?.schema[kind]\n\n for (const key in configSchemaKind) {\n schemaKind[key] = {\n ...schemaKind[key],\n ...configSchemaKind[key],\n }\n }\n }\n\n try {\n const program = createProgram(gl, _vertex.template, _fragment.template)\n\n // The same rule the views follow: `compile` made this program, so `compile`\n // is what deletes it. A program handed in from outside would not be ours to\n // delete, and there is no way to hand one in.\n const resolvedView = view(gl, program, schema)\n const dispose = once(() => {\n resolvedView.dispose()\n gl.deleteProgram(program)\n })\n options?.signal?.addEventListener('abort', dispose)\n\n return {\n program,\n schema,\n view: resolvedView,\n dispose,\n vertex: _vertex.template,\n fragment: _fragment.template,\n } as unknown as Prettify<CompileResult<TVertex, TFragment, TSchema>>\n } catch (error) {\n console.error('Error while creating WebGLProgram - vertex\\n\\n', _vertex.template)\n console.error('Error while creating WebGLProgram - fragment\\n\\n', _fragment.template)\n\n throw error\n }\n}\n\nfunction resolveGLSLTag<TTag extends GLSL>(tag: TTag, options?: { webgl2?: boolean }) {\n return {\n template: compile.toString(tag, options),\n schema: compile.toSchema(tag),\n }\n}\n\ncompile.toSchema = function <TTag extends GLSL>(tag: TTag) {\n const result = {\n uniforms: {},\n attributes: {},\n interleavedAttributes: {},\n }\n\n tag.slots.forEach(function handleSlot(slot: GLSLSlot) {\n if (typeof slot !== 'object') {\n return\n }\n\n if (Array.isArray(slot)) {\n slot.forEach(handleSlot)\n return\n }\n\n if (slot.type === 'glsl') {\n const { uniforms, attributes, interleavedAttributes } = compile.toSchema(slot)\n\n result.uniforms = {\n ...result.uniforms,\n ...uniforms,\n }\n result.attributes = {\n ...result.attributes,\n ...attributes,\n }\n result.interleavedAttributes = {\n ...result.interleavedAttributes,\n ...interleavedAttributes,\n }\n\n return\n }\n\n const { key: name, type, ...rest } = slot\n\n // @ts-expect-error\n result[`${type}s`][name] = rest\n })\n\n return result as unknown as Prettify<GLSLToSchema<TTag>>\n}\n\ncompile.toString = function <TTag extends GLSL>(\n { template: [initial, ...rest], slots }: TTag,\n config?: { webgl2?: boolean },\n) {\n const v300 = config?.webgl2 ?? !!initial?.startsWith('#version 300 es')\n\n let template = initial ?? ''\n\n for (let i = 0; i < rest.length; i++) {\n template += `${resolveGlslSlotToString(slots[i]!, v300)}${rest[i]}`\n }\n\n return template\n}\n\nfunction resolveGlslSlotToString(slot: GLSLSlot, v300: boolean): string {\n if (typeof slot !== 'object') {\n return toID(slot)\n }\n\n if (Array.isArray(slot)) {\n return slot.map(slot => resolveGlslSlotToString(slot, v300)).join('')\n }\n\n switch (slot.type) {\n case 'glsl':\n return compile.toString(slot)\n case 'interleavedAttribute':\n return slot.layout.reduce(\n (a, v) =>\n v300 ? `${a}in ${v.kind} ${toID(v.key)};\\n` : `${a}attribute ${v.kind} ${toID(v.key)};\\n`,\n '',\n )\n case 'uniform':\n if ('size' in slot) {\n return `${slot.type} ${slot.kind} ${toID(slot.key)}[${slot.size}];`\n }\n return `${slot.type} ${slot.kind} ${toID(slot.key)};`\n }\n\n if (slot.type === 'attribute') {\n return `${v300 ? 'in' : slot.type} ${slot.kind} ${toID(slot.key)};`\n }\n\n throw new Error(`Unexpected slot: ${JSON.stringify(slot)}`)\n}\n\nconst QUAD_FLOAT_ARRAY = new Float32Array([-1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, 1])\n\ncompile.toQuad = function <TFragment extends GLSL, TSchema extends ViewSchemaPartial>(\n gl: GL,\n fragment: TFragment,\n options?: CompileOptions<TSchema>,\n) {\n const webgl2 = options?.webgl2 ?? fragment.template[0]?.startsWith('#version 300 es')\n\n const vertex = webgl2\n ? glsl`#version 300 es\nprecision mediump float;\n\n${attribute.vec2('a_quad')}\n\nout vec2 v_uv;\n\nvoid main() {\n v_uv = a_quad;\n gl_Position = vec4(v_uv, 0.0, 1.0);\n}`\n : glsl`precision mediump float;\n\n${attribute.vec2('a_quad')}\n\nvarying vec2 v_uv;\n\nvoid main() {\n v_uv = a_quad;\n gl_Position = vec4(v_uv, 0.0, 1.0);\n}`\n\n const result = compile(gl, vertex, fragment, options)\n\n // @ts-expect-error\n result.view.attributes.a_quad.set(QUAD_FLOAT_ARRAY)\n\n return result\n}\n"],"mappings":"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","names":["key","slot"]}
@@ -1,9 +1,6 @@
1
1
  type Prettify<T> = {
2
2
  [K in keyof T]: T[K];
3
3
  } & {};
4
- type RemoveSuffix<T, S extends string> = {
5
- [K in keyof T as K extends `${infer Prefix}${S}` ? Prefix : K]: T[K];
6
- };
7
4
  type DeepMerge<A extends readonly [...any]> = A extends [infer L, ...infer R] ? DeepMergeTwo<L, DeepMerge<R>> : unknown;
8
5
  type DeepMergeTwo<TBase, TOverride> = {
9
6
  [K in keyof TBase | keyof TOverride]: K extends keyof TOverride ? K extends keyof TBase ? ShallowMerge<[TBase[K], TOverride[K]]> : TOverride[K] : K extends keyof TBase ? TBase[K] : never;
@@ -24,7 +21,29 @@ type ShallowMergeTwo<L, R> = Id<Pick<L, Exclude<keyof L, keyof R>> & Pick<R, Exc
24
21
 
25
22
  /**********************************************************************************/
26
23
  /**********************************************************************************/
27
- type GL = WebGLRenderingContext | WebGL2RenderingContext;
24
+ type NumericKeys<T> = {
25
+ [K in keyof T]: T[K] extends number ? K : never;
26
+ }[keyof T];
27
+ /** What a WebGL1 context has and view.gl calls. */
28
+ type WebGL1Core = Pick<WebGLRenderingContext, 'activeTexture' | 'attachShader' | 'bindBuffer' | 'bindFramebuffer' | 'bindTexture' | 'bufferData' | 'bufferSubData' | 'checkFramebufferStatus' | 'compileShader' | 'createBuffer' | 'createFramebuffer' | 'createProgram' | 'createShader' | 'createTexture' | 'deleteBuffer' | 'deleteFramebuffer' | 'deleteProgram' | 'deleteShader' | 'deleteTexture' | 'disableVertexAttribArray' | 'enableVertexAttribArray' | 'framebufferTexture2D' | 'getAttribLocation' | 'getError' | 'getParameter' | 'getProgramInfoLog' | 'getProgramParameter' | 'getShaderInfoLog' | 'getShaderParameter' | 'getUniformLocation' | 'getVertexAttrib' | 'linkProgram' | 'shaderSource' | 'texImage2D' | 'texParameteri' | 'uniform1f' | 'uniform1fv' | 'uniform1i' | 'uniform1iv' | 'uniform2f' | 'uniform2fv' | 'uniform2i' | 'uniform2iv' | 'uniform3f' | 'uniform3fv' | 'uniform3i' | 'uniform3iv' | 'uniform4f' | 'uniform4fv' | 'uniform4i' | 'uniform4iv' | 'uniformMatrix2fv' | 'uniformMatrix3fv' | 'uniformMatrix4fv' | 'vertexAttrib1f' | 'vertexAttrib2f' | 'vertexAttrib3f' | 'vertexAttrib4f' | 'vertexAttribPointer'> & Readonly<Record<NumericKeys<WebGLRenderingContext>, number>>;
29
+ /** Instancing: a WebGL2 context's own, or a wrapper's, under the WebGL2 names. */
30
+ type Instancing = Pick<WebGL2RenderingContext, 'vertexAttribDivisor' | 'drawArraysInstanced' | 'drawElementsInstanced'> & {
31
+ readonly VERTEX_ATTRIB_ARRAY_DIVISOR: number;
32
+ };
33
+ /** Vertex arrays: a WebGL2 context's own, or a wrapper's, under the WebGL2 names. */
34
+ type VertexArrays = Pick<WebGL2RenderingContext, 'createVertexArray' | 'bindVertexArray' | 'deleteVertexArray'> & {
35
+ readonly VERTEX_ARRAY_BINDING: number;
36
+ };
37
+ /** WebGL2-only members view.gl reaches for where a schema asks for them. */
38
+ type WebGL2Only = Pick<WebGL2RenderingContext, 'vertexAttribIPointer' | 'uniform1ui' | 'uniform1uiv' | 'uniform2ui' | 'uniform2uiv' | 'uniform3ui' | 'uniform3uiv' | 'uniform4ui' | 'uniform4uiv' | 'uniformMatrix2x3fv' | 'uniformMatrix2x4fv' | 'uniformMatrix3x2fv' | 'uniformMatrix3x4fv' | 'uniformMatrix4x2fv' | 'uniformMatrix4x3fv' | 'RGBA32F' | 'RGB32F' | 'RGBA16F' | 'RGB16F' | 'HALF_FLOAT'>;
39
+ /**
40
+ * The context view.gl draws with: what a WebGL1 context has, plus each WebGL2
41
+ * feature as optional members under its WebGL2 names. A WebGL2 context is one
42
+ * as it is. A WebGL1 context with the extensions is one once a wrapper puts
43
+ * the extension's calls on it under the WebGL2 names; view.gl asks the context
44
+ * nothing and looks no extension up.
45
+ */
46
+ type GL = WebGL1Core & Partial<Instancing> & Partial<VertexArrays> & Partial<WebGL2Only>;
28
47
  type GLTarget = 'ARRAY_BUFFER' | 'ELEMENT_ARRAY_BUFFER';
29
48
  type GLUsage = 'STATIC_DRAW' | 'DYNAMIC_DRAW' | 'STREAM_DRAW';
30
49
  type GLTextureTarget = 'TEXTURE_2D' | 'TEXTURE_CUBE_MAP';
@@ -450,10 +469,9 @@ interface ViewOptions {
450
469
  *
451
470
  * The other two lifetimes have their own spellings, and neither is this one. A
452
471
  * buffer that outlives the view, or is shared between views, is a standalone
453
- * `bufferView` — `compile.toQuad`'s per-context quad, a buffer handed from one
454
- * program's view to another's. A buffer that belongs to somebody else is NAMED
455
- * by an attribute schema, and is borrowed: the view uses it and never deletes
456
- * it.
472
+ * `bufferView` — a buffer handed from one program's view to another's. A
473
+ * buffer that belongs to somebody else is NAMED by an attribute schema, and is
474
+ * borrowed: the view uses it and never deletes it.
457
475
  */
458
476
  interface ViewSchema {
459
477
  uniforms?: UniformSchema;
@@ -596,4 +614,4 @@ type CompileResult<TVertex extends GLSL, TFragment extends GLSL, TOverrideSchema
596
614
  dispose(): void;
597
615
  } : never;
598
616
 
599
- export type { TextureParameterName as $, AttributeSchema as A, BufferSchema as B, AttributeFormat as C, AttributeDefinition as D, AttributeMethods as E, FramebufferDefinition as F, GL as G, InterleavedAttributeLayout as H, InterleavedAttributeSchema as I, InterleavedAttributeDefinition as J, KIND_TO_UNIFORM_FN_NAME_MAP as K, BufferSource as L, ConstantAttributeMethods as M, ConstantSource as N, InterleavedAttributeMethods as O, BufferDefinition as P, BufferMethods as Q, RemoveSuffix as R, Source as S, TextureDefinition as T, UniformSchema as U, ViewOptions as V, Framebuffer as W, TextureTarget as X, TextureSchema as Y, TextureMethods as Z, TextureView as _, ViewSchema as a, TextureParameterValue as a0, TextureParameters as a1, InternalFormat as a2, Format as a3, PixelType as a4, TexImage2DOptions as a5, ViewSchemaPartial as a6, GLSLResult as a7, GLSLSlot as a8, GLSL as a9, UniformToken as aa, UniformTokenFn as ab, AttributeToken as ac, AttributeTokenFn as ad, InterleaveToken as ae, GLSLToSchema as af, GLSLToView as ag, GLSLSlotsToSchema as ah, FlattenSlots as ai, MergeGLSLSchema as aj, CompileResult as ak, Prettify as al, View as b, VertexArrayParticipant as c, VertexArrayMethods as d, UniformView as e, AttributeView as f, InterleavedAttributeView as g, BufferView as h, GLTarget as i, GLUsage as j, GLTextureTarget as k, GLTextureFormat as l, GLTextureType as m, GLTextureFilter as n, GLTextureWrap as o, KIND_TO_SIZE_MAP as p, UniformFnName as q, UniformKind as r, UniformKindMap as s, UniformDefinition as t, UniformElementMethods as u, UniformArrayMethods as v, UniformMethods as w, UniformValue as x, AttributeShortHand as y, AttributeKind as z };
617
+ export type { TextureView as $, AttributeSchema as A, BufferSchema as B, AttributeShortHand as C, AttributeKind as D, AttributeFormat as E, FramebufferDefinition as F, GL as G, AttributeDefinition as H, InterleavedAttributeSchema as I, AttributeMethods as J, KIND_TO_UNIFORM_FN_NAME_MAP as K, InterleavedAttributeLayout as L, InterleavedAttributeDefinition as M, BufferSource as N, ConstantAttributeMethods as O, ConstantSource as P, InterleavedAttributeMethods as Q, BufferDefinition as R, Source as S, TextureDefinition as T, UniformSchema as U, ViewOptions as V, BufferMethods as W, Framebuffer as X, TextureTarget as Y, TextureSchema as Z, TextureMethods as _, ViewSchema as a, TextureParameterName as a0, TextureParameterValue as a1, TextureParameters as a2, InternalFormat as a3, Format as a4, PixelType as a5, TexImage2DOptions as a6, ViewSchemaPartial as a7, GLSLResult as a8, GLSLSlot as a9, GLSL as aa, UniformToken as ab, UniformTokenFn as ac, AttributeToken as ad, AttributeTokenFn as ae, InterleaveToken as af, GLSLToSchema as ag, GLSLToView as ah, GLSLSlotsToSchema as ai, FlattenSlots as aj, MergeGLSLSchema as ak, CompileResult as al, Prettify as am, View as b, VertexArrayParticipant as c, VertexArrayMethods as d, UniformView as e, AttributeView as f, InterleavedAttributeView as g, BufferView as h, Instancing as i, VertexArrays as j, GLTarget as k, GLUsage as l, GLTextureTarget as m, GLTextureFormat as n, GLTextureType as o, GLTextureFilter as p, GLTextureWrap as q, KIND_TO_SIZE_MAP as r, UniformFnName as s, UniformKind as t, UniformKindMap as u, UniformDefinition as v, UniformElementMethods as w, UniformArrayMethods as x, UniformMethods as y, UniformValue as z };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bigmistqke/view.gl",
3
- "version": "0.8.0",
3
+ "version": "0.9.0",
4
4
  "description": "👀 Utilities for managing WebGL resources.",
5
5
  "exports": {
6
6
  ".": {
@@ -16,26 +16,11 @@
16
16
  "types": "./dist/gl.d.mts"
17
17
  }
18
18
  },
19
- "scripts": {
20
- "build": "tsup",
21
- "bump": "bumpp",
22
- "dev:build": "vite build dev",
23
- "dev:preview": "vite preview dev",
24
- "dev:watch": "concurrently \"pnpm dev:preview\" \"vite dev:build --watch\"",
25
- "dev": "vite serve dev",
26
- "format": "prettier -w \"src/**/*.{js,ts,json,css,tsx,jsx}\" \"dev/**/*.{js,ts,json,css,tsx,jsx}\"",
27
- "lint:types": "tsc --noEmit",
28
- "prepublishOnly": "pnpm test && pnpm build",
29
- "test:coverage": "vitest run --coverage",
30
- "test:ui": "vitest --ui",
31
- "test": "vitest --run"
32
- },
33
19
  "keywords": [
34
20
  "WebGL"
35
21
  ],
36
22
  "author": "",
37
23
  "license": "ISC",
38
- "packageManager": "pnpm@10.5.1",
39
24
  "devDependencies": {
40
25
  "@vitest/ui": "^3.2.4",
41
26
  "bumpp": "^10.4.0",
@@ -47,5 +32,18 @@
47
32
  "vite": "^7.0.2",
48
33
  "vite-tsconfig-paths": "^5.1.4",
49
34
  "vitest": "^3.2.4"
35
+ },
36
+ "scripts": {
37
+ "build": "tsup",
38
+ "bump": "bumpp",
39
+ "dev:build": "vite build dev",
40
+ "dev:preview": "vite preview dev",
41
+ "dev:watch": "concurrently \"pnpm dev:preview\" \"vite dev:build --watch\"",
42
+ "dev": "vite serve dev",
43
+ "format": "prettier -w \"src/**/*.{js,ts,json,css,tsx,jsx}\" \"dev/**/*.{js,ts,json,css,tsx,jsx}\"",
44
+ "lint:types": "tsc --noEmit",
45
+ "test:coverage": "vitest run --coverage",
46
+ "test:ui": "vitest --ui",
47
+ "test": "vitest --run"
50
48
  }
51
- }
49
+ }
package/src/errors.ts CHANGED
@@ -49,6 +49,15 @@ export class AttributeNotFoundError extends Error {
49
49
  }
50
50
  }
51
51
 
52
+ /** A divisor of 1 asked of a context without `vertexAttribDivisor`: the draw cannot instance. */
53
+ export class InstancingUnavailableError extends Error {
54
+ override name = 'InstancingUnavailableError'
55
+
56
+ constructor(readonly attribute: string) {
57
+ super(`Attribute '${attribute}' is instanced, but the context has no instancing`)
58
+ }
59
+ }
60
+
52
61
  /** Throws a {@link GLCreateError} for a `create*` that answered `null`. */
53
62
  export function created<T>(value: T | null, resource: GLResource): T {
54
63
  if (value === null) {
package/src/gl.ts CHANGED
@@ -1,14 +1,7 @@
1
- import { RemoveSuffix } from './type-utils'
2
1
  import type { GL, ViewOptions } from './types'
3
2
  import { created, ProgramLinkError, ShaderCompileError } from './errors'
4
3
  import { once } from './utils'
5
4
 
6
- function isWebGL2RenderingContext(
7
- gl: WebGLRenderingContext | WebGL2RenderingContext,
8
- ): gl is WebGL2RenderingContext {
9
- return typeof WebGL2RenderingContext !== 'undefined' && gl instanceof WebGL2RenderingContext
10
- }
11
-
12
5
  /**********************************************************************************/
13
6
  /* */
14
7
  /* Create Shader */
@@ -74,73 +67,3 @@ export function createProgram(
74
67
 
75
68
  return program
76
69
  }
77
-
78
- /**********************************************************************************/
79
- /* */
80
- /* Get Instanced Arrays */
81
- /* */
82
- /**********************************************************************************/
83
-
84
- const INSTANCED_ARRAYS_WRAPPER_MAP = new WeakMap<
85
- GL,
86
- RemoveSuffix<
87
- Pick<
88
- ANGLE_instanced_arrays,
89
- 'drawArraysInstancedANGLE' | 'drawElementsInstancedANGLE' | 'vertexAttribDivisorANGLE'
90
- >,
91
- 'ANGLE'
92
- > | null
93
- >()
94
- export function getInstancedArrays(gl: GL) {
95
- if (isWebGL2RenderingContext(gl)) return gl
96
-
97
- const cached = INSTANCED_ARRAYS_WRAPPER_MAP.get(gl)
98
- if (cached) return cached
99
-
100
- const ext = gl.getExtension('ANGLE_instanced_arrays')
101
- if (!ext) return undefined
102
-
103
- const wrapper = {
104
- drawArraysInstanced: ext.drawArraysInstancedANGLE.bind(ext),
105
- drawElementsInstanced: ext.drawElementsInstancedANGLE.bind(ext),
106
- vertexAttribDivisor: ext.vertexAttribDivisorANGLE.bind(ext),
107
- }
108
- INSTANCED_ARRAYS_WRAPPER_MAP.set(gl, wrapper)
109
-
110
- return wrapper
111
- }
112
-
113
- /**********************************************************************************/
114
- /* */
115
- /* Get Vertex Array Object */
116
- /* */
117
- /**********************************************************************************/
118
-
119
- const VERTEX_ARRAY_OBJECT_WRAPPER_MAP = new WeakMap<
120
- GL,
121
- RemoveSuffix<
122
- Pick<
123
- OES_vertex_array_object,
124
- 'bindVertexArrayOES' | 'createVertexArrayOES' | 'deleteVertexArrayOES'
125
- >,
126
- 'OES'
127
- > | null
128
- >()
129
- export function getVertexArrayObject(gl: GL) {
130
- if (isWebGL2RenderingContext(gl)) return gl
131
-
132
- const cached = VERTEX_ARRAY_OBJECT_WRAPPER_MAP.get(gl)
133
- if (cached) return cached
134
-
135
- const ext = gl.getExtension('OES_vertex_array_object')
136
- if (!ext) return null
137
-
138
- const wrapper = {
139
- bindVertexArray: ext.bindVertexArrayOES.bind(ext),
140
- createVertexArray: ext.createVertexArrayOES.bind(ext),
141
- deleteVertexArray: ext.deleteVertexArrayOES.bind(ext),
142
- }
143
- VERTEX_ARRAY_OBJECT_WRAPPER_MAP.set(gl, wrapper)
144
-
145
- return wrapper
146
- }
package/src/index.ts CHANGED
@@ -1,5 +1,4 @@
1
- import { AttributeNotFoundError, created } from './errors'
2
- import { getInstancedArrays, getVertexArrayObject } from './gl'
1
+ import { AttributeNotFoundError, InstancingUnavailableError, created } from './errors'
3
2
  import type {
4
3
  AttributeFormat,
5
4
  AttributeKind,
@@ -9,6 +8,7 @@ import type {
9
8
  BufferSchema,
10
9
  BufferView,
11
10
  GL,
11
+ Instancing,
12
12
  UniformView as InferUniformView,
13
13
  GLUsage,
14
14
  InterleavedAttributeLayout,
@@ -18,6 +18,7 @@ import type {
18
18
  UniformSchema,
19
19
  VertexArrayMethods,
20
20
  VertexArrayParticipant,
21
+ VertexArrays,
21
22
  View,
22
23
  ViewOptions,
23
24
  ViewSchema,
@@ -146,13 +147,32 @@ export function view<TSchema extends ViewSchema>(
146
147
  * outlive the call that did it.
147
148
  */
148
149
  function bindDefaultVertexArray(gl: GL): () => void {
149
- const feature = getVertexArrayObject(gl)
150
- if (!feature) {
150
+ if (!hasVertexArrays(gl)) {
151
151
  return () => {}
152
152
  }
153
153
  const previous = readVertexArrayBinding(gl)
154
- feature.bindVertexArray(null)
155
- return () => feature.bindVertexArray(previous)
154
+ gl.bindVertexArray(null)
155
+ return () => gl.bindVertexArray(previous)
156
+ }
157
+
158
+ /** Whether `gl` carries the vertex-array feature, every member of it. */
159
+ function hasVertexArrays(gl: GL): gl is GL & VertexArrays {
160
+ return (
161
+ typeof gl.createVertexArray === 'function' &&
162
+ typeof gl.bindVertexArray === 'function' &&
163
+ typeof gl.deleteVertexArray === 'function' &&
164
+ typeof gl.VERTEX_ARRAY_BINDING === 'number'
165
+ )
166
+ }
167
+
168
+ /** Whether `gl` carries the instancing feature, every member of it. */
169
+ function hasInstancing(gl: GL): gl is GL & Instancing {
170
+ return (
171
+ typeof gl.vertexAttribDivisor === 'function' &&
172
+ typeof gl.drawArraysInstanced === 'function' &&
173
+ typeof gl.drawElementsInstanced === 'function' &&
174
+ typeof gl.VERTEX_ATTRIB_ARRAY_DIVISOR === 'number'
175
+ )
156
176
  }
157
177
 
158
178
  /**
@@ -169,7 +189,7 @@ export function vaoView(
169
189
  participants: VertexArrayParticipant[],
170
190
  { signal }: ViewOptions = {},
171
191
  ): VertexArrayMethods {
172
- const feature = getVertexArrayObject(gl)
192
+ const feature = hasVertexArrays(gl) ? gl : null
173
193
 
174
194
  const methods: VertexArrayMethods = !feature
175
195
  ? {
@@ -183,7 +203,7 @@ export function vaoView(
183
203
  }
184
204
  : (() => {
185
205
  const vertexArray = created(feature.createVertexArray(), 'vertexArray')
186
- const previous = readVertexArrayBinding(gl)
206
+ const previous = readVertexArrayBinding(feature)
187
207
  feature.bindVertexArray(vertexArray)
188
208
  // Discarding the restorers is the point: the state lives in this array
189
209
  // now, and the array is thrown away whole.
@@ -194,7 +214,7 @@ export function vaoView(
194
214
 
195
215
  return {
196
216
  bind() {
197
- const previousVertexArray = readVertexArrayBinding(gl)
217
+ const previousVertexArray = readVertexArrayBinding(feature)
198
218
  feature.bindVertexArray(vertexArray)
199
219
  // Context state the array cannot hold — see applyToContext.
200
220
  participants.forEach(participant => participant.applyToContext?.())
@@ -341,11 +361,19 @@ function handleAttribute(
341
361
  glType: GLType,
342
362
  isIntegerKind: boolean,
343
363
  normalized: boolean,
344
- instanced?: boolean,
364
+ instanced: boolean | undefined,
365
+ name: string,
345
366
  ) {
346
367
  gl.enableVertexAttribArray(location)
347
368
  if (isIntegerKind) {
348
- ;(gl as WebGL2RenderingContext).vertexAttribIPointer(location, size, gl[glType], stride, offset)
369
+ assertedNotNullish(gl.vertexAttribIPointer, 'the context has no vertexAttribIPointer').call(
370
+ gl,
371
+ location,
372
+ size,
373
+ gl[glType],
374
+ stride,
375
+ offset,
376
+ )
349
377
  } else {
350
378
  gl.vertexAttribPointer(location, size, gl[glType], normalized, stride, offset)
351
379
  }
@@ -353,30 +381,27 @@ function handleAttribute(
353
381
  // Always stated, never inherited: the divisor belongs to the location rather
354
382
  // than to the program, so a location left at 1 by an earlier instanced
355
383
  // attribute would feed one value to every vertex here.
356
- // Get instanced-arrays-feature: extension if webgl, gl if webgl2
357
- const instancedArrays = getInstancedArrays(gl)
358
384
  if (instanced) {
359
- assertedNotNullish(instancedArrays).vertexAttribDivisor(location, 1)
360
- } else if (instancedArrays) {
361
- instancedArrays.vertexAttribDivisor(location, 0)
385
+ if (!hasInstancing(gl)) {
386
+ throw new InstancingUnavailableError(name)
387
+ }
388
+ gl.vertexAttribDivisor(location, 1)
389
+ } else if (hasInstancing(gl)) {
390
+ gl.vertexAttribDivisor(location, 0)
362
391
  }
363
392
  }
364
393
 
365
- // Same enum in webgl2 and in the webgl1 extensions, which expose no constants
366
- const VERTEX_ATTRIB_ARRAY_DIVISOR = 0x88fe
367
- const VERTEX_ARRAY_BINDING = 0x85b5
368
-
369
394
  // Anything but an array or null would make the restore throw a TypeError.
370
- function readVertexArrayBinding(gl: GL): WebGLVertexArrayObject | null {
371
- const bound: unknown = gl.getParameter(VERTEX_ARRAY_BINDING)
395
+ function readVertexArrayBinding(gl: GL & VertexArrays): WebGLVertexArrayObject | null {
396
+ const bound: unknown = gl.getParameter(gl.VERTEX_ARRAY_BINDING)
372
397
  return typeof bound === 'object' ? (bound as WebGLVertexArrayObject | null) : null
373
398
  }
374
399
 
375
400
  // Reads the divisor currently attached to a location, 0 where instancing is
376
401
  // unavailable and nothing can have set one
377
402
  function readDivisor(gl: GL, location: number): number {
378
- return getInstancedArrays(gl)
379
- ? (gl.getVertexAttrib(location, VERTEX_ATTRIB_ARRAY_DIVISOR) as number)
403
+ return hasInstancing(gl)
404
+ ? (gl.getVertexAttrib(location, gl.VERTEX_ATTRIB_ARRAY_DIVISOR) as number)
380
405
  : 0
381
406
  }
382
407
 
@@ -395,8 +420,7 @@ export function attributeView<T extends AttributeSchema>(
395
420
  const name = toID(key)
396
421
 
397
422
  // Whoever made the buffer deletes it. A schema can name a buffer that
398
- // belongs to another view — the quad `compile.toQuad` shares across every
399
- // program on a context, an attribute two programs step over — and
423
+ // belongs to another view — an attribute two programs step over — and
400
424
  // deleting one of those would take it out from under its owner.
401
425
  const buffer = providedBuffer ?? created(gl.createBuffer(), 'buffer')
402
426
 
@@ -417,9 +441,11 @@ export function attributeView<T extends AttributeSchema>(
417
441
  // it runs, so this binding is the setup for the pointer, not a lasting
418
442
  // part of the vertex array's state.
419
443
  gl.bindBuffer(gl.ARRAY_BUFFER, buffer)
420
- handleAttribute(gl, location, size, 0, 0, glType, isIntKind, normalized, instanced)
444
+ handleAttribute(gl, location, size, 0, 0, glType, isIntKind, normalized, instanced, name)
421
445
  return once(() => {
422
- getInstancedArrays(gl)?.vertexAttribDivisor(location, previousDivisor)
446
+ if (hasInstancing(gl)) {
447
+ gl.vertexAttribDivisor(location, previousDivisor)
448
+ }
423
449
  })
424
450
  }
425
451
 
@@ -581,6 +607,7 @@ export function interleavedAttributeView<T extends InterleavedAttributeSchema>(
581
607
  isIntKind,
582
608
  normalized,
583
609
  divisor === 1,
610
+ name,
584
611
  )
585
612
  })
586
613
 
@@ -603,10 +630,11 @@ export function interleavedAttributeView<T extends InterleavedAttributeSchema>(
603
630
  handle(divisor)
604
631
  }
605
632
  return once(() => {
606
- const instancedArrays = getInstancedArrays(gl)
607
- previousDivisors.forEach(([location, previous]) => {
608
- instancedArrays?.vertexAttribDivisor(location, previous)
609
- })
633
+ if (hasInstancing(gl)) {
634
+ previousDivisors.forEach(([location, previous]) => {
635
+ gl.vertexAttribDivisor(location, previous)
636
+ })
637
+ }
610
638
  })
611
639
  }
612
640
 
package/src/tag.ts CHANGED
@@ -18,7 +18,7 @@ import type {
18
18
  ViewSchema,
19
19
  ViewSchemaPartial,
20
20
  } from './types'
21
- import { createUpsertMap, once } from './utils'
21
+ import { once } from './utils'
22
22
 
23
23
  export function glsl<TSlot extends GLSLSlot, const TSlots extends TSlot[]>(
24
24
  template: TemplateStringsArray,
@@ -110,12 +110,7 @@ export function compile<
110
110
  TVertex extends GLSL,
111
111
  TFragment extends GLSL,
112
112
  TSchema extends ViewSchemaPartial,
113
- >(
114
- gl: WebGLRenderingContext,
115
- vertex: TVertex,
116
- fragment: TFragment,
117
- options?: CompileOptions<TSchema> | undefined,
118
- ) {
113
+ >(gl: GL, vertex: TVertex, fragment: TFragment, options?: CompileOptions<TSchema> | undefined) {
119
114
  const _vertex = resolveGLSLTag(vertex, options) as { template: string; schema: ViewSchema }
120
115
  const _fragment = resolveGLSLTag(fragment, options) as { template: string; schema: ViewSchema }
121
116
 
@@ -281,22 +276,19 @@ function resolveGlslSlotToString(slot: GLSLSlot, v300: boolean): string {
281
276
  }
282
277
 
283
278
  const QUAD_FLOAT_ARRAY = new Float32Array([-1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, 1])
284
- const QUAD_BUFFER_MAP = createUpsertMap(WeakMap<GL, WebGLBuffer>)
285
279
 
286
280
  compile.toQuad = function <TFragment extends GLSL, TSchema extends ViewSchemaPartial>(
287
281
  gl: GL,
288
282
  fragment: TFragment,
289
283
  options?: CompileOptions<TSchema>,
290
284
  ) {
291
- const buffer = QUAD_BUFFER_MAP.getOrInsert(gl, gl.createBuffer.bind(gl))
292
-
293
285
  const webgl2 = options?.webgl2 ?? fragment.template[0]?.startsWith('#version 300 es')
294
286
 
295
287
  const vertex = webgl2
296
288
  ? glsl`#version 300 es
297
289
  precision mediump float;
298
290
 
299
- ${attribute.vec2('a_quad', { buffer })}
291
+ ${attribute.vec2('a_quad')}
300
292
 
301
293
  out vec2 v_uv;
302
294
 
@@ -306,7 +298,7 @@ void main() {
306
298
  }`
307
299
  : glsl`precision mediump float;
308
300
 
309
- ${attribute.vec2('a_quad', { buffer })}
301
+ ${attribute.vec2('a_quad')}
310
302
 
311
303
  varying vec2 v_uv;
312
304