webgpu: Update wgpu and revamp computepass (#32575)

* Do not wait on drop, but rather wake poller thread

* Update wgpu and render stuff

* Set some good expectations

* Update wgpu again

* handle IPC error as warning

* More good expectations

* Some more expectations

CTS does not match the spec: https://github.com/gpuweb/cts/issues/3806

* This expectations are due to other changes in servo

also happening on main

* Explain error_command_encoders and remove RefCell around it

* fixup

* store validness of passes

* More good expectations

* More docs

* this assert is wrong

* This is even more right per CTS/spec

Only Command encoder state errors are allowed here, but wgpu does not exposes them.

* More good expectations

* One bad expectation

* Fix my english
This commit is contained in:
Samson 2024-06-28 06:49:35 +02:00 committed by GitHub
parent fced0b4940
commit e9cf4d4971
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
16 changed files with 372 additions and 1870 deletions

View file

@ -16,8 +16,7 @@ use wgc::binding_model::{
BindGroupDescriptor, BindGroupLayoutDescriptor, PipelineLayoutDescriptor,
};
use wgc::command::{
ComputePass, ImageCopyBuffer, ImageCopyTexture, RenderBundleDescriptor, RenderBundleEncoder,
RenderPass,
ImageCopyBuffer, ImageCopyTexture, RenderBundleDescriptor, RenderBundleEncoder, RenderPass,
};
use wgc::device::HostMap;
use wgc::id;
@ -191,6 +190,7 @@ pub enum WebGPURequest {
DropShaderModule(id::ShaderModuleId),
DropRenderBundle(id::RenderBundleId),
DropQuerySet(id::QuerySetId),
DropComputePass(id::ComputePassEncoderId),
Exit(IpcSender<()>),
RenderBundleEncoderFinish {
render_bundle_encoder: RenderBundleEncoder,
@ -210,13 +210,45 @@ pub enum WebGPURequest {
device_id: id::DeviceId,
pipeline_id: PipelineId,
},
RunComputePass {
BeginComputePass {
command_encoder_id: id::CommandEncoderId,
compute_pass: Option<ComputePass>,
compute_pass_id: ComputePassId,
label: Option<Cow<'static, str>>,
device_id: id::DeviceId,
},
RunRenderPass {
ComputePassSetPipeline {
compute_pass_id: ComputePassId,
pipeline_id: id::ComputePipelineId,
device_id: id::DeviceId,
},
ComputePassSetBindGroup {
compute_pass_id: ComputePassId,
index: u32,
bind_group_id: id::BindGroupId,
offsets: Vec<u32>,
device_id: id::DeviceId,
},
ComputePassDispatchWorkgroups {
compute_pass_id: ComputePassId,
x: u32,
y: u32,
z: u32,
device_id: id::DeviceId,
},
ComputePassDispatchWorkgroupsIndirect {
compute_pass_id: ComputePassId,
buffer_id: id::BufferId,
offset: u64,
device_id: id::DeviceId,
},
EndComputePass {
compute_pass_id: ComputePassId,
device_id: id::DeviceId,
command_encoder_id: id::CommandEncoderId,
},
EndRenderPass {
render_pass: Option<RenderPass>,
device_id: id::DeviceId,
},
Submit {
queue_id: id::QueueId,

View file

@ -5,6 +5,8 @@
use malloc_size_of::{MallocSizeOf, MallocSizeOfOps};
use serde::{Deserialize, Serialize};
pub use crate::wgc::id::markers::ComputePassEncoder as ComputePass;
pub use crate::wgc::id::ComputePassEncoderId as ComputePassId;
use crate::wgc::id::{
AdapterId, BindGroupId, BindGroupLayoutId, BufferId, CommandBufferId, CommandEncoderId,
ComputePipelineId, DeviceId, PipelineLayoutId, QueueId, RenderBundleId, RenderPipelineId,
@ -43,3 +45,4 @@ webgpu_resource!(WebGPUShaderModule, ShaderModuleId);
webgpu_resource!(WebGPUSurface, SurfaceId);
webgpu_resource!(WebGPUTexture, TextureId);
webgpu_resource!(WebGPUTextureView, TextureViewId);
webgpu_resource!(WebGPUComputePass, ComputePassId);

View file

@ -10,9 +10,9 @@ use serde::{Deserialize, Serialize};
use crate::gpu_error::Error;
use crate::identity::WebGPUDevice;
use crate::wgc::id::{
AdapterId, BindGroupId, BindGroupLayoutId, BufferId, CommandBufferId, ComputePipelineId,
DeviceId, PipelineLayoutId, QuerySetId, RenderBundleId, RenderPipelineId, SamplerId,
ShaderModuleId, StagingBufferId, SurfaceId, TextureId, TextureViewId,
AdapterId, BindGroupId, BindGroupLayoutId, BufferId, CommandBufferId, ComputePassEncoderId,
ComputePipelineId, DeviceId, PipelineLayoutId, QuerySetId, RenderBundleId, RenderPipelineId,
SamplerId, ShaderModuleId, StagingBufferId, SurfaceId, TextureId, TextureViewId,
};
/// <https://gpuweb.github.io/gpuweb/#enumdef-gpudevicelostreason>
@ -44,6 +44,7 @@ pub enum WebGPUMsg {
FreeRenderBundle(RenderBundleId),
FreeStagingBuffer(StagingBufferId),
FreeQuerySet(QuerySetId),
FreeComputePass(ComputePassEncoderId),
UncapturedError {
device: WebGPUDevice,
pipeline_id: PipelineId,

View file

@ -4,7 +4,6 @@
//! Data and main loop of WebGPU thread.
use std::cell::RefCell;
use std::collections::HashMap;
use std::slice;
use std::sync::{Arc, Mutex};
@ -26,14 +25,15 @@ use wgc::instance::parse_backends_from_comma_list;
use wgc::pipeline::ShaderModuleDescriptor;
use wgc::resource::{BufferMapCallback, BufferMapOperation};
use wgc::{gfx_select, id};
use wgpu_core::command::{ComputePassDescriptor, DynComputePass};
use wgt::InstanceDescriptor;
pub use {wgpu_core as wgc, wgpu_types as wgt};
use crate::gpu_error::ErrorScope;
use crate::poll_thread::Poller;
use crate::{
Error, PopError, PresentationData, Transmute, WebGPU, WebGPUAdapter, WebGPUDevice, WebGPUMsg,
WebGPUQueue, WebGPURequest, WebGPUResponse,
ComputePassId, Error, PopError, PresentationData, Transmute, WebGPU, WebGPUAdapter,
WebGPUDevice, WebGPUMsg, WebGPUQueue, WebGPURequest, WebGPUResponse,
};
pub const PRESENTATION_BUFFER_COUNT: usize = 10;
@ -73,13 +73,20 @@ pub(crate) struct WGPU {
devices: Arc<Mutex<HashMap<DeviceId, DeviceScope>>>,
// Track invalid adapters https://gpuweb.github.io/gpuweb/#invalid
_invalid_adapters: Vec<WebGPUAdapter>,
//TODO: Remove this (https://github.com/gfx-rs/wgpu/issues/867)
error_command_encoders: RefCell<HashMap<id::CommandEncoderId, String>>,
// TODO: Remove this (https://github.com/gfx-rs/wgpu/issues/867)
/// This stores first error on command encoder,
/// because wgpu does not invalidate command encoder object
/// (this is also reused for invalidation of command buffers)
error_command_encoders: HashMap<id::CommandEncoderId, String>,
webrender_api: Arc<Mutex<RenderApi>>,
webrender_document: DocumentId,
external_images: Arc<Mutex<WebrenderExternalImageRegistry>>,
wgpu_image_map: Arc<Mutex<HashMap<u64, PresentationData>>>,
/// Provides access to poller thread
poller: Poller,
/// Store compute passes (that have not ended yet) and their validity
compute_passes: HashMap<ComputePassId, (Box<dyn DynComputePass>, bool)>,
//render_passes: HashMap<RenderPassId, Box<dyn DynRenderPass>>,
}
impl WGPU {
@ -118,11 +125,12 @@ impl WGPU {
adapters: Vec::new(),
devices: Arc::new(Mutex::new(HashMap::new())),
_invalid_adapters: Vec::new(),
error_command_encoders: RefCell::new(HashMap::new()),
error_command_encoders: HashMap::new(),
webrender_api: Arc::new(Mutex::new(webrender_api_sender.create_api())),
webrender_document,
external_images,
wgpu_image_map,
compute_passes: HashMap::new(),
}
}
@ -199,13 +207,11 @@ impl WGPU {
} => {
let global = &self.global;
let result = if is_error {
Err(Error::Internal(String::from("Invalid GPUCommandEncoder")))
} else if let Some(err) = self
.error_command_encoders
.borrow()
.get(&command_encoder_id)
Err(Error::Validation(String::from("Invalid GPUCommandEncoder")))
} else if let Some(err) =
self.error_command_encoders.get(&command_encoder_id)
{
Err(Error::Internal(err.clone()))
Err(Error::Validation(err.clone()))
} else if let Some(error) =
gfx_select!(command_encoder_id => global.command_encoder_finish(
command_encoder_id,
@ -218,7 +224,9 @@ impl WGPU {
Ok(())
};
// invalidate command buffer too
self.encoder_record_error(command_encoder_id, &result);
// dispatch validation error
self.maybe_dispatch_error(device_id, result.err());
},
WebGPURequest::CopyBufferToBuffer {
@ -575,7 +583,6 @@ impl WGPU {
},
WebGPURequest::DropCommandBuffer(id) => {
self.error_command_encoders
.borrow_mut()
.remove(&id.into_command_encoder_id());
let global = &self.global;
gfx_select!(id => global.command_buffer_drop(id));
@ -745,35 +752,129 @@ impl WGPU {
)
}
},
WebGPURequest::RunComputePass {
WebGPURequest::BeginComputePass {
command_encoder_id,
compute_pass,
compute_pass_id,
label,
device_id: _device_id,
} => {
let global = &self.global;
let result = if let Some(pass) = compute_pass {
gfx_select!(command_encoder_id => global.command_encoder_run_compute_pass(
let (pass, error) = gfx_select!(
command_encoder_id => global.command_encoder_create_compute_pass_dyn(
command_encoder_id,
&pass
)).map_err(|e| format!("{:?}", e))
} else {
Err(String::from("Invalid ComputePass"))
};
self.encoder_record_error(command_encoder_id, &result);
&ComputePassDescriptor { label, timestamp_writes: None }
));
assert!(
self.compute_passes
.insert(compute_pass_id, (pass, error.is_none()))
.is_none(),
"ComputePass should not exist yet."
);
// TODO: Command encoder state errors
// self.maybe_dispatch_wgpu_error(device_id, error);
},
WebGPURequest::RunRenderPass {
command_encoder_id,
render_pass,
WebGPURequest::ComputePassSetPipeline {
compute_pass_id,
pipeline_id,
device_id,
} => {
let global = &self.global;
let result = if let Some(pass) = render_pass {
gfx_select!(command_encoder_id => global.command_encoder_run_render_pass(
command_encoder_id,
&pass
)).map_err(|e| format!("{:?}", e))
if let Some((pass, valid)) = self.compute_passes.get_mut(&compute_pass_id) {
*valid &= pass.set_pipeline(&self.global, pipeline_id).is_ok();
} else {
Err(String::from("Invalid RenderPass"))
self.maybe_dispatch_error(
device_id,
Some(Error::Validation("pass already ended".to_string())),
);
};
self.encoder_record_error(command_encoder_id, &result);
},
WebGPURequest::ComputePassSetBindGroup {
compute_pass_id,
index,
bind_group_id,
offsets,
device_id,
} => {
if let Some((pass, valid)) = self.compute_passes.get_mut(&compute_pass_id) {
*valid &= pass
.set_bind_group(&self.global, index, bind_group_id, &offsets)
.is_ok();
} else {
self.maybe_dispatch_error(
device_id,
Some(Error::Validation("pass already ended".to_string())),
);
};
},
WebGPURequest::ComputePassDispatchWorkgroups {
compute_pass_id,
x,
y,
z,
device_id,
} => {
if let Some((pass, valid)) = self.compute_passes.get_mut(&compute_pass_id) {
*valid &= pass.dispatch_workgroups(&self.global, x, y, z).is_ok();
} else {
self.maybe_dispatch_error(
device_id,
Some(Error::Validation("pass already ended".to_string())),
);
};
},
WebGPURequest::ComputePassDispatchWorkgroupsIndirect {
compute_pass_id,
buffer_id,
offset,
device_id,
} => {
if let Some((pass, valid)) = self.compute_passes.get_mut(&compute_pass_id) {
*valid &= pass
.dispatch_workgroups_indirect(&self.global, buffer_id, offset)
.is_ok();
} else {
self.maybe_dispatch_error(
device_id,
Some(Error::Validation("pass already ended".to_string())),
);
};
},
WebGPURequest::EndComputePass {
compute_pass_id,
device_id,
command_encoder_id,
} => {
// TODO: Command encoder state error
if let Some((mut pass, valid)) =
self.compute_passes.remove(&compute_pass_id)
{
if pass.end(&self.global).is_ok() && !valid {
self.encoder_record_error(
command_encoder_id,
&Err::<(), _>("Pass is invalid".to_string()),
);
}
} else {
self.dispatch_error(
device_id,
Error::Validation("pass already ended".to_string()),
);
};
},
WebGPURequest::EndRenderPass {
render_pass,
device_id,
} => {
if let Some(render_pass) = render_pass {
let command_encoder_id = render_pass.parent_id();
let global = &self.global;
let result = gfx_select!(command_encoder_id => global.render_pass_end(&render_pass));
self.maybe_dispatch_wgpu_error(device_id, result.err())
} else {
self.dispatch_error(
device_id,
Error::Validation("Render pass already ended".to_string()),
)
}
},
WebGPURequest::Submit {
queue_id,
@ -782,11 +883,10 @@ impl WGPU {
let global = &self.global;
let cmd_id = command_buffers.iter().find(|id| {
self.error_command_encoders
.borrow()
.contains_key(&id.into_command_encoder_id())
});
let result = if cmd_id.is_some() {
Err(Error::Internal(String::from(
Err(Error::Validation(String::from(
"Invalid command buffer submitted",
)))
} else {
@ -1026,7 +1126,8 @@ impl WGPU {
},
WebGPURequest::DropTexture(id) => {
let global = &self.global;
gfx_select!(id => global.texture_drop(id, true));
gfx_select!(id => global.texture_drop(id, false));
self.poller.wake();
if let Err(e) = self.script_sender.send(WebGPUMsg::FreeTexture(id)) {
warn!("Unable to send FreeTexture({:?}) ({:?})", id, e);
};
@ -1040,7 +1141,8 @@ impl WGPU {
},
WebGPURequest::DropBuffer(id) => {
let global = &self.global;
gfx_select!(id => global.buffer_drop(id, true));
gfx_select!(id => global.buffer_drop(id, false));
self.poller.wake();
if let Err(e) = self.script_sender.send(WebGPUMsg::FreeBuffer(id)) {
warn!("Unable to send FreeBuffer({:?}) ({:?})", id, e);
};
@ -1060,6 +1162,13 @@ impl WGPU {
warn!("Unable to send FreeComputePipeline({:?}) ({:?})", id, e);
};
},
WebGPURequest::DropComputePass(id) => {
// Compute pass might have already ended
self.compute_passes.remove(&id);
if let Err(e) = self.script_sender.send(WebGPUMsg::FreeComputePass(id)) {
warn!("Unable to send FreeComputePass({:?}) ({:?})", id, e);
};
},
WebGPURequest::DropRenderPipeline(id) => {
let global = &self.global;
gfx_select!(id => global.render_pipeline_drop(id));
@ -1084,7 +1193,8 @@ impl WGPU {
},
WebGPURequest::DropTextureView(id) => {
let global = &self.global;
let _result = gfx_select!(id => global.texture_view_drop(id, true));
let _result = gfx_select!(id => global.texture_view_drop(id, false));
self.poller.wake();
if let Err(e) = self.script_sender.send(WebGPUMsg::FreeTextureView(id)) {
warn!("Unable to send FreeTextureView({:?}) ({:?})", id, e);
};
@ -1214,13 +1324,12 @@ impl WGPU {
}
fn encoder_record_error<U, T: std::fmt::Debug>(
&self,
&mut self,
encoder_id: id::CommandEncoderId,
result: &Result<U, T>,
) {
if let Err(ref e) = result {
self.error_command_encoders
.borrow_mut()
.entry(encoder_id)
.or_insert_with(|| format!("{:?}", e));
}