/
githubmirror
/
deno
Обзор
Документация
Войти
/
githubmirror
/
deno
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
ext/webgpu/error.rs
430 строк
11 KB
Leo Kettmeir
chore: update webgpu (#34319)
08 июн 2026, 23:29
Не верифицирован
08 июн 2026, 23:29
2b313e2
Код
Авторство
О чём код?
// Copyright 2018-2026 the Deno authors. MIT license. use std::fmt::Display; use std::fmt::Formatter; use std::sync::Mutex; use std::sync::OnceLock; use deno_core::JsRuntime; use deno_core::V8TaskSpawner; use deno_core::cppgc::make_cppgc_object; use deno_core::v8; use wgpu_core::binding_model::CreateBindGroupError; use wgpu_core::binding_model::CreateBindGroupLayoutError; use wgpu_core::binding_model::CreatePipelineLayoutError; use wgpu_core::binding_model::GetBindGroupLayoutError; use wgpu_core::command::ClearError; use wgpu_core::command::CommandEncoderError; use wgpu_core::command::ComputePassError; use wgpu_core::command::CreateRenderBundleError; use wgpu_core::command::EncoderStateError; use wgpu_core::command::PassStateError; use wgpu_core::command::QueryError; use wgpu_core::command::RenderBundleError; use wgpu_core::command::RenderPassError; use wgpu_core::device::DeviceError; use wgpu_core::device::queue::QueueSubmitError; use wgpu_core::device::queue::QueueWriteError; use wgpu_core::pipeline::CreateComputePipelineError; use wgpu_core::pipeline::CreateRenderPipelineError; use wgpu_core::pipeline::CreateShaderModuleError; use wgpu_core::present::ConfigureSurfaceError; use wgpu_core::present::SurfaceError; use wgpu_core::resource::BufferAccessError; use wgpu_core::resource::CreateBufferError; use wgpu_core::resource::CreateQuerySetError; use wgpu_core::resource::CreateSamplerError; use wgpu_core::resource::CreateTextureError; use wgpu_core::resource::CreateTextureViewError; use wgpu_types::error::ErrorType; use wgpu_types::error::WebGpuError; use crate::device::GPUDeviceLostInfo; use crate::device::GPUDeviceLostReason; pub type ErrorHandler = std::rc::Rc<DeviceErrorHandler>; pub struct DeviceErrorHandler { pub is_lost: OnceLock<()>, pub scopes: Mutex<Vec<(GPUErrorFilter, Option<GPUError>)>>, lost_resolver: Mutex<Option<v8::Global<v8::PromiseResolver>>>, spawner: V8TaskSpawner, // The error handler is constructed before the device. A weak // reference to the device is placed here with `set_device` // after the device is constructed. device: OnceLock<v8::Weak<v8::Object>>, } impl DeviceErrorHandler { pub fn new( lost_resolver: v8::Global<v8::PromiseResolver>, spawner: V8TaskSpawner, ) -> Self { Self { is_lost: Default::default(), scopes: Mutex::new(vec![]), lost_resolver: Mutex::new(Some(lost_resolver)), device: OnceLock::new(), spawner, } } pub fn set_device(&self, device: v8::Weak<v8::Object>) { self.device.set(device).unwrap() } pub fn push_error<E: Into<GPUError>>(&self, err: Option<E>) { let Some(err) = err else { return; }; if self.is_lost.get().is_some() { return; } let err = err.into(); if let GPUError::Lost(reason) = err { let _ = self.is_lost.set(()); if let Some(resolver) = self.lost_resolver.lock().unwrap().take() { self.spawner.spawn(move |scope| { let resolver = v8::Local::new(scope, resolver); let info = make_cppgc_object(scope, GPUDeviceLostInfo { reason }); let info = v8::Local::new(scope, info); resolver.resolve(scope, info.into()); }); } return; } let error_filter = match err { GPUError::Lost(_) => unreachable!(), GPUError::Validation(_) => GPUErrorFilter::Validation, GPUError::OutOfMemory => GPUErrorFilter::OutOfMemory, GPUError::Internal => GPUErrorFilter::Internal, }; let mut scopes = self.scopes.lock().unwrap(); let scope = scopes .iter_mut() .rfind(|(filter, _)| filter == &error_filter); if let Some(scope) = scope { // Only saving the first error in the scope as it's likely the culprit. if scope.1.is_none() { scope.1 = Some(err); } } else { let device = self .device .get() .expect("set_device was not called") .clone(); self.spawner.spawn(move |scope| { let state = JsRuntime::op_state_from(&*scope); let Some(device) = device.to_local(scope) else { // The device has already gone away, so we don't have // anywhere to report the error. return; }; let key = v8::String::new(scope, "dispatchEvent").unwrap(); let val = device.get(scope, key.into()).unwrap(); let func = v8::Global::new(scope, val.try_cast::<v8::Function>().unwrap()); let device = v8::Global::new(scope, device.cast::<v8::Value>()); let error_event_class = state.borrow().borrow::<crate::ErrorEventClass>().0.clone(); let error = deno_core::error::to_v8_error(scope, &err); let error_event_class = v8::Local::new(scope, error_event_class.clone()); let constructor = v8::Local::<v8::Function>::try_from(error_event_class).unwrap(); let kind = v8::String::new(scope, "uncapturederror").unwrap(); let obj = v8::Object::new(scope); let key = v8::String::new(scope, "error").unwrap(); obj.set(scope, key.into(), error); let event = constructor .new_instance(scope, &[kind.into(), obj.into()]) .unwrap(); let recv = v8::Local::new(scope, device); func.open(scope).call(scope, recv, &[event.into()]); }); } } } #[derive(deno_core::WebIDL, Eq, PartialEq)] #[webidl(enum)] pub enum GPUErrorFilter { Validation, OutOfMemory, Internal, } #[derive(Debug, deno_error::JsError)] pub enum GPUError { // TODO(@crowlKats): consider adding an unreachable value that uses unreachable!() #[class("UNREACHABLE")] Lost(GPUDeviceLostReason), #[class("GPUValidationError")] Validation(String), #[class("GPUOutOfMemoryError")] OutOfMemory, #[class("GPUInternalError")] Internal, } impl Display for GPUError { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { match self { GPUError::Lost(_) => Ok(()), GPUError::Validation(s) => f.write_str(s), GPUError::OutOfMemory => f.write_str("not enough memory left"), GPUError::Internal => Ok(()), } } } impl std::error::Error for GPUError {} impl GPUError { fn from_webgpu(e: impl WebGpuError) -> Self { match e.webgpu_error_type() { ErrorType::Internal => GPUError::Internal, ErrorType::DeviceLost => GPUError::Lost(GPUDeviceLostReason::Unknown), // TODO: this variant should be ignored, register the lost callback instead. ErrorType::OutOfMemory => GPUError::OutOfMemory, ErrorType::Validation => GPUError::Validation(fmt_err(&e)), } } } pub(crate) fn fmt_err(err: &(dyn std::error::Error + 'static)) -> String { let mut output = err.to_string(); let mut e = err.source(); while let Some(source) = e { output.push_str(&format!(": {source}")); e = source.source(); } if output.is_empty() { output.push_str("validation error"); } output } impl From<EncoderStateError> for GPUError { fn from(err: EncoderStateError) -> Self { GPUError::from_webgpu(err) } } impl From<PassStateError> for GPUError { fn from(err: PassStateError) -> Self { GPUError::Validation(fmt_err(&err)) } } impl From<CreateBufferError> for GPUError { fn from(err: CreateBufferError) -> Self { GPUError::from_webgpu(err) } } impl From<DeviceError> for GPUError { fn from(err: DeviceError) -> Self { GPUError::from_webgpu(err) } } impl From<BufferAccessError> for GPUError { fn from(err: BufferAccessError) -> Self { GPUError::from_webgpu(err) } } impl From<CreateBindGroupLayoutError> for GPUError { fn from(err: CreateBindGroupLayoutError) -> Self { GPUError::from_webgpu(err) } } impl From<CreatePipelineLayoutError> for GPUError { fn from(err: CreatePipelineLayoutError) -> Self { GPUError::from_webgpu(err) } } impl From<CreateBindGroupError> for GPUError { fn from(err: CreateBindGroupError) -> Self { GPUError::from_webgpu(err) } } impl From<RenderBundleError> for GPUError { fn from(err: RenderBundleError) -> Self { GPUError::from_webgpu(err) } } impl From<CreateRenderBundleError> for GPUError { fn from(err: CreateRenderBundleError) -> Self { GPUError::from_webgpu(err) } } impl From<CommandEncoderError> for GPUError { fn from(err: CommandEncoderError) -> Self { GPUError::from_webgpu(err) } } impl From<QueryError> for GPUError { fn from(err: QueryError) -> Self { GPUError::from_webgpu(err) } } impl From<ComputePassError> for GPUError { fn from(err: ComputePassError) -> Self { GPUError::from_webgpu(err) } } impl From<CreateComputePipelineError> for GPUError { fn from(err: CreateComputePipelineError) -> Self { GPUError::from_webgpu(err) } } impl From<GetBindGroupLayoutError> for GPUError { fn from(err: GetBindGroupLayoutError) -> Self { GPUError::from_webgpu(err) } } impl From<CreateRenderPipelineError> for GPUError { fn from(err: CreateRenderPipelineError) -> Self { GPUError::from_webgpu(err) } } impl From<RenderPassError> for GPUError { fn from(err: RenderPassError) -> Self { GPUError::from_webgpu(err) } } impl From<CreateSamplerError> for GPUError { fn from(err: CreateSamplerError) -> Self { GPUError::from_webgpu(err) } } impl From<CreateShaderModuleError> for GPUError { fn from(err: CreateShaderModuleError) -> Self { GPUError::from_webgpu(err) } } impl From<CreateTextureError> for GPUError { fn from(err: CreateTextureError) -> Self { GPUError::from_webgpu(err) } } impl From<CreateTextureViewError> for GPUError { fn from(err: CreateTextureViewError) -> Self { GPUError::from_webgpu(err) } } impl From<CreateQuerySetError> for GPUError { fn from(err: CreateQuerySetError) -> Self { GPUError::from_webgpu(err) } } impl From<QueueSubmitError> for GPUError { fn from(err: QueueSubmitError) -> Self { GPUError::from_webgpu(err) } } impl From<QueueWriteError> for GPUError { fn from(err: QueueWriteError) -> Self { GPUError::from_webgpu(err) } } impl From<ClearError> for GPUError { fn from(err: ClearError) -> Self { GPUError::from_webgpu(err) } } impl From<ConfigureSurfaceError> for GPUError { fn from(err: ConfigureSurfaceError) -> Self { GPUError::from_webgpu(err) } } impl From<SurfaceError> for GPUError { fn from(err: SurfaceError) -> Self { GPUError::from_webgpu(err) } } #[derive(Debug, thiserror::Error, deno_error::JsError)] pub enum GPUGenericError { #[class(type)] #[error("Illegal constructor")] InvalidConstructor, } pub enum GPUPipelineErrorReason { Validation, Internal, } impl Display for GPUPipelineErrorReason { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { match self { Self::Validation => f.write_str("validation"), Self::Internal => f.write_str("internal"), } } } pub(crate) fn make_pipeline_error<'a>( scope: &mut v8::PinScope<'a, '_>, reason: GPUPipelineErrorReason, message: &str, ) -> v8::Local<'a, v8::Object> { let state = JsRuntime::op_state_from(scope); let class = state .borrow() .borrow::<crate::PipelineErrorClass>() .0 .clone(); let constructor = v8::Local::<v8::Function>::try_from(v8::Local::new(scope, class)).unwrap(); let message_str = v8::String::new(scope, message).unwrap(); let reason_str = v8::String::new(scope, &reason.to_string()).unwrap(); let options = v8::Object::new(scope); let key = v8::String::new(scope, "reason").unwrap(); options.set(scope, key.into(), reason_str.into()); constructor .new_instance(scope, &[message_str.into(), options.into()]) .unwrap() }