webgpu: Use wgpu's instead of string errors and update limits handling (#32925)

* Use wgpu specific errors

Signed-off-by: sagudev <16504129+sagudev@users.noreply.github.com>

* fixup expect

Signed-off-by: sagudev <16504129+sagudev@users.noreply.github.com>

* WIP

Signed-off-by: sagudev <16504129+sagudev@users.noreply.github.com>

* Fix records erasing enforcerange

Signed-off-by: sagudev <16504129+sagudev@users.noreply.github.com>

* page can already be destroyed

Signed-off-by: sagudev <16504129+sagudev@users.noreply.github.com>

* Support more limits

Signed-off-by: sagudev <16504129+sagudev@users.noreply.github.com>

* Set good results

Signed-off-by: sagudev <16504129+sagudev@users.noreply.github.com>

* Set OK (not PASS) expect CRASH

Signed-off-by: sagudev <16504129+sagudev@users.noreply.github.com>

* fixup expectation

Signed-off-by: sagudev <16504129+sagudev@users.noreply.github.com>

* bad expectations

https://github.com/gfx-rs/wgpu/issues/6075
Signed-off-by: sagudev <16504129+sagudev@users.noreply.github.com>

* set bad expectation

render bundleencoder needs to be rewritten

Signed-off-by: sagudev <16504129+sagudev@users.noreply.github.com>

---------

Signed-off-by: sagudev <16504129+sagudev@users.noreply.github.com>
This commit is contained in:
Samson 2024-08-04 19:19:46 +02:00 committed by GitHub
parent b366a02318
commit 5e59988c87
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 496 additions and 871 deletions

View file

@ -718,7 +718,7 @@ def getJSToNativeConversionInfo(type, descriptorProvider, failureCode=None,
isMember="Sequence",
isAutoRooted=isAutoRooted)
declType = wrapInNativeContainerType(type, innerInfo.declType)
config = getConversionConfigForType(type, isEnforceRange, isClamp, treatNullAs)
config = getConversionConfigForType(type, innerContainerType(type).hasEnforceRange(), isClamp, treatNullAs)
if type.nullable():
declType = CGWrapper(declType, pre="Option<", post=" >")

View file

@ -2,15 +2,17 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
use std::convert::TryFrom;
use std::rc::Rc;
use dom_struct::dom_struct;
use js::jsapi::{Heap, JSObject};
use webgpu::wgc::instance::RequestDeviceError;
use webgpu::wgt::MemoryHints;
use webgpu::{wgt, WebGPU, WebGPUAdapter, WebGPURequest, WebGPUResponse};
use super::bindings::codegen::Bindings::WebGPUBinding::GPUDeviceLostReason;
use super::gpusupportedfeatures::GPUSupportedFeatures;
use super::gpusupportedlimits::set_limit;
use super::types::{GPUAdapterInfo, GPUSupportedLimits};
use crate::dom::bindings::codegen::Bindings::WebGPUBinding::{
GPUAdapterMethods, GPUDeviceDescriptor,
@ -129,84 +131,10 @@ impl GPUAdapterMethods for GPUAdapter {
};
if let Some(limits) = &descriptor.requiredLimits {
for (limit, value) in (*limits).iter() {
let v = u32::try_from(*value).unwrap_or(u32::MAX);
match limit.as_ref() {
"maxTextureDimension1D" => desc.required_limits.max_texture_dimension_1d = v,
"maxTextureDimension2D" => desc.required_limits.max_texture_dimension_2d = v,
"maxTextureDimension3D" => desc.required_limits.max_texture_dimension_3d = v,
"maxTextureArrayLayers" => desc.required_limits.max_texture_array_layers = v,
"maxBindGroups" => desc.required_limits.max_bind_groups = v,
"maxBindingsPerBindGroup" => {
desc.required_limits.max_bindings_per_bind_group = v
},
"maxDynamicUniformBuffersPerPipelineLayout" => {
desc.required_limits
.max_dynamic_uniform_buffers_per_pipeline_layout = v
},
"maxDynamicStorageBuffersPerPipelineLayout" => {
desc.required_limits
.max_dynamic_storage_buffers_per_pipeline_layout = v
},
"maxSampledTexturesPerShaderStage" => {
desc.required_limits.max_sampled_textures_per_shader_stage = v
},
"maxSamplersPerShaderStage" => {
desc.required_limits.max_samplers_per_shader_stage = v
},
"maxStorageBuffersPerShaderStage" => {
desc.required_limits.max_storage_buffers_per_shader_stage = v
},
"maxStorageTexturesPerShaderStage" => {
desc.required_limits.max_storage_textures_per_shader_stage = v
},
"maxUniformBuffersPerShaderStage" => {
desc.required_limits.max_uniform_buffers_per_shader_stage = v
},
"maxUniformBufferBindingSize" => {
desc.required_limits.max_uniform_buffer_binding_size = v
},
"maxStorageBufferBindingSize" => {
desc.required_limits.max_storage_buffer_binding_size = v
},
"minUniformBufferOffsetAlignment" => {
desc.required_limits.min_uniform_buffer_offset_alignment = v
},
"minStorageBufferOffsetAlignment" => {
desc.required_limits.min_storage_buffer_offset_alignment = v
},
"maxVertexBuffers" => desc.required_limits.max_vertex_buffers = v,
"maxBufferSize" => desc.required_limits.max_buffer_size = *value,
"maxVertexAttributes" => desc.required_limits.max_vertex_attributes = v,
"maxVertexBufferArrayStride" => {
desc.required_limits.max_vertex_buffer_array_stride = v
},
"maxInterStageShaderComponents" => {
desc.required_limits.max_inter_stage_shader_components = v
},
"maxComputeWorkgroupStorageSize" => {
desc.required_limits.max_compute_workgroup_storage_size = v
},
"maxComputeInvocationsPerWorkgroup" => {
desc.required_limits.max_compute_invocations_per_workgroup = v
},
"maxComputeWorkgroupSizeX" => {
desc.required_limits.max_compute_workgroup_size_x = v
},
"maxComputeWorkgroupSizeY" => {
desc.required_limits.max_compute_workgroup_size_y = v
},
"maxComputeWorkgroupSizeZ" => {
desc.required_limits.max_compute_workgroup_size_z = v
},
"maxComputeWorkgroupsPerDimension" => {
desc.required_limits.max_compute_workgroups_per_dimension = v
},
_ => {
error!("Unknown required limit: {limit} with value {value}");
// we should reject but spec is still evolving
// promise.reject_error(Error::Operation);
// return promise;
},
if !set_limit(&mut desc.required_limits, limit.as_ref(), *value) {
warn!("Unknown GPUDevice limit: {limit}");
promise.reject_error(Error::Operation);
return promise;
}
}
}
@ -267,8 +195,7 @@ impl GPUAdapterMethods for GPUAdapter {
impl AsyncWGPUListener for GPUAdapter {
fn handle_response(&self, response: WebGPUResponse, promise: &Rc<Promise>) {
match response {
WebGPUResponse::Device(Ok(device)) => {
let descriptor = device.descriptor;
WebGPUResponse::Device((device_id, queue_id, Ok(descriptor))) => {
let device = GPUDevice::new(
&self.global(),
self.channel.clone(),
@ -276,16 +203,37 @@ impl AsyncWGPUListener for GPUAdapter {
Heap::default(),
descriptor.required_features,
descriptor.required_limits,
device.device_id,
device.queue_id,
device_id,
queue_id,
descriptor.label.unwrap_or_default(),
);
self.global().add_gpu_device(&device);
promise.resolve_native(&device);
},
WebGPUResponse::Device(Err(e)) => {
warn!("Could not get GPUDevice({:?})", e);
promise.reject_error(Error::Operation);
WebGPUResponse::Device((_, _, Err(RequestDeviceError::UnsupportedFeature(f)))) => {
promise.reject_error(Error::Type(
RequestDeviceError::UnsupportedFeature(f).to_string(),
))
},
WebGPUResponse::Device((
_,
_,
Err(RequestDeviceError::LimitsExceeded(_) | RequestDeviceError::InvalidAdapter),
)) => promise.reject_error(Error::Operation),
WebGPUResponse::Device((device_id, queue_id, Err(e))) => {
let device = GPUDevice::new(
&self.global(),
self.channel.clone(),
self,
Heap::default(),
wgt::Features::default(),
wgt::Limits::default(),
device_id,
queue_id,
String::new(),
);
device.lose(GPUDeviceLostReason::Unknown, e.to_string());
promise.resolve_native(&device);
},
WebGPUResponse::None => unreachable!("Failed to get a response for RequestDevice"),
_ => unreachable!("GPUAdapter received wrong WebGPUResponse"),

View file

@ -3,6 +3,7 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
use dom_struct::dom_struct;
use num_traits::bounds::UpperBounded;
use webgpu::wgt::Limits;
use GPUSupportedLimits_Binding::GPUSupportedLimitsMethods;
@ -173,4 +174,144 @@ impl GPUSupportedLimitsMethods for GPUSupportedLimits {
fn MaxComputeWorkgroupsPerDimension(&self) -> u32 {
self.limits.max_compute_workgroups_per_dimension
}
/// <https://gpuweb.github.io/gpuweb/#dom-gpusupportedlimits-maxbindgroupsplusvertexbuffers>
fn MaxBindGroupsPlusVertexBuffers(&self) -> u32 {
// Not on wgpu yet, so we craft it manually
self.limits.max_bind_groups + self.limits.max_vertex_buffers
}
/// <https://gpuweb.github.io/gpuweb/#dom-gpusupportedlimits-maxinterstageshadervariables>
fn MaxInterStageShaderVariables(&self) -> u32 {
// Not in wgpu yet, so we use default value from spec
16
}
/// <https://gpuweb.github.io/gpuweb/#dom-gpusupportedlimits-maxcolorattachments>
fn MaxColorAttachments(&self) -> u32 {
self.limits.max_color_attachments
}
/// <https://gpuweb.github.io/gpuweb/#dom-gpusupportedlimits-maxcolorattachmentbytespersample>
fn MaxColorAttachmentBytesPerSample(&self) -> u32 {
self.limits.max_color_attachment_bytes_per_sample
}
}
/// Returns false if unknown limit or other value error
pub fn set_limit(limits: &mut Limits, limit: &str, value: u64) -> bool {
/// per spec defaults are lower bounds for values
///
/// https://www.w3.org/TR/webgpu/#limit-class-maximum
fn set_maximum<T>(limit: &mut T, value: u64) -> bool
where
T: Ord + Copy + TryFrom<u64> + UpperBounded,
{
if let Ok(value) = T::try_from(value) {
*limit = value.max(*limit);
true
} else {
false
}
}
/// per spec defaults are higher bounds for values
///
/// <https://www.w3.org/TR/webgpu/#limit-class-alignment>
fn set_alignment<T>(limit: &mut T, value: u64) -> bool
where
T: Ord + Copy + TryFrom<u64> + UpperBounded,
{
if !value.is_power_of_two() {
return false;
}
if let Ok(value) = T::try_from(value) {
*limit = value.min(*limit);
true
} else {
false
}
}
match limit {
"maxTextureDimension1D" => set_maximum(&mut limits.max_texture_dimension_1d, value),
"maxTextureDimension2D" => set_maximum(&mut limits.max_texture_dimension_2d, value),
"maxTextureDimension3D" => set_maximum(&mut limits.max_texture_dimension_3d, value),
"maxTextureArrayLayers" => set_maximum(&mut limits.max_texture_array_layers, value),
"maxBindGroups" => set_maximum(&mut limits.max_bind_groups, value),
"maxBindGroupsPlusVertexBuffers" => {
// not in wgpu but we're allowed to give back better limits than requested.
// we use dummy value to still produce value verification
let mut v: u32 = 0;
set_maximum(&mut v, value)
},
"maxBindingsPerBindGroup" => set_maximum(&mut limits.max_bindings_per_bind_group, value),
"maxDynamicUniformBuffersPerPipelineLayout" => set_maximum(
&mut limits.max_dynamic_uniform_buffers_per_pipeline_layout,
value,
),
"maxDynamicStorageBuffersPerPipelineLayout" => set_maximum(
&mut limits.max_dynamic_storage_buffers_per_pipeline_layout,
value,
),
"maxSampledTexturesPerShaderStage" => {
set_maximum(&mut limits.max_sampled_textures_per_shader_stage, value)
},
"maxSamplersPerShaderStage" => {
set_maximum(&mut limits.max_samplers_per_shader_stage, value)
},
"maxStorageBuffersPerShaderStage" => {
set_maximum(&mut limits.max_storage_buffers_per_shader_stage, value)
},
"maxStorageTexturesPerShaderStage" => {
set_maximum(&mut limits.max_storage_textures_per_shader_stage, value)
},
"maxUniformBuffersPerShaderStage" => {
set_maximum(&mut limits.max_uniform_buffers_per_shader_stage, value)
},
"maxUniformBufferBindingSize" => {
set_maximum(&mut limits.max_uniform_buffer_binding_size, value)
},
"maxStorageBufferBindingSize" => {
set_maximum(&mut limits.max_storage_buffer_binding_size, value)
},
"minUniformBufferOffsetAlignment" => {
set_alignment(&mut limits.min_uniform_buffer_offset_alignment, value)
},
"minStorageBufferOffsetAlignment" => {
set_alignment(&mut limits.min_storage_buffer_offset_alignment, value)
},
"maxVertexBuffers" => set_maximum(&mut limits.max_vertex_buffers, value),
"maxBufferSize" => set_maximum(&mut limits.max_buffer_size, value),
"maxVertexAttributes" => set_maximum(&mut limits.max_vertex_attributes, value),
"maxVertexBufferArrayStride" => {
set_maximum(&mut limits.max_vertex_buffer_array_stride, value)
},
"maxInterStageShaderComponents" => {
set_maximum(&mut limits.max_inter_stage_shader_components, value)
},
"maxInterStageShaderVariables" => {
// not in wgpu but we're allowed to give back better limits than requested.
// we use dummy value to still produce value verification
let mut v: u32 = 0;
set_maximum(&mut v, value)
},
"maxColorAttachments" => set_maximum(&mut limits.max_color_attachments, value),
"maxColorAttachmentBytesPerSample" => {
set_maximum(&mut limits.max_color_attachment_bytes_per_sample, value)
},
"maxComputeWorkgroupStorageSize" => {
set_maximum(&mut limits.max_compute_workgroup_storage_size, value)
},
"maxComputeInvocationsPerWorkgroup" => {
set_maximum(&mut limits.max_compute_invocations_per_workgroup, value)
},
"maxComputeWorkgroupSizeX" => set_maximum(&mut limits.max_compute_workgroup_size_x, value),
"maxComputeWorkgroupSizeY" => set_maximum(&mut limits.max_compute_workgroup_size_y, value),
"maxComputeWorkgroupSizeZ" => set_maximum(&mut limits.max_compute_workgroup_size_z, value),
"maxComputeWorkgroupsPerDimension" => {
set_maximum(&mut limits.max_compute_workgroups_per_dimension, value)
},
_ => false,
}
}

View file

@ -21,7 +21,7 @@ interface GPUSupportedLimits {
readonly attribute unsigned long maxTextureDimension3D;
readonly attribute unsigned long maxTextureArrayLayers;
readonly attribute unsigned long maxBindGroups;
//readonly attribute unsigned long maxBindGroupsPlusVertexBuffers;
readonly attribute unsigned long maxBindGroupsPlusVertexBuffers;
readonly attribute unsigned long maxBindingsPerBindGroup;
readonly attribute unsigned long maxDynamicUniformBuffersPerPipelineLayout;
readonly attribute unsigned long maxDynamicStorageBuffersPerPipelineLayout;
@ -39,9 +39,9 @@ interface GPUSupportedLimits {
readonly attribute unsigned long maxVertexAttributes;
readonly attribute unsigned long maxVertexBufferArrayStride;
readonly attribute unsigned long maxInterStageShaderComponents;
//readonly attribute unsigned long maxInterStageShaderVariables;
//readonly attribute unsigned long maxColorAttachments;
//readonly attribute unsigned long maxColorAttachmentBytesPerSample;
readonly attribute unsigned long maxInterStageShaderVariables;
readonly attribute unsigned long maxColorAttachments;
readonly attribute unsigned long maxColorAttachmentBytesPerSample;
readonly attribute unsigned long maxComputeWorkgroupStorageSize;
readonly attribute unsigned long maxComputeInvocationsPerWorkgroup;
readonly attribute unsigned long maxComputeWorkgroupSizeX;

View file

@ -2427,8 +2427,9 @@ impl ScriptThread {
pipeline_id,
} => {
self.gpu_id_hub.free_device_id(device_id);
let global = self.documents.borrow().find_global(pipeline_id).unwrap();
if let Some(global) = self.documents.borrow().find_global(pipeline_id) {
global.remove_gpu_device(WebGPUDevice(device_id));
} // page can already be destroyed
},
WebGPUMsg::FreeBuffer(id) => self.gpu_id_hub.free_buffer_id(id),
WebGPUMsg::FreePipelineLayout(id) => self.gpu_id_hub.free_pipeline_layout_id(id),

View file

@ -7,6 +7,8 @@
use ipc_channel::ipc::IpcSharedMemory;
use serde::{Deserialize, Serialize};
use wgc::pipeline::CreateShaderModuleError;
use wgpu_core::instance::{RequestAdapterError, RequestDeviceError};
use wgpu_core::resource::BufferAccessError;
pub use {wgpu_core as wgc, wgpu_types as wgt};
use crate::identity::*;
@ -63,22 +65,20 @@ pub struct Adapter {
pub channel: WebGPU,
}
#[derive(Debug, Deserialize, Serialize)]
pub struct Device {
pub device_id: WebGPUDevice,
pub queue_id: WebGPUQueue,
pub descriptor: wgt::DeviceDescriptor<Option<String>>,
}
#[derive(Debug, Deserialize, Serialize)]
#[allow(clippy::large_enum_variant)]
pub enum WebGPUResponse {
/// WebGPU is disabled
None,
// TODO: use wgpu errors
Adapter(Result<Adapter, String>),
Device(Result<Device, String>),
BufferMapAsync(Result<IpcSharedMemory, String>),
Adapter(Result<Adapter, RequestAdapterError>),
Device(
(
WebGPUDevice,
WebGPUQueue,
Result<wgt::DeviceDescriptor<Option<String>>, RequestDeviceError>,
),
),
BufferMapAsync(Result<IpcSharedMemory, BufferAccessError>),
SubmittedWorkDone,
PoppedErrorScope(Result<Option<Error>, PopError>),
CompilationInfo(Option<ShaderCompilationInfo>),

View file

@ -38,8 +38,8 @@ use crate::gpu_error::ErrorScope;
use crate::poll_thread::Poller;
use crate::render_commands::apply_render_command;
use crate::{
Adapter, ComputePassId, Device, Error, PopError, PresentationData, RenderPassId, Transmute,
WebGPU, WebGPUAdapter, WebGPUDevice, WebGPUMsg, WebGPUQueue, WebGPURequest, WebGPUResponse,
Adapter, ComputePassId, Error, PopError, PresentationData, RenderPassId, Transmute, WebGPU,
WebGPUAdapter, WebGPUDevice, WebGPUMsg, WebGPUQueue, WebGPURequest, WebGPUResponse,
};
pub const PRESENTATION_BUFFER_COUNT: usize = 10;
@ -189,8 +189,7 @@ impl WGPU {
let callback = BufferMapCallback::from_rust(Box::from(
move |result: BufferAccessResult| {
drop(token);
let response = result
.map(|_| {
let response = result.map(|_| {
let global = &glob;
let (slice_pointer, range_size) = gfx_select!(buffer_id =>
global.buffer_get_mapped_range(buffer_id, 0, None))
@ -204,8 +203,7 @@ impl WGPU {
};
IpcSharedMemory::from_bytes(data)
})
.map_err(|e| e.to_string());
});
if let Err(e) =
resp_sender.send(WebGPUResponse::BufferMapAsync(response))
{
@ -226,13 +224,14 @@ impl WGPU {
operation
));
self.poller.wake();
if let Err(ref e) = result {
if let Err(e) = &result {
if let Err(w) =
sender.send(WebGPUResponse::BufferMapAsync(Err(e.to_string())))
sender.send(WebGPUResponse::BufferMapAsync(Err(e.to_owned())))
{
warn!("Failed to send BufferMapAsync Response ({:?})", w);
}
}
// Per spec we also need to raise validation error here
self.maybe_dispatch_wgpu_error(device_id, result.err());
},
WebGPURequest::CommandEncoderFinish {
@ -691,8 +690,7 @@ impl WGPU {
limits,
channel: WebGPU(self.sender.clone()),
}
})
.map_err(|e| e.to_string());
});
if let Err(e) = sender.send(WebGPUResponse::Adapter(response)) {
warn!(
@ -722,8 +720,11 @@ impl WGPU {
Some(device_id),
Some(device_id.transmute()),
));
let device = WebGPUDevice(device_id);
let queue = WebGPUQueue(queue_id);
if let Some(e) = error {
if let Err(e) = sender.send(WebGPUResponse::Device(Err(e.to_string())))
if let Err(e) =
sender.send(WebGPUResponse::Device((device, queue, Err(e))))
{
warn!(
"Failed to send response to WebGPURequest::RequestDevice ({})",
@ -732,8 +733,6 @@ impl WGPU {
}
continue;
}
let device = WebGPUDevice(device_id);
let queue = WebGPUQueue(queue_id);
{
self.devices
.lock()
@ -777,11 +776,9 @@ impl WGPU {
}
}));
gfx_select!(device_id => global.device_set_device_lost_closure(device_id, callback));
if let Err(e) = sender.send(WebGPUResponse::Device(Ok(Device {
device_id: device,
queue_id: queue,
descriptor,
}))) {
if let Err(e) =
sender.send(WebGPUResponse::Device((device, queue, Ok(descriptor))))
{
warn!(
"Failed to send response to WebGPURequest::RequestDevice ({})",
e

File diff suppressed because it is too large Load diff