diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 4400bc5..5a9e4ea 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -68,10 +68,26 @@ jobs: if: runner.os == 'Linux' && matrix.runtime_smoke != false run: xvfb-run -a npm test + - name: Smoke test three.js integration on Linux + if: runner.os == 'Linux' && matrix.runtime_smoke != false + run: xvfb-run -a npm run test:three + + - name: Smoke test twgl.js integration on Linux + if: runner.os == 'Linux' && matrix.runtime_smoke != false + run: xvfb-run -a npm run test:twgl + - name: Smoke test WebGL2 bindings if: runner.os != 'Linux' && matrix.runtime_smoke != false run: npm test + - name: Smoke test three.js integration + if: runner.os != 'Linux' && matrix.runtime_smoke != false + run: npm run test:three + + - name: Smoke test twgl.js integration + if: runner.os != 'Linux' && matrix.runtime_smoke != false + run: npm run test:twgl + - name: Skip runtime smoke if: matrix.runtime_smoke == false run: | @@ -121,3 +137,11 @@ jobs: - name: Smoke test WebGL2 bindings under WSL shell: wsl-bash {0} run: xvfb-run -a npm test + + - name: Smoke test three.js integration under WSL + shell: wsl-bash {0} + run: xvfb-run -a npm run test:three + + - name: Smoke test twgl.js integration under WSL + shell: wsl-bash {0} + run: xvfb-run -a npm run test:twgl diff --git a/binding/webgl_rendering_context.cc b/binding/webgl_rendering_context.cc index a7f8ecc..fcefe25 100644 --- a/binding/webgl_rendering_context.cc +++ b/binding/webgl_rendering_context.cc @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include @@ -39,6 +40,7 @@ namespace nodejsgl { static constexpr GLenum kUnmaskedVendorWebGL = 0x9245; static constexpr GLenum kUnmaskedRendererWebGL = 0x9246; static constexpr GLenum kMaxClientWaitTimeoutWebGL = 0x9247; +static std::atomic next_context_id{1}; // Basic type to control what byte-width the ArrayLike buffer is for cleanup. enum NodeJSGLArrayType { @@ -103,6 +105,170 @@ class ArrayLikeBuffer { } }; +struct WebGLUniformLocationHandle { + GLint location; + GLuint program; + uint64_t context_id; + uint64_t program_generation; + WebGLUniformLocationHandle *previous; + WebGLUniformLocationHandle *next; +}; + +struct UniformLocationParam { + GLint location; + WebGLUniformLocationHandle *handle; +}; + +static std::mutex uniform_location_handle_mutex; +static WebGLUniformLocationHandle *uniform_location_handle_head = nullptr; + +static void RegisterWebGLUniformLocationHandle( + WebGLUniformLocationHandle *handle) { + std::lock_guard lock(uniform_location_handle_mutex); + handle->previous = nullptr; + handle->next = uniform_location_handle_head; + if (uniform_location_handle_head != nullptr) { + uniform_location_handle_head->previous = handle; + } + uniform_location_handle_head = handle; +} + +static void UnregisterWebGLUniformLocationHandle( + WebGLUniformLocationHandle *handle) { + std::lock_guard lock(uniform_location_handle_mutex); + if (handle->previous != nullptr) { + handle->previous->next = handle->next; + } else if (uniform_location_handle_head == handle) { + uniform_location_handle_head = handle->next; + } + if (handle->next != nullptr) { + handle->next->previous = handle->previous; + } + handle->previous = nullptr; + handle->next = nullptr; +} + +static bool IsRegisteredWebGLUniformLocationHandle( + WebGLUniformLocationHandle *handle) { + std::lock_guard lock(uniform_location_handle_mutex); + for (WebGLUniformLocationHandle *current = uniform_location_handle_head; + current != nullptr; current = current->next) { + if (current == handle) { + return true; + } + } + return false; +} + +static void CleanupWebGLUniformLocation(napi_env env, void *native, + void *hint) { + WebGLUniformLocationHandle *handle = + static_cast(native); + if (handle != nullptr) { + UnregisterWebGLUniformLocationHandle(handle); + delete handle; + } +} + +static napi_status WrapWebGLUniformLocation(napi_env env, GLint location, + GLuint program, + WebGLRenderingContext *context, + napi_value *wrapped_value) { + napi_status nstatus = napi_create_object(env, wrapped_value); + ENSURE_NAPI_OK_RETVAL(env, nstatus, nstatus); + + WebGLUniformLocationHandle *handle = new (std::nothrow) + WebGLUniformLocationHandle{location, + program, + context->GetContextId(), + context->GetProgramGeneration(program), + nullptr, + nullptr}; + if (handle == nullptr) { + NAPI_THROW_ERROR(env, "Could not allocate WebGLUniformLocation"); + return napi_generic_failure; + } + + nstatus = napi_wrap(env, *wrapped_value, handle, CleanupWebGLUniformLocation, + nullptr, nullptr); + if (nstatus != napi_ok) { + delete handle; + } + ENSURE_NAPI_OK_RETVAL(env, nstatus, nstatus); + + RegisterWebGLUniformLocationHandle(handle); + return napi_ok; +} + +static napi_status GetWebGLUniformLocationHandle( + napi_env env, napi_value value, WebGLUniformLocationHandle **handle) { + void *wrapped_handle = nullptr; + napi_status nstatus = napi_unwrap(env, value, &wrapped_handle); + if (nstatus != napi_ok || wrapped_handle == nullptr) { + NAPI_THROW_ERROR(env, "location must be a WebGLUniformLocation object"); + return napi_invalid_arg; + } + + WebGLUniformLocationHandle *candidate = + static_cast(wrapped_handle); + if (!IsRegisteredWebGLUniformLocationHandle(candidate)) { + NAPI_THROW_ERROR(env, "location must be a WebGLUniformLocation object"); + return napi_invalid_arg; + } + + *handle = candidate; + return napi_ok; +} + +static napi_status GetUniformLocationParam(napi_env env, napi_value value, + UniformLocationParam *param) { + napi_valuetype value_type; + napi_status nstatus = napi_typeof(env, value, &value_type); + ENSURE_NAPI_OK_RETVAL(env, nstatus, nstatus); + + param->handle = nullptr; + if (value_type == napi_null || value_type == napi_undefined) { + param->location = -1; + return napi_ok; + } + if (value_type == napi_number) { + nstatus = napi_get_value_int32(env, value, ¶m->location); + ENSURE_NAPI_OK_RETVAL(env, nstatus, nstatus); + return napi_ok; + } + + ENSURE_VALUE_IS_OBJECT_RETVAL(env, value, napi_invalid_arg); + WebGLUniformLocationHandle *handle = nullptr; + nstatus = GetWebGLUniformLocationHandle(env, value, &handle); + ENSURE_NAPI_OK_RETVAL(env, nstatus, nstatus); + param->location = handle->location; + param->handle = handle; + return napi_ok; +} + +static bool ValidateUniformLocationForProgram(WebGLRenderingContext *context, + UniformLocationParam *param, + GLuint program) { + if (param->handle == nullptr) { + return true; + } + if (param->handle->context_id == context->GetContextId() && + param->handle->program == program && + param->handle->program_generation == + context->GetProgramGeneration(program)) { + return true; + } + context->QueueError(GL_INVALID_OPERATION); + param->location = -1; + return false; +} + +static bool ValidateUniformLocationForCurrentProgram( + WebGLRenderingContext *context, UniformLocationParam *param) { + return ValidateUniformLocationForProgram(context, param, + context->GetCurrentProgram()); +} + bool WebGLRenderingContext::CheckForErrors() { GLenum error; bool had_error = false; @@ -315,6 +481,70 @@ static napi_status GetContextDoubleParams(napi_env env, napi_callback_info info, return napi_ok; } +static napi_status GetUniformNumberValue(napi_env env, napi_value value, + double *result) { + return napi_get_value_double(env, value, result); +} + +static napi_status GetUniformNumberValue(napi_env env, napi_value value, + uint32_t *result) { + return napi_get_value_uint32(env, value, result); +} + +static napi_status GetUniformNumberValue(napi_env env, napi_value value, + int32_t *result) { + return napi_get_value_int32(env, value, result); +} + +template +static napi_status GetUniformValueParam(napi_env env, napi_value value, + T *result) { + napi_valuetype value_type; + napi_status nstatus = napi_typeof(env, value, &value_type); + ENSURE_NAPI_OK_RETVAL(env, nstatus, nstatus); + + if (value_type == napi_null || value_type == napi_undefined) { + *result = 0; + return napi_ok; + } + ENSURE_VALUE_IS_NUMBER_RETVAL(env, value, napi_invalid_arg); + + nstatus = GetUniformNumberValue(env, value, result); + ENSURE_NAPI_OK_RETVAL(env, nstatus, nstatus); + return napi_ok; +} + +template +static napi_status GetContextUniformParams(napi_env env, + napi_callback_info info, + WebGLRenderingContext **context, + size_t value_count, GLint *location, + T *values) { + napi_status nstatus; + + size_t argc = value_count + 1; + std::vector args(argc); + napi_value js_this; + nstatus = napi_get_cb_info(env, info, &argc, args.data(), &js_this, nullptr); + ENSURE_NAPI_OK_RETVAL(env, nstatus, nstatus); + ENSURE_ARGC_RETVAL(env, argc, value_count + 1, napi_invalid_arg); + + UniformLocationParam location_param; + nstatus = GetUniformLocationParam(env, args[0], &location_param); + ENSURE_NAPI_OK_RETVAL(env, nstatus, nstatus); + + for (size_t i = 0; i < value_count; ++i) { + nstatus = GetUniformValueParam(env, args[i + 1], &values[i]); + ENSURE_NAPI_OK_RETVAL(env, nstatus, nstatus); + } + + nstatus = UnwrapContext(env, js_this, context); + ENSURE_NAPI_OK_RETVAL(env, nstatus, nstatus); + ValidateUniformLocationForCurrentProgram(*context, &location_param); + *location = location_param.location; + return napi_ok; +} + template static napi_status GetNullableUint32Param(napi_env env, napi_value value, T *result) { @@ -729,10 +959,10 @@ static napi_status GetUniformMatrixParams(napi_env env, napi_callback_info info, nstatus = napi_get_cb_info(env, info, &argc, args, &js_this, nullptr); ENSURE_NAPI_OK_RETVAL(env, nstatus, nstatus); ENSURE_ARGC_RETVAL(env, argc, 3, napi_invalid_arg); - ENSURE_VALUE_IS_NUMBER_RETVAL(env, args[0], napi_invalid_arg); ENSURE_VALUE_IS_BOOLEAN_RETVAL(env, args[1], napi_invalid_arg); - nstatus = napi_get_value_int32(env, args[0], location); + UniformLocationParam location_param; + nstatus = GetUniformLocationParam(env, args[0], &location_param); ENSURE_NAPI_OK_RETVAL(env, nstatus, nstatus); bool transpose_bool; nstatus = napi_get_value_bool(env, args[1], &transpose_bool); @@ -742,6 +972,8 @@ static napi_status GetUniformMatrixParams(napi_env env, napi_callback_info info, ENSURE_NAPI_OK_RETVAL(env, nstatus, nstatus); nstatus = UnwrapContext(env, js_this, context); ENSURE_NAPI_OK_RETVAL(env, nstatus, nstatus); + ValidateUniformLocationForCurrentProgram(*context, &location_param); + *location = location_param.location; return napi_ok; } @@ -2008,6 +2240,65 @@ static void QueueWebGLErrorFromNativeFailure(EGLContextWrapper *wrapper, } } +static bool DrainNativeErrors(EGLContextWrapper *wrapper, + std::deque *pending_errors) { + bool had_error = false; + GLenum native_error = GL_NO_ERROR; + while ((native_error = wrapper->glGetError()) != GL_NO_ERROR) { + pending_errors->push_back(native_error); + had_error = true; + } + return had_error; +} + +void WebGLRenderingContext::QueueError(GLenum error) { + QueueWebGLError(eglContextWrapper_, &pending_errors_, error); +} + +GLuint WebGLRenderingContext::GetCurrentProgram() const { + GLint current_program = 0; + eglContextWrapper_->glGetIntegerv(GL_CURRENT_PROGRAM, ¤t_program); + return current_program > 0 ? static_cast(current_program) : 0; +} + +uint64_t WebGLRenderingContext::GetContextId() const { return context_id_; } + +uint64_t WebGLRenderingContext::GetProgramGeneration(GLuint program) const { + auto it = program_generations_.find(program); + return it != program_generations_.end() ? it->second : 0; +} + +void WebGLRenderingContext::RegisterProgram(GLuint program) { + if (program != 0) { + deleted_programs_.erase(program); + program_generations_[program] = next_program_generation_++; + } +} + +void WebGLRenderingContext::MarkProgramLinked(GLuint program) { + if (program != 0) { + program_generations_[program] = next_program_generation_++; + } +} + +void WebGLRenderingContext::MarkProgramDeleted(GLuint program) { + if (program == 0) { + return; + } + if (GetCurrentProgram() == program) { + deleted_programs_.insert(program); + } else { + deleted_programs_.erase(program); + program_generations_.erase(program); + } +} + +void WebGLRenderingContext::FinalizeDeletedProgramIfUnused(GLuint program) { + if (program != 0 && deleted_programs_.erase(program) > 0) { + program_generations_.erase(program); + } +} + napi_ref WebGLRenderingContext::constructor_ref_; WebGLRenderingContext::WebGLRenderingContext(napi_env env, @@ -2019,7 +2310,9 @@ WebGLRenderingContext::WebGLRenderingContext(napi_env env, unpack_color_space_("srgb"), has_enabled_extensions_filter_(opts.has_enabled_extensions_filter), enabled_extensions_(opts.enabled_extensions), - disabled_extensions_(opts.disabled_extensions) { + disabled_extensions_(opts.disabled_extensions), + context_id_(next_context_id.fetch_add(1)), + next_program_generation_(1) { eglContextWrapper_ = EGLContextWrapper::Create(env, opts); if (!eglContextWrapper_) { bool exception_pending = false; @@ -4785,6 +5078,7 @@ napi_value WebGLRenderingContext::CreateProgram(napi_env env, ENSURE_NAPI_OK_RETVAL(env, nstatus, nullptr); GLuint program = context->eglContextWrapper_->glCreateProgram(); + context->RegisterProgram(program); // TODO(kreeger): Keep track of global objects. context->alloc_count_++; @@ -5036,6 +5330,7 @@ napi_value WebGLRenderingContext::DeleteProgram(napi_env env, ENSURE_NAPI_OK_RETVAL(env, nstatus, nullptr); if (program != 0) { + context->MarkProgramDeleted(program); context->eglContextWrapper_->glDeleteProgram(program); if (context->alloc_count_.load() > 0) { context->alloc_count_--; @@ -8303,24 +8598,27 @@ napi_value WebGLRenderingContext::GetUniform(napi_env env, nstatus = GetUint32AllowNull(env, args[0], &program); ENSURE_NAPI_OK_RETVAL(env, nstatus, nullptr); - napi_valuetype location_type; - nstatus = napi_typeof(env, args[1], &location_type); + UniformLocationParam location_param; + nstatus = GetUniformLocationParam(env, args[1], &location_param); ENSURE_NAPI_OK_RETVAL(env, nstatus, nullptr); - if (location_type == napi_null || location_type == napi_undefined) { + if (location_param.location < 0) { napi_value null_value; nstatus = napi_get_null(env, &null_value); ENSURE_NAPI_OK_RETVAL(env, nstatus, nullptr); return null_value; } - ENSURE_VALUE_IS_NUMBER_RETVAL(env, args[1], nullptr); - GLint location; - nstatus = napi_get_value_int32(env, args[1], &location); - ENSURE_NAPI_OK_RETVAL(env, nstatus, nullptr); WebGLRenderingContext *context = nullptr; nstatus = UnwrapContext(env, js_this, &context); ENSURE_NAPI_OK_RETVAL(env, nstatus, nullptr); ENSURE_GL_PROC_RETVAL(env, context, glGetUniformLocation, nullptr); + if (!ValidateUniformLocationForProgram(context, &location_param, program)) { + napi_value null_value; + nstatus = napi_get_null(env, &null_value); + ENSURE_NAPI_OK_RETVAL(env, nstatus, nullptr); + return null_value; + } + GLint location = location_param.location; GLint active_uniforms = 0; GLint max_name_length = 0; @@ -8551,7 +8849,12 @@ napi_value WebGLRenderingContext::GetUniformLocation(napi_env env, program, uniform_name.c_str()); napi_value location_value; - nstatus = napi_create_int32(env, location, &location_value); + if (location < 0) { + nstatus = napi_get_null(env, &location_value); + } else { + nstatus = WrapWebGLUniformLocation(env, location, program, context, + &location_value); + } ENSURE_NAPI_OK_RETVAL(env, nstatus, nullptr); #if DEBUG @@ -9069,7 +9372,21 @@ napi_value WebGLRenderingContext::LinkProgram(napi_env env, GetContextUint32Params(env, info, &context, 1, &program); ENSURE_NAPI_OK_RETVAL(env, nstatus, nullptr); + if (context->eglContextWrapper_->glIsProgram(program) != GL_TRUE) { + context->QueueError(GL_INVALID_VALUE); + return nullptr; + } + DrainNativeErrors(context->eglContextWrapper_, &context->pending_errors_); context->eglContextWrapper_->glLinkProgram(program); + if (!DrainNativeErrors(context->eglContextWrapper_, + &context->pending_errors_)) { + GLint link_status = GL_FALSE; + context->eglContextWrapper_->glGetProgramiv(program, GL_LINK_STATUS, + &link_status); + if (link_status == GL_TRUE) { + context->MarkProgramLinked(program); + } + } #if DEBUG context->CheckForErrors(); @@ -10477,31 +10794,18 @@ napi_value WebGLRenderingContext::TransformFeedbackVaryings( napi_value WebGLRenderingContext::Uniform1i(napi_env env, napi_callback_info info) { LOG_CALL("Uniform1i"); - napi_status nstatus; - - size_t argc = 2; - napi_value args[2]; - napi_value js_this; - nstatus = napi_get_cb_info(env, info, &argc, args, &js_this, nullptr); - ENSURE_NAPI_OK_RETVAL(env, nstatus, nullptr); - ENSURE_ARGC_RETVAL(env, argc, 2, nullptr); - - ENSURE_VALUE_IS_NUMBER_RETVAL(env, args[0], nullptr); - ENSURE_VALUE_IS_NUMBER_RETVAL(env, args[1], nullptr); - - GLint location; - nstatus = napi_get_value_int32(env, args[0], &location); - ENSURE_NAPI_OK_RETVAL(env, nstatus, nullptr); - - GLint v0; - nstatus = napi_get_value_int32(env, args[1], &v0); - ENSURE_NAPI_OK_RETVAL(env, nstatus, nullptr); - + GLint location = -1; WebGLRenderingContext *context = nullptr; - nstatus = UnwrapContext(env, js_this, &context); + int32_t values[1]; + napi_status nstatus = + GetContextUniformParams(env, info, &context, 1, &location, values); ENSURE_NAPI_OK_RETVAL(env, nstatus, nullptr); - context->eglContextWrapper_->glUniform1i(location, v0); + if (location < 0) { + return nullptr; + } + + context->eglContextWrapper_->glUniform1i(location, values[0]); #if DEBUG context->CheckForErrors(); @@ -10522,11 +10826,10 @@ napi_value WebGLRenderingContext::Uniform1iv(napi_env env, ENSURE_NAPI_OK_RETVAL(env, nstatus, nullptr); ENSURE_ARGC_RETVAL(env, argc, 2, nullptr); - ENSURE_VALUE_IS_NUMBER_RETVAL(env, args[0], nullptr); - - GLint location; - nstatus = napi_get_value_int32(env, args[0], &location); + UniformLocationParam location_param; + nstatus = GetUniformLocationParam(env, args[0], &location_param); ENSURE_NAPI_OK_RETVAL(env, nstatus, nullptr); + GLint location = location_param.location; ArrayLikeBuffer alb(kInt32); nstatus = GetArrayLikeBuffer(env, args[1], &alb); @@ -10535,6 +10838,12 @@ napi_value WebGLRenderingContext::Uniform1iv(napi_env env, WebGLRenderingContext *context = nullptr; nstatus = UnwrapContext(env, js_this, &context); ENSURE_NAPI_OK_RETVAL(env, nstatus, nullptr); + ValidateUniformLocationForCurrentProgram(context, &location_param); + location = location_param.location; + + if (location < 0) { + return nullptr; + } context->eglContextWrapper_->glUniform1iv(location, static_cast(alb.size()), @@ -10550,12 +10859,17 @@ napi_value WebGLRenderingContext::Uniform1iv(napi_env env, napi_value WebGLRenderingContext::Uniform1ui(napi_env env, napi_callback_info info) { LOG_CALL("Uniform1ui"); + GLint location = -1; WebGLRenderingContext *context = nullptr; - uint32_t args[2]; - napi_status nstatus = GetContextUint32Params(env, info, &context, 2, args); + uint32_t values[1]; + napi_status nstatus = + GetContextUniformParams(env, info, &context, 1, &location, values); ENSURE_NAPI_OK_RETVAL(env, nstatus, nullptr); + if (location < 0) { + return nullptr; + } ENSURE_GL_PROC_RETVAL(env, context, glUniform1ui, nullptr); - context->eglContextWrapper_->glUniform1ui(args[0], args[1]); + context->eglContextWrapper_->glUniform1ui(location, values[0]); return nullptr; } @@ -10570,11 +10884,10 @@ napi_value WebGLRenderingContext::Uniform1uiv(napi_env env, nstatus = napi_get_cb_info(env, info, &argc, args, &js_this, nullptr); ENSURE_NAPI_OK_RETVAL(env, nstatus, nullptr); ENSURE_ARGC_RETVAL(env, argc, 2, nullptr); - ENSURE_VALUE_IS_NUMBER_RETVAL(env, args[0], nullptr); - - GLint location; - nstatus = napi_get_value_int32(env, args[0], &location); + UniformLocationParam location_param; + nstatus = GetUniformLocationParam(env, args[0], &location_param); ENSURE_NAPI_OK_RETVAL(env, nstatus, nullptr); + GLint location = location_param.location; ArrayLikeBuffer alb(kUint32); nstatus = GetArrayLikeBuffer(env, args[1], &alb); ENSURE_NAPI_OK_RETVAL(env, nstatus, nullptr); @@ -10582,6 +10895,11 @@ napi_value WebGLRenderingContext::Uniform1uiv(napi_env env, WebGLRenderingContext *context = nullptr; nstatus = UnwrapContext(env, js_this, &context); ENSURE_NAPI_OK_RETVAL(env, nstatus, nullptr); + ValidateUniformLocationForCurrentProgram(context, &location_param); + location = location_param.location; + if (location < 0) { + return nullptr; + } ENSURE_GL_PROC_RETVAL(env, context, glUniform1uiv, nullptr); context->eglContextWrapper_->glUniform1uiv( location, static_cast(alb.size()), @@ -10593,31 +10911,19 @@ napi_value WebGLRenderingContext::Uniform1uiv(napi_env env, napi_value WebGLRenderingContext::Uniform1f(napi_env env, napi_callback_info info) { LOG_CALL("Uniform1f"); - napi_status nstatus; - - size_t argc = 2; - napi_value args[2]; - napi_value js_this; - nstatus = napi_get_cb_info(env, info, &argc, args, &js_this, nullptr); - ENSURE_NAPI_OK_RETVAL(env, nstatus, nullptr); - ENSURE_ARGC_RETVAL(env, argc, 2, nullptr); - - ENSURE_VALUE_IS_NUMBER_RETVAL(env, args[0], nullptr); - ENSURE_VALUE_IS_NUMBER_RETVAL(env, args[1], nullptr); - - GLint location; - nstatus = napi_get_value_int32(env, args[0], &location); - ENSURE_NAPI_OK_RETVAL(env, nstatus, nullptr); - - double v0; - nstatus = napi_get_value_double(env, args[1], &v0); - ENSURE_NAPI_OK_RETVAL(env, nstatus, nullptr); - + GLint location = -1; WebGLRenderingContext *context = nullptr; - nstatus = UnwrapContext(env, js_this, &context); + double values[1]; + napi_status nstatus = + GetContextUniformParams(env, info, &context, 1, &location, values); ENSURE_NAPI_OK_RETVAL(env, nstatus, nullptr); - context->eglContextWrapper_->glUniform1f(location, static_cast(v0)); + if (location < 0) { + return nullptr; + } + + context->eglContextWrapper_->glUniform1f(location, + static_cast(values[0])); #if DEBUG context->CheckForErrors(); @@ -10638,11 +10944,10 @@ napi_value WebGLRenderingContext::Uniform1fv(napi_env env, ENSURE_NAPI_OK_RETVAL(env, nstatus, nullptr); ENSURE_ARGC_RETVAL(env, argc, 2, nullptr); - ENSURE_VALUE_IS_NUMBER_RETVAL(env, args[0], nullptr); - - GLint location; - nstatus = napi_get_value_int32(env, args[0], &location); + UniformLocationParam location_param; + nstatus = GetUniformLocationParam(env, args[0], &location_param); ENSURE_NAPI_OK_RETVAL(env, nstatus, nullptr); + GLint location = location_param.location; ArrayLikeBuffer alb; nstatus = GetArrayLikeBuffer(env, args[1], &alb); @@ -10651,6 +10956,12 @@ napi_value WebGLRenderingContext::Uniform1fv(napi_env env, WebGLRenderingContext *context = nullptr; nstatus = UnwrapContext(env, js_this, &context); ENSURE_NAPI_OK_RETVAL(env, nstatus, nullptr); + ValidateUniformLocationForCurrentProgram(context, &location_param); + location = location_param.location; + + if (location < 0) { + return nullptr; + } context->eglContextWrapper_->glUniform1fv( location, alb.size(), reinterpret_cast(alb.data)); @@ -10665,37 +10976,20 @@ napi_value WebGLRenderingContext::Uniform1fv(napi_env env, napi_value WebGLRenderingContext::Uniform2f(napi_env env, napi_callback_info info) { LOG_CALL("Uniform2f"); - napi_status nstatus; - - size_t argc = 3; - napi_value args[3]; - napi_value js_this; - nstatus = napi_get_cb_info(env, info, &argc, args, &js_this, nullptr); - ENSURE_NAPI_OK_RETVAL(env, nstatus, nullptr); - ENSURE_ARGC_RETVAL(env, argc, 3, nullptr); - - ENSURE_VALUE_IS_NUMBER_RETVAL(env, args[0], nullptr); - ENSURE_VALUE_IS_NUMBER_RETVAL(env, args[1], nullptr); - ENSURE_VALUE_IS_NUMBER_RETVAL(env, args[2], nullptr); - - GLint location; - nstatus = napi_get_value_int32(env, args[0], &location); - ENSURE_NAPI_OK_RETVAL(env, nstatus, nullptr); - - double v0; - nstatus = napi_get_value_double(env, args[1], &v0); - ENSURE_NAPI_OK_RETVAL(env, nstatus, nullptr); - - double v1; - nstatus = napi_get_value_double(env, args[2], &v1); - ENSURE_NAPI_OK_RETVAL(env, nstatus, nullptr); - + GLint location = -1; WebGLRenderingContext *context = nullptr; - nstatus = UnwrapContext(env, js_this, &context); + double values[2]; + napi_status nstatus = + GetContextUniformParams(env, info, &context, 2, &location, values); ENSURE_NAPI_OK_RETVAL(env, nstatus, nullptr); - context->eglContextWrapper_->glUniform2f(location, static_cast(v0), - static_cast(v1)); + if (location < 0) { + return nullptr; + } + + context->eglContextWrapper_->glUniform2f(location, + static_cast(values[0]), + static_cast(values[1])); #if DEBUG context->CheckForErrors(); @@ -10716,11 +11010,10 @@ napi_value WebGLRenderingContext::Uniform2fv(napi_env env, ENSURE_NAPI_OK_RETVAL(env, nstatus, nullptr); ENSURE_ARGC_RETVAL(env, argc, 2, nullptr); - ENSURE_VALUE_IS_NUMBER_RETVAL(env, args[0], nullptr); - - GLint location; - nstatus = napi_get_value_int32(env, args[0], &location); + UniformLocationParam location_param; + nstatus = GetUniformLocationParam(env, args[0], &location_param); ENSURE_NAPI_OK_RETVAL(env, nstatus, nullptr); + GLint location = location_param.location; ArrayLikeBuffer alb; nstatus = GetArrayLikeBuffer(env, args[1], &alb); @@ -10729,6 +11022,12 @@ napi_value WebGLRenderingContext::Uniform2fv(napi_env env, WebGLRenderingContext *context = nullptr; nstatus = UnwrapContext(env, js_this, &context); ENSURE_NAPI_OK_RETVAL(env, nstatus, nullptr); + ValidateUniformLocationForCurrentProgram(context, &location_param); + location = location_param.location; + + if (location < 0) { + return nullptr; + } context->eglContextWrapper_->glUniform2fv( location, static_cast(alb.size() >> 1), @@ -10744,14 +11043,18 @@ napi_value WebGLRenderingContext::Uniform2fv(napi_env env, napi_value WebGLRenderingContext::Uniform2i(napi_env env, napi_callback_info info) { LOG_CALL("Uniform2i"); - napi_status nstatus; - + GLint location = -1; WebGLRenderingContext *context = nullptr; - int32_t args[3]; - nstatus = GetContextInt32Params(env, info, &context, 3, args); + int32_t values[2]; + napi_status nstatus = + GetContextUniformParams(env, info, &context, 2, &location, values); ENSURE_NAPI_OK_RETVAL(env, nstatus, nullptr); - context->eglContextWrapper_->glUniform2i(args[0], args[1], args[2]); + if (location < 0) { + return nullptr; + } + + context->eglContextWrapper_->glUniform2i(location, values[0], values[1]); #if DEBUG context->CheckForErrors(); @@ -10772,11 +11075,10 @@ napi_value WebGLRenderingContext::Uniform2iv(napi_env env, ENSURE_NAPI_OK_RETVAL(env, nstatus, nullptr); ENSURE_ARGC_RETVAL(env, argc, 2, nullptr); - ENSURE_VALUE_IS_NUMBER_RETVAL(env, args[0], nullptr); - - GLint location; - nstatus = napi_get_value_int32(env, args[0], &location); + UniformLocationParam location_param; + nstatus = GetUniformLocationParam(env, args[0], &location_param); ENSURE_NAPI_OK_RETVAL(env, nstatus, nullptr); + GLint location = location_param.location; ArrayLikeBuffer alb(kInt32); nstatus = GetArrayLikeBuffer(env, args[1], &alb); @@ -10785,6 +11087,12 @@ napi_value WebGLRenderingContext::Uniform2iv(napi_env env, WebGLRenderingContext *context = nullptr; nstatus = UnwrapContext(env, js_this, &context); ENSURE_NAPI_OK_RETVAL(env, nstatus, nullptr); + ValidateUniformLocationForCurrentProgram(context, &location_param); + location = location_param.location; + + if (location < 0) { + return nullptr; + } context->eglContextWrapper_->glUniform2iv( location, static_cast(alb.size() >> 1), @@ -10800,12 +11108,17 @@ napi_value WebGLRenderingContext::Uniform2iv(napi_env env, napi_value WebGLRenderingContext::Uniform2ui(napi_env env, napi_callback_info info) { LOG_CALL("Uniform2ui"); + GLint location = -1; WebGLRenderingContext *context = nullptr; - uint32_t args[3]; - napi_status nstatus = GetContextUint32Params(env, info, &context, 3, args); + uint32_t values[2]; + napi_status nstatus = + GetContextUniformParams(env, info, &context, 2, &location, values); ENSURE_NAPI_OK_RETVAL(env, nstatus, nullptr); + if (location < 0) { + return nullptr; + } ENSURE_GL_PROC_RETVAL(env, context, glUniform2ui, nullptr); - context->eglContextWrapper_->glUniform2ui(args[0], args[1], args[2]); + context->eglContextWrapper_->glUniform2ui(location, values[0], values[1]); return nullptr; } @@ -10820,16 +11133,21 @@ napi_value WebGLRenderingContext::Uniform2uiv(napi_env env, nstatus = napi_get_cb_info(env, info, &argc, args, &js_this, nullptr); ENSURE_NAPI_OK_RETVAL(env, nstatus, nullptr); ENSURE_ARGC_RETVAL(env, argc, 2, nullptr); - ENSURE_VALUE_IS_NUMBER_RETVAL(env, args[0], nullptr); - GLint location; - nstatus = napi_get_value_int32(env, args[0], &location); + UniformLocationParam location_param; + nstatus = GetUniformLocationParam(env, args[0], &location_param); ENSURE_NAPI_OK_RETVAL(env, nstatus, nullptr); + GLint location = location_param.location; ArrayLikeBuffer alb(kUint32); nstatus = GetArrayLikeBuffer(env, args[1], &alb); ENSURE_NAPI_OK_RETVAL(env, nstatus, nullptr); WebGLRenderingContext *context = nullptr; nstatus = UnwrapContext(env, js_this, &context); ENSURE_NAPI_OK_RETVAL(env, nstatus, nullptr); + ValidateUniformLocationForCurrentProgram(context, &location_param); + location = location_param.location; + if (location < 0) { + return nullptr; + } ENSURE_GL_PROC_RETVAL(env, context, glUniform2uiv, nullptr); context->eglContextWrapper_->glUniform2uiv( location, static_cast(alb.size() >> 1), @@ -10841,13 +11159,19 @@ napi_value WebGLRenderingContext::Uniform2uiv(napi_env env, napi_value WebGLRenderingContext::Uniform3i(napi_env env, napi_callback_info info) { LOG_CALL("Uniform3i"); - - GLint args[4]; + GLint location = -1; WebGLRenderingContext *context = nullptr; - napi_status nstatus = GetContextInt32Params(env, info, &context, 4, args); + int32_t values[3]; + napi_status nstatus = + GetContextUniformParams(env, info, &context, 3, &location, values); ENSURE_NAPI_OK_RETVAL(env, nstatus, nullptr); - context->eglContextWrapper_->glUniform3i(args[0], args[1], args[2], args[3]); + if (location < 0) { + return nullptr; + } + + context->eglContextWrapper_->glUniform3i(location, values[0], values[1], + values[2]); #if DEBUG context->CheckForErrors(); @@ -10868,11 +11192,10 @@ napi_value WebGLRenderingContext::Uniform3iv(napi_env env, ENSURE_NAPI_OK_RETVAL(env, nstatus, nullptr); ENSURE_ARGC_RETVAL(env, argc, 2, nullptr); - ENSURE_VALUE_IS_NUMBER_RETVAL(env, args[0], nullptr); - - GLint location; - nstatus = napi_get_value_int32(env, args[0], &location); + UniformLocationParam location_param; + nstatus = GetUniformLocationParam(env, args[0], &location_param); ENSURE_NAPI_OK_RETVAL(env, nstatus, nullptr); + GLint location = location_param.location; ArrayLikeBuffer alb(kInt32); nstatus = GetArrayLikeBuffer(env, args[1], &alb); @@ -10881,6 +11204,12 @@ napi_value WebGLRenderingContext::Uniform3iv(napi_env env, WebGLRenderingContext *context = nullptr; nstatus = UnwrapContext(env, js_this, &context); ENSURE_NAPI_OK_RETVAL(env, nstatus, nullptr); + ValidateUniformLocationForCurrentProgram(context, &location_param); + location = location_param.location; + + if (location < 0) { + return nullptr; + } context->eglContextWrapper_->glUniform3iv( location, static_cast(alb.size() / 3), @@ -10896,43 +11225,20 @@ napi_value WebGLRenderingContext::Uniform3iv(napi_env env, napi_value WebGLRenderingContext::Uniform3f(napi_env env, napi_callback_info info) { LOG_CALL("Uniform3f"); - napi_status nstatus; - - size_t argc = 4; - napi_value args[4]; - napi_value js_this; - nstatus = napi_get_cb_info(env, info, &argc, args, &js_this, nullptr); - ENSURE_NAPI_OK_RETVAL(env, nstatus, nullptr); - ENSURE_ARGC_RETVAL(env, argc, 4, nullptr); - - ENSURE_VALUE_IS_NUMBER_RETVAL(env, args[0], nullptr); - ENSURE_VALUE_IS_NUMBER_RETVAL(env, args[1], nullptr); - ENSURE_VALUE_IS_NUMBER_RETVAL(env, args[2], nullptr); - ENSURE_VALUE_IS_NUMBER_RETVAL(env, args[3], nullptr); - - GLint location; - nstatus = napi_get_value_int32(env, args[0], &location); - ENSURE_NAPI_OK_RETVAL(env, nstatus, nullptr); - - double v0; - nstatus = napi_get_value_double(env, args[1], &v0); - ENSURE_NAPI_OK_RETVAL(env, nstatus, nullptr); - - double v1; - nstatus = napi_get_value_double(env, args[2], &v1); - ENSURE_NAPI_OK_RETVAL(env, nstatus, nullptr); - - double v2; - nstatus = napi_get_value_double(env, args[3], &v2); - ENSURE_NAPI_OK_RETVAL(env, nstatus, nullptr); - + GLint location = -1; WebGLRenderingContext *context = nullptr; - nstatus = UnwrapContext(env, js_this, &context); + double values[3]; + napi_status nstatus = + GetContextUniformParams(env, info, &context, 3, &location, values); ENSURE_NAPI_OK_RETVAL(env, nstatus, nullptr); - context->eglContextWrapper_->glUniform3f(location, static_cast(v0), - static_cast(v1), - static_cast(v2)); + if (location < 0) { + return nullptr; + } + + context->eglContextWrapper_->glUniform3f( + location, static_cast(values[0]), + static_cast(values[1]), static_cast(values[2])); #if DEBUG context->CheckForErrors(); @@ -10953,11 +11259,10 @@ napi_value WebGLRenderingContext::Uniform3fv(napi_env env, ENSURE_NAPI_OK_RETVAL(env, nstatus, nullptr); ENSURE_ARGC_RETVAL(env, argc, 2, nullptr); - ENSURE_VALUE_IS_NUMBER_RETVAL(env, args[0], nullptr); - - GLint location; - nstatus = napi_get_value_int32(env, args[0], &location); + UniformLocationParam location_param; + nstatus = GetUniformLocationParam(env, args[0], &location_param); ENSURE_NAPI_OK_RETVAL(env, nstatus, nullptr); + GLint location = location_param.location; ArrayLikeBuffer alb; nstatus = GetArrayLikeBuffer(env, args[1], &alb); @@ -10966,6 +11271,12 @@ napi_value WebGLRenderingContext::Uniform3fv(napi_env env, WebGLRenderingContext *context = nullptr; nstatus = UnwrapContext(env, js_this, &context); ENSURE_NAPI_OK_RETVAL(env, nstatus, nullptr); + ValidateUniformLocationForCurrentProgram(context, &location_param); + location = location_param.location; + + if (location < 0) { + return nullptr; + } context->eglContextWrapper_->glUniform3fv( location, static_cast(alb.size() / 3), @@ -10981,12 +11292,18 @@ napi_value WebGLRenderingContext::Uniform3fv(napi_env env, napi_value WebGLRenderingContext::Uniform3ui(napi_env env, napi_callback_info info) { LOG_CALL("Uniform3ui"); + GLint location = -1; WebGLRenderingContext *context = nullptr; - uint32_t args[4]; - napi_status nstatus = GetContextUint32Params(env, info, &context, 4, args); + uint32_t values[3]; + napi_status nstatus = + GetContextUniformParams(env, info, &context, 3, &location, values); ENSURE_NAPI_OK_RETVAL(env, nstatus, nullptr); + if (location < 0) { + return nullptr; + } ENSURE_GL_PROC_RETVAL(env, context, glUniform3ui, nullptr); - context->eglContextWrapper_->glUniform3ui(args[0], args[1], args[2], args[3]); + context->eglContextWrapper_->glUniform3ui(location, values[0], values[1], + values[2]); return nullptr; } @@ -11001,16 +11318,21 @@ napi_value WebGLRenderingContext::Uniform3uiv(napi_env env, nstatus = napi_get_cb_info(env, info, &argc, args, &js_this, nullptr); ENSURE_NAPI_OK_RETVAL(env, nstatus, nullptr); ENSURE_ARGC_RETVAL(env, argc, 2, nullptr); - ENSURE_VALUE_IS_NUMBER_RETVAL(env, args[0], nullptr); - GLint location; - nstatus = napi_get_value_int32(env, args[0], &location); + UniformLocationParam location_param; + nstatus = GetUniformLocationParam(env, args[0], &location_param); ENSURE_NAPI_OK_RETVAL(env, nstatus, nullptr); + GLint location = location_param.location; ArrayLikeBuffer alb(kUint32); nstatus = GetArrayLikeBuffer(env, args[1], &alb); ENSURE_NAPI_OK_RETVAL(env, nstatus, nullptr); WebGLRenderingContext *context = nullptr; nstatus = UnwrapContext(env, js_this, &context); ENSURE_NAPI_OK_RETVAL(env, nstatus, nullptr); + ValidateUniformLocationForCurrentProgram(context, &location_param); + location = location_param.location; + if (location < 0) { + return nullptr; + } ENSURE_GL_PROC_RETVAL(env, context, glUniform3uiv, nullptr); context->eglContextWrapper_->glUniform3uiv( location, static_cast(alb.size() / 3), @@ -11031,11 +11353,10 @@ napi_value WebGLRenderingContext::Uniform4fv(napi_env env, ENSURE_NAPI_OK_RETVAL(env, nstatus, nullptr); ENSURE_ARGC_RETVAL(env, argc, 2, nullptr); - ENSURE_VALUE_IS_NUMBER_RETVAL(env, args[0], nullptr); - - GLint location; - nstatus = napi_get_value_int32(env, args[0], &location); + UniformLocationParam location_param; + nstatus = GetUniformLocationParam(env, args[0], &location_param); ENSURE_NAPI_OK_RETVAL(env, nstatus, nullptr); + GLint location = location_param.location; ArrayLikeBuffer alb; nstatus = GetArrayLikeBuffer(env, args[1], &alb); @@ -11044,6 +11365,12 @@ napi_value WebGLRenderingContext::Uniform4fv(napi_env env, WebGLRenderingContext *context = nullptr; nstatus = UnwrapContext(env, js_this, &context); ENSURE_NAPI_OK_RETVAL(env, nstatus, nullptr); + ValidateUniformLocationForCurrentProgram(context, &location_param); + location = location_param.location; + + if (location < 0) { + return nullptr; + } context->eglContextWrapper_->glUniform4fv( location, static_cast(alb.size() >> 2), @@ -11059,15 +11386,19 @@ napi_value WebGLRenderingContext::Uniform4fv(napi_env env, napi_value WebGLRenderingContext::Uniform4i(napi_env env, napi_callback_info info) { LOG_CALL("Uniform4i"); - napi_status nstatus; - + GLint location = -1; WebGLRenderingContext *context = nullptr; - int32_t args[5]; - nstatus = GetContextInt32Params(env, info, &context, 5, args); + int32_t values[4]; + napi_status nstatus = + GetContextUniformParams(env, info, &context, 4, &location, values); ENSURE_NAPI_OK_RETVAL(env, nstatus, nullptr); - context->eglContextWrapper_->glUniform4i(args[0], args[1], args[2], args[3], - args[4]); + if (location < 0) { + return nullptr; + } + + context->eglContextWrapper_->glUniform4i(location, values[0], values[1], + values[2], values[3]); #if DEBUG context->CheckForErrors(); @@ -11088,11 +11419,10 @@ napi_value WebGLRenderingContext::Uniform4iv(napi_env env, ENSURE_NAPI_OK_RETVAL(env, nstatus, nullptr); ENSURE_ARGC_RETVAL(env, argc, 2, nullptr); - ENSURE_VALUE_IS_NUMBER_RETVAL(env, args[0], nullptr); - - GLint location; - nstatus = napi_get_value_int32(env, args[0], &location); + UniformLocationParam location_param; + nstatus = GetUniformLocationParam(env, args[0], &location_param); ENSURE_NAPI_OK_RETVAL(env, nstatus, nullptr); + GLint location = location_param.location; ArrayLikeBuffer alb(kInt32); nstatus = GetArrayLikeBuffer(env, args[1], &alb); @@ -11101,6 +11431,12 @@ napi_value WebGLRenderingContext::Uniform4iv(napi_env env, WebGLRenderingContext *context = nullptr; nstatus = UnwrapContext(env, js_this, &context); ENSURE_NAPI_OK_RETVAL(env, nstatus, nullptr); + ValidateUniformLocationForCurrentProgram(context, &location_param); + location = location_param.location; + + if (location < 0) { + return nullptr; + } context->eglContextWrapper_->glUniform4iv( location, static_cast(alb.size() >> 2), @@ -11116,48 +11452,21 @@ napi_value WebGLRenderingContext::Uniform4iv(napi_env env, napi_value WebGLRenderingContext::Uniform4f(napi_env env, napi_callback_info info) { LOG_CALL("Uniform4f"); - napi_status nstatus; - - size_t argc = 5; - napi_value args[5]; - napi_value js_this; - nstatus = napi_get_cb_info(env, info, &argc, args, &js_this, nullptr); - ENSURE_NAPI_OK_RETVAL(env, nstatus, nullptr); - ENSURE_ARGC_RETVAL(env, argc, 5, nullptr); - + GLint location = -1; WebGLRenderingContext *context = nullptr; - nstatus = UnwrapContext(env, js_this, &context); - ENSURE_NAPI_OK_RETVAL(env, nstatus, nullptr); - - ENSURE_VALUE_IS_NUMBER_RETVAL(env, args[0], nullptr); - ENSURE_VALUE_IS_NUMBER_RETVAL(env, args[1], nullptr); - ENSURE_VALUE_IS_NUMBER_RETVAL(env, args[2], nullptr); - ENSURE_VALUE_IS_NUMBER_RETVAL(env, args[3], nullptr); - ENSURE_VALUE_IS_NUMBER_RETVAL(env, args[4], nullptr); - - GLint location; - nstatus = napi_get_value_int32(env, args[0], &location); - ENSURE_NAPI_OK_RETVAL(env, nstatus, nullptr); - - double v0; - nstatus = napi_get_value_double(env, args[1], &v0); - ENSURE_NAPI_OK_RETVAL(env, nstatus, nullptr); - - double v1; - nstatus = napi_get_value_double(env, args[2], &v1); - ENSURE_NAPI_OK_RETVAL(env, nstatus, nullptr); - - double v2; - nstatus = napi_get_value_double(env, args[3], &v2); + double values[4]; + napi_status nstatus = + GetContextUniformParams(env, info, &context, 4, &location, values); ENSURE_NAPI_OK_RETVAL(env, nstatus, nullptr); - double v3; - nstatus = napi_get_value_double(env, args[4], &v3); - ENSURE_NAPI_OK_RETVAL(env, nstatus, nullptr); + if (location < 0) { + return nullptr; + } context->eglContextWrapper_->glUniform4f( - location, static_cast(v0), static_cast(v1), - static_cast(v2), static_cast(v3)); + location, static_cast(values[0]), + static_cast(values[1]), static_cast(values[2]), + static_cast(values[3])); #if DEBUG context->CheckForErrors(); @@ -11169,13 +11478,18 @@ napi_value WebGLRenderingContext::Uniform4f(napi_env env, napi_value WebGLRenderingContext::Uniform4ui(napi_env env, napi_callback_info info) { LOG_CALL("Uniform4ui"); + GLint location = -1; WebGLRenderingContext *context = nullptr; - uint32_t args[5]; - napi_status nstatus = GetContextUint32Params(env, info, &context, 5, args); + uint32_t values[4]; + napi_status nstatus = + GetContextUniformParams(env, info, &context, 4, &location, values); ENSURE_NAPI_OK_RETVAL(env, nstatus, nullptr); + if (location < 0) { + return nullptr; + } ENSURE_GL_PROC_RETVAL(env, context, glUniform4ui, nullptr); - context->eglContextWrapper_->glUniform4ui(args[0], args[1], args[2], args[3], - args[4]); + context->eglContextWrapper_->glUniform4ui(location, values[0], values[1], + values[2], values[3]); return nullptr; } @@ -11190,16 +11504,21 @@ napi_value WebGLRenderingContext::Uniform4uiv(napi_env env, nstatus = napi_get_cb_info(env, info, &argc, args, &js_this, nullptr); ENSURE_NAPI_OK_RETVAL(env, nstatus, nullptr); ENSURE_ARGC_RETVAL(env, argc, 2, nullptr); - ENSURE_VALUE_IS_NUMBER_RETVAL(env, args[0], nullptr); - GLint location; - nstatus = napi_get_value_int32(env, args[0], &location); + UniformLocationParam location_param; + nstatus = GetUniformLocationParam(env, args[0], &location_param); ENSURE_NAPI_OK_RETVAL(env, nstatus, nullptr); + GLint location = location_param.location; ArrayLikeBuffer alb(kUint32); nstatus = GetArrayLikeBuffer(env, args[1], &alb); ENSURE_NAPI_OK_RETVAL(env, nstatus, nullptr); WebGLRenderingContext *context = nullptr; nstatus = UnwrapContext(env, js_this, &context); ENSURE_NAPI_OK_RETVAL(env, nstatus, nullptr); + ValidateUniformLocationForCurrentProgram(context, &location_param); + location = location_param.location; + if (location < 0) { + return nullptr; + } ENSURE_GL_PROC_RETVAL(env, context, glUniform4uiv, nullptr); context->eglContextWrapper_->glUniform4uiv( location, static_cast(alb.size() >> 2), @@ -11236,12 +11555,12 @@ napi_value WebGLRenderingContext::UniformMatrix2fv(napi_env env, nstatus = napi_get_cb_info(env, info, &argc, args, &js_this, nullptr); ENSURE_NAPI_OK_RETVAL(env, nstatus, nullptr); - ENSURE_VALUE_IS_NUMBER_RETVAL(env, args[0], nullptr); ENSURE_VALUE_IS_BOOLEAN_RETVAL(env, args[1], nullptr); - GLint location; - nstatus = napi_get_value_int32(env, args[0], &location); + UniformLocationParam location_param; + nstatus = GetUniformLocationParam(env, args[0], &location_param); ENSURE_NAPI_OK_RETVAL(env, nstatus, nullptr); + GLint location = location_param.location; bool transpose; nstatus = napi_get_value_bool(env, args[1], &transpose); @@ -11254,6 +11573,12 @@ napi_value WebGLRenderingContext::UniformMatrix2fv(napi_env env, WebGLRenderingContext *context = nullptr; nstatus = UnwrapContext(env, js_this, &context); ENSURE_NAPI_OK_RETVAL(env, nstatus, nullptr); + ValidateUniformLocationForCurrentProgram(context, &location_param); + location = location_param.location; + + if (location < 0) { + return nullptr; + } context->eglContextWrapper_->glUniformMatrix2fv( location, static_cast(alb.size() >> 2), @@ -11277,6 +11602,9 @@ napi_value WebGLRenderingContext::UniformMatrix2x3fv(napi_env env, napi_status nstatus = GetUniformMatrixParams(env, info, &context, &location, &transpose, &alb); ENSURE_NAPI_OK_RETVAL(env, nstatus, nullptr); + if (location < 0) { + return nullptr; + } ENSURE_GL_PROC_RETVAL(env, context, glUniformMatrix2x3fv, nullptr); context->eglContextWrapper_->glUniformMatrix2x3fv( location, static_cast(alb.size() / 6), transpose, @@ -11295,6 +11623,9 @@ napi_value WebGLRenderingContext::UniformMatrix2x4fv(napi_env env, napi_status nstatus = GetUniformMatrixParams(env, info, &context, &location, &transpose, &alb); ENSURE_NAPI_OK_RETVAL(env, nstatus, nullptr); + if (location < 0) { + return nullptr; + } ENSURE_GL_PROC_RETVAL(env, context, glUniformMatrix2x4fv, nullptr); context->eglContextWrapper_->glUniformMatrix2x4fv( location, static_cast(alb.size() / 8), transpose, @@ -11315,12 +11646,12 @@ napi_value WebGLRenderingContext::UniformMatrix3fv(napi_env env, nstatus = napi_get_cb_info(env, info, &argc, args, &js_this, nullptr); ENSURE_NAPI_OK_RETVAL(env, nstatus, nullptr); - ENSURE_VALUE_IS_NUMBER_RETVAL(env, args[0], nullptr); ENSURE_VALUE_IS_BOOLEAN_RETVAL(env, args[1], nullptr); - GLint location; - nstatus = napi_get_value_int32(env, args[0], &location); + UniformLocationParam location_param; + nstatus = GetUniformLocationParam(env, args[0], &location_param); ENSURE_NAPI_OK_RETVAL(env, nstatus, nullptr); + GLint location = location_param.location; bool transpose; nstatus = napi_get_value_bool(env, args[1], &transpose); @@ -11333,6 +11664,12 @@ napi_value WebGLRenderingContext::UniformMatrix3fv(napi_env env, WebGLRenderingContext *context = nullptr; nstatus = UnwrapContext(env, js_this, &context); ENSURE_NAPI_OK_RETVAL(env, nstatus, nullptr); + ValidateUniformLocationForCurrentProgram(context, &location_param); + location = location_param.location; + + if (location < 0) { + return nullptr; + } context->eglContextWrapper_->glUniformMatrix3fv( location, static_cast(alb.size() / 9), @@ -11356,6 +11693,9 @@ napi_value WebGLRenderingContext::UniformMatrix3x2fv(napi_env env, napi_status nstatus = GetUniformMatrixParams(env, info, &context, &location, &transpose, &alb); ENSURE_NAPI_OK_RETVAL(env, nstatus, nullptr); + if (location < 0) { + return nullptr; + } ENSURE_GL_PROC_RETVAL(env, context, glUniformMatrix3x2fv, nullptr); context->eglContextWrapper_->glUniformMatrix3x2fv( location, static_cast(alb.size() / 6), transpose, @@ -11374,6 +11714,9 @@ napi_value WebGLRenderingContext::UniformMatrix3x4fv(napi_env env, napi_status nstatus = GetUniformMatrixParams(env, info, &context, &location, &transpose, &alb); ENSURE_NAPI_OK_RETVAL(env, nstatus, nullptr); + if (location < 0) { + return nullptr; + } ENSURE_GL_PROC_RETVAL(env, context, glUniformMatrix3x4fv, nullptr); context->eglContextWrapper_->glUniformMatrix3x4fv( location, static_cast(alb.size() / 12), transpose, @@ -11394,12 +11737,12 @@ napi_value WebGLRenderingContext::UniformMatrix4fv(napi_env env, nstatus = napi_get_cb_info(env, info, &argc, args, &js_this, nullptr); ENSURE_NAPI_OK_RETVAL(env, nstatus, nullptr); - ENSURE_VALUE_IS_NUMBER_RETVAL(env, args[0], nullptr); ENSURE_VALUE_IS_BOOLEAN_RETVAL(env, args[1], nullptr); - GLint location; - nstatus = napi_get_value_int32(env, args[0], &location); + UniformLocationParam location_param; + nstatus = GetUniformLocationParam(env, args[0], &location_param); ENSURE_NAPI_OK_RETVAL(env, nstatus, nullptr); + GLint location = location_param.location; bool transpose; nstatus = napi_get_value_bool(env, args[1], &transpose); @@ -11412,6 +11755,12 @@ napi_value WebGLRenderingContext::UniformMatrix4fv(napi_env env, WebGLRenderingContext *context = nullptr; nstatus = UnwrapContext(env, js_this, &context); ENSURE_NAPI_OK_RETVAL(env, nstatus, nullptr); + ValidateUniformLocationForCurrentProgram(context, &location_param); + location = location_param.location; + + if (location < 0) { + return nullptr; + } context->eglContextWrapper_->glUniformMatrix4fv( location, static_cast(alb.size() >> 4), @@ -11435,6 +11784,9 @@ napi_value WebGLRenderingContext::UniformMatrix4x2fv(napi_env env, napi_status nstatus = GetUniformMatrixParams(env, info, &context, &location, &transpose, &alb); ENSURE_NAPI_OK_RETVAL(env, nstatus, nullptr); + if (location < 0) { + return nullptr; + } ENSURE_GL_PROC_RETVAL(env, context, glUniformMatrix4x2fv, nullptr); context->eglContextWrapper_->glUniformMatrix4x2fv( location, static_cast(alb.size() / 8), transpose, @@ -11453,6 +11805,9 @@ napi_value WebGLRenderingContext::UniformMatrix4x3fv(napi_env env, napi_status nstatus = GetUniformMatrixParams(env, info, &context, &location, &transpose, &alb); ENSURE_NAPI_OK_RETVAL(env, nstatus, nullptr); + if (location < 0) { + return nullptr; + } ENSURE_GL_PROC_RETVAL(env, context, glUniformMatrix4x3fv, nullptr); context->eglContextWrapper_->glUniformMatrix4x3fv( location, static_cast(alb.size() / 12), transpose, @@ -11471,7 +11826,11 @@ napi_value WebGLRenderingContext::UseProgram(napi_env env, GetContextUint32Params(env, info, &context, 1, &program); ENSURE_NAPI_OK_RETVAL(env, nstatus, nullptr); + GLuint previous_program = context->GetCurrentProgram(); context->eglContextWrapper_->glUseProgram(program); + if (context->GetCurrentProgram() != previous_program) { + context->FinalizeDeletedProgramIfUnused(previous_program); + } #if DEBUG context->CheckForErrors(); diff --git a/binding/webgl_rendering_context.h b/binding/webgl_rendering_context.h index 08a005f..26e7406 100644 --- a/binding/webgl_rendering_context.h +++ b/binding/webgl_rendering_context.h @@ -21,8 +21,11 @@ #include #include +#include #include #include +#include +#include #include #include "egl_context_wrapper.h" @@ -54,12 +57,20 @@ class WebGLRenderingContext { napi_value context_value); bool HasNativeResources() const; bool EnsureNativeContextCurrent() const; + void QueueError(GLenum error); + GLuint GetCurrentProgram() const; + uint64_t GetContextId() const; + uint64_t GetProgramGeneration(GLuint program) const; private: WebGLRenderingContext(napi_env env, GLContextOptions opts); ~WebGLRenderingContext(); void DestroyNativeResources(); bool IsExtensionAllowed(const char* extension_name) const; + void RegisterProgram(GLuint program); + void MarkProgramLinked(GLuint program); + void MarkProgramDeleted(GLuint program); + void FinalizeDeletedProgramIfUnused(GLuint program); static napi_value InitInternal(napi_env env, napi_callback_info info); static void Cleanup(napi_env env, void* native, void* hint); @@ -351,6 +362,10 @@ class WebGLRenderingContext { bool has_enabled_extensions_filter_; std::vector enabled_extensions_; std::vector disabled_extensions_; + uint64_t context_id_; + uint64_t next_program_generation_; + std::unordered_map program_generations_; + std::unordered_set deleted_programs_; std::atomic alloc_count_; }; diff --git a/package.json b/package.json index fd2f9b6..3fc1092 100644 --- a/package.json +++ b/package.json @@ -42,6 +42,8 @@ "scripts/clean-dist.js", "scripts/install.js", "test/regl-smoke.js", + "test/three-smoke.js", + "test/twgl-smoke.js", "test/visual-3d-texture-arrays.js", "test/webgl2-smoke.js", "LICENSE", @@ -55,6 +57,8 @@ "install": "node scripts/install.js", "test": "npm run test:webgl2 && npm run test:regl", "test:regl": "node test/regl-smoke.js", + "test:three": "node test/three-smoke.js", + "test:twgl": "node test/twgl-smoke.js", "visual:3d-textures": "node test/visual-3d-texture-arrays.js", "test:webgl2": "node test/webgl2-smoke.js" }, @@ -64,6 +68,8 @@ "@types/progress": "2.0.3", "@types/webgl2": "0.0.4", "regl": "2.1.1", + "three": "0.184.0", + "twgl.js": "7.0.0", "typescript": "3.0.3" }, "dependencies": { diff --git a/test/three-smoke.js b/test/three-smoke.js new file mode 100644 index 0000000..7d8f000 --- /dev/null +++ b/test/three-smoke.js @@ -0,0 +1,146 @@ +"use strict"; + +const assert = require("assert"); +const THREE = require("three"); +const nodeGles = require(".."); + +const WIDTH = 96; +const HEIGHT = 48; + +function createContext() { + return nodeGles.createWebGLRenderingContext({ + width: WIDTH, + height: HEIGHT, + majorVersion: 3, + minorVersion: 0 + }); +} + +function createCanvas(gl) { + const canvas = { + width: WIDTH, + height: HEIGHT, + style: {}, + addEventListener() {}, + removeEventListener() {}, + getContext(type) { + if (type === "webgl2" || type === "webgl" || + type === "experimental-webgl") { + return gl; + } + return null; + } + }; + gl.canvas = canvas; + return canvas; +} + +function readDefaultPixel(gl, x, y) { + const pixel = new Uint8Array(4); + gl.readPixels(x, y, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, pixel); + return pixel; +} + +function assertPixelNear(pixel, expected, label, tolerance = 8) { + for (let i = 0; i < 4; ++i) { + assert( + Math.abs(pixel[i] - expected[i]) <= tolerance, + `${label}: channel ${i} expected ${expected[i]}, got ${pixel[i]}`); + } +} + +function assertSamples(gl, samples) { + for (const sample of samples) { + assertPixelNear( + readDefaultPixel(gl, sample.x, sample.y), sample.expected, + sample.label); + } +} + +function makeTriangle() { + const geometry = new THREE.BufferGeometry(); + geometry.setAttribute("position", new THREE.BufferAttribute(new Float32Array([ + -0.72, -0.72, 0, + 0.72, -0.72, 0, + 0.0, 0.78, 0 + ]), 3)); + const material = new THREE.MeshBasicMaterial({color: 0x2f6fff}); + const mesh = new THREE.Mesh(geometry, material); + mesh.position.x = -2; + return {geometry, material, mesh}; +} + +function makeCircle() { + const geometry = new THREE.CircleGeometry(0.62, 40); + const material = new THREE.MeshBasicMaterial({color: 0x21d66b}); + const mesh = new THREE.Mesh(geometry, material); + return {geometry, material, mesh}; +} + +function makeBox() { + const geometry = new THREE.BoxGeometry(1.05, 1.05, 1.05); + const material = new THREE.MeshBasicMaterial({color: 0xff4b3e}); + const mesh = new THREE.Mesh(geometry, material); + mesh.position.x = 2; + mesh.rotation.x = 0.45; + mesh.rotation.y = 0.6; + return {geometry, material, mesh}; +} + +function disposeDrawable(drawable) { + drawable.geometry.dispose(); + drawable.material.dispose(); +} + +function testThreeShapeRendering() { + const gl = createContext(); + let renderer = null; + const drawables = []; + + try { + renderer = new THREE.WebGLRenderer({ + canvas: createCanvas(gl), + context: gl, + alpha: true, + antialias: false + }); + renderer.setSize(WIDTH, HEIGHT, false); + renderer.setClearColor(0x111111, 1); + + const scene = new THREE.Scene(); + const camera = new THREE.OrthographicCamera(-3, 3, 1.5, -1.5, 0.1, 10); + camera.position.z = 5; + + drawables.push(makeTriangle(), makeCircle(), makeBox()); + for (const drawable of drawables) { + scene.add(drawable.mesh); + } + + renderer.render(scene, camera); + + assertSamples(gl, [ + {x: 16, y: 18, expected: [47, 111, 255, 255], label: "triangle lower"}, + {x: 16, y: 24, expected: [47, 111, 255, 255], label: "triangle center"}, + {x: 16, y: 30, expected: [47, 111, 255, 255], label: "triangle upper"}, + {x: 42, y: 24, expected: [33, 214, 107, 255], label: "circle left"}, + {x: 48, y: 24, expected: [33, 214, 107, 255], label: "circle center"}, + {x: 54, y: 24, expected: [33, 214, 107, 255], label: "circle right"}, + {x: 76, y: 24, expected: [255, 75, 62, 255], label: "box left"}, + {x: 80, y: 24, expected: [255, 75, 62, 255], label: "box center"}, + {x: 84, y: 24, expected: [255, 75, 62, 255], label: "box right"}, + {x: 2, y: 2, expected: [17, 17, 17, 255], label: "background corner"}, + {x: 32, y: 44, expected: [17, 17, 17, 255], label: "background gap"} + ]); + assert.strictEqual(gl.getError(), gl.NO_ERROR, "three smoke GL error"); + } finally { + if (renderer !== null) { + renderer.dispose(); + } + for (const drawable of drawables) { + disposeDrawable(drawable); + } + gl.destroy(); + } +} + +testThreeShapeRendering(); diff --git a/test/twgl-smoke.js b/test/twgl-smoke.js new file mode 100644 index 0000000..ff3e82f --- /dev/null +++ b/test/twgl-smoke.js @@ -0,0 +1,101 @@ +"use strict"; + +const assert = require("assert"); +const twgl = require("twgl.js"); +const nodeGles = require(".."); + +const WIDTH = 32; +const HEIGHT = 32; + +function createContext(width = WIDTH, height = HEIGHT) { + return nodeGles.createWebGLRenderingContext({ + width, + height, + majorVersion: 3, + minorVersion: 0 + }); +} + +function readPixel(gl, x, y) { + const pixel = new Uint8Array(4); + gl.readPixels(x, y, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, pixel); + return pixel; +} + +function assertPixelNear(pixel, expected, label, tolerance = 4) { + for (let i = 0; i < 4; ++i) { + assert( + Math.abs(pixel[i] - expected[i]) <= tolerance, + `${label}: channel ${i} expected ${expected[i]}, got ${pixel[i]}`); + } +} + +function coloredRectanglesArrays() { + return { + position: { + numComponents: 2, + data: [ + -0.9, -0.7, -0.15, -0.7, -0.9, 0.7, -0.15, 0.7, + 0.15, -0.7, 0.9, -0.7, 0.15, 0.7, 0.9, 0.7 + ] + }, + color: { + numComponents: 4, + data: [ + 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, + 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1 + ] + }, + indices: [0, 1, 2, 2, 1, 3, 4, 5, 6, 6, 5, 7] + }; +} + +function testProgramBufferAndDrawHelpers() { + const gl = createContext(); + try { + const programInfo = twgl.createProgramInfo(gl, [` +attribute vec2 position; +attribute vec4 color; +varying vec4 v_color; +void main() { + v_color = color; + gl_Position = vec4(position, 0.0, 1.0); +} +`, ` +precision mediump float; +uniform vec4 u_tint; +varying vec4 v_color; +void main() { + gl_FragColor = v_color * u_tint; +} +`]); + const bufferInfo = + twgl.createBufferInfoFromArrays(gl, coloredRectanglesArrays()); + + const tintLocation = gl.getUniformLocation(programInfo.program, "u_tint"); + assert.notStrictEqual(tintLocation, null); + assert.strictEqual(typeof tintLocation, "object"); + assert.strictEqual(Boolean(tintLocation), true); + assert.strictEqual(typeof programInfo.uniformSetters.u_tint, "function"); + + gl.viewport(0, 0, WIDTH, HEIGHT); + gl.clearColor(0, 0, 0, 1); + gl.clear(gl.COLOR_BUFFER_BIT); + gl.useProgram(programInfo.program); + + twgl.setBuffersAndAttributes(gl, programInfo, bufferInfo); + twgl.setUniforms(programInfo, {u_tint: [1, 1, 1, 1]}); + twgl.drawBufferInfo(gl, bufferInfo); + + assertPixelNear(readPixel(gl, 8, 16), [255, 0, 0, 255], "left rectangle"); + assertPixelNear(readPixel(gl, 24, 16), [0, 255, 0, 255], + "right rectangle"); + assertPixelNear(readPixel(gl, 16, 16), [0, 0, 0, 255], + "rectangle gap"); + assert.strictEqual(gl.getError(), gl.NO_ERROR, "default draw GL error"); + } finally { + gl.destroy(); + } +} + +testProgramBufferAndDrawHelpers(); diff --git a/test/webgl2-smoke.js b/test/webgl2-smoke.js index d54b997..06def21 100644 --- a/test/webgl2-smoke.js +++ b/test/webgl2-smoke.js @@ -88,6 +88,13 @@ void main() { assert.strictEqual(gl.getProgramInfoLog(program), ""); } +function testInvalidProgramLinkErrors(gl) { + gl.linkProgram(0); + assert.strictEqual(gl.getError(), gl.INVALID_VALUE); + gl.linkProgram(999999); + assert.strictEqual(gl.getError(), gl.INVALID_VALUE); +} + function assertRedPixel(gl, x, y, label) { const pixel = new Uint8Array(4); gl.readPixels(x, y, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, pixel); @@ -1258,6 +1265,7 @@ void main() { precision highp float; uniform uint u_scalar; uniform uvec4 u_vector; +uniform uint u_unused; out vec4 out_color; void main() { bool ok = u_scalar == 7u && all(equal(u_vector, uvec4(1u, 2u, 3u, 4u))); @@ -1270,15 +1278,134 @@ void main() { gl.clearColor(1, 0, 0, 1); gl.clear(gl.COLOR_BUFFER_BIT); gl.useProgram(program); - gl.uniform1ui(gl.getUniformLocation(program, "u_scalar"), 7); - gl.uniform4uiv( - gl.getUniformLocation(program, "u_vector"), - new Uint32Array([1, 2, 3, 4])); + const scalarLocation = gl.getUniformLocation(program, "u_scalar"); + const vectorLocation = gl.getUniformLocation(program, "u_vector"); + assert.notStrictEqual(scalarLocation, null); + assert.strictEqual(typeof scalarLocation, "object"); + assert.strictEqual(Boolean(scalarLocation), true); + assert.strictEqual(gl.getUniformLocation(program, "u_missing"), null); + const unusedLocation = gl.getUniformLocation(program, "u_unused"); + assert.strictEqual(unusedLocation, null); + gl.uniform1ui(unusedLocation, 123); + assertNoError(gl, "inactive uniform location no-op"); + gl.uniform1ui(scalarLocation, 7); + gl.uniform4uiv(vectorLocation, new Uint32Array([1, 2, 3, 4])); + assert.strictEqual(gl.getUniform(program, scalarLocation), 7); gl.drawArrays(gl.TRIANGLES, 0, 3); const pixel = new Uint8Array(4); gl.readPixels(8, 8, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, pixel); assertPixelNear(pixel, [0, 255, 0, 255], "unsigned integer uniforms"); assertNoError(gl, "unsigned integer uniforms"); + gl.useProgram(null); + gl.uniform1ui(unusedLocation, 123); + assertNoError(gl, "inactive uniform location no-op without current program"); +} + +function testUniformLocationProgramValidation(gl) { + const vertexSource = `#version 300 es +void main() { + vec2 positions[3] = vec2[3]( + vec2(-1.0, -1.0), + vec2(3.0, -1.0), + vec2(-1.0, 3.0)); + gl_Position = vec4(positions[gl_VertexID], 0.0, 1.0); +} +`; + const fragmentSource = `#version 300 es +precision highp float; +uniform vec4 u_color; +out vec4 out_color; +void main() { + out_color = u_color; +} +`; + const firstProgram = + createProgramFromSources(gl, vertexSource, fragmentSource); + const secondProgram = + createProgramFromSources(gl, vertexSource, fragmentSource); + const firstLocation = gl.getUniformLocation(firstProgram, "u_color"); + const secondLocation = gl.getUniformLocation(secondProgram, "u_color"); + + gl.useProgram(secondProgram); + gl.getError(); + gl.uniform4f(firstLocation, 0, 1, 0, 1); + assert.strictEqual( + gl.getError(), gl.INVALID_OPERATION, + "foreign uniform location should be rejected"); + assert.strictEqual(gl.getUniform(secondProgram, firstLocation), null); + assert.strictEqual( + gl.getError(), gl.INVALID_OPERATION, + "getUniform should reject a location from another program"); + + gl.uniform4f(secondLocation, 0, 1, 0, 1); + assertNoError(gl, "valid uniform location after rejected location"); + gl.drawArrays(gl.TRIANGLES, 0, 3); + const pixel = new Uint8Array(4); + gl.readPixels(8, 8, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, pixel); + assertPixelNear(pixel, [0, 255, 0, 255], + "valid uniform after rejected location"); + + gl.linkProgram(secondProgram); + assert.strictEqual( + gl.getProgramParameter(secondProgram, gl.LINK_STATUS), true, + gl.getProgramInfoLog(secondProgram)); + gl.useProgram(secondProgram); + gl.getError(); + gl.uniform4f(secondLocation, 1, 0, 0, 1); + assert.strictEqual( + gl.getError(), gl.INVALID_OPERATION, + "stale uniform location should be rejected after relink"); + const relinkedLocation = gl.getUniformLocation(secondProgram, "u_color"); + gl.uniform4f(relinkedLocation, 0, 0, 1, 1); + assertNoError(gl, "valid uniform location after relink"); + + gl.deleteProgram(secondProgram); + gl.useProgram(null); + gl.getError(); + assert.strictEqual(gl.getUniform(secondProgram, relinkedLocation), null); + assert.strictEqual( + gl.getError(), gl.INVALID_OPERATION, + "deleted program uniform location should be rejected after unbind"); +} + +function testUniformLocationContextValidation(gl) { + const otherGl = createContext(); + const vertexSource = `#version 300 es +void main() { + vec2 positions[3] = vec2[3]( + vec2(-1.0, -1.0), + vec2(3.0, -1.0), + vec2(-1.0, 3.0)); + gl_Position = vec4(positions[gl_VertexID], 0.0, 1.0); +} +`; + const fragmentSource = `#version 300 es +precision highp float; +uniform vec4 u_color; +out vec4 out_color; +void main() { + out_color = u_color; +} +`; + const program = createProgramFromSources(gl, vertexSource, fragmentSource); + const otherProgram = + createProgramFromSources(otherGl, vertexSource, fragmentSource); + const location = gl.getUniformLocation(program, "u_color"); + const otherLocation = otherGl.getUniformLocation(otherProgram, "u_color"); + + otherGl.useProgram(otherProgram); + otherGl.getError(); + otherGl.uniform4f(location, 1, 0, 0, 1); + assert.strictEqual( + otherGl.getError(), otherGl.INVALID_OPERATION, + "uniform location from another context should be rejected"); + assert.strictEqual(otherGl.getUniform(otherProgram, location), null); + assert.strictEqual( + otherGl.getError(), otherGl.INVALID_OPERATION, + "getUniform should reject a location from another context"); + + otherGl.uniform4f(otherLocation, 0, 1, 0, 1); + assertNoError(otherGl, "valid uniform location after cross-context reject"); } function testNonSquareUniformMatrices(gl) { @@ -3093,6 +3220,7 @@ testCompressedTextureS3TC(gl); testExtensionCreationOptions(); testUnsupportedExtensionDoesNotWriteStderr(); testInfoLogEmptyStrings(gl); +testInvalidProgramLinkErrors(gl); testGetParameterSemantics(gl); testExposedExtensionSemantics(gl); testEXTFloatBlendImplicitEnable(); @@ -3104,6 +3232,8 @@ testPixelBufferObjectNumericOffsets(gl); testMultisampleFramebufferOperations(gl); testTexStorage2DAndMultiPixelReadback(gl); testUnsignedIntegerUniforms(gl); +testUniformLocationProgramValidation(gl); +testUniformLocationContextValidation(gl); testNonSquareUniformMatrices(gl); testUniformBufferObjects(gl); testQueryObjects(gl); diff --git a/yarn.lock b/yarn.lock index 023bbb2..b3a97a2 100644 --- a/yarn.lock +++ b/yarn.lock @@ -76,6 +76,16 @@ regl@2.1.1: resolved "https://registry.yarnpkg.com/regl/-/regl-2.1.1.tgz#fb3eecbc684031ec6172f68aaab2cbe9c3aa3148" integrity sha512-+IOGrxl3FZ8ZM9ixCWQZzFRiRn7Rzn9bu3iFHwg/yz4tlOUQgbO4PHLgG+1ZT60zcIV8tief6Qrmyl8qcoJP0g== +three@0.184.0: + version "0.184.0" + resolved "https://registry.yarnpkg.com/three/-/three-0.184.0.tgz#5bca0a3851eea5345e4c205567b40dfa49b791b5" + integrity sha512-wtTRjG92pM5eUg/KuUnHsqSAlPM296brTOcLgMRqEeylYTh/CdtvKUvCyyCQTzFuStieWxvZb8mVTMvdPyUpxg== + +twgl.js@7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/twgl.js/-/twgl.js-7.0.0.tgz#cb8f47d4b588b5ce71a6d67a413333bed7502b8c" + integrity sha512-9HHZ8emdZb2nKPU0FDIGDlkp6AD9rWFv7ThqdbmUqyBhD3WTQpLN+89w4B+YshGwD1fL6PMVpVKZqC0pikgmcA== + typescript@3.0.3: version "3.0.3" resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.0.3.tgz#4853b3e275ecdaa27f78fda46dc273a7eb7fc1c8"