mirror of
https://github.com/denoland/deno.git
synced 2025-03-04 09:57:11 -05:00

Fixes #25874 Fixes #26760 Fixes #24288 Fixes #24798 Fixes #25627 Fixes #25915 Fixes #26769
478 lines
12 KiB
Rust
478 lines
12 KiB
Rust
// Copyright 2018-2025 the Deno authors. MIT license.
|
|
|
|
use std::collections::HashSet;
|
|
use std::rc::Rc;
|
|
use std::sync::Arc;
|
|
|
|
use deno_core::cppgc::SameObject;
|
|
use deno_core::op2;
|
|
use deno_core::v8;
|
|
use deno_core::GarbageCollected;
|
|
use deno_core::OpState;
|
|
use deno_core::WebIDL;
|
|
use tokio::sync::Mutex;
|
|
|
|
use super::device::GPUDevice;
|
|
use crate::webidl::features_to_feature_names;
|
|
use crate::webidl::GPUFeatureName;
|
|
use crate::Instance;
|
|
|
|
#[derive(WebIDL)]
|
|
#[webidl(dictionary)]
|
|
pub(crate) struct GPURequestAdapterOptions {
|
|
pub power_preference: Option<GPUPowerPreference>,
|
|
#[webidl(default = false)]
|
|
pub force_fallback_adapter: bool,
|
|
}
|
|
|
|
#[derive(WebIDL)]
|
|
#[webidl(enum)]
|
|
pub(crate) enum GPUPowerPreference {
|
|
LowPower,
|
|
HighPerformance,
|
|
}
|
|
|
|
#[derive(WebIDL)]
|
|
#[webidl(dictionary)]
|
|
struct GPUDeviceDescriptor {
|
|
#[webidl(default = String::new())]
|
|
label: String,
|
|
|
|
#[webidl(default = vec![])]
|
|
required_features: Vec<GPUFeatureName>,
|
|
#[webidl(default = Default::default())]
|
|
#[options(enforce_range = true)]
|
|
required_limits: indexmap::IndexMap<String, Option<u64>>,
|
|
}
|
|
|
|
pub struct GPUAdapter {
|
|
pub instance: Instance,
|
|
pub id: wgpu_core::id::AdapterId,
|
|
|
|
pub features: SameObject<GPUSupportedFeatures>,
|
|
pub limits: SameObject<GPUSupportedLimits>,
|
|
pub info: Rc<SameObject<GPUAdapterInfo>>,
|
|
}
|
|
|
|
impl Drop for GPUAdapter {
|
|
fn drop(&mut self) {
|
|
self.instance.adapter_drop(self.id);
|
|
}
|
|
}
|
|
|
|
impl GarbageCollected for GPUAdapter {}
|
|
|
|
#[op2]
|
|
impl GPUAdapter {
|
|
#[getter]
|
|
#[global]
|
|
fn info(&self, scope: &mut v8::HandleScope) -> v8::Global<v8::Object> {
|
|
self.info.get(scope, |_| {
|
|
let info = self.instance.adapter_get_info(self.id);
|
|
let limits = self.instance.adapter_limits(self.id);
|
|
|
|
GPUAdapterInfo {
|
|
info,
|
|
subgroup_min_size: limits.min_subgroup_size,
|
|
subgroup_max_size: limits.max_subgroup_size,
|
|
}
|
|
})
|
|
}
|
|
|
|
#[getter]
|
|
#[global]
|
|
fn features(&self, scope: &mut v8::HandleScope) -> v8::Global<v8::Object> {
|
|
self.features.get(scope, |scope| {
|
|
let features = self.instance.adapter_features(self.id);
|
|
let features = features_to_feature_names(features);
|
|
GPUSupportedFeatures::new(scope, features)
|
|
})
|
|
}
|
|
#[getter]
|
|
#[global]
|
|
fn limits(&self, scope: &mut v8::HandleScope) -> v8::Global<v8::Object> {
|
|
self.limits.get(scope, |_| {
|
|
let adapter_limits = self.instance.adapter_limits(self.id);
|
|
GPUSupportedLimits(adapter_limits)
|
|
})
|
|
}
|
|
#[getter]
|
|
fn is_fallback_adapter(&self) -> bool {
|
|
// TODO(lucacasonato): report correctly from wgpu
|
|
false
|
|
}
|
|
|
|
#[async_method(fake)]
|
|
#[global]
|
|
fn request_device(
|
|
&self,
|
|
state: &mut OpState,
|
|
isolate_ptr: *mut v8::Isolate,
|
|
scope: &mut v8::HandleScope,
|
|
#[webidl] descriptor: GPUDeviceDescriptor,
|
|
) -> Result<v8::Global<v8::Value>, CreateDeviceError> {
|
|
let features = self.instance.adapter_features(self.id);
|
|
let supported_features = features_to_feature_names(features);
|
|
let required_features = descriptor
|
|
.required_features
|
|
.iter()
|
|
.cloned()
|
|
.collect::<HashSet<_>>();
|
|
|
|
if !required_features.is_subset(&supported_features) {
|
|
return Err(CreateDeviceError::RequiredFeaturesNotASubset);
|
|
}
|
|
|
|
let required_limits = serde_json::from_value(serde_json::to_value(
|
|
descriptor.required_limits,
|
|
)?)?;
|
|
|
|
let wgpu_descriptor = wgpu_types::DeviceDescriptor {
|
|
label: crate::transform_label(descriptor.label.clone()),
|
|
required_features: super::webidl::feature_names_to_features(
|
|
descriptor.required_features,
|
|
),
|
|
required_limits,
|
|
memory_hints: Default::default(),
|
|
};
|
|
|
|
let (device, queue) = self.instance.adapter_request_device(
|
|
self.id,
|
|
&wgpu_descriptor,
|
|
std::env::var("DENO_WEBGPU_TRACE")
|
|
.ok()
|
|
.as_ref()
|
|
.map(std::path::Path::new),
|
|
None,
|
|
None,
|
|
)?;
|
|
|
|
let (lost_sender, lost_receiver) = tokio::sync::oneshot::channel();
|
|
let (uncaptured_sender, mut uncaptured_receiver) =
|
|
tokio::sync::mpsc::unbounded_channel();
|
|
let (
|
|
uncaptured_sender_is_closed_sender,
|
|
mut uncaptured_sender_is_closed_receiver,
|
|
) = tokio::sync::oneshot::channel::<()>();
|
|
|
|
let device = GPUDevice {
|
|
instance: self.instance.clone(),
|
|
id: device,
|
|
queue,
|
|
label: descriptor.label,
|
|
queue_obj: SameObject::new(),
|
|
adapter_info: self.info.clone(),
|
|
error_handler: Arc::new(super::error::DeviceErrorHandler::new(
|
|
lost_sender,
|
|
uncaptured_sender,
|
|
uncaptured_sender_is_closed_sender,
|
|
)),
|
|
adapter: self.id,
|
|
lost_receiver: Mutex::new(Some(lost_receiver)),
|
|
limits: SameObject::new(),
|
|
features: SameObject::new(),
|
|
};
|
|
let device = deno_core::cppgc::make_cppgc_object(scope, device);
|
|
let event_target_setup = state.borrow::<crate::EventTargetSetup>();
|
|
let webidl_brand = v8::Local::new(scope, event_target_setup.brand.clone());
|
|
device.set(scope, webidl_brand, webidl_brand);
|
|
let set_event_target_data =
|
|
v8::Local::new(scope, event_target_setup.set_event_target_data.clone())
|
|
.cast::<v8::Function>();
|
|
let null = v8::null(scope);
|
|
set_event_target_data.call(scope, null.into(), &[device.into()]);
|
|
|
|
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::<crate::ErrorEventClass>().0.clone();
|
|
|
|
let context = scope.get_current_context();
|
|
let context = v8::Global::new(scope, context);
|
|
|
|
let task_device = device.clone();
|
|
deno_unsync::spawn(async move {
|
|
loop {
|
|
// TODO(@crowlKats): check for uncaptured_receiver.is_closed instead once tokio is upgraded
|
|
if !matches!(
|
|
uncaptured_sender_is_closed_receiver.try_recv(),
|
|
Err(tokio::sync::oneshot::error::TryRecvError::Empty)
|
|
) {
|
|
break;
|
|
}
|
|
let Some(error) = uncaptured_receiver.recv().await else {
|
|
break;
|
|
};
|
|
|
|
// SAFETY: eh, it's safe
|
|
let isolate: &mut v8::Isolate = unsafe { &mut *isolate_ptr };
|
|
let scope = &mut v8::HandleScope::with_context(isolate, &context);
|
|
let error = deno_core::error::to_v8_error(scope, &error);
|
|
|
|
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, task_device.clone());
|
|
func.open(scope).call(scope, recv, &[event.into()]);
|
|
}
|
|
});
|
|
|
|
Ok(device)
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, thiserror::Error, deno_error::JsError)]
|
|
pub enum CreateDeviceError {
|
|
#[class(type)]
|
|
#[error("requiredFeatures must be a subset of the adapter features")]
|
|
RequiredFeaturesNotASubset,
|
|
#[class(inherit)]
|
|
#[error(transparent)]
|
|
Serde(#[from] serde_json::Error),
|
|
#[class(type)]
|
|
#[error(transparent)]
|
|
Device(#[from] wgpu_core::instance::RequestDeviceError),
|
|
}
|
|
|
|
pub struct GPUSupportedLimits(pub wgpu_types::Limits);
|
|
|
|
impl GarbageCollected for GPUSupportedLimits {}
|
|
|
|
#[op2]
|
|
impl GPUSupportedLimits {
|
|
#[getter]
|
|
fn maxTextureDimension1D(&self) -> u32 {
|
|
self.0.max_texture_dimension_1d
|
|
}
|
|
|
|
#[getter]
|
|
fn maxTextureDimension2D(&self) -> u32 {
|
|
self.0.max_texture_dimension_2d
|
|
}
|
|
|
|
#[getter]
|
|
fn maxTextureDimension3D(&self) -> u32 {
|
|
self.0.max_texture_dimension_3d
|
|
}
|
|
|
|
#[getter]
|
|
fn maxTextureArrayLayers(&self) -> u32 {
|
|
self.0.max_texture_array_layers
|
|
}
|
|
|
|
#[getter]
|
|
fn maxBindGroups(&self) -> u32 {
|
|
self.0.max_bind_groups
|
|
}
|
|
|
|
// TODO(@crowlKats): support max_bind_groups_plus_vertex_buffers
|
|
|
|
#[getter]
|
|
fn maxBindingsPerBindGroup(&self) -> u32 {
|
|
self.0.max_bindings_per_bind_group
|
|
}
|
|
|
|
#[getter]
|
|
fn maxDynamicUniformBuffersPerPipelineLayout(&self) -> u32 {
|
|
self.0.max_dynamic_uniform_buffers_per_pipeline_layout
|
|
}
|
|
|
|
#[getter]
|
|
fn maxDynamicStorageBuffersPerPipelineLayout(&self) -> u32 {
|
|
self.0.max_dynamic_storage_buffers_per_pipeline_layout
|
|
}
|
|
|
|
#[getter]
|
|
fn maxSampledTexturesPerShaderStage(&self) -> u32 {
|
|
self.0.max_sampled_textures_per_shader_stage
|
|
}
|
|
|
|
#[getter]
|
|
fn maxSamplersPerShaderStage(&self) -> u32 {
|
|
self.0.max_samplers_per_shader_stage
|
|
}
|
|
|
|
#[getter]
|
|
fn maxStorageBuffersPerShaderStage(&self) -> u32 {
|
|
self.0.max_storage_buffers_per_shader_stage
|
|
}
|
|
|
|
#[getter]
|
|
fn maxStorageTexturesPerShaderStage(&self) -> u32 {
|
|
self.0.max_storage_textures_per_shader_stage
|
|
}
|
|
|
|
#[getter]
|
|
fn maxUniformBuffersPerShaderStage(&self) -> u32 {
|
|
self.0.max_uniform_buffers_per_shader_stage
|
|
}
|
|
|
|
#[getter]
|
|
fn maxUniformBufferBindingSize(&self) -> u32 {
|
|
self.0.max_uniform_buffer_binding_size
|
|
}
|
|
|
|
#[getter]
|
|
fn maxStorageBufferBindingSize(&self) -> u32 {
|
|
self.0.max_storage_buffer_binding_size
|
|
}
|
|
|
|
#[getter]
|
|
fn minUniformBufferOffsetAlignment(&self) -> u32 {
|
|
self.0.min_uniform_buffer_offset_alignment
|
|
}
|
|
|
|
#[getter]
|
|
fn minStorageBufferOffsetAlignment(&self) -> u32 {
|
|
self.0.min_storage_buffer_offset_alignment
|
|
}
|
|
|
|
#[getter]
|
|
fn maxVertexBuffers(&self) -> u32 {
|
|
self.0.max_vertex_buffers
|
|
}
|
|
|
|
#[getter]
|
|
#[number]
|
|
fn maxBufferSize(&self) -> u64 {
|
|
self.0.max_buffer_size
|
|
}
|
|
|
|
#[getter]
|
|
fn maxVertexAttributes(&self) -> u32 {
|
|
self.0.max_vertex_attributes
|
|
}
|
|
|
|
#[getter]
|
|
fn maxVertexBufferArrayStride(&self) -> u32 {
|
|
self.0.max_vertex_buffer_array_stride
|
|
}
|
|
|
|
// TODO(@crowlKats): support max_inter_stage_shader_variables
|
|
|
|
#[getter]
|
|
fn maxColorAttachments(&self) -> u32 {
|
|
self.0.max_color_attachments
|
|
}
|
|
|
|
#[getter]
|
|
fn maxColorAttachmentBytesPerSample(&self) -> u32 {
|
|
self.0.max_color_attachment_bytes_per_sample
|
|
}
|
|
|
|
#[getter]
|
|
fn maxComputeWorkgroupStorageSize(&self) -> u32 {
|
|
self.0.max_compute_workgroup_storage_size
|
|
}
|
|
|
|
#[getter]
|
|
fn maxComputeInvocationsPerWorkgroup(&self) -> u32 {
|
|
self.0.max_compute_invocations_per_workgroup
|
|
}
|
|
|
|
#[getter]
|
|
fn maxComputeWorkgroupSizeX(&self) -> u32 {
|
|
self.0.max_compute_workgroup_size_x
|
|
}
|
|
|
|
#[getter]
|
|
fn maxComputeWorkgroupSizeY(&self) -> u32 {
|
|
self.0.max_compute_workgroup_size_y
|
|
}
|
|
|
|
#[getter]
|
|
fn maxComputeWorkgroupSizeZ(&self) -> u32 {
|
|
self.0.max_compute_workgroup_size_z
|
|
}
|
|
|
|
#[getter]
|
|
fn maxComputeWorkgroupsPerDimension(&self) -> u32 {
|
|
self.0.max_compute_workgroups_per_dimension
|
|
}
|
|
}
|
|
|
|
pub struct GPUSupportedFeatures(v8::Global<v8::Value>);
|
|
|
|
impl GarbageCollected for GPUSupportedFeatures {}
|
|
|
|
impl GPUSupportedFeatures {
|
|
pub fn new(
|
|
scope: &mut v8::HandleScope,
|
|
features: HashSet<GPUFeatureName>,
|
|
) -> Self {
|
|
let set = v8::Set::new(scope);
|
|
|
|
for feature in features {
|
|
let key = v8::String::new(scope, feature.as_str()).unwrap();
|
|
set.add(scope, key.into());
|
|
}
|
|
|
|
Self(v8::Global::new(scope, <v8::Local<v8::Value>>::from(set)))
|
|
}
|
|
}
|
|
|
|
#[op2]
|
|
impl GPUSupportedFeatures {
|
|
#[global]
|
|
#[symbol("setlike_set")]
|
|
fn set(&self) -> v8::Global<v8::Value> {
|
|
self.0.clone()
|
|
}
|
|
}
|
|
|
|
pub struct GPUAdapterInfo {
|
|
pub info: wgpu_types::AdapterInfo,
|
|
pub subgroup_min_size: u32,
|
|
pub subgroup_max_size: u32,
|
|
}
|
|
|
|
impl GarbageCollected for GPUAdapterInfo {}
|
|
|
|
#[op2]
|
|
impl GPUAdapterInfo {
|
|
#[getter]
|
|
#[string]
|
|
fn vendor(&self) -> String {
|
|
self.info.vendor.to_string()
|
|
}
|
|
|
|
#[getter]
|
|
#[string]
|
|
fn architecture(&self) -> &'static str {
|
|
"" // TODO: wgpu#2170
|
|
}
|
|
|
|
#[getter]
|
|
#[string]
|
|
fn device(&self) -> String {
|
|
self.info.device.to_string()
|
|
}
|
|
|
|
#[getter]
|
|
#[string]
|
|
fn description(&self) -> String {
|
|
self.info.name.clone()
|
|
}
|
|
|
|
#[getter]
|
|
fn subgroup_min_size(&self) -> u32 {
|
|
self.subgroup_min_size
|
|
}
|
|
|
|
#[getter]
|
|
fn subgroup_max_size(&self) -> u32 {
|
|
self.subgroup_max_size
|
|
}
|
|
}
|