@bigmistqke/view.gl 0.7.1 → 0.8.1
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 +29 -2
- package/dist/{chunk-YQ6SJAWS.mjs → chunk-6UTWQ36A.mjs} +17 -13
- package/dist/chunk-6UTWQ36A.mjs.map +1 -0
- package/dist/{chunk-BZDGEIOT.mjs → chunk-BDNRC4WD.mjs} +59 -25
- package/dist/chunk-BDNRC4WD.mjs.map +1 -0
- package/dist/gl.d.mts +17 -3
- package/dist/gl.mjs +1 -1
- package/dist/index.d.mts +40 -3
- package/dist/index.mjs +13 -3
- package/dist/tag.d.mts +1 -1
- package/dist/tag.mjs +4 -7
- package/dist/tag.mjs.map +1 -1
- package/dist/{types-DZbMcty2.d.mts → types-Cy9u5YKs.d.mts} +4 -8
- package/package.json +15 -17
- package/src/errors.ts +58 -0
- package/src/gl.ts +15 -45
- package/src/index.ts +18 -13
- package/src/tag.ts +3 -6
- package/src/types.ts +3 -4
- package/src/utils.ts +3 -2
- package/test/errors.test.ts +189 -0
- package/test/index.test.ts +78 -0
- package/test/tag.test.ts +34 -10
- package/dist/chunk-BZDGEIOT.mjs.map +0 -1
- package/dist/chunk-YQ6SJAWS.mjs.map +0 -1
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":"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","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>(\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])\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;
|
|
@@ -450,10 +447,9 @@ interface ViewOptions {
|
|
|
450
447
|
*
|
|
451
448
|
* The other two lifetimes have their own spellings, and neither is this one. A
|
|
452
449
|
* buffer that outlives the view, or is shared between views, is a standalone
|
|
453
|
-
* `bufferView` —
|
|
454
|
-
*
|
|
455
|
-
*
|
|
456
|
-
* it.
|
|
450
|
+
* `bufferView` — a buffer handed from one program's view to another's. A
|
|
451
|
+
* buffer that belongs to somebody else is NAMED by an attribute schema, and is
|
|
452
|
+
* borrowed: the view uses it and never deletes it.
|
|
457
453
|
*/
|
|
458
454
|
interface ViewSchema {
|
|
459
455
|
uniforms?: UniformSchema;
|
|
@@ -596,4 +592,4 @@ type CompileResult<TVertex extends GLSL, TFragment extends GLSL, TOverrideSchema
|
|
|
596
592
|
dispose(): void;
|
|
597
593
|
} : never;
|
|
598
594
|
|
|
599
|
-
export type {
|
|
595
|
+
export type { TextureParameterValue 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, Framebuffer as R, Source as S, TextureDefinition as T, UniformSchema as U, ViewOptions as V, TextureTarget as W, TextureSchema as X, TextureMethods as Y, TextureView as Z, TextureParameterName as _, ViewSchema as a, TextureParameters as a0, InternalFormat as a1, Format as a2, PixelType as a3, TexImage2DOptions as a4, ViewSchemaPartial as a5, GLSLResult as a6, GLSLSlot as a7, GLSL as a8, UniformToken as a9, UniformTokenFn as aa, AttributeToken as ab, AttributeTokenFn as ac, InterleaveToken as ad, GLSLToSchema as ae, GLSLToView as af, GLSLSlotsToSchema as ag, FlattenSlots as ah, MergeGLSLSchema as ai, CompileResult as aj, Prettify as ak, 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 };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bigmistqke/view.gl",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.8.1",
|
|
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
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A shader that did not compile. `infoLog` is what the driver answered, `null`
|
|
3
|
+
* and `''` kept apart; `glError` is `getError()` read at the failure.
|
|
4
|
+
*/
|
|
5
|
+
export class ShaderCompileError extends Error {
|
|
6
|
+
override name = 'ShaderCompileError'
|
|
7
|
+
|
|
8
|
+
constructor(
|
|
9
|
+
readonly stage: 'vertex' | 'fragment',
|
|
10
|
+
readonly infoLog: string | null,
|
|
11
|
+
readonly glError: number,
|
|
12
|
+
) {
|
|
13
|
+
super(`Failed to compile ${stage} shader: ${infoLog}`)
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* A program that did not link. `infoLog` is what the driver answered, `null`
|
|
19
|
+
* and `''` kept apart; `glError` is `getError()` read at the failure.
|
|
20
|
+
*/
|
|
21
|
+
export class ProgramLinkError extends Error {
|
|
22
|
+
override name = 'ProgramLinkError'
|
|
23
|
+
|
|
24
|
+
constructor(
|
|
25
|
+
readonly infoLog: string | null,
|
|
26
|
+
readonly glError: number,
|
|
27
|
+
) {
|
|
28
|
+
super(`Failed to link program: ${infoLog}`)
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export type GLResource = 'shader' | 'program' | 'buffer' | 'vertexArray' | 'texture' | 'framebuffer'
|
|
33
|
+
|
|
34
|
+
/** A `create*` call that answered `null`. */
|
|
35
|
+
export class GLCreateError extends Error {
|
|
36
|
+
override name = 'GLCreateError'
|
|
37
|
+
|
|
38
|
+
constructor(readonly resource: GLResource) {
|
|
39
|
+
super(`Failed to create ${resource}`)
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/** A program with no active attribute of that name: `getAttribLocation` answered -1. */
|
|
44
|
+
export class AttributeNotFoundError extends Error {
|
|
45
|
+
override name = 'AttributeNotFoundError'
|
|
46
|
+
|
|
47
|
+
constructor(readonly attribute: string) {
|
|
48
|
+
super(`Attribute '${attribute}' not found`)
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/** Throws a {@link GLCreateError} for a `create*` that answered `null`. */
|
|
53
|
+
export function created<T>(value: T | null, resource: GLResource): T {
|
|
54
|
+
if (value === null) {
|
|
55
|
+
throw new GLCreateError(resource)
|
|
56
|
+
}
|
|
57
|
+
return value
|
|
58
|
+
}
|
package/src/gl.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { RemoveSuffix } from './type-utils'
|
|
2
1
|
import type { GL, ViewOptions } from './types'
|
|
2
|
+
import { created, ProgramLinkError, ShaderCompileError } from './errors'
|
|
3
3
|
import { once } from './utils'
|
|
4
4
|
|
|
5
5
|
function isWebGL2RenderingContext(
|
|
@@ -15,18 +15,19 @@ function isWebGL2RenderingContext(
|
|
|
15
15
|
/**********************************************************************************/
|
|
16
16
|
|
|
17
17
|
export function createShader(gl: GL, type: number, source: string): WebGLShader {
|
|
18
|
-
const shader = gl.createShader(type)
|
|
19
|
-
if (!shader) throw new Error('Failed to create shader')
|
|
18
|
+
const shader = created(gl.createShader(type), 'shader')
|
|
20
19
|
|
|
21
20
|
gl.shaderSource(shader, source)
|
|
22
21
|
gl.compileShader(shader)
|
|
23
22
|
|
|
24
23
|
if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
|
|
25
|
-
const
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
24
|
+
const error = new ShaderCompileError(
|
|
25
|
+
type === gl.VERTEX_SHADER ? 'vertex' : 'fragment',
|
|
26
|
+
gl.getShaderInfoLog(shader),
|
|
27
|
+
gl.getError(),
|
|
29
28
|
)
|
|
29
|
+
gl.deleteShader(shader)
|
|
30
|
+
throw error
|
|
30
31
|
}
|
|
31
32
|
|
|
32
33
|
return shader
|
|
@@ -44,8 +45,7 @@ export function createProgram(
|
|
|
44
45
|
fragmentSource: string,
|
|
45
46
|
{ signal }: ViewOptions = {},
|
|
46
47
|
): WebGLProgram {
|
|
47
|
-
const program = gl.createProgram()
|
|
48
|
-
if (!program) throw new Error('Failed to create WebGL program')
|
|
48
|
+
const program = created(gl.createProgram(), 'program')
|
|
49
49
|
|
|
50
50
|
const vertexShader = createShader(gl, gl.VERTEX_SHADER, vertexSource)
|
|
51
51
|
const fragmentShader = createShader(gl, gl.FRAGMENT_SHADER, fragmentSource)
|
|
@@ -55,11 +55,11 @@ export function createProgram(
|
|
|
55
55
|
gl.linkProgram(program)
|
|
56
56
|
|
|
57
57
|
if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
|
|
58
|
-
const
|
|
58
|
+
const error = new ProgramLinkError(gl.getProgramInfoLog(program), gl.getError())
|
|
59
59
|
gl.deleteProgram(program)
|
|
60
60
|
gl.deleteShader(vertexShader)
|
|
61
61
|
gl.deleteShader(fragmentShader)
|
|
62
|
-
throw
|
|
62
|
+
throw error
|
|
63
63
|
}
|
|
64
64
|
|
|
65
65
|
// Clean up shaders after linking
|
|
@@ -80,33 +80,18 @@ export function createProgram(
|
|
|
80
80
|
/* */
|
|
81
81
|
/**********************************************************************************/
|
|
82
82
|
|
|
83
|
-
|
|
84
|
-
GL,
|
|
85
|
-
RemoveSuffix<
|
|
86
|
-
Pick<
|
|
87
|
-
ANGLE_instanced_arrays,
|
|
88
|
-
'drawArraysInstancedANGLE' | 'drawElementsInstancedANGLE' | 'vertexAttribDivisorANGLE'
|
|
89
|
-
>,
|
|
90
|
-
'ANGLE'
|
|
91
|
-
> | null
|
|
92
|
-
>()
|
|
83
|
+
/** Instancing on `gl`: the context on WebGL2, `ANGLE_instanced_arrays` on WebGL1, else `undefined`. */
|
|
93
84
|
export function getInstancedArrays(gl: GL) {
|
|
94
85
|
if (isWebGL2RenderingContext(gl)) return gl
|
|
95
86
|
|
|
96
|
-
const cached = INSTANCED_ARRAYS_WRAPPER_MAP.get(gl)
|
|
97
|
-
if (cached) return cached
|
|
98
|
-
|
|
99
87
|
const ext = gl.getExtension('ANGLE_instanced_arrays')
|
|
100
88
|
if (!ext) return undefined
|
|
101
89
|
|
|
102
|
-
|
|
90
|
+
return {
|
|
103
91
|
drawArraysInstanced: ext.drawArraysInstancedANGLE.bind(ext),
|
|
104
92
|
drawElementsInstanced: ext.drawElementsInstancedANGLE.bind(ext),
|
|
105
93
|
vertexAttribDivisor: ext.vertexAttribDivisorANGLE.bind(ext),
|
|
106
94
|
}
|
|
107
|
-
INSTANCED_ARRAYS_WRAPPER_MAP.set(gl, wrapper)
|
|
108
|
-
|
|
109
|
-
return wrapper
|
|
110
95
|
}
|
|
111
96
|
|
|
112
97
|
/**********************************************************************************/
|
|
@@ -115,31 +100,16 @@ export function getInstancedArrays(gl: GL) {
|
|
|
115
100
|
/* */
|
|
116
101
|
/**********************************************************************************/
|
|
117
102
|
|
|
118
|
-
|
|
119
|
-
GL,
|
|
120
|
-
RemoveSuffix<
|
|
121
|
-
Pick<
|
|
122
|
-
OES_vertex_array_object,
|
|
123
|
-
'bindVertexArrayOES' | 'createVertexArrayOES' | 'deleteVertexArrayOES'
|
|
124
|
-
>,
|
|
125
|
-
'OES'
|
|
126
|
-
> | null
|
|
127
|
-
>()
|
|
103
|
+
/** Vertex arrays on `gl`: the context on WebGL2, `OES_vertex_array_object` on WebGL1, else `null`. */
|
|
128
104
|
export function getVertexArrayObject(gl: GL) {
|
|
129
105
|
if (isWebGL2RenderingContext(gl)) return gl
|
|
130
106
|
|
|
131
|
-
const cached = VERTEX_ARRAY_OBJECT_WRAPPER_MAP.get(gl)
|
|
132
|
-
if (cached) return cached
|
|
133
|
-
|
|
134
107
|
const ext = gl.getExtension('OES_vertex_array_object')
|
|
135
108
|
if (!ext) return null
|
|
136
109
|
|
|
137
|
-
|
|
110
|
+
return {
|
|
138
111
|
bindVertexArray: ext.bindVertexArrayOES.bind(ext),
|
|
139
112
|
createVertexArray: ext.createVertexArrayOES.bind(ext),
|
|
140
113
|
deleteVertexArray: ext.deleteVertexArrayOES.bind(ext),
|
|
141
114
|
}
|
|
142
|
-
VERTEX_ARRAY_OBJECT_WRAPPER_MAP.set(gl, wrapper)
|
|
143
|
-
|
|
144
|
-
return wrapper
|
|
145
115
|
}
|
package/src/index.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { AttributeNotFoundError, created } from './errors'
|
|
1
2
|
import { getInstancedArrays, getVertexArrayObject } from './gl'
|
|
2
3
|
import type {
|
|
3
4
|
AttributeFormat,
|
|
@@ -31,6 +32,7 @@ import {
|
|
|
31
32
|
once,
|
|
32
33
|
} from './utils'
|
|
33
34
|
import { createUpsertMap, mapObject } from './utils'
|
|
35
|
+
export * from './errors'
|
|
34
36
|
export * from './types'
|
|
35
37
|
// Documented under Utils but reachable from nowhere until now: utils is not a
|
|
36
38
|
// build entry, and named rather than star so the internal helpers stay internal.
|
|
@@ -148,7 +150,7 @@ function bindDefaultVertexArray(gl: GL): () => void {
|
|
|
148
150
|
if (!feature) {
|
|
149
151
|
return () => {}
|
|
150
152
|
}
|
|
151
|
-
const previous = gl
|
|
153
|
+
const previous = readVertexArrayBinding(gl)
|
|
152
154
|
feature.bindVertexArray(null)
|
|
153
155
|
return () => feature.bindVertexArray(previous)
|
|
154
156
|
}
|
|
@@ -180,8 +182,8 @@ export function vaoView(
|
|
|
180
182
|
dispose() {},
|
|
181
183
|
}
|
|
182
184
|
: (() => {
|
|
183
|
-
const vertexArray =
|
|
184
|
-
const previous = gl
|
|
185
|
+
const vertexArray = created(feature.createVertexArray(), 'vertexArray')
|
|
186
|
+
const previous = readVertexArrayBinding(gl)
|
|
185
187
|
feature.bindVertexArray(vertexArray)
|
|
186
188
|
// Discarding the restorers is the point: the state lives in this array
|
|
187
189
|
// now, and the array is thrown away whole.
|
|
@@ -192,9 +194,7 @@ export function vaoView(
|
|
|
192
194
|
|
|
193
195
|
return {
|
|
194
196
|
bind() {
|
|
195
|
-
const previousVertexArray = gl
|
|
196
|
-
VERTEX_ARRAY_BINDING,
|
|
197
|
-
) as WebGLVertexArrayObject | null
|
|
197
|
+
const previousVertexArray = readVertexArrayBinding(gl)
|
|
198
198
|
feature.bindVertexArray(vertexArray)
|
|
199
199
|
// Context state the array cannot hold — see applyToContext.
|
|
200
200
|
participants.forEach(participant => participant.applyToContext?.())
|
|
@@ -366,6 +366,12 @@ function handleAttribute(
|
|
|
366
366
|
const VERTEX_ATTRIB_ARRAY_DIVISOR = 0x88fe
|
|
367
367
|
const VERTEX_ARRAY_BINDING = 0x85b5
|
|
368
368
|
|
|
369
|
+
// 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)
|
|
372
|
+
return typeof bound === 'object' ? (bound as WebGLVertexArrayObject | null) : null
|
|
373
|
+
}
|
|
374
|
+
|
|
369
375
|
// Reads the divisor currently attached to a location, 0 where instancing is
|
|
370
376
|
// unavailable and nothing can have set one
|
|
371
377
|
function readDivisor(gl: GL, location: number): number {
|
|
@@ -389,14 +395,13 @@ export function attributeView<T extends AttributeSchema>(
|
|
|
389
395
|
const name = toID(key)
|
|
390
396
|
|
|
391
397
|
// Whoever made the buffer deletes it. A schema can name a buffer that
|
|
392
|
-
// belongs to another view —
|
|
393
|
-
// program on a context, an attribute two programs step over — and
|
|
398
|
+
// belongs to another view — an attribute two programs step over — and
|
|
394
399
|
// deleting one of those would take it out from under its owner.
|
|
395
|
-
const buffer = providedBuffer ??
|
|
400
|
+
const buffer = providedBuffer ?? created(gl.createBuffer(), 'buffer')
|
|
396
401
|
|
|
397
402
|
const location = gl.getAttribLocation(program, name)
|
|
398
403
|
if (location < 0) {
|
|
399
|
-
throw new
|
|
404
|
+
throw new AttributeNotFoundError(name)
|
|
400
405
|
}
|
|
401
406
|
|
|
402
407
|
const size = kindToSize(kind)
|
|
@@ -551,7 +556,7 @@ export function interleavedAttributeView<T extends InterleavedAttributeSchema>(
|
|
|
551
556
|
const location = gl.getAttribLocation(program, name)
|
|
552
557
|
|
|
553
558
|
if (location < 0) {
|
|
554
|
-
throw new
|
|
559
|
+
throw new AttributeNotFoundError(name)
|
|
555
560
|
}
|
|
556
561
|
|
|
557
562
|
locations.push(location)
|
|
@@ -582,7 +587,7 @@ export function interleavedAttributeView<T extends InterleavedAttributeSchema>(
|
|
|
582
587
|
const stride = index
|
|
583
588
|
|
|
584
589
|
// Create a buffer
|
|
585
|
-
const buffer =
|
|
590
|
+
const buffer = created(gl.createBuffer(), 'buffer')
|
|
586
591
|
|
|
587
592
|
// This layout used to create a vertex array of its own, here, which is why
|
|
588
593
|
// the other participants in a draw had to be bound after it to be included
|
|
@@ -670,7 +675,7 @@ export function bufferView<T extends BufferSchema>(
|
|
|
670
675
|
): BufferView<T> {
|
|
671
676
|
// Initialize buffers
|
|
672
677
|
const buffers = mapObject(schema, ({ target = 'ARRAY_BUFFER', usage = 'STATIC_DRAW' }) => {
|
|
673
|
-
const buffer =
|
|
678
|
+
const buffer = created(gl.createBuffer(), 'buffer')
|
|
674
679
|
|
|
675
680
|
function applyToVertexArray() {
|
|
676
681
|
// Snapshot before the change, restore in the disposer. Restoring the
|
package/src/tag.ts
CHANGED
|
@@ -18,7 +18,7 @@ import type {
|
|
|
18
18
|
ViewSchema,
|
|
19
19
|
ViewSchemaPartial,
|
|
20
20
|
} from './types'
|
|
21
|
-
import {
|
|
21
|
+
import { once } from './utils'
|
|
22
22
|
|
|
23
23
|
export function glsl<TSlot extends GLSLSlot, const TSlots extends TSlot[]>(
|
|
24
24
|
template: TemplateStringsArray,
|
|
@@ -281,22 +281,19 @@ function resolveGlslSlotToString(slot: GLSLSlot, v300: boolean): string {
|
|
|
281
281
|
}
|
|
282
282
|
|
|
283
283
|
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
284
|
|
|
286
285
|
compile.toQuad = function <TFragment extends GLSL, TSchema extends ViewSchemaPartial>(
|
|
287
286
|
gl: GL,
|
|
288
287
|
fragment: TFragment,
|
|
289
288
|
options?: CompileOptions<TSchema>,
|
|
290
289
|
) {
|
|
291
|
-
const buffer = QUAD_BUFFER_MAP.getOrInsert(gl, gl.createBuffer.bind(gl))
|
|
292
|
-
|
|
293
290
|
const webgl2 = options?.webgl2 ?? fragment.template[0]?.startsWith('#version 300 es')
|
|
294
291
|
|
|
295
292
|
const vertex = webgl2
|
|
296
293
|
? glsl`#version 300 es
|
|
297
294
|
precision mediump float;
|
|
298
295
|
|
|
299
|
-
${attribute.vec2('a_quad'
|
|
296
|
+
${attribute.vec2('a_quad')}
|
|
300
297
|
|
|
301
298
|
out vec2 v_uv;
|
|
302
299
|
|
|
@@ -306,7 +303,7 @@ void main() {
|
|
|
306
303
|
}`
|
|
307
304
|
: glsl`precision mediump float;
|
|
308
305
|
|
|
309
|
-
${attribute.vec2('a_quad'
|
|
306
|
+
${attribute.vec2('a_quad')}
|
|
310
307
|
|
|
311
308
|
varying vec2 v_uv;
|
|
312
309
|
|
package/src/types.ts
CHANGED
|
@@ -670,10 +670,9 @@ export interface ViewOptions {
|
|
|
670
670
|
*
|
|
671
671
|
* The other two lifetimes have their own spellings, and neither is this one. A
|
|
672
672
|
* buffer that outlives the view, or is shared between views, is a standalone
|
|
673
|
-
* `bufferView` —
|
|
674
|
-
*
|
|
675
|
-
*
|
|
676
|
-
* it.
|
|
673
|
+
* `bufferView` — a buffer handed from one program's view to another's. A
|
|
674
|
+
* buffer that belongs to somebody else is NAMED by an attribute schema, and is
|
|
675
|
+
* borrowed: the view uses it and never deletes it.
|
|
677
676
|
*/
|
|
678
677
|
export interface ViewSchema {
|
|
679
678
|
uniforms?: UniformSchema
|
package/src/utils.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { created } from './errors'
|
|
1
2
|
import type {
|
|
2
3
|
AttributeKind,
|
|
3
4
|
FramebufferDefinition,
|
|
@@ -135,7 +136,7 @@ export function createTexture(
|
|
|
135
136
|
data?: ArrayBufferView | null,
|
|
136
137
|
{ signal }: ViewOptions = {},
|
|
137
138
|
): WebGLTexture {
|
|
138
|
-
const texture =
|
|
139
|
+
const texture = created(gl.createTexture(), 'texture')
|
|
139
140
|
|
|
140
141
|
function getTextureConstant(name: string) {
|
|
141
142
|
if (!(name in gl)) {
|
|
@@ -217,7 +218,7 @@ export function createFramebuffer(
|
|
|
217
218
|
const texture = providedTexture ?? createTexture(gl, definition)
|
|
218
219
|
|
|
219
220
|
// Create framebuffer
|
|
220
|
-
const framebuffer =
|
|
221
|
+
const framebuffer = created(gl.createFramebuffer(), 'framebuffer')
|
|
221
222
|
|
|
222
223
|
gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffer)
|
|
223
224
|
|