mirror of
https://github.com/lexmount/moli.git
synced 2026-09-28 08:01:37 +00:00
fix(webgl): align query types, identity strings and GL errors
This commit is contained in:
@@ -12,6 +12,7 @@ const WEBGL_VIEWPORT_SLOT: &str = "__moliWebGlViewport";
|
||||
const WEBGL_ERROR_SLOT: &str = "__moliWebGlError";
|
||||
const WEBGL_EXTENSIONS_SLOT: &str = "__moliWebGlExtensions";
|
||||
const WEBGL_VIEWPORT: u32 = 0x0BA2;
|
||||
const WEBGL_INVALID_ENUM: u32 = 0x0500;
|
||||
const WEBGL_INVALID_VALUE: u32 = 0x0501;
|
||||
const WEBGL_MAX_VIEWPORT_DIMS: [i32; 2] = [8192, 8192];
|
||||
const WEBGL2_DRAWING_BUFFER_COLOR_SPACE_SLOT: &str = "__moliWebGl2DrawingBufferColorSpace";
|
||||
@@ -48,6 +49,15 @@ struct WebGlGetParameterArgs {
|
||||
pname: u32,
|
||||
}
|
||||
|
||||
#[derive(webidl::WebIdlArgs)]
|
||||
#[webidl(prefix = "WebGLRenderingContext.getShaderPrecisionFormat")]
|
||||
struct WebGlGetShaderPrecisionFormatArgs {
|
||||
#[webidl(required)]
|
||||
shader_type: u32,
|
||||
#[webidl(required)]
|
||||
precision_type: u32,
|
||||
}
|
||||
|
||||
#[derive(webidl::WebIdlArgs)]
|
||||
#[webidl(prefix = "WebGLRenderingContext.viewport")]
|
||||
struct WebGlViewportArgs {
|
||||
@@ -67,7 +77,7 @@ struct WebGlContextStateDeclaration<'s> {
|
||||
#[webapi(slot = WEBGL_VIEWPORT_SLOT)]
|
||||
viewport: v8::Local<'s, v8::Array>,
|
||||
#[webapi(slot = WEBGL_ERROR_SLOT)]
|
||||
error: u32,
|
||||
errors: v8::Local<'s, v8::Set>,
|
||||
#[webapi(slot = WEBGL_EXTENSIONS_SLOT)]
|
||||
extensions: v8::Local<'s, v8::Map>,
|
||||
}
|
||||
@@ -151,6 +161,7 @@ struct WebGl2ContextObjectDeclaration {
|
||||
pub(crate) const WEBGL_CONSTANTS: &[(&str, u32)] = &[
|
||||
("VIEWPORT", WEBGL_VIEWPORT),
|
||||
("NO_ERROR", 0),
|
||||
("INVALID_ENUM", WEBGL_INVALID_ENUM),
|
||||
("INVALID_VALUE", WEBGL_INVALID_VALUE),
|
||||
("DEPTH_TEST", 0x0B71),
|
||||
("LEQUAL", 0x0203),
|
||||
@@ -193,6 +204,7 @@ pub(crate) const WEBGL_CONSTANTS: &[(&str, u32)] = &[
|
||||
("LOW_INT", 0x8DF3),
|
||||
("MEDIUM_INT", 0x8DF4),
|
||||
("HIGH_INT", 0x8DF5),
|
||||
("COMPRESSED_TEXTURE_FORMATS", 0x86A3),
|
||||
];
|
||||
|
||||
pub(crate) const WEBGL2_CONSTANTS: &[(&str, u32)] = &[
|
||||
@@ -227,7 +239,6 @@ pub(crate) const WEBGL2_CONSTANTS: &[(&str, u32)] = &[
|
||||
("MAX_SERVER_WAIT_TIMEOUT", 0x9111),
|
||||
("MAX_ELEMENT_INDEX", 0x8D6B),
|
||||
("MAX_CLIENT_WAIT_TIMEOUT_WEBGL", 0x9247),
|
||||
("COMPRESSED_TEXTURE_FORMATS", 0x86A3),
|
||||
("RENDERBUFFER", 0x8D41),
|
||||
("SAMPLES", 0x80A9),
|
||||
("FRAMEBUFFER", 0x8D40),
|
||||
@@ -364,15 +375,17 @@ pub(crate) fn webgl_get_parameter_callback<'s>(
|
||||
webgl2_get_parameter_callback(scope, args, rv);
|
||||
return;
|
||||
}
|
||||
let Some(extensions) = webgl_extensions(scope, args.this()) else {
|
||||
return;
|
||||
};
|
||||
let Some(parsed) = webidl::parse_args::<WebGlGetParameterArgs>(scope, &args) else {
|
||||
rv.set_undefined();
|
||||
return;
|
||||
};
|
||||
match parsed.pname {
|
||||
WEBGL_VIEWPORT => return_webgl_viewport(scope, args.this(), &mut rv),
|
||||
0x846D | 0x846E => rv.set(
|
||||
webgl_array_value(scope, &[1, 1]).unwrap_or_else(|| v8::Array::new(scope, 0).into()),
|
||||
),
|
||||
0x846D | 0x846E => rv.set(webgl_float32_array(scope, &[1.0, 1.0])),
|
||||
0x86A3 => rv.set(webgl_uint32_array(scope, &[])),
|
||||
0x0D3A => rv.set(webgl_int32_array(scope, &WEBGL_MAX_VIEWPORT_DIMS)),
|
||||
0x0D52..=0x0D55 => rv.set(v8::Integer::new(scope, 8).into()),
|
||||
0x0D56 => rv.set(v8::Integer::new(scope, 24).into()),
|
||||
@@ -382,11 +395,19 @@ pub(crate) fn webgl_get_parameter_callback<'s>(
|
||||
0x8872 | 0x8B4C => rv.set(v8::Integer::new(scope, 8).into()),
|
||||
0x8B4D => rv.set(v8::Integer::new(scope, 16).into()),
|
||||
0x8DFB..=0x8DFD => rv.set(v8::Integer::new(scope, 128).into()),
|
||||
0x1F00 | 0x9245 => rv.set(v8::String::empty(scope).into()),
|
||||
0x1F01 | 0x9246 => rv.set(v8::String::empty(scope).into()),
|
||||
0x1F02 => rv.set(v8::String::new(scope, "WebGL 1.0").unwrap().into()),
|
||||
0x8B8C => rv.set(v8::String::new(scope, "WebGL GLSL ES 1.0").unwrap().into()),
|
||||
_ => rv.set(v8::null(scope).into()),
|
||||
// These masked API strings match the Chromium service surface, not a
|
||||
// claim about the physical GPU. Unmasked hardware identity stays empty.
|
||||
0x1F00 => rv.set(v8str(scope, "WebKit").into()),
|
||||
0x1F01 => rv.set(v8str(scope, "WebKit WebGL").into()),
|
||||
0x9245 | 0x9246 if webgl_debug_renderer_info_enabled(scope, extensions) => {
|
||||
rv.set(v8::String::empty(scope).into())
|
||||
}
|
||||
0x1F02 => rv.set(v8str(scope, "WebGL 1.0 (OpenGL ES 2.0 Chromium)").into()),
|
||||
0x8B8C => rv.set(v8str(scope, "WebGL GLSL ES 1.0 (OpenGL ES GLSL ES 1.0 Chromium)").into()),
|
||||
_ => {
|
||||
record_webgl_error(scope, args.this(), WEBGL_INVALID_ENUM);
|
||||
rv.set_null();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -395,6 +416,9 @@ pub(crate) fn webgl2_get_parameter_callback<'s>(
|
||||
args: v8::FunctionCallbackArguments<'s>,
|
||||
mut rv: v8::ReturnValue<'_, v8::Value>,
|
||||
) {
|
||||
let Some(extensions) = webgl_extensions(scope, args.this()) else {
|
||||
return;
|
||||
};
|
||||
let Some(parsed) = webidl::parse_args::<WebGl2GetParameterArgs>(scope, &args) else {
|
||||
rv.set_undefined();
|
||||
return;
|
||||
@@ -425,7 +449,9 @@ pub(crate) fn webgl2_get_parameter_callback<'s>(
|
||||
0x8A34 => rv.set(v8::Integer::new(scope, 256).into()),
|
||||
0x1F00 => rv.set(v8str(scope, "WebKit").into()),
|
||||
0x1F01 => rv.set(v8str(scope, "WebKit WebGL").into()),
|
||||
0x9245 | 0x9246 => rv.set(v8::String::empty(scope).into()),
|
||||
0x9245 | 0x9246 if webgl_debug_renderer_info_enabled(scope, extensions) => {
|
||||
rv.set(v8::String::empty(scope).into())
|
||||
}
|
||||
0x1F02 => rv.set(v8str(scope, "WebGL 2.0 (OpenGL ES 3.0 Chromium)").into()),
|
||||
0x8B8C => {
|
||||
rv.set(v8str(scope, "WebGL GLSL ES 3.00 (OpenGL ES GLSL ES 3.0 Chromium)").into())
|
||||
@@ -433,17 +459,28 @@ pub(crate) fn webgl2_get_parameter_callback<'s>(
|
||||
0x86A3 => rv.set(webgl_uint32_array(scope, &[])),
|
||||
0x0D33 | 0x84E8 | 0x851C => rv.set(v8::Integer::new(scope, 8192).into()),
|
||||
0x0D3A => rv.set(webgl_int32_array(scope, &WEBGL_MAX_VIEWPORT_DIMS)),
|
||||
0x846D | 0x846E => rv.set(webgl_int32_array(scope, &[1, 1])),
|
||||
0x846D | 0x846E => rv.set(webgl_float32_array(scope, &[1.0, 1.0])),
|
||||
0x0D52..=0x0D55 => rv.set(v8::Integer::new(scope, 8).into()),
|
||||
0x0D56 => rv.set(v8::Integer::new(scope, 24).into()),
|
||||
0x0D57 => rv.set(v8::Integer::new(scope, 0).into()),
|
||||
0x8869 => rv.set(v8::Integer::new(scope, 16).into()),
|
||||
0x8872 | 0x8B4C => rv.set(v8::Integer::new(scope, 16).into()),
|
||||
0x8B4D => rv.set(v8::Integer::new(scope, 64).into()),
|
||||
_ => rv.set(v8::null(scope).into()),
|
||||
_ => {
|
||||
record_webgl_error(scope, args.this(), WEBGL_INVALID_ENUM);
|
||||
rv.set_null();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn webgl_debug_renderer_info_enabled(
|
||||
scope: &mut v8::PinScope<'_, '_>,
|
||||
extensions: v8::Local<'_, v8::Map>,
|
||||
) -> bool {
|
||||
let key = v8str(scope, WebGlExtension::DebugRendererInfo.into()).into();
|
||||
extensions.has(scope, key) == Some(true)
|
||||
}
|
||||
|
||||
pub(crate) fn webgl2_get_internalformat_parameter_callback<'s>(
|
||||
scope: &mut v8::PinScope<'s, '_>,
|
||||
args: v8::FunctionCallbackArguments<'s>,
|
||||
@@ -486,6 +523,21 @@ fn webgl_int32_array<'s>(
|
||||
.into()
|
||||
}
|
||||
|
||||
fn webgl_float32_array<'s>(
|
||||
scope: &mut v8::PinScope<'s, '_>,
|
||||
values: &[f32],
|
||||
) -> v8::Local<'s, v8::Value> {
|
||||
let mut bytes = Vec::with_capacity(std::mem::size_of_val(values));
|
||||
for value in values {
|
||||
bytes.extend_from_slice(&value.to_ne_bytes());
|
||||
}
|
||||
let backing_store = v8::ArrayBuffer::new_backing_store_from_vec(bytes).make_shared();
|
||||
let buffer = v8::ArrayBuffer::with_backing_store(scope, &backing_store);
|
||||
v8::Float32Array::new(scope, buffer, 0, values.len())
|
||||
.expect("WebGL Float32Array construction should succeed")
|
||||
.into()
|
||||
}
|
||||
|
||||
fn webgl_uint32_array<'s>(
|
||||
scope: &mut v8::PinScope<'s, '_>,
|
||||
values: &[u32],
|
||||
@@ -588,17 +640,35 @@ pub(crate) fn webgl_get_error_callback<'s>(
|
||||
args: v8::FunctionCallbackArguments<'s>,
|
||||
mut rv: v8::ReturnValue<'_, v8::Value>,
|
||||
) {
|
||||
let Some(error) = get_private_value(scope, args.this(), WEBGL_ERROR_SLOT) else {
|
||||
let Some(errors) = get_private_value(scope, args.this(), WEBGL_ERROR_SLOT)
|
||||
.and_then(|value| v8::Local::<v8::Set>::try_from(value).ok())
|
||||
else {
|
||||
throw_type_error(scope, "Illegal invocation");
|
||||
return;
|
||||
};
|
||||
if errors.size() == 0 {
|
||||
rv.set_uint32(0);
|
||||
return;
|
||||
}
|
||||
let error = errors
|
||||
.as_array(scope)
|
||||
.get_index(scope, 0)
|
||||
.expect("pending WebGL errors have an own first entry");
|
||||
errors.delete(scope, error);
|
||||
rv.set(error);
|
||||
set_private_value(
|
||||
scope,
|
||||
args.this(),
|
||||
WEBGL_ERROR_SLOT,
|
||||
v8::Integer::new(scope, 0).into(),
|
||||
);
|
||||
}
|
||||
|
||||
fn record_webgl_error<'s>(
|
||||
scope: &mut v8::PinScope<'s, '_>,
|
||||
context: v8::Local<'s, v8::Object>,
|
||||
error: u32,
|
||||
) {
|
||||
let errors = get_private_value(scope, context, WEBGL_ERROR_SLOT)
|
||||
.and_then(|value| v8::Local::<v8::Set>::try_from(value).ok())
|
||||
.expect("validated WebGL receiver has error state");
|
||||
// Like Blink's synthetic error list, preserve distinct pending errors but
|
||||
// coalesce repeated occurrences until getError consumes them.
|
||||
errors.add(scope, v8::Integer::new_from_unsigned(scope, error).into());
|
||||
}
|
||||
|
||||
pub(crate) fn webgl_viewport_callback<'s>(
|
||||
@@ -616,12 +686,7 @@ pub(crate) fn webgl_viewport_callback<'s>(
|
||||
if parsed.width < 0 || parsed.height < 0 {
|
||||
// GL errors preserve the previous viewport and remain pending until
|
||||
// getError consumes them. WebIDL conversion failures throw instead.
|
||||
set_private_value(
|
||||
scope,
|
||||
args.this(),
|
||||
WEBGL_ERROR_SLOT,
|
||||
v8::Integer::new_from_unsigned(scope, WEBGL_INVALID_VALUE).into(),
|
||||
);
|
||||
record_webgl_error(scope, args.this(), WEBGL_INVALID_VALUE);
|
||||
return;
|
||||
}
|
||||
set_webgl_viewport(
|
||||
@@ -675,7 +740,8 @@ pub(super) fn init_webgl_context_object<'s>(
|
||||
let values = [0, 0, 300, 150].map(|value| v8::Integer::new(scope, value).into());
|
||||
let viewport = v8::Array::new_with_elements(scope, &values);
|
||||
let extensions = v8::Map::new(scope);
|
||||
WebGlContextStateDeclaration::new(viewport, 0, extensions)
|
||||
let errors = v8::Set::new(scope);
|
||||
WebGlContextStateDeclaration::new(viewport, errors, extensions)
|
||||
.initialize(scope, context)
|
||||
.expect("WebGL context state should initialize");
|
||||
}
|
||||
@@ -778,12 +844,36 @@ pub(crate) fn webgl_is_context_lost_callback(
|
||||
rv.set(v8::Boolean::new(scope, false).into());
|
||||
}
|
||||
|
||||
pub(crate) fn webgl_get_shader_precision_format_callback(
|
||||
scope: &mut v8::PinScope<'_, '_>,
|
||||
_args: v8::FunctionCallbackArguments<'_>,
|
||||
pub(crate) fn webgl_get_shader_precision_format_callback<'s>(
|
||||
scope: &mut v8::PinScope<'s, '_>,
|
||||
args: v8::FunctionCallbackArguments<'s>,
|
||||
mut rv: v8::ReturnValue<'_, v8::Value>,
|
||||
) {
|
||||
let value = WebGlShaderPrecisionFormat::default()
|
||||
if webgl_extensions(scope, args.this()).is_none() {
|
||||
return;
|
||||
}
|
||||
let Some(parsed) = webidl::parse_args::<WebGlGetShaderPrecisionFormatArgs>(scope, &args) else {
|
||||
return;
|
||||
};
|
||||
if !matches!(parsed.shader_type, 0x8B30 | 0x8B31) {
|
||||
record_webgl_error(scope, args.this(), WEBGL_INVALID_ENUM);
|
||||
rv.set_null();
|
||||
return;
|
||||
}
|
||||
// A stable software-GL precision profile. Integer precision is always 0;
|
||||
// the floating-point mantissa width is not meaningful for integer formats.
|
||||
let (range_min, range_max, precision) = match parsed.precision_type {
|
||||
0x8DF0 | 0x8DF1 => (15, 15, 10),
|
||||
0x8DF2 => (127, 127, 23),
|
||||
0x8DF3 | 0x8DF4 => (15, 14, 0),
|
||||
0x8DF5 => (31, 30, 0),
|
||||
_ => {
|
||||
record_webgl_error(scope, args.this(), WEBGL_INVALID_ENUM);
|
||||
rv.set_null();
|
||||
return;
|
||||
}
|
||||
};
|
||||
let value = WebGlShaderPrecisionFormat::new(precision, range_min, range_max)
|
||||
.bind(scope)
|
||||
.expect("WebGL shader precision format declaration should bind");
|
||||
rv.set(value.into());
|
||||
@@ -804,16 +894,6 @@ struct WebGlShaderPrecisionFormat {
|
||||
range_max: i32,
|
||||
}
|
||||
|
||||
impl Default for WebGlShaderPrecisionFormat {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
precision: 23,
|
||||
range_min: 127,
|
||||
range_max: 127,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn webgl_lose_context_noop_callback(
|
||||
_scope: &mut v8::PinScope<'_, '_>,
|
||||
_args: v8::FunctionCallbackArguments<'_>,
|
||||
|
||||
@@ -1,5 +1,17 @@
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn webgl_basic_queries_preserve_types_extension_gates_and_errors() {
|
||||
let mut vm = new_storage_test_vm("https://webgl-query-contract.test/");
|
||||
let result = vm
|
||||
.eval(include_str!("../../../tests/fixtures/webgl-queries.js"))
|
||||
.expect("WebGL queries should preserve their types and error contracts");
|
||||
assert_eq!(
|
||||
result,
|
||||
r#"["html:webgl","offscreen:webgl","html:webgl2","offscreen:webgl2"]"#
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn webgl_extension_registry_and_context_identity_are_consistent() {
|
||||
let mut vm = new_storage_test_vm("https://webgl-extensions.test/");
|
||||
@@ -461,7 +473,7 @@ fn webgl_fingerprint_pipeline_methods_are_available() {
|
||||
|
||||
assert_eq!(
|
||||
result,
|
||||
r#"{"methods":["function","function","function","function","function"],"handles":["object","object","object","object"],"attrib":0,"constants":[34962,35044,35633,35632,5],"descriptor":[34962,true,false,false],"ownerCanvas":{"same":true,"ownEnumerable":false,"dataUrl":"string","hashTail":"SuQmCC"},"parameters":{"line":[1,1],"viewport":[8192,8192],"redBits":8,"vendor":"","version":"WebGL 1.0","missing":null},"precision":{"precision":23,"rangeMin":127,"rangeMax":127},"shaderStatus":true,"programStatus":true,"shaderLog":"","error":0}"#
|
||||
r#"{"methods":["function","function","function","function","function"],"handles":["object","object","object","object"],"attrib":0,"constants":[34962,35044,35633,35632,5],"descriptor":[34962,true,false,false],"ownerCanvas":{"same":true,"ownEnumerable":false,"dataUrl":"string","hashTail":"SuQmCC"},"parameters":{"line":[1,1],"viewport":[8192,8192],"redBits":8,"vendor":"WebKit","version":"WebGL 1.0 (OpenGL ES 2.0 Chromium)","missing":null},"precision":{"precision":23,"rangeMin":127,"rangeMax":127},"shaderStatus":true,"programStatus":true,"shaderLog":"","error":1280}"#
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -2082,6 +2082,26 @@ async fn worker_offscreen_canvas_exposes_webgl_identity_consistently() {
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn worker_webgl_queries_match_the_window_contract() {
|
||||
ensure_v8();
|
||||
let mut handle = spawn_worker(
|
||||
format!(
|
||||
"postMessage(JSON.parse({})); close();",
|
||||
include_str!("../../../../tests/fixtures/webgl-queries.js")
|
||||
),
|
||||
"test://worker_webgl_queries".into(),
|
||||
);
|
||||
let msg = timeout(TIMEOUT, handle.recv())
|
||||
.await
|
||||
.expect("timed out")
|
||||
.expect("channel closed");
|
||||
assert_eq!(
|
||||
expect_post_json(msg),
|
||||
r#"["offscreen:webgl","offscreen:webgl2"]"#
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn worker_webgl_extensions_use_intrinsic_objects_and_context_local_identity() {
|
||||
ensure_v8();
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
// No GPU-specific identity or numeric limit is assumed by this contract probe.
|
||||
(() => {
|
||||
const results = [];
|
||||
for (const kind of ['webgl', 'webgl2']) {
|
||||
for (const offscreen of typeof document === 'undefined' ? [true] : [false, true]) {
|
||||
const label = `${offscreen ? 'offscreen' : 'html'}:${kind}`;
|
||||
const check = (condition, message) => {
|
||||
if (!condition) throw new Error(`${label}: ${message}`);
|
||||
};
|
||||
const create = () => (offscreen ? new OffscreenCanvas(1, 1) : document.createElement('canvas')).getContext(kind);
|
||||
const gl = create();
|
||||
check(gl.getParameter(gl.VENDOR) === 'WebKit', 'masked vendor');
|
||||
check(gl.getParameter(gl.RENDERER) === 'WebKit WebGL', 'masked renderer');
|
||||
check(gl.getParameter(gl.VERSION).startsWith(kind === 'webgl' ? 'WebGL 1.0 (' : 'WebGL 2.0 ('), 'version format');
|
||||
check(gl.getParameter(gl.SHADING_LANGUAGE_VERSION).startsWith('WebGL GLSL ES '), 'shading language version');
|
||||
gl.getSupportedExtensions();
|
||||
check(gl.getParameter(37445) === null, 'enumerating extensions does not enable debug queries');
|
||||
check(gl.getError() === gl.INVALID_ENUM, 'disabled debug query signals an error');
|
||||
const debug = gl.getExtension('webgl_debug_renderer_info');
|
||||
for (const pname of [debug.UNMASKED_VENDOR_WEBGL, debug.UNMASKED_RENDERER_WEBGL]) {
|
||||
check(typeof gl.getParameter(pname) === 'string', 'enabled debug query returns a string');
|
||||
}
|
||||
const other = create();
|
||||
check(other.getParameter(37446) === null && other.getError() === other.INVALID_ENUM, 'activation is context-local');
|
||||
for (const pname of [gl.ALIASED_LINE_WIDTH_RANGE, gl.ALIASED_POINT_SIZE_RANGE]) {
|
||||
const range = gl.getParameter(pname);
|
||||
check(range instanceof Float32Array && range.length === 2, 'range query is Float32Array');
|
||||
const first = range[0];
|
||||
range[0] = NaN;
|
||||
check(gl.getParameter(pname)[0] === first, 'range query returns a copy');
|
||||
}
|
||||
check(gl.getParameter(gl.COMPRESSED_TEXTURE_FORMATS) instanceof Uint32Array, 'compressed formats query type');
|
||||
check(gl.getParameter(gl.MAX_VIEWPORT_DIMS) instanceof Int32Array, 'viewport limits query type');
|
||||
for (const shader of [gl.VERTEX_SHADER, gl.FRAGMENT_SHADER]) {
|
||||
for (const name of ['LOW_INT', 'MEDIUM_INT', 'HIGH_INT']) {
|
||||
const precision = gl.getShaderPrecisionFormat(shader, gl[name]);
|
||||
check(precision.precision === 0 && precision.rangeMin > 0 && precision.rangeMax > 0, 'integer precision');
|
||||
}
|
||||
check(gl.getShaderPrecisionFormat(shader, gl.HIGH_FLOAT).precision > 0, 'floating-point precision');
|
||||
}
|
||||
check(gl.getError() === gl.NO_ERROR, 'valid queries leave no error');
|
||||
for (const call of [() => gl.getParameter(), () => gl.getParameter.call({}, gl.VENDOR),
|
||||
() => gl.getShaderPrecisionFormat(gl.VERTEX_SHADER),
|
||||
() => gl.getShaderPrecisionFormat.call({}, gl.VERTEX_SHADER, gl.HIGH_FLOAT)]) {
|
||||
let threw = false;
|
||||
try { call(); } catch (error) { threw = error instanceof TypeError; }
|
||||
check(threw, 'query checks receiver and required arguments');
|
||||
}
|
||||
check(gl.getError() === gl.NO_ERROR, 'WebIDL exceptions do not create GL errors');
|
||||
check(gl.getParameter(0xffffffff) === null, 'unknown enum returns null');
|
||||
check(gl.getParameter(0xffffffff) === null, 'repeat invalid query returns null');
|
||||
gl.viewport(0, 0, -1, 1);
|
||||
check(gl.getError() === gl.INVALID_ENUM, 'first pending error');
|
||||
check(gl.getError() === gl.INVALID_VALUE, 'distinct error is preserved');
|
||||
check(gl.getError() === gl.NO_ERROR, 'repeated pending errors coalesce');
|
||||
check(gl.getShaderPrecisionFormat(0, gl.HIGH_FLOAT) === null, 'invalid shader enum');
|
||||
check(gl.getError() === gl.INVALID_ENUM, 'invalid shader enum error');
|
||||
check(gl.getShaderPrecisionFormat(gl.VERTEX_SHADER, 0) === null, 'invalid precision enum');
|
||||
check(gl.getError() === gl.INVALID_ENUM, 'invalid precision enum error');
|
||||
results.push(label);
|
||||
}
|
||||
}
|
||||
return JSON.stringify(results);
|
||||
})()
|
||||
Reference in New Issue
Block a user