@luma.gl/webgl 9.1.0-alpha.14 → 9.1.0-alpha.15

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/dist.dev.js CHANGED
@@ -2142,1848 +2142,1849 @@ var __exports__ = (() => {
2142
2142
  };
2143
2143
 
2144
2144
  // src/adapter/webgl-canvas-context.ts
2145
- var import_core10 = __toESM(require_core(), 1);
2145
+ var import_core5 = __toESM(require_core(), 1);
2146
2146
 
2147
2147
  // src/adapter/resources/webgl-framebuffer.ts
2148
- var import_core9 = __toESM(require_core(), 1);
2149
-
2150
- // src/adapter/resources/webgl-texture.ts
2151
- var import_core8 = __toESM(require_core(), 1);
2152
-
2153
- // src/context/state-tracker/with-parameters.ts
2154
- function withGLParameters(gl, parameters, func) {
2155
- if (isObjectEmpty2(parameters)) {
2156
- return func(gl);
2157
- }
2158
- const { nocatch = true } = parameters;
2159
- const webglState = WebGLStateTracker.get(gl);
2160
- webglState.push();
2161
- setGLParameters(gl, parameters);
2162
- let value;
2163
- if (nocatch) {
2164
- value = func(gl);
2165
- webglState.pop();
2166
- } else {
2167
- try {
2168
- value = func(gl);
2169
- } finally {
2170
- webglState.pop();
2171
- }
2172
- }
2173
- return value;
2174
- }
2175
- function isObjectEmpty2(object) {
2176
- for (const key in object) {
2177
- return false;
2178
- }
2179
- return true;
2180
- }
2181
-
2182
- // src/adapter/converters/device-parameters.ts
2183
2148
  var import_core4 = __toESM(require_core(), 1);
2184
- function withDeviceAndGLParameters(device, parameters, glParameters, func) {
2185
- if (isObjectEmpty3(parameters)) {
2186
- return func(device);
2187
- }
2188
- const webglDevice = device;
2189
- webglDevice.pushState();
2190
- try {
2191
- setDeviceParameters(device, parameters);
2192
- setGLParameters(webglDevice.gl, glParameters);
2193
- return func(device);
2194
- } finally {
2195
- webglDevice.popState();
2149
+ var WEBGLFramebuffer = class extends import_core4.Framebuffer {
2150
+ device;
2151
+ gl;
2152
+ handle;
2153
+ colorAttachments = [];
2154
+ depthStencilAttachment = null;
2155
+ constructor(device, props) {
2156
+ super(device, props);
2157
+ const isDefaultFramebuffer = props.handle === null;
2158
+ this.device = device;
2159
+ this.gl = device.gl;
2160
+ this.handle = this.props.handle || isDefaultFramebuffer ? this.props.handle : this.gl.createFramebuffer();
2161
+ if (!isDefaultFramebuffer) {
2162
+ device.setSpectorMetadata(this.handle, { id: this.props.id, props: this.props });
2163
+ this.autoCreateAttachmentTextures();
2164
+ this.updateAttachments();
2165
+ }
2196
2166
  }
2197
- }
2198
- function withDeviceParameters(device, parameters, func) {
2199
- if (isObjectEmpty3(parameters)) {
2200
- return func(device);
2167
+ /** destroys any auto created resources etc. */
2168
+ destroy() {
2169
+ super.destroy();
2170
+ if (!this.destroyed && this.handle !== null) {
2171
+ this.gl.deleteFramebuffer(this.handle);
2172
+ }
2201
2173
  }
2202
- const webglDevice = device;
2203
- webglDevice.pushState();
2204
- try {
2205
- setDeviceParameters(device, parameters);
2206
- return func(device);
2207
- } finally {
2208
- webglDevice.popState();
2174
+ updateAttachments() {
2175
+ const prevHandle = this.gl.bindFramebuffer(
2176
+ 36160 /* FRAMEBUFFER */,
2177
+ this.handle
2178
+ );
2179
+ for (let i = 0; i < this.colorAttachments.length; ++i) {
2180
+ const attachment = this.colorAttachments[i];
2181
+ if (attachment) {
2182
+ const attachmentPoint = 36064 /* COLOR_ATTACHMENT0 */ + i;
2183
+ this._attachTextureView(attachmentPoint, attachment);
2184
+ }
2185
+ }
2186
+ if (this.depthStencilAttachment) {
2187
+ const attachmentPoint = getDepthStencilAttachmentWebGL(
2188
+ this.depthStencilAttachment.props.format
2189
+ );
2190
+ this._attachTextureView(attachmentPoint, this.depthStencilAttachment);
2191
+ }
2192
+ if (this.props.check !== false) {
2193
+ const status = this.gl.checkFramebufferStatus(36160 /* FRAMEBUFFER */);
2194
+ if (status !== 36053 /* FRAMEBUFFER_COMPLETE */) {
2195
+ throw new Error(`Framebuffer ${_getFrameBufferStatus(status)}`);
2196
+ }
2197
+ }
2198
+ this.gl.bindFramebuffer(36160 /* FRAMEBUFFER */, prevHandle);
2209
2199
  }
2210
- }
2211
- function setDeviceParameters(device, parameters) {
2212
- const webglDevice = device;
2213
- const { gl } = webglDevice;
2214
- if (parameters.cullMode) {
2215
- switch (parameters.cullMode) {
2216
- case "none":
2217
- gl.disable(2884 /* CULL_FACE */);
2200
+ // PRIVATE
2201
+ /** In WebGL we must use renderbuffers for depth/stencil attachments (unless we have extensions) */
2202
+ // protected override createDepthStencilTexture(format: TextureFormat): Texture {
2203
+ // // return new WEBGLRenderbuffer(this.device, {
2204
+ // return new WEBGLTexture(this.device, {
2205
+ // id: `${this.id}-depth-stencil`,
2206
+ // format,
2207
+ // width: this.width,
2208
+ // height: this.height,
2209
+ // mipmaps: false
2210
+ // });
2211
+ // }
2212
+ /**
2213
+ * @param attachment
2214
+ * @param texture
2215
+ * @param layer = 0 - index into WEBGLTextureArray and Texture3D or face for `TextureCubeMap`
2216
+ * @param level = 0 - mipmapLevel
2217
+ */
2218
+ _attachTextureView(attachment, textureView) {
2219
+ const { gl } = this.device;
2220
+ const { texture } = textureView;
2221
+ const level = textureView.props.baseMipLevel;
2222
+ const layer = textureView.props.baseArrayLayer;
2223
+ gl.bindTexture(texture.glTarget, texture.handle);
2224
+ switch (texture.glTarget) {
2225
+ case 35866 /* TEXTURE_2D_ARRAY */:
2226
+ case 32879 /* TEXTURE_3D */:
2227
+ gl.framebufferTextureLayer(36160 /* FRAMEBUFFER */, attachment, texture.handle, level, layer);
2218
2228
  break;
2219
- case "front":
2220
- gl.enable(2884 /* CULL_FACE */);
2221
- gl.cullFace(1028 /* FRONT */);
2229
+ case 34067 /* TEXTURE_CUBE_MAP */:
2230
+ const face = mapIndexToCubeMapFace(layer);
2231
+ gl.framebufferTexture2D(36160 /* FRAMEBUFFER */, attachment, face, texture.handle, level);
2222
2232
  break;
2223
- case "back":
2224
- gl.enable(2884 /* CULL_FACE */);
2225
- gl.cullFace(1029 /* BACK */);
2233
+ case 3553 /* TEXTURE_2D */:
2234
+ gl.framebufferTexture2D(36160 /* FRAMEBUFFER */, attachment, 3553 /* TEXTURE_2D */, texture.handle, level);
2226
2235
  break;
2236
+ default:
2237
+ throw new Error("Illegal texture type");
2227
2238
  }
2239
+ gl.bindTexture(texture.glTarget, null);
2228
2240
  }
2229
- if (parameters.frontFace) {
2230
- gl.frontFace(
2231
- map("frontFace", parameters.frontFace, {
2232
- ccw: 2305 /* CCW */,
2233
- cw: 2304 /* CW */
2234
- })
2235
- );
2241
+ };
2242
+ function mapIndexToCubeMapFace(layer) {
2243
+ return layer < 34069 /* TEXTURE_CUBE_MAP_POSITIVE_X */ ? layer + 34069 /* TEXTURE_CUBE_MAP_POSITIVE_X */ : layer;
2244
+ }
2245
+ function _getFrameBufferStatus(status) {
2246
+ switch (status) {
2247
+ case 36053 /* FRAMEBUFFER_COMPLETE */:
2248
+ return "success";
2249
+ case 36054 /* FRAMEBUFFER_INCOMPLETE_ATTACHMENT */:
2250
+ return "Mismatched attachments";
2251
+ case 36055 /* FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT */:
2252
+ return "No attachments";
2253
+ case 36057 /* FRAMEBUFFER_INCOMPLETE_DIMENSIONS */:
2254
+ return "Height/width mismatch";
2255
+ case 36061 /* FRAMEBUFFER_UNSUPPORTED */:
2256
+ return "Unsupported or split attachments";
2257
+ case 36182 /* FRAMEBUFFER_INCOMPLETE_MULTISAMPLE */:
2258
+ return "Samples mismatch";
2259
+ default:
2260
+ return `${status}`;
2236
2261
  }
2237
- if (parameters.unclippedDepth) {
2238
- if (device.features.has("depth-clip-control")) {
2239
- gl.enable(34383 /* DEPTH_CLAMP_EXT */);
2240
- }
2262
+ }
2263
+
2264
+ // src/adapter/webgl-canvas-context.ts
2265
+ var WebGLCanvasContext = class extends import_core5.CanvasContext {
2266
+ device;
2267
+ format = "rgba8unorm";
2268
+ depthStencilFormat = "depth24plus";
2269
+ presentationSize;
2270
+ _framebuffer = null;
2271
+ constructor(device, props) {
2272
+ super(props);
2273
+ this.device = device;
2274
+ this.presentationSize = [-1, -1];
2275
+ this._setAutoCreatedCanvasId(`${this.device.id}-canvas`);
2276
+ this.update();
2241
2277
  }
2242
- if (parameters.depthBias !== void 0) {
2243
- gl.enable(32823 /* POLYGON_OFFSET_FILL */);
2244
- gl.polygonOffset(parameters.depthBias, parameters.depthBiasSlopeScale || 0);
2278
+ getCurrentFramebuffer() {
2279
+ this.update();
2280
+ this._framebuffer = this._framebuffer || new WEBGLFramebuffer(this.device, { handle: null });
2281
+ return this._framebuffer;
2245
2282
  }
2246
- if (parameters.provokingVertex) {
2247
- if (device.features.has("provoking-vertex-webgl")) {
2248
- const extensions = webglDevice.getExtension("WEBGL_provoking_vertex");
2249
- const ext = extensions.WEBGL_provoking_vertex;
2250
- const vertex = map(
2251
- "provokingVertex",
2252
- parameters.provokingVertex,
2253
- {
2254
- first: 36429 /* FIRST_VERTEX_CONVENTION_WEBGL */,
2255
- last: 36430 /* LAST_VERTEX_CONVENTION_WEBGL */
2256
- }
2257
- );
2258
- ext?.provokingVertexWEBGL(vertex);
2283
+ /** Resizes and updates render targets if necessary */
2284
+ update() {
2285
+ const size = this.getPixelSize();
2286
+ const sizeChanged = size[0] !== this.presentationSize[0] || size[1] !== this.presentationSize[1];
2287
+ if (sizeChanged) {
2288
+ this.presentationSize = size;
2289
+ this.resize();
2259
2290
  }
2260
2291
  }
2261
- if (parameters.polygonMode || parameters.polygonOffsetLine) {
2262
- if (device.features.has("polygon-mode-webgl")) {
2263
- if (parameters.polygonMode) {
2264
- const extensions = webglDevice.getExtension("WEBGL_polygon_mode");
2265
- const ext = extensions.WEBGL_polygon_mode;
2266
- const mode = map("polygonMode", parameters.polygonMode, {
2267
- fill: 6914 /* FILL_WEBGL */,
2268
- line: 6913 /* LINE_WEBGL */
2269
- });
2270
- ext?.polygonModeWEBGL(1028 /* FRONT */, mode);
2271
- ext?.polygonModeWEBGL(1029 /* BACK */, mode);
2272
- }
2273
- if (parameters.polygonOffsetLine) {
2274
- gl.enable(10754 /* POLYGON_OFFSET_LINE_WEBGL */);
2275
- }
2292
+ /**
2293
+ * Resize the canvas' drawing buffer.
2294
+ *
2295
+ * Can match the canvas CSS size, and optionally also consider devicePixelRatio
2296
+ * Can be called every frame
2297
+ *
2298
+ * Regardless of size, the drawing buffer will always be scaled to the viewport, but
2299
+ * for best visual results, usually set to either:
2300
+ * canvas CSS width x canvas CSS height
2301
+ * canvas CSS width * devicePixelRatio x canvas CSS height * devicePixelRatio
2302
+ * See http://webgl2fundamentals.org/webgl/lessons/webgl-resizing-the-canvas.html
2303
+ */
2304
+ resize(options) {
2305
+ if (!this.device.gl)
2306
+ return;
2307
+ if (this.canvas) {
2308
+ const devicePixelRatio = this.getDevicePixelRatio(options?.useDevicePixels);
2309
+ this.setDevicePixelRatio(devicePixelRatio, options);
2310
+ return;
2276
2311
  }
2277
2312
  }
2278
- if (device.features.has("shader-clip-cull-distance-webgl")) {
2279
- if (parameters.clipDistance0) {
2280
- gl.enable(12288 /* CLIP_DISTANCE0_WEBGL */);
2281
- }
2282
- if (parameters.clipDistance1) {
2283
- gl.enable(12289 /* CLIP_DISTANCE1_WEBGL */);
2284
- }
2285
- if (parameters.clipDistance2) {
2286
- gl.enable(12290 /* CLIP_DISTANCE2_WEBGL */);
2287
- }
2288
- if (parameters.clipDistance3) {
2289
- gl.enable(12291 /* CLIP_DISTANCE3_WEBGL */);
2290
- }
2291
- if (parameters.clipDistance4) {
2292
- gl.enable(12292 /* CLIP_DISTANCE4_WEBGL */);
2293
- }
2294
- if (parameters.clipDistance5) {
2295
- gl.enable(12293 /* CLIP_DISTANCE5_WEBGL */);
2296
- }
2297
- if (parameters.clipDistance6) {
2298
- gl.enable(12294 /* CLIP_DISTANCE6_WEBGL */);
2313
+ commit() {
2314
+ }
2315
+ };
2316
+
2317
+ // src/context/debug/spector.ts
2318
+ var import_core6 = __toESM(require_core(), 1);
2319
+
2320
+ // src/utils/load-script.ts
2321
+ async function loadScript(scriptUrl, scriptId) {
2322
+ const head = document.getElementsByTagName("head")[0];
2323
+ if (!head) {
2324
+ throw new Error("loadScript");
2325
+ }
2326
+ const script = document.createElement("script");
2327
+ script.setAttribute("type", "text/javascript");
2328
+ script.setAttribute("src", scriptUrl);
2329
+ if (scriptId) {
2330
+ script.id = scriptId;
2331
+ }
2332
+ return new Promise((resolve, reject) => {
2333
+ script.onload = resolve;
2334
+ script.onerror = (error) => reject(new Error(`Unable to load script '${scriptUrl}': ${error}`));
2335
+ head.appendChild(script);
2336
+ });
2337
+ }
2338
+
2339
+ // src/context/debug/spector.ts
2340
+ var LOG_LEVEL = 1;
2341
+ var spector = null;
2342
+ var initialized = false;
2343
+ var DEFAULT_SPECTOR_PROPS = {
2344
+ debugWithSpectorJS: import_core6.log.get("spector") || import_core6.log.get("spectorjs"),
2345
+ // https://github.com/BabylonJS/Spector.js#basic-usage
2346
+ // https://forum.babylonjs.com/t/spectorcdn-is-temporarily-off/48241
2347
+ // spectorUrl: 'https://spectorcdn.babylonjs.com/spector.bundle.js';
2348
+ spectorUrl: "https://cdn.jsdelivr.net/npm/spectorjs@0.9.30/dist/spector.bundle.js",
2349
+ gl: void 0
2350
+ };
2351
+ async function loadSpectorJS(props) {
2352
+ if (!globalThis.SPECTOR) {
2353
+ try {
2354
+ await loadScript(props.spectorUrl || DEFAULT_SPECTOR_PROPS.spectorUrl);
2355
+ } catch (error) {
2356
+ import_core6.log.warn(String(error));
2299
2357
  }
2300
- if (parameters.clipDistance7) {
2301
- gl.enable(12295 /* CLIP_DISTANCE7_WEBGL */);
2358
+ }
2359
+ }
2360
+ function initializeSpectorJS(props) {
2361
+ props = { ...DEFAULT_SPECTOR_PROPS, ...props };
2362
+ if (!props.debugWithSpectorJS) {
2363
+ return null;
2364
+ }
2365
+ if (!spector && globalThis.SPECTOR && !globalThis.luma?.spector) {
2366
+ import_core6.log.probe(LOG_LEVEL, "SPECTOR found and initialized. Start with `luma.spector.displayUI()`")();
2367
+ const { Spector } = globalThis.SPECTOR;
2368
+ spector = new Spector();
2369
+ if (globalThis.luma) {
2370
+ globalThis.luma.spector = spector;
2302
2371
  }
2303
2372
  }
2304
- if (parameters.depthWriteEnabled !== void 0) {
2305
- gl.depthMask(mapBoolean("depthWriteEnabled", parameters.depthWriteEnabled));
2373
+ if (!spector) {
2374
+ return null;
2306
2375
  }
2307
- if (parameters.depthCompare) {
2308
- parameters.depthCompare !== "always" ? gl.enable(2929 /* DEPTH_TEST */) : gl.disable(2929 /* DEPTH_TEST */);
2309
- gl.depthFunc(convertCompareFunction("depthCompare", parameters.depthCompare));
2376
+ if (!initialized) {
2377
+ initialized = true;
2378
+ spector.spyCanvases();
2379
+ spector?.onCaptureStarted.add(
2380
+ (capture) => import_core6.log.info("Spector capture started:", capture)()
2381
+ );
2382
+ spector?.onCapture.add((capture) => {
2383
+ import_core6.log.info("Spector capture complete:", capture)();
2384
+ spector?.getResultUI();
2385
+ spector?.resultView.display();
2386
+ spector?.resultView.addCapture(capture);
2387
+ });
2310
2388
  }
2311
- if (parameters.stencilWriteMask) {
2312
- const mask = parameters.stencilWriteMask;
2313
- gl.stencilMaskSeparate(1028 /* FRONT */, mask);
2314
- gl.stencilMaskSeparate(1029 /* BACK */, mask);
2389
+ if (props.gl) {
2390
+ const gl = props.gl;
2391
+ const device = gl.device;
2392
+ spector?.startCapture(props.gl, 500);
2393
+ gl.device = device;
2394
+ new Promise((resolve) => setTimeout(resolve, 2e3)).then((_) => {
2395
+ import_core6.log.info("Spector capture stopped after 2 seconds")();
2396
+ spector?.stopCapture();
2397
+ });
2315
2398
  }
2316
- if (parameters.stencilReadMask) {
2317
- import_core4.log.warn("stencilReadMask not supported under WebGL");
2399
+ return spector;
2400
+ }
2401
+
2402
+ // src/context/debug/webgl-developer-tools.ts
2403
+ var import_core7 = __toESM(require_core(), 1);
2404
+
2405
+ // ../../node_modules/@probe.gl/env/dist/lib/globals.js
2406
+ var document_ = globalThis.document || {};
2407
+ var process_ = globalThis.process || {};
2408
+ var console_ = globalThis.console;
2409
+ var navigator_ = globalThis.navigator || {};
2410
+
2411
+ // ../../node_modules/@probe.gl/env/dist/lib/is-electron.js
2412
+ function isElectron(mockUserAgent) {
2413
+ if (typeof window !== "undefined" && window.process?.type === "renderer") {
2414
+ return true;
2318
2415
  }
2319
- if (parameters.stencilCompare) {
2320
- const mask = parameters.stencilReadMask || 4294967295;
2321
- const glValue = convertCompareFunction("depthCompare", parameters.stencilCompare);
2322
- parameters.stencilCompare !== "always" ? gl.enable(2960 /* STENCIL_TEST */) : gl.disable(2960 /* STENCIL_TEST */);
2323
- gl.stencilFuncSeparate(1028 /* FRONT */, glValue, 0, mask);
2324
- gl.stencilFuncSeparate(1029 /* BACK */, glValue, 0, mask);
2416
+ if (typeof process !== "undefined" && Boolean(process.versions?.["electron"])) {
2417
+ return true;
2325
2418
  }
2326
- if (parameters.stencilPassOperation && parameters.stencilFailOperation && parameters.stencilDepthFailOperation) {
2327
- const dppass = convertStencilOperation("stencilPassOperation", parameters.stencilPassOperation);
2328
- const sfail = convertStencilOperation("stencilFailOperation", parameters.stencilFailOperation);
2329
- const dpfail = convertStencilOperation(
2330
- "stencilDepthFailOperation",
2331
- parameters.stencilDepthFailOperation
2332
- );
2333
- gl.stencilOpSeparate(1028 /* FRONT */, sfail, dpfail, dppass);
2334
- gl.stencilOpSeparate(1029 /* BACK */, sfail, dpfail, dppass);
2419
+ const realUserAgent = typeof navigator !== "undefined" && navigator.userAgent;
2420
+ const userAgent = mockUserAgent || realUserAgent;
2421
+ return Boolean(userAgent && userAgent.indexOf("Electron") >= 0);
2422
+ }
2423
+
2424
+ // ../../node_modules/@probe.gl/env/dist/lib/is-browser.js
2425
+ function isBrowser() {
2426
+ const isNode = (
2427
+ // @ts-expect-error
2428
+ typeof process === "object" && String(process) === "[object process]" && !process?.browser
2429
+ );
2430
+ return !isNode || isElectron();
2431
+ }
2432
+
2433
+ // ../../node_modules/@probe.gl/env/dist/lib/get-browser.js
2434
+ function getBrowser(mockUserAgent) {
2435
+ if (!mockUserAgent && !isBrowser()) {
2436
+ return "Node";
2335
2437
  }
2336
- switch (parameters.blend) {
2337
- case true:
2338
- gl.enable(3042 /* BLEND */);
2339
- break;
2340
- case false:
2341
- gl.disable(3042 /* BLEND */);
2342
- break;
2343
- default:
2438
+ if (isElectron(mockUserAgent)) {
2439
+ return "Electron";
2344
2440
  }
2345
- if (parameters.blendColorOperation || parameters.blendAlphaOperation) {
2346
- const colorEquation = convertBlendOperationToEquation(
2347
- "blendColorOperation",
2348
- parameters.blendColorOperation || "add"
2349
- );
2350
- const alphaEquation = convertBlendOperationToEquation(
2351
- "blendAlphaOperation",
2352
- parameters.blendAlphaOperation || "add"
2353
- );
2354
- gl.blendEquationSeparate(colorEquation, alphaEquation);
2355
- const colorSrcFactor = convertBlendFactorToFunction(
2356
- "blendColorSrcFactor",
2357
- parameters.blendColorSrcFactor || "one"
2358
- );
2359
- const colorDstFactor = convertBlendFactorToFunction(
2360
- "blendColorDstFactor",
2361
- parameters.blendColorDstFactor || "zero"
2362
- );
2363
- const alphaSrcFactor = convertBlendFactorToFunction(
2364
- "blendAlphaSrcFactor",
2365
- parameters.blendAlphaSrcFactor || "one"
2366
- );
2367
- const alphaDstFactor = convertBlendFactorToFunction(
2368
- "blendAlphaDstFactor",
2369
- parameters.blendAlphaDstFactor || "zero"
2370
- );
2371
- gl.blendFuncSeparate(colorSrcFactor, colorDstFactor, alphaSrcFactor, alphaDstFactor);
2441
+ const userAgent = mockUserAgent || navigator_.userAgent || "";
2442
+ if (userAgent.indexOf("Edge") > -1) {
2443
+ return "Edge";
2444
+ }
2445
+ if (globalThis.chrome) {
2446
+ return "Chrome";
2447
+ }
2448
+ if (globalThis.safari) {
2449
+ return "Safari";
2450
+ }
2451
+ if (globalThis.mozInnerScreenX) {
2452
+ return "Firefox";
2372
2453
  }
2454
+ return "Unknown";
2373
2455
  }
2374
- function convertCompareFunction(parameter, value) {
2375
- return map(parameter, value, {
2376
- never: 512 /* NEVER */,
2377
- less: 513 /* LESS */,
2378
- equal: 514 /* EQUAL */,
2379
- "less-equal": 515 /* LEQUAL */,
2380
- greater: 516 /* GREATER */,
2381
- "not-equal": 517 /* NOTEQUAL */,
2382
- "greater-equal": 518 /* GEQUAL */,
2383
- always: 519 /* ALWAYS */
2384
- });
2456
+
2457
+ // src/context/debug/webgl-developer-tools.ts
2458
+ var WEBGL_DEBUG_CDN_URL = "https://unpkg.com/webgl-debug@2.0.1/index.js";
2459
+ function getWebGLContextData(gl) {
2460
+ gl.luma = gl.luma || {};
2461
+ return gl.luma;
2385
2462
  }
2386
- function convertStencilOperation(parameter, value) {
2387
- return map(parameter, value, {
2388
- keep: 7680 /* KEEP */,
2389
- zero: 0 /* ZERO */,
2390
- replace: 7681 /* REPLACE */,
2391
- invert: 5386 /* INVERT */,
2392
- "increment-clamp": 7682 /* INCR */,
2393
- "decrement-clamp": 7683 /* DECR */,
2394
- "increment-wrap": 34055 /* INCR_WRAP */,
2395
- "decrement-wrap": 34056 /* DECR_WRAP */
2396
- });
2463
+ async function loadWebGLDeveloperTools() {
2464
+ if (isBrowser() && !globalThis.WebGLDebugUtils) {
2465
+ globalThis.global = globalThis.global || globalThis;
2466
+ globalThis.global.module = {};
2467
+ await loadScript(WEBGL_DEBUG_CDN_URL);
2468
+ }
2397
2469
  }
2398
- function convertBlendOperationToEquation(parameter, value) {
2399
- return map(parameter, value, {
2400
- add: 32774 /* FUNC_ADD */,
2401
- subtract: 32778 /* FUNC_SUBTRACT */,
2402
- "reverse-subtract": 32779 /* FUNC_REVERSE_SUBTRACT */,
2403
- min: 32775 /* MIN */,
2404
- max: 32776 /* MAX */
2405
- });
2470
+ function makeDebugContext(gl, props = {}) {
2471
+ return props.debug ? getDebugContext(gl, props) : getRealContext(gl);
2406
2472
  }
2407
- function convertBlendFactorToFunction(parameter, value) {
2408
- return map(parameter, value, {
2409
- one: 1 /* ONE */,
2410
- zero: 0 /* ZERO */,
2411
- "src-color": 768 /* SRC_COLOR */,
2412
- "one-minus-src-color": 769 /* ONE_MINUS_SRC_COLOR */,
2413
- "dst-color": 774 /* DST_COLOR */,
2414
- "one-minus-dst-color": 775 /* ONE_MINUS_DST_COLOR */,
2415
- "src-alpha": 770 /* SRC_ALPHA */,
2416
- "one-minus-src-alpha": 771 /* ONE_MINUS_SRC_ALPHA */,
2417
- "dst-alpha": 772 /* DST_ALPHA */,
2418
- "one-minus-dst-alpha": 773 /* ONE_MINUS_DST_ALPHA */,
2419
- "src-alpha-saturated": 776 /* SRC_ALPHA_SATURATE */,
2420
- "constant-color": 32769 /* CONSTANT_COLOR */,
2421
- "one-minus-constant-color": 32770 /* ONE_MINUS_CONSTANT_COLOR */,
2422
- "constant-alpha": 32771 /* CONSTANT_ALPHA */,
2423
- "one-minus-constant-alpha": 32772 /* ONE_MINUS_CONSTANT_ALPHA */
2424
- });
2425
- }
2426
- function message(parameter, value) {
2427
- return `Illegal parameter ${value} for ${parameter}`;
2428
- }
2429
- function map(parameter, value, valueMap) {
2430
- if (!(value in valueMap)) {
2431
- throw new Error(message(parameter, value));
2432
- }
2433
- return valueMap[value];
2434
- }
2435
- function mapBoolean(parameter, value) {
2436
- return value;
2437
- }
2438
- function isObjectEmpty3(obj) {
2439
- let isEmpty = true;
2440
- for (const key in obj) {
2441
- isEmpty = false;
2442
- break;
2443
- }
2444
- return isEmpty;
2473
+ function getRealContext(gl) {
2474
+ const data = getWebGLContextData(gl);
2475
+ return data.realContext ? data.realContext : gl;
2445
2476
  }
2446
-
2447
- // src/adapter/converters/sampler-parameters.ts
2448
- function convertSamplerParametersToWebGL(props) {
2449
- const params = {};
2450
- if (props.addressModeU) {
2451
- params[10242 /* TEXTURE_WRAP_S */] = convertAddressMode(props.addressModeU);
2452
- }
2453
- if (props.addressModeV) {
2454
- params[10243 /* TEXTURE_WRAP_T */] = convertAddressMode(props.addressModeV);
2455
- }
2456
- if (props.addressModeW) {
2457
- params[32882 /* TEXTURE_WRAP_R */] = convertAddressMode(props.addressModeW);
2458
- }
2459
- if (props.magFilter) {
2460
- params[10240 /* TEXTURE_MAG_FILTER */] = convertMaxFilterMode(props.magFilter);
2461
- }
2462
- if (props.minFilter || props.mipmapFilter) {
2463
- params[10241 /* TEXTURE_MIN_FILTER */] = convertMinFilterMode(
2464
- props.minFilter || "linear",
2465
- props.mipmapFilter
2466
- );
2467
- }
2468
- if (props.lodMinClamp !== void 0) {
2469
- params[33082 /* TEXTURE_MIN_LOD */] = props.lodMinClamp;
2470
- }
2471
- if (props.lodMaxClamp !== void 0) {
2472
- params[33083 /* TEXTURE_MAX_LOD */] = props.lodMaxClamp;
2477
+ function getDebugContext(gl, props) {
2478
+ if (!globalThis.WebGLDebugUtils) {
2479
+ import_core7.log.warn("webgl-debug not loaded")();
2480
+ return gl;
2473
2481
  }
2474
- if (props.type === "comparison-sampler") {
2475
- params[34892 /* TEXTURE_COMPARE_MODE */] = 34894 /* COMPARE_REF_TO_TEXTURE */;
2482
+ const data = getWebGLContextData(gl);
2483
+ if (data.debugContext) {
2484
+ return data.debugContext;
2476
2485
  }
2477
- if (props.compare) {
2478
- params[34893 /* TEXTURE_COMPARE_FUNC */] = convertCompareFunction("compare", props.compare);
2486
+ globalThis.WebGLDebugUtils.init({ ...GLEnum, ...gl });
2487
+ const glDebug = globalThis.WebGLDebugUtils.makeDebugContext(
2488
+ gl,
2489
+ onGLError.bind(null, props),
2490
+ onValidateGLFunc.bind(null, props)
2491
+ );
2492
+ for (const key in GLEnum) {
2493
+ if (!(key in glDebug) && typeof GLEnum[key] === "number") {
2494
+ glDebug[key] = GLEnum[key];
2495
+ }
2479
2496
  }
2480
- if (props.maxAnisotropy) {
2481
- params[34046 /* TEXTURE_MAX_ANISOTROPY_EXT */] = props.maxAnisotropy;
2497
+ class WebGLDebugContext {
2482
2498
  }
2483
- return params;
2499
+ Object.setPrototypeOf(glDebug, Object.getPrototypeOf(gl));
2500
+ Object.setPrototypeOf(WebGLDebugContext, glDebug);
2501
+ const debugContext = Object.create(WebGLDebugContext);
2502
+ data.realContext = gl;
2503
+ data.debugContext = debugContext;
2504
+ debugContext.debug = true;
2505
+ return debugContext;
2484
2506
  }
2485
- function convertAddressMode(addressMode) {
2486
- switch (addressMode) {
2487
- case "clamp-to-edge":
2488
- return 33071 /* CLAMP_TO_EDGE */;
2489
- case "repeat":
2490
- return 10497 /* REPEAT */;
2491
- case "mirror-repeat":
2492
- return 33648 /* MIRRORED_REPEAT */;
2493
- }
2507
+ function getFunctionString(functionName, functionArgs) {
2508
+ functionArgs = Array.from(functionArgs).map((arg) => arg === void 0 ? "undefined" : arg);
2509
+ let args = globalThis.WebGLDebugUtils.glFunctionArgsToString(functionName, functionArgs);
2510
+ args = `${args.slice(0, 100)}${args.length > 100 ? "..." : ""}`;
2511
+ return `gl.${functionName}(${args})`;
2494
2512
  }
2495
- function convertMaxFilterMode(maxFilter) {
2496
- switch (maxFilter) {
2497
- case "nearest":
2498
- return 9728 /* NEAREST */;
2499
- case "linear":
2500
- return 9729 /* LINEAR */;
2513
+ function onGLError(props, err, functionName, args) {
2514
+ args = Array.from(args).map((arg) => arg === void 0 ? "undefined" : arg);
2515
+ const errorMessage = globalThis.WebGLDebugUtils.glEnumToString(err);
2516
+ const functionArgs = globalThis.WebGLDebugUtils.glFunctionArgsToString(functionName, args);
2517
+ const message2 = `${errorMessage} in gl.${functionName}(${functionArgs})`;
2518
+ import_core7.log.error(message2)();
2519
+ debugger;
2520
+ if (props.throwOnError) {
2521
+ throw new Error(message2);
2501
2522
  }
2502
2523
  }
2503
- function convertMinFilterMode(minFilter, mipmapFilter) {
2504
- if (!mipmapFilter) {
2505
- return convertMaxFilterMode(minFilter);
2524
+ function onValidateGLFunc(props, functionName, functionArgs) {
2525
+ let functionString = "";
2526
+ if (import_core7.log.level >= 1) {
2527
+ functionString = getFunctionString(functionName, functionArgs);
2528
+ import_core7.log.log(1, functionString)();
2506
2529
  }
2507
- switch (minFilter) {
2508
- case "nearest":
2509
- return mipmapFilter === "nearest" ? 9984 /* NEAREST_MIPMAP_NEAREST */ : 9986 /* NEAREST_MIPMAP_LINEAR */;
2510
- case "linear":
2511
- return mipmapFilter === "nearest" ? 9985 /* LINEAR_MIPMAP_NEAREST */ : 9987 /* LINEAR_MIPMAP_LINEAR */;
2530
+ if (props.break && props.break.length > 0) {
2531
+ functionString = functionString || getFunctionString(functionName, functionArgs);
2532
+ const isBreakpoint = props.break.every(
2533
+ (breakOn) => functionString.indexOf(breakOn) !== -1
2534
+ );
2535
+ if (isBreakpoint) {
2536
+ debugger;
2537
+ }
2538
+ }
2539
+ for (const arg of functionArgs) {
2540
+ if (arg === void 0) {
2541
+ functionString = functionString || getFunctionString(functionName, functionArgs);
2542
+ if (props.throwOnError) {
2543
+ throw new Error(`Undefined argument: ${functionString}`);
2544
+ } else {
2545
+ import_core7.log.error(`Undefined argument: ${functionString}`)();
2546
+ debugger;
2547
+ }
2548
+ }
2512
2549
  }
2513
2550
  }
2514
2551
 
2515
- // src/adapter/resources/webgl-sampler.ts
2516
- var import_core5 = __toESM(require_core(), 1);
2517
- var WEBGLSampler = class extends import_core5.Sampler {
2552
+ // src/utils/uid.ts
2553
+ var uidCounters = {};
2554
+ function uid(id = "id") {
2555
+ uidCounters[id] = uidCounters[id] || 1;
2556
+ const count = uidCounters[id]++;
2557
+ return `${id}-${count}`;
2558
+ }
2559
+
2560
+ // src/adapter/resources/webgl-buffer.ts
2561
+ var import_core8 = __toESM(require_core(), 1);
2562
+ var WEBGLBuffer = class extends import_core8.Buffer {
2518
2563
  device;
2564
+ gl;
2519
2565
  handle;
2520
- parameters;
2521
- constructor(device, props) {
2566
+ /** Target in OpenGL defines the type of buffer */
2567
+ glTarget;
2568
+ /** Usage is a hint on how frequently the buffer will be updates */
2569
+ glUsage;
2570
+ /** Index type is needed when issuing draw calls, so we pre-compute it */
2571
+ glIndexType = 5123 /* UNSIGNED_SHORT */;
2572
+ /** Number of bytes allocated on the GPU for this buffer */
2573
+ byteLength;
2574
+ /** Number of bytes used */
2575
+ bytesUsed;
2576
+ constructor(device, props = {}) {
2522
2577
  super(device, props);
2523
2578
  this.device = device;
2524
- this.parameters = convertSamplerParametersToWebGL(props);
2525
- this.handle = this.handle || this.device.gl.createSampler();
2526
- this._setSamplerParameters(this.parameters);
2527
- }
2528
- destroy() {
2529
- if (this.handle) {
2530
- this.device.gl.deleteSampler(this.handle);
2531
- this.handle = void 0;
2579
+ this.gl = this.device.gl;
2580
+ const handle = typeof props === "object" ? props.handle : void 0;
2581
+ this.handle = handle || this.gl.createBuffer();
2582
+ device.setSpectorMetadata(this.handle, { ...this.props, data: typeof this.props.data });
2583
+ this.glTarget = getWebGLTarget(this.props.usage);
2584
+ this.glUsage = getWebGLUsage(this.props.usage);
2585
+ this.glIndexType = this.props.indexType === "uint32" ? 5125 /* UNSIGNED_INT */ : 5123 /* UNSIGNED_SHORT */;
2586
+ if (props.data) {
2587
+ this._initWithData(props.data, props.byteOffset, props.byteLength);
2588
+ } else {
2589
+ this._initWithByteLength(props.byteLength || 0);
2532
2590
  }
2533
2591
  }
2534
- toString() {
2535
- return `Sampler(${this.id},${JSON.stringify(this.props)})`;
2592
+ // PRIVATE METHODS
2593
+ /** Allocate a new buffer and initialize to contents of typed array */
2594
+ _initWithData(data, byteOffset = 0, byteLength = data.byteLength + byteOffset) {
2595
+ const glTarget = this.glTarget;
2596
+ this.gl.bindBuffer(glTarget, this.handle);
2597
+ this.gl.bufferData(glTarget, byteLength, this.glUsage);
2598
+ this.gl.bufferSubData(glTarget, byteOffset, data);
2599
+ this.gl.bindBuffer(glTarget, null);
2600
+ this.bytesUsed = byteLength;
2601
+ this.byteLength = byteLength;
2602
+ this._setDebugData(data, byteOffset, byteLength);
2603
+ this.trackAllocatedMemory(byteLength);
2536
2604
  }
2537
- /** Set sampler parameters on the sampler */
2538
- _setSamplerParameters(parameters) {
2539
- for (const [pname, value] of Object.entries(parameters)) {
2540
- const param = Number(pname);
2541
- switch (param) {
2542
- case 33082 /* TEXTURE_MIN_LOD */:
2543
- case 33083 /* TEXTURE_MAX_LOD */:
2544
- this.device.gl.samplerParameterf(this.handle, param, value);
2545
- break;
2546
- default:
2547
- this.device.gl.samplerParameteri(this.handle, param, value);
2548
- break;
2549
- }
2605
+ // Allocate a GPU buffer of specified size.
2606
+ _initWithByteLength(byteLength) {
2607
+ let data = byteLength;
2608
+ if (byteLength === 0) {
2609
+ data = new Float32Array(0);
2550
2610
  }
2611
+ const glTarget = this.glTarget;
2612
+ this.gl.bindBuffer(glTarget, this.handle);
2613
+ this.gl.bufferData(glTarget, data, this.glUsage);
2614
+ this.gl.bindBuffer(glTarget, null);
2615
+ this.bytesUsed = byteLength;
2616
+ this.byteLength = byteLength;
2617
+ this._setDebugData(null, 0, byteLength);
2618
+ this.trackAllocatedMemory(byteLength);
2619
+ return this;
2551
2620
  }
2552
- };
2553
-
2554
- // src/adapter/resources/webgl-texture-view.ts
2555
- var import_core6 = __toESM(require_core(), 1);
2556
- var WEBGLTextureView = class extends import_core6.TextureView {
2557
- device;
2558
- gl;
2559
- handle;
2560
- // Does not have a WebGL representation
2561
- texture;
2562
- constructor(device, props) {
2563
- super(device, { ...import_core6.Texture.defaultProps, ...props });
2564
- this.device = device;
2565
- this.gl = this.device.gl;
2566
- this.handle = null;
2567
- this.texture = props.texture;
2621
+ destroy() {
2622
+ if (!this.destroyed && this.handle) {
2623
+ this.removeStats();
2624
+ this.trackDeallocatedMemory();
2625
+ this.gl.deleteBuffer(this.handle);
2626
+ this.destroyed = true;
2627
+ this.handle = null;
2628
+ }
2629
+ }
2630
+ write(data, byteOffset = 0) {
2631
+ const srcOffset = 0;
2632
+ const byteLength = void 0;
2633
+ const glTarget = 36663 /* COPY_WRITE_BUFFER */;
2634
+ this.gl.bindBuffer(glTarget, this.handle);
2635
+ if (srcOffset !== 0 || byteLength !== void 0) {
2636
+ this.gl.bufferSubData(glTarget, byteOffset, data, srcOffset, byteLength);
2637
+ } else {
2638
+ this.gl.bufferSubData(glTarget, byteOffset, data);
2639
+ }
2640
+ this.gl.bindBuffer(glTarget, null);
2641
+ this._setDebugData(data, byteOffset, data.byteLength);
2642
+ }
2643
+ /** Asynchronously read data from the buffer */
2644
+ async readAsync(byteOffset = 0, byteLength) {
2645
+ return this.readSyncWebGL(byteOffset, byteLength);
2646
+ }
2647
+ /** Synchronously read data from the buffer. WebGL only. */
2648
+ readSyncWebGL(byteOffset = 0, byteLength) {
2649
+ byteLength = byteLength ?? this.byteLength - byteOffset;
2650
+ const data = new Uint8Array(byteLength);
2651
+ const dstOffset = 0;
2652
+ this.gl.bindBuffer(36662 /* COPY_READ_BUFFER */, this.handle);
2653
+ this.gl.getBufferSubData(36662 /* COPY_READ_BUFFER */, byteOffset, data, dstOffset, byteLength);
2654
+ this.gl.bindBuffer(36662 /* COPY_READ_BUFFER */, null);
2655
+ this._setDebugData(data, byteOffset, byteLength);
2656
+ return data;
2568
2657
  }
2569
2658
  };
2570
-
2571
- // src/adapter/helpers/webgl-texture-utils.ts
2572
- var import_core7 = __toESM(require_core(), 1);
2573
-
2574
- // src/adapter/helpers/typed-array-utils.ts
2575
- var ERR_TYPE_DEDUCTION = "Failed to deduce GL constant from typed array";
2576
- function getGLTypeFromTypedArray(arrayOrType) {
2577
- const type = ArrayBuffer.isView(arrayOrType) ? arrayOrType.constructor : arrayOrType;
2578
- switch (type) {
2579
- case Float32Array:
2580
- return 5126 /* FLOAT */;
2581
- case Uint16Array:
2582
- return 5123 /* UNSIGNED_SHORT */;
2583
- case Uint32Array:
2584
- return 5125 /* UNSIGNED_INT */;
2585
- case Uint8Array:
2586
- return 5121 /* UNSIGNED_BYTE */;
2587
- case Uint8ClampedArray:
2588
- return 5121 /* UNSIGNED_BYTE */;
2589
- case Int8Array:
2590
- return 5120 /* BYTE */;
2591
- case Int16Array:
2592
- return 5122 /* SHORT */;
2593
- case Int32Array:
2594
- return 5124 /* INT */;
2595
- default:
2596
- throw new Error(ERR_TYPE_DEDUCTION);
2659
+ function getWebGLTarget(usage) {
2660
+ if (usage & import_core8.Buffer.INDEX) {
2661
+ return 34963 /* ELEMENT_ARRAY_BUFFER */;
2662
+ }
2663
+ if (usage & import_core8.Buffer.VERTEX) {
2664
+ return 34962 /* ARRAY_BUFFER */;
2665
+ }
2666
+ if (usage & import_core8.Buffer.UNIFORM) {
2667
+ return 35345 /* UNIFORM_BUFFER */;
2597
2668
  }
2669
+ return 34962 /* ARRAY_BUFFER */;
2598
2670
  }
2599
- function getTypedArrayFromGLType(glType, options) {
2600
- const { clamped = true } = options || {};
2601
- switch (glType) {
2602
- case 5126 /* FLOAT */:
2603
- return Float32Array;
2604
- case 5123 /* UNSIGNED_SHORT */:
2605
- case 33635 /* UNSIGNED_SHORT_5_6_5 */:
2606
- case 32819 /* UNSIGNED_SHORT_4_4_4_4 */:
2607
- case 32820 /* UNSIGNED_SHORT_5_5_5_1 */:
2608
- return Uint16Array;
2609
- case 5125 /* UNSIGNED_INT */:
2610
- return Uint32Array;
2611
- case 5121 /* UNSIGNED_BYTE */:
2612
- return clamped ? Uint8ClampedArray : Uint8Array;
2613
- case 5120 /* BYTE */:
2614
- return Int8Array;
2615
- case 5122 /* SHORT */:
2616
- return Int16Array;
2617
- case 5124 /* INT */:
2618
- return Int32Array;
2619
- default:
2620
- throw new Error("Failed to deduce typed array type from GL constant");
2671
+ function getWebGLUsage(usage) {
2672
+ if (usage & import_core8.Buffer.INDEX) {
2673
+ return 35044 /* STATIC_DRAW */;
2674
+ }
2675
+ if (usage & import_core8.Buffer.VERTEX) {
2676
+ return 35044 /* STATIC_DRAW */;
2621
2677
  }
2678
+ if (usage & import_core8.Buffer.UNIFORM) {
2679
+ return 35048 /* DYNAMIC_DRAW */;
2680
+ }
2681
+ return 35044 /* STATIC_DRAW */;
2622
2682
  }
2623
2683
 
2624
- // src/adapter/helpers/format-utils.ts
2625
- function glFormatToComponents(format) {
2626
- switch (format) {
2627
- case 6406 /* ALPHA */:
2628
- case 33326 /* R32F */:
2629
- case 6403 /* RED */:
2630
- return 1;
2631
- case 33328 /* RG32F */:
2632
- case 33319 /* RG */:
2633
- return 2;
2634
- case 6407 /* RGB */:
2635
- case 34837 /* RGB32F */:
2636
- return 3;
2637
- case 6408 /* RGBA */:
2638
- case 34836 /* RGBA32F */:
2639
- return 4;
2640
- default:
2641
- return 0;
2684
+ // src/adapter/resources/webgl-shader.ts
2685
+ var import_core9 = __toESM(require_core(), 1);
2686
+
2687
+ // src/adapter/helpers/parse-shader-compiler-log.ts
2688
+ function parseShaderCompilerLog(errLog) {
2689
+ const lines = errLog.split(/\r?\n/);
2690
+ const messages = [];
2691
+ for (const line of lines) {
2692
+ if (line.length <= 1) {
2693
+ continue;
2694
+ }
2695
+ const segments = line.split(":");
2696
+ if (segments.length === 2) {
2697
+ const [messageType2, message2] = segments;
2698
+ messages.push({
2699
+ message: message2.trim(),
2700
+ type: getMessageType(messageType2),
2701
+ lineNum: 0,
2702
+ linePos: 0
2703
+ });
2704
+ continue;
2705
+ }
2706
+ const [messageType, linePosition, lineNumber, ...rest] = segments;
2707
+ let lineNum = parseInt(lineNumber, 10);
2708
+ if (isNaN(lineNum)) {
2709
+ lineNum = 0;
2710
+ }
2711
+ let linePos = parseInt(linePosition, 10);
2712
+ if (isNaN(linePos)) {
2713
+ linePos = 0;
2714
+ }
2715
+ messages.push({
2716
+ message: rest.join(":").trim(),
2717
+ type: getMessageType(messageType),
2718
+ lineNum,
2719
+ linePos
2720
+ // TODO
2721
+ });
2642
2722
  }
2723
+ return messages;
2643
2724
  }
2644
- function glTypeToBytes(type) {
2645
- switch (type) {
2646
- case 5121 /* UNSIGNED_BYTE */:
2647
- return 1;
2648
- case 33635 /* UNSIGNED_SHORT_5_6_5 */:
2649
- case 32819 /* UNSIGNED_SHORT_4_4_4_4 */:
2650
- case 32820 /* UNSIGNED_SHORT_5_5_5_1 */:
2651
- return 2;
2652
- case 5126 /* FLOAT */:
2653
- return 4;
2654
- default:
2655
- return 0;
2656
- }
2725
+ function getMessageType(messageType) {
2726
+ const MESSAGE_TYPES = ["warning", "error", "info"];
2727
+ const lowerCaseType = messageType.toLowerCase();
2728
+ return MESSAGE_TYPES.includes(lowerCaseType) ? lowerCaseType : "info";
2657
2729
  }
2658
2730
 
2659
- // src/adapter/helpers/webgl-texture-utils.ts
2660
- function initializeTextureStorage(gl, levels, options) {
2661
- const { dimension, width, height, depth = 0 } = options;
2662
- const { glInternalFormat } = options;
2663
- const glTarget = options.glTarget;
2664
- switch (dimension) {
2665
- case "2d-array":
2666
- case "3d":
2667
- gl.texStorage3D(glTarget, levels, glInternalFormat, width, height, depth);
2668
- break;
2669
- default:
2670
- gl.texStorage2D(glTarget, levels, glInternalFormat, width, height);
2731
+ // src/adapter/resources/webgl-shader.ts
2732
+ var WEBGLShader = class extends import_core9.Shader {
2733
+ device;
2734
+ handle;
2735
+ constructor(device, props) {
2736
+ super(device, props);
2737
+ this.device = device;
2738
+ switch (this.props.stage) {
2739
+ case "vertex":
2740
+ this.handle = this.props.handle || this.device.gl.createShader(35633 /* VERTEX_SHADER */);
2741
+ break;
2742
+ case "fragment":
2743
+ this.handle = this.props.handle || this.device.gl.createShader(35632 /* FRAGMENT_SHADER */);
2744
+ break;
2745
+ default:
2746
+ throw new Error(this.props.stage);
2747
+ }
2748
+ this._compile(this.source);
2671
2749
  }
2672
- }
2673
- function copyExternalImageToMipLevel(gl, handle, image, options) {
2674
- const { width, height } = options;
2675
- const { dimension, depth = 0, mipLevel = 0 } = options;
2676
- const { x = 0, y = 0, z = 0 } = options;
2677
- const { glFormat, glType } = options;
2678
- const glTarget = getWebGLCubeFaceTarget(options.glTarget, dimension, depth);
2679
- switch (dimension) {
2680
- case "2d-array":
2681
- case "3d":
2682
- gl.bindTexture(glTarget, handle);
2683
- gl.texSubImage3D(glTarget, mipLevel, x, y, z, width, height, depth, glFormat, glType, image);
2684
- gl.bindTexture(glTarget, null);
2685
- break;
2686
- case "2d":
2687
- case "cube":
2688
- gl.bindTexture(glTarget, handle);
2689
- gl.texSubImage2D(glTarget, mipLevel, x, y, width, height, glFormat, glType, image);
2690
- gl.bindTexture(glTarget, null);
2691
- break;
2692
- default:
2693
- throw new Error(dimension);
2750
+ destroy() {
2751
+ if (this.handle) {
2752
+ this.removeStats();
2753
+ this.device.gl.deleteShader(this.handle);
2754
+ this.destroyed = true;
2755
+ }
2694
2756
  }
2695
- }
2696
- function copyCPUDataToMipLevel(gl, typedArray, options) {
2697
- const { dimension, width, height, depth = 0, mipLevel = 0, byteOffset = 0 } = options;
2698
- const { x = 0, y = 0, z = 0 } = options;
2699
- const { glFormat, glType, compressed } = options;
2700
- const glTarget = getWebGLCubeFaceTarget(options.glTarget, dimension, depth);
2701
- switch (dimension) {
2702
- case "2d-array":
2703
- case "3d":
2704
- if (compressed) {
2705
- gl.compressedTexSubImage3D(glTarget, mipLevel, x, y, z, width, height, depth, glFormat, typedArray, byteOffset);
2706
- } else {
2707
- gl.texSubImage3D(glTarget, mipLevel, x, y, z, width, height, depth, glFormat, glType, typedArray, byteOffset);
2708
- }
2709
- break;
2710
- case "2d":
2711
- case "cube":
2712
- if (compressed) {
2713
- gl.compressedTexSubImage2D(glTarget, mipLevel, x, y, width, height, glFormat, typedArray, byteOffset);
2714
- } else {
2715
- gl.texSubImage2D(glTarget, mipLevel, x, y, width, height, glFormat, glType, typedArray, byteOffset);
2716
- }
2717
- break;
2718
- default:
2719
- throw new Error(dimension);
2757
+ async getCompilationInfo() {
2758
+ await this._waitForCompilationComplete();
2759
+ return this.getCompilationInfoSync();
2720
2760
  }
2721
- }
2722
- function getWebGLTextureTarget(dimension) {
2723
- switch (dimension) {
2724
- case "1d":
2725
- break;
2726
- case "2d":
2727
- return 3553 /* TEXTURE_2D */;
2728
- case "3d":
2729
- return 32879 /* TEXTURE_3D */;
2730
- case "cube":
2731
- return 34067 /* TEXTURE_CUBE_MAP */;
2732
- case "2d-array":
2733
- return 35866 /* TEXTURE_2D_ARRAY */;
2734
- case "cube-array":
2735
- break;
2736
- }
2737
- throw new Error(dimension);
2738
- }
2739
- function getWebGLCubeFaceTarget(glTarget, dimension, level) {
2740
- return dimension === "cube" ? 34069 /* TEXTURE_CUBE_MAP_POSITIVE_X */ + level : glTarget;
2741
- }
2742
- function readPixelsToArray(source, options) {
2743
- const {
2744
- sourceX = 0,
2745
- sourceY = 0,
2746
- sourceAttachment = 36064 /* COLOR_ATTACHMENT0 */
2747
- // TODO - support gl.readBuffer
2748
- } = options || {};
2749
- let {
2750
- target = null,
2751
- // following parameters are auto deduced if not provided
2752
- sourceWidth,
2753
- sourceHeight,
2754
- sourceDepth,
2755
- sourceFormat,
2756
- sourceType
2757
- } = options || {};
2758
- const { framebuffer, deleteFramebuffer } = getFramebuffer(source);
2759
- const { gl, handle } = framebuffer;
2760
- const attachment = sourceAttachment - 36064 /* COLOR_ATTACHMENT0 */;
2761
- sourceWidth ||= framebuffer.width;
2762
- sourceHeight ||= framebuffer.height;
2763
- sourceDepth = framebuffer.colorAttachments[attachment]?.texture?.depth || 1;
2764
- sourceFormat ||= framebuffer.colorAttachments[attachment]?.texture?.glFormat || 6408 /* RGBA */;
2765
- sourceType ||= framebuffer.colorAttachments[attachment]?.texture?.glType || 5121 /* UNSIGNED_BYTE */;
2766
- target = getPixelArray(target, sourceType, sourceFormat, sourceWidth, sourceHeight, sourceDepth);
2767
- sourceType = sourceType || getGLTypeFromTypedArray(target);
2768
- const prevHandle = gl.bindFramebuffer(36160 /* FRAMEBUFFER */, handle);
2769
- gl.readPixels(sourceX, sourceY, sourceWidth, sourceHeight, sourceFormat, sourceType, target);
2770
- gl.bindFramebuffer(36160 /* FRAMEBUFFER */, prevHandle || null);
2771
- if (deleteFramebuffer) {
2772
- framebuffer.destroy();
2773
- }
2774
- return target;
2775
- }
2776
- function readPixelsToBuffer(source, options) {
2777
- const {
2778
- target,
2779
- sourceX = 0,
2780
- sourceY = 0,
2781
- sourceFormat = 6408 /* RGBA */,
2782
- targetByteOffset = 0
2783
- } = options || {};
2784
- let { sourceWidth, sourceHeight, sourceType } = options || {};
2785
- const { framebuffer, deleteFramebuffer } = getFramebuffer(source);
2786
- sourceWidth = sourceWidth || framebuffer.width;
2787
- sourceHeight = sourceHeight || framebuffer.height;
2788
- const webglFramebuffer = framebuffer;
2789
- sourceType = sourceType || 5121 /* UNSIGNED_BYTE */;
2790
- let webglBufferTarget = target;
2791
- if (!webglBufferTarget) {
2792
- const components = glFormatToComponents(sourceFormat);
2793
- const byteCount = glTypeToBytes(sourceType);
2794
- const byteLength = targetByteOffset + sourceWidth * sourceHeight * components * byteCount;
2795
- webglBufferTarget = webglFramebuffer.device.createBuffer({ byteLength });
2796
- }
2797
- const commandEncoder = source.device.createCommandEncoder();
2798
- commandEncoder.copyTextureToBuffer({
2799
- source,
2800
- width: sourceWidth,
2801
- height: sourceHeight,
2802
- origin: [sourceX, sourceY],
2803
- destination: webglBufferTarget,
2804
- byteOffset: targetByteOffset
2805
- });
2806
- commandEncoder.destroy();
2807
- if (deleteFramebuffer) {
2808
- framebuffer.destroy();
2809
- }
2810
- return webglBufferTarget;
2811
- }
2812
- function getFramebuffer(source) {
2813
- if (!(source instanceof import_core7.Framebuffer)) {
2814
- return { framebuffer: toFramebuffer(source), deleteFramebuffer: true };
2815
- }
2816
- return { framebuffer: source, deleteFramebuffer: false };
2817
- }
2818
- function toFramebuffer(texture, props) {
2819
- const { device, width, height, id } = texture;
2820
- const framebuffer = device.createFramebuffer({
2821
- ...props,
2822
- id: `framebuffer-for-${id}`,
2823
- width,
2824
- height,
2825
- colorAttachments: [texture]
2826
- });
2827
- return framebuffer;
2828
- }
2829
- function getPixelArray(pixelArray, type, format, width, height, depth) {
2830
- if (pixelArray) {
2831
- return pixelArray;
2832
- }
2833
- type = type || 5121 /* UNSIGNED_BYTE */;
2834
- const ArrayType = getTypedArrayFromGLType(type, { clamped: false });
2835
- const components = glFormatToComponents(format);
2836
- return new ArrayType(width * height * components);
2837
- }
2838
-
2839
- // src/adapter/resources/webgl-texture.ts
2840
- function normalizeTextureData(data, options) {
2841
- let lodArray;
2842
- if (ArrayBuffer.isView(data)) {
2843
- lodArray = [
2844
- {
2845
- // ts-expect-error does data really need to be Uint8ClampedArray?
2846
- data,
2847
- width: options.width,
2848
- height: options.height
2849
- // depth: options.depth
2850
- }
2851
- ];
2852
- } else if (!Array.isArray(data)) {
2853
- lodArray = [data];
2854
- } else {
2855
- lodArray = data;
2856
- }
2857
- return lodArray;
2858
- }
2859
- var WEBGLTexture = class extends import_core8.Texture {
2860
- MAX_ATTRIBUTES;
2861
- device;
2862
- gl;
2863
- handle;
2864
- sampler = void 0;
2865
- // TODO - currently unused in WebGL. Create dummy sampler?
2866
- view = void 0;
2867
- // TODO - currently unused in WebGL. Create dummy view?
2868
- mipmaps = false;
2869
- /**
2870
- * @note `target` cannot be modified by bind:
2871
- * textures are special because when you first bind them to a target,
2872
- * When you first bind a texture as a GL_TEXTURE_2D, you are saying that this texture is a 2D texture.
2873
- * And it will always be a 2D texture; this state cannot be changed ever.
2874
- * A texture that was first bound as a GL_TEXTURE_2D, must always be bound as a GL_TEXTURE_2D;
2875
- * attempting to bind it as GL_TEXTURE_3D will give rise to a run-time error
2876
- */
2877
- glTarget;
2878
- // Texture type
2879
- /** The WebGL format - essentially channel structure */
2880
- glFormat;
2881
- /** The WebGL data format - the type of each channel */
2882
- glType;
2883
- /** The WebGL constant corresponding to the WebGPU style constant in format */
2884
- glInternalFormat;
2885
- /** Whether the internal format is compressed */
2886
- compressed;
2887
- // data;
2888
- // inherited props
2889
- // dimension: ...
2890
- // format: GLTextureTarget;
2891
- // width: number = undefined;
2892
- // height: number = undefined;
2893
- // depth: number = undefined;
2894
- // state
2895
- /** Texture binding slot */
2896
- textureUnit = 0;
2897
- /** For automatically updating video */
2898
- _video = null;
2899
- constructor(device, props) {
2900
- props = import_core8.Texture._fixProps(props);
2901
- super(device, { ...import_core8.Texture.defaultProps, ...props, data: void 0 });
2902
- this.device = device;
2903
- this.gl = this.device.gl;
2904
- this.glTarget = getWebGLTextureTarget(this.props.dimension);
2905
- const format = getTextureFormatWebGL(this.props.format);
2906
- this.glInternalFormat = format.internalFormat;
2907
- this.glFormat = format.format;
2908
- this.glType = format.type;
2909
- this.compressed = format.compressed;
2910
- if (typeof HTMLVideoElement !== "undefined" && props.data instanceof HTMLVideoElement && // @ts-expect-error
2911
- props.data.readyState < HTMLVideoElement.HAVE_METADATA) {
2912
- const video = props.data;
2913
- this._video = null;
2914
- video.addEventListener("loadeddata", () => this.initialize(props));
2915
- }
2916
- this.initialize({ ...this.props, data: props.data });
2917
- Object.seal(this);
2918
- }
2919
- /**
2920
- * Initialize texture with supplied props
2921
- */
2922
- // eslint-disable-next-line max-statements
2923
- initialize(props = {}) {
2924
- this.handle = this.props.handle || this.gl.createTexture();
2925
- this.device.setSpectorMetadata(this.handle, { ...this.props, data: typeof this.props.data });
2926
- const data = props.data;
2927
- let { width, height } = props;
2928
- if (!width || !height) {
2929
- const textureSize = import_core8.Texture.getTextureDataSize(data);
2930
- width = textureSize?.width || 1;
2931
- height = textureSize?.height || 1;
2932
- }
2933
- this.width = width;
2934
- this.height = height;
2935
- this.depth = props.depth;
2936
- this.setSampler(props.sampler);
2937
- this.view = new WEBGLTextureView(this.device, { ...this.props, texture: this });
2938
- this.bind();
2939
- if (!this.props.data) {
2940
- initializeTextureStorage(this.gl, this.mipLevels, this);
2941
- }
2942
- if (props.data) {
2943
- switch (props.dimension) {
2944
- case "1d":
2945
- this.setTexture1DData(props.data);
2946
- break;
2947
- case "2d":
2948
- this.setTexture2DData(props.data);
2949
- break;
2950
- case "3d":
2951
- this.setTexture3DData(props.data);
2952
- break;
2953
- case "cube":
2954
- this.setTextureCubeData(props.data);
2955
- break;
2956
- case "2d-array":
2957
- this.setTextureArrayData(props.data);
2958
- break;
2959
- case "cube-array":
2960
- this.setTextureCubeArrayData(props.data);
2961
- break;
2962
- default:
2963
- throw new Error(props.dimension);
2964
- }
2965
- }
2966
- this.mipmaps = Boolean(props.mipmaps);
2967
- if (this.mipmaps) {
2968
- this.generateMipmap();
2969
- }
2970
- }
2971
- /*
2972
- initializeCube(props?: TextureProps): void {
2973
- const {mipmaps = true} = props; // , parameters = {} as Record<GL, any>} = props;
2974
-
2975
- // Store props for accessors
2976
- // this.props = props;
2977
-
2978
- // @ts-expect-error
2979
- this.setCubeMapData(props).then(() => {
2980
- // TODO - should genMipmap() be called on the cubemap or on the faces?
2981
- // TODO - without generateMipmap() cube textures do not work at all!!! Why?
2982
- if (mipmaps) {
2983
- this.generateMipmap(props);
2984
- }
2985
-
2986
- this.setSampler(props.sampler);
2987
-
2988
- // v8 compatibility?
2989
- // const {parameters = {} as Record<GL, any>} = props;
2990
- // this._setSamplerParameters(parameters);
2991
- });
2992
- }
2993
- */
2994
- destroy() {
2995
- if (this.handle) {
2996
- this.gl.deleteTexture(this.handle);
2997
- this.removeStats();
2998
- this.trackDeallocatedMemory("Texture");
2999
- this.destroyed = true;
3000
- }
3001
- }
3002
- toString() {
3003
- return `Texture(${this.id},${this.width}x${this.height})`;
3004
- }
3005
- createView(props) {
3006
- return new WEBGLTextureView(this.device, { ...props, texture: this });
3007
- }
3008
- setSampler(sampler = {}) {
3009
- let samplerProps;
3010
- if (sampler instanceof WEBGLSampler) {
3011
- this.sampler = sampler;
3012
- samplerProps = sampler.props;
3013
- } else {
3014
- this.sampler = new WEBGLSampler(this.device, sampler);
3015
- samplerProps = sampler;
3016
- }
3017
- const parameters = convertSamplerParametersToWebGL(samplerProps);
3018
- this._setSamplerParameters(parameters);
2761
+ getCompilationInfoSync() {
2762
+ const log12 = this.device.gl.getShaderInfoLog(this.handle);
2763
+ return log12 ? parseShaderCompilerLog(log12) : [];
3019
2764
  }
3020
- /** Update external texture (video frame or canvas) */
3021
- update() {
3022
- import_core8.log.warn("Texture.update() not implemented");
2765
+ getTranslatedSource() {
2766
+ const extensions = this.device.getExtension("WEBGL_debug_shaders");
2767
+ const ext = extensions.WEBGL_debug_shaders;
2768
+ return ext?.getTranslatedShaderSource(this.handle) || null;
3023
2769
  }
3024
- // Call to regenerate mipmaps after modifying texture(s)
3025
- generateMipmap(params = {}) {
3026
- if (!this.props.data) {
2770
+ // PRIVATE METHODS
2771
+ /** Compile a shader and get compilation status */
2772
+ async _compile(source) {
2773
+ const addGLSLVersion = (source2) => source2.startsWith("#version ") ? source2 : `#version 300 es
2774
+ ${source2}`;
2775
+ source = addGLSLVersion(source);
2776
+ const { gl } = this.device;
2777
+ gl.shaderSource(this.handle, source);
2778
+ gl.compileShader(this.handle);
2779
+ if (import_core9.log.level === 0) {
2780
+ this.compilationStatus = "pending";
3027
2781
  return;
3028
2782
  }
3029
- this.mipmaps = true;
3030
- this.gl.bindTexture(this.glTarget, this.handle);
3031
- withGLParameters(this.gl, params, () => {
3032
- this.gl.generateMipmap(this.glTarget);
3033
- });
3034
- this.gl.bindTexture(this.glTarget, null);
3035
- }
3036
- // Image Data Setters
3037
- copyExternalImage(options) {
3038
- const size = import_core8.Texture.getExternalImageSize(options.image);
3039
- const opts = { ...import_core8.Texture.defaultCopyExternalImageOptions, ...size, ...options };
3040
- const { image, depth, mipLevel, x, y, z } = opts;
3041
- let { width, height } = opts;
3042
- const { dimension, glTarget, glFormat, glInternalFormat, glType } = this;
3043
- width = Math.min(width, size.width - x);
3044
- height = Math.min(height, size.height - y);
3045
- if (options.sourceX || options.sourceY) {
3046
- throw new Error(
3047
- "WebGL does not yet support sourceX/sourceY in copyExternalImage; requires copyTexSubImage2D from a framebuffer"
3048
- );
3049
- }
3050
- copyExternalImageToMipLevel(this.device.gl, this.handle, image, {
3051
- dimension,
3052
- mipLevel,
3053
- x,
3054
- y,
3055
- z,
3056
- width,
3057
- height,
3058
- depth,
3059
- glFormat,
3060
- glInternalFormat,
3061
- glType,
3062
- glTarget
3063
- });
3064
- return { width: opts.width, height: opts.height };
3065
- }
3066
- setTexture1DData(data) {
3067
- throw new Error("setTexture1DData not supported in WebGL.");
3068
- }
3069
- /** Set a simple texture */
3070
- setTexture2DData(lodData, depth = 0) {
3071
- this.bind();
3072
- const lodArray = normalizeTextureData(lodData, this);
3073
- if (lodArray.length > 1 && this.props.mipmaps !== false) {
3074
- import_core8.log.warn(`Texture ${this.id} mipmap and multiple LODs.`)();
3075
- }
3076
- for (let lodLevel = 0; lodLevel < lodArray.length; lodLevel++) {
3077
- const imageData = lodArray[lodLevel];
3078
- this._setMipLevel(depth, lodLevel, imageData);
3079
- }
3080
- this.unbind();
3081
- }
3082
- /**
3083
- * Sets a 3D texture
3084
- * @param data
3085
- */
3086
- setTexture3DData(data) {
3087
- if (this.props.dimension !== "3d") {
3088
- throw new Error(this.id);
3089
- }
3090
- if (ArrayBuffer.isView(data)) {
3091
- this.bind();
3092
- copyCPUDataToMipLevel(this.device.gl, data, this);
3093
- this.unbind();
2783
+ if (!this.device.features.has("compilation-status-async-webgl")) {
2784
+ this._getCompilationStatus();
2785
+ this.debugShader();
2786
+ if (this.compilationStatus === "error") {
2787
+ throw new Error(`GLSL compilation errors in ${this.props.stage} shader ${this.props.id}`);
2788
+ }
2789
+ return;
3094
2790
  }
2791
+ import_core9.log.once(1, "Shader compilation is asynchronous")();
2792
+ await this._waitForCompilationComplete();
2793
+ import_core9.log.info(2, `Shader ${this.id} - async compilation complete: ${this.compilationStatus}`)();
2794
+ this._getCompilationStatus();
2795
+ this.debugShader();
3095
2796
  }
3096
- /**
3097
- * Set a Texture Cube Data
3098
- * @todo - could support TextureCubeArray with depth
3099
- * @param data
3100
- * @param index
3101
- */
3102
- setTextureCubeData(data, depth = 0) {
3103
- if (this.props.dimension !== "cube") {
3104
- throw new Error(this.id);
2797
+ /** Use KHR_parallel_shader_compile extension if available */
2798
+ async _waitForCompilationComplete() {
2799
+ const waitMs = async (ms) => await new Promise((resolve) => setTimeout(resolve, ms));
2800
+ const DELAY_MS = 10;
2801
+ if (!this.device.features.has("compilation-status-async-webgl")) {
2802
+ await waitMs(DELAY_MS);
2803
+ return;
3105
2804
  }
3106
- for (const face of import_core8.Texture.CubeFaces) {
3107
- this.setTextureCubeFaceData(data[face], face);
2805
+ const { gl } = this.device;
2806
+ for (; ; ) {
2807
+ const complete = gl.getShaderParameter(this.handle, 37297 /* COMPLETION_STATUS_KHR */);
2808
+ if (complete) {
2809
+ return;
2810
+ }
2811
+ await waitMs(DELAY_MS);
3108
2812
  }
3109
2813
  }
3110
2814
  /**
3111
- * Sets an entire texture array
3112
- * @param data
2815
+ * Get the shader compilation status
2816
+ * TODO - Load log even when no error reported, to catch warnings?
2817
+ * https://gamedev.stackexchange.com/questions/30429/how-to-detect-glsl-warnings
3113
2818
  */
3114
- setTextureArrayData(data) {
3115
- if (this.props.dimension !== "2d-array") {
3116
- throw new Error(this.id);
3117
- }
3118
- throw new Error("setTextureArrayData not implemented.");
2819
+ _getCompilationStatus() {
2820
+ this.compilationStatus = this.device.gl.getShaderParameter(this.handle, 35713 /* COMPILE_STATUS */) ? "success" : "error";
3119
2821
  }
3120
- /**
3121
- * Sets an entire texture cube array
3122
- * @param data
3123
- */
3124
- setTextureCubeArrayData(data) {
3125
- throw new Error("setTextureCubeArrayData not supported in WebGL2.");
2822
+ };
2823
+
2824
+ // src/adapter/resources/webgl-sampler.ts
2825
+ var import_core11 = __toESM(require_core(), 1);
2826
+
2827
+ // src/adapter/converters/device-parameters.ts
2828
+ var import_core10 = __toESM(require_core(), 1);
2829
+ function withDeviceAndGLParameters(device, parameters, glParameters, func) {
2830
+ if (isObjectEmpty2(parameters)) {
2831
+ return func(device);
3126
2832
  }
3127
- setTextureCubeFaceData(lodData, face, depth = 0) {
3128
- if (Array.isArray(lodData) && lodData.length > 1 && this.props.mipmaps !== false) {
3129
- import_core8.log.warn(`${this.id} has mipmap and multiple LODs.`)();
3130
- }
3131
- const faceDepth = import_core8.Texture.CubeFaces.indexOf(face);
3132
- this.setTexture2DData(lodData, faceDepth);
2833
+ const webglDevice = device;
2834
+ webglDevice.pushState();
2835
+ try {
2836
+ setDeviceParameters(device, parameters);
2837
+ setGLParameters(webglDevice.gl, glParameters);
2838
+ return func(device);
2839
+ } finally {
2840
+ webglDevice.popState();
3133
2841
  }
3134
- // INTERNAL METHODS
3135
- /** @todo update this method to accept LODs */
3136
- setImageDataForFace(options) {
3137
- const {
3138
- face,
3139
- width,
3140
- height,
3141
- pixels,
3142
- data,
3143
- format = 6408 /* RGBA */,
3144
- type = 5121 /* UNSIGNED_BYTE */
3145
- // generateMipmap = false // TODO
3146
- } = options;
3147
- const { gl } = this;
3148
- const imageData = pixels || data;
3149
- this.bind();
3150
- if (imageData instanceof Promise) {
3151
- imageData.then(
3152
- (resolvedImageData) => this.setImageDataForFace(
3153
- Object.assign({}, options, {
3154
- face,
3155
- data: resolvedImageData,
3156
- pixels: resolvedImageData
3157
- })
3158
- )
3159
- );
3160
- } else if (this.width || this.height) {
3161
- gl.texImage2D(face, 0, format, width, height, 0, format, type, imageData);
3162
- } else {
3163
- gl.texImage2D(face, 0, format, format, type, imageData);
3164
- }
2842
+ }
2843
+ function withDeviceParameters(device, parameters, func) {
2844
+ if (isObjectEmpty2(parameters)) {
2845
+ return func(device);
3165
2846
  }
3166
- _getImageDataMap(faceData) {
3167
- for (let i = 0; i < import_core8.Texture.CubeFaces.length; ++i) {
3168
- const faceName = import_core8.Texture.CubeFaces[i];
3169
- if (faceData[faceName]) {
3170
- faceData[34069 /* TEXTURE_CUBE_MAP_POSITIVE_X */ + i] = faceData[faceName];
3171
- delete faceData[faceName];
3172
- }
2847
+ const webglDevice = device;
2848
+ webglDevice.pushState();
2849
+ try {
2850
+ setDeviceParameters(device, parameters);
2851
+ return func(device);
2852
+ } finally {
2853
+ webglDevice.popState();
2854
+ }
2855
+ }
2856
+ function setDeviceParameters(device, parameters) {
2857
+ const webglDevice = device;
2858
+ const { gl } = webglDevice;
2859
+ if (parameters.cullMode) {
2860
+ switch (parameters.cullMode) {
2861
+ case "none":
2862
+ gl.disable(2884 /* CULL_FACE */);
2863
+ break;
2864
+ case "front":
2865
+ gl.enable(2884 /* CULL_FACE */);
2866
+ gl.cullFace(1028 /* FRONT */);
2867
+ break;
2868
+ case "back":
2869
+ gl.enable(2884 /* CULL_FACE */);
2870
+ gl.cullFace(1029 /* BACK */);
2871
+ break;
3173
2872
  }
3174
- return faceData;
3175
2873
  }
3176
- // RESOURCE METHODS
3177
- /**
3178
- * Sets sampler parameters on texture
3179
- */
3180
- _setSamplerParameters(parameters) {
3181
- import_core8.log.log(1, "texture sampler parameters", parameters)();
3182
- this.gl.bindTexture(this.glTarget, this.handle);
3183
- for (const [pname, pvalue] of Object.entries(parameters)) {
3184
- const param = Number(pname);
3185
- const value = pvalue;
3186
- switch (param) {
3187
- case 33082 /* TEXTURE_MIN_LOD */:
3188
- case 33083 /* TEXTURE_MAX_LOD */:
3189
- this.gl.texParameterf(this.glTarget, param, value);
3190
- break;
3191
- case 10241 /* TEXTURE_MIN_FILTER */:
3192
- this.gl.texParameteri(this.glTarget, param, value);
3193
- break;
3194
- case 10242 /* TEXTURE_WRAP_S */:
3195
- case 10243 /* TEXTURE_WRAP_T */:
3196
- this.gl.texParameteri(this.glTarget, param, value);
3197
- break;
3198
- case 34046 /* TEXTURE_MAX_ANISOTROPY_EXT */:
3199
- if (this.device.features.has("texture-filterable-anisotropic-webgl")) {
3200
- this.gl.texParameteri(this.glTarget, param, value);
3201
- }
3202
- break;
3203
- default:
3204
- this.gl.texParameteri(this.glTarget, param, value);
3205
- break;
3206
- }
2874
+ if (parameters.frontFace) {
2875
+ gl.frontFace(
2876
+ map("frontFace", parameters.frontFace, {
2877
+ ccw: 2305 /* CCW */,
2878
+ cw: 2304 /* CW */
2879
+ })
2880
+ );
2881
+ }
2882
+ if (parameters.unclippedDepth) {
2883
+ if (device.features.has("depth-clip-control")) {
2884
+ gl.enable(34383 /* DEPTH_CLAMP_EXT */);
3207
2885
  }
3208
- this.gl.bindTexture(this.glTarget, null);
3209
2886
  }
3210
- // CLASSIC
3211
- /*
3212
- setCubeMapData(options: {
3213
- width: number;
3214
- height: number;
3215
- data: Record<GL, Texture2DData> | Record<TextureCubeFace, Texture2DData>;
3216
- format?: any;
3217
- type?: any;
3218
- /** @deprecated Use .data *
3219
- pixels: any;
3220
- }): void {
3221
- const {gl} = this;
3222
-
3223
- const {width, height, pixels, data, format = GL.RGBA, type = GL.UNSIGNED_BYTE} = options;
3224
-
3225
- // pixel data (imageDataMap) is an Object from Face to Image or Promise.
3226
- // For example:
3227
- // {
3228
- // GL.TEXTURE_CUBE_MAP_POSITIVE_X : Image-or-Promise,
3229
- // GL.TEXTURE_CUBE_MAP_NEGATIVE_X : Image-or-Promise,
3230
- // ... }
3231
- // To provide multiple level-of-details (LODs) this can be Face to Array
3232
- // of Image or Promise, like this
3233
- // {
3234
- // GL.TEXTURE_CUBE_MAP_POSITIVE_X : [Image-or-Promise-LOD-0, Image-or-Promise-LOD-1],
3235
- // GL.TEXTURE_CUBE_MAP_NEGATIVE_X : [Image-or-Promise-LOD-0, Image-or-Promise-LOD-1],
3236
- // ... }
3237
-
3238
- const imageDataMap = this._getImageDataMap(pixels || data);
3239
-
3240
- const resolvedFaces = WEBGLTexture.FACES.map(face => {
3241
- const facePixels = imageDataMap[face];
3242
- return Array.isArray(facePixels) ? facePixels : [facePixels];
3243
- });
3244
- this.bind();
3245
-
3246
- WEBGLTexture.FACES.forEach((face, index) => {
3247
- if (resolvedFaces[index].length > 1 && this.props.mipmaps !== false) {
3248
- // If the user provides multiple LODs, then automatic mipmap
3249
- // generation generateMipmap() should be disabled to avoid overwritting them.
3250
- log.warn(`${this.id} has mipmap and multiple LODs.`)();
2887
+ if (parameters.depthBias !== void 0) {
2888
+ gl.enable(32823 /* POLYGON_OFFSET_FILL */);
2889
+ gl.polygonOffset(parameters.depthBias, parameters.depthBiasSlopeScale || 0);
2890
+ }
2891
+ if (parameters.provokingVertex) {
2892
+ if (device.features.has("provoking-vertex-webgl")) {
2893
+ const extensions = webglDevice.getExtension("WEBGL_provoking_vertex");
2894
+ const ext = extensions.WEBGL_provoking_vertex;
2895
+ const vertex = map(
2896
+ "provokingVertex",
2897
+ parameters.provokingVertex,
2898
+ {
2899
+ first: 36429 /* FIRST_VERTEX_CONVENTION_WEBGL */,
2900
+ last: 36430 /* LAST_VERTEX_CONVENTION_WEBGL */
3251
2901
  }
3252
- resolvedFaces[index].forEach((image, lodLevel) => {
3253
- // TODO: adjust width & height for LOD!
3254
- if (width && height) {
3255
- gl.texImage2D(face, lodLevel, format, width, height, 0 /* border*, format, type, image);
3256
- } else {
3257
- gl.texImage2D(face, lodLevel, format, format, type, image);
3258
- }
2902
+ );
2903
+ ext?.provokingVertexWEBGL(vertex);
2904
+ }
2905
+ }
2906
+ if (parameters.polygonMode || parameters.polygonOffsetLine) {
2907
+ if (device.features.has("polygon-mode-webgl")) {
2908
+ if (parameters.polygonMode) {
2909
+ const extensions = webglDevice.getExtension("WEBGL_polygon_mode");
2910
+ const ext = extensions.WEBGL_polygon_mode;
2911
+ const mode = map("polygonMode", parameters.polygonMode, {
2912
+ fill: 6914 /* FILL_WEBGL */,
2913
+ line: 6913 /* LINE_WEBGL */
3259
2914
  });
3260
- });
3261
-
3262
- this.unbind();
2915
+ ext?.polygonModeWEBGL(1028 /* FRONT */, mode);
2916
+ ext?.polygonModeWEBGL(1029 /* BACK */, mode);
2917
+ }
2918
+ if (parameters.polygonOffsetLine) {
2919
+ gl.enable(10754 /* POLYGON_OFFSET_LINE_WEBGL */);
2920
+ }
3263
2921
  }
3264
- */
3265
- // INTERNAL SETTERS
3266
- /**
3267
- * Copy a region of data from a CPU memory buffer into this texture.
3268
- * @todo - GLUnpackParameters parameters
3269
- */
3270
- _setMipLevel(depth, mipLevel, textureData, glTarget = this.glTarget) {
3271
- if (import_core8.Texture.isExternalImage(textureData)) {
3272
- copyExternalImageToMipLevel(this.device.gl, this.handle, textureData, {
3273
- ...this,
3274
- depth,
3275
- mipLevel,
3276
- glTarget
3277
- });
3278
- return;
2922
+ }
2923
+ if (device.features.has("shader-clip-cull-distance-webgl")) {
2924
+ if (parameters.clipDistance0) {
2925
+ gl.enable(12288 /* CLIP_DISTANCE0_WEBGL */);
3279
2926
  }
3280
- if (import_core8.Texture.isTextureLevelData(textureData)) {
3281
- copyCPUDataToMipLevel(this.device.gl, textureData.data, {
3282
- ...this,
3283
- depth,
3284
- mipLevel,
3285
- glTarget
3286
- });
3287
- return;
2927
+ if (parameters.clipDistance1) {
2928
+ gl.enable(12289 /* CLIP_DISTANCE1_WEBGL */);
2929
+ }
2930
+ if (parameters.clipDistance2) {
2931
+ gl.enable(12290 /* CLIP_DISTANCE2_WEBGL */);
2932
+ }
2933
+ if (parameters.clipDistance3) {
2934
+ gl.enable(12291 /* CLIP_DISTANCE3_WEBGL */);
2935
+ }
2936
+ if (parameters.clipDistance4) {
2937
+ gl.enable(12292 /* CLIP_DISTANCE4_WEBGL */);
2938
+ }
2939
+ if (parameters.clipDistance5) {
2940
+ gl.enable(12293 /* CLIP_DISTANCE5_WEBGL */);
2941
+ }
2942
+ if (parameters.clipDistance6) {
2943
+ gl.enable(12294 /* CLIP_DISTANCE6_WEBGL */);
2944
+ }
2945
+ if (parameters.clipDistance7) {
2946
+ gl.enable(12295 /* CLIP_DISTANCE7_WEBGL */);
3288
2947
  }
3289
- throw new Error("Texture: invalid image data");
3290
2948
  }
3291
- // HELPERS
3292
- getActiveUnit() {
3293
- return this.gl.getParameter(34016 /* ACTIVE_TEXTURE */) - 33984 /* TEXTURE0 */;
2949
+ if (parameters.depthWriteEnabled !== void 0) {
2950
+ gl.depthMask(mapBoolean("depthWriteEnabled", parameters.depthWriteEnabled));
3294
2951
  }
3295
- bind(textureUnit) {
3296
- const { gl } = this;
3297
- if (textureUnit !== void 0) {
3298
- this.textureUnit = textureUnit;
3299
- gl.activeTexture(gl.TEXTURE0 + textureUnit);
3300
- }
3301
- gl.bindTexture(this.glTarget, this.handle);
3302
- return textureUnit;
2952
+ if (parameters.depthCompare) {
2953
+ parameters.depthCompare !== "always" ? gl.enable(2929 /* DEPTH_TEST */) : gl.disable(2929 /* DEPTH_TEST */);
2954
+ gl.depthFunc(convertCompareFunction("depthCompare", parameters.depthCompare));
3303
2955
  }
3304
- unbind(textureUnit) {
3305
- const { gl } = this;
3306
- if (textureUnit !== void 0) {
3307
- this.textureUnit = textureUnit;
3308
- gl.activeTexture(gl.TEXTURE0 + textureUnit);
3309
- }
3310
- gl.bindTexture(this.glTarget, null);
3311
- return textureUnit;
2956
+ if (parameters.stencilWriteMask) {
2957
+ const mask = parameters.stencilWriteMask;
2958
+ gl.stencilMaskSeparate(1028 /* FRONT */, mask);
2959
+ gl.stencilMaskSeparate(1029 /* BACK */, mask);
3312
2960
  }
3313
- };
2961
+ if (parameters.stencilReadMask) {
2962
+ import_core10.log.warn("stencilReadMask not supported under WebGL");
2963
+ }
2964
+ if (parameters.stencilCompare) {
2965
+ const mask = parameters.stencilReadMask || 4294967295;
2966
+ const glValue = convertCompareFunction("depthCompare", parameters.stencilCompare);
2967
+ parameters.stencilCompare !== "always" ? gl.enable(2960 /* STENCIL_TEST */) : gl.disable(2960 /* STENCIL_TEST */);
2968
+ gl.stencilFuncSeparate(1028 /* FRONT */, glValue, 0, mask);
2969
+ gl.stencilFuncSeparate(1029 /* BACK */, glValue, 0, mask);
2970
+ }
2971
+ if (parameters.stencilPassOperation && parameters.stencilFailOperation && parameters.stencilDepthFailOperation) {
2972
+ const dppass = convertStencilOperation("stencilPassOperation", parameters.stencilPassOperation);
2973
+ const sfail = convertStencilOperation("stencilFailOperation", parameters.stencilFailOperation);
2974
+ const dpfail = convertStencilOperation(
2975
+ "stencilDepthFailOperation",
2976
+ parameters.stencilDepthFailOperation
2977
+ );
2978
+ gl.stencilOpSeparate(1028 /* FRONT */, sfail, dpfail, dppass);
2979
+ gl.stencilOpSeparate(1029 /* BACK */, sfail, dpfail, dppass);
2980
+ }
2981
+ switch (parameters.blend) {
2982
+ case true:
2983
+ gl.enable(3042 /* BLEND */);
2984
+ break;
2985
+ case false:
2986
+ gl.disable(3042 /* BLEND */);
2987
+ break;
2988
+ default:
2989
+ }
2990
+ if (parameters.blendColorOperation || parameters.blendAlphaOperation) {
2991
+ const colorEquation = convertBlendOperationToEquation(
2992
+ "blendColorOperation",
2993
+ parameters.blendColorOperation || "add"
2994
+ );
2995
+ const alphaEquation = convertBlendOperationToEquation(
2996
+ "blendAlphaOperation",
2997
+ parameters.blendAlphaOperation || "add"
2998
+ );
2999
+ gl.blendEquationSeparate(colorEquation, alphaEquation);
3000
+ const colorSrcFactor = convertBlendFactorToFunction(
3001
+ "blendColorSrcFactor",
3002
+ parameters.blendColorSrcFactor || "one"
3003
+ );
3004
+ const colorDstFactor = convertBlendFactorToFunction(
3005
+ "blendColorDstFactor",
3006
+ parameters.blendColorDstFactor || "zero"
3007
+ );
3008
+ const alphaSrcFactor = convertBlendFactorToFunction(
3009
+ "blendAlphaSrcFactor",
3010
+ parameters.blendAlphaSrcFactor || "one"
3011
+ );
3012
+ const alphaDstFactor = convertBlendFactorToFunction(
3013
+ "blendAlphaDstFactor",
3014
+ parameters.blendAlphaDstFactor || "zero"
3015
+ );
3016
+ gl.blendFuncSeparate(colorSrcFactor, colorDstFactor, alphaSrcFactor, alphaDstFactor);
3017
+ }
3018
+ }
3019
+ function convertCompareFunction(parameter, value) {
3020
+ return map(parameter, value, {
3021
+ never: 512 /* NEVER */,
3022
+ less: 513 /* LESS */,
3023
+ equal: 514 /* EQUAL */,
3024
+ "less-equal": 515 /* LEQUAL */,
3025
+ greater: 516 /* GREATER */,
3026
+ "not-equal": 517 /* NOTEQUAL */,
3027
+ "greater-equal": 518 /* GEQUAL */,
3028
+ always: 519 /* ALWAYS */
3029
+ });
3030
+ }
3031
+ function convertStencilOperation(parameter, value) {
3032
+ return map(parameter, value, {
3033
+ keep: 7680 /* KEEP */,
3034
+ zero: 0 /* ZERO */,
3035
+ replace: 7681 /* REPLACE */,
3036
+ invert: 5386 /* INVERT */,
3037
+ "increment-clamp": 7682 /* INCR */,
3038
+ "decrement-clamp": 7683 /* DECR */,
3039
+ "increment-wrap": 34055 /* INCR_WRAP */,
3040
+ "decrement-wrap": 34056 /* DECR_WRAP */
3041
+ });
3042
+ }
3043
+ function convertBlendOperationToEquation(parameter, value) {
3044
+ return map(parameter, value, {
3045
+ add: 32774 /* FUNC_ADD */,
3046
+ subtract: 32778 /* FUNC_SUBTRACT */,
3047
+ "reverse-subtract": 32779 /* FUNC_REVERSE_SUBTRACT */,
3048
+ min: 32775 /* MIN */,
3049
+ max: 32776 /* MAX */
3050
+ });
3051
+ }
3052
+ function convertBlendFactorToFunction(parameter, value) {
3053
+ return map(parameter, value, {
3054
+ one: 1 /* ONE */,
3055
+ zero: 0 /* ZERO */,
3056
+ "src-color": 768 /* SRC_COLOR */,
3057
+ "one-minus-src-color": 769 /* ONE_MINUS_SRC_COLOR */,
3058
+ "dst-color": 774 /* DST_COLOR */,
3059
+ "one-minus-dst-color": 775 /* ONE_MINUS_DST_COLOR */,
3060
+ "src-alpha": 770 /* SRC_ALPHA */,
3061
+ "one-minus-src-alpha": 771 /* ONE_MINUS_SRC_ALPHA */,
3062
+ "dst-alpha": 772 /* DST_ALPHA */,
3063
+ "one-minus-dst-alpha": 773 /* ONE_MINUS_DST_ALPHA */,
3064
+ "src-alpha-saturated": 776 /* SRC_ALPHA_SATURATE */,
3065
+ "constant-color": 32769 /* CONSTANT_COLOR */,
3066
+ "one-minus-constant-color": 32770 /* ONE_MINUS_CONSTANT_COLOR */,
3067
+ "constant-alpha": 32771 /* CONSTANT_ALPHA */,
3068
+ "one-minus-constant-alpha": 32772 /* ONE_MINUS_CONSTANT_ALPHA */
3069
+ });
3070
+ }
3071
+ function message(parameter, value) {
3072
+ return `Illegal parameter ${value} for ${parameter}`;
3073
+ }
3074
+ function map(parameter, value, valueMap) {
3075
+ if (!(value in valueMap)) {
3076
+ throw new Error(message(parameter, value));
3077
+ }
3078
+ return valueMap[value];
3079
+ }
3080
+ function mapBoolean(parameter, value) {
3081
+ return value;
3082
+ }
3083
+ function isObjectEmpty2(obj) {
3084
+ let isEmpty = true;
3085
+ for (const key in obj) {
3086
+ isEmpty = false;
3087
+ break;
3088
+ }
3089
+ return isEmpty;
3090
+ }
3314
3091
 
3315
- // src/adapter/resources/webgl-framebuffer.ts
3316
- var WEBGLFramebuffer = class extends import_core9.Framebuffer {
3317
- device;
3318
- gl;
3319
- handle;
3320
- colorAttachments = [];
3321
- depthStencilAttachment = null;
3322
- constructor(device, props) {
3323
- super(device, props);
3324
- const isDefaultFramebuffer = props.handle === null;
3325
- this.device = device;
3326
- this.gl = device.gl;
3327
- this.handle = this.props.handle || isDefaultFramebuffer ? this.props.handle : this.gl.createFramebuffer();
3328
- if (!isDefaultFramebuffer) {
3329
- device.setSpectorMetadata(this.handle, { id: this.props.id, props: this.props });
3330
- this.autoCreateAttachmentTextures();
3331
- this.updateAttachments();
3332
- }
3092
+ // src/adapter/converters/sampler-parameters.ts
3093
+ function convertSamplerParametersToWebGL(props) {
3094
+ const params = {};
3095
+ if (props.addressModeU) {
3096
+ params[10242 /* TEXTURE_WRAP_S */] = convertAddressMode(props.addressModeU);
3333
3097
  }
3334
- /** destroys any auto created resources etc. */
3335
- destroy() {
3336
- super.destroy();
3337
- if (!this.destroyed && this.handle !== null) {
3338
- this.gl.deleteFramebuffer(this.handle);
3339
- }
3098
+ if (props.addressModeV) {
3099
+ params[10243 /* TEXTURE_WRAP_T */] = convertAddressMode(props.addressModeV);
3100
+ }
3101
+ if (props.addressModeW) {
3102
+ params[32882 /* TEXTURE_WRAP_R */] = convertAddressMode(props.addressModeW);
3103
+ }
3104
+ if (props.magFilter) {
3105
+ params[10240 /* TEXTURE_MAG_FILTER */] = convertMaxFilterMode(props.magFilter);
3106
+ }
3107
+ if (props.minFilter || props.mipmapFilter) {
3108
+ params[10241 /* TEXTURE_MIN_FILTER */] = convertMinFilterMode(
3109
+ props.minFilter || "linear",
3110
+ props.mipmapFilter
3111
+ );
3112
+ }
3113
+ if (props.lodMinClamp !== void 0) {
3114
+ params[33082 /* TEXTURE_MIN_LOD */] = props.lodMinClamp;
3115
+ }
3116
+ if (props.lodMaxClamp !== void 0) {
3117
+ params[33083 /* TEXTURE_MAX_LOD */] = props.lodMaxClamp;
3118
+ }
3119
+ if (props.type === "comparison-sampler") {
3120
+ params[34892 /* TEXTURE_COMPARE_MODE */] = 34894 /* COMPARE_REF_TO_TEXTURE */;
3340
3121
  }
3341
- updateAttachments() {
3342
- const prevHandle = this.gl.bindFramebuffer(
3343
- 36160 /* FRAMEBUFFER */,
3344
- this.handle
3345
- );
3346
- for (let i = 0; i < this.colorAttachments.length; ++i) {
3347
- const attachment = this.colorAttachments[i];
3348
- if (attachment) {
3349
- const attachmentPoint = 36064 /* COLOR_ATTACHMENT0 */ + i;
3350
- this._attachTextureView(attachmentPoint, attachment);
3351
- }
3352
- }
3353
- if (this.depthStencilAttachment) {
3354
- const attachmentPoint = getDepthStencilAttachmentWebGL(
3355
- this.depthStencilAttachment.props.format
3356
- );
3357
- this._attachTextureView(attachmentPoint, this.depthStencilAttachment);
3358
- }
3359
- if (this.props.check !== false) {
3360
- const status = this.gl.checkFramebufferStatus(36160 /* FRAMEBUFFER */);
3361
- if (status !== 36053 /* FRAMEBUFFER_COMPLETE */) {
3362
- throw new Error(`Framebuffer ${_getFrameBufferStatus(status)}`);
3363
- }
3364
- }
3365
- this.gl.bindFramebuffer(36160 /* FRAMEBUFFER */, prevHandle);
3122
+ if (props.compare) {
3123
+ params[34893 /* TEXTURE_COMPARE_FUNC */] = convertCompareFunction("compare", props.compare);
3366
3124
  }
3367
- // PRIVATE
3368
- /** In WebGL we must use renderbuffers for depth/stencil attachments (unless we have extensions) */
3369
- createDepthStencilTexture(format) {
3370
- return new WEBGLTexture(this.device, {
3371
- id: `${this.id}-depth-stencil`,
3372
- format,
3373
- width: this.width,
3374
- height: this.height,
3375
- mipmaps: false
3376
- });
3125
+ if (props.maxAnisotropy) {
3126
+ params[34046 /* TEXTURE_MAX_ANISOTROPY_EXT */] = props.maxAnisotropy;
3377
3127
  }
3378
- /**
3379
- * @param attachment
3380
- * @param texture
3381
- * @param layer = 0 - index into WEBGLTextureArray and Texture3D or face for `TextureCubeMap`
3382
- * @param level = 0 - mipmapLevel
3383
- */
3384
- _attachTextureView(attachment, textureView) {
3385
- const { gl } = this.device;
3386
- const { texture } = textureView;
3387
- const level = textureView.props.baseMipLevel;
3388
- const layer = textureView.props.baseArrayLayer;
3389
- gl.bindTexture(texture.glTarget, texture.handle);
3390
- switch (texture.glTarget) {
3391
- case 35866 /* TEXTURE_2D_ARRAY */:
3392
- case 32879 /* TEXTURE_3D */:
3393
- gl.framebufferTextureLayer(36160 /* FRAMEBUFFER */, attachment, texture.handle, level, layer);
3394
- break;
3395
- case 34067 /* TEXTURE_CUBE_MAP */:
3396
- const face = mapIndexToCubeMapFace(layer);
3397
- gl.framebufferTexture2D(36160 /* FRAMEBUFFER */, attachment, face, texture.handle, level);
3398
- break;
3399
- case 3553 /* TEXTURE_2D */:
3400
- gl.framebufferTexture2D(36160 /* FRAMEBUFFER */, attachment, 3553 /* TEXTURE_2D */, texture.handle, level);
3401
- break;
3402
- default:
3403
- throw new Error("Illegal texture type");
3404
- }
3405
- gl.bindTexture(texture.glTarget, null);
3128
+ return params;
3129
+ }
3130
+ function convertAddressMode(addressMode) {
3131
+ switch (addressMode) {
3132
+ case "clamp-to-edge":
3133
+ return 33071 /* CLAMP_TO_EDGE */;
3134
+ case "repeat":
3135
+ return 10497 /* REPEAT */;
3136
+ case "mirror-repeat":
3137
+ return 33648 /* MIRRORED_REPEAT */;
3406
3138
  }
3407
- };
3408
- function mapIndexToCubeMapFace(layer) {
3409
- return layer < 34069 /* TEXTURE_CUBE_MAP_POSITIVE_X */ ? layer + 34069 /* TEXTURE_CUBE_MAP_POSITIVE_X */ : layer;
3410
3139
  }
3411
- function _getFrameBufferStatus(status) {
3412
- switch (status) {
3413
- case 36053 /* FRAMEBUFFER_COMPLETE */:
3414
- return "success";
3415
- case 36054 /* FRAMEBUFFER_INCOMPLETE_ATTACHMENT */:
3416
- return "Mismatched attachments";
3417
- case 36055 /* FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT */:
3418
- return "No attachments";
3419
- case 36057 /* FRAMEBUFFER_INCOMPLETE_DIMENSIONS */:
3420
- return "Height/width mismatch";
3421
- case 36061 /* FRAMEBUFFER_UNSUPPORTED */:
3422
- return "Unsupported or split attachments";
3423
- case 36182 /* FRAMEBUFFER_INCOMPLETE_MULTISAMPLE */:
3424
- return "Samples mismatch";
3425
- default:
3426
- return `${status}`;
3140
+ function convertMaxFilterMode(maxFilter) {
3141
+ switch (maxFilter) {
3142
+ case "nearest":
3143
+ return 9728 /* NEAREST */;
3144
+ case "linear":
3145
+ return 9729 /* LINEAR */;
3146
+ }
3147
+ }
3148
+ function convertMinFilterMode(minFilter, mipmapFilter) {
3149
+ if (!mipmapFilter) {
3150
+ return convertMaxFilterMode(minFilter);
3151
+ }
3152
+ switch (minFilter) {
3153
+ case "nearest":
3154
+ return mipmapFilter === "nearest" ? 9984 /* NEAREST_MIPMAP_NEAREST */ : 9986 /* NEAREST_MIPMAP_LINEAR */;
3155
+ case "linear":
3156
+ return mipmapFilter === "nearest" ? 9985 /* LINEAR_MIPMAP_NEAREST */ : 9987 /* LINEAR_MIPMAP_LINEAR */;
3427
3157
  }
3428
3158
  }
3429
3159
 
3430
- // src/adapter/webgl-canvas-context.ts
3431
- var WebGLCanvasContext = class extends import_core10.CanvasContext {
3160
+ // src/adapter/resources/webgl-sampler.ts
3161
+ var WEBGLSampler = class extends import_core11.Sampler {
3432
3162
  device;
3433
- format = "rgba8unorm";
3434
- depthStencilFormat = "depth24plus";
3435
- presentationSize;
3436
- _framebuffer = null;
3163
+ handle;
3164
+ parameters;
3437
3165
  constructor(device, props) {
3438
- super(props);
3166
+ super(device, props);
3439
3167
  this.device = device;
3440
- this.presentationSize = [-1, -1];
3441
- this._setAutoCreatedCanvasId(`${this.device.id}-canvas`);
3442
- this.update();
3443
- }
3444
- getCurrentFramebuffer() {
3445
- this.update();
3446
- this._framebuffer = this._framebuffer || new WEBGLFramebuffer(this.device, { handle: null });
3447
- return this._framebuffer;
3168
+ this.parameters = convertSamplerParametersToWebGL(props);
3169
+ this.handle = this.handle || this.device.gl.createSampler();
3170
+ this._setSamplerParameters(this.parameters);
3448
3171
  }
3449
- /** Resizes and updates render targets if necessary */
3450
- update() {
3451
- const size = this.getPixelSize();
3452
- const sizeChanged = size[0] !== this.presentationSize[0] || size[1] !== this.presentationSize[1];
3453
- if (sizeChanged) {
3454
- this.presentationSize = size;
3455
- this.resize();
3172
+ destroy() {
3173
+ if (this.handle) {
3174
+ this.device.gl.deleteSampler(this.handle);
3175
+ this.handle = void 0;
3456
3176
  }
3457
3177
  }
3458
- /**
3459
- * Resize the canvas' drawing buffer.
3460
- *
3461
- * Can match the canvas CSS size, and optionally also consider devicePixelRatio
3462
- * Can be called every frame
3463
- *
3464
- * Regardless of size, the drawing buffer will always be scaled to the viewport, but
3465
- * for best visual results, usually set to either:
3466
- * canvas CSS width x canvas CSS height
3467
- * canvas CSS width * devicePixelRatio x canvas CSS height * devicePixelRatio
3468
- * See http://webgl2fundamentals.org/webgl/lessons/webgl-resizing-the-canvas.html
3469
- */
3470
- resize(options) {
3471
- if (!this.device.gl)
3472
- return;
3473
- if (this.canvas) {
3474
- const devicePixelRatio = this.getDevicePixelRatio(options?.useDevicePixels);
3475
- this.setDevicePixelRatio(devicePixelRatio, options);
3476
- return;
3477
- }
3178
+ toString() {
3179
+ return `Sampler(${this.id},${JSON.stringify(this.props)})`;
3478
3180
  }
3479
- commit() {
3181
+ /** Set sampler parameters on the sampler */
3182
+ _setSamplerParameters(parameters) {
3183
+ for (const [pname, value] of Object.entries(parameters)) {
3184
+ const param = Number(pname);
3185
+ switch (param) {
3186
+ case 33082 /* TEXTURE_MIN_LOD */:
3187
+ case 33083 /* TEXTURE_MAX_LOD */:
3188
+ this.device.gl.samplerParameterf(this.handle, param, value);
3189
+ break;
3190
+ default:
3191
+ this.device.gl.samplerParameteri(this.handle, param, value);
3192
+ break;
3193
+ }
3194
+ }
3480
3195
  }
3481
3196
  };
3482
3197
 
3483
- // src/context/debug/spector.ts
3484
- var import_core11 = __toESM(require_core(), 1);
3198
+ // src/adapter/resources/webgl-texture.ts
3199
+ var import_core14 = __toESM(require_core(), 1);
3485
3200
 
3486
- // src/utils/load-script.ts
3487
- async function loadScript(scriptUrl, scriptId) {
3488
- const head = document.getElementsByTagName("head")[0];
3489
- if (!head) {
3490
- throw new Error("loadScript");
3491
- }
3492
- const script = document.createElement("script");
3493
- script.setAttribute("type", "text/javascript");
3494
- script.setAttribute("src", scriptUrl);
3495
- if (scriptId) {
3496
- script.id = scriptId;
3201
+ // src/context/state-tracker/with-parameters.ts
3202
+ function withGLParameters(gl, parameters, func) {
3203
+ if (isObjectEmpty3(parameters)) {
3204
+ return func(gl);
3497
3205
  }
3498
- return new Promise((resolve, reject) => {
3499
- script.onload = resolve;
3500
- script.onerror = (error) => reject(new Error(`Unable to load script '${scriptUrl}': ${error}`));
3501
- head.appendChild(script);
3502
- });
3503
- }
3504
-
3505
- // src/context/debug/spector.ts
3506
- var LOG_LEVEL = 1;
3507
- var spector = null;
3508
- var initialized = false;
3509
- var DEFAULT_SPECTOR_PROPS = {
3510
- debugWithSpectorJS: import_core11.log.get("spector") || import_core11.log.get("spectorjs"),
3511
- // https://github.com/BabylonJS/Spector.js#basic-usage
3512
- // https://forum.babylonjs.com/t/spectorcdn-is-temporarily-off/48241
3513
- // spectorUrl: 'https://spectorcdn.babylonjs.com/spector.bundle.js';
3514
- spectorUrl: "https://cdn.jsdelivr.net/npm/spectorjs@0.9.30/dist/spector.bundle.js",
3515
- gl: void 0
3516
- };
3517
- async function loadSpectorJS(props) {
3518
- if (!globalThis.SPECTOR) {
3206
+ const { nocatch = true } = parameters;
3207
+ const webglState = WebGLStateTracker.get(gl);
3208
+ webglState.push();
3209
+ setGLParameters(gl, parameters);
3210
+ let value;
3211
+ if (nocatch) {
3212
+ value = func(gl);
3213
+ webglState.pop();
3214
+ } else {
3519
3215
  try {
3520
- await loadScript(props.spectorUrl || DEFAULT_SPECTOR_PROPS.spectorUrl);
3521
- } catch (error) {
3522
- import_core11.log.warn(String(error));
3523
- }
3524
- }
3525
- }
3526
- function initializeSpectorJS(props) {
3527
- props = { ...DEFAULT_SPECTOR_PROPS, ...props };
3528
- if (!props.debugWithSpectorJS) {
3529
- return null;
3530
- }
3531
- if (!spector && globalThis.SPECTOR && !globalThis.luma?.spector) {
3532
- import_core11.log.probe(LOG_LEVEL, "SPECTOR found and initialized. Start with `luma.spector.displayUI()`")();
3533
- const { Spector } = globalThis.SPECTOR;
3534
- spector = new Spector();
3535
- if (globalThis.luma) {
3536
- globalThis.luma.spector = spector;
3537
- }
3538
- }
3539
- if (!spector) {
3540
- return null;
3541
- }
3542
- if (!initialized) {
3543
- initialized = true;
3544
- spector.spyCanvases();
3545
- spector?.onCaptureStarted.add(
3546
- (capture) => import_core11.log.info("Spector capture started:", capture)()
3547
- );
3548
- spector?.onCapture.add((capture) => {
3549
- import_core11.log.info("Spector capture complete:", capture)();
3550
- spector?.getResultUI();
3551
- spector?.resultView.display();
3552
- spector?.resultView.addCapture(capture);
3553
- });
3216
+ value = func(gl);
3217
+ } finally {
3218
+ webglState.pop();
3219
+ }
3554
3220
  }
3555
- if (props.gl) {
3556
- const gl = props.gl;
3557
- const device = gl.device;
3558
- spector?.startCapture(props.gl, 500);
3559
- gl.device = device;
3560
- new Promise((resolve) => setTimeout(resolve, 2e3)).then((_) => {
3561
- import_core11.log.info("Spector capture stopped after 2 seconds")();
3562
- spector?.stopCapture();
3563
- });
3221
+ return value;
3222
+ }
3223
+ function isObjectEmpty3(object) {
3224
+ for (const key in object) {
3225
+ return false;
3564
3226
  }
3565
- return spector;
3227
+ return true;
3566
3228
  }
3567
3229
 
3568
- // src/context/debug/webgl-developer-tools.ts
3230
+ // src/adapter/resources/webgl-texture-view.ts
3569
3231
  var import_core12 = __toESM(require_core(), 1);
3232
+ var WEBGLTextureView = class extends import_core12.TextureView {
3233
+ device;
3234
+ gl;
3235
+ handle;
3236
+ // Does not have a WebGL representation
3237
+ texture;
3238
+ constructor(device, props) {
3239
+ super(device, { ...import_core12.Texture.defaultProps, ...props });
3240
+ this.device = device;
3241
+ this.gl = this.device.gl;
3242
+ this.handle = null;
3243
+ this.texture = props.texture;
3244
+ }
3245
+ };
3570
3246
 
3571
- // ../../node_modules/@probe.gl/env/dist/lib/globals.js
3572
- var document_ = globalThis.document || {};
3573
- var process_ = globalThis.process || {};
3574
- var console_ = globalThis.console;
3575
- var navigator_ = globalThis.navigator || {};
3247
+ // src/adapter/helpers/webgl-texture-utils.ts
3248
+ var import_core13 = __toESM(require_core(), 1);
3576
3249
 
3577
- // ../../node_modules/@probe.gl/env/dist/lib/is-electron.js
3578
- function isElectron(mockUserAgent) {
3579
- if (typeof window !== "undefined" && window.process?.type === "renderer") {
3580
- return true;
3581
- }
3582
- if (typeof process !== "undefined" && Boolean(process.versions?.["electron"])) {
3583
- return true;
3250
+ // src/adapter/helpers/typed-array-utils.ts
3251
+ var ERR_TYPE_DEDUCTION = "Failed to deduce GL constant from typed array";
3252
+ function getGLTypeFromTypedArray(arrayOrType) {
3253
+ const type = ArrayBuffer.isView(arrayOrType) ? arrayOrType.constructor : arrayOrType;
3254
+ switch (type) {
3255
+ case Float32Array:
3256
+ return 5126 /* FLOAT */;
3257
+ case Uint16Array:
3258
+ return 5123 /* UNSIGNED_SHORT */;
3259
+ case Uint32Array:
3260
+ return 5125 /* UNSIGNED_INT */;
3261
+ case Uint8Array:
3262
+ return 5121 /* UNSIGNED_BYTE */;
3263
+ case Uint8ClampedArray:
3264
+ return 5121 /* UNSIGNED_BYTE */;
3265
+ case Int8Array:
3266
+ return 5120 /* BYTE */;
3267
+ case Int16Array:
3268
+ return 5122 /* SHORT */;
3269
+ case Int32Array:
3270
+ return 5124 /* INT */;
3271
+ default:
3272
+ throw new Error(ERR_TYPE_DEDUCTION);
3584
3273
  }
3585
- const realUserAgent = typeof navigator !== "undefined" && navigator.userAgent;
3586
- const userAgent = mockUserAgent || realUserAgent;
3587
- return Boolean(userAgent && userAgent.indexOf("Electron") >= 0);
3588
3274
  }
3589
-
3590
- // ../../node_modules/@probe.gl/env/dist/lib/is-browser.js
3591
- function isBrowser() {
3592
- const isNode = (
3593
- // @ts-expect-error
3594
- typeof process === "object" && String(process) === "[object process]" && !process?.browser
3595
- );
3596
- return !isNode || isElectron();
3275
+ function getTypedArrayFromGLType(glType, options) {
3276
+ const { clamped = true } = options || {};
3277
+ switch (glType) {
3278
+ case 5126 /* FLOAT */:
3279
+ return Float32Array;
3280
+ case 5123 /* UNSIGNED_SHORT */:
3281
+ case 33635 /* UNSIGNED_SHORT_5_6_5 */:
3282
+ case 32819 /* UNSIGNED_SHORT_4_4_4_4 */:
3283
+ case 32820 /* UNSIGNED_SHORT_5_5_5_1 */:
3284
+ return Uint16Array;
3285
+ case 5125 /* UNSIGNED_INT */:
3286
+ return Uint32Array;
3287
+ case 5121 /* UNSIGNED_BYTE */:
3288
+ return clamped ? Uint8ClampedArray : Uint8Array;
3289
+ case 5120 /* BYTE */:
3290
+ return Int8Array;
3291
+ case 5122 /* SHORT */:
3292
+ return Int16Array;
3293
+ case 5124 /* INT */:
3294
+ return Int32Array;
3295
+ default:
3296
+ throw new Error("Failed to deduce typed array type from GL constant");
3297
+ }
3597
3298
  }
3598
3299
 
3599
- // ../../node_modules/@probe.gl/env/dist/lib/get-browser.js
3600
- function getBrowser(mockUserAgent) {
3601
- if (!mockUserAgent && !isBrowser()) {
3602
- return "Node";
3300
+ // src/adapter/helpers/format-utils.ts
3301
+ function glFormatToComponents(format) {
3302
+ switch (format) {
3303
+ case 6406 /* ALPHA */:
3304
+ case 33326 /* R32F */:
3305
+ case 6403 /* RED */:
3306
+ return 1;
3307
+ case 33328 /* RG32F */:
3308
+ case 33319 /* RG */:
3309
+ return 2;
3310
+ case 6407 /* RGB */:
3311
+ case 34837 /* RGB32F */:
3312
+ return 3;
3313
+ case 6408 /* RGBA */:
3314
+ case 34836 /* RGBA32F */:
3315
+ return 4;
3316
+ default:
3317
+ return 0;
3603
3318
  }
3604
- if (isElectron(mockUserAgent)) {
3605
- return "Electron";
3319
+ }
3320
+ function glTypeToBytes(type) {
3321
+ switch (type) {
3322
+ case 5121 /* UNSIGNED_BYTE */:
3323
+ return 1;
3324
+ case 33635 /* UNSIGNED_SHORT_5_6_5 */:
3325
+ case 32819 /* UNSIGNED_SHORT_4_4_4_4 */:
3326
+ case 32820 /* UNSIGNED_SHORT_5_5_5_1 */:
3327
+ return 2;
3328
+ case 5126 /* FLOAT */:
3329
+ return 4;
3330
+ default:
3331
+ return 0;
3606
3332
  }
3607
- const userAgent = mockUserAgent || navigator_.userAgent || "";
3608
- if (userAgent.indexOf("Edge") > -1) {
3609
- return "Edge";
3333
+ }
3334
+
3335
+ // src/adapter/helpers/webgl-texture-utils.ts
3336
+ function initializeTextureStorage(gl, levels, options) {
3337
+ const { dimension, width, height, depth = 0 } = options;
3338
+ const { glInternalFormat } = options;
3339
+ const glTarget = options.glTarget;
3340
+ switch (dimension) {
3341
+ case "2d-array":
3342
+ case "3d":
3343
+ gl.texStorage3D(glTarget, levels, glInternalFormat, width, height, depth);
3344
+ break;
3345
+ default:
3346
+ gl.texStorage2D(glTarget, levels, glInternalFormat, width, height);
3610
3347
  }
3611
- if (globalThis.chrome) {
3612
- return "Chrome";
3348
+ }
3349
+ function copyExternalImageToMipLevel(gl, handle, image, options) {
3350
+ const { width, height } = options;
3351
+ const { dimension, depth = 0, mipLevel = 0 } = options;
3352
+ const { x = 0, y = 0, z = 0 } = options;
3353
+ const { glFormat, glType } = options;
3354
+ const glTarget = getWebGLCubeFaceTarget(options.glTarget, dimension, depth);
3355
+ switch (dimension) {
3356
+ case "2d-array":
3357
+ case "3d":
3358
+ gl.bindTexture(glTarget, handle);
3359
+ gl.texSubImage3D(glTarget, mipLevel, x, y, z, width, height, depth, glFormat, glType, image);
3360
+ gl.bindTexture(glTarget, null);
3361
+ break;
3362
+ case "2d":
3363
+ case "cube":
3364
+ gl.bindTexture(glTarget, handle);
3365
+ gl.texSubImage2D(glTarget, mipLevel, x, y, width, height, glFormat, glType, image);
3366
+ gl.bindTexture(glTarget, null);
3367
+ break;
3368
+ default:
3369
+ throw new Error(dimension);
3613
3370
  }
3614
- if (globalThis.safari) {
3615
- return "Safari";
3371
+ }
3372
+ function copyCPUDataToMipLevel(gl, typedArray, options) {
3373
+ const { dimension, width, height, depth = 0, mipLevel = 0, byteOffset = 0 } = options;
3374
+ const { x = 0, y = 0, z = 0 } = options;
3375
+ const { glFormat, glType, compressed } = options;
3376
+ const glTarget = getWebGLCubeFaceTarget(options.glTarget, dimension, depth);
3377
+ switch (dimension) {
3378
+ case "2d-array":
3379
+ case "3d":
3380
+ if (compressed) {
3381
+ gl.compressedTexSubImage3D(glTarget, mipLevel, x, y, z, width, height, depth, glFormat, typedArray, byteOffset);
3382
+ } else {
3383
+ gl.texSubImage3D(glTarget, mipLevel, x, y, z, width, height, depth, glFormat, glType, typedArray, byteOffset);
3384
+ }
3385
+ break;
3386
+ case "2d":
3387
+ case "cube":
3388
+ if (compressed) {
3389
+ gl.compressedTexSubImage2D(glTarget, mipLevel, x, y, width, height, glFormat, typedArray, byteOffset);
3390
+ } else {
3391
+ gl.texSubImage2D(glTarget, mipLevel, x, y, width, height, glFormat, glType, typedArray, byteOffset);
3392
+ }
3393
+ break;
3394
+ default:
3395
+ throw new Error(dimension);
3616
3396
  }
3617
- if (globalThis.mozInnerScreenX) {
3618
- return "Firefox";
3397
+ }
3398
+ function getWebGLTextureTarget(dimension) {
3399
+ switch (dimension) {
3400
+ case "1d":
3401
+ break;
3402
+ case "2d":
3403
+ return 3553 /* TEXTURE_2D */;
3404
+ case "3d":
3405
+ return 32879 /* TEXTURE_3D */;
3406
+ case "cube":
3407
+ return 34067 /* TEXTURE_CUBE_MAP */;
3408
+ case "2d-array":
3409
+ return 35866 /* TEXTURE_2D_ARRAY */;
3410
+ case "cube-array":
3411
+ break;
3619
3412
  }
3620
- return "Unknown";
3413
+ throw new Error(dimension);
3621
3414
  }
3622
-
3623
- // src/context/debug/webgl-developer-tools.ts
3624
- var WEBGL_DEBUG_CDN_URL = "https://unpkg.com/webgl-debug@2.0.1/index.js";
3625
- function getWebGLContextData(gl) {
3626
- gl.luma = gl.luma || {};
3627
- return gl.luma;
3415
+ function getWebGLCubeFaceTarget(glTarget, dimension, level) {
3416
+ return dimension === "cube" ? 34069 /* TEXTURE_CUBE_MAP_POSITIVE_X */ + level : glTarget;
3628
3417
  }
3629
- async function loadWebGLDeveloperTools() {
3630
- if (isBrowser() && !globalThis.WebGLDebugUtils) {
3631
- globalThis.global = globalThis.global || globalThis;
3632
- globalThis.global.module = {};
3633
- await loadScript(WEBGL_DEBUG_CDN_URL);
3418
+ function readPixelsToArray(source, options) {
3419
+ const {
3420
+ sourceX = 0,
3421
+ sourceY = 0,
3422
+ sourceAttachment = 36064 /* COLOR_ATTACHMENT0 */
3423
+ // TODO - support gl.readBuffer
3424
+ } = options || {};
3425
+ let {
3426
+ target = null,
3427
+ // following parameters are auto deduced if not provided
3428
+ sourceWidth,
3429
+ sourceHeight,
3430
+ sourceDepth,
3431
+ sourceFormat,
3432
+ sourceType
3433
+ } = options || {};
3434
+ const { framebuffer, deleteFramebuffer } = getFramebuffer(source);
3435
+ const { gl, handle } = framebuffer;
3436
+ const attachment = sourceAttachment - 36064 /* COLOR_ATTACHMENT0 */;
3437
+ sourceWidth ||= framebuffer.width;
3438
+ sourceHeight ||= framebuffer.height;
3439
+ sourceDepth = framebuffer.colorAttachments[attachment]?.texture?.depth || 1;
3440
+ sourceFormat ||= framebuffer.colorAttachments[attachment]?.texture?.glFormat || 6408 /* RGBA */;
3441
+ sourceType ||= framebuffer.colorAttachments[attachment]?.texture?.glType || 5121 /* UNSIGNED_BYTE */;
3442
+ target = getPixelArray(target, sourceType, sourceFormat, sourceWidth, sourceHeight, sourceDepth);
3443
+ sourceType = sourceType || getGLTypeFromTypedArray(target);
3444
+ const prevHandle = gl.bindFramebuffer(36160 /* FRAMEBUFFER */, handle);
3445
+ gl.readPixels(sourceX, sourceY, sourceWidth, sourceHeight, sourceFormat, sourceType, target);
3446
+ gl.bindFramebuffer(36160 /* FRAMEBUFFER */, prevHandle || null);
3447
+ if (deleteFramebuffer) {
3448
+ framebuffer.destroy();
3634
3449
  }
3450
+ return target;
3635
3451
  }
3636
- function makeDebugContext(gl, props = {}) {
3637
- return props.debug ? getDebugContext(gl, props) : getRealContext(gl);
3638
- }
3639
- function getRealContext(gl) {
3640
- const data = getWebGLContextData(gl);
3641
- return data.realContext ? data.realContext : gl;
3642
- }
3643
- function getDebugContext(gl, props) {
3644
- if (!globalThis.WebGLDebugUtils) {
3645
- import_core12.log.warn("webgl-debug not loaded")();
3646
- return gl;
3647
- }
3648
- const data = getWebGLContextData(gl);
3649
- if (data.debugContext) {
3650
- return data.debugContext;
3452
+ function readPixelsToBuffer(source, options) {
3453
+ const {
3454
+ target,
3455
+ sourceX = 0,
3456
+ sourceY = 0,
3457
+ sourceFormat = 6408 /* RGBA */,
3458
+ targetByteOffset = 0
3459
+ } = options || {};
3460
+ let { sourceWidth, sourceHeight, sourceType } = options || {};
3461
+ const { framebuffer, deleteFramebuffer } = getFramebuffer(source);
3462
+ sourceWidth = sourceWidth || framebuffer.width;
3463
+ sourceHeight = sourceHeight || framebuffer.height;
3464
+ const webglFramebuffer = framebuffer;
3465
+ sourceType = sourceType || 5121 /* UNSIGNED_BYTE */;
3466
+ let webglBufferTarget = target;
3467
+ if (!webglBufferTarget) {
3468
+ const components = glFormatToComponents(sourceFormat);
3469
+ const byteCount = glTypeToBytes(sourceType);
3470
+ const byteLength = targetByteOffset + sourceWidth * sourceHeight * components * byteCount;
3471
+ webglBufferTarget = webglFramebuffer.device.createBuffer({ byteLength });
3651
3472
  }
3652
- globalThis.WebGLDebugUtils.init({ ...GLEnum, ...gl });
3653
- const glDebug = globalThis.WebGLDebugUtils.makeDebugContext(
3654
- gl,
3655
- onGLError.bind(null, props),
3656
- onValidateGLFunc.bind(null, props)
3657
- );
3658
- for (const key in GLEnum) {
3659
- if (!(key in glDebug) && typeof GLEnum[key] === "number") {
3660
- glDebug[key] = GLEnum[key];
3661
- }
3473
+ const commandEncoder = source.device.createCommandEncoder();
3474
+ commandEncoder.copyTextureToBuffer({
3475
+ source,
3476
+ width: sourceWidth,
3477
+ height: sourceHeight,
3478
+ origin: [sourceX, sourceY],
3479
+ destination: webglBufferTarget,
3480
+ byteOffset: targetByteOffset
3481
+ });
3482
+ commandEncoder.destroy();
3483
+ if (deleteFramebuffer) {
3484
+ framebuffer.destroy();
3662
3485
  }
3663
- class WebGLDebugContext {
3486
+ return webglBufferTarget;
3487
+ }
3488
+ function getFramebuffer(source) {
3489
+ if (!(source instanceof import_core13.Framebuffer)) {
3490
+ return { framebuffer: toFramebuffer(source), deleteFramebuffer: true };
3664
3491
  }
3665
- Object.setPrototypeOf(glDebug, Object.getPrototypeOf(gl));
3666
- Object.setPrototypeOf(WebGLDebugContext, glDebug);
3667
- const debugContext = Object.create(WebGLDebugContext);
3668
- data.realContext = gl;
3669
- data.debugContext = debugContext;
3670
- debugContext.debug = true;
3671
- return debugContext;
3492
+ return { framebuffer: source, deleteFramebuffer: false };
3672
3493
  }
3673
- function getFunctionString(functionName, functionArgs) {
3674
- functionArgs = Array.from(functionArgs).map((arg) => arg === void 0 ? "undefined" : arg);
3675
- let args = globalThis.WebGLDebugUtils.glFunctionArgsToString(functionName, functionArgs);
3676
- args = `${args.slice(0, 100)}${args.length > 100 ? "..." : ""}`;
3677
- return `gl.${functionName}(${args})`;
3494
+ function toFramebuffer(texture, props) {
3495
+ const { device, width, height, id } = texture;
3496
+ const framebuffer = device.createFramebuffer({
3497
+ ...props,
3498
+ id: `framebuffer-for-${id}`,
3499
+ width,
3500
+ height,
3501
+ colorAttachments: [texture]
3502
+ });
3503
+ return framebuffer;
3678
3504
  }
3679
- function onGLError(props, err, functionName, args) {
3680
- args = Array.from(args).map((arg) => arg === void 0 ? "undefined" : arg);
3681
- const errorMessage = globalThis.WebGLDebugUtils.glEnumToString(err);
3682
- const functionArgs = globalThis.WebGLDebugUtils.glFunctionArgsToString(functionName, args);
3683
- const message2 = `${errorMessage} in gl.${functionName}(${functionArgs})`;
3684
- import_core12.log.error(message2)();
3685
- debugger;
3686
- if (props.throwOnError) {
3687
- throw new Error(message2);
3505
+ function getPixelArray(pixelArray, type, format, width, height, depth) {
3506
+ if (pixelArray) {
3507
+ return pixelArray;
3688
3508
  }
3509
+ type = type || 5121 /* UNSIGNED_BYTE */;
3510
+ const ArrayType = getTypedArrayFromGLType(type, { clamped: false });
3511
+ const components = glFormatToComponents(format);
3512
+ return new ArrayType(width * height * components);
3689
3513
  }
3690
- function onValidateGLFunc(props, functionName, functionArgs) {
3691
- let functionString = "";
3692
- if (import_core12.log.level >= 1) {
3693
- functionString = getFunctionString(functionName, functionArgs);
3694
- import_core12.log.log(1, functionString)();
3695
- }
3696
- if (props.break && props.break.length > 0) {
3697
- functionString = functionString || getFunctionString(functionName, functionArgs);
3698
- const isBreakpoint = props.break.every(
3699
- (breakOn) => functionString.indexOf(breakOn) !== -1
3700
- );
3701
- if (isBreakpoint) {
3702
- debugger;
3703
- }
3704
- }
3705
- for (const arg of functionArgs) {
3706
- if (arg === void 0) {
3707
- functionString = functionString || getFunctionString(functionName, functionArgs);
3708
- if (props.throwOnError) {
3709
- throw new Error(`Undefined argument: ${functionString}`);
3710
- } else {
3711
- import_core12.log.error(`Undefined argument: ${functionString}`)();
3712
- debugger;
3514
+
3515
+ // src/adapter/resources/webgl-texture.ts
3516
+ function normalizeTextureData(data, options) {
3517
+ let lodArray;
3518
+ if (ArrayBuffer.isView(data)) {
3519
+ lodArray = [
3520
+ {
3521
+ // ts-expect-error does data really need to be Uint8ClampedArray?
3522
+ data,
3523
+ width: options.width,
3524
+ height: options.height
3525
+ // depth: options.depth
3713
3526
  }
3714
- }
3527
+ ];
3528
+ } else if (!Array.isArray(data)) {
3529
+ lodArray = [data];
3530
+ } else {
3531
+ lodArray = data;
3715
3532
  }
3533
+ return lodArray;
3716
3534
  }
3717
-
3718
- // src/utils/uid.ts
3719
- var uidCounters = {};
3720
- function uid(id = "id") {
3721
- uidCounters[id] = uidCounters[id] || 1;
3722
- const count = uidCounters[id]++;
3723
- return `${id}-${count}`;
3724
- }
3725
-
3726
- // src/adapter/resources/webgl-buffer.ts
3727
- var import_core13 = __toESM(require_core(), 1);
3728
- var WEBGLBuffer = class extends import_core13.Buffer {
3535
+ var WEBGLTexture = class extends import_core14.Texture {
3536
+ MAX_ATTRIBUTES;
3729
3537
  device;
3730
3538
  gl;
3731
3539
  handle;
3732
- /** Target in OpenGL defines the type of buffer */
3540
+ sampler = void 0;
3541
+ // TODO - currently unused in WebGL. Create dummy sampler?
3542
+ view = void 0;
3543
+ // TODO - currently unused in WebGL. Create dummy view?
3544
+ mipmaps = false;
3545
+ /**
3546
+ * @note `target` cannot be modified by bind:
3547
+ * textures are special because when you first bind them to a target,
3548
+ * When you first bind a texture as a GL_TEXTURE_2D, you are saying that this texture is a 2D texture.
3549
+ * And it will always be a 2D texture; this state cannot be changed ever.
3550
+ * A texture that was first bound as a GL_TEXTURE_2D, must always be bound as a GL_TEXTURE_2D;
3551
+ * attempting to bind it as GL_TEXTURE_3D will give rise to a run-time error
3552
+ */
3733
3553
  glTarget;
3734
- /** Usage is a hint on how frequently the buffer will be updates */
3735
- glUsage;
3736
- /** Index type is needed when issuing draw calls, so we pre-compute it */
3737
- glIndexType = 5123 /* UNSIGNED_SHORT */;
3738
- /** Number of bytes allocated on the GPU for this buffer */
3739
- byteLength;
3740
- /** Number of bytes used */
3741
- bytesUsed;
3742
- constructor(device, props = {}) {
3743
- super(device, props);
3554
+ // Texture type
3555
+ /** The WebGL format - essentially channel structure */
3556
+ glFormat;
3557
+ /** The WebGL data format - the type of each channel */
3558
+ glType;
3559
+ /** The WebGL constant corresponding to the WebGPU style constant in format */
3560
+ glInternalFormat;
3561
+ /** Whether the internal format is compressed */
3562
+ compressed;
3563
+ // data;
3564
+ // inherited props
3565
+ // dimension: ...
3566
+ // format: GLTextureTarget;
3567
+ // width: number = undefined;
3568
+ // height: number = undefined;
3569
+ // depth: number = undefined;
3570
+ // state
3571
+ /** Texture binding slot */
3572
+ textureUnit = 0;
3573
+ /** For automatically updating video */
3574
+ _video = null;
3575
+ constructor(device, props) {
3576
+ props = import_core14.Texture._fixProps(props);
3577
+ super(device, { ...import_core14.Texture.defaultProps, ...props, data: void 0 });
3744
3578
  this.device = device;
3745
3579
  this.gl = this.device.gl;
3746
- const handle = typeof props === "object" ? props.handle : void 0;
3747
- this.handle = handle || this.gl.createBuffer();
3748
- device.setSpectorMetadata(this.handle, { ...this.props, data: typeof this.props.data });
3749
- this.glTarget = getWebGLTarget(this.props.usage);
3750
- this.glUsage = getWebGLUsage(this.props.usage);
3751
- this.glIndexType = this.props.indexType === "uint32" ? 5125 /* UNSIGNED_INT */ : 5123 /* UNSIGNED_SHORT */;
3752
- if (props.data) {
3753
- this._initWithData(props.data, props.byteOffset, props.byteLength);
3754
- } else {
3755
- this._initWithByteLength(props.byteLength || 0);
3580
+ this.glTarget = getWebGLTextureTarget(this.props.dimension);
3581
+ const format = getTextureFormatWebGL(this.props.format);
3582
+ this.glInternalFormat = format.internalFormat;
3583
+ this.glFormat = format.format;
3584
+ this.glType = format.type;
3585
+ this.compressed = format.compressed;
3586
+ if (typeof HTMLVideoElement !== "undefined" && props.data instanceof HTMLVideoElement && // @ts-expect-error
3587
+ props.data.readyState < HTMLVideoElement.HAVE_METADATA) {
3588
+ const video = props.data;
3589
+ this._video = null;
3590
+ video.addEventListener("loadeddata", () => this.initialize(props));
3591
+ }
3592
+ this.initialize({ ...this.props, data: props.data });
3593
+ Object.seal(this);
3594
+ }
3595
+ /**
3596
+ * Initialize texture with supplied props
3597
+ */
3598
+ // eslint-disable-next-line max-statements
3599
+ initialize(props = {}) {
3600
+ this.handle = this.props.handle || this.gl.createTexture();
3601
+ this.device.setSpectorMetadata(this.handle, { ...this.props, data: typeof this.props.data });
3602
+ const data = props.data;
3603
+ let { width, height } = props;
3604
+ if (!width || !height) {
3605
+ const textureSize = import_core14.Texture.getTextureDataSize(data);
3606
+ width = textureSize?.width || 1;
3607
+ height = textureSize?.height || 1;
3608
+ }
3609
+ this.width = width;
3610
+ this.height = height;
3611
+ this.depth = props.depth;
3612
+ this.setSampler(props.sampler);
3613
+ this.view = new WEBGLTextureView(this.device, { ...this.props, texture: this });
3614
+ this.bind();
3615
+ if (!this.props.data) {
3616
+ initializeTextureStorage(this.gl, this.mipLevels, this);
3756
3617
  }
3757
- }
3758
- // PRIVATE METHODS
3759
- /** Allocate a new buffer and initialize to contents of typed array */
3760
- _initWithData(data, byteOffset = 0, byteLength = data.byteLength + byteOffset) {
3761
- const glTarget = this.glTarget;
3762
- this.gl.bindBuffer(glTarget, this.handle);
3763
- this.gl.bufferData(glTarget, byteLength, this.glUsage);
3764
- this.gl.bufferSubData(glTarget, byteOffset, data);
3765
- this.gl.bindBuffer(glTarget, null);
3766
- this.bytesUsed = byteLength;
3767
- this.byteLength = byteLength;
3768
- this._setDebugData(data, byteOffset, byteLength);
3769
- this.trackAllocatedMemory(byteLength);
3770
- }
3771
- // Allocate a GPU buffer of specified size.
3772
- _initWithByteLength(byteLength) {
3773
- let data = byteLength;
3774
- if (byteLength === 0) {
3775
- data = new Float32Array(0);
3618
+ if (props.data) {
3619
+ switch (props.dimension) {
3620
+ case "1d":
3621
+ this.setTexture1DData(props.data);
3622
+ break;
3623
+ case "2d":
3624
+ this.setTexture2DData(props.data);
3625
+ break;
3626
+ case "3d":
3627
+ this.setTexture3DData(props.data);
3628
+ break;
3629
+ case "cube":
3630
+ this.setTextureCubeData(props.data);
3631
+ break;
3632
+ case "2d-array":
3633
+ this.setTextureArrayData(props.data);
3634
+ break;
3635
+ case "cube-array":
3636
+ this.setTextureCubeArrayData(props.data);
3637
+ break;
3638
+ default:
3639
+ throw new Error(props.dimension);
3640
+ }
3641
+ }
3642
+ this.mipmaps = Boolean(props.mipmaps);
3643
+ if (this.mipmaps) {
3644
+ this.generateMipmap();
3776
3645
  }
3777
- const glTarget = this.glTarget;
3778
- this.gl.bindBuffer(glTarget, this.handle);
3779
- this.gl.bufferData(glTarget, data, this.glUsage);
3780
- this.gl.bindBuffer(glTarget, null);
3781
- this.bytesUsed = byteLength;
3782
- this.byteLength = byteLength;
3783
- this._setDebugData(null, 0, byteLength);
3784
- this.trackAllocatedMemory(byteLength);
3785
- return this;
3786
3646
  }
3647
+ /*
3648
+ initializeCube(props?: TextureProps): void {
3649
+ const {mipmaps = true} = props; // , parameters = {} as Record<GL, any>} = props;
3650
+
3651
+ // Store props for accessors
3652
+ // this.props = props;
3653
+
3654
+ // @ts-expect-error
3655
+ this.setCubeMapData(props).then(() => {
3656
+ // TODO - should genMipmap() be called on the cubemap or on the faces?
3657
+ // TODO - without generateMipmap() cube textures do not work at all!!! Why?
3658
+ if (mipmaps) {
3659
+ this.generateMipmap(props);
3660
+ }
3661
+
3662
+ this.setSampler(props.sampler);
3663
+
3664
+ // v8 compatibility?
3665
+ // const {parameters = {} as Record<GL, any>} = props;
3666
+ // this._setSamplerParameters(parameters);
3667
+ });
3668
+ }
3669
+ */
3787
3670
  destroy() {
3788
- if (!this.destroyed && this.handle) {
3671
+ if (this.handle) {
3672
+ this.gl.deleteTexture(this.handle);
3789
3673
  this.removeStats();
3790
- this.trackDeallocatedMemory();
3791
- this.gl.deleteBuffer(this.handle);
3674
+ this.trackDeallocatedMemory("Texture");
3792
3675
  this.destroyed = true;
3793
- this.handle = null;
3794
- }
3795
- }
3796
- write(data, byteOffset = 0) {
3797
- const srcOffset = 0;
3798
- const byteLength = void 0;
3799
- const glTarget = 36663 /* COPY_WRITE_BUFFER */;
3800
- this.gl.bindBuffer(glTarget, this.handle);
3801
- if (srcOffset !== 0 || byteLength !== void 0) {
3802
- this.gl.bufferSubData(glTarget, byteOffset, data, srcOffset, byteLength);
3803
- } else {
3804
- this.gl.bufferSubData(glTarget, byteOffset, data);
3805
3676
  }
3806
- this.gl.bindBuffer(glTarget, null);
3807
- this._setDebugData(data, byteOffset, data.byteLength);
3808
- }
3809
- /** Asynchronously read data from the buffer */
3810
- async readAsync(byteOffset = 0, byteLength) {
3811
- return this.readSyncWebGL(byteOffset, byteLength);
3812
3677
  }
3813
- /** Synchronously read data from the buffer. WebGL only. */
3814
- readSyncWebGL(byteOffset = 0, byteLength) {
3815
- byteLength = byteLength ?? this.byteLength - byteOffset;
3816
- const data = new Uint8Array(byteLength);
3817
- const dstOffset = 0;
3818
- this.gl.bindBuffer(36662 /* COPY_READ_BUFFER */, this.handle);
3819
- this.gl.getBufferSubData(36662 /* COPY_READ_BUFFER */, byteOffset, data, dstOffset, byteLength);
3820
- this.gl.bindBuffer(36662 /* COPY_READ_BUFFER */, null);
3821
- this._setDebugData(data, byteOffset, byteLength);
3822
- return data;
3678
+ toString() {
3679
+ return `Texture(${this.id},${this.width}x${this.height})`;
3823
3680
  }
3824
- };
3825
- function getWebGLTarget(usage) {
3826
- if (usage & import_core13.Buffer.INDEX) {
3827
- return 34963 /* ELEMENT_ARRAY_BUFFER */;
3681
+ createView(props) {
3682
+ return new WEBGLTextureView(this.device, { ...props, texture: this });
3828
3683
  }
3829
- if (usage & import_core13.Buffer.VERTEX) {
3830
- return 34962 /* ARRAY_BUFFER */;
3684
+ setSampler(sampler = {}) {
3685
+ let samplerProps;
3686
+ if (sampler instanceof WEBGLSampler) {
3687
+ this.sampler = sampler;
3688
+ samplerProps = sampler.props;
3689
+ } else {
3690
+ this.sampler = new WEBGLSampler(this.device, sampler);
3691
+ samplerProps = sampler;
3692
+ }
3693
+ const parameters = convertSamplerParametersToWebGL(samplerProps);
3694
+ this._setSamplerParameters(parameters);
3831
3695
  }
3832
- if (usage & import_core13.Buffer.UNIFORM) {
3833
- return 35345 /* UNIFORM_BUFFER */;
3696
+ /** Update external texture (video frame or canvas) */
3697
+ update() {
3698
+ import_core14.log.warn("Texture.update() not implemented");
3834
3699
  }
3835
- return 34962 /* ARRAY_BUFFER */;
3836
- }
3837
- function getWebGLUsage(usage) {
3838
- if (usage & import_core13.Buffer.INDEX) {
3839
- return 35044 /* STATIC_DRAW */;
3700
+ // Call to regenerate mipmaps after modifying texture(s)
3701
+ generateMipmap(params = {}) {
3702
+ if (!this.props.data) {
3703
+ return;
3704
+ }
3705
+ this.mipmaps = true;
3706
+ this.gl.bindTexture(this.glTarget, this.handle);
3707
+ withGLParameters(this.gl, params, () => {
3708
+ this.gl.generateMipmap(this.glTarget);
3709
+ });
3710
+ this.gl.bindTexture(this.glTarget, null);
3840
3711
  }
3841
- if (usage & import_core13.Buffer.VERTEX) {
3842
- return 35044 /* STATIC_DRAW */;
3712
+ // Image Data Setters
3713
+ copyExternalImage(options) {
3714
+ const size = import_core14.Texture.getExternalImageSize(options.image);
3715
+ const opts = { ...import_core14.Texture.defaultCopyExternalImageOptions, ...size, ...options };
3716
+ const { image, depth, mipLevel, x, y, z } = opts;
3717
+ let { width, height } = opts;
3718
+ const { dimension, glTarget, glFormat, glInternalFormat, glType } = this;
3719
+ width = Math.min(width, size.width - x);
3720
+ height = Math.min(height, size.height - y);
3721
+ if (options.sourceX || options.sourceY) {
3722
+ throw new Error(
3723
+ "WebGL does not yet support sourceX/sourceY in copyExternalImage; requires copyTexSubImage2D from a framebuffer"
3724
+ );
3725
+ }
3726
+ copyExternalImageToMipLevel(this.device.gl, this.handle, image, {
3727
+ dimension,
3728
+ mipLevel,
3729
+ x,
3730
+ y,
3731
+ z,
3732
+ width,
3733
+ height,
3734
+ depth,
3735
+ glFormat,
3736
+ glInternalFormat,
3737
+ glType,
3738
+ glTarget
3739
+ });
3740
+ return { width: opts.width, height: opts.height };
3843
3741
  }
3844
- if (usage & import_core13.Buffer.UNIFORM) {
3845
- return 35048 /* DYNAMIC_DRAW */;
3742
+ setTexture1DData(data) {
3743
+ throw new Error("setTexture1DData not supported in WebGL.");
3846
3744
  }
3847
- return 35044 /* STATIC_DRAW */;
3848
- }
3849
-
3850
- // src/adapter/resources/webgl-shader.ts
3851
- var import_core14 = __toESM(require_core(), 1);
3852
-
3853
- // src/adapter/helpers/parse-shader-compiler-log.ts
3854
- function parseShaderCompilerLog(errLog) {
3855
- const lines = errLog.split(/\r?\n/);
3856
- const messages = [];
3857
- for (const line of lines) {
3858
- if (line.length <= 1) {
3859
- continue;
3860
- }
3861
- const segments = line.split(":");
3862
- if (segments.length === 2) {
3863
- const [messageType2, message2] = segments;
3864
- messages.push({
3865
- message: message2.trim(),
3866
- type: getMessageType(messageType2),
3867
- lineNum: 0,
3868
- linePos: 0
3869
- });
3870
- continue;
3871
- }
3872
- const [messageType, linePosition, lineNumber, ...rest] = segments;
3873
- let lineNum = parseInt(lineNumber, 10);
3874
- if (isNaN(lineNum)) {
3875
- lineNum = 0;
3745
+ /** Set a simple texture */
3746
+ setTexture2DData(lodData, depth = 0) {
3747
+ this.bind();
3748
+ const lodArray = normalizeTextureData(lodData, this);
3749
+ if (lodArray.length > 1 && this.props.mipmaps !== false) {
3750
+ import_core14.log.warn(`Texture ${this.id} mipmap and multiple LODs.`)();
3876
3751
  }
3877
- let linePos = parseInt(linePosition, 10);
3878
- if (isNaN(linePos)) {
3879
- linePos = 0;
3752
+ for (let lodLevel = 0; lodLevel < lodArray.length; lodLevel++) {
3753
+ const imageData = lodArray[lodLevel];
3754
+ this._setMipLevel(depth, lodLevel, imageData);
3880
3755
  }
3881
- messages.push({
3882
- message: rest.join(":").trim(),
3883
- type: getMessageType(messageType),
3884
- lineNum,
3885
- linePos
3886
- // TODO
3887
- });
3756
+ this.unbind();
3888
3757
  }
3889
- return messages;
3890
- }
3891
- function getMessageType(messageType) {
3892
- const MESSAGE_TYPES = ["warning", "error", "info"];
3893
- const lowerCaseType = messageType.toLowerCase();
3894
- return MESSAGE_TYPES.includes(lowerCaseType) ? lowerCaseType : "info";
3895
- }
3896
-
3897
- // src/adapter/resources/webgl-shader.ts
3898
- var WEBGLShader = class extends import_core14.Shader {
3899
- device;
3900
- handle;
3901
- constructor(device, props) {
3902
- super(device, props);
3903
- this.device = device;
3904
- switch (this.props.stage) {
3905
- case "vertex":
3906
- this.handle = this.props.handle || this.device.gl.createShader(35633 /* VERTEX_SHADER */);
3907
- break;
3908
- case "fragment":
3909
- this.handle = this.props.handle || this.device.gl.createShader(35632 /* FRAGMENT_SHADER */);
3910
- break;
3911
- default:
3912
- throw new Error(this.props.stage);
3758
+ /**
3759
+ * Sets a 3D texture
3760
+ * @param data
3761
+ */
3762
+ setTexture3DData(data) {
3763
+ if (this.props.dimension !== "3d") {
3764
+ throw new Error(this.id);
3765
+ }
3766
+ if (ArrayBuffer.isView(data)) {
3767
+ this.bind();
3768
+ copyCPUDataToMipLevel(this.device.gl, data, this);
3769
+ this.unbind();
3913
3770
  }
3914
- this._compile(this.source);
3915
3771
  }
3916
- destroy() {
3917
- if (this.handle) {
3918
- this.removeStats();
3919
- this.device.gl.deleteShader(this.handle);
3920
- this.destroyed = true;
3772
+ /**
3773
+ * Set a Texture Cube Data
3774
+ * @todo - could support TextureCubeArray with depth
3775
+ * @param data
3776
+ * @param index
3777
+ */
3778
+ setTextureCubeData(data, depth = 0) {
3779
+ if (this.props.dimension !== "cube") {
3780
+ throw new Error(this.id);
3781
+ }
3782
+ for (const face of import_core14.Texture.CubeFaces) {
3783
+ this.setTextureCubeFaceData(data[face], face);
3921
3784
  }
3922
3785
  }
3923
- async getCompilationInfo() {
3924
- await this._waitForCompilationComplete();
3925
- return this.getCompilationInfoSync();
3786
+ /**
3787
+ * Sets an entire texture array
3788
+ * @param data
3789
+ */
3790
+ setTextureArrayData(data) {
3791
+ if (this.props.dimension !== "2d-array") {
3792
+ throw new Error(this.id);
3793
+ }
3794
+ throw new Error("setTextureArrayData not implemented.");
3926
3795
  }
3927
- getCompilationInfoSync() {
3928
- const log12 = this.device.gl.getShaderInfoLog(this.handle);
3929
- return log12 ? parseShaderCompilerLog(log12) : [];
3796
+ /**
3797
+ * Sets an entire texture cube array
3798
+ * @param data
3799
+ */
3800
+ setTextureCubeArrayData(data) {
3801
+ throw new Error("setTextureCubeArrayData not supported in WebGL2.");
3930
3802
  }
3931
- getTranslatedSource() {
3932
- const extensions = this.device.getExtension("WEBGL_debug_shaders");
3933
- const ext = extensions.WEBGL_debug_shaders;
3934
- return ext?.getTranslatedShaderSource(this.handle) || null;
3803
+ setTextureCubeFaceData(lodData, face, depth = 0) {
3804
+ if (Array.isArray(lodData) && lodData.length > 1 && this.props.mipmaps !== false) {
3805
+ import_core14.log.warn(`${this.id} has mipmap and multiple LODs.`)();
3806
+ }
3807
+ const faceDepth = import_core14.Texture.CubeFaces.indexOf(face);
3808
+ this.setTexture2DData(lodData, faceDepth);
3935
3809
  }
3936
- // PRIVATE METHODS
3937
- /** Compile a shader and get compilation status */
3938
- async _compile(source) {
3939
- const addGLSLVersion = (source2) => source2.startsWith("#version ") ? source2 : `#version 300 es
3940
- ${source2}`;
3941
- source = addGLSLVersion(source);
3942
- const { gl } = this.device;
3943
- gl.shaderSource(this.handle, source);
3944
- gl.compileShader(this.handle);
3945
- if (import_core14.log.level === 0) {
3946
- this.compilationStatus = "pending";
3947
- return;
3810
+ // INTERNAL METHODS
3811
+ /** @todo update this method to accept LODs */
3812
+ setImageDataForFace(options) {
3813
+ const {
3814
+ face,
3815
+ width,
3816
+ height,
3817
+ pixels,
3818
+ data,
3819
+ format = 6408 /* RGBA */,
3820
+ type = 5121 /* UNSIGNED_BYTE */
3821
+ // generateMipmap = false // TODO
3822
+ } = options;
3823
+ const { gl } = this;
3824
+ const imageData = pixels || data;
3825
+ this.bind();
3826
+ if (imageData instanceof Promise) {
3827
+ imageData.then(
3828
+ (resolvedImageData) => this.setImageDataForFace(
3829
+ Object.assign({}, options, {
3830
+ face,
3831
+ data: resolvedImageData,
3832
+ pixels: resolvedImageData
3833
+ })
3834
+ )
3835
+ );
3836
+ } else if (this.width || this.height) {
3837
+ gl.texImage2D(face, 0, format, width, height, 0, format, type, imageData);
3838
+ } else {
3839
+ gl.texImage2D(face, 0, format, format, type, imageData);
3948
3840
  }
3949
- if (!this.device.features.has("compilation-status-async-webgl")) {
3950
- this._getCompilationStatus();
3951
- this.debugShader();
3952
- if (this.compilationStatus === "error") {
3953
- throw new Error(`GLSL compilation errors in ${this.props.stage} shader ${this.props.id}`);
3841
+ }
3842
+ _getImageDataMap(faceData) {
3843
+ for (let i = 0; i < import_core14.Texture.CubeFaces.length; ++i) {
3844
+ const faceName = import_core14.Texture.CubeFaces[i];
3845
+ if (faceData[faceName]) {
3846
+ faceData[34069 /* TEXTURE_CUBE_MAP_POSITIVE_X */ + i] = faceData[faceName];
3847
+ delete faceData[faceName];
3954
3848
  }
3955
- return;
3956
3849
  }
3957
- import_core14.log.once(1, "Shader compilation is asynchronous")();
3958
- await this._waitForCompilationComplete();
3959
- import_core14.log.info(2, `Shader ${this.id} - async compilation complete: ${this.compilationStatus}`)();
3960
- this._getCompilationStatus();
3961
- this.debugShader();
3850
+ return faceData;
3962
3851
  }
3963
- /** Use KHR_parallel_shader_compile extension if available */
3964
- async _waitForCompilationComplete() {
3965
- const waitMs = async (ms) => await new Promise((resolve) => setTimeout(resolve, ms));
3966
- const DELAY_MS = 10;
3967
- if (!this.device.features.has("compilation-status-async-webgl")) {
3968
- await waitMs(DELAY_MS);
3969
- return;
3970
- }
3971
- const { gl } = this.device;
3972
- for (; ; ) {
3973
- const complete = gl.getShaderParameter(this.handle, 37297 /* COMPLETION_STATUS_KHR */);
3974
- if (complete) {
3975
- return;
3852
+ // RESOURCE METHODS
3853
+ /**
3854
+ * Sets sampler parameters on texture
3855
+ */
3856
+ _setSamplerParameters(parameters) {
3857
+ import_core14.log.log(1, "texture sampler parameters", parameters)();
3858
+ this.gl.bindTexture(this.glTarget, this.handle);
3859
+ for (const [pname, pvalue] of Object.entries(parameters)) {
3860
+ const param = Number(pname);
3861
+ const value = pvalue;
3862
+ switch (param) {
3863
+ case 33082 /* TEXTURE_MIN_LOD */:
3864
+ case 33083 /* TEXTURE_MAX_LOD */:
3865
+ this.gl.texParameterf(this.glTarget, param, value);
3866
+ break;
3867
+ case 10241 /* TEXTURE_MIN_FILTER */:
3868
+ this.gl.texParameteri(this.glTarget, param, value);
3869
+ break;
3870
+ case 10242 /* TEXTURE_WRAP_S */:
3871
+ case 10243 /* TEXTURE_WRAP_T */:
3872
+ this.gl.texParameteri(this.glTarget, param, value);
3873
+ break;
3874
+ case 34046 /* TEXTURE_MAX_ANISOTROPY_EXT */:
3875
+ if (this.device.features.has("texture-filterable-anisotropic-webgl")) {
3876
+ this.gl.texParameteri(this.glTarget, param, value);
3877
+ }
3878
+ break;
3879
+ default:
3880
+ this.gl.texParameteri(this.glTarget, param, value);
3881
+ break;
3976
3882
  }
3977
- await waitMs(DELAY_MS);
3978
3883
  }
3884
+ this.gl.bindTexture(this.glTarget, null);
3979
3885
  }
3886
+ // CLASSIC
3887
+ /*
3888
+ setCubeMapData(options: {
3889
+ width: number;
3890
+ height: number;
3891
+ data: Record<GL, Texture2DData> | Record<TextureCubeFace, Texture2DData>;
3892
+ format?: any;
3893
+ type?: any;
3894
+ /** @deprecated Use .data *
3895
+ pixels: any;
3896
+ }): void {
3897
+ const {gl} = this;
3898
+
3899
+ const {width, height, pixels, data, format = GL.RGBA, type = GL.UNSIGNED_BYTE} = options;
3900
+
3901
+ // pixel data (imageDataMap) is an Object from Face to Image or Promise.
3902
+ // For example:
3903
+ // {
3904
+ // GL.TEXTURE_CUBE_MAP_POSITIVE_X : Image-or-Promise,
3905
+ // GL.TEXTURE_CUBE_MAP_NEGATIVE_X : Image-or-Promise,
3906
+ // ... }
3907
+ // To provide multiple level-of-details (LODs) this can be Face to Array
3908
+ // of Image or Promise, like this
3909
+ // {
3910
+ // GL.TEXTURE_CUBE_MAP_POSITIVE_X : [Image-or-Promise-LOD-0, Image-or-Promise-LOD-1],
3911
+ // GL.TEXTURE_CUBE_MAP_NEGATIVE_X : [Image-or-Promise-LOD-0, Image-or-Promise-LOD-1],
3912
+ // ... }
3913
+
3914
+ const imageDataMap = this._getImageDataMap(pixels || data);
3915
+
3916
+ const resolvedFaces = WEBGLTexture.FACES.map(face => {
3917
+ const facePixels = imageDataMap[face];
3918
+ return Array.isArray(facePixels) ? facePixels : [facePixels];
3919
+ });
3920
+ this.bind();
3921
+
3922
+ WEBGLTexture.FACES.forEach((face, index) => {
3923
+ if (resolvedFaces[index].length > 1 && this.props.mipmaps !== false) {
3924
+ // If the user provides multiple LODs, then automatic mipmap
3925
+ // generation generateMipmap() should be disabled to avoid overwritting them.
3926
+ log.warn(`${this.id} has mipmap and multiple LODs.`)();
3927
+ }
3928
+ resolvedFaces[index].forEach((image, lodLevel) => {
3929
+ // TODO: adjust width & height for LOD!
3930
+ if (width && height) {
3931
+ gl.texImage2D(face, lodLevel, format, width, height, 0 /* border*, format, type, image);
3932
+ } else {
3933
+ gl.texImage2D(face, lodLevel, format, format, type, image);
3934
+ }
3935
+ });
3936
+ });
3937
+
3938
+ this.unbind();
3939
+ }
3940
+ */
3941
+ // INTERNAL SETTERS
3980
3942
  /**
3981
- * Get the shader compilation status
3982
- * TODO - Load log even when no error reported, to catch warnings?
3983
- * https://gamedev.stackexchange.com/questions/30429/how-to-detect-glsl-warnings
3943
+ * Copy a region of data from a CPU memory buffer into this texture.
3944
+ * @todo - GLUnpackParameters parameters
3984
3945
  */
3985
- _getCompilationStatus() {
3986
- this.compilationStatus = this.device.gl.getShaderParameter(this.handle, 35713 /* COMPILE_STATUS */) ? "success" : "error";
3946
+ _setMipLevel(depth, mipLevel, textureData, glTarget = this.glTarget) {
3947
+ if (import_core14.Texture.isExternalImage(textureData)) {
3948
+ copyExternalImageToMipLevel(this.device.gl, this.handle, textureData, {
3949
+ ...this,
3950
+ depth,
3951
+ mipLevel,
3952
+ glTarget
3953
+ });
3954
+ return;
3955
+ }
3956
+ if (import_core14.Texture.isTextureLevelData(textureData)) {
3957
+ copyCPUDataToMipLevel(this.device.gl, textureData.data, {
3958
+ ...this,
3959
+ depth,
3960
+ mipLevel,
3961
+ glTarget
3962
+ });
3963
+ return;
3964
+ }
3965
+ throw new Error("Texture: invalid image data");
3966
+ }
3967
+ // HELPERS
3968
+ getActiveUnit() {
3969
+ return this.gl.getParameter(34016 /* ACTIVE_TEXTURE */) - 33984 /* TEXTURE0 */;
3970
+ }
3971
+ bind(textureUnit) {
3972
+ const { gl } = this;
3973
+ if (textureUnit !== void 0) {
3974
+ this.textureUnit = textureUnit;
3975
+ gl.activeTexture(gl.TEXTURE0 + textureUnit);
3976
+ }
3977
+ gl.bindTexture(this.glTarget, this.handle);
3978
+ return textureUnit;
3979
+ }
3980
+ unbind(textureUnit) {
3981
+ const { gl } = this;
3982
+ if (textureUnit !== void 0) {
3983
+ this.textureUnit = textureUnit;
3984
+ gl.activeTexture(gl.TEXTURE0 + textureUnit);
3985
+ }
3986
+ gl.bindTexture(this.glTarget, null);
3987
+ return textureUnit;
3987
3988
  }
3988
3989
  };
3989
3990