@aethervtt/shard-gpu-webgl2 0.0.0-stage → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +2 -2
  3. package/dist/binding.d.ts +32 -0
  4. package/dist/binding.js +81 -0
  5. package/dist/cache.d.ts +73 -0
  6. package/dist/cache.js +184 -0
  7. package/dist/commands.d.ts +101 -0
  8. package/dist/commands.js +391 -0
  9. package/dist/constants.d.ts +41 -0
  10. package/dist/constants.js +38 -0
  11. package/dist/copies.d.ts +61 -0
  12. package/dist/copies.js +629 -0
  13. package/dist/device.d.ts +168 -0
  14. package/dist/device.js +401 -0
  15. package/dist/errors.d.ts +8 -0
  16. package/dist/errors.js +18 -0
  17. package/dist/fake-gl.d.ts +338 -0
  18. package/dist/fake-gl.js +980 -0
  19. package/dist/fbo.d.ts +30 -0
  20. package/dist/fbo.js +142 -0
  21. package/dist/formats.d.ts +25 -0
  22. package/dist/formats.js +106 -0
  23. package/dist/gl.d.ts +216 -0
  24. package/dist/gl.js +232 -0
  25. package/dist/gpu.d.ts +58 -0
  26. package/dist/gpu.js +215 -0
  27. package/dist/index.d.ts +8 -0
  28. package/dist/index.js +42 -0
  29. package/dist/naga.d.ts +37 -0
  30. package/dist/naga.js +99 -0
  31. package/dist/pipeline.d.ts +113 -0
  32. package/dist/pipeline.js +294 -0
  33. package/dist/present.d.ts +18 -0
  34. package/dist/present.js +108 -0
  35. package/dist/queries.d.ts +41 -0
  36. package/dist/queries.js +110 -0
  37. package/dist/queue.d.ts +13 -0
  38. package/dist/queue.js +50 -0
  39. package/dist/readback.d.ts +17 -0
  40. package/dist/readback.js +104 -0
  41. package/dist/replay.d.ts +78 -0
  42. package/dist/replay.js +678 -0
  43. package/dist/resources.d.ts +96 -0
  44. package/dist/resources.js +355 -0
  45. package/dist/state.d.ts +43 -0
  46. package/dist/state.js +169 -0
  47. package/package.json +36 -4
  48. package/wasm/shard_naga.wasm +0 -0
@@ -0,0 +1,96 @@
1
+ import type { ReadJob } from './copies.js';
2
+ import type { Webgl2Device } from './device.js';
3
+ import { type GlFormat } from './formats.js';
4
+ import type { QueryRead } from './queries.js';
5
+ export declare class Webgl2Buffer {
6
+ readonly id: number;
7
+ readonly size: number;
8
+ /**
9
+ * What GL holds: uniform buffers round up to 16 bytes, so a std140 block (rounded up to 16) can
10
+ * always be bound over a WGSL struct (which isn't).
11
+ */
12
+ readonly allocated: number;
13
+ readonly usage: number;
14
+ readonly label: string;
15
+ readonly gl: WebGLBuffer | null;
16
+ /** INDEX usage: the GL element buffer, and its bytes on the CPU (the restart check). */
17
+ readonly index: boolean;
18
+ shadow: Uint8Array | undefined;
19
+ /** A uint32 copy of a uint16 index buffer that holds 65535, made when first drawn from. */
20
+ promoted: WebGLBuffer | null | undefined;
21
+ /** Whether the CPU copy holds 65535, as of `restartVersion`. */
22
+ restart: boolean;
23
+ restartVersion: number;
24
+ /** Bumped by every write, so derived copies know they're stale. */
25
+ version: number;
26
+ mapState: GPUBufferMapState;
27
+ /** MAP_READ: the bytes a finished map hands out, from `mappedOffset`. */
28
+ mapped: ArrayBuffer | undefined;
29
+ mappedOffset: number;
30
+ /** Texture reads into this buffer, repacked when it's mapped. */
31
+ readonly reads: ReadJob[];
32
+ /** Timer queries resolved into this buffer, written when it's mapped (0074). */
33
+ readonly queryReads: QueryRead[];
34
+ destroyed: boolean;
35
+ private readonly device;
36
+ constructor(device: Webgl2Device, descriptor: GPUBufferDescriptor);
37
+ /** Where copies read and write this buffer: its own target for index buffers. */
38
+ get target(): number;
39
+ /** Writes `data` at `offset`: `dataOffset` and `size` count elements of a typed array (writeBuffer's rule). */
40
+ write(offset: number, data: BufferSource, dataOffset?: number, size?: number): void;
41
+ mapAsync(mode: number, offset?: number, size?: number): Promise<undefined>;
42
+ getMappedRange(offset?: number, size?: number): ArrayBuffer;
43
+ unmap(): void;
44
+ destroy(): void;
45
+ }
46
+ export declare class Webgl2Texture {
47
+ readonly id: number;
48
+ readonly label: string;
49
+ readonly width: number;
50
+ readonly height: number;
51
+ readonly depthOrArrayLayers: number;
52
+ readonly mipLevelCount: number;
53
+ readonly sampleCount: number;
54
+ readonly dimension: GPUTextureDimension;
55
+ readonly format: GPUTextureFormat;
56
+ readonly usage: number;
57
+ readonly info: GlFormat;
58
+ /** Its one binding dimension (compatibility mode's `textureBindingViewDimension`). */
59
+ readonly bindingDimension: GPUTextureViewDimension;
60
+ readonly target: number;
61
+ readonly gl: WebGLTexture | null;
62
+ readonly renderbuffer: WebGLRenderbuffer | null;
63
+ /** The mip range a view last bound (texture parameters, set when it changes). */
64
+ baseLevel: number;
65
+ maxLevel: number;
66
+ destroyed: boolean;
67
+ /** A canvas surface's current texture: presented after the submit that draws it. */
68
+ surface: unknown;
69
+ private readonly device;
70
+ constructor(device: Webgl2Device, descriptor: GPUTextureDescriptor);
71
+ /** The compatibility-mode attribute. */
72
+ get textureBindingViewDimension(): GPUTextureViewDimension;
73
+ createView(descriptor?: GPUTextureViewDescriptor): Webgl2TextureView;
74
+ destroy(): void;
75
+ }
76
+ export declare class Webgl2TextureView {
77
+ readonly id: number;
78
+ readonly texture: Webgl2Texture;
79
+ readonly dimension: GPUTextureViewDimension;
80
+ readonly baseMipLevel: number;
81
+ readonly mipLevelCount: number;
82
+ readonly baseArrayLayer: number;
83
+ readonly arrayLayerCount: number;
84
+ readonly label: string;
85
+ constructor(texture: Webgl2Texture, d: GPUTextureViewDescriptor);
86
+ /** Whether binding this view reads the texture as its one binding dimension (baseline's rule). */
87
+ bindable(): boolean;
88
+ }
89
+ export declare const COMPARE: Record<GPUCompareFunction, number>;
90
+ export declare class Webgl2Sampler {
91
+ readonly id: number;
92
+ readonly gl: WebGLSampler | null;
93
+ readonly label: string;
94
+ readonly compare: boolean;
95
+ constructor(device: Webgl2Device, d?: GPUSamplerDescriptor);
96
+ }
@@ -0,0 +1,355 @@
1
+ import { BufferUsage, TextureUsage } from "./constants.js";
2
+ import { unsupported } from "./errors.js";
3
+ import { formatOf } from "./formats.js";
4
+ import { GL } from "./gl.js";
5
+ let nextId = 1;
6
+ class Webgl2Buffer {
7
+ id = nextId++;
8
+ size;
9
+ /**
10
+ * What GL holds: uniform buffers round up to 16 bytes, so a std140 block (rounded up to 16) can
11
+ * always be bound over a WGSL struct (which isn't).
12
+ */
13
+ allocated;
14
+ usage;
15
+ label;
16
+ gl;
17
+ /** INDEX usage: the GL element buffer, and its bytes on the CPU (the restart check). */
18
+ index;
19
+ shadow;
20
+ /** A uint32 copy of a uint16 index buffer that holds 65535, made when first drawn from. */
21
+ promoted;
22
+ /** Whether the CPU copy holds 65535, as of `restartVersion`. */
23
+ restart = false;
24
+ restartVersion = -1;
25
+ /** Bumped by every write, so derived copies know they're stale. */
26
+ version = 0;
27
+ mapState = "unmapped";
28
+ /** MAP_READ: the bytes a finished map hands out, from `mappedOffset`. */
29
+ mapped;
30
+ mappedOffset = 0;
31
+ /** Texture reads into this buffer, repacked when it's mapped. */
32
+ reads = [];
33
+ /** Timer queries resolved into this buffer, written when it's mapped (0074). */
34
+ queryReads = [];
35
+ destroyed = false;
36
+ device;
37
+ constructor(device, descriptor) {
38
+ this.device = device;
39
+ this.size = descriptor.size;
40
+ this.allocated = descriptor.usage & BufferUsage.UNIFORM ? Math.ceil(descriptor.size / 16) * 16 : descriptor.size;
41
+ this.usage = descriptor.usage;
42
+ this.label = descriptor.label ?? "";
43
+ this.index = (descriptor.usage & BufferUsage.INDEX) !== 0;
44
+ const gl = device.gl;
45
+ this.gl = gl.createBuffer();
46
+ const target = this.index ? GL.ELEMENT_ARRAY_BUFFER : GL.COPY_WRITE_BUFFER;
47
+ if (this.index) {
48
+ this.shadow = new Uint8Array(this.size);
49
+ device.state.bindVertexArray(null);
50
+ }
51
+ gl.bindBuffer(target, this.gl);
52
+ const hint = descriptor.usage & BufferUsage.MAP_READ ? GL.STREAM_READ : GL.DYNAMIC_DRAW;
53
+ gl.bufferData(target, this.allocated, hint);
54
+ gl.bindBuffer(target, null);
55
+ if (descriptor.mappedAtCreation) {
56
+ throw unsupported("create a buffer mapped at creation", "Write it with queue.writeBuffer.");
57
+ }
58
+ }
59
+ /** Where copies read and write this buffer: its own target for index buffers. */
60
+ get target() {
61
+ return this.index ? GL.ELEMENT_ARRAY_BUFFER : GL.COPY_WRITE_BUFFER;
62
+ }
63
+ /** Writes `data` at `offset`: `dataOffset` and `size` count elements of a typed array (writeBuffer's rule). */
64
+ write(offset, data, dataOffset = 0, size) {
65
+ const gl = this.device.gl;
66
+ const target = this.target;
67
+ const view = ArrayBuffer.isView(data) && !(data instanceof DataView);
68
+ const unit = view ? data.BYTES_PER_ELEMENT : 1;
69
+ const total = ArrayBuffer.isView(data) ? data.byteLength / unit : data.byteLength;
70
+ const count = size ?? total - dataOffset;
71
+ if (count <= 0) return;
72
+ if (this.index) this.device.state.bindVertexArray(null);
73
+ gl.bindBuffer(target, this.gl);
74
+ if (view) {
75
+ gl.bufferSubData(target, offset, data, dataOffset, count);
76
+ } else {
77
+ gl.bufferSubData(target, offset, bytesOf(data, dataOffset, count));
78
+ }
79
+ gl.bindBuffer(target, null);
80
+ if (this.shadow) {
81
+ const byteOffset = (ArrayBuffer.isView(data) ? data.byteOffset : 0) + dataOffset * unit;
82
+ const buffer = ArrayBuffer.isView(data) ? data.buffer : data;
83
+ this.shadow.set(new Uint8Array(buffer, byteOffset, count * unit), offset);
84
+ }
85
+ this.version++;
86
+ }
87
+ mapAsync(mode, offset = 0, size) {
88
+ return this.device.mapBuffer(this, mode, offset, size ?? this.size - offset);
89
+ }
90
+ getMappedRange(offset = this.mappedOffset, size) {
91
+ const mapped = this.mapped;
92
+ if (!mapped) throw new Error(`Buffer "${this.label}" isn't mapped`);
93
+ const start = offset - this.mappedOffset;
94
+ const length = size ?? mapped.byteLength - start;
95
+ return start === 0 && length === mapped.byteLength ? mapped : mapped.slice(start, start + length);
96
+ }
97
+ unmap() {
98
+ this.mapped = void 0;
99
+ this.mapState = "unmapped";
100
+ }
101
+ destroy() {
102
+ if (this.destroyed) return;
103
+ this.destroyed = true;
104
+ this.device.forgetBuffer(this);
105
+ this.device.gl.deleteBuffer(this.gl);
106
+ if (this.promoted) this.device.gl.deleteBuffer(this.promoted);
107
+ this.shadow = void 0;
108
+ this.mapped = void 0;
109
+ this.mapState = "unmapped";
110
+ }
111
+ }
112
+ function bytesOf(data, byteOffset, length) {
113
+ if (ArrayBuffer.isView(data))
114
+ return new Uint8Array(data.buffer, data.byteOffset + byteOffset, length);
115
+ return new Uint8Array(data, byteOffset, length);
116
+ }
117
+ function targetOf(dimension, layers, binding) {
118
+ if (dimension === "3d") return { target: GL.TEXTURE_3D, view: "3d" };
119
+ if (dimension === "1d") throw unsupported("make a 1D texture");
120
+ const view = binding ?? (layers === 1 ? "2d" : layers === 6 ? "2d-array" : "2d-array");
121
+ if (view === "cube") return { target: GL.TEXTURE_CUBE_MAP, view };
122
+ if (view === "cube-array") throw unsupported("bind a cube array");
123
+ if (view === "2d-array") return { target: GL.TEXTURE_2D_ARRAY, view };
124
+ if (layers > 1) {
125
+ throw unsupported(
126
+ `bind a ${layers}-layer texture as '2d'`,
127
+ "Create it with textureBindingViewDimension '2d-array' (or 'cube')."
128
+ );
129
+ }
130
+ return { target: GL.TEXTURE_2D, view: "2d" };
131
+ }
132
+ class Webgl2Texture {
133
+ id = nextId++;
134
+ label;
135
+ width;
136
+ height;
137
+ depthOrArrayLayers;
138
+ mipLevelCount;
139
+ sampleCount;
140
+ dimension;
141
+ format;
142
+ usage;
143
+ info;
144
+ /** Its one binding dimension (compatibility mode's `textureBindingViewDimension`). */
145
+ bindingDimension;
146
+ target;
147
+ gl;
148
+ renderbuffer;
149
+ /** The mip range a view last bound (texture parameters, set when it changes). */
150
+ baseLevel = -1;
151
+ maxLevel = -1;
152
+ destroyed = false;
153
+ /** A canvas surface's current texture: presented after the submit that draws it. */
154
+ surface;
155
+ device;
156
+ constructor(device, descriptor) {
157
+ this.device = device;
158
+ const size = descriptor.size;
159
+ this.width = Array.isArray(size) ? size[0] : size.width;
160
+ this.height = (Array.isArray(size) ? size[1] : size.height) ?? 1;
161
+ this.depthOrArrayLayers = (Array.isArray(size) ? size[2] : size.depthOrArrayLayers) ?? 1;
162
+ this.mipLevelCount = descriptor.mipLevelCount ?? 1;
163
+ this.sampleCount = descriptor.sampleCount ?? 1;
164
+ this.dimension = descriptor.dimension ?? "2d";
165
+ this.format = descriptor.format;
166
+ this.usage = descriptor.usage;
167
+ this.label = descriptor.label ?? "";
168
+ this.info = formatOf(descriptor.format);
169
+ if (descriptor.viewFormats?.some((f) => f !== descriptor.format)) {
170
+ throw unsupported(
171
+ `view "${this.label}" in another format (${descriptor.viewFormats.join(", ")})`,
172
+ "Baseline textures have one format: the asset layer makes a twin for the other."
173
+ );
174
+ }
175
+ if (this.usage & TextureUsage.STORAGE_BINDING) {
176
+ throw unsupported(
177
+ `make "${this.label}" a storage texture`,
178
+ "Storage textures are compute: the full tier only."
179
+ );
180
+ }
181
+ const gl = device.gl;
182
+ const onlyAttachment = (this.usage & ~(TextureUsage.RENDER_ATTACHMENT | TextureUsage.COPY_SRC)) === 0;
183
+ if (this.sampleCount > 1 || this.info.kind === "stencil" && onlyAttachment) {
184
+ if (this.usage & TextureUsage.TEXTURE_BINDING) {
185
+ throw unsupported(
186
+ `sample the multisampled texture "${this.label}"`,
187
+ "Resolve it, then sample the resolve target."
188
+ );
189
+ }
190
+ this.renderbuffer = gl.createRenderbuffer();
191
+ this.gl = null;
192
+ this.target = GL.RENDERBUFFER;
193
+ this.bindingDimension = "2d";
194
+ gl.bindRenderbuffer(GL.RENDERBUFFER, this.renderbuffer);
195
+ if (this.sampleCount > 1) {
196
+ const most = device.samplesFor(this.info.internal);
197
+ if (this.sampleCount > most) {
198
+ device.raise(
199
+ "validation",
200
+ `"${this.label}" asks for ${this.sampleCount} samples of ${this.format}; this device renders at most ${most}`
201
+ );
202
+ }
203
+ gl.renderbufferStorageMultisample(
204
+ GL.RENDERBUFFER,
205
+ Math.min(this.sampleCount, most),
206
+ this.info.internal,
207
+ this.width,
208
+ this.height
209
+ );
210
+ } else {
211
+ gl.renderbufferStorage(GL.RENDERBUFFER, this.info.internal, this.width, this.height);
212
+ }
213
+ gl.bindRenderbuffer(GL.RENDERBUFFER, null);
214
+ return;
215
+ }
216
+ if (this.info.kind === "stencil")
217
+ throw unsupported(`sample the stencil texture "${this.label}"`);
218
+ const t = targetOf(
219
+ this.dimension,
220
+ this.depthOrArrayLayers,
221
+ descriptor.textureBindingViewDimension
222
+ );
223
+ this.target = t.target;
224
+ this.bindingDimension = t.view;
225
+ this.renderbuffer = null;
226
+ this.gl = gl.createTexture();
227
+ device.state.bindTexture(this.target, this.gl);
228
+ if (this.target === GL.TEXTURE_2D || this.target === GL.TEXTURE_CUBE_MAP) {
229
+ gl.texStorage2D(this.target, this.mipLevelCount, this.info.internal, this.width, this.height);
230
+ } else {
231
+ gl.texStorage3D(
232
+ this.target,
233
+ this.mipLevelCount,
234
+ this.info.internal,
235
+ this.width,
236
+ this.height,
237
+ this.depthOrArrayLayers
238
+ );
239
+ }
240
+ gl.texParameteri(this.target, GL.TEXTURE_MIN_FILTER, GL.NEAREST);
241
+ gl.texParameteri(this.target, GL.TEXTURE_MAG_FILTER, GL.NEAREST);
242
+ this.baseLevel = 0;
243
+ this.maxLevel = this.mipLevelCount - 1;
244
+ gl.texParameteri(this.target, GL.TEXTURE_BASE_LEVEL, 0);
245
+ gl.texParameteri(this.target, GL.TEXTURE_MAX_LEVEL, this.maxLevel);
246
+ }
247
+ /** The compatibility-mode attribute. */
248
+ get textureBindingViewDimension() {
249
+ return this.bindingDimension;
250
+ }
251
+ createView(descriptor = {}) {
252
+ return new Webgl2TextureView(this, descriptor);
253
+ }
254
+ destroy() {
255
+ if (this.destroyed) return;
256
+ this.destroyed = true;
257
+ if (this.gl) this.device.gl.deleteTexture(this.gl);
258
+ if (this.renderbuffer) this.device.gl.deleteRenderbuffer(this.renderbuffer);
259
+ this.device.forgetTexture(this);
260
+ }
261
+ }
262
+ class Webgl2TextureView {
263
+ id = nextId++;
264
+ texture;
265
+ dimension;
266
+ baseMipLevel;
267
+ mipLevelCount;
268
+ baseArrayLayer;
269
+ arrayLayerCount;
270
+ label;
271
+ constructor(texture, d) {
272
+ this.texture = texture;
273
+ this.label = d.label ?? texture.label;
274
+ if (d.format && d.format !== texture.format) {
275
+ throw unsupported(
276
+ `view "${texture.label}" (${texture.format}) as ${d.format}`,
277
+ "Baseline textures have one format: the asset layer makes a twin for the other."
278
+ );
279
+ }
280
+ this.baseMipLevel = d.baseMipLevel ?? 0;
281
+ this.mipLevelCount = d.mipLevelCount ?? texture.mipLevelCount - this.baseMipLevel;
282
+ this.baseArrayLayer = d.baseArrayLayer ?? 0;
283
+ const layers = texture.dimension === "3d" ? 1 : texture.depthOrArrayLayers;
284
+ this.arrayLayerCount = d.arrayLayerCount ?? layers - this.baseArrayLayer;
285
+ const whole = this.baseArrayLayer === 0 && this.arrayLayerCount === layers;
286
+ this.dimension = d.dimension ?? (texture.dimension === "3d" ? "3d" : whole ? texture.bindingDimension : this.arrayLayerCount > 1 ? "2d-array" : "2d");
287
+ }
288
+ /** Whether binding this view reads the texture as its one binding dimension (baseline's rule). */
289
+ bindable() {
290
+ const t = this.texture;
291
+ if (t.renderbuffer) return false;
292
+ if (this.dimension !== t.bindingDimension) return false;
293
+ const layers = t.dimension === "3d" ? 1 : t.depthOrArrayLayers;
294
+ return this.baseArrayLayer === 0 && this.arrayLayerCount === layers;
295
+ }
296
+ }
297
+ const WRAP = {
298
+ "clamp-to-edge": GL.CLAMP_TO_EDGE,
299
+ repeat: GL.REPEAT,
300
+ "mirror-repeat": GL.MIRRORED_REPEAT
301
+ };
302
+ const COMPARE = {
303
+ never: GL.NEVER,
304
+ less: GL.LESS,
305
+ equal: GL.EQUAL,
306
+ "less-equal": GL.LEQUAL,
307
+ greater: GL.GREATER,
308
+ "not-equal": GL.NOTEQUAL,
309
+ "greater-equal": GL.GEQUAL,
310
+ always: GL.ALWAYS
311
+ };
312
+ class Webgl2Sampler {
313
+ id = nextId++;
314
+ gl;
315
+ label;
316
+ compare;
317
+ constructor(device, d = {}) {
318
+ const gl = device.gl;
319
+ this.label = d.label ?? "";
320
+ this.gl = gl.createSampler();
321
+ this.compare = d.compare !== void 0;
322
+ const mip = d.mipmapFilter ?? "nearest";
323
+ const min = d.minFilter === "linear" ? mip === "linear" ? GL.LINEAR_MIPMAP_LINEAR : GL.LINEAR_MIPMAP_NEAREST : mip === "linear" ? GL.NEAREST_MIPMAP_LINEAR : GL.NEAREST_MIPMAP_NEAREST;
324
+ gl.samplerParameteri(this.gl, GL.TEXTURE_MIN_FILTER, min);
325
+ gl.samplerParameteri(
326
+ this.gl,
327
+ GL.TEXTURE_MAG_FILTER,
328
+ d.magFilter === "linear" ? GL.LINEAR : GL.NEAREST
329
+ );
330
+ gl.samplerParameteri(this.gl, GL.TEXTURE_WRAP_S, WRAP[d.addressModeU ?? "clamp-to-edge"]);
331
+ gl.samplerParameteri(this.gl, GL.TEXTURE_WRAP_T, WRAP[d.addressModeV ?? "clamp-to-edge"]);
332
+ gl.samplerParameteri(this.gl, GL.TEXTURE_WRAP_R, WRAP[d.addressModeW ?? "clamp-to-edge"]);
333
+ gl.samplerParameterf(this.gl, GL.TEXTURE_MIN_LOD, d.lodMinClamp ?? 0);
334
+ gl.samplerParameterf(this.gl, GL.TEXTURE_MAX_LOD, d.lodMaxClamp ?? 32);
335
+ if (d.compare) {
336
+ gl.samplerParameteri(this.gl, GL.TEXTURE_COMPARE_MODE, GL.COMPARE_REF_TO_TEXTURE);
337
+ gl.samplerParameteri(this.gl, GL.TEXTURE_COMPARE_FUNC, COMPARE[d.compare]);
338
+ }
339
+ const anisotropy = d.maxAnisotropy ?? 1;
340
+ if (anisotropy > 1 && device.caps.anisotropy > 1) {
341
+ gl.samplerParameterf(
342
+ this.gl,
343
+ GL.TEXTURE_MAX_ANISOTROPY_EXT,
344
+ Math.min(anisotropy, device.caps.anisotropy)
345
+ );
346
+ }
347
+ }
348
+ }
349
+ export {
350
+ COMPARE,
351
+ Webgl2Buffer,
352
+ Webgl2Sampler,
353
+ Webgl2Texture,
354
+ Webgl2TextureView
355
+ };
@@ -0,0 +1,43 @@
1
+ import type { Webgl2RenderPipeline } from './pipeline.js';
2
+ export declare class GlState {
3
+ private readonly gl;
4
+ private program;
5
+ private vao;
6
+ private activeUnit;
7
+ private readonly textures;
8
+ private readonly targets;
9
+ private readonly samplers;
10
+ private readonly ubo;
11
+ private readonly uboOffset;
12
+ private readonly uboSize;
13
+ private readonly enabled;
14
+ private pipeline;
15
+ private stencilRef;
16
+ /** OES_draw_buffers_indexed: per-target blending, for pipelines whose targets differ. */
17
+ private readonly indexed;
18
+ constructor(gl: WebGL2RenderingContext, indexed: OES_draw_buffers_indexed | null);
19
+ /** After a context loss or anything outside the cache touched the context. */
20
+ reset(): void;
21
+ forgetProgram(): void;
22
+ useProgram(program: WebGLProgram | null): void;
23
+ bindVertexArray(vao: WebGLVertexArrayObject | null): void;
24
+ /** The unit uploads and parameter changes bind on. Draws bind their own units over it. */
25
+ scratchUnit: number;
26
+ /** Binds on the scratch unit, for uploads and parameters. */
27
+ bindTexture(target: number, texture: WebGLTexture | null): void;
28
+ /** Binds `texture` on `unit` and leaves that unit active (texture parameters apply to it). */
29
+ bindUnit(unit: number, target: number, texture: WebGLTexture | null): void;
30
+ activate(unit: number): void;
31
+ /** Forgets a deleted texture wherever it's bound. */
32
+ forgetTexture(texture: WebGLTexture | null): void;
33
+ bindSampler(unit: number, sampler: WebGLSampler | null): void;
34
+ bindUniformBuffer(point: number, buffer: WebGLBuffer | null, offset: number, size: number): void;
35
+ forgetBuffer(buffer: WebGLBuffer | null): void;
36
+ enable(cap: number, on: boolean): void;
37
+ /** A pipeline's fixed-function state: only what differs from the last pipeline's. */
38
+ applyPipeline(p: Webgl2RenderPipeline): void;
39
+ /** The stencil reference, for the pipeline's compare functions. */
40
+ applyStencil(p: Webgl2RenderPipeline, reference: number): void;
41
+ /** Clears ignore masks and the scissor in GL: open them, and let the next pipeline set them. */
42
+ prepareClear(): void;
43
+ }
package/dist/state.js ADDED
@@ -0,0 +1,169 @@
1
+ import { GL } from "./gl.js";
2
+ class GlState {
3
+ gl;
4
+ program = null;
5
+ vao = void 0;
6
+ activeUnit = -1;
7
+ textures = [];
8
+ targets = [];
9
+ samplers = [];
10
+ ubo = [];
11
+ uboOffset = [];
12
+ uboSize = [];
13
+ enabled = /* @__PURE__ */ new Map();
14
+ pipeline;
15
+ stencilRef = -1;
16
+ /** OES_draw_buffers_indexed: per-target blending, for pipelines whose targets differ. */
17
+ indexed;
18
+ constructor(gl, indexed) {
19
+ this.gl = gl;
20
+ this.indexed = indexed;
21
+ }
22
+ /** After a context loss or anything outside the cache touched the context. */
23
+ reset() {
24
+ this.program = null;
25
+ this.vao = void 0;
26
+ this.activeUnit = -1;
27
+ this.textures.length = 0;
28
+ this.targets.length = 0;
29
+ this.samplers.length = 0;
30
+ this.ubo.length = 0;
31
+ this.uboOffset.length = 0;
32
+ this.uboSize.length = 0;
33
+ this.enabled.clear();
34
+ this.pipeline = void 0;
35
+ this.stencilRef = -1;
36
+ }
37
+ forgetProgram() {
38
+ this.program = null;
39
+ this.pipeline = void 0;
40
+ }
41
+ useProgram(program) {
42
+ if (this.program === program) return;
43
+ this.program = program;
44
+ this.gl.useProgram(program);
45
+ }
46
+ bindVertexArray(vao) {
47
+ if (this.vao === vao) return;
48
+ this.vao = vao;
49
+ this.gl.bindVertexArray(vao);
50
+ }
51
+ /** The unit uploads and parameter changes bind on. Draws bind their own units over it. */
52
+ scratchUnit = 15;
53
+ /** Binds on the scratch unit, for uploads and parameters. */
54
+ bindTexture(target, texture) {
55
+ this.bindUnit(this.scratchUnit, target, texture);
56
+ }
57
+ /** Binds `texture` on `unit` and leaves that unit active (texture parameters apply to it). */
58
+ bindUnit(unit, target, texture) {
59
+ this.activate(unit);
60
+ if (this.textures[unit] === texture && this.targets[unit] === target) return;
61
+ const old = this.targets[unit];
62
+ if (old !== void 0 && old !== target && this.textures[unit]) this.gl.bindTexture(old, null);
63
+ this.gl.bindTexture(target, texture);
64
+ this.textures[unit] = texture;
65
+ this.targets[unit] = target;
66
+ }
67
+ activate(unit) {
68
+ if (this.activeUnit === unit) return;
69
+ this.activeUnit = unit;
70
+ this.gl.activeTexture(GL.TEXTURE0 + unit);
71
+ }
72
+ /** Forgets a deleted texture wherever it's bound. */
73
+ forgetTexture(texture) {
74
+ for (let i = 0; i < this.textures.length; i++)
75
+ if (this.textures[i] === texture) this.textures[i] = null;
76
+ }
77
+ bindSampler(unit, sampler) {
78
+ if (this.samplers[unit] === sampler) return;
79
+ this.samplers[unit] = sampler;
80
+ this.gl.bindSampler(unit, sampler);
81
+ }
82
+ bindUniformBuffer(point, buffer, offset, size) {
83
+ if (this.ubo[point] === buffer && this.uboOffset[point] === offset && this.uboSize[point] === size)
84
+ return;
85
+ this.ubo[point] = buffer;
86
+ this.uboOffset[point] = offset;
87
+ this.uboSize[point] = size;
88
+ this.gl.bindBufferRange(GL.UNIFORM_BUFFER, point, buffer, offset, size);
89
+ }
90
+ forgetBuffer(buffer) {
91
+ for (let i = 0; i < this.ubo.length; i++) if (this.ubo[i] === buffer) this.ubo[i] = null;
92
+ }
93
+ enable(cap, on) {
94
+ if (this.enabled.get(cap) === on) return;
95
+ this.enabled.set(cap, on);
96
+ if (on) this.gl.enable(cap);
97
+ else this.gl.disable(cap);
98
+ }
99
+ /** A pipeline's fixed-function state: only what differs from the last pipeline's. */
100
+ applyPipeline(p) {
101
+ this.useProgram(p.program);
102
+ const last = this.pipeline;
103
+ if (last === p) return;
104
+ this.pipeline = p;
105
+ const gl = this.gl;
106
+ this.enable(GL.CULL_FACE, p.cull !== 0);
107
+ if (p.cull !== 0 && (!last || last.cull !== p.cull)) gl.cullFace(p.cull);
108
+ if (!last || last.frontFace !== p.frontFace) gl.frontFace(p.frontFace);
109
+ this.enable(GL.DEPTH_TEST, p.depthTest);
110
+ if (p.depthTest) {
111
+ gl.depthFunc(p.depthFunc);
112
+ gl.depthMask(p.depthWrite);
113
+ }
114
+ const bias = p.depthBias !== 0 || p.depthSlope !== 0;
115
+ this.enable(GL.POLYGON_OFFSET_FILL, bias);
116
+ if (bias) gl.polygonOffset(p.depthSlope, p.depthBias);
117
+ this.enable(GL.STENCIL_TEST, p.stencil);
118
+ if (p.stencil) {
119
+ gl.stencilOpSeparate(GL.FRONT, p.stencilFront[1], p.stencilFront[2], p.stencilFront[3]);
120
+ gl.stencilOpSeparate(GL.BACK, p.stencilBack[1], p.stencilBack[2], p.stencilBack[3]);
121
+ gl.stencilMask(p.stencilWriteMask);
122
+ this.stencilRef = -1;
123
+ }
124
+ const x = this.indexed;
125
+ if (p.uniformTargets || !x) {
126
+ const blend = p.blends[0];
127
+ this.enable(GL.BLEND, blend?.enabled === true);
128
+ if (blend?.enabled) {
129
+ gl.blendEquationSeparate(blend.colorOp, blend.alphaOp);
130
+ gl.blendFuncSeparate(blend.colorSrc, blend.colorDst, blend.alphaSrc, blend.alphaDst);
131
+ }
132
+ const m = p.masks[0] ?? 15;
133
+ gl.colorMask((m & 1) !== 0, (m & 2) !== 0, (m & 4) !== 0, (m & 8) !== 0);
134
+ return;
135
+ }
136
+ this.enabled.delete(GL.BLEND);
137
+ for (let i = 0; i < p.blends.length; i++) {
138
+ const b = p.blends[i];
139
+ if (b.enabled) {
140
+ x.enableiOES(GL.BLEND, i);
141
+ x.blendEquationSeparateiOES(i, b.colorOp, b.alphaOp);
142
+ x.blendFuncSeparateiOES(i, b.colorSrc, b.colorDst, b.alphaSrc, b.alphaDst);
143
+ } else {
144
+ x.disableiOES(GL.BLEND, i);
145
+ }
146
+ const m = p.masks[i];
147
+ x.colorMaskiOES(i, (m & 1) !== 0, (m & 2) !== 0, (m & 4) !== 0, (m & 8) !== 0);
148
+ }
149
+ }
150
+ /** The stencil reference, for the pipeline's compare functions. */
151
+ applyStencil(p, reference) {
152
+ if (!p.stencil || this.stencilRef === reference) return;
153
+ this.stencilRef = reference;
154
+ this.gl.stencilFuncSeparate(GL.FRONT, p.stencilFront[0], reference, p.stencilReadMask);
155
+ this.gl.stencilFuncSeparate(GL.BACK, p.stencilBack[0], reference, p.stencilReadMask);
156
+ }
157
+ /** Clears ignore masks and the scissor in GL: open them, and let the next pipeline set them. */
158
+ prepareClear() {
159
+ const gl = this.gl;
160
+ this.enable(GL.SCISSOR_TEST, false);
161
+ gl.colorMask(true, true, true, true);
162
+ gl.depthMask(true);
163
+ gl.stencilMask(255);
164
+ this.pipeline = void 0;
165
+ }
166
+ }
167
+ export {
168
+ GlState
169
+ };