[error] shader module creation, separate descriptor

This commit is contained in:
Dzmitry Malyshau 2020-11-19 23:23:19 -05:00
parent e96e5f917c
commit fccbca28bd
7 changed files with 160 additions and 117 deletions

2
Cargo.lock generated
View File

@ -1121,7 +1121,7 @@ dependencies = [
[[package]] [[package]]
name = "naga" name = "naga"
version = "0.2.0" version = "0.2.0"
source = "git+https://github.com/gfx-rs/naga?rev=27ec95a2381ae8e0afd07fa13c83a436a9e0bd41#27ec95a2381ae8e0afd07fa13c83a436a9e0bd41" source = "git+https://github.com/gfx-rs/naga?rev=7c7702c9d8d59325328aa7081347e151a7bb63d7#7c7702c9d8d59325328aa7081347e151a7bb63d7"
dependencies = [ dependencies = [
"bitflags", "bitflags",
"fxhash", "fxhash",

View File

@ -208,20 +208,23 @@ impl GlobalPlay for wgc::hub::Global<IdentityPassThroughFactory> {
A::DestroyBindGroup(id) => { A::DestroyBindGroup(id) => {
self.bind_group_drop::<B>(id); self.bind_group_drop::<B>(id);
} }
A::CreateShaderModule { id, data } => { A::CreateShaderModule { id, data, label } => {
let source = if data.ends_with(".wgsl") { let desc = wgc::pipeline::ShaderModuleDescriptor {
let code = fs::read_to_string(dir.join(data)).unwrap(); source: if data.ends_with(".wgsl") {
wgc::pipeline::ShaderModuleSource::Wgsl(Cow::Owned(code)) let code = fs::read_to_string(dir.join(data)).unwrap();
} else { wgc::pipeline::ShaderModuleSource::Wgsl(Cow::Owned(code))
let byte_vec = fs::read(dir.join(data)).unwrap(); } else {
let spv = byte_vec let byte_vec = fs::read(dir.join(data)).unwrap();
.chunks(4) let spv = byte_vec
.map(|c| u32::from_le_bytes([c[0], c[1], c[2], c[3]])) .chunks(4)
.collect::<Vec<_>>(); .map(|c| u32::from_le_bytes([c[0], c[1], c[2], c[3]]))
wgc::pipeline::ShaderModuleSource::SpirV(Cow::Owned(spv)) .collect::<Vec<_>>();
wgc::pipeline::ShaderModuleSource::SpirV(Cow::Owned(spv))
},
label,
}; };
self.device_create_shader_module::<B>(device, source, id) let (_, error) = self.device_create_shader_module::<B>(device, &desc, id);
.unwrap(); assert_eq!(error, None);
} }
A::DestroyShaderModule(id) => { A::DestroyShaderModule(id) => {
self.shader_module_drop::<B>(id); self.shader_module_drop::<B>(id);

View File

@ -9,6 +9,7 @@
)), )),
CreateShaderModule( CreateShaderModule(
id: Id(0, 1, Empty), id: Id(0, 1, Empty),
label: None,
data: "empty.comp.spv", data: "empty.comp.spv",
), ),
CreateComputePipeline(Id(0, 1, Empty), ( CreateComputePipeline(Id(0, 1, Empty), (

View File

@ -40,7 +40,7 @@ gpu-alloc = { git = "https://github.com/zakarumych/gpu-alloc", rev = "7bd745e488
[dependencies.naga] [dependencies.naga]
version = "0.2" version = "0.2"
git = "https://github.com/gfx-rs/naga" git = "https://github.com/gfx-rs/naga"
rev = "27ec95a2381ae8e0afd07fa13c83a436a9e0bd41" rev = "7c7702c9d8d59325328aa7081347e151a7bb63d7"
features = ["spv-in", "spv-out", "wgsl-in"] features = ["spv-in", "spv-out", "wgsl-in"]
[dependencies.wgt] [dependencies.wgt]

View File

@ -833,6 +833,89 @@ impl<B: GfxBackend> Device<B> {
}) })
} }
fn create_shader_module<'a>(
&self,
self_id: id::DeviceId,
desc: &'a pipeline::ShaderModuleDescriptor<'a>,
) -> Result<(pipeline::ShaderModule<B>, Cow<'a, [u32]>), pipeline::CreateShaderModuleError>
{
let spv_flags = if cfg!(debug_assertions) {
naga::back::spv::WriterFlags::DEBUG
} else {
naga::back::spv::WriterFlags::empty()
};
let (spv, naga) = match desc.source {
pipeline::ShaderModuleSource::SpirV(ref spv) => {
let module = if self.private_features.shader_validation {
// Parse the given shader code and store its representation.
let spv_iter = spv.iter().cloned();
naga::front::spv::Parser::new(spv_iter, &Default::default())
.parse()
.map_err(|err| {
// TODO: eventually, when Naga gets support for all features,
// we want to convert these to a hard error,
tracing::warn!("Failed to parse shader SPIR-V code: {:?}", err);
tracing::warn!("Shader module will not be validated");
})
.ok()
} else {
None
};
(Cow::Borrowed(&**spv), module)
}
pipeline::ShaderModuleSource::Wgsl(ref code) => {
// TODO: refactor the corresponding Naga error to be owned, and then
// display it instead of unwrapping
let module = naga::front::wgsl::parse_str(code).unwrap();
let spv = naga::back::spv::Writer::new(&module.header, spv_flags).write(&module);
(
Cow::Owned(spv),
if self.private_features.shader_validation {
Some(module)
} else {
None
},
)
} /*
pipeline::ShaderModuleSource::Naga(module) => {
let spv = naga::back::spv::Writer::new(&module.header, spv_flags).write(&module);
(
Cow::Owned(spv),
if device.private_features.shader_validation {
Some(module)
} else {
None
},
)
}*/
};
if let Some(ref module) = naga {
naga::proc::Validator::new().validate(module)?;
}
let raw = unsafe {
self.raw
.create_shader_module(&spv)
.map_err(|err| match err {
hal::device::ShaderError::OutOfMemory(_) => DeviceError::OutOfMemory,
_ => panic!("failed to create shader module: {}", err),
})?
};
let shader = pipeline::ShaderModule {
raw,
device_id: Stored {
value: id::Valid(self_id),
ref_count: self.life_guard.add_ref(),
},
module: naga,
#[cfg(debug_assertions)]
label: desc.label.to_string_or_default(),
};
Ok((shader, spv))
}
/// Create a compatible render pass with a given key. /// Create a compatible render pass with a given key.
/// ///
/// This functions doesn't consider the following aspects for compatibility: /// This functions doesn't consider the following aspects for compatibility:
@ -2569,119 +2652,59 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
pub fn device_create_shader_module<B: GfxBackend>( pub fn device_create_shader_module<B: GfxBackend>(
&self, &self,
device_id: id::DeviceId, device_id: id::DeviceId,
source: pipeline::ShaderModuleSource, desc: &pipeline::ShaderModuleDescriptor,
id_in: Input<G, id::ShaderModuleId>, id_in: Input<G, id::ShaderModuleId>,
) -> Result<id::ShaderModuleId, pipeline::CreateShaderModuleError> { ) -> (
id::ShaderModuleId,
Option<pipeline::CreateShaderModuleError>,
) {
span!(_guard, INFO, "Device::create_shader_module"); span!(_guard, INFO, "Device::create_shader_module");
let hub = B::hub(self); let hub = B::hub(self);
let mut token = Token::root(); let mut token = Token::root();
let (device_guard, mut token) = hub.devices.read(&mut token); let (device_guard, mut token) = hub.devices.read(&mut token);
let device = device_guard let error = loop {
.get(device_id) let device = match device_guard.get(device_id) {
.map_err(|_| DeviceError::Invalid)?; Ok(device) => device,
let spv_flags = if cfg!(debug_assertions) { Err(_) => break DeviceError::Invalid.into(),
naga::back::spv::WriterFlags::DEBUG };
} else { let (shader, spv) = match device.create_shader_module(device_id, desc) {
naga::back::spv::WriterFlags::empty() Ok(pair) => pair,
}; Err(e) => break e,
};
let (spv, naga) = match source { let id = hub
pipeline::ShaderModuleSource::SpirV(spv) => { .shader_modules
let module = if device.private_features.shader_validation { .register_identity(id_in, shader, &mut token);
// Parse the given shader code and store its representation. #[cfg(feature = "trace")]
let spv_iter = spv.into_iter().cloned(); if let Some(ref trace) = device.trace {
naga::front::spv::Parser::new(spv_iter, &Default::default()) let mut trace = trace.lock();
.parse() let data = trace.make_binary("spv", unsafe {
.map_err(|err| { std::slice::from_raw_parts(spv.as_ptr() as *const u8, spv.len() * 4)
// TODO: eventually, when Naga gets support for all features, });
// we want to convert these to a hard error, let label = desc.label.clone();
tracing::warn!("Failed to parse shader SPIR-V code: {:?}", err); trace.add(trace::Action::CreateShaderModule {
tracing::warn!("Shader module will not be validated"); id: id.0,
}) data,
.ok() label,
} else { });
None
};
(spv, module)
}
pipeline::ShaderModuleSource::Wgsl(code) => {
// TODO: refactor the corresponding Naga error to be owned, and then
// display it instead of unwrapping
let module = naga::front::wgsl::parse_str(&code).unwrap();
let spv = naga::back::spv::Writer::new(&module.header, spv_flags).write(&module);
(
Cow::Owned(spv),
if device.private_features.shader_validation {
Some(module)
} else {
None
},
)
}
pipeline::ShaderModuleSource::Naga(module) => {
let spv = naga::back::spv::Writer::new(&module.header, spv_flags).write(&module);
(
Cow::Owned(spv),
if device.private_features.shader_validation {
Some(module)
} else {
None
},
)
} }
let _ = spv;
return (id.0, None);
}; };
if let Some(ref module) = naga { let id =
naga::proc::Validator::new().validate(module)?; hub.shader_modules
} .register_error(id_in, desc.label.borrow_or_default(), &mut token);
(id, Some(error))
let raw = unsafe {
device
.raw
.create_shader_module(&spv)
.map_err(|err| match err {
hal::device::ShaderError::OutOfMemory(_) => DeviceError::OutOfMemory,
_ => panic!("failed to create shader module: {}", err),
})?
};
let shader = pipeline::ShaderModule {
raw,
device_id: Stored {
value: id::Valid(device_id),
ref_count: device.life_guard.add_ref(),
},
module: naga,
};
let id = hub
.shader_modules
.register_identity(id_in, shader, &mut token);
#[cfg(feature = "trace")]
if let Some(ref trace) = device.trace {
let mut trace = trace.lock();
let data = trace.make_binary("spv", unsafe {
std::slice::from_raw_parts(spv.as_ptr() as *const u8, spv.len() * 4)
});
trace.add(trace::Action::CreateShaderModule { id: id.0, data });
}
Ok(id.0)
} }
pub fn shader_module_label<B: GfxBackend>(&self, id: id::ShaderModuleId) -> String { pub fn shader_module_label<B: GfxBackend>(&self, id: id::ShaderModuleId) -> String {
B::hub(self).shader_modules.label_for_resource(id) B::hub(self).shader_modules.label_for_resource(id)
} }
pub fn shader_module_error<B: GfxBackend>(
&self,
id_in: Input<G, id::ShaderModuleId>,
) -> id::ShaderModuleId {
let hub = B::hub(self);
let mut token = Token::root();
let (_, mut token) = hub.devices.read(&mut token);
hub.shader_modules.register_error(id_in, "", &mut token)
}
pub fn shader_module_drop<B: GfxBackend>(&self, shader_module_id: id::ShaderModuleId) { pub fn shader_module_drop<B: GfxBackend>(&self, shader_module_id: id::ShaderModuleId) {
span!(_guard, INFO, "ShaderModule::drop"); span!(_guard, INFO, "ShaderModule::drop");

View File

@ -71,6 +71,7 @@ pub enum Action<'a> {
DestroyBindGroup(id::BindGroupId), DestroyBindGroup(id::BindGroupId),
CreateShaderModule { CreateShaderModule {
id: id::ShaderModuleId, id: id::ShaderModuleId,
label: crate::Label<'a>,
data: FileName, data: FileName,
}, },
DestroyShaderModule(id::ShaderModuleId), DestroyShaderModule(id::ShaderModuleId),

View File

@ -14,13 +14,23 @@ use std::borrow::Cow;
use thiserror::Error; use thiserror::Error;
use wgt::{BufferAddress, IndexFormat, InputStepMode}; use wgt::{BufferAddress, IndexFormat, InputStepMode};
// Unable to serialize with `naga::Module` in here:
// requires naga serialization feature.
#[derive(Debug)] #[derive(Debug)]
#[cfg_attr(feature = "trace", derive(serde::Serialize))]
#[cfg_attr(feature = "replay", derive(serde::Deserialize))]
pub enum ShaderModuleSource<'a> { pub enum ShaderModuleSource<'a> {
SpirV(Cow<'a, [u32]>), SpirV(Cow<'a, [u32]>),
Wgsl(Cow<'a, str>), Wgsl(Cow<'a, str>),
Naga(naga::Module), // Unable to serialize with `naga::Module` in here:
// requires naga serialization feature.
//Naga(naga::Module),
}
#[derive(Debug)]
#[cfg_attr(feature = "trace", derive(serde::Serialize))]
#[cfg_attr(feature = "replay", derive(serde::Deserialize))]
pub struct ShaderModuleDescriptor<'a> {
pub label: Label<'a>,
pub source: ShaderModuleSource<'a>,
} }
#[derive(Debug)] #[derive(Debug)]
@ -28,6 +38,8 @@ pub struct ShaderModule<B: hal::Backend> {
pub(crate) raw: B::ShaderModule, pub(crate) raw: B::ShaderModule,
pub(crate) device_id: Stored<DeviceId>, pub(crate) device_id: Stored<DeviceId>,
pub(crate) module: Option<naga::Module>, pub(crate) module: Option<naga::Module>,
#[cfg(debug_assertions)]
pub(crate) label: String,
} }
impl<B: hal::Backend> Resource for ShaderModule<B> { impl<B: hal::Backend> Resource for ShaderModule<B> {
@ -38,11 +50,14 @@ impl<B: hal::Backend> Resource for ShaderModule<B> {
} }
fn label(&self) -> &str { fn label(&self) -> &str {
"<ShaderModule>" #[cfg(debug_assertions)]
return &self.label;
#[cfg(not(debug_assertions))]
return "";
} }
} }
#[derive(Clone, Debug, Error)] #[derive(Clone, Debug, Error, PartialEq)]
pub enum CreateShaderModuleError { pub enum CreateShaderModuleError {
#[error(transparent)] #[error(transparent)]
Device(#[from] DeviceError), Device(#[from] DeviceError),