[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]]
name = "naga"
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 = [
"bitflags",
"fxhash",

View File

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

View File

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

View File

@ -40,7 +40,7 @@ gpu-alloc = { git = "https://github.com/zakarumych/gpu-alloc", rev = "7bd745e488
[dependencies.naga]
version = "0.2"
git = "https://github.com/gfx-rs/naga"
rev = "27ec95a2381ae8e0afd07fa13c83a436a9e0bd41"
rev = "7c7702c9d8d59325328aa7081347e151a7bb63d7"
features = ["spv-in", "spv-out", "wgsl-in"]
[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.
///
/// 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>(
&self,
device_id: id::DeviceId,
source: pipeline::ShaderModuleSource,
desc: &pipeline::ShaderModuleDescriptor,
id_in: Input<G, id::ShaderModuleId>,
) -> Result<id::ShaderModuleId, pipeline::CreateShaderModuleError> {
) -> (
id::ShaderModuleId,
Option<pipeline::CreateShaderModuleError>,
) {
span!(_guard, INFO, "Device::create_shader_module");
let hub = B::hub(self);
let mut token = Token::root();
let (device_guard, mut token) = hub.devices.read(&mut token);
let device = device_guard
.get(device_id)
.map_err(|_| DeviceError::Invalid)?;
let spv_flags = if cfg!(debug_assertions) {
naga::back::spv::WriterFlags::DEBUG
} else {
naga::back::spv::WriterFlags::empty()
};
let error = loop {
let device = match device_guard.get(device_id) {
Ok(device) => device,
Err(_) => break DeviceError::Invalid.into(),
};
let (shader, spv) = match device.create_shader_module(device_id, desc) {
Ok(pair) => pair,
Err(e) => break e,
};
let (spv, naga) = match source {
pipeline::ShaderModuleSource::SpirV(spv) => {
let module = if device.private_features.shader_validation {
// Parse the given shader code and store its representation.
let spv_iter = spv.into_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
};
(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 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)
});
let label = desc.label.clone();
trace.add(trace::Action::CreateShaderModule {
id: id.0,
data,
label,
});
}
let _ = spv;
return (id.0, None);
};
if let Some(ref module) = naga {
naga::proc::Validator::new().validate(module)?;
}
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)
let id =
hub.shader_modules
.register_error(id_in, desc.label.borrow_or_default(), &mut token);
(id, Some(error))
}
pub fn shader_module_label<B: GfxBackend>(&self, id: id::ShaderModuleId) -> String {
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) {
span!(_guard, INFO, "ShaderModule::drop");

View File

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

View File

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