diff --git a/binding/webgl_rendering_context.cc b/binding/webgl_rendering_context.cc index 16689a0..df9b699 100644 --- a/binding/webgl_rendering_context.cc +++ b/binding/webgl_rendering_context.cc @@ -507,7 +507,11 @@ static napi_status GetUniformValueParam(napi_env env, napi_value value, *result = 0; return napi_ok; } - ENSURE_VALUE_IS_NUMBER_RETVAL(env, value, napi_invalid_arg); + // WebIDL converts a uniform value with ToNumber, so `true` sets 1, as in browsers. + if (value_type != napi_number) { + nstatus = napi_coerce_to_number(env, value, &value); + ENSURE_NAPI_OK_RETVAL(env, nstatus, nstatus); + } nstatus = GetUniformNumberValue(env, value, result); ENSURE_NAPI_OK_RETVAL(env, nstatus, nstatus); @@ -1041,6 +1045,16 @@ static napi_status GetArrayLikeBuffer(napi_env env, napi_value array_like_value, nstatus = napi_get_element(env, array_like_value, i, &cur_value); ENSURE_NAPI_OK_RETVAL(env, nstatus, nstatus); + // WebIDL converts each element of a numeric sequence with ToNumber, so + // [true, false] reads as [1, 0], as in browsers. + napi_valuetype cur_type; + nstatus = napi_typeof(env, cur_value, &cur_type); + ENSURE_NAPI_OK_RETVAL(env, nstatus, nstatus); + if (cur_type != napi_number) { + nstatus = napi_coerce_to_number(env, cur_value, &cur_value); + ENSURE_NAPI_OK_RETVAL(env, nstatus, nstatus); + } + switch (alb->array_type) { case kFloat32: { double value; diff --git a/test/webgl2-smoke.js b/test/webgl2-smoke.js index 298f11d..f291d52 100644 --- a/test/webgl2-smoke.js +++ b/test/webgl2-smoke.js @@ -1404,6 +1404,52 @@ void main() { assertNoError(gl, "inactive uniform location no-op without current program"); } +// WebIDL converts a uniform value, and each element of a numeric sequence, with +// ToNumber, so browsers accept booleans and numeric strings. three.js passes +// booleans for its bool uniforms. +function testUniformValueConversion(gl) { + const program = createProgramFromSources(gl, `#version 300 es +void main() { + gl_Position = vec4(0.0, 0.0, 0.0, 1.0); +} +`, `#version 300 es +precision highp float; +uniform int u_flag; +uniform float u_scale; +uniform ivec2 u_pair; +uniform vec2 u_vector; +out vec4 out_color; +void main() { + out_color = vec4(float(u_flag) * u_scale, vec2(u_pair) + u_vector, 1.0); +} +`); + gl.useProgram(program); + const flag = gl.getUniformLocation(program, "u_flag"); + const scale = gl.getUniformLocation(program, "u_scale"); + const pair = gl.getUniformLocation(program, "u_pair"); + const vector = gl.getUniformLocation(program, "u_vector"); + + gl.uniform1i(flag, true); + assert.strictEqual(gl.getUniform(program, flag), 1, "uniform1i(true)"); + gl.uniform1i(flag, false); + assert.strictEqual(gl.getUniform(program, flag), 0, "uniform1i(false)"); + gl.uniform1i(flag, "3"); + assert.strictEqual(gl.getUniform(program, flag), 3, "uniform1i(\"3\")"); + gl.uniform1f(scale, true); + assert.strictEqual(gl.getUniform(program, scale), 1, "uniform1f(true)"); + gl.uniform2iv(pair, [true, false]); + assert.deepStrictEqual( + Array.from(gl.getUniform(program, pair)), [1, 0], "uniform2iv booleans"); + gl.uniform2fv(vector, [true, "0.5"]); + assert.deepStrictEqual( + Array.from(gl.getUniform(program, vector)), [1, 0.5], + "uniform2fv boolean and string"); + assert.throws(() => gl.uniform1i(flag, Symbol("x")), TypeError); + assert.throws(() => gl.uniform2iv(pair, [Symbol("x"), 1]), TypeError); + assertNoError(gl, "uniform value conversion"); + gl.useProgram(null); +} + function testUniformLocationProgramValidation(gl) { const vertexSource = `#version 300 es void main() { @@ -3337,6 +3383,7 @@ testPixelBufferObjectNumericOffsets(gl); testMultisampleFramebufferOperations(gl); testTexStorage2DAndMultiPixelReadback(gl); testUnsignedIntegerUniforms(gl); +testUniformValueConversion(gl); testUniformLocationProgramValidation(gl); testUniformLocationContextValidation(gl); testNonSquareUniformMatrices(gl);