Add pervasive Naga support to shader module loading

The purpose of the PR is to support Naga modules everywhere.
As a requirement, it updates the gfx-rs version used.
Most of the logic is dedicated towards building a shader interface,
where previously we just used naga's IR. Now we have our own mini-IR.
This commit is contained in:
Dzmitry Malyshau 2020-12-03 00:30:38 -05:00
parent d164b0dbc6
commit 749f737c89
12 changed files with 826 additions and 887 deletions

30
Cargo.lock generated
View File

@ -441,7 +441,7 @@ dependencies = [
[[package]]
name = "gfx-auxil"
version = "0.5.0"
source = "git+https://github.com/gfx-rs/gfx?rev=1d14789011cb892f4c1a205d3f8a87d479c2e354#1d14789011cb892f4c1a205d3f8a87d479c2e354"
source = "git+https://github.com/gfx-rs/gfx?rev=654ad48ee39ce2a341407ae2857ddf4db639ea54#654ad48ee39ce2a341407ae2857ddf4db639ea54"
dependencies = [
"fxhash",
"gfx-hal",
@ -451,7 +451,7 @@ dependencies = [
[[package]]
name = "gfx-backend-dx11"
version = "0.6.0"
source = "git+https://github.com/gfx-rs/gfx?rev=1d14789011cb892f4c1a205d3f8a87d479c2e354#1d14789011cb892f4c1a205d3f8a87d479c2e354"
source = "git+https://github.com/gfx-rs/gfx?rev=654ad48ee39ce2a341407ae2857ddf4db639ea54#654ad48ee39ce2a341407ae2857ddf4db639ea54"
dependencies = [
"arrayvec",
"bitflags",
@ -472,7 +472,7 @@ dependencies = [
[[package]]
name = "gfx-backend-dx12"
version = "0.6.2"
source = "git+https://github.com/gfx-rs/gfx?rev=1d14789011cb892f4c1a205d3f8a87d479c2e354#1d14789011cb892f4c1a205d3f8a87d479c2e354"
source = "git+https://github.com/gfx-rs/gfx?rev=654ad48ee39ce2a341407ae2857ddf4db639ea54#654ad48ee39ce2a341407ae2857ddf4db639ea54"
dependencies = [
"arrayvec",
"bit-set",
@ -492,7 +492,7 @@ dependencies = [
[[package]]
name = "gfx-backend-empty"
version = "0.6.0"
source = "git+https://github.com/gfx-rs/gfx?rev=1d14789011cb892f4c1a205d3f8a87d479c2e354#1d14789011cb892f4c1a205d3f8a87d479c2e354"
source = "git+https://github.com/gfx-rs/gfx?rev=654ad48ee39ce2a341407ae2857ddf4db639ea54#654ad48ee39ce2a341407ae2857ddf4db639ea54"
dependencies = [
"gfx-hal",
"log",
@ -502,7 +502,7 @@ dependencies = [
[[package]]
name = "gfx-backend-gl"
version = "0.6.0"
source = "git+https://github.com/gfx-rs/gfx?rev=1d14789011cb892f4c1a205d3f8a87d479c2e354#1d14789011cb892f4c1a205d3f8a87d479c2e354"
source = "git+https://github.com/gfx-rs/gfx?rev=654ad48ee39ce2a341407ae2857ddf4db639ea54#654ad48ee39ce2a341407ae2857ddf4db639ea54"
dependencies = [
"arrayvec",
"bitflags",
@ -524,7 +524,7 @@ dependencies = [
[[package]]
name = "gfx-backend-metal"
version = "0.6.0"
source = "git+https://github.com/gfx-rs/gfx?rev=1d14789011cb892f4c1a205d3f8a87d479c2e354#1d14789011cb892f4c1a205d3f8a87d479c2e354"
source = "git+https://github.com/gfx-rs/gfx?rev=654ad48ee39ce2a341407ae2857ddf4db639ea54#654ad48ee39ce2a341407ae2857ddf4db639ea54"
dependencies = [
"arrayvec",
"bitflags",
@ -537,6 +537,7 @@ dependencies = [
"lazy_static",
"log",
"metal",
"naga",
"objc",
"parking_lot 0.11.0",
"range-alloc",
@ -548,7 +549,7 @@ dependencies = [
[[package]]
name = "gfx-backend-vulkan"
version = "0.6.5"
source = "git+https://github.com/gfx-rs/gfx?rev=1d14789011cb892f4c1a205d3f8a87d479c2e354#1d14789011cb892f4c1a205d3f8a87d479c2e354"
source = "git+https://github.com/gfx-rs/gfx?rev=654ad48ee39ce2a341407ae2857ddf4db639ea54#654ad48ee39ce2a341407ae2857ddf4db639ea54"
dependencies = [
"arrayvec",
"ash",
@ -567,10 +568,12 @@ dependencies = [
[[package]]
name = "gfx-hal"
version = "0.6.0"
source = "git+https://github.com/gfx-rs/gfx?rev=1d14789011cb892f4c1a205d3f8a87d479c2e354#1d14789011cb892f4c1a205d3f8a87d479c2e354"
source = "git+https://github.com/gfx-rs/gfx?rev=654ad48ee39ce2a341407ae2857ddf4db639ea54#654ad48ee39ce2a341407ae2857ddf4db639ea54"
dependencies = [
"bitflags",
"naga",
"raw-window-handle",
"thiserror",
]
[[package]]
@ -605,7 +608,7 @@ dependencies = [
[[package]]
name = "gpu-descriptor"
version = "0.1.0"
source = "git+https://github.com/zakarumych/gpu-descriptor?rev=831460c4b5120d9a74744d542f39a95b9816b5ab#831460c4b5120d9a74744d542f39a95b9816b5ab"
source = "git+https://github.com/zakarumych/gpu-descriptor?rev=df74fd8c7bea03149058a41aab0e4fe04077b266#df74fd8c7bea03149058a41aab0e4fe04077b266"
dependencies = [
"bitflags",
"gpu-descriptor-types",
@ -615,7 +618,7 @@ dependencies = [
[[package]]
name = "gpu-descriptor-types"
version = "0.1.0"
source = "git+https://github.com/zakarumych/gpu-descriptor?rev=831460c4b5120d9a74744d542f39a95b9816b5ab#831460c4b5120d9a74744d542f39a95b9816b5ab"
source = "git+https://github.com/zakarumych/gpu-descriptor?rev=df74fd8c7bea03149058a41aab0e4fe04077b266#df74fd8c7bea03149058a41aab0e4fe04077b266"
dependencies = [
"bitflags",
]
@ -830,7 +833,7 @@ version = "0.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "62bb907fe88d54d8d9ce32a3cceab4218ed2f6b7d35617cafe9adf84e43919cb"
dependencies = [
"libc 0.2.80",
"libc 0.1.12",
]
[[package]]
@ -924,13 +927,14 @@ dependencies = [
[[package]]
name = "naga"
version = "0.2.0"
source = "git+https://github.com/gfx-rs/naga?rev=96c80738650822de35f77ab6a589f309460c8f39#96c80738650822de35f77ab6a589f309460c8f39"
source = "git+https://github.com/gfx-rs/naga?tag=gfx-2#0d81b1f78c763a2f564194ec108bcb8ead10ea2e"
dependencies = [
"bitflags",
"fxhash",
"log",
"num-traits",
"petgraph",
"serde",
"spirv_headers",
"thiserror",
]
@ -1213,7 +1217,7 @@ dependencies = [
[[package]]
name = "range-alloc"
version = "0.1.1"
source = "git+https://github.com/gfx-rs/gfx?rev=1d14789011cb892f4c1a205d3f8a87d479c2e354#1d14789011cb892f4c1a205d3f8a87d479c2e354"
source = "git+https://github.com/gfx-rs/gfx?rev=654ad48ee39ce2a341407ae2857ddf4db639ea54#654ad48ee39ce2a341407ae2857ddf4db639ea54"
[[package]]
name = "raw-window-handle"

View File

@ -5,3 +5,15 @@ members = [
"wgpu-core",
"wgpu-types",
]
#[patch."https://github.com/gfx-rs/gfx"]
#hal = { package = "gfx-hal", path = "../gfx/src/hal" }
#gfx-backend-vulkan = { path = "../gfx/src/backend/vulkan" }
#gfx-backend-metal = { path = "../gfx/src/backend/metal", features = ["naga"] }
#gfx-backend-gl = { path = "../gfx/src/backend/gl" }
#gfx-backend-dx12 = { path = "../gfx/src/backend/dx12" }
#gfx-backend-dx11 = { path = "../gfx/src/backend/dx11" }
#gfx-backend-empty = { path = "../gfx/src/backend/empty" }
#[patch."https://github.com/gfx-rs/naga"]
#naga = { path = "../naga" }

View File

@ -209,22 +209,23 @@ impl GlobalPlay for wgc::hub::Global<IdentityPassThroughFactory> {
A::DestroyBindGroup(id) => {
self.bind_group_drop::<B>(id);
}
A::CreateShaderModule { id, data, label } => {
A::CreateShaderModule { id, label, data } => {
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,
experimental_translation: false,
};
let (_, error) = self.device_create_shader_module::<B>(device, &desc, id);
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))
};
let (_, error) = self.device_create_shader_module::<B>(device, &desc, source, id);
if let Some(e) = error {
panic!("{:?}", e);
}

View File

@ -213,5 +213,12 @@ impl Corpus {
#[test]
fn test_api() {
wgpu_subscriber::initialize_default_subscriber(
std::env::var("WGPU_CHROME_TRACE")
.as_ref()
.map(Path::new)
.ok(),
);
Corpus::run_from(PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("tests/data/all.ron"))
}

View File

@ -14,9 +14,9 @@ license = "MPL-2.0"
[features]
default = []
# Enable API tracing
trace = ["ron", "serde", "wgt/trace"]
trace = ["ron", "serde", "wgt/trace", "naga/serialize"]
# Enable API replaying
replay = ["serde", "wgt/replay"]
replay = ["serde", "wgt/replay", "naga/deserialize"]
# Enable serializable compute/render passes, and bundle encoders.
serial-pass = ["serde", "wgt/serde", "arrayvec/serde"]
@ -25,8 +25,6 @@ arrayvec = "0.5"
bitflags = "1.0"
copyless = "0.1"
fxhash = "0.2"
hal = { package = "gfx-hal", git = "https://github.com/gfx-rs/gfx", rev = "1d14789011cb892f4c1a205d3f8a87d479c2e354" }
gfx-backend-empty = { git = "https://github.com/gfx-rs/gfx", rev = "1d14789011cb892f4c1a205d3f8a87d479c2e354" }
parking_lot = "0.11"
raw-window-handle = { version = "0.3", optional = true }
ron = { version = "0.6", optional = true }
@ -35,12 +33,27 @@ smallvec = "1"
tracing = { version = "0.1", default-features = false, features = ["std"] }
thiserror = "1"
gpu-alloc = { git = "https://github.com/zakarumych/gpu-alloc", rev = "d07be73f9439a37c89f5b72f2500cbf0eb4ff613" }
gpu-descriptor = { git = "https://github.com/zakarumych/gpu-descriptor", rev = "831460c4b5120d9a74744d542f39a95b9816b5ab"}
gpu-descriptor = { git = "https://github.com/zakarumych/gpu-descriptor", rev = "df74fd8c7bea03149058a41aab0e4fe04077b266"}
hal = { package = "gfx-hal", git = "https://github.com/gfx-rs/gfx", rev = "654ad48ee39ce2a341407ae2857ddf4db639ea54" }
gfx-backend-empty = { git = "https://github.com/gfx-rs/gfx", rev = "654ad48ee39ce2a341407ae2857ddf4db639ea54" }
[target.'cfg(all(unix, not(target_os = "ios"), not(target_os = "macos")))'.dependencies]
gfx-backend-vulkan = { git = "https://github.com/gfx-rs/gfx", rev = "654ad48ee39ce2a341407ae2857ddf4db639ea54" }
gfx-backend-gl = { git = "https://github.com/gfx-rs/gfx", rev = "654ad48ee39ce2a341407ae2857ddf4db639ea54" }
[target.'cfg(any(target_os = "ios", target_os = "macos"))'.dependencies]
gfx-backend-metal = { git = "https://github.com/gfx-rs/gfx", rev = "654ad48ee39ce2a341407ae2857ddf4db639ea54", features = ["naga"] }
gfx-backend-vulkan = { git = "https://github.com/gfx-rs/gfx", rev = "654ad48ee39ce2a341407ae2857ddf4db639ea54", optional = true }
[target.'cfg(windows)'.dependencies]
gfx-backend-dx12 = { git = "https://github.com/gfx-rs/gfx", rev = "654ad48ee39ce2a341407ae2857ddf4db639ea54" }
gfx-backend-dx11 = { git = "https://github.com/gfx-rs/gfx", rev = "654ad48ee39ce2a341407ae2857ddf4db639ea54" }
gfx-backend-vulkan = { git = "https://github.com/gfx-rs/gfx", rev = "654ad48ee39ce2a341407ae2857ddf4db639ea54" }
[dependencies.naga]
version = "0.2"
git = "https://github.com/gfx-rs/naga"
rev = "96c80738650822de35f77ab6a589f309460c8f39"
tag = "gfx-2"
features = ["spv-in", "spv-out", "wgsl-in"]
[dependencies.wgt]
@ -48,19 +61,6 @@ path = "../wgpu-types"
package = "wgpu-types"
version = "0.6"
[target.'cfg(all(unix, not(target_os = "ios"), not(target_os = "macos")))'.dependencies]
gfx-backend-vulkan = { git = "https://github.com/gfx-rs/gfx", rev = "1d14789011cb892f4c1a205d3f8a87d479c2e354" }
gfx-backend-gl = { git = "https://github.com/gfx-rs/gfx", rev = "1d14789011cb892f4c1a205d3f8a87d479c2e354" }
[target.'cfg(any(target_os = "ios", target_os = "macos"))'.dependencies]
gfx-backend-metal = { git = "https://github.com/gfx-rs/gfx", rev = "1d14789011cb892f4c1a205d3f8a87d479c2e354" }
gfx-backend-vulkan = { git = "https://github.com/gfx-rs/gfx", rev = "1d14789011cb892f4c1a205d3f8a87d479c2e354", optional = true }
[target.'cfg(windows)'.dependencies]
gfx-backend-dx12 = { git = "https://github.com/gfx-rs/gfx", rev = "1d14789011cb892f4c1a205d3f8a87d479c2e354" }
gfx-backend-dx11 = { git = "https://github.com/gfx-rs/gfx", rev = "1d14789011cb892f4c1a205d3f8a87d479c2e354" }
gfx-backend-vulkan = { git = "https://github.com/gfx-rs/gfx", rev = "1d14789011cb892f4c1a205d3f8a87d479c2e354" }
[dev-dependencies]
loom = "0.3"

View File

@ -6,9 +6,7 @@ fn main() {
// Setup cfg aliases
cfg_aliases::cfg_aliases! {
// Vendors/systems
ios: { target_os = "ios" },
macos: { target_os = "macos" },
apple: { any(ios, macos) },
apple: { any(target_os = "ios", target_os = "macos") },
// Backends
vulkan: { any(windows, all(unix, not(apple)), feature = "gfx-backend-vulkan") },

View File

@ -500,7 +500,6 @@ impl RenderBundle {
use hal::command::CommandBuffer as _;
let mut offsets = self.base.dynamic_offsets.as_slice();
let mut index_type = hal::IndexType::U16;
let mut pipeline_layout_id = None::<id::Valid<id::PipelineLayoutId>>;
for command in self.base.commands.iter() {
@ -531,7 +530,7 @@ impl RenderBundle {
offset,
size,
} => {
index_type = conv::map_index_format(index_format);
let index_type = conv::map_index_format(index_format);
let &(ref buffer, _) = buffer_guard
.get(buffer_id)

View File

@ -17,7 +17,7 @@ struct DescriptorDevice<'a, B: hal::Backend>(&'a B::Device);
impl<B: hal::Backend> DescriptorAllocator<B> {
pub fn new() -> Self {
DescriptorAllocator(unsafe { gpu_descriptor::DescriptorAllocator::new(0) })
DescriptorAllocator(gpu_descriptor::DescriptorAllocator::new(0))
}
pub fn allocate(
@ -27,18 +27,19 @@ impl<B: hal::Backend> DescriptorAllocator<B> {
layout_descriptor_count: &DescriptorTotalCount,
count: u32,
) -> Result<Vec<DescriptorSet<B>>, DeviceError> {
self.0
.allocate(
unsafe {
self.0.allocate(
&DescriptorDevice::<B>(device),
layout,
gpu_descriptor::DescriptorSetLayoutCreateFlags::empty(),
layout_descriptor_count,
count,
)
.map_err(|err| {
tracing::warn!("Descriptor set allocation failed: {}", err);
DeviceError::OutOfMemory
})
}
.map_err(|err| {
tracing::warn!("Descriptor set allocation failed: {}", err);
DeviceError::OutOfMemory
})
}
pub fn free(&mut self, device: &B::Device, sets: impl IntoIterator<Item = DescriptorSet<B>>) {
@ -46,7 +47,7 @@ impl<B: hal::Backend> DescriptorAllocator<B> {
}
pub fn cleanup(&mut self, device: &B::Device) {
self.0.cleanup(&DescriptorDevice::<B>(device))
unsafe { self.0.cleanup(&DescriptorDevice::<B>(device)) }
}
}
@ -121,7 +122,7 @@ impl<B: hal::Backend>
self.0,
max_sets as usize,
ranges,
hal::pso::DescriptorPoolCreateFlags::from_bits_truncate(flags.bits() as u32),
hal::pso::DescriptorPoolCreateFlags::from_bits_truncate(flags.bits()),
) {
Ok(pool) => Ok(pool),
Err(hal::device::OutOfMemory::Host) => {

View File

@ -836,84 +836,105 @@ 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
desc: &pipeline::ShaderModuleDescriptor<'a>,
source: pipeline::ShaderModuleSource<'a>,
) -> Result<pipeline::ShaderModule<B>, pipeline::CreateShaderModuleError> {
// First, try to produce a Naga module.
let (spv, module) = match source {
pipeline::ShaderModuleSource::SpirV(spv) => {
// Parse the given shader code and store its representation.
let parser =
naga::front::spv::Parser::new(spv.iter().cloned(), &Default::default());
let module = match parser.parse() {
Ok(module) => Some(module),
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 or reflected");
None
}
};
(Cow::Borrowed(&**spv), module)
(Some(spv), module)
}
pipeline::ShaderModuleSource::Wgsl(ref code) => {
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 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
},
)
}*/
match naga::front::wgsl::parse_str(&code) {
Ok(module) => (None, Some(module)),
Err(err) => {
tracing::error!("Failed to parse WGSL code: {}", err);
return Err(pipeline::CreateShaderModuleError::Parsing);
}
}
}
pipeline::ShaderModuleSource::Naga(module) => (None, Some(module)),
};
if let Some(ref module) = naga {
naga::proc::Validator::new().validate(module)?;
}
let interface = module.as_ref().map(|m| validation::Interface::new(m));
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 naga_result = match module {
// If succeeded, then validate it and attempt to give it to gfx-hal directly.
Some(module) => {
if self.private_features.shader_validation {
naga::proc::Validator::new().validate(&module)?;
}
if desc.experimental_translation {
match unsafe { self.raw.create_shader_module_from_naga(module) } {
Ok(raw) => Ok(raw),
Err((hal::device::ShaderError::CompilationFailed(msg), module)) => {
tracing::warn!("Shader module compilation failed: {}", msg);
Err(Some(module))
}
Err((_, module)) => Err(Some(module)),
}
} else {
Err(Some(module))
}
}
None => Err(None),
};
let shader = pipeline::ShaderModule {
raw,
// Otherwise, fall back to SPIR-V.
let spv_result = match naga_result {
Ok(raw) => Ok(raw),
Err(maybe_module) => {
let spv = match spv {
Some(data) => data,
None => {
// Produce a SPIR-V from the Naga module
let module = maybe_module.unwrap();
let mut flags = naga::back::spv::WriterFlags::empty();
if cfg!(debug_assertions) {
flags |= naga::back::spv::WriterFlags::DEBUG;
}
let data = naga::back::spv::write_vec(&module, flags);
Cow::Owned(data)
}
};
unsafe { self.raw.create_shader_module(&spv) }
}
};
Ok(pipeline::ShaderModule {
raw: match spv_result {
Ok(raw) => raw,
Err(hal::device::ShaderError::OutOfMemory(_)) => {
return Err(DeviceError::OutOfMemory.into());
}
Err(error) => {
tracing::error!("Shader error: {}", error);
return Err(pipeline::CreateShaderModuleError::Parsing);
}
},
device_id: Stored {
value: id::Valid(self_id),
ref_count: self.life_guard.add_ref(),
},
module: naga,
interface,
#[cfg(debug_assertions)]
label: desc.label.to_string_or_default(),
};
Ok((shader, spv))
})
}
/// Create a compatible render pass with a given key.
@ -968,14 +989,12 @@ impl<B: GfxBackend> Device<B> {
fn get_introspection_bind_group_layouts<'a>(
pipeline_layout: &binding_model::PipelineLayout<B>,
bgl_guard: &'a Storage<binding_model::BindGroupLayout<B>, id::BindGroupLayoutId>,
) -> validation::IntrospectionBindGroupLayouts<'a> {
validation::IntrospectionBindGroupLayouts::Given(
pipeline_layout
.bind_group_layout_ids
.iter()
.map(|&id| &bgl_guard[id].entries)
.collect(),
)
) -> ArrayVec<[&'a binding_model::BindEntryMap; MAX_BIND_GROUPS]> {
pipeline_layout
.bind_group_layout_ids
.iter()
.map(|&id| &bgl_guard[id].entries)
.collect()
}
fn create_bind_group_layout(
@ -1633,7 +1652,7 @@ impl<B: GfxBackend> Device<B> {
let mut derived_group_layouts =
ArrayVec::<[binding_model::BindEntryMap; MAX_BIND_GROUPS]>::new();
let interface = validation::StageInterface::default();
let io = validation::StageIo::default();
let pipeline_stage = &desc.compute_stage;
let (shader_module_guard, _) = hub.shader_modules.read(&mut token);
@ -1645,24 +1664,30 @@ impl<B: GfxBackend> Device<B> {
})?;
let flag = wgt::ShaderStage::COMPUTE;
if let Some(ref module) = shader_module.module {
let group_layouts = match desc.layout {
Some(pipeline_layout_id) => Device::get_introspection_bind_group_layouts(
if let Some(ref interface) = shader_module.interface {
let provided_layouts = match desc.layout {
Some(pipeline_layout_id) => Some(Device::get_introspection_bind_group_layouts(
pipeline_layout_guard
.get(pipeline_layout_id)
.map_err(|_| pipeline::CreateComputePipelineError::InvalidLayout)?,
&*bgl_guard,
),
)),
None => {
for _ in 0..self.limits.max_bind_groups {
derived_group_layouts.push(binding_model::BindEntryMap::default());
}
validation::IntrospectionBindGroupLayouts::Derived(&mut derived_group_layouts)
None
}
};
let _ =
validation::check_stage(module, group_layouts, &entry_point_name, flag, interface)
.map_err(pipeline::CreateComputePipelineError::Stage)?;
let _ = interface
.check_stage(
provided_layouts.as_ref().map(|p| p.as_slice()),
&mut derived_group_layouts,
&entry_point_name,
flag,
io,
)
.map_err(pipeline::CreateComputePipelineError::Stage)?;
} else if desc.layout.is_none() {
return Err(pipeline::ImplicitLayoutError::ReflectionError(flag).into());
}
@ -1768,7 +1793,7 @@ impl<B: GfxBackend> Device<B> {
.unwrap_or_default();
let rasterizer = conv::map_rasterization_state_descriptor(&rasterization_state);
let mut interface = validation::StageInterface::default();
let mut io = validation::StageIo::default();
let mut validated_stages = wgt::ShaderStage::empty();
let desc_vbs = &desc.vertex_state.vertex_buffers;
@ -1814,9 +1839,9 @@ impl<B: GfxBackend> Device<B> {
offset: attribute.offset as u32,
},
});
interface.insert(
io.insert(
attribute.shader_location,
validation::MaybeOwned::Owned(validation::map_vertex_format(attribute.format)),
validation::NumericType::from_vertex_format(attribute.format),
);
}
}
@ -1929,27 +1954,26 @@ impl<B: GfxBackend> Device<B> {
error: validation::StageError::InvalidModule,
})?;
if let Some(ref module) = shader_module.module {
let group_layouts = match desc.layout {
Some(pipeline_layout_id) => Device::get_introspection_bind_group_layouts(
if let Some(ref interface) = shader_module.interface {
let provided_layouts = match desc.layout {
Some(pipeline_layout_id) => Some(Device::get_introspection_bind_group_layouts(
pipeline_layout_guard
.get(pipeline_layout_id)
.map_err(|_| pipeline::CreateRenderPipelineError::InvalidLayout)?,
&*bgl_guard,
),
None => validation::IntrospectionBindGroupLayouts::Derived(
&mut derived_group_layouts,
),
)),
None => None,
};
interface = validation::check_stage(
module,
group_layouts,
&entry_point_name,
flag,
interface,
)
.map_err(|error| pipeline::CreateRenderPipelineError::Stage { flag, error })?;
io = interface
.check_stage(
provided_layouts.as_ref().map(|p| p.as_slice()),
&mut derived_group_layouts,
&entry_point_name,
flag,
io,
)
.map_err(|error| pipeline::CreateRenderPipelineError::Stage { flag, error })?;
validated_stages |= flag;
}
@ -1972,30 +1996,30 @@ impl<B: GfxBackend> Device<B> {
}
})?;
let group_layouts = match desc.layout {
Some(pipeline_layout_id) => Device::get_introspection_bind_group_layouts(
let provided_layouts = match desc.layout {
Some(pipeline_layout_id) => Some(Device::get_introspection_bind_group_layouts(
pipeline_layout_guard
.get(pipeline_layout_id)
.map_err(|_| pipeline::CreateRenderPipelineError::InvalidLayout)?,
&*bgl_guard,
),
None => validation::IntrospectionBindGroupLayouts::Derived(
&mut derived_group_layouts,
),
)),
None => None,
};
if validated_stages == wgt::ShaderStage::VERTEX {
if let Some(ref module) = shader_module.module {
interface = validation::check_stage(
module,
group_layouts,
&entry_point_name,
flag,
interface,
)
.map_err(|error| {
pipeline::CreateRenderPipelineError::Stage { flag, error }
})?;
if let Some(ref interface) = shader_module.interface {
io = interface
.check_stage(
provided_layouts.as_ref().map(|p| p.as_slice()),
&mut derived_group_layouts,
&entry_point_name,
flag,
io,
)
.map_err(|error| pipeline::CreateRenderPipelineError::Stage {
flag,
error,
})?;
validated_stages |= flag;
}
}
@ -2011,13 +2035,13 @@ impl<B: GfxBackend> Device<B> {
if validated_stages.contains(wgt::ShaderStage::FRAGMENT) {
for (i, state) in color_states.iter().enumerate() {
match interface.get(&(i as wgt::ShaderLocation)) {
match io.get(&(i as wgt::ShaderLocation)) {
Some(output) if validation::check_texture_format(state.format, output) => {}
Some(output) => {
tracing::warn!(
"Incompatible fragment output[{}] from shader: {:?}, expected {:?}",
i,
&**output,
output,
state.format,
);
return Err(
@ -2247,11 +2271,11 @@ pub enum DeviceError {
OutOfMemory,
}
impl From<hal::device::OomOrDeviceLost> for DeviceError {
fn from(err: hal::device::OomOrDeviceLost) -> Self {
impl From<hal::device::WaitError> for DeviceError {
fn from(err: hal::device::WaitError) -> Self {
match err {
hal::device::OomOrDeviceLost::OutOfMemory(_) => Self::OutOfMemory,
hal::device::OomOrDeviceLost::DeviceLost(_) => Self::Lost,
hal::device::WaitError::OutOfMemory(_) => Self::OutOfMemory,
hal::device::WaitError::DeviceLost(_) => Self::Lost,
}
}
}
@ -3238,6 +3262,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
&self,
device_id: id::DeviceId,
desc: &pipeline::ShaderModuleDescriptor,
source: pipeline::ShaderModuleSource,
id_in: Input<G, id::ShaderModuleId>,
) -> (
id::ShaderModuleId,
@ -3254,20 +3279,41 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
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,
#[cfg(feature = "trace")]
let data = match device.trace {
Some(ref trace) => {
let mut trace = trace.lock();
match source {
pipeline::ShaderModuleSource::SpirV(ref spv) => {
trace.make_binary("spv", unsafe {
std::slice::from_raw_parts(spv.as_ptr() as *const u8, spv.len() * 4)
})
}
pipeline::ShaderModuleSource::Wgsl(ref code) => {
trace.make_binary("wgsl", code.as_bytes())
}
pipeline::ShaderModuleSource::Naga(ref module) => {
let config = ron::ser::PrettyConfig::new();
let mut ron = Vec::new();
ron::ser::to_writer_pretty(&mut ron, &module, config).unwrap();
trace.make_binary("ron", &ron)
}
}
}
None => String::new(),
};
let shader = match device.create_shader_module(device_id, desc, source) {
Ok(shader) => shader,
Err(e) => break e,
};
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,
@ -3276,7 +3322,6 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
});
}
let _ = spv;
return (id.0, None);
};
@ -3859,8 +3904,8 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
B::get_surface_mut(surface)
.configure_swapchain(&device.raw, config)
.map_err(|err| match err {
hal::window::CreationError::OutOfMemory(_) => DeviceError::OutOfMemory,
hal::window::CreationError::DeviceLost(_) => DeviceError::Lost,
hal::window::SwapchainError::OutOfMemory(_) => DeviceError::OutOfMemory,
hal::window::SwapchainError::DeviceLost(_) => DeviceError::Lost,
_ => panic!("failed to configure swap chain on creation: {}", err),
})?;
}

View File

@ -7,37 +7,33 @@ use crate::{
device::{DeviceError, RenderPassContext},
hub::Resource,
id::{DeviceId, PipelineLayoutId, ShaderModuleId},
validation::StageError,
Label, LifeGuard, Stored,
validation, Label, LifeGuard, Stored,
};
use std::borrow::Cow;
use thiserror::Error;
use wgt::{BufferAddress, IndexFormat, InputStepMode};
#[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>),
// Unable to serialize with `naga::Module` in here:
// requires naga serialization feature.
//Naga(naga::Module),
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>,
/// If enabled, `wgpu` will attempt to operate on `Naga` representation
/// of the shader module for both validation and translation into the
/// backend shader languages, where `gfx-hal` supports this.
pub experimental_translation: bool,
}
#[derive(Debug)]
pub struct ShaderModule<B: hal::Backend> {
pub(crate) raw: B::ShaderModule,
pub(crate) device_id: Stored<DeviceId>,
pub(crate) module: Option<naga::Module>,
pub(crate) interface: Option<validation::Interface>,
#[cfg(debug_assertions)]
pub(crate) label: String,
}
@ -59,6 +55,8 @@ impl<B: hal::Backend> Resource for ShaderModule<B> {
#[derive(Clone, Debug, Error)]
pub enum CreateShaderModuleError {
#[error("Failed to parse WGSL")]
Parsing,
#[error(transparent)]
Device(#[from] DeviceError),
#[error(transparent)]
@ -113,7 +111,7 @@ pub enum CreateComputePipelineError {
#[error("unable to derive an implicit layout")]
Implicit(#[from] ImplicitLayoutError),
#[error(transparent)]
Stage(StageError),
Stage(validation::StageError),
}
#[derive(Debug)]
@ -220,7 +218,7 @@ pub enum CreateRenderPipelineError {
Stage {
flag: wgt::ShaderStage,
#[source]
error: StageError,
error: validation::StageError,
},
}

View File

@ -160,9 +160,8 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
None,
match err {
hal::window::AcquireError::OutOfMemory(_) => Err(DeviceError::OutOfMemory)?,
hal::window::AcquireError::NotReady => unreachable!(), // we always set a timeout
hal::window::AcquireError::Timeout => SwapChainStatus::Timeout,
hal::window::AcquireError::OutOfDate => SwapChainStatus::Outdated,
hal::window::AcquireError::NotReady { .. } => SwapChainStatus::Timeout,
hal::window::AcquireError::OutOfDate(_) => SwapChainStatus::Outdated,
hal::window::AcquireError::SurfaceLost(_) => SwapChainStatus::Lost,
hal::window::AcquireError::DeviceLost(_) => Err(DeviceError::Lost)?,
},
@ -283,7 +282,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
hal::window::PresentError::OutOfMemory(_) => {
Err(SwapChainError::Device(DeviceError::OutOfMemory))
}
hal::window::PresentError::OutOfDate => Ok(SwapChainStatus::Outdated),
hal::window::PresentError::OutOfDate(_) => Ok(SwapChainStatus::Outdated),
hal::window::PresentError::SurfaceLost(_) => Ok(SwapChainStatus::Lost),
hal::window::PresentError::DeviceLost(_) => {
Err(SwapChainError::Device(DeviceError::Lost))

File diff suppressed because it is too large Load Diff