Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore(webgl): Consolidate WebGL extension handling #1964

Merged
merged 4 commits into from
Feb 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 24 additions & 3 deletions modules/constants/src/webgl-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -345,37 +345,59 @@ export type GLParameters = GLValueParameters & GLFunctionParameters;

/** WebGL2 Extensions */
export type GLExtensions = {
/** https://registry.khronos.org/webgl/extensions/EXT_color_buffer_float */
EXT_color_buffer_float?: EXT_color_buffer_float | null;
/** https://registry.khronos.org/webgl/extensions/EXT_color_buffer_half_float */
EXT_color_buffer_half_float?: EXT_color_buffer_half_float | null;
/** https://registry.khronos.org/webgl/extensions/EXT_texture_compression_bptc */
EXT_texture_compression_bptc?: EXT_texture_compression_bptc | null;
/** https://registry.khronos.org/webgl/extensions/EXT_texture_compression_rgtc */
EXT_texture_compression_rgtc?: EXT_texture_compression_rgtc | null;
/** https://registry.khronos.org/webgl/extensions/EXT_texture_filter_anisotropic */
EXT_texture_filter_anisotropic?: EXT_texture_filter_anisotropic | null;
/** https://registry.khronos.org/webgl/extensions/KHR_parallel_shader_compile */
KHR_parallel_shader_compile?: KHR_parallel_shader_compile | null;
/** https://registry.khronos.org/webgl/extensions/OES_fbo_render_mipmap */
OES_fbo_render_mipmap?: OES_fbo_render_mipmap | null;
/** https://registry.khronos.org/webgl/extensions/OES_texture_float */
OES_texture_float?: OES_texture_float | null;
/** https://registry.khronos.org/webgl/extensions/OES_texture_float_linear */
OES_texture_float_linear?: OES_texture_float_linear | null;
/** https://registry.khronos.org/webgl/extensions/OES_texture_half_float */
OES_texture_half_float?: OES_texture_half_float | null;
/** https://registry.khronos.org/webgl/extensions/OES_texture_half_float_linear */
OES_texture_half_float_linear?: OES_texture_half_float_linear | null;
/** https://registry.khronos.org/webgl/extensions/OES_vertex_array_object */
OES_vertex_array_object?: OES_vertex_array_object | null;
/** https://registry.khronos.org/webgl/extensions/EXT_float_blend */
EXT_float_blend?: EXT_float_blend | null;
/** https://registry.khronos.org/webgl/extensions/OVR_multiview2 */
OVR_multiview2?: OVR_multiview2 | null;
/** https://registry.khronos.org/webgl/extensions/WEBGL_compressed_texture_astc */
WEBGL_compressed_texture_astc?: WEBGL_compressed_texture_astc | null;
/** https://registry.khronos.org/webgl/extensions/WEBGL_compressed_texture_etc */
WEBGL_compressed_texture_etc?: WEBGL_compressed_texture_etc | null;
/** https://registry.khronos.org/webgl/extensions/WEBGL_compressed_texture_etc1 */
WEBGL_compressed_texture_etc1?: WEBGL_compressed_texture_etc1 | null;
/** https://registry.khronos.org/webgl/extensions/WEBGL_compressed_texture_pvrtc */
WEBGL_compressed_texture_pvrtc?: WEBGL_compressed_texture_pvrtc | null;
/** https://registry.khronos.org/webgl/extensions/WEBGL_compressed_texture_s3tc */
WEBGL_compressed_texture_s3tc?: WEBGL_compressed_texture_s3tc | null;
/** https://registry.khronos.org/webgl/extensions/WEBGL_compressed_texture_s3tc_srgb */
WEBGL_compressed_texture_s3tc_srgb?: WEBGL_compressed_texture_s3tc_srgb | null;
/** https://registry.khronos.org/webgl/extensions/WEBGL_debug_renderer_info */
WEBGL_debug_renderer_info?: WEBGL_debug_renderer_info | null;
/** https://registry.khronos.org/webgl/extensions/WEBGL_debug_shaders */
WEBGL_debug_shaders?: WEBGL_debug_shaders | null;
/** https://registry.khronos.org/webgl/extensions/WEBGL_lose_context */
WEBGL_lose_context?: WEBGL_lose_context | null;

// Predefined typescript types not available for the following extensions

/** https://registry.khronos.org/webgl/extensions/EXT_depth_clamp/ */
EXT_depth_clamp?: EXT_depth_clamp | null;

/** https://registry.khronos.org/webgl/extensions/WEBGL_provoking_vertex/ */
WEBGL_provoking_vertex?: WEBGL_provoking_vertex | null;

/** https://registry.khronos.org/webgl/extensions/WEBGL_polygon_mode/ */
WEBGL_polygon_mode?: WEBGL_polygon_mode | null;

Expand Down Expand Up @@ -404,7 +426,6 @@ export type GLExtensions = {

// ANGLE_instanced_arrays?: ANGLE_instanced_arrays | null;
// EXT_blend_minmax?: EXT_blend_minmax | null;
// EXT_float_blend?: EXT_float_blend | null;
// EXT_frag_depth?: EXT_frag_depth | null;
// EXT_sRGB?: EXT_sRGB | null;
// EXT_shader_texture_lod?: EXT_shader_texture_lod | null;
Expand Down
88 changes: 55 additions & 33 deletions modules/core/src/adapter/device.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,36 +20,6 @@ import type {CommandEncoder, CommandEncoderProps} from './resources/command-enco
import type {VertexArray, VertexArrayProps} from './resources/vertex-array';
import type {TransformFeedback, TransformFeedbackProps} from './resources/transform-feedback';

/** Device properties */
export type DeviceProps = {
id?: string;

type?: 'webgl' | 'webgpu' | 'best-available';

// Common parameters
canvas?: HTMLCanvasElement | OffscreenCanvas | string | null; // A canvas element or a canvas string id
container?: HTMLElement | string | null;
width?: number /** width is only used when creating a new canvas */;
height?: number /** height is only used when creating a new canvas */;

// WebGLContext PARAMETERS - Can only be set on context creation...
// alpha?: boolean; // Default render target has an alpha buffer.
// depth?: boolean; // Default render target has a depth buffer of at least 16 bits.
// stencil?: boolean; // Default render target has a stencil buffer of at least 8 bits.
// antialias?: boolean; // Boolean that indicates whether or not to perform anti-aliasing.
// premultipliedAlpha?: boolean; // Boolean that indicates that the page compositor will assume the drawing buffer contains colors with pre-multiplied alpha.
// preserveDrawingBuffer?: boolean; // Default render target buffers will not be automatically cleared and will preserve their values until cleared or overwritten
// failIfMajorPerformanceCaveat?: boolean; // Do not create if the system performance is low.

// Unclear if these are still supported
debug?: boolean; // Instrument context (at the expense of performance)
manageState?: boolean; // Set to false to disable WebGL state management instrumentation
break?: string[]; // TODO: types

// @deprecated Attach to existing context
gl?: WebGL2RenderingContext | null;
};

/**
* Identifies the GPU vendor and driver.
* @note Chrome WebGPU does not provide much information, though more can be enabled with
Expand Down Expand Up @@ -125,7 +95,7 @@ export type WebGPUDeviceFeature =
| 'texture-compression-etc2'
| 'texture-compression-astc';

// Removed WebGPU features...
// WebGPU features that have been removed from the WebGPU spec...
// 'depth-clamping' |
// 'pipeline-statistics-query' |

Expand Down Expand Up @@ -170,6 +140,57 @@ export type DeviceFeature =
| WebGLDeviceFeature
| WebGLCompressedTextureFeatures;

/** Set-like class for features (lets apps check for WebGL / WebGPU extensions) */
export class DeviceFeatures {
protected features: Set<DeviceFeature>;

constructor(features: DeviceFeature[] = []) {
this.features = new Set<DeviceFeature>(features);
}

*[Symbol.iterator](): IterableIterator<DeviceFeature> {
yield* this.features;
}

has(feature: DeviceFeature): boolean {
return this.features.has(feature);
}
}

/** Device properties */
export type DeviceProps = {
id?: string;

type?: 'webgl' | 'webgpu' | 'best-available';

// Common parameters
canvas?: HTMLCanvasElement | OffscreenCanvas | string | null; // A canvas element or a canvas string id
container?: HTMLElement | string | null;
width?: number /** width is only used when creating a new canvas */;
height?: number /** height is only used when creating a new canvas */;

// WebGLContext PARAMETERS - Can only be set on context creation...
// alpha?: boolean; // Default render target has an alpha buffer.
// depth?: boolean; // Default render target has a depth buffer of at least 16 bits.
// stencil?: boolean; // Default render target has a stencil buffer of at least 8 bits.
// antialias?: boolean; // Boolean that indicates whether or not to perform anti-aliasing.
// premultipliedAlpha?: boolean; // Boolean that indicates that the page compositor will assume the drawing buffer contains colors with pre-multiplied alpha.
// preserveDrawingBuffer?: boolean; // Default render target buffers will not be automatically cleared and will preserve their values until cleared or overwritten
// failIfMajorPerformanceCaveat?: boolean; // Do not create if the system performance is low.

/** Instrument context (at the expense of performance) */
debug?: boolean;
/** Initialize the SpectorJS WebGL debugger */
spector?: boolean;

// Unclear if these are still supported
manageState?: boolean; // Set to false to disable WebGL state management instrumentation
break?: string[]; // TODO: types

// @deprecated Attach to existing context
gl?: WebGL2RenderingContext | null;
};

/**
* WebGPU Device/WebGL context abstraction
*/
Expand All @@ -182,7 +203,8 @@ export abstract class Device {
manageState: true,
width: 800, // width are height are only used by headless gl
height: 600,
debug: Boolean(log.get('debug')), // Instrument context (at the expense of performance)
debug: false, // Instrument context (at the expense of performance)
spector: false, // Initialize the SpectorJS WebGL debugger
break: [],

// alpha: undefined,
Expand Down Expand Up @@ -226,7 +248,7 @@ export abstract class Device {
abstract info: DeviceInfo;

/** Optional capability discovery */
abstract get features(): Set<DeviceFeature>;
abstract features: DeviceFeatures;

/** WebGPU style device limits */
abstract get limits(): DeviceLimits;
Expand Down
2 changes: 1 addition & 1 deletion modules/core/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ export {isTypedArray, isNumberArray} from './utils/is-array';
export {luma} from './lib/luma';

export type {DeviceProps, DeviceLimits, DeviceInfo, DeviceFeature} from './adapter/device';
export {Device} from './adapter/device';
export {Device, DeviceFeatures} from './adapter/device';
export type {CanvasContextProps} from './adapter/canvas-context';
export {CanvasContext} from './adapter/canvas-context';

Expand Down
2 changes: 1 addition & 1 deletion modules/engine/src/model/model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -751,7 +751,7 @@ export function getPlatformInfo(device: Device): PlatformInfo {
shaderLanguage: device.info.shadingLanguage,
shaderLanguageVersion: device.info.shadingLanguageVersion as 100 | 300,
gpu: device.info.gpu,
features: device.features
features: new Set(device.features)
};
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ function getInfo(device: Device): PlatformInfo {
gpu: device.info.gpu,
shaderLanguage: device.info.shadingLanguage,
shaderLanguageVersion: device.info.shadingLanguageVersion as 100 | 300,
features: device.features
features: new Set(device.features)
};
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ function getInfo(device: Device): PlatformInfo {
gpu: device.info.gpu,
shaderLanguage: device.info.shadingLanguage,
shaderLanguageVersion: device.info.shadingLanguageVersion as 100 | 300,
features: device.features
features: new Set(device.features)
};
}

Expand Down
72 changes: 43 additions & 29 deletions modules/webgl/src/adapter/converters/texture-formats.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@

import type {TextureFormat, DeviceFeature} from '@luma.gl/core';
import {decodeTextureFormat} from '@luma.gl/core';
import {GL} from '@luma.gl/constants';
import {GL, GLExtensions} from '@luma.gl/constants';
import {getWebGLExtension} from '../../context/helpers/webgl-extensions';
import {getGLFromVertexType} from './vertex-formats';

/* eslint-disable camelcase */
Expand Down Expand Up @@ -38,7 +39,7 @@ const EXT_color_buffer_float = 'EXT_color_buffer_float';
// const EXT_HALF_FLOAT_WEBGL1 = 'EXT_color_buffer_half_float';

// prettier-ignore
const TEXTURE_FEATURE_CHECKS: Partial<Record<DeviceFeature, string[]>> = {
export const TEXTURE_FEATURES: Partial<Record<DeviceFeature, string[]>> = {
'float32-renderable-webgl': ['EXT_color_buffer_float'], // [false, 'EXT_color_buffer_float'],
'float16-renderable-webgl': ['EXT_color_buffer_half_float'],
'norm16-renderable-webgl': [EXT_texture_norm16],
Expand All @@ -61,19 +62,27 @@ const TEXTURE_FEATURE_CHECKS: Partial<Record<DeviceFeature, string[]>> = {
'texture-compression-atc-webgl': [X_ATC]
};

function checkTextureFeature(gl: WebGL2RenderingContext, feature: DeviceFeature): boolean {
const extensions = TEXTURE_FEATURE_CHECKS[feature] || [];
return extensions.every(extension => gl.getExtension(extension));
}

function checkTextureFeatures(gl: WebGL2RenderingContext, features: DeviceFeature[]): boolean {
return features.every(feature => checkTextureFeature(gl, feature));
/** Return a list of texture feature strings (for Device.features). Mainly compressed texture support */
// export function getTextureFeatures(
// gl: WebGL2RenderingContext,
// extensions: GLExtensions
// ): DeviceFeature[] {
// const textureFeatures = Object.keys(TEXTURE_FEATURES) as DeviceFeature[];
// return textureFeatures.filter(feature => checkTextureFeature(gl, feature, extensions));
// }

export function isTextureFeature(feature: DeviceFeature): boolean {
return feature in TEXTURE_FEATURES;
}

/** Return a list of texture feature strings (for Device.features). Mainly compressed texture support */
export function getTextureFeatures(gl: WebGL2RenderingContext): DeviceFeature[] {
const textureFeatures = Object.keys(TEXTURE_FEATURE_CHECKS) as DeviceFeature[];
return textureFeatures.filter(feature => checkTextureFeature(gl, feature));
/** Checks a texture feature (for Device.features). Mainly compressed texture support */
export function checkTextureFeature(
gl: WebGL2RenderingContext,
feature: DeviceFeature,
extensions: GLExtensions
): boolean {
const textureExtensions = TEXTURE_FEATURES[feature] || [];
return textureExtensions.every(extension => getWebGLExtension(gl, extension, extensions));
}

// TEXTURE FORMATS
Expand Down Expand Up @@ -435,7 +444,8 @@ const TYPE_SIZES = {
/** Checks if a texture format is supported */
export function isTextureFormatSupported(
gl: WebGL2RenderingContext,
formatOrGL: TextureFormat | GL
formatOrGL: TextureFormat | GL,
extensions: GLExtensions
): boolean {
const format = convertGLToTextureFormat(formatOrGL);
const info = TEXTURE_FORMATS[format];
Expand All @@ -449,17 +459,18 @@ export function isTextureFormatSupported(
// Check extensions
const extension = info.x || info.gl2ext;
if (extension) {
return Boolean(gl.getExtension(extension));
return Boolean(getWebGLExtension(gl, extension, extensions));
}
return true;
}

export function isRenderbufferFormatSupported(
gl: WebGL2RenderingContext,
format: TextureFormat
format: TextureFormat,
extensions: GLExtensions
): boolean {
// Note: Order is important since the function call initializes extensions.
return isTextureFormatSupported(gl, format) && TEXTURE_FORMATS[format]?.renderbuffer;
return isTextureFormatSupported(gl, format, extensions) && TEXTURE_FORMATS[format]?.renderbuffer;
}

/**
Expand Down Expand Up @@ -491,7 +502,8 @@ export function convertTextureFormatToGL(format: TextureFormat): GL | undefined
/** Checks if a texture format is supported */
export function getTextureFormatSupport(
gl: WebGL2RenderingContext,
formatOrGL: TextureFormat | GL
formatOrGL: TextureFormat | GL,
extensions: GLExtensions
): {
supported: boolean;
filterable?: boolean;
Expand All @@ -511,20 +523,20 @@ export function getTextureFormatSupport(

// Support Check that we have a GL constant
let supported = info.gl === undefined;
supported = supported && checkTextureFeatures(gl, [info.f]);
supported = supported && checkTextureFeature(gl, info.f, extensions);

// Filtering
// const filterable = info.filter
// ? checkTextureFeatures(gl, [info.filter])
// ? checkTextureFeature(gl, infofilter])
// : decoded && !decoded.signed;
// const renderable = info.filter
// ? checkTextureFeatures(gl, [info.render])
// ? checkTextureFeature(gl, inforender])
// : decoded && !decoded.signed;

return {
supported,
renderable: supported && checkTextureFeatures(gl, [info.render]),
filterable: supported && checkTextureFeatures(gl, [info.filter]),
renderable: supported && checkTextureFeature(gl, info.render, extensions),
filterable: supported && checkTextureFeature(gl, info.filter, extensions),
blendable: false, // tod,
storable: false
};
Expand All @@ -533,10 +545,11 @@ export function getTextureFormatSupport(
/** Checks whether linear filtering (interpolated sampling) is available for floating point textures */
export function isTextureFormatFilterable(
gl: WebGL2RenderingContext,
formatOrGL: TextureFormat | GL
formatOrGL: TextureFormat | GL,
extensions: GLExtensions
): boolean {
const format = convertGLToTextureFormat(formatOrGL);
if (!isTextureFormatSupported(gl, format)) {
if (!isTextureFormatSupported(gl, format, extensions)) {
return false;
}
try {
Expand All @@ -548,20 +561,21 @@ export function isTextureFormatFilterable(
return false;
}
if (format.endsWith('32float')) {
return Boolean(gl.getExtension('OES_texture_float_linear'));
return Boolean(getWebGLExtension(gl, 'OES_texture_float_linear, extensions', extensions));
}
if (format.endsWith('16float')) {
return Boolean(gl.getExtension('OES_texture_half_float_linear'));
return Boolean(getWebGLExtension(gl, 'OES_texture_half_float_linear, extensions', extensions));
}
return true;
}

export function isTextureFormatRenderable(
gl: WebGL2RenderingContext,
formatOrGL: TextureFormat | GL
formatOrGL: TextureFormat | GL,
extensions: GLExtensions
): boolean {
const format = convertGLToTextureFormat(formatOrGL);
if (!isTextureFormatSupported(gl, format)) {
if (!isTextureFormatSupported(gl, format, extensions)) {
return false;
}
if (typeof format === 'number') {
Expand Down
Loading
Loading