mirror of
https://github.com/denoland/deno.git
synced 2025-02-12 16:59:32 -05:00
863 lines
24 KiB
Rust
863 lines
24 KiB
Rust
use std::borrow::Cow;
|
|
use std::cell::RefCell;
|
|
use std::num::NonZeroU64;
|
|
use std::rc::Rc;
|
|
|
|
use deno_core::cppgc::SameObject;
|
|
use deno_core::op2;
|
|
use deno_core::v8;
|
|
use deno_core::GarbageCollected;
|
|
use deno_error::JsErrorBox;
|
|
use wgpu_core::binding_model::BindingResource;
|
|
use wgpu_core::pipeline::ProgrammableStageDescriptor;
|
|
use wgpu_types::BindingType;
|
|
|
|
use super::bind_group::GPUBindGroup;
|
|
use super::bind_group::GPUBindingResource;
|
|
use super::bind_group_layout::GPUBindGroupLayout;
|
|
use super::buffer::GPUBuffer;
|
|
use super::compute_pipeline::GPUComputePipeline;
|
|
use super::pipeline_layout::GPUPipelineLayout;
|
|
use super::queue::GPUQueue;
|
|
use super::sampler::GPUSampler;
|
|
use super::shader::GPUShaderModule;
|
|
use super::texture::GPUTexture;
|
|
use crate::adapter::GPUAdapterInfo;
|
|
use crate::adapter::GPUSupportedFeatures;
|
|
use crate::adapter::GPUSupportedLimits;
|
|
use crate::command_encoder::GPUCommandEncoder;
|
|
use crate::query_set::GPUQuerySet;
|
|
use crate::render_bundle::GPURenderBundleEncoder;
|
|
use crate::render_pipeline::GPURenderPipeline;
|
|
use crate::webidl::features_to_feature_names;
|
|
use crate::Instance;
|
|
|
|
pub struct GPUDevice {
|
|
pub instance: Instance,
|
|
pub id: wgpu_core::id::DeviceId,
|
|
pub adapter: wgpu_core::id::AdapterId,
|
|
pub queue: wgpu_core::id::QueueId,
|
|
|
|
pub label: String,
|
|
|
|
pub features: SameObject<GPUSupportedFeatures>,
|
|
pub limits: SameObject<GPUSupportedLimits>,
|
|
pub adapter_info: Rc<SameObject<GPUAdapterInfo>>,
|
|
|
|
pub queue_obj: SameObject<GPUQueue>,
|
|
|
|
pub error_handler: super::error::ErrorHandler,
|
|
pub lost_receiver:
|
|
tokio::sync::Mutex<Option<tokio::sync::oneshot::Receiver<()>>>,
|
|
}
|
|
|
|
impl Drop for GPUDevice {
|
|
fn drop(&mut self) {
|
|
self.instance.device_drop(self.id);
|
|
}
|
|
}
|
|
|
|
impl GarbageCollected for GPUDevice {}
|
|
|
|
// TODO: extend EventTarget
|
|
// TODO: setEventTargetData on instance
|
|
#[op2]
|
|
impl GPUDevice {
|
|
#[getter]
|
|
#[string]
|
|
fn label(&self) -> String {
|
|
self.label.clone()
|
|
}
|
|
#[setter]
|
|
#[string]
|
|
fn label(&self, #[webidl] _label: String) {
|
|
// TODO(@crowlKats): no-op, needs wpgu to implement changing the label
|
|
}
|
|
|
|
#[getter]
|
|
#[global]
|
|
fn features(&self, scope: &mut v8::HandleScope) -> v8::Global<v8::Object> {
|
|
self.features.get(scope, |scope| {
|
|
let features = self.instance.device_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 limits = self.instance.device_limits(self.id);
|
|
GPUSupportedLimits(limits)
|
|
})
|
|
}
|
|
|
|
#[getter]
|
|
#[global]
|
|
fn adapter_info(
|
|
&self,
|
|
scope: &mut v8::HandleScope,
|
|
) -> v8::Global<v8::Object> {
|
|
self.adapter_info.get(scope, |_| {
|
|
let info = self.instance.adapter_get_info(self.adapter);
|
|
GPUAdapterInfo(info)
|
|
})
|
|
}
|
|
|
|
#[getter]
|
|
#[global]
|
|
fn queue(&self, scope: &mut v8::HandleScope) -> v8::Global<v8::Object> {
|
|
self.queue_obj.get(scope, |_| GPUQueue {
|
|
id: self.queue,
|
|
error_handler: self.error_handler.clone(),
|
|
instance: self.instance.clone(),
|
|
label: self.label.clone(),
|
|
})
|
|
}
|
|
|
|
#[fast]
|
|
fn destroy(&self) {
|
|
self.instance.device_destroy(self.id);
|
|
}
|
|
|
|
#[required(1)]
|
|
#[cppgc]
|
|
fn create_buffer(
|
|
&self,
|
|
#[webidl] descriptor: super::buffer::GPUBufferDescriptor,
|
|
) -> Result<GPUBuffer, JsErrorBox> {
|
|
let wgpu_descriptor = wgpu_core::resource::BufferDescriptor {
|
|
label: Some(Cow::Owned(descriptor.label.clone())),
|
|
size: descriptor.size,
|
|
usage: wgpu_types::BufferUsages::from_bits(descriptor.usage)
|
|
.ok_or_else(|| JsErrorBox::type_error("usage is not valid"))?,
|
|
mapped_at_creation: descriptor.mapped_at_creation,
|
|
};
|
|
|
|
let (id, err) =
|
|
self
|
|
.instance
|
|
.device_create_buffer(self.id, &wgpu_descriptor, None);
|
|
|
|
self.error_handler.push_error(err);
|
|
|
|
Ok(GPUBuffer {
|
|
instance: self.instance.clone(),
|
|
error_handler: self.error_handler.clone(),
|
|
id,
|
|
device: self.id,
|
|
label: descriptor.label,
|
|
size: descriptor.size,
|
|
usage: descriptor.usage,
|
|
map_state: RefCell::new(if descriptor.mapped_at_creation {
|
|
"mapped"
|
|
} else {
|
|
"unmapped"
|
|
}),
|
|
map_mode: RefCell::new(None),
|
|
mapped_js_buffers: RefCell::new(vec![]),
|
|
})
|
|
}
|
|
|
|
#[required(1)]
|
|
#[cppgc]
|
|
fn create_texture(
|
|
&self,
|
|
#[webidl] descriptor: super::texture::GPUTextureDescriptor,
|
|
) -> Result<GPUTexture, JsErrorBox> {
|
|
let wgpu_descriptor = wgpu_core::resource::TextureDescriptor {
|
|
label: Some(Cow::Owned(descriptor.label.clone())),
|
|
size: descriptor.size.into(),
|
|
mip_level_count: descriptor.mip_level_count,
|
|
sample_count: descriptor.sample_count,
|
|
dimension: descriptor.dimension.clone().into(),
|
|
format: descriptor.format.clone().into(),
|
|
usage: wgpu_types::TextureUsages::from_bits(descriptor.usage)
|
|
.ok_or_else(|| JsErrorBox::type_error("usage is not valid"))?,
|
|
view_formats: descriptor
|
|
.view_formats
|
|
.into_iter()
|
|
.map(Into::into)
|
|
.collect(),
|
|
};
|
|
|
|
let (id, err) =
|
|
self
|
|
.instance
|
|
.device_create_texture(self.id, &wgpu_descriptor, None);
|
|
|
|
self.error_handler.push_error(err);
|
|
|
|
Ok(GPUTexture {
|
|
instance: self.instance.clone(),
|
|
error_handler: self.error_handler.clone(),
|
|
id,
|
|
label: descriptor.label,
|
|
size: wgpu_descriptor.size,
|
|
mip_level_count: wgpu_descriptor.mip_level_count,
|
|
sample_count: wgpu_descriptor.sample_count,
|
|
dimension: descriptor.dimension,
|
|
format: descriptor.format,
|
|
usage: descriptor.usage,
|
|
})
|
|
}
|
|
|
|
#[cppgc]
|
|
fn create_sampler(
|
|
&self,
|
|
#[webidl] descriptor: super::sampler::GPUSamplerDescriptor,
|
|
) -> Result<GPUSampler, JsErrorBox> {
|
|
let wgpu_descriptor = wgpu_core::resource::SamplerDescriptor {
|
|
label: Some(Cow::Owned(descriptor.label.clone())),
|
|
address_modes: [
|
|
descriptor.address_mode_u.into(),
|
|
descriptor.address_mode_v.into(),
|
|
descriptor.address_mode_w.into(),
|
|
],
|
|
mag_filter: descriptor.mag_filter.into(),
|
|
min_filter: descriptor.min_filter.into(),
|
|
mipmap_filter: descriptor.mipmap_filter.into(),
|
|
lod_min_clamp: descriptor.lod_min_clamp,
|
|
lod_max_clamp: descriptor.lod_max_clamp,
|
|
compare: descriptor.compare.map(Into::into),
|
|
anisotropy_clamp: descriptor.max_anisotropy,
|
|
border_color: None,
|
|
};
|
|
|
|
let (id, err) =
|
|
self
|
|
.instance
|
|
.device_create_sampler(self.id, &wgpu_descriptor, None);
|
|
|
|
self.error_handler.push_error(err);
|
|
|
|
Ok(GPUSampler {
|
|
instance: self.instance.clone(),
|
|
id,
|
|
label: descriptor.label,
|
|
})
|
|
}
|
|
|
|
#[required(1)]
|
|
#[cppgc]
|
|
fn create_bind_group_layout(
|
|
&self,
|
|
#[webidl]
|
|
descriptor: super::bind_group_layout::GPUBindGroupLayoutDescriptor,
|
|
) -> Result<GPUBindGroupLayout, JsErrorBox> {
|
|
let mut entries = Vec::with_capacity(descriptor.entries.len());
|
|
|
|
for entry in descriptor.entries {
|
|
let n_entries = [
|
|
entry.buffer.is_some(),
|
|
entry.sampler.is_some(),
|
|
entry.texture.is_some(),
|
|
entry.storage_texture.is_some(),
|
|
]
|
|
.into_iter()
|
|
.filter(|t| *t)
|
|
.count();
|
|
|
|
if n_entries != 1 {
|
|
return Err(JsErrorBox::type_error("Only one of 'buffer', 'sampler', 'texture' and 'storageTexture' may be specified"));
|
|
}
|
|
|
|
let ty = if let Some(buffer) = entry.buffer {
|
|
BindingType::Buffer {
|
|
ty: buffer.r#type.into(),
|
|
has_dynamic_offset: buffer.has_dynamic_offset,
|
|
min_binding_size: NonZeroU64::new(buffer.min_binding_size),
|
|
}
|
|
} else if let Some(sampler) = entry.sampler {
|
|
BindingType::Sampler(sampler.r#type.into())
|
|
} else if let Some(texture) = entry.texture {
|
|
BindingType::Texture {
|
|
sample_type: texture.sample_type.into(),
|
|
view_dimension: texture.view_dimension.into(),
|
|
multisampled: texture.multisampled,
|
|
}
|
|
} else if let Some(storage_texture) = entry.storage_texture {
|
|
BindingType::StorageTexture {
|
|
access: storage_texture.access.into(),
|
|
format: storage_texture.format.into(),
|
|
view_dimension: storage_texture.view_dimension.into(),
|
|
}
|
|
} else {
|
|
unreachable!()
|
|
};
|
|
|
|
entries.push(wgpu_types::BindGroupLayoutEntry {
|
|
binding: entry.binding,
|
|
visibility: wgpu_types::ShaderStages::from_bits(entry.visibility)
|
|
.ok_or_else(|| JsErrorBox::type_error("usage is not valid"))?,
|
|
ty,
|
|
count: None, // native-only
|
|
});
|
|
}
|
|
|
|
let wgpu_descriptor = wgpu_core::binding_model::BindGroupLayoutDescriptor {
|
|
label: Some(Cow::Owned(descriptor.label.clone())),
|
|
entries: Cow::Owned(entries),
|
|
};
|
|
|
|
let (id, err) = self.instance.device_create_bind_group_layout(
|
|
self.id,
|
|
&wgpu_descriptor,
|
|
None,
|
|
);
|
|
|
|
self.error_handler.push_error(err);
|
|
|
|
Ok(GPUBindGroupLayout {
|
|
instance: self.instance.clone(),
|
|
id,
|
|
label: descriptor.label,
|
|
})
|
|
}
|
|
|
|
#[required(1)]
|
|
#[cppgc]
|
|
fn create_pipeline_layout(
|
|
&self,
|
|
#[webidl] descriptor: super::pipeline_layout::GPUPipelineLayoutDescriptor,
|
|
) -> Result<GPUPipelineLayout, JsErrorBox> {
|
|
let bind_group_layouts = descriptor
|
|
.bind_group_layouts
|
|
.into_iter()
|
|
.map(|maybe_bind_group_layout| {
|
|
maybe_bind_group_layout
|
|
.into_option()
|
|
.map(|l| l.id)
|
|
.ok_or_else(|| {
|
|
JsErrorBox::type_error(
|
|
"Nullable GPUBindGroupLayouts are currently not supported",
|
|
)
|
|
})
|
|
})
|
|
.collect::<Result<_, JsErrorBox>>()?;
|
|
|
|
let wgpu_descriptor = wgpu_core::binding_model::PipelineLayoutDescriptor {
|
|
label: Some(Cow::Owned(descriptor.label.clone())),
|
|
bind_group_layouts: Cow::Owned(bind_group_layouts),
|
|
push_constant_ranges: Default::default(),
|
|
};
|
|
|
|
let (id, err) = self.instance.device_create_pipeline_layout(
|
|
self.id,
|
|
&wgpu_descriptor,
|
|
None,
|
|
);
|
|
|
|
self.error_handler.push_error(err);
|
|
|
|
Ok(GPUPipelineLayout {
|
|
instance: self.instance.clone(),
|
|
id,
|
|
label: descriptor.label,
|
|
})
|
|
}
|
|
|
|
#[required(1)]
|
|
#[cppgc]
|
|
fn create_bind_group(
|
|
&self,
|
|
#[webidl] descriptor: super::bind_group::GPUBindGroupDescriptor,
|
|
) -> GPUBindGroup {
|
|
let entries = descriptor
|
|
.entries
|
|
.into_iter()
|
|
.map(|entry| wgpu_core::binding_model::BindGroupEntry {
|
|
binding: entry.binding,
|
|
resource: match entry.resource {
|
|
GPUBindingResource::Sampler(sampler) => {
|
|
BindingResource::Sampler(sampler.id)
|
|
}
|
|
GPUBindingResource::TextureView(texture_view) => {
|
|
BindingResource::TextureView(texture_view.id)
|
|
}
|
|
GPUBindingResource::BufferBinding(buffer_binding) => {
|
|
BindingResource::Buffer(wgpu_core::binding_model::BufferBinding {
|
|
buffer_id: buffer_binding.buffer.id,
|
|
offset: buffer_binding.offset,
|
|
size: buffer_binding.size.and_then(NonZeroU64::new),
|
|
})
|
|
}
|
|
},
|
|
})
|
|
.collect::<Vec<_>>();
|
|
|
|
let wgpu_descriptor = wgpu_core::binding_model::BindGroupDescriptor {
|
|
label: Some(Cow::Owned(descriptor.label.clone())),
|
|
layout: descriptor.layout.id,
|
|
entries: Cow::Owned(entries),
|
|
};
|
|
|
|
let (id, err) =
|
|
self
|
|
.instance
|
|
.device_create_bind_group(self.id, &wgpu_descriptor, None);
|
|
|
|
self.error_handler.push_error(err);
|
|
|
|
GPUBindGroup {
|
|
instance: self.instance.clone(),
|
|
id,
|
|
label: descriptor.label,
|
|
}
|
|
}
|
|
|
|
#[required(1)]
|
|
#[cppgc]
|
|
fn create_shader_module(
|
|
&self,
|
|
#[webidl] descriptor: super::shader::GPUShaderModuleDescriptor,
|
|
) -> GPUShaderModule {
|
|
let wgpu_descriptor = wgpu_core::pipeline::ShaderModuleDescriptor {
|
|
label: Some(Cow::Owned(descriptor.label.clone())),
|
|
shader_bound_checks: wgpu_types::ShaderBoundChecks::default(),
|
|
};
|
|
|
|
let (id, err) = self.instance.device_create_shader_module(
|
|
self.id,
|
|
&wgpu_descriptor,
|
|
wgpu_core::pipeline::ShaderModuleSource::Wgsl(Cow::Owned(
|
|
descriptor.code,
|
|
)),
|
|
None,
|
|
);
|
|
|
|
self.error_handler.push_error(err);
|
|
|
|
GPUShaderModule {
|
|
instance: self.instance.clone(),
|
|
id,
|
|
label: descriptor.label,
|
|
}
|
|
}
|
|
|
|
#[required(1)]
|
|
#[cppgc]
|
|
fn create_compute_pipeline(
|
|
&self,
|
|
#[webidl] descriptor: super::compute_pipeline::GPUComputePipelineDescriptor,
|
|
) -> GPUComputePipeline {
|
|
self.new_compute_pipeline(descriptor)
|
|
}
|
|
|
|
#[required(1)]
|
|
#[cppgc]
|
|
fn create_render_pipeline(
|
|
&self,
|
|
#[webidl] descriptor: super::render_pipeline::GPURenderPipelineDescriptor,
|
|
) -> Result<GPURenderPipeline, JsErrorBox> {
|
|
self.new_render_pipeline(descriptor)
|
|
}
|
|
|
|
#[async_method]
|
|
#[required(1)]
|
|
#[cppgc]
|
|
async fn create_compute_pipeline_async(
|
|
&self,
|
|
#[webidl] descriptor: super::compute_pipeline::GPUComputePipelineDescriptor,
|
|
) -> GPUComputePipeline {
|
|
self.new_compute_pipeline(descriptor)
|
|
}
|
|
|
|
#[async_method]
|
|
#[required(1)]
|
|
#[cppgc]
|
|
async fn create_render_pipeline_async(
|
|
&self,
|
|
#[webidl] descriptor: super::render_pipeline::GPURenderPipelineDescriptor,
|
|
) -> Result<GPURenderPipeline, JsErrorBox> {
|
|
self.new_render_pipeline(descriptor)
|
|
}
|
|
|
|
#[required(1)]
|
|
#[cppgc]
|
|
fn create_command_encoder(
|
|
&self,
|
|
#[webidl] descriptor: super::command_encoder::GPUCommandEncoderDescriptor,
|
|
) -> GPUCommandEncoder {
|
|
let wgpu_descriptor = wgpu_types::CommandEncoderDescriptor {
|
|
label: Some(Cow::Owned(descriptor.label.clone())),
|
|
};
|
|
|
|
let (id, err) = self.instance.device_create_command_encoder(
|
|
self.id,
|
|
&wgpu_descriptor,
|
|
None,
|
|
);
|
|
|
|
self.error_handler.push_error(err);
|
|
|
|
GPUCommandEncoder {
|
|
instance: self.instance.clone(),
|
|
error_handler: self.error_handler.clone(),
|
|
id,
|
|
label: descriptor.label,
|
|
}
|
|
}
|
|
|
|
#[required(1)]
|
|
#[cppgc]
|
|
fn create_render_bundle_encoder(
|
|
&self,
|
|
#[webidl]
|
|
descriptor: super::render_bundle::GPURenderBundleEncoderDescriptor,
|
|
) -> GPURenderBundleEncoder {
|
|
let wgpu_descriptor = wgpu_core::command::RenderBundleEncoderDescriptor {
|
|
label: Some(Cow::Owned(descriptor.label.clone())),
|
|
color_formats: Cow::Owned(
|
|
descriptor
|
|
.color_formats
|
|
.into_iter()
|
|
.map(|format| format.into_option().map(Into::into))
|
|
.collect::<Vec<_>>(),
|
|
),
|
|
depth_stencil: descriptor.depth_stencil_format.map(|format| {
|
|
wgpu_types::RenderBundleDepthStencil {
|
|
format: format.into(),
|
|
depth_read_only: descriptor.depth_read_only,
|
|
stencil_read_only: descriptor.stencil_read_only,
|
|
}
|
|
}),
|
|
sample_count: descriptor.sample_count,
|
|
multiview: None,
|
|
};
|
|
|
|
let res = wgpu_core::command::RenderBundleEncoder::new(
|
|
&wgpu_descriptor,
|
|
self.id,
|
|
None,
|
|
);
|
|
let (encoder, err) = match res {
|
|
Ok(encoder) => (encoder, None),
|
|
Err(e) => (
|
|
wgpu_core::command::RenderBundleEncoder::dummy(self.id),
|
|
Some(e),
|
|
),
|
|
};
|
|
|
|
self.error_handler.push_error(err);
|
|
|
|
GPURenderBundleEncoder {
|
|
instance: self.instance.clone(),
|
|
error_handler: self.error_handler.clone(),
|
|
encoder: RefCell::new(Some(encoder)),
|
|
label: descriptor.label,
|
|
}
|
|
}
|
|
|
|
#[required(1)]
|
|
#[cppgc]
|
|
fn create_query_set(
|
|
&self,
|
|
#[webidl] descriptor: crate::query_set::GPUQuerySetDescriptor,
|
|
) -> GPUQuerySet {
|
|
let wgpu_descriptor = wgpu_core::resource::QuerySetDescriptor {
|
|
label: Some(Cow::Owned(descriptor.label.clone())),
|
|
ty: descriptor.r#type.clone().into(),
|
|
count: descriptor.count,
|
|
};
|
|
|
|
let (id, err) =
|
|
self
|
|
.instance
|
|
.device_create_query_set(self.id, &wgpu_descriptor, None);
|
|
|
|
self.error_handler.push_error(err);
|
|
|
|
GPUQuerySet {
|
|
instance: self.instance.clone(),
|
|
id,
|
|
r#type: descriptor.r#type,
|
|
count: descriptor.count,
|
|
label: descriptor.label,
|
|
}
|
|
}
|
|
|
|
// TODO: support returning same promise
|
|
#[async_method]
|
|
#[getter]
|
|
#[cppgc]
|
|
async fn lost(&self) -> GPUDeviceLostInfo {
|
|
if let Some(lost_receiver) = self.lost_receiver.lock().await.take() {
|
|
let _ = lost_receiver.await;
|
|
}
|
|
|
|
GPUDeviceLostInfo
|
|
}
|
|
|
|
#[required(1)]
|
|
fn push_error_scope(&self, #[webidl] filter: super::error::GPUErrorFilter) {
|
|
self
|
|
.error_handler
|
|
.scopes
|
|
.lock()
|
|
.unwrap()
|
|
.push((filter, vec![]));
|
|
}
|
|
|
|
#[async_method]
|
|
async fn pop_error_scope<'a>(
|
|
&self,
|
|
//scope: &mut v8::HandleScope<'a>,
|
|
) -> Result<v8::Local<'a, v8::Value>, JsErrorBox> {
|
|
/* if self.error_handler.is_lost.get().is_some() {
|
|
return Ok(v8::null(scope).into());
|
|
}
|
|
|
|
let Some((_, errors)) = self.error_handler.scopes.borrow_mut().pop() else {
|
|
return Err(JsErrorBox::new(
|
|
"DOMExceptionOperationError",
|
|
"There are no error scopes on the error scope stack",
|
|
));
|
|
};
|
|
|
|
if let Some(err) = errors.into_iter().next() {
|
|
Ok(deno_core::error::to_v8_error(scope, &err))
|
|
} else {
|
|
Ok(v8::null(scope).into())
|
|
}*/
|
|
|
|
todo!()
|
|
}
|
|
}
|
|
|
|
impl GPUDevice {
|
|
fn new_compute_pipeline(
|
|
&self,
|
|
descriptor: super::compute_pipeline::GPUComputePipelineDescriptor,
|
|
) -> GPUComputePipeline {
|
|
let wgpu_descriptor = wgpu_core::pipeline::ComputePipelineDescriptor {
|
|
label: Some(Cow::Owned(descriptor.label.clone())),
|
|
layout: descriptor.layout.into(),
|
|
stage: ProgrammableStageDescriptor {
|
|
module: descriptor.compute.module.id,
|
|
entry_point: descriptor.compute.entry_point.map(Into::into),
|
|
constants: Cow::Owned(
|
|
descriptor.compute.constants.into_iter().collect(),
|
|
),
|
|
zero_initialize_workgroup_memory: true,
|
|
},
|
|
cache: None,
|
|
};
|
|
|
|
let (id, err) = self.instance.device_create_compute_pipeline(
|
|
self.id,
|
|
&wgpu_descriptor,
|
|
None,
|
|
None,
|
|
);
|
|
|
|
self.error_handler.push_error(err);
|
|
|
|
GPUComputePipeline {
|
|
instance: self.instance.clone(),
|
|
error_handler: self.error_handler.clone(),
|
|
id,
|
|
label: descriptor.label.clone(),
|
|
}
|
|
}
|
|
|
|
fn new_render_pipeline(
|
|
&self,
|
|
descriptor: super::render_pipeline::GPURenderPipelineDescriptor,
|
|
) -> Result<GPURenderPipeline, JsErrorBox> {
|
|
let vertex = wgpu_core::pipeline::VertexState {
|
|
stage: ProgrammableStageDescriptor {
|
|
module: descriptor.vertex.module.id,
|
|
entry_point: descriptor.vertex.entry_point.map(Into::into),
|
|
constants: Cow::Owned(
|
|
descriptor.vertex.constants.into_iter().collect(),
|
|
),
|
|
zero_initialize_workgroup_memory: true,
|
|
},
|
|
buffers: Cow::Owned(
|
|
descriptor
|
|
.vertex
|
|
.buffers
|
|
.into_iter()
|
|
.map(|b| {
|
|
let layout = b.into_option().ok_or_else(|| {
|
|
JsErrorBox::type_error(
|
|
"Nullable GPUVertexBufferLayouts are currently not supported",
|
|
)
|
|
})?;
|
|
|
|
Ok(wgpu_core::pipeline::VertexBufferLayout {
|
|
array_stride: layout.array_stride,
|
|
step_mode: layout.step_mode.into(),
|
|
attributes: Cow::Owned(
|
|
layout
|
|
.attributes
|
|
.into_iter()
|
|
.map(|attr| wgpu_types::VertexAttribute {
|
|
format: attr.format.into(),
|
|
offset: attr.offset,
|
|
shader_location: attr.shader_location,
|
|
})
|
|
.collect(),
|
|
),
|
|
})
|
|
})
|
|
.collect::<Result<_, JsErrorBox>>()?,
|
|
),
|
|
};
|
|
|
|
let primitive = wgpu_types::PrimitiveState {
|
|
topology: descriptor.primitive.topology.into(),
|
|
strip_index_format: descriptor
|
|
.primitive
|
|
.strip_index_format
|
|
.map(Into::into),
|
|
front_face: descriptor.primitive.front_face.into(),
|
|
cull_mode: descriptor.primitive.cull_mode.into(),
|
|
unclipped_depth: descriptor.primitive.unclipped_depth,
|
|
polygon_mode: Default::default(),
|
|
conservative: false,
|
|
};
|
|
|
|
let depth_stencil = descriptor.depth_stencil.map(|depth_stencil| {
|
|
let front = wgpu_types::StencilFaceState {
|
|
compare: depth_stencil.stencil_front.compare.into(),
|
|
fail_op: depth_stencil.stencil_front.fail_op.into(),
|
|
depth_fail_op: depth_stencil.stencil_front.depth_fail_op.into(),
|
|
pass_op: depth_stencil.stencil_front.pass_op.into(),
|
|
};
|
|
let back = wgpu_types::StencilFaceState {
|
|
compare: depth_stencil.stencil_back.compare.into(),
|
|
fail_op: depth_stencil.stencil_back.fail_op.into(),
|
|
depth_fail_op: depth_stencil.stencil_back.depth_fail_op.into(),
|
|
pass_op: depth_stencil.stencil_back.pass_op.into(),
|
|
};
|
|
|
|
wgpu_types::DepthStencilState {
|
|
format: depth_stencil.format.into(),
|
|
depth_write_enabled: depth_stencil
|
|
.depth_write_enabled
|
|
.unwrap_or_default(),
|
|
depth_compare: depth_stencil
|
|
.depth_compare
|
|
.map(Into::into)
|
|
.unwrap_or(wgpu_types::CompareFunction::Never), // TODO: wgpu should be optional here
|
|
stencil: wgpu_types::StencilState {
|
|
front,
|
|
back,
|
|
read_mask: depth_stencil.stencil_read_mask,
|
|
write_mask: depth_stencil.stencil_write_mask,
|
|
},
|
|
bias: wgpu_types::DepthBiasState {
|
|
constant: depth_stencil.depth_bias,
|
|
slope_scale: depth_stencil.depth_bias_slope_scale,
|
|
clamp: depth_stencil.depth_bias_clamp,
|
|
},
|
|
}
|
|
});
|
|
|
|
let multisample = wgpu_types::MultisampleState {
|
|
count: descriptor.multisample.count,
|
|
mask: descriptor.multisample.mask as u64,
|
|
alpha_to_coverage_enabled: descriptor
|
|
.multisample
|
|
.alpha_to_coverage_enabled,
|
|
};
|
|
|
|
let fragment = descriptor
|
|
.fragment
|
|
.map(|fragment| {
|
|
Ok::<_, JsErrorBox>(wgpu_core::pipeline::FragmentState {
|
|
stage: ProgrammableStageDescriptor {
|
|
module: fragment.module.id,
|
|
entry_point: fragment.entry_point.map(Into::into),
|
|
constants: Cow::Owned(fragment.constants.into_iter().collect()),
|
|
zero_initialize_workgroup_memory: true,
|
|
},
|
|
targets: Cow::Owned(
|
|
fragment
|
|
.targets
|
|
.into_iter()
|
|
.map(|target| {
|
|
target
|
|
.into_option()
|
|
.map(|target| {
|
|
Ok(wgpu_types::ColorTargetState {
|
|
format: target.format.into(),
|
|
blend: target.blend.map(|blend| wgpu_types::BlendState {
|
|
color: wgpu_types::BlendComponent {
|
|
src_factor: blend.color.src_factor.into(),
|
|
dst_factor: blend.color.dst_factor.into(),
|
|
operation: blend.color.operation.into(),
|
|
},
|
|
alpha: wgpu_types::BlendComponent {
|
|
src_factor: blend.alpha.src_factor.into(),
|
|
dst_factor: blend.alpha.dst_factor.into(),
|
|
operation: blend.alpha.operation.into(),
|
|
},
|
|
}),
|
|
write_mask: wgpu_types::ColorWrites::from_bits(
|
|
target.write_mask,
|
|
)
|
|
.ok_or_else(|| {
|
|
JsErrorBox::type_error("usage is not valid")
|
|
})?,
|
|
})
|
|
})
|
|
.transpose()
|
|
})
|
|
.collect::<Result<_, JsErrorBox>>()?,
|
|
),
|
|
})
|
|
})
|
|
.transpose()?;
|
|
|
|
let wgpu_descriptor = wgpu_core::pipeline::RenderPipelineDescriptor {
|
|
label: Some(Cow::Owned(descriptor.label.clone())),
|
|
layout: descriptor.layout.into(),
|
|
vertex,
|
|
primitive,
|
|
depth_stencil,
|
|
multisample,
|
|
fragment,
|
|
cache: None,
|
|
multiview: None,
|
|
};
|
|
|
|
let (id, err) = self.instance.device_create_render_pipeline(
|
|
self.id,
|
|
&wgpu_descriptor,
|
|
None,
|
|
None,
|
|
);
|
|
|
|
self.error_handler.push_error(err);
|
|
|
|
Ok(GPURenderPipeline {
|
|
instance: self.instance.clone(),
|
|
error_handler: self.error_handler.clone(),
|
|
id,
|
|
label: descriptor.label,
|
|
})
|
|
}
|
|
}
|
|
|
|
pub struct GPUDeviceLostInfo;
|
|
|
|
impl GarbageCollected for GPUDeviceLostInfo {}
|
|
|
|
#[op2]
|
|
impl GPUDeviceLostInfo {
|
|
#[getter]
|
|
#[string]
|
|
fn reason(&self) -> &'static str {
|
|
"unknown"
|
|
}
|
|
|
|
#[getter]
|
|
#[string]
|
|
fn message(&self) -> &'static str {
|
|
"device was lost"
|
|
}
|
|
}
|