@itwin/webgl-compatibility 4.0.0-dev.8 → 4.0.0-dev.81

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.
@@ -1,338 +1,339 @@
1
- /*---------------------------------------------------------------------------------------------
2
- * Copyright (c) Bentley Systems, Incorporated. All rights reserved.
3
- * See LICENSE.md in the project root for license terms and full copyright notice.
4
- *--------------------------------------------------------------------------------------------*/
5
- /** @packageDocumentation
6
- * @module Compatibility
7
- */
8
- import { ProcessDetector } from "@itwin/core-bentley";
9
- import { WebGLFeature, WebGLRenderCompatibilityStatus, } from "./RenderCompatibility";
10
- const knownExtensions = [
11
- "WEBGL_draw_buffers",
12
- "OES_element_index_uint",
13
- "OES_texture_float",
14
- "OES_texture_float_linear",
15
- "OES_texture_half_float",
16
- "OES_texture_half_float_linear",
17
- "EXT_texture_filter_anisotropic",
18
- "WEBGL_depth_texture",
19
- "EXT_color_buffer_float",
20
- "EXT_shader_texture_lod",
21
- "EXT_frag_depth",
22
- "ANGLE_instanced_arrays",
23
- "OES_vertex_array_object",
24
- "WEBGL_lose_context",
25
- "EXT_disjoint_timer_query",
26
- "EXT_disjoint_timer_query_webgl2",
27
- "OES_standard_derivatives",
28
- "EXT_float_blend",
29
- ];
30
- /** Describes the type of a render target. Used by Capabilities to represent maximum precision render target available on host system.
31
- * @internal
32
- */
33
- export var RenderType;
34
- (function (RenderType) {
35
- RenderType[RenderType["TextureUnsignedByte"] = 0] = "TextureUnsignedByte";
36
- RenderType[RenderType["TextureHalfFloat"] = 1] = "TextureHalfFloat";
37
- RenderType[RenderType["TextureFloat"] = 2] = "TextureFloat";
38
- })(RenderType || (RenderType = {}));
39
- /**
40
- * Describes the type of a depth buffer. Used by Capabilities to represent maximum depth buffer precision available on host system.
41
- * Note: the commented-out values are unimplemented but left in place for reference, in case desired for future implementation.
42
- * @internal
43
- */
44
- export var DepthType;
45
- (function (DepthType) {
46
- DepthType[DepthType["RenderBufferUnsignedShort16"] = 0] = "RenderBufferUnsignedShort16";
47
- // TextureUnsignedShort16, // core to WebGL2; available to WebGL1 via WEBGL_depth_texture
48
- // TextureUnsignedInt24, // core to WebGL2
49
- DepthType[DepthType["TextureUnsignedInt24Stencil8"] = 1] = "TextureUnsignedInt24Stencil8";
50
- DepthType[DepthType["TextureUnsignedInt32"] = 2] = "TextureUnsignedInt32";
51
- // TextureFloat32, // core to WebGL2
52
- // TextureFloat32Stencil8, // core to WeBGL2
53
- })(DepthType || (DepthType = {}));
54
- const maxTexSizeAllowed = 4096; // many devices and browsers have issues with source textures larger than this
55
- // Regexes to match Intel UHD/HD 620/630 integrated GPUS that suffer from GraphicsDriverBugs.fragDepthDoesNotDisableEarlyZ.
56
- const buggyIntelMatchers = [
57
- // Original unmasked renderer string when workaround we implemented.
58
- /ANGLE \(Intel\(R\) (U)?HD Graphics 6(2|3)0 Direct3D11/,
59
- // New unmasked renderer string circa October 2021.
60
- /ANGLE \(Intel, Intel\(R\) (U)?HD Graphics 6(2|3)0 Direct3D11/,
61
- ];
62
- // Regexes to match Mali GPUs known to suffer from GraphicsDriverBugs.msaaWillHang.
63
- const buggyMaliMatchers = [
64
- /Mali-G71/,
65
- /Mali-G72/,
66
- /Mali-G76/,
67
- ];
68
- // Regexes to match as many Intel integrated GPUs as possible.
69
- // https://en.wikipedia.org/wiki/List_of_Intel_graphics_processing_units
70
- const integratedIntelGpuMatchers = [
71
- /(U)?HD Graphics/,
72
- /Iris/,
73
- ];
74
- function isIntegratedGraphics(args) {
75
- if (args.unmaskedRenderer && args.unmaskedRenderer.includes("Intel") && integratedIntelGpuMatchers.some((x) => x.test(args.unmaskedRenderer)))
76
- return true;
77
- // NB: For now, we do not attempt to detect AMD integrated graphics.
78
- // It appears that AMD integrated graphics are not usually paired with a graphics card so detecting integrated usage there is less important than Intel.
79
- return false;
80
- }
81
- /** Describes the rendering capabilities of the host system.
82
- * @internal
83
- */
84
- export class Capabilities {
85
- constructor() {
86
- this._maxRenderType = RenderType.TextureUnsignedByte;
87
- this._maxDepthType = DepthType.RenderBufferUnsignedShort16;
88
- this._maxTextureSize = 0;
89
- this._maxColorAttachments = 0;
90
- this._maxDrawBuffers = 0;
91
- this._maxFragTextureUnits = 0;
92
- this._maxVertTextureUnits = 0;
93
- this._maxVertAttribs = 0;
94
- this._maxVertUniformVectors = 0;
95
- this._maxVaryingVectors = 0;
96
- this._maxFragUniformVectors = 0;
97
- this._maxAntialiasSamples = 1;
98
- this._supportsCreateImageBitmap = false;
99
- this._maxTexSizeAllow = maxTexSizeAllowed;
100
- this._extensionMap = {}; // Use this map to store actual extension objects retrieved from GL.
101
- this._presentFeatures = []; // List of features the system can support (not necessarily dependent on extensions)
102
- this._isWebGL2 = false;
103
- this._isMobile = false;
104
- this._driverBugs = {};
105
- }
106
- get maxRenderType() { return this._maxRenderType; }
107
- get maxDepthType() { return this._maxDepthType; }
108
- get maxTextureSize() { return this._maxTextureSize; }
109
- get maxTexSizeAllow() { return this._maxTexSizeAllow; }
110
- get supportsCreateImageBitmap() { return this._supportsCreateImageBitmap; }
111
- get maxColorAttachments() { return this._maxColorAttachments; }
112
- get maxDrawBuffers() { return this._maxDrawBuffers; }
113
- get maxFragTextureUnits() { return this._maxFragTextureUnits; }
114
- get maxVertTextureUnits() { return this._maxVertTextureUnits; }
115
- get maxVertAttribs() { return this._maxVertAttribs; }
116
- get maxVertUniformVectors() { return this._maxVertUniformVectors; }
117
- get maxVaryingVectors() { return this._maxVaryingVectors; }
118
- get maxFragUniformVectors() { return this._maxFragUniformVectors; }
119
- get maxAntialiasSamples() { return this._maxAntialiasSamples; }
120
- get isWebGL2() { return this._isWebGL2; }
121
- get driverBugs() { return this._driverBugs; }
122
- /** These getters check for existence of extension objects to determine availability of features. In WebGL2, could just return true for some. */
123
- get supportsNonPowerOf2Textures() { return false; }
124
- get supportsDrawBuffers() { return this._isWebGL2 || this.queryExtensionObject("WEBGL_draw_buffers") !== undefined; }
125
- get supportsInstancing() { return this._isWebGL2 || this.queryExtensionObject("ANGLE_instanced_arrays") !== undefined; }
126
- get supports32BitElementIndex() { return this._isWebGL2 || this.queryExtensionObject("OES_element_index_uint") !== undefined; }
127
- get supportsTextureFloat() { return this._isWebGL2 || this.queryExtensionObject("OES_texture_float") !== undefined; }
128
- get supportsTextureFloatLinear() { return this._isWebGL2 || this.queryExtensionObject("OES_texture_float_linear") !== undefined; }
129
- get supportsTextureHalfFloat() { return this._isWebGL2 || this.queryExtensionObject("OES_texture_half_float") !== undefined; }
130
- get supportsTextureHalfFloatLinear() { return this._isWebGL2 || this.queryExtensionObject("OES_texture_half_float_linear") !== undefined; }
131
- get supportsTextureFilterAnisotropic() { return this.queryExtensionObject("EXT_texture_filter_anisotropic") !== undefined; }
132
- get supportsShaderTextureLOD() { return this._isWebGL2 || this.queryExtensionObject("EXT_shader_texture_lod") !== undefined; }
133
- get supportsVertexArrayObjects() { return this._isWebGL2 || this.queryExtensionObject("OES_vertex_array_object") !== undefined; }
134
- get supportsFragDepth() { return this._isWebGL2 || this.queryExtensionObject("EXT_frag_depth") !== undefined; }
135
- get supportsDisjointTimerQuery() { return (this._isWebGL2 && this.queryExtensionObject("EXT_disjoint_timer_query_webgl2") !== undefined) || this.queryExtensionObject("EXT_disjoint_timer_query") !== undefined; }
136
- get supportsStandardDerivatives() { return this._isWebGL2 || this.queryExtensionObject("OES_standard_derivatives") !== undefined; }
137
- get supportsMRTTransparency() { return this.maxColorAttachments >= 2; }
138
- get supportsMRTPickShaders() { return this.maxColorAttachments >= 3; }
139
- get supportsShadowMaps() {
140
- return this.supportsTextureFloat || this.supportsTextureHalfFloat;
141
- }
142
- get supportsAntiAliasing() { return this._isWebGL2 && this.maxAntialiasSamples > 1; }
143
- get isMobile() { return this._isMobile; }
144
- findExtension(name) {
145
- const ext = this._extensionMap[name];
146
- return null !== ext ? ext : undefined;
147
- }
148
- /** Queries an extension object if available. This is necessary for other parts of the system to access some constants within extensions. */
149
- queryExtensionObject(ext) {
150
- return this.findExtension(ext);
151
- }
152
- get _hasRequiredTextureUnits() { return this.maxFragTextureUnits >= 4 && this.maxVertTextureUnits >= 5; }
153
- /** Return an array containing any features not supported by the system as compared to the input array. */
154
- _findMissingFeatures(featuresToSeek) {
155
- const missingFeatures = [];
156
- for (const featureName of featuresToSeek) {
157
- if (-1 === this._presentFeatures.indexOf(featureName))
158
- missingFeatures.push(featureName);
159
- }
160
- return missingFeatures;
161
- }
162
- /** Populate and return an array containing features that this system supports. */
163
- _gatherFeatures() {
164
- const features = [];
165
- // simply check for presence of various extensions if that gives enough information
166
- if (this._isWebGL2 || this._extensionMap["OES_element_index_uint"] !== undefined)
167
- features.push(WebGLFeature.UintElementIndex);
168
- if (this._isWebGL2 || this._extensionMap["ANGLE_instanced_arrays"] !== undefined)
169
- features.push(WebGLFeature.Instancing);
170
- if (this.supportsMRTTransparency)
171
- features.push(WebGLFeature.MrtTransparency);
172
- if (this.supportsMRTPickShaders)
173
- features.push(WebGLFeature.MrtPick);
174
- if (this.supportsShadowMaps)
175
- features.push(WebGLFeature.ShadowMaps);
176
- if (this._hasRequiredTextureUnits)
177
- features.push(WebGLFeature.MinimalTextureUnits);
178
- if (this.supportsFragDepth)
179
- features.push(WebGLFeature.FragDepth);
180
- if (this.supportsStandardDerivatives)
181
- features.push(WebGLFeature.StandardDerivatives);
182
- if (this.supportsAntiAliasing)
183
- features.push(WebGLFeature.AntiAliasing);
184
- if (DepthType.TextureUnsignedInt24Stencil8 === this._maxDepthType)
185
- features.push(WebGLFeature.DepthTexture);
186
- // check if at least half-float rendering is available based on maximum discovered renderable target
187
- if (RenderType.TextureUnsignedByte !== this._maxRenderType)
188
- features.push(WebGLFeature.FloatRendering);
189
- return features;
190
- }
191
- /** Retrieve compatibility status based on presence of various features. */
192
- _getCompatibilityStatus(missingRequiredFeatures, missingOptionalFeatures) {
193
- let status = WebGLRenderCompatibilityStatus.AllOkay;
194
- if (missingOptionalFeatures.length > 0)
195
- status = WebGLRenderCompatibilityStatus.MissingOptionalFeatures;
196
- if (missingRequiredFeatures.length > 0)
197
- status = WebGLRenderCompatibilityStatus.MissingRequiredFeatures;
198
- return status;
199
- }
200
- /** Initializes the capabilities based on a GL context. Must be called first. */
201
- init(gl, disabledExtensions) {
202
- const gl2 = !(gl instanceof WebGLRenderingContext) ? gl : undefined;
203
- this._isWebGL2 = undefined !== gl2;
204
- this._isMobile = ProcessDetector.isMobileBrowser;
205
- const debugInfo = gl.getExtension("WEBGL_debug_renderer_info");
206
- const unmaskedRenderer = debugInfo !== null ? gl.getParameter(debugInfo.UNMASKED_RENDERER_WEBGL) : undefined;
207
- const unmaskedVendor = debugInfo !== null ? gl.getParameter(debugInfo.UNMASKED_VENDOR_WEBGL) : undefined;
208
- this._driverBugs = {};
209
- if (unmaskedRenderer && buggyIntelMatchers.some((x) => x.test(unmaskedRenderer)))
210
- this._driverBugs.fragDepthDoesNotDisableEarlyZ = true;
211
- if (unmaskedRenderer && buggyMaliMatchers.some((x) => x.test(unmaskedRenderer)))
212
- this._driverBugs.msaaWillHang = true;
213
- this._maxTextureSize = gl.getParameter(gl.MAX_TEXTURE_SIZE);
214
- this._supportsCreateImageBitmap = typeof createImageBitmap === "function" && ProcessDetector.isChromium && !ProcessDetector.isIOSBrowser;
215
- this._maxTexSizeAllow = Math.min(this._maxTextureSize, maxTexSizeAllowed);
216
- this._maxFragTextureUnits = gl.getParameter(gl.MAX_TEXTURE_IMAGE_UNITS);
217
- this._maxVertTextureUnits = gl.getParameter(gl.MAX_VERTEX_TEXTURE_IMAGE_UNITS);
218
- this._maxVertAttribs = gl.getParameter(gl.MAX_VERTEX_ATTRIBS);
219
- this._maxVertUniformVectors = gl.getParameter(gl.MAX_VERTEX_UNIFORM_VECTORS);
220
- this._maxVaryingVectors = gl.getParameter(gl.MAX_VARYING_VECTORS);
221
- this._maxFragUniformVectors = gl.getParameter(gl.MAX_FRAGMENT_UNIFORM_VECTORS);
222
- this._maxAntialiasSamples = this._driverBugs.msaaWillHang ? 1 : (this._isWebGL2 && undefined !== gl2 ? gl.getParameter(gl2.MAX_SAMPLES) : 1);
223
- const extensions = gl.getSupportedExtensions(); // This just retrieves a list of available extensions (not necessarily enabled).
224
- if (extensions) {
225
- for (const extStr of extensions) {
226
- const ext = extStr;
227
- if (-1 === knownExtensions.indexOf(ext))
228
- continue;
229
- else if (undefined !== disabledExtensions && -1 !== disabledExtensions.indexOf(ext))
230
- continue;
231
- const extObj = gl.getExtension(ext); // This call enables the extension and returns a WebGLObject containing extension instance.
232
- if (null !== extObj)
233
- this._extensionMap[ext] = extObj;
234
- }
235
- }
236
- if (this._isWebGL2 && undefined !== gl2) {
237
- this._maxColorAttachments = gl.getParameter(gl2.MAX_COLOR_ATTACHMENTS);
238
- this._maxDrawBuffers = gl.getParameter(gl2.MAX_DRAW_BUFFERS);
239
- }
240
- else {
241
- const dbExt = this.queryExtensionObject("WEBGL_draw_buffers");
242
- this._maxColorAttachments = dbExt !== undefined ? gl.getParameter(dbExt.MAX_COLOR_ATTACHMENTS_WEBGL) : 1;
243
- this._maxDrawBuffers = dbExt !== undefined ? gl.getParameter(dbExt.MAX_DRAW_BUFFERS_WEBGL) : 1;
244
- }
245
- // Determine the maximum color-renderable attachment type.
246
- const allowFloatRender = (undefined === disabledExtensions || -1 === disabledExtensions.indexOf("OES_texture_float"))
247
- // iOS>=15 allows full-float rendering. However, it does not actually work on non-M1 devices.
248
- // Because of this, for now we disallow full float rendering on iOS devices.
249
- // ###TODO: Re-assess this after future iOS updates.
250
- && !ProcessDetector.isIOSBrowser
251
- // Samsung Galaxy Note 8 exhibits same issue as described above for iOS >= 15.
252
- // It uses specifically Mali-G71 MP20 but reports its renderer as follows.
253
- // Samsung Galaxy A50 and S9 exhibits same issue; they use Mali-G72.
254
- // HUAWEI P30 exhibits same issue; it uses Mali-G76.
255
- && unmaskedRenderer !== "Mali-G71" && unmaskedRenderer !== "Mali-G72" && unmaskedRenderer !== "Mali-G76";
256
- if (allowFloatRender && undefined !== this.queryExtensionObject("EXT_float_blend") && this.isTextureRenderable(gl, gl.FLOAT)) {
257
- this._maxRenderType = RenderType.TextureFloat;
258
- }
259
- else if (this.isWebGL2) {
260
- this._maxRenderType = (this.isTextureRenderable(gl, gl.HALF_FLOAT)) ? RenderType.TextureHalfFloat : RenderType.TextureUnsignedByte;
261
- }
262
- else {
263
- const hfExt = this.queryExtensionObject("OES_texture_half_float");
264
- this._maxRenderType = (hfExt !== undefined && this.isTextureRenderable(gl, hfExt.HALF_FLOAT_OES)) ? RenderType.TextureHalfFloat : RenderType.TextureUnsignedByte;
265
- }
266
- // Determine the maximum depth attachment type.
267
- // this._maxDepthType = this.queryExtensionObject("WEBGL_depth_texture") !== undefined ? DepthType.TextureUnsignedInt32 : DepthType.RenderBufferUnsignedShort16;
268
- this._maxDepthType = this._isWebGL2 || this.queryExtensionObject("WEBGL_depth_texture") !== undefined ? DepthType.TextureUnsignedInt24Stencil8 : DepthType.RenderBufferUnsignedShort16;
269
- this._presentFeatures = this._gatherFeatures();
270
- const missingRequiredFeatures = this._findMissingFeatures(Capabilities.requiredFeatures);
271
- const missingOptionalFeatures = this._findMissingFeatures(Capabilities.optionalFeatures);
272
- return {
273
- status: this._getCompatibilityStatus(missingRequiredFeatures, missingOptionalFeatures),
274
- missingRequiredFeatures,
275
- missingOptionalFeatures,
276
- unmaskedRenderer,
277
- unmaskedVendor,
278
- usingIntegratedGraphics: isIntegratedGraphics({ unmaskedVendor, unmaskedRenderer }),
279
- driverBugs: { ...this._driverBugs },
280
- userAgent: navigator.userAgent,
281
- createdContext: gl,
282
- };
283
- }
284
- static create(gl, disabledExtensions) {
285
- const caps = new Capabilities();
286
- const compatibility = caps.init(gl, disabledExtensions);
287
- if (WebGLRenderCompatibilityStatus.CannotCreateContext === compatibility.status || WebGLRenderCompatibilityStatus.MissingRequiredFeatures === compatibility.status)
288
- return undefined;
289
- return caps;
290
- }
291
- /** Determines if a particular texture type is color-renderable on the host system. */
292
- isTextureRenderable(gl, texType) {
293
- const tex = gl.createTexture();
294
- gl.bindTexture(gl.TEXTURE_2D, tex);
295
- if (this.isWebGL2) {
296
- if (gl.FLOAT === texType)
297
- gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA32F, 1, 1, 0, gl.RGBA, texType, null);
298
- else
299
- gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA16F, 1, 1, 0, gl.RGBA, texType, null);
300
- }
301
- else
302
- gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 1, 1, 0, gl.RGBA, texType, null);
303
- const fb = gl.createFramebuffer();
304
- gl.bindFramebuffer(gl.FRAMEBUFFER, fb);
305
- gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, tex, 0);
306
- const fbStatus = gl.checkFramebufferStatus(gl.FRAMEBUFFER);
307
- gl.bindFramebuffer(gl.FRAMEBUFFER, null);
308
- gl.deleteFramebuffer(fb);
309
- gl.deleteTexture(tex);
310
- gl.getError(); // clear any errors
311
- return fbStatus === gl.FRAMEBUFFER_COMPLETE;
312
- }
313
- setMaxAnisotropy(desiredMax, gl) {
314
- const ext = this.queryExtensionObject("EXT_texture_filter_anisotropic");
315
- if (undefined === ext)
316
- return;
317
- if (undefined === this._maxAnisotropy)
318
- this._maxAnisotropy = gl.getParameter(ext.MAX_TEXTURE_MAX_ANISOTROPY_EXT);
319
- const max = (undefined !== desiredMax) ? Math.min(desiredMax, this._maxAnisotropy) : this._maxAnisotropy;
320
- gl.texParameterf(gl.TEXTURE_2D, ext.TEXTURE_MAX_ANISOTROPY_EXT, max);
321
- }
322
- }
323
- Capabilities.optionalFeatures = [
324
- WebGLFeature.MrtTransparency,
325
- WebGLFeature.MrtPick,
326
- WebGLFeature.DepthTexture,
327
- WebGLFeature.FloatRendering,
328
- WebGLFeature.Instancing,
329
- WebGLFeature.ShadowMaps,
330
- WebGLFeature.FragDepth,
331
- WebGLFeature.StandardDerivatives,
332
- WebGLFeature.AntiAliasing,
333
- ];
334
- Capabilities.requiredFeatures = [
335
- WebGLFeature.UintElementIndex,
336
- WebGLFeature.MinimalTextureUnits,
337
- ];
1
+ /*---------------------------------------------------------------------------------------------
2
+ * Copyright (c) Bentley Systems, Incorporated. All rights reserved.
3
+ * See LICENSE.md in the project root for license terms and full copyright notice.
4
+ *--------------------------------------------------------------------------------------------*/
5
+ /** @packageDocumentation
6
+ * @module Compatibility
7
+ */
8
+ import { ProcessDetector } from "@itwin/core-bentley";
9
+ import { WebGLFeature, WebGLRenderCompatibilityStatus, } from "./RenderCompatibility";
10
+ const knownExtensions = [
11
+ "WEBGL_draw_buffers",
12
+ "OES_element_index_uint",
13
+ "OES_texture_float",
14
+ "OES_texture_float_linear",
15
+ "OES_texture_half_float",
16
+ "OES_texture_half_float_linear",
17
+ "EXT_texture_filter_anisotropic",
18
+ "WEBGL_depth_texture",
19
+ "EXT_color_buffer_float",
20
+ "EXT_shader_texture_lod",
21
+ "EXT_frag_depth",
22
+ "ANGLE_instanced_arrays",
23
+ "OES_vertex_array_object",
24
+ "WEBGL_lose_context",
25
+ "EXT_disjoint_timer_query",
26
+ "EXT_disjoint_timer_query_webgl2",
27
+ "OES_standard_derivatives",
28
+ "EXT_float_blend",
29
+ ];
30
+ /** Describes the type of a render target. Used by Capabilities to represent maximum precision render target available on host system.
31
+ * @internal
32
+ */
33
+ export var RenderType;
34
+ (function (RenderType) {
35
+ RenderType[RenderType["TextureUnsignedByte"] = 0] = "TextureUnsignedByte";
36
+ RenderType[RenderType["TextureHalfFloat"] = 1] = "TextureHalfFloat";
37
+ RenderType[RenderType["TextureFloat"] = 2] = "TextureFloat";
38
+ })(RenderType || (RenderType = {}));
39
+ /**
40
+ * Describes the type of a depth buffer. Used by Capabilities to represent maximum depth buffer precision available on host system.
41
+ * Note: the commented-out values are unimplemented but left in place for reference, in case desired for future implementation.
42
+ * @internal
43
+ */
44
+ export var DepthType;
45
+ (function (DepthType) {
46
+ DepthType[DepthType["RenderBufferUnsignedShort16"] = 0] = "RenderBufferUnsignedShort16";
47
+ // TextureUnsignedShort16, // core to WebGL2; available to WebGL1 via WEBGL_depth_texture
48
+ // TextureUnsignedInt24, // core to WebGL2
49
+ DepthType[DepthType["TextureUnsignedInt24Stencil8"] = 1] = "TextureUnsignedInt24Stencil8";
50
+ DepthType[DepthType["TextureUnsignedInt32"] = 2] = "TextureUnsignedInt32";
51
+ // TextureFloat32, // core to WebGL2
52
+ // TextureFloat32Stencil8, // core to WeBGL2
53
+ })(DepthType || (DepthType = {}));
54
+ const maxTexSizeAllowed = 4096; // many devices and browsers have issues with source textures larger than this
55
+ // Regexes to match Intel UHD/HD 620/630 integrated GPUS that suffer from GraphicsDriverBugs.fragDepthDoesNotDisableEarlyZ.
56
+ const buggyIntelMatchers = [
57
+ // Original unmasked renderer string when workaround we implemented.
58
+ /ANGLE \(Intel\(R\) (U)?HD Graphics 6(2|3)0 Direct3D11/,
59
+ // New unmasked renderer string circa October 2021.
60
+ /ANGLE \(Intel, Intel\(R\) (U)?HD Graphics 6(2|3)0 Direct3D11/,
61
+ ];
62
+ // Regexes to match Mali GPUs known to suffer from GraphicsDriverBugs.msaaWillHang.
63
+ const buggyMaliMatchers = [
64
+ /Mali-G71/,
65
+ /Mali-G72/,
66
+ /Mali-G76/,
67
+ ];
68
+ // Regexes to match as many Intel integrated GPUs as possible.
69
+ // https://en.wikipedia.org/wiki/List_of_Intel_graphics_processing_units
70
+ const integratedIntelGpuMatchers = [
71
+ /(U)?HD Graphics/,
72
+ /Iris/,
73
+ ];
74
+ function isIntegratedGraphics(args) {
75
+ if (args.unmaskedRenderer && args.unmaskedRenderer.includes("Intel") && integratedIntelGpuMatchers.some((x) => x.test(args.unmaskedRenderer)))
76
+ return true;
77
+ // NB: For now, we do not attempt to detect AMD integrated graphics.
78
+ // It appears that AMD integrated graphics are not usually paired with a graphics card so detecting integrated usage there is less important than Intel.
79
+ return false;
80
+ }
81
+ /** Describes the rendering capabilities of the host system.
82
+ * @internal
83
+ */
84
+ class Capabilities {
85
+ constructor() {
86
+ this._maxRenderType = RenderType.TextureUnsignedByte;
87
+ this._maxDepthType = DepthType.RenderBufferUnsignedShort16;
88
+ this._maxTextureSize = 0;
89
+ this._maxColorAttachments = 0;
90
+ this._maxDrawBuffers = 0;
91
+ this._maxFragTextureUnits = 0;
92
+ this._maxVertTextureUnits = 0;
93
+ this._maxVertAttribs = 0;
94
+ this._maxVertUniformVectors = 0;
95
+ this._maxVaryingVectors = 0;
96
+ this._maxFragUniformVectors = 0;
97
+ this._maxAntialiasSamples = 1;
98
+ this._supportsCreateImageBitmap = false;
99
+ this._maxTexSizeAllow = maxTexSizeAllowed;
100
+ this._extensionMap = {}; // Use this map to store actual extension objects retrieved from GL.
101
+ this._presentFeatures = []; // List of features the system can support (not necessarily dependent on extensions)
102
+ this._isWebGL2 = false;
103
+ this._isMobile = false;
104
+ this._driverBugs = {};
105
+ }
106
+ get maxRenderType() { return this._maxRenderType; }
107
+ get maxDepthType() { return this._maxDepthType; }
108
+ get maxTextureSize() { return this._maxTextureSize; }
109
+ get maxTexSizeAllow() { return this._maxTexSizeAllow; }
110
+ get supportsCreateImageBitmap() { return this._supportsCreateImageBitmap; }
111
+ get maxColorAttachments() { return this._maxColorAttachments; }
112
+ get maxDrawBuffers() { return this._maxDrawBuffers; }
113
+ get maxFragTextureUnits() { return this._maxFragTextureUnits; }
114
+ get maxVertTextureUnits() { return this._maxVertTextureUnits; }
115
+ get maxVertAttribs() { return this._maxVertAttribs; }
116
+ get maxVertUniformVectors() { return this._maxVertUniformVectors; }
117
+ get maxVaryingVectors() { return this._maxVaryingVectors; }
118
+ get maxFragUniformVectors() { return this._maxFragUniformVectors; }
119
+ get maxAntialiasSamples() { return this._maxAntialiasSamples; }
120
+ get isWebGL2() { return this._isWebGL2; }
121
+ get driverBugs() { return this._driverBugs; }
122
+ /** These getters check for existence of extension objects to determine availability of features. In WebGL2, could just return true for some. */
123
+ get supportsNonPowerOf2Textures() { return false; }
124
+ get supportsDrawBuffers() { return this._isWebGL2 || this.queryExtensionObject("WEBGL_draw_buffers") !== undefined; }
125
+ get supportsInstancing() { return this._isWebGL2 || this.queryExtensionObject("ANGLE_instanced_arrays") !== undefined; }
126
+ get supports32BitElementIndex() { return this._isWebGL2 || this.queryExtensionObject("OES_element_index_uint") !== undefined; }
127
+ get supportsTextureFloat() { return this._isWebGL2 || this.queryExtensionObject("OES_texture_float") !== undefined; }
128
+ get supportsTextureFloatLinear() { return this._isWebGL2 || this.queryExtensionObject("OES_texture_float_linear") !== undefined; }
129
+ get supportsTextureHalfFloat() { return this._isWebGL2 || this.queryExtensionObject("OES_texture_half_float") !== undefined; }
130
+ get supportsTextureHalfFloatLinear() { return this._isWebGL2 || this.queryExtensionObject("OES_texture_half_float_linear") !== undefined; }
131
+ get supportsTextureFilterAnisotropic() { return this.queryExtensionObject("EXT_texture_filter_anisotropic") !== undefined; }
132
+ get supportsShaderTextureLOD() { return this._isWebGL2 || this.queryExtensionObject("EXT_shader_texture_lod") !== undefined; }
133
+ get supportsVertexArrayObjects() { return this._isWebGL2 || this.queryExtensionObject("OES_vertex_array_object") !== undefined; }
134
+ get supportsFragDepth() { return this._isWebGL2 || this.queryExtensionObject("EXT_frag_depth") !== undefined; }
135
+ get supportsDisjointTimerQuery() { return (this._isWebGL2 && this.queryExtensionObject("EXT_disjoint_timer_query_webgl2") !== undefined) || this.queryExtensionObject("EXT_disjoint_timer_query") !== undefined; }
136
+ get supportsStandardDerivatives() { return this._isWebGL2 || this.queryExtensionObject("OES_standard_derivatives") !== undefined; }
137
+ get supportsMRTTransparency() { return this.maxColorAttachments >= 2; }
138
+ get supportsMRTPickShaders() { return this.maxColorAttachments >= 3; }
139
+ get supportsShadowMaps() {
140
+ return this.supportsTextureFloat || this.supportsTextureHalfFloat;
141
+ }
142
+ get supportsAntiAliasing() { return this._isWebGL2 && this.maxAntialiasSamples > 1; }
143
+ get isMobile() { return this._isMobile; }
144
+ findExtension(name) {
145
+ const ext = this._extensionMap[name];
146
+ return null !== ext ? ext : undefined;
147
+ }
148
+ /** Queries an extension object if available. This is necessary for other parts of the system to access some constants within extensions. */
149
+ queryExtensionObject(ext) {
150
+ return this.findExtension(ext);
151
+ }
152
+ get _hasRequiredTextureUnits() { return this.maxFragTextureUnits >= 4 && this.maxVertTextureUnits >= 5; }
153
+ /** Return an array containing any features not supported by the system as compared to the input array. */
154
+ _findMissingFeatures(featuresToSeek) {
155
+ const missingFeatures = [];
156
+ for (const featureName of featuresToSeek) {
157
+ if (-1 === this._presentFeatures.indexOf(featureName))
158
+ missingFeatures.push(featureName);
159
+ }
160
+ return missingFeatures;
161
+ }
162
+ /** Populate and return an array containing features that this system supports. */
163
+ _gatherFeatures() {
164
+ const features = [];
165
+ // simply check for presence of various extensions if that gives enough information
166
+ if (this._isWebGL2 || this._extensionMap["OES_element_index_uint"] !== undefined)
167
+ features.push(WebGLFeature.UintElementIndex);
168
+ if (this._isWebGL2 || this._extensionMap["ANGLE_instanced_arrays"] !== undefined)
169
+ features.push(WebGLFeature.Instancing);
170
+ if (this.supportsMRTTransparency)
171
+ features.push(WebGLFeature.MrtTransparency);
172
+ if (this.supportsMRTPickShaders)
173
+ features.push(WebGLFeature.MrtPick);
174
+ if (this.supportsShadowMaps)
175
+ features.push(WebGLFeature.ShadowMaps);
176
+ if (this._hasRequiredTextureUnits)
177
+ features.push(WebGLFeature.MinimalTextureUnits);
178
+ if (this.supportsFragDepth)
179
+ features.push(WebGLFeature.FragDepth);
180
+ if (this.supportsStandardDerivatives)
181
+ features.push(WebGLFeature.StandardDerivatives);
182
+ if (this.supportsAntiAliasing)
183
+ features.push(WebGLFeature.AntiAliasing);
184
+ if (DepthType.TextureUnsignedInt24Stencil8 === this._maxDepthType)
185
+ features.push(WebGLFeature.DepthTexture);
186
+ // check if at least half-float rendering is available based on maximum discovered renderable target
187
+ if (RenderType.TextureUnsignedByte !== this._maxRenderType)
188
+ features.push(WebGLFeature.FloatRendering);
189
+ return features;
190
+ }
191
+ /** Retrieve compatibility status based on presence of various features. */
192
+ _getCompatibilityStatus(missingRequiredFeatures, missingOptionalFeatures) {
193
+ let status = WebGLRenderCompatibilityStatus.AllOkay;
194
+ if (missingOptionalFeatures.length > 0)
195
+ status = WebGLRenderCompatibilityStatus.MissingOptionalFeatures;
196
+ if (missingRequiredFeatures.length > 0)
197
+ status = WebGLRenderCompatibilityStatus.MissingRequiredFeatures;
198
+ return status;
199
+ }
200
+ /** Initializes the capabilities based on a GL context. Must be called first. */
201
+ init(gl, disabledExtensions) {
202
+ const gl2 = !(gl instanceof WebGLRenderingContext) ? gl : undefined;
203
+ this._isWebGL2 = undefined !== gl2;
204
+ this._isMobile = ProcessDetector.isMobileBrowser;
205
+ const debugInfo = gl.getExtension("WEBGL_debug_renderer_info");
206
+ const unmaskedRenderer = debugInfo !== null ? gl.getParameter(debugInfo.UNMASKED_RENDERER_WEBGL) : undefined;
207
+ const unmaskedVendor = debugInfo !== null ? gl.getParameter(debugInfo.UNMASKED_VENDOR_WEBGL) : undefined;
208
+ this._driverBugs = {};
209
+ if (unmaskedRenderer && buggyIntelMatchers.some((x) => x.test(unmaskedRenderer)))
210
+ this._driverBugs.fragDepthDoesNotDisableEarlyZ = true;
211
+ if (unmaskedRenderer && buggyMaliMatchers.some((x) => x.test(unmaskedRenderer)))
212
+ this._driverBugs.msaaWillHang = true;
213
+ this._maxTextureSize = gl.getParameter(gl.MAX_TEXTURE_SIZE);
214
+ this._supportsCreateImageBitmap = typeof createImageBitmap === "function" && ProcessDetector.isChromium && !ProcessDetector.isIOSBrowser;
215
+ this._maxTexSizeAllow = Math.min(this._maxTextureSize, maxTexSizeAllowed);
216
+ this._maxFragTextureUnits = gl.getParameter(gl.MAX_TEXTURE_IMAGE_UNITS);
217
+ this._maxVertTextureUnits = gl.getParameter(gl.MAX_VERTEX_TEXTURE_IMAGE_UNITS);
218
+ this._maxVertAttribs = gl.getParameter(gl.MAX_VERTEX_ATTRIBS);
219
+ this._maxVertUniformVectors = gl.getParameter(gl.MAX_VERTEX_UNIFORM_VECTORS);
220
+ this._maxVaryingVectors = gl.getParameter(gl.MAX_VARYING_VECTORS);
221
+ this._maxFragUniformVectors = gl.getParameter(gl.MAX_FRAGMENT_UNIFORM_VECTORS);
222
+ this._maxAntialiasSamples = this._driverBugs.msaaWillHang ? 1 : (this._isWebGL2 && undefined !== gl2 ? gl.getParameter(gl2.MAX_SAMPLES) : 1);
223
+ const extensions = gl.getSupportedExtensions(); // This just retrieves a list of available extensions (not necessarily enabled).
224
+ if (extensions) {
225
+ for (const extStr of extensions) {
226
+ const ext = extStr;
227
+ if (-1 === knownExtensions.indexOf(ext))
228
+ continue;
229
+ else if (undefined !== disabledExtensions && -1 !== disabledExtensions.indexOf(ext))
230
+ continue;
231
+ const extObj = gl.getExtension(ext); // This call enables the extension and returns a WebGLObject containing extension instance.
232
+ if (null !== extObj)
233
+ this._extensionMap[ext] = extObj;
234
+ }
235
+ }
236
+ if (this._isWebGL2 && undefined !== gl2) {
237
+ this._maxColorAttachments = gl.getParameter(gl2.MAX_COLOR_ATTACHMENTS);
238
+ this._maxDrawBuffers = gl.getParameter(gl2.MAX_DRAW_BUFFERS);
239
+ }
240
+ else {
241
+ const dbExt = this.queryExtensionObject("WEBGL_draw_buffers");
242
+ this._maxColorAttachments = dbExt !== undefined ? gl.getParameter(dbExt.MAX_COLOR_ATTACHMENTS_WEBGL) : 1;
243
+ this._maxDrawBuffers = dbExt !== undefined ? gl.getParameter(dbExt.MAX_DRAW_BUFFERS_WEBGL) : 1;
244
+ }
245
+ // Determine the maximum color-renderable attachment type.
246
+ const allowFloatRender = (undefined === disabledExtensions || -1 === disabledExtensions.indexOf("OES_texture_float"))
247
+ // iOS>=15 allows full-float rendering. However, it does not actually work on non-M1 devices.
248
+ // Because of this, for now we disallow full float rendering on iOS devices.
249
+ // ###TODO: Re-assess this after future iOS updates.
250
+ && !ProcessDetector.isIOSBrowser
251
+ // Samsung Galaxy Note 8 exhibits same issue as described above for iOS >= 15.
252
+ // It uses specifically Mali-G71 MP20 but reports its renderer as follows.
253
+ // Samsung Galaxy A50 and S9 exhibits same issue; they use Mali-G72.
254
+ // HUAWEI P30 exhibits same issue; it uses Mali-G76.
255
+ && unmaskedRenderer !== "Mali-G71" && unmaskedRenderer !== "Mali-G72" && unmaskedRenderer !== "Mali-G76";
256
+ if (allowFloatRender && undefined !== this.queryExtensionObject("EXT_float_blend") && this.isTextureRenderable(gl, gl.FLOAT)) {
257
+ this._maxRenderType = RenderType.TextureFloat;
258
+ }
259
+ else if (this.isWebGL2) {
260
+ this._maxRenderType = (this.isTextureRenderable(gl, gl.HALF_FLOAT)) ? RenderType.TextureHalfFloat : RenderType.TextureUnsignedByte;
261
+ }
262
+ else {
263
+ const hfExt = this.queryExtensionObject("OES_texture_half_float");
264
+ this._maxRenderType = (hfExt !== undefined && this.isTextureRenderable(gl, hfExt.HALF_FLOAT_OES)) ? RenderType.TextureHalfFloat : RenderType.TextureUnsignedByte;
265
+ }
266
+ // Determine the maximum depth attachment type.
267
+ // this._maxDepthType = this.queryExtensionObject("WEBGL_depth_texture") !== undefined ? DepthType.TextureUnsignedInt32 : DepthType.RenderBufferUnsignedShort16;
268
+ this._maxDepthType = this._isWebGL2 || this.queryExtensionObject("WEBGL_depth_texture") !== undefined ? DepthType.TextureUnsignedInt24Stencil8 : DepthType.RenderBufferUnsignedShort16;
269
+ this._presentFeatures = this._gatherFeatures();
270
+ const missingRequiredFeatures = this._findMissingFeatures(Capabilities.requiredFeatures);
271
+ const missingOptionalFeatures = this._findMissingFeatures(Capabilities.optionalFeatures);
272
+ return {
273
+ status: this._getCompatibilityStatus(missingRequiredFeatures, missingOptionalFeatures),
274
+ missingRequiredFeatures,
275
+ missingOptionalFeatures,
276
+ unmaskedRenderer,
277
+ unmaskedVendor,
278
+ usingIntegratedGraphics: isIntegratedGraphics({ unmaskedVendor, unmaskedRenderer }),
279
+ driverBugs: { ...this._driverBugs },
280
+ userAgent: navigator.userAgent,
281
+ createdContext: gl,
282
+ };
283
+ }
284
+ static create(gl, disabledExtensions) {
285
+ const caps = new Capabilities();
286
+ const compatibility = caps.init(gl, disabledExtensions);
287
+ if (WebGLRenderCompatibilityStatus.CannotCreateContext === compatibility.status || WebGLRenderCompatibilityStatus.MissingRequiredFeatures === compatibility.status)
288
+ return undefined;
289
+ return caps;
290
+ }
291
+ /** Determines if a particular texture type is color-renderable on the host system. */
292
+ isTextureRenderable(gl, texType) {
293
+ const tex = gl.createTexture();
294
+ gl.bindTexture(gl.TEXTURE_2D, tex);
295
+ if (this.isWebGL2) {
296
+ if (gl.FLOAT === texType)
297
+ gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA32F, 1, 1, 0, gl.RGBA, texType, null);
298
+ else
299
+ gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA16F, 1, 1, 0, gl.RGBA, texType, null);
300
+ }
301
+ else
302
+ gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 1, 1, 0, gl.RGBA, texType, null);
303
+ const fb = gl.createFramebuffer();
304
+ gl.bindFramebuffer(gl.FRAMEBUFFER, fb);
305
+ gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, tex, 0);
306
+ const fbStatus = gl.checkFramebufferStatus(gl.FRAMEBUFFER);
307
+ gl.bindFramebuffer(gl.FRAMEBUFFER, null);
308
+ gl.deleteFramebuffer(fb);
309
+ gl.deleteTexture(tex);
310
+ gl.getError(); // clear any errors
311
+ return fbStatus === gl.FRAMEBUFFER_COMPLETE;
312
+ }
313
+ setMaxAnisotropy(desiredMax, gl) {
314
+ const ext = this.queryExtensionObject("EXT_texture_filter_anisotropic");
315
+ if (undefined === ext)
316
+ return;
317
+ if (undefined === this._maxAnisotropy)
318
+ this._maxAnisotropy = gl.getParameter(ext.MAX_TEXTURE_MAX_ANISOTROPY_EXT);
319
+ const max = (undefined !== desiredMax) ? Math.min(desiredMax, this._maxAnisotropy) : this._maxAnisotropy;
320
+ gl.texParameterf(gl.TEXTURE_2D, ext.TEXTURE_MAX_ANISOTROPY_EXT, max);
321
+ }
322
+ }
323
+ Capabilities.optionalFeatures = [
324
+ WebGLFeature.MrtTransparency,
325
+ WebGLFeature.MrtPick,
326
+ WebGLFeature.DepthTexture,
327
+ WebGLFeature.FloatRendering,
328
+ WebGLFeature.Instancing,
329
+ WebGLFeature.ShadowMaps,
330
+ WebGLFeature.FragDepth,
331
+ WebGLFeature.StandardDerivatives,
332
+ WebGLFeature.AntiAliasing,
333
+ ];
334
+ Capabilities.requiredFeatures = [
335
+ WebGLFeature.UintElementIndex,
336
+ WebGLFeature.MinimalTextureUnits,
337
+ ];
338
+ export { Capabilities };
338
339
  //# sourceMappingURL=Capabilities.js.map