Bug 1853105 - Update wgpu to revision 7fea9e934efd8d5dc03b9aa3e06b775c1ac4a23e. r=webgpu-reviewers,supply-chain-reviewers,ErichDonGubler

Differential Revision: https://phabricator.services.mozilla.com/D188214
This commit is contained in:
Nicolas Silva 2023-09-20 07:32:45 +00:00
parent 9aba49cf43
commit d135870658
60 changed files with 2153 additions and 1456 deletions

View file

@ -25,14 +25,14 @@ git = "https://github.com/franziskuskiefer/cose-rust"
rev = "43c22248d136c8b38fe42ea709d08da6355cf04b" rev = "43c22248d136c8b38fe42ea709d08da6355cf04b"
replace-with = "vendored-sources" replace-with = "vendored-sources"
[source."git+https://github.com/gfx-rs/naga?rev=7a19f3af909202c7eafd36633b5584bfbb353ecb"] [source."git+https://github.com/gfx-rs/naga?rev=cc87b8f9eb30bb55d0735b89d3df3e099e1a6e7c"]
git = "https://github.com/gfx-rs/naga" git = "https://github.com/gfx-rs/naga"
rev = "7a19f3af909202c7eafd36633b5584bfbb353ecb" rev = "cc87b8f9eb30bb55d0735b89d3df3e099e1a6e7c"
replace-with = "vendored-sources" replace-with = "vendored-sources"
[source."git+https://github.com/gfx-rs/wgpu?rev=332cd0325da52675432830870584ec9766679c34"] [source."git+https://github.com/gfx-rs/wgpu?rev=7fea9e934efd8d5dc03b9aa3e06b775c1ac4a23e"]
git = "https://github.com/gfx-rs/wgpu" git = "https://github.com/gfx-rs/wgpu"
rev = "332cd0325da52675432830870584ec9766679c34" rev = "7fea9e934efd8d5dc03b9aa3e06b775c1ac4a23e"
replace-with = "vendored-sources" replace-with = "vendored-sources"
[source."git+https://github.com/glandium/prost?rev=95964e9d33df3c2a9c3f14285e262867cab6f96b"] [source."git+https://github.com/glandium/prost?rev=95964e9d33df3c2a9c3f14285e262867cab6f96b"]

8
Cargo.lock generated
View file

@ -3724,7 +3724,7 @@ checksum = "a2983372caf4480544083767bf2d27defafe32af49ab4df3a0b7fc90793a3664"
[[package]] [[package]]
name = "naga" name = "naga"
version = "0.13.0" version = "0.13.0"
source = "git+https://github.com/gfx-rs/naga?rev=7a19f3af909202c7eafd36633b5584bfbb353ecb#7a19f3af909202c7eafd36633b5584bfbb353ecb" source = "git+https://github.com/gfx-rs/naga?rev=cc87b8f9eb30bb55d0735b89d3df3e099e1a6e7c#cc87b8f9eb30bb55d0735b89d3df3e099e1a6e7c"
dependencies = [ dependencies = [
"bit-set", "bit-set",
"bitflags 2.999.999", "bitflags 2.999.999",
@ -6301,7 +6301,7 @@ dependencies = [
[[package]] [[package]]
name = "wgpu-core" name = "wgpu-core"
version = "0.17.0" version = "0.17.0"
source = "git+https://github.com/gfx-rs/wgpu?rev=332cd0325da52675432830870584ec9766679c34#332cd0325da52675432830870584ec9766679c34" source = "git+https://github.com/gfx-rs/wgpu?rev=7fea9e934efd8d5dc03b9aa3e06b775c1ac4a23e#7fea9e934efd8d5dc03b9aa3e06b775c1ac4a23e"
dependencies = [ dependencies = [
"arrayvec", "arrayvec",
"bit-vec", "bit-vec",
@ -6324,7 +6324,7 @@ dependencies = [
[[package]] [[package]]
name = "wgpu-hal" name = "wgpu-hal"
version = "0.17.0" version = "0.17.0"
source = "git+https://github.com/gfx-rs/wgpu?rev=332cd0325da52675432830870584ec9766679c34#332cd0325da52675432830870584ec9766679c34" source = "git+https://github.com/gfx-rs/wgpu?rev=7fea9e934efd8d5dc03b9aa3e06b775c1ac4a23e#7fea9e934efd8d5dc03b9aa3e06b775c1ac4a23e"
dependencies = [ dependencies = [
"android_system_properties", "android_system_properties",
"arrayvec", "arrayvec",
@ -6360,7 +6360,7 @@ dependencies = [
[[package]] [[package]]
name = "wgpu-types" name = "wgpu-types"
version = "0.17.0" version = "0.17.0"
source = "git+https://github.com/gfx-rs/wgpu?rev=332cd0325da52675432830870584ec9766679c34#332cd0325da52675432830870584ec9766679c34" source = "git+https://github.com/gfx-rs/wgpu?rev=7fea9e934efd8d5dc03b9aa3e06b775c1ac4a23e#7fea9e934efd8d5dc03b9aa3e06b775c1ac4a23e"
dependencies = [ dependencies = [
"bitflags 2.999.999", "bitflags 2.999.999",
"js-sys", "js-sys",

View file

@ -17,7 +17,7 @@ default = []
[dependencies.wgc] [dependencies.wgc]
package = "wgpu-core" package = "wgpu-core"
git = "https://github.com/gfx-rs/wgpu" git = "https://github.com/gfx-rs/wgpu"
rev = "332cd0325da52675432830870584ec9766679c34" rev = "7fea9e934efd8d5dc03b9aa3e06b775c1ac4a23e"
#Note: "replay" shouldn't ideally be needed, #Note: "replay" shouldn't ideally be needed,
# but it allows us to serialize everything across IPC. # but it allows us to serialize everything across IPC.
features = ["replay", "trace", "serial-pass", "strict_asserts", "wgsl"] features = ["replay", "trace", "serial-pass", "strict_asserts", "wgsl"]
@ -27,32 +27,32 @@ features = ["replay", "trace", "serial-pass", "strict_asserts", "wgsl"]
[target.'cfg(any(target_os = "macos", target_os = "ios"))'.dependencies.wgc] [target.'cfg(any(target_os = "macos", target_os = "ios"))'.dependencies.wgc]
package = "wgpu-core" package = "wgpu-core"
git = "https://github.com/gfx-rs/wgpu" git = "https://github.com/gfx-rs/wgpu"
rev = "332cd0325da52675432830870584ec9766679c34" rev = "7fea9e934efd8d5dc03b9aa3e06b775c1ac4a23e"
features = ["metal"] features = ["metal"]
# We want the wgpu-core Direct3D backends on Windows. # We want the wgpu-core Direct3D backends on Windows.
[target.'cfg(windows)'.dependencies.wgc] [target.'cfg(windows)'.dependencies.wgc]
package = "wgpu-core" package = "wgpu-core"
git = "https://github.com/gfx-rs/wgpu" git = "https://github.com/gfx-rs/wgpu"
rev = "332cd0325da52675432830870584ec9766679c34" rev = "7fea9e934efd8d5dc03b9aa3e06b775c1ac4a23e"
features = ["dx11", "dx12"] features = ["dx11", "dx12"]
# We want the wgpu-core Vulkan backend on Linux and Windows. # We want the wgpu-core Vulkan backend on Linux and Windows.
[target.'cfg(any(windows, all(unix, not(any(target_os = "macos", target_os = "ios")))))'.dependencies.wgc] [target.'cfg(any(windows, all(unix, not(any(target_os = "macos", target_os = "ios")))))'.dependencies.wgc]
package = "wgpu-core" package = "wgpu-core"
git = "https://github.com/gfx-rs/wgpu" git = "https://github.com/gfx-rs/wgpu"
rev = "332cd0325da52675432830870584ec9766679c34" rev = "7fea9e934efd8d5dc03b9aa3e06b775c1ac4a23e"
features = ["vulkan"] features = ["vulkan"]
[dependencies.wgt] [dependencies.wgt]
package = "wgpu-types" package = "wgpu-types"
git = "https://github.com/gfx-rs/wgpu" git = "https://github.com/gfx-rs/wgpu"
rev = "332cd0325da52675432830870584ec9766679c34" rev = "7fea9e934efd8d5dc03b9aa3e06b775c1ac4a23e"
[dependencies.wgh] [dependencies.wgh]
package = "wgpu-hal" package = "wgpu-hal"
git = "https://github.com/gfx-rs/wgpu" git = "https://github.com/gfx-rs/wgpu"
rev = "332cd0325da52675432830870584ec9766679c34" rev = "7fea9e934efd8d5dc03b9aa3e06b775c1ac4a23e"
[target.'cfg(windows)'.dependencies] [target.'cfg(windows)'.dependencies]
d3d12 = "0.7.0" d3d12 = "0.7.0"

View file

@ -20,11 +20,11 @@ origin:
# Human-readable identifier for this version/release # Human-readable identifier for this version/release
# Generally "version NNN", "tag SSS", "bookmark SSS" # Generally "version NNN", "tag SSS", "bookmark SSS"
release: commit 332cd0325da52675432830870584ec9766679c34 release: commit 7fea9e934efd8d5dc03b9aa3e06b775c1ac4a23e
# Revision to pull in # Revision to pull in
# Must be a long or short commit SHA (long preferred) # Must be a long or short commit SHA (long preferred)
revision: 332cd0325da52675432830870584ec9766679c34 revision: 7fea9e934efd8d5dc03b9aa3e06b775c1ac4a23e
license: ['MIT', 'Apache-2.0'] license: ['MIT', 'Apache-2.0']

View file

@ -2283,6 +2283,11 @@ who = "Teodor Tanasoaia <ttanasoaia@mozilla.com>"
criteria = "safe-to-deploy" criteria = "safe-to-deploy"
delta = "0.12.0@git:b99d58ea435090e561377949f428bce2c18451bb -> 0.12.0@git:76003dc0035d53a474d366dcdf49d2e4d12e921f" delta = "0.12.0@git:b99d58ea435090e561377949f428bce2c18451bb -> 0.12.0@git:76003dc0035d53a474d366dcdf49d2e4d12e921f"
[[audits.naga]]
who = "Nicolas Silva <nical@fastmail.com>"
criteria = "safe-to-deploy"
delta = "0.13.0@git:7a19f3af909202c7eafd36633b5584bfbb353ecb -> 0.13.0@git:cc87b8f9eb30bb55d0735b89d3df3e099e1a6e7c"
[[audits.naga]] [[audits.naga]]
who = "Erich Gubler <egubler@mozilla.com" who = "Erich Gubler <egubler@mozilla.com"
criteria = "safe-to-deploy" criteria = "safe-to-deploy"
@ -3992,6 +3997,11 @@ who = "Erich Gubler <erichdongubler@gmail.com>"
criteria = "safe-to-deploy" criteria = "safe-to-deploy"
delta = "0.17.0@git:09b010b26af6876ce84991576a168a572172f08d -> 0.17.0@git:7c6b85756237f77bfe8d6231dfc7a1412ff662b6" delta = "0.17.0@git:09b010b26af6876ce84991576a168a572172f08d -> 0.17.0@git:7c6b85756237f77bfe8d6231dfc7a1412ff662b6"
[[audits.wgpu-core]]
who = "Nicolas Silva <nical@fastmail.com>"
criteria = "safe-to-deploy"
delta = "0.17.0@git:332cd0325da52675432830870584ec9766679c34 -> 0.17.0@git:7fea9e934efd8d5dc03b9aa3e06b775c1ac4a23e"
[[audits.wgpu-core]] [[audits.wgpu-core]]
who = "Erich Gubler <erichdongubler@gmail.com>" who = "Erich Gubler <erichdongubler@gmail.com>"
criteria = "safe-to-deploy" criteria = "safe-to-deploy"
@ -4100,6 +4110,11 @@ who = "Erich Gubler <erichdongubler@gmail.com>"
criteria = "safe-to-deploy" criteria = "safe-to-deploy"
delta = "0.17.0@git:09b010b26af6876ce84991576a168a572172f08d -> 0.17.0@git:7c6b85756237f77bfe8d6231dfc7a1412ff662b6" delta = "0.17.0@git:09b010b26af6876ce84991576a168a572172f08d -> 0.17.0@git:7c6b85756237f77bfe8d6231dfc7a1412ff662b6"
[[audits.wgpu-hal]]
who = "Nicolas Silva <nical@fastmail.com>"
criteria = "safe-to-deploy"
delta = "0.17.0@git:332cd0325da52675432830870584ec9766679c34 -> 0.17.0@git:7fea9e934efd8d5dc03b9aa3e06b775c1ac4a23e"
[[audits.wgpu-hal]] [[audits.wgpu-hal]]
who = "Erich Gubler <erichdongubler@gmail.com>" who = "Erich Gubler <erichdongubler@gmail.com>"
criteria = "safe-to-deploy" criteria = "safe-to-deploy"
@ -4208,6 +4223,11 @@ who = "Erich Gubler <erichdongubler@gmail.com>"
criteria = "safe-to-deploy" criteria = "safe-to-deploy"
delta = "0.17.0@git:09b010b26af6876ce84991576a168a572172f08d -> 0.17.0@git:7c6b85756237f77bfe8d6231dfc7a1412ff662b6" delta = "0.17.0@git:09b010b26af6876ce84991576a168a572172f08d -> 0.17.0@git:7c6b85756237f77bfe8d6231dfc7a1412ff662b6"
[[audits.wgpu-types]]
who = "Nicolas Silva <nical@fastmail.com>"
criteria = "safe-to-deploy"
delta = "0.17.0@git:332cd0325da52675432830870584ec9766679c34 -> 0.17.0@git:7fea9e934efd8d5dc03b9aa3e06b775c1ac4a23e"
[[audits.wgpu-types]] [[audits.wgpu-types]]
who = "Erich Gubler <erichdongubler@gmail.com>" who = "Erich Gubler <erichdongubler@gmail.com>"
criteria = "safe-to-deploy" criteria = "safe-to-deploy"

File diff suppressed because one or more lines are too long

View file

@ -41,6 +41,8 @@ bitflags::bitflags! {
const TEXTURE_LEVELS = 1 << 19; const TEXTURE_LEVELS = 1 << 19;
/// Image size query /// Image size query
const IMAGE_SIZE = 1 << 20; const IMAGE_SIZE = 1 << 20;
/// Dual source blending
const DUAL_SOURCE_BLENDING = 1 << 21;
} }
} }
@ -104,6 +106,7 @@ impl FeaturesManager {
check_feature!(CULL_DISTANCE, 450, 300 /* with extension */); check_feature!(CULL_DISTANCE, 450, 300 /* with extension */);
check_feature!(SAMPLE_VARIABLES, 400, 300); check_feature!(SAMPLE_VARIABLES, 400, 300);
check_feature!(DYNAMIC_ARRAY_SIZE, 430, 310); check_feature!(DYNAMIC_ARRAY_SIZE, 430, 310);
check_feature!(DUAL_SOURCE_BLENDING, 330, 300 /* with extension */);
match version { match version {
Version::Embedded { is_webgl: true, .. } => check_feature!(MULTI_VIEW, 140, 300), Version::Embedded { is_webgl: true, .. } => check_feature!(MULTI_VIEW, 140, 300),
_ => check_feature!(MULTI_VIEW, 140, 310), _ => check_feature!(MULTI_VIEW, 140, 310),
@ -233,6 +236,10 @@ impl FeaturesManager {
// https://www.khronos.org/registry/OpenGL/extensions/ARB/ARB_texture_query_levels.txt // https://www.khronos.org/registry/OpenGL/extensions/ARB/ARB_texture_query_levels.txt
writeln!(out, "#extension GL_ARB_texture_query_levels : require")?; writeln!(out, "#extension GL_ARB_texture_query_levels : require")?;
} }
if self.0.contains(Features::DUAL_SOURCE_BLENDING) && version.is_es() {
// https://registry.khronos.org/OpenGL/extensions/EXT/EXT_blend_func_extended.txt
writeln!(out, "#extension GL_EXT_blend_func_extended : require")?;
}
Ok(()) Ok(())
} }
@ -497,6 +504,7 @@ impl<'a, W> Writer<'a, W> {
location: _, location: _,
interpolation, interpolation,
sampling, sampling,
second_blend_source,
} => { } => {
if interpolation == Some(Interpolation::Linear) { if interpolation == Some(Interpolation::Linear) {
self.features.request(Features::NOPERSPECTIVE_QUALIFIER); self.features.request(Features::NOPERSPECTIVE_QUALIFIER);
@ -504,6 +512,9 @@ impl<'a, W> Writer<'a, W> {
if sampling == Some(Sampling::Sample) { if sampling == Some(Sampling::Sample) {
self.features.request(Features::SAMPLE_QUALIFIER); self.features.request(Features::SAMPLE_QUALIFIER);
} }
if second_blend_source {
self.features.request(Features::DUAL_SOURCE_BLENDING);
}
} }
} }
} }

View file

@ -477,4 +477,7 @@ pub const RESERVED_KEYWORDS: &[&str] = &[
// entry point name (should not be shadowed) // entry point name (should not be shadowed)
// //
"main", "main",
// Naga utilities:
super::MODF_FUNCTION,
super::FREXP_FUNCTION,
]; ];

View file

@ -72,6 +72,9 @@ pub const SUPPORTED_ES_VERSIONS: &[u16] = &[300, 310, 320];
/// of detail for bounds checking in `ImageLoad` /// of detail for bounds checking in `ImageLoad`
const CLAMPED_LOD_SUFFIX: &str = "_clamped_lod"; const CLAMPED_LOD_SUFFIX: &str = "_clamped_lod";
pub(crate) const MODF_FUNCTION: &str = "naga_modf";
pub(crate) const FREXP_FUNCTION: &str = "naga_frexp";
/// Mapping between resources and bindings. /// Mapping between resources and bindings.
pub type BindingMap = std::collections::BTreeMap<crate::ResourceBinding, u8>; pub type BindingMap = std::collections::BTreeMap<crate::ResourceBinding, u8>;
@ -333,6 +336,12 @@ struct VaryingName<'a> {
impl fmt::Display for VaryingName<'_> { impl fmt::Display for VaryingName<'_> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self.binding { match *self.binding {
crate::Binding::Location {
second_blend_source: true,
..
} => {
write!(f, "_fs2p_location1",)
}
crate::Binding::Location { location, .. } => { crate::Binding::Location { location, .. } => {
let prefix = match (self.stage, self.output) { let prefix = match (self.stage, self.output) {
(ShaderStage::Compute, _) => unreachable!(), (ShaderStage::Compute, _) => unreachable!(),
@ -625,6 +634,53 @@ impl<'a, W: Write> Writer<'a, W> {
} }
} }
// Write functions to create special types.
for (type_key, struct_ty) in self.module.special_types.predeclared_types.iter() {
match type_key {
&crate::PredeclaredType::ModfResult { size, width }
| &crate::PredeclaredType::FrexpResult { size, width } => {
let arg_type_name_owner;
let arg_type_name = if let Some(size) = size {
arg_type_name_owner =
format!("{}vec{}", if width == 8 { "d" } else { "" }, size as u8);
&arg_type_name_owner
} else if width == 8 {
"double"
} else {
"float"
};
let other_type_name_owner;
let (defined_func_name, called_func_name, other_type_name) =
if matches!(type_key, &crate::PredeclaredType::ModfResult { .. }) {
(MODF_FUNCTION, "modf", arg_type_name)
} else {
let other_type_name = if let Some(size) = size {
other_type_name_owner = format!("ivec{}", size as u8);
&other_type_name_owner
} else {
"int"
};
(FREXP_FUNCTION, "frexp", other_type_name)
};
let struct_name = &self.names[&NameKey::Type(*struct_ty)];
writeln!(self.out)?;
writeln!(
self.out,
"{} {defined_func_name}({arg_type_name} arg) {{
{other_type_name} other;
{arg_type_name} fract = {called_func_name}(arg, other);
return {}(fract, other);
}}",
struct_name, struct_name
)?;
}
&crate::PredeclaredType::AtomicCompareExchangeWeakResult { .. } => {}
}
}
// Write all named constants // Write all named constants
let mut constants = self let mut constants = self
.module .module
@ -1235,12 +1291,13 @@ impl<'a, W: Write> Writer<'a, W> {
Some(binding) => binding, Some(binding) => binding,
}; };
let (location, interpolation, sampling) = match *binding { let (location, interpolation, sampling, second_blend_source) = match *binding {
crate::Binding::Location { crate::Binding::Location {
location, location,
interpolation, interpolation,
sampling, sampling,
} => (location, interpolation, sampling), second_blend_source,
} => (location, interpolation, sampling, second_blend_source),
crate::Binding::BuiltIn(built_in) => { crate::Binding::BuiltIn(built_in) => {
if let crate::BuiltIn::Position { invariant: true } = built_in { if let crate::BuiltIn::Position { invariant: true } = built_in {
match (self.options.version, self.entry_point.stage) { match (self.options.version, self.entry_point.stage) {
@ -1281,7 +1338,11 @@ impl<'a, W: Write> Writer<'a, W> {
// Write the I/O locations, if allowed // Write the I/O locations, if allowed
if self.options.version.supports_explicit_locations() || !emit_interpolation_and_auxiliary { if self.options.version.supports_explicit_locations() || !emit_interpolation_and_auxiliary {
write!(self.out, "layout(location = {location}) ")?; if second_blend_source {
write!(self.out, "layout(location = {location}, index = 1) ")?;
} else {
write!(self.out, "layout(location = {location}) ")?;
}
} }
// Write the interpolation qualifier. // Write the interpolation qualifier.
@ -1318,6 +1379,7 @@ impl<'a, W: Write> Writer<'a, W> {
location, location,
interpolation: None, interpolation: None,
sampling: None, sampling: None,
second_blend_source,
}, },
stage: self.entry_point.stage, stage: self.entry_point.stage,
output, output,
@ -2985,8 +3047,8 @@ impl<'a, W: Write> Writer<'a, W> {
Mf::Round => "roundEven", Mf::Round => "roundEven",
Mf::Fract => "fract", Mf::Fract => "fract",
Mf::Trunc => "trunc", Mf::Trunc => "trunc",
Mf::Modf => "modf", Mf::Modf => MODF_FUNCTION,
Mf::Frexp => "frexp", Mf::Frexp => FREXP_FUNCTION,
Mf::Ldexp => "ldexp", Mf::Ldexp => "ldexp",
// exponent // exponent
Mf::Exp => "exp", Mf::Exp => "exp",

View file

@ -781,6 +781,59 @@ impl<'a, W: Write> super::Writer<'a, W> {
Ok(()) Ok(())
} }
/// Write functions to create special types.
pub(super) fn write_special_functions(&mut self, module: &crate::Module) -> BackendResult {
for (type_key, struct_ty) in module.special_types.predeclared_types.iter() {
match type_key {
&crate::PredeclaredType::ModfResult { size, width }
| &crate::PredeclaredType::FrexpResult { size, width } => {
let arg_type_name_owner;
let arg_type_name = if let Some(size) = size {
arg_type_name_owner = format!(
"{}{}",
if width == 8 { "double" } else { "float" },
size as u8
);
&arg_type_name_owner
} else if width == 8 {
"double"
} else {
"float"
};
let (defined_func_name, called_func_name, second_field_name, sign_multiplier) =
if matches!(type_key, &crate::PredeclaredType::ModfResult { .. }) {
(super::writer::MODF_FUNCTION, "modf", "whole", "")
} else {
(
super::writer::FREXP_FUNCTION,
"frexp",
"exp_",
"sign(arg) * ",
)
};
let struct_name = &self.names[&NameKey::Type(*struct_ty)];
writeln!(
self.out,
"{struct_name} {defined_func_name}({arg_type_name} arg) {{
{arg_type_name} other;
{struct_name} result;
result.fract = {sign_multiplier}{called_func_name}(arg, other);
result.{second_field_name} = other;
return result;
}}"
)?;
writeln!(self.out)?;
}
&crate::PredeclaredType::AtomicCompareExchangeWeakResult { .. } => {}
}
}
Ok(())
}
/// Helper function that writes compose wrapped functions /// Helper function that writes compose wrapped functions
pub(super) fn write_wrapped_compose_functions( pub(super) fn write_wrapped_compose_functions(
&mut self, &mut self,

View file

@ -814,6 +814,9 @@ pub const RESERVED: &[&str] = &[
"TextureBuffer", "TextureBuffer",
"ConstantBuffer", "ConstantBuffer",
"RayQuery", "RayQuery",
// Naga utilities
super::writer::MODF_FUNCTION,
super::writer::FREXP_FUNCTION,
]; ];
// DXC scalar types, from https://github.com/microsoft/DirectXShaderCompiler/blob/18c9e114f9c314f93e68fbc72ce207d4ed2e65ae/tools/clang/lib/AST/ASTContextHLSL.cpp#L48-L254 // DXC scalar types, from https://github.com/microsoft/DirectXShaderCompiler/blob/18c9e114f9c314f93e68fbc72ce207d4ed2e65ae/tools/clang/lib/AST/ASTContextHLSL.cpp#L48-L254

View file

@ -17,6 +17,9 @@ const SPECIAL_BASE_VERTEX: &str = "base_vertex";
const SPECIAL_BASE_INSTANCE: &str = "base_instance"; const SPECIAL_BASE_INSTANCE: &str = "base_instance";
const SPECIAL_OTHER: &str = "other"; const SPECIAL_OTHER: &str = "other";
pub(crate) const MODF_FUNCTION: &str = "naga_modf";
pub(crate) const FREXP_FUNCTION: &str = "naga_frexp";
struct EpStructMember { struct EpStructMember {
name: String, name: String,
ty: Handle<crate::Type>, ty: Handle<crate::Type>,
@ -244,6 +247,8 @@ impl<'a, W: fmt::Write> super::Writer<'a, W> {
} }
} }
self.write_special_functions(module)?;
self.write_wrapped_compose_functions(module, &module.const_expressions)?; self.write_wrapped_compose_functions(module, &module.const_expressions)?;
// Write all named constants // Write all named constants
@ -416,7 +421,17 @@ impl<'a, W: fmt::Write> super::Writer<'a, W> {
let builtin_str = builtin.to_hlsl_str()?; let builtin_str = builtin.to_hlsl_str()?;
write!(self.out, " : {builtin_str}")?; write!(self.out, " : {builtin_str}")?;
} }
crate::Binding::Location { location, .. } => { crate::Binding::Location {
second_blend_source: true,
..
} => {
write!(self.out, " : SV_Target1")?;
}
crate::Binding::Location {
location,
second_blend_source: false,
..
} => {
if stage == Some((crate::ShaderStage::Fragment, Io::Output)) { if stage == Some((crate::ShaderStage::Fragment, Io::Output)) {
write!(self.out, " : SV_Target{location}")?; write!(self.out, " : SV_Target{location}")?;
} else { } else {
@ -2665,8 +2680,8 @@ impl<'a, W: fmt::Write> super::Writer<'a, W> {
Mf::Round => Function::Regular("round"), Mf::Round => Function::Regular("round"),
Mf::Fract => Function::Regular("frac"), Mf::Fract => Function::Regular("frac"),
Mf::Trunc => Function::Regular("trunc"), Mf::Trunc => Function::Regular("trunc"),
Mf::Modf => Function::Regular("modf"), Mf::Modf => Function::Regular(MODF_FUNCTION),
Mf::Frexp => Function::Regular("frexp"), Mf::Frexp => Function::Regular(FREXP_FUNCTION),
Mf::Ldexp => Function::Regular("ldexp"), Mf::Ldexp => Function::Regular("ldexp"),
// exponent // exponent
Mf::Exp => Function::Regular("exp"), Mf::Exp => Function::Regular("exp"),

View file

@ -214,4 +214,6 @@ pub const RESERVED: &[&str] = &[
// Naga utilities // Naga utilities
"DefaultConstructible", "DefaultConstructible",
"clamped_lod_e", "clamped_lod_e",
super::writer::FREXP_FUNCTION,
super::writer::MODF_FUNCTION,
]; ];

View file

@ -82,7 +82,10 @@ pub type EntryPointResourceMap = std::collections::BTreeMap<String, EntryPointRe
enum ResolvedBinding { enum ResolvedBinding {
BuiltIn(crate::BuiltIn), BuiltIn(crate::BuiltIn),
Attribute(u32), Attribute(u32),
Color(u32), Color {
location: u32,
second_blend_source: bool,
},
User { User {
prefix: &'static str, prefix: &'static str,
index: u32, index: u32,
@ -245,9 +248,20 @@ impl Options {
location, location,
interpolation, interpolation,
sampling, sampling,
second_blend_source,
} => match mode { } => match mode {
LocationMode::VertexInput => Ok(ResolvedBinding::Attribute(location)), LocationMode::VertexInput => Ok(ResolvedBinding::Attribute(location)),
LocationMode::FragmentOutput => Ok(ResolvedBinding::Color(location)), LocationMode::FragmentOutput => {
if second_blend_source && self.lang_version < (1, 2) {
return Err(Error::UnsupportedAttribute(
"second_blend_source".to_string(),
));
}
Ok(ResolvedBinding::Color {
location,
second_blend_source,
})
}
LocationMode::VertexOutput | LocationMode::FragmentInput => { LocationMode::VertexOutput | LocationMode::FragmentInput => {
Ok(ResolvedBinding::User { Ok(ResolvedBinding::User {
prefix: if self.spirv_cross_compatibility { prefix: if self.spirv_cross_compatibility {
@ -404,7 +418,16 @@ impl ResolvedBinding {
write!(out, "{name}")?; write!(out, "{name}")?;
} }
Self::Attribute(index) => write!(out, "attribute({index})")?, Self::Attribute(index) => write!(out, "attribute({index})")?,
Self::Color(index) => write!(out, "color({index})")?, Self::Color {
location,
second_blend_source,
} => {
if second_blend_source {
write!(out, "color({location}) index(1)")?
} else {
write!(out, "color({location})")?
}
}
Self::User { Self::User {
prefix, prefix,
index, index,

View file

@ -32,6 +32,9 @@ const RAY_QUERY_FIELD_INTERSECTION: &str = "intersection";
const RAY_QUERY_FIELD_READY: &str = "ready"; const RAY_QUERY_FIELD_READY: &str = "ready";
const RAY_QUERY_FUN_MAP_INTERSECTION: &str = "_map_intersection_type"; const RAY_QUERY_FUN_MAP_INTERSECTION: &str = "_map_intersection_type";
pub(crate) const MODF_FUNCTION: &str = "naga_modf";
pub(crate) const FREXP_FUNCTION: &str = "naga_frexp";
/// Write the Metal name for a Naga numeric type: scalar, vector, or matrix. /// Write the Metal name for a Naga numeric type: scalar, vector, or matrix.
/// ///
/// The `sizes` slice determines whether this function writes a /// The `sizes` slice determines whether this function writes a
@ -1181,6 +1184,31 @@ impl<W: Write> Writer<W> {
Ok(()) Ok(())
} }
/// Emit code for the sign(i32) expression.
///
fn put_isign(
&mut self,
arg: Handle<crate::Expression>,
context: &ExpressionContext,
) -> BackendResult {
write!(self.out, "{NAMESPACE}::select({NAMESPACE}::select(")?;
match context.resolve_type(arg) {
&crate::TypeInner::Vector { size, .. } => {
let size = back::vector_size_str(size);
write!(self.out, "int{size}(-1), int{size}(1)")?;
}
_ => {
write!(self.out, "-1, 1")?;
}
}
write!(self.out, ", (")?;
self.put_expression(arg, context, true)?;
write!(self.out, " > 0)), 0, (")?;
self.put_expression(arg, context, true)?;
write!(self.out, " == 0))")?;
Ok(())
}
fn put_const_expression( fn put_const_expression(
&mut self, &mut self,
expr_handle: Handle<crate::Expression>, expr_handle: Handle<crate::Expression>,
@ -1644,8 +1672,9 @@ impl<W: Write> Writer<W> {
} => { } => {
use crate::MathFunction as Mf; use crate::MathFunction as Mf;
let scalar_argument = match *context.resolve_type(arg) { let arg_type = context.resolve_type(arg);
crate::TypeInner::Scalar { .. } => true, let scalar_argument = match arg_type {
&crate::TypeInner::Scalar { .. } => true,
_ => false, _ => false,
}; };
@ -1678,8 +1707,8 @@ impl<W: Write> Writer<W> {
Mf::Round => "rint", Mf::Round => "rint",
Mf::Fract => "fract", Mf::Fract => "fract",
Mf::Trunc => "trunc", Mf::Trunc => "trunc",
Mf::Modf => "modf", Mf::Modf => MODF_FUNCTION,
Mf::Frexp => "frexp", Mf::Frexp => FREXP_FUNCTION,
Mf::Ldexp => "ldexp", Mf::Ldexp => "ldexp",
// exponent // exponent
Mf::Exp => "exp", Mf::Exp => "exp",
@ -1710,7 +1739,12 @@ impl<W: Write> Writer<W> {
Mf::Reflect => "reflect", Mf::Reflect => "reflect",
Mf::Refract => "refract", Mf::Refract => "refract",
// computational // computational
Mf::Sign => "sign", Mf::Sign => match arg_type.scalar_kind() {
Some(crate::ScalarKind::Sint) => {
return self.put_isign(arg, context);
}
_ => "sign",
},
Mf::Fma => "fma", Mf::Fma => "fma",
Mf::Mix => "mix", Mf::Mix => "mix",
Mf::Step => "step", Mf::Step => "step",
@ -1813,6 +1847,9 @@ impl<W: Write> Writer<W> {
write!(self.out, "((")?; write!(self.out, "((")?;
self.put_expression(arg, context, false)?; self.put_expression(arg, context, false)?;
write!(self.out, ") * 57.295779513082322865)")?; write!(self.out, ") * 57.295779513082322865)")?;
} else if fun == Mf::Modf || fun == Mf::Frexp {
write!(self.out, "{fun_name}")?;
self.put_call_parameters(iter::once(arg), context)?;
} else { } else {
write!(self.out, "{NAMESPACE}::{fun_name}")?; write!(self.out, "{NAMESPACE}::{fun_name}")?;
self.put_call_parameters( self.put_call_parameters(
@ -2417,6 +2454,16 @@ impl<W: Write> Writer<W> {
crate::MathFunction::FindMsb => { crate::MathFunction::FindMsb => {
self.need_bake_expressions.insert(arg); self.need_bake_expressions.insert(arg);
} }
crate::MathFunction::Sign => {
// WGSL's `sign` function works also on signed ints, but Metal's only
// works on floating points, so we emit inline code for integer `sign`
// calls. But that code uses each argument 2 times (see `put_isign`),
// so to avoid duplicated evaluation, we must bake the argument.
let inner = context.resolve_type(expr_handle);
if inner.scalar_kind() == Some(crate::ScalarKind::Sint) {
self.need_bake_expressions.insert(arg);
}
}
_ => {} _ => {}
} }
} }
@ -3236,6 +3283,57 @@ impl<W: Write> Writer<W> {
} }
} }
} }
// Write functions to create special types.
for (type_key, struct_ty) in module.special_types.predeclared_types.iter() {
match type_key {
&crate::PredeclaredType::ModfResult { size, width }
| &crate::PredeclaredType::FrexpResult { size, width } => {
let arg_type_name_owner;
let arg_type_name = if let Some(size) = size {
arg_type_name_owner = format!(
"{NAMESPACE}::{}{}",
if width == 8 { "double" } else { "float" },
size as u8
);
&arg_type_name_owner
} else if width == 8 {
"double"
} else {
"float"
};
let other_type_name_owner;
let (defined_func_name, called_func_name, other_type_name) =
if matches!(type_key, &crate::PredeclaredType::ModfResult { .. }) {
(MODF_FUNCTION, "modf", arg_type_name)
} else {
let other_type_name = if let Some(size) = size {
other_type_name_owner = format!("int{}", size as u8);
&other_type_name_owner
} else {
"int"
};
(FREXP_FUNCTION, "frexp", other_type_name)
};
let struct_name = &self.names[&NameKey::Type(*struct_ty)];
writeln!(self.out)?;
writeln!(
self.out,
"{} {defined_func_name}({arg_type_name} arg) {{
{other_type_name} other;
{arg_type_name} fract = {NAMESPACE}::{called_func_name}(arg, other);
return {}{{ fract, other }};
}}",
struct_name, struct_name
)?;
}
&crate::PredeclaredType::AtomicCompareExchangeWeakResult { .. } => {}
}
}
Ok(()) Ok(())
} }

View file

@ -787,8 +787,8 @@ impl<'w> BlockContext<'w> {
Mf::Floor => MathOp::Ext(spirv::GLOp::Floor), Mf::Floor => MathOp::Ext(spirv::GLOp::Floor),
Mf::Fract => MathOp::Ext(spirv::GLOp::Fract), Mf::Fract => MathOp::Ext(spirv::GLOp::Fract),
Mf::Trunc => MathOp::Ext(spirv::GLOp::Trunc), Mf::Trunc => MathOp::Ext(spirv::GLOp::Trunc),
Mf::Modf => MathOp::Ext(spirv::GLOp::Modf), Mf::Modf => MathOp::Ext(spirv::GLOp::ModfStruct),
Mf::Frexp => MathOp::Ext(spirv::GLOp::Frexp), Mf::Frexp => MathOp::Ext(spirv::GLOp::FrexpStruct),
Mf::Ldexp => MathOp::Ext(spirv::GLOp::Ldexp), Mf::Ldexp => MathOp::Ext(spirv::GLOp::Ldexp),
// geometry // geometry
Mf::Dot => match *self.fun_info[arg].ty.inner_with(&self.ir_module.types) { Mf::Dot => match *self.fun_info[arg].ty.inner_with(&self.ir_module.types) {

View file

@ -1434,6 +1434,7 @@ impl Writer {
location, location,
interpolation, interpolation,
sampling, sampling,
second_blend_source,
} => { } => {
self.decorate(id, Decoration::Location, &[location]); self.decorate(id, Decoration::Location, &[location]);
@ -1473,6 +1474,9 @@ impl Writer {
} }
} }
} }
if second_blend_source {
self.decorate(id, Decoration::Index, &[1]);
}
} }
crate::Binding::BuiltIn(built_in) => { crate::Binding::BuiltIn(built_in) => {
use crate::BuiltIn as Bi; use crate::BuiltIn as Bi;

View file

@ -17,6 +17,7 @@ enum Attribute {
Invariant, Invariant,
Interpolate(Option<crate::Interpolation>, Option<crate::Sampling>), Interpolate(Option<crate::Interpolation>, Option<crate::Sampling>),
Location(u32), Location(u32),
SecondBlendSource,
Stage(ShaderStage), Stage(ShaderStage),
WorkGroupSize([u32; 3]), WorkGroupSize([u32; 3]),
} }
@ -96,6 +97,14 @@ impl<W: Write> Writer<W> {
self.ep_results.clear(); self.ep_results.clear();
} }
fn is_builtin_wgsl_struct(&self, module: &Module, handle: Handle<crate::Type>) -> bool {
module
.special_types
.predeclared_types
.values()
.any(|t| *t == handle)
}
pub fn write(&mut self, module: &Module, info: &valid::ModuleInfo) -> BackendResult { pub fn write(&mut self, module: &Module, info: &valid::ModuleInfo) -> BackendResult {
self.reset(module); self.reset(module);
@ -108,13 +117,13 @@ impl<W: Write> Writer<W> {
// Write all structs // Write all structs
for (handle, ty) in module.types.iter() { for (handle, ty) in module.types.iter() {
if let TypeInner::Struct { if let TypeInner::Struct { ref members, .. } = ty.inner {
ref members, {
span: _, if !self.is_builtin_wgsl_struct(module, handle) {
} = ty.inner self.write_struct(module, handle, members)?;
{ writeln!(self.out)?;
self.write_struct(module, handle, members)?; }
writeln!(self.out)?; }
} }
} }
@ -319,6 +328,7 @@ impl<W: Write> Writer<W> {
for attribute in attributes { for attribute in attributes {
match *attribute { match *attribute {
Attribute::Location(id) => write!(self.out, "@location({id}) ")?, Attribute::Location(id) => write!(self.out, "@location({id}) ")?,
Attribute::SecondBlendSource => write!(self.out, "@second_blend_source ")?,
Attribute::BuiltIn(builtin_attrib) => { Attribute::BuiltIn(builtin_attrib) => {
let builtin = builtin_str(builtin_attrib)?; let builtin = builtin_str(builtin_attrib)?;
write!(self.out, "@builtin({builtin}) ")?; write!(self.out, "@builtin({builtin}) ")?;
@ -1917,9 +1927,20 @@ fn map_binding_to_attribute(binding: &crate::Binding) -> Vec<Attribute> {
location, location,
interpolation, interpolation,
sampling, sampling,
second_blend_source: false,
} => vec![ } => vec![
Attribute::Location(location), Attribute::Location(location),
Attribute::Interpolate(interpolation, sampling), Attribute::Interpolate(interpolation, sampling),
], ],
crate::Binding::Location {
location,
interpolation,
sampling,
second_blend_source: true,
} => vec![
Attribute::Location(location),
Attribute::SecondBlendSource,
Attribute::Interpolate(interpolation, sampling),
],
} }
} }

View file

@ -7,7 +7,7 @@ use super::{
Error, ErrorKind, Frontend, Result, Error, ErrorKind, Frontend, Result,
}; };
use crate::{ use crate::{
BinaryOperator, Block, DerivativeAxis as Axis, DerivativeControl as Ctrl, Expression, Handle, BinaryOperator, DerivativeAxis as Axis, DerivativeControl as Ctrl, Expression, Handle,
ImageClass, ImageDimension as Dim, ImageQuery, MathFunction, Module, RelationalFunction, ImageClass, ImageDimension as Dim, ImageQuery, MathFunction, Module, RelationalFunction,
SampleLevel, ScalarKind as Sk, Span, Type, TypeInner, UnaryOperator, VectorSize, SampleLevel, ScalarKind as Sk, Span, Type, TypeInner, UnaryOperator, VectorSize,
}; };
@ -1280,14 +1280,14 @@ fn inject_common_builtin(
0b10 => Some(VectorSize::Tri), 0b10 => Some(VectorSize::Tri),
_ => Some(VectorSize::Quad), _ => Some(VectorSize::Quad),
}; };
let ty = || match size { let ty = |kind| match size {
Some(size) => TypeInner::Vector { Some(size) => TypeInner::Vector {
size, size,
kind: Sk::Float, kind,
width: float_width, width: float_width,
}, },
None => TypeInner::Scalar { None => TypeInner::Scalar {
kind: Sk::Float, kind,
width: float_width, width: float_width,
}, },
}; };
@ -1300,9 +1300,15 @@ fn inject_common_builtin(
_ => unreachable!(), _ => unreachable!(),
}; };
let second_kind = if fun == MacroCall::MathFunction(MathFunction::Ldexp) {
Sk::Sint
} else {
Sk::Float
};
declaration declaration
.overloads .overloads
.push(module.add_builtin(vec![ty(), ty()], fun)) .push(module.add_builtin(vec![ty(Sk::Float), ty(second_kind)], fun))
} }
} }
"transpose" => { "transpose" => {
@ -1678,7 +1684,6 @@ impl MacroCall {
&self, &self,
frontend: &mut Frontend, frontend: &mut Frontend,
ctx: &mut Context, ctx: &mut Context,
body: &mut Block,
args: &mut [Handle<Expression>], args: &mut [Handle<Expression>],
meta: Span, meta: Span,
) -> Result<Option<Handle<Expression>>> { ) -> Result<Option<Handle<Expression>>> {
@ -1688,14 +1693,8 @@ impl MacroCall {
args[0] args[0]
} }
MacroCall::SamplerShadow => { MacroCall::SamplerShadow => {
sampled_to_depth( sampled_to_depth(ctx, args[0], meta, &mut frontend.errors);
&mut frontend.module, ctx.invalidate_expression(args[0], meta)?;
ctx,
args[0],
meta,
&mut frontend.errors,
);
frontend.invalidate_expression(ctx, args[0], meta)?;
ctx.samplers.insert(args[0], args[1]); ctx.samplers.insert(args[0], args[1]);
args[0] args[0]
} }
@ -1708,7 +1707,7 @@ impl MacroCall {
let mut coords = args[1]; let mut coords = args[1];
if proj { if proj {
let size = match *frontend.resolve_type(ctx, coords, meta)? { let size = match *ctx.resolve_type(coords, meta)? {
TypeInner::Vector { size, .. } => size, TypeInner::Vector { size, .. } => size,
_ => unreachable!(), _ => unreachable!(),
}; };
@ -1718,8 +1717,7 @@ impl MacroCall {
index: size as u32 - 1, index: size as u32 - 1,
}, },
Span::default(), Span::default(),
body, )?;
);
let left = if let VectorSize::Bi = size { let left = if let VectorSize::Bi = size {
ctx.add_expression( ctx.add_expression(
Expression::AccessIndex { Expression::AccessIndex {
@ -1727,8 +1725,7 @@ impl MacroCall {
index: 0, index: 0,
}, },
Span::default(), Span::default(),
body, )?
)
} else { } else {
let size = match size { let size = match size {
VectorSize::Tri => VectorSize::Bi, VectorSize::Tri => VectorSize::Bi,
@ -1737,9 +1734,8 @@ impl MacroCall {
right = ctx.add_expression( right = ctx.add_expression(
Expression::Splat { size, value: right }, Expression::Splat { size, value: right },
Span::default(), Span::default(),
body, )?;
); ctx.vector_resize(size, coords, Span::default())?
ctx.vector_resize(size, coords, Span::default(), body)
}; };
coords = ctx.add_expression( coords = ctx.add_expression(
Expression::Binary { Expression::Binary {
@ -1748,13 +1744,11 @@ impl MacroCall {
right, right,
}, },
Span::default(), Span::default(),
body, )?;
);
} }
let extra = args.get(2).copied(); let extra = args.get(2).copied();
let comps = let comps = frontend.coordinate_components(ctx, args[0], coords, extra, meta)?;
frontend.coordinate_components(ctx, args[0], coords, extra, meta, body)?;
let mut num_args = 2; let mut num_args = 2;
@ -1801,7 +1795,7 @@ impl MacroCall {
true => { true => {
let offset_arg = args[num_args]; let offset_arg = args[num_args];
num_args += 1; num_args += 1;
match frontend.solve_constant(ctx, offset_arg, meta) { match ctx.solve_constant(offset_arg, meta) {
Ok(v) => Some(v), Ok(v) => Some(v),
Err(e) => { Err(e) => {
frontend.errors.push(e); frontend.errors.push(e);
@ -1820,7 +1814,7 @@ impl MacroCall {
.map_or(SampleLevel::Auto, SampleLevel::Bias); .map_or(SampleLevel::Auto, SampleLevel::Bias);
} }
texture_call(ctx, args[0], level, comps, texture_offset, body, meta)? texture_call(ctx, args[0], level, comps, texture_offset, meta)?
} }
MacroCall::TextureSize { arrayed } => { MacroCall::TextureSize { arrayed } => {
@ -1832,20 +1826,18 @@ impl MacroCall {
}, },
}, },
Span::default(), Span::default(),
body, )?;
);
if arrayed { if arrayed {
let mut components = Vec::with_capacity(4); let mut components = Vec::with_capacity(4);
let size = match *frontend.resolve_type(ctx, expr, meta)? { let size = match *ctx.resolve_type(expr, meta)? {
TypeInner::Vector { size: ori_size, .. } => { TypeInner::Vector { size: ori_size, .. } => {
for index in 0..(ori_size as u32) { for index in 0..(ori_size as u32) {
components.push(ctx.add_expression( components.push(ctx.add_expression(
Expression::AccessIndex { base: expr, index }, Expression::AccessIndex { base: expr, index },
Span::default(), Span::default(),
body, )?)
))
} }
match ori_size { match ori_size {
@ -1865,10 +1857,9 @@ impl MacroCall {
query: ImageQuery::NumLayers, query: ImageQuery::NumLayers,
}, },
Span::default(), Span::default(),
body, )?);
));
let ty = frontend.module.types.insert( let ty = ctx.module.types.insert(
Type { Type {
name: None, name: None,
inner: TypeInner::Vector { inner: TypeInner::Vector {
@ -1880,7 +1871,7 @@ impl MacroCall {
Span::default(), Span::default(),
); );
expr = ctx.add_expression(Expression::Compose { components, ty }, meta, body) expr = ctx.add_expression(Expression::Compose { components, ty }, meta)?
} }
ctx.add_expression( ctx.add_expression(
@ -1890,12 +1881,10 @@ impl MacroCall {
convert: Some(4), convert: Some(4),
}, },
Span::default(), Span::default(),
body, )?
)
} }
MacroCall::ImageLoad { multi } => { MacroCall::ImageLoad { multi } => {
let comps = let comps = frontend.coordinate_components(ctx, args[0], args[1], None, meta)?;
frontend.coordinate_components(ctx, args[0], args[1], None, meta, body)?;
let (sample, level) = match (multi, args.get(2)) { let (sample, level) = match (multi, args.get(2)) {
(_, None) => (None, None), (_, None) => (None, None),
(true, Some(&arg)) => (Some(arg), None), (true, Some(&arg)) => (Some(arg), None),
@ -1910,14 +1899,12 @@ impl MacroCall {
level, level,
}, },
Span::default(), Span::default(),
body, )?
)
} }
MacroCall::ImageStore => { MacroCall::ImageStore => {
let comps = let comps = frontend.coordinate_components(ctx, args[0], args[1], None, meta)?;
frontend.coordinate_components(ctx, args[0], args[1], None, meta, body)?; ctx.emit_restart();
ctx.emit_restart(body); ctx.body.push(
body.push(
crate::Statement::ImageStore { crate::Statement::ImageStore {
image: args[0], image: args[0],
coordinate: comps.coordinate, coordinate: comps.coordinate,
@ -1937,8 +1924,7 @@ impl MacroCall {
arg3: args.get(3).copied(), arg3: args.get(3).copied(),
}, },
Span::default(), Span::default(),
body, )?,
),
mc @ (MacroCall::FindLsbUint | MacroCall::FindMsbUint) => { mc @ (MacroCall::FindLsbUint | MacroCall::FindMsbUint) => {
let fun = match mc { let fun = match mc {
MacroCall::FindLsbUint => MathFunction::FindLsb, MacroCall::FindLsbUint => MathFunction::FindLsb,
@ -1954,8 +1940,7 @@ impl MacroCall {
arg3: None, arg3: None,
}, },
Span::default(), Span::default(),
body, )?;
);
ctx.add_expression( ctx.add_expression(
Expression::As { Expression::As {
expr: res, expr: res,
@ -1963,8 +1948,7 @@ impl MacroCall {
convert: Some(4), convert: Some(4),
}, },
Span::default(), Span::default(),
body, )?
)
} }
MacroCall::BitfieldInsert => { MacroCall::BitfieldInsert => {
let conv_arg_2 = ctx.add_expression( let conv_arg_2 = ctx.add_expression(
@ -1974,8 +1958,7 @@ impl MacroCall {
convert: Some(4), convert: Some(4),
}, },
Span::default(), Span::default(),
body, )?;
);
let conv_arg_3 = ctx.add_expression( let conv_arg_3 = ctx.add_expression(
Expression::As { Expression::As {
expr: args[3], expr: args[3],
@ -1983,8 +1966,7 @@ impl MacroCall {
convert: Some(4), convert: Some(4),
}, },
Span::default(), Span::default(),
body, )?;
);
ctx.add_expression( ctx.add_expression(
Expression::Math { Expression::Math {
fun: MathFunction::InsertBits, fun: MathFunction::InsertBits,
@ -1994,8 +1976,7 @@ impl MacroCall {
arg3: Some(conv_arg_3), arg3: Some(conv_arg_3),
}, },
Span::default(), Span::default(),
body, )?
)
} }
MacroCall::BitfieldExtract => { MacroCall::BitfieldExtract => {
let conv_arg_1 = ctx.add_expression( let conv_arg_1 = ctx.add_expression(
@ -2005,8 +1986,7 @@ impl MacroCall {
convert: Some(4), convert: Some(4),
}, },
Span::default(), Span::default(),
body, )?;
);
let conv_arg_2 = ctx.add_expression( let conv_arg_2 = ctx.add_expression(
Expression::As { Expression::As {
expr: args[2], expr: args[2],
@ -2014,8 +1994,7 @@ impl MacroCall {
convert: Some(4), convert: Some(4),
}, },
Span::default(), Span::default(),
body, )?;
);
ctx.add_expression( ctx.add_expression(
Expression::Math { Expression::Math {
fun: MathFunction::ExtractBits, fun: MathFunction::ExtractBits,
@ -2025,8 +2004,7 @@ impl MacroCall {
arg3: None, arg3: None,
}, },
Span::default(), Span::default(),
body, )?
)
} }
MacroCall::Relational(fun) => ctx.add_expression( MacroCall::Relational(fun) => ctx.add_expression(
Expression::Relational { Expression::Relational {
@ -2034,13 +2012,10 @@ impl MacroCall {
argument: args[0], argument: args[0],
}, },
Span::default(), Span::default(),
body, )?,
), MacroCall::Unary(op) => {
MacroCall::Unary(op) => ctx.add_expression( ctx.add_expression(Expression::Unary { op, expr: args[0] }, Span::default())?
Expression::Unary { op, expr: args[0] }, }
Span::default(),
body,
),
MacroCall::Binary(op) => ctx.add_expression( MacroCall::Binary(op) => ctx.add_expression(
Expression::Binary { Expression::Binary {
op, op,
@ -2048,10 +2023,9 @@ impl MacroCall {
right: args[1], right: args[1],
}, },
Span::default(), Span::default(),
body, )?,
),
MacroCall::Mod(size) => { MacroCall::Mod(size) => {
ctx.implicit_splat(frontend, &mut args[1], meta, size)?; ctx.implicit_splat(&mut args[1], meta, size)?;
// x - y * floor(x / y) // x - y * floor(x / y)
@ -2062,8 +2036,7 @@ impl MacroCall {
right: args[1], right: args[1],
}, },
Span::default(), Span::default(),
body, )?;
);
let floor = ctx.add_expression( let floor = ctx.add_expression(
Expression::Math { Expression::Math {
fun: MathFunction::Floor, fun: MathFunction::Floor,
@ -2073,8 +2046,7 @@ impl MacroCall {
arg3: None, arg3: None,
}, },
Span::default(), Span::default(),
body, )?;
);
let mult = ctx.add_expression( let mult = ctx.add_expression(
Expression::Binary { Expression::Binary {
op: BinaryOperator::Multiply, op: BinaryOperator::Multiply,
@ -2082,8 +2054,7 @@ impl MacroCall {
right: args[1], right: args[1],
}, },
Span::default(), Span::default(),
body, )?;
);
ctx.add_expression( ctx.add_expression(
Expression::Binary { Expression::Binary {
op: BinaryOperator::Subtract, op: BinaryOperator::Subtract,
@ -2091,11 +2062,10 @@ impl MacroCall {
right: mult, right: mult,
}, },
Span::default(), Span::default(),
body, )?
)
} }
MacroCall::Splatted(fun, size, i) => { MacroCall::Splatted(fun, size, i) => {
ctx.implicit_splat(frontend, &mut args[i], meta, size)?; ctx.implicit_splat(&mut args[i], meta, size)?;
ctx.add_expression( ctx.add_expression(
Expression::Math { Expression::Math {
@ -2106,8 +2076,7 @@ impl MacroCall {
arg3: args.get(3).copied(), arg3: args.get(3).copied(),
}, },
Span::default(), Span::default(),
body, )?
)
} }
MacroCall::MixBoolean => ctx.add_expression( MacroCall::MixBoolean => ctx.add_expression(
Expression::Select { Expression::Select {
@ -2116,11 +2085,10 @@ impl MacroCall {
reject: args[0], reject: args[0],
}, },
Span::default(), Span::default(),
body, )?,
),
MacroCall::Clamp(size) => { MacroCall::Clamp(size) => {
ctx.implicit_splat(frontend, &mut args[1], meta, size)?; ctx.implicit_splat(&mut args[1], meta, size)?;
ctx.implicit_splat(frontend, &mut args[2], meta, size)?; ctx.implicit_splat(&mut args[2], meta, size)?;
ctx.add_expression( ctx.add_expression(
Expression::Math { Expression::Math {
@ -2131,8 +2099,7 @@ impl MacroCall {
arg3: args.get(3).copied(), arg3: args.get(3).copied(),
}, },
Span::default(), Span::default(),
body, )?
)
} }
MacroCall::BitCast(kind) => ctx.add_expression( MacroCall::BitCast(kind) => ctx.add_expression(
Expression::As { Expression::As {
@ -2141,8 +2108,7 @@ impl MacroCall {
convert: None, convert: None,
}, },
Span::default(), Span::default(),
body, )?,
),
MacroCall::Derivate(axis, ctrl) => ctx.add_expression( MacroCall::Derivate(axis, ctrl) => ctx.add_expression(
Expression::Derivative { Expression::Derivative {
axis, axis,
@ -2150,16 +2116,16 @@ impl MacroCall {
expr: args[0], expr: args[0],
}, },
Span::default(), Span::default(),
body, )?,
),
MacroCall::Barrier => { MacroCall::Barrier => {
ctx.emit_restart(body); ctx.emit_restart();
body.push(crate::Statement::Barrier(crate::Barrier::all()), meta); ctx.body
.push(crate::Statement::Barrier(crate::Barrier::all()), meta);
return Ok(None); return Ok(None);
} }
MacroCall::SmoothStep { splatted } => { MacroCall::SmoothStep { splatted } => {
ctx.implicit_splat(frontend, &mut args[0], meta, splatted)?; ctx.implicit_splat(&mut args[0], meta, splatted)?;
ctx.implicit_splat(frontend, &mut args[1], meta, splatted)?; ctx.implicit_splat(&mut args[1], meta, splatted)?;
ctx.add_expression( ctx.add_expression(
Expression::Math { Expression::Math {
@ -2170,8 +2136,7 @@ impl MacroCall {
arg3: None, arg3: None,
}, },
Span::default(), Span::default(),
body, )?
)
} }
})) }))
} }
@ -2183,7 +2148,6 @@ fn texture_call(
level: SampleLevel, level: SampleLevel,
comps: CoordComponents, comps: CoordComponents,
offset: Option<Handle<Expression>>, offset: Option<Handle<Expression>>,
body: &mut Block,
meta: Span, meta: Span,
) -> Result<Handle<Expression>> { ) -> Result<Handle<Expression>> {
if let Some(sampler) = ctx.samplers.get(&image).copied() { if let Some(sampler) = ctx.samplers.get(&image).copied() {
@ -2205,8 +2169,7 @@ fn texture_call(
depth_ref: comps.depth_ref, depth_ref: comps.depth_ref,
}, },
meta, meta,
body, )?)
))
} else { } else {
Err(Error { Err(Error {
kind: ErrorKind::SemanticError("Bad call".into()), kind: ErrorKind::SemanticError("Bad call".into()),
@ -2235,13 +2198,12 @@ impl Frontend {
coord: Handle<Expression>, coord: Handle<Expression>,
extra: Option<Handle<Expression>>, extra: Option<Handle<Expression>>,
meta: Span, meta: Span,
body: &mut Block,
) -> Result<CoordComponents> { ) -> Result<CoordComponents> {
if let TypeInner::Image { if let TypeInner::Image {
dim, dim,
arrayed, arrayed,
class, class,
} = *self.resolve_type(ctx, image, meta)? } = *ctx.resolve_type(image, meta)?
{ {
let image_size = match dim { let image_size = match dim {
Dim::D1 => None, Dim::D1 => None,
@ -2249,7 +2211,7 @@ impl Frontend {
Dim::D3 => Some(VectorSize::Tri), Dim::D3 => Some(VectorSize::Tri),
Dim::Cube => Some(VectorSize::Tri), Dim::Cube => Some(VectorSize::Tri),
}; };
let coord_size = match *self.resolve_type(ctx, coord, meta)? { let coord_size = match *ctx.resolve_type(coord, meta)? {
TypeInner::Vector { size, .. } => Some(size), TypeInner::Vector { size, .. } => Some(size),
_ => None, _ => None,
}; };
@ -2261,7 +2223,7 @@ impl Frontend {
let coordinate = match (image_size, coord_size) { let coordinate = match (image_size, coord_size) {
(Some(size), Some(coord_s)) if size != coord_s => { (Some(size), Some(coord_s)) if size != coord_s => {
ctx.vector_resize(size, coord, Span::default(), body) ctx.vector_resize(size, coord, Span::default())?
} }
(None, Some(_)) => ctx.add_expression( (None, Some(_)) => ctx.add_expression(
Expression::AccessIndex { Expression::AccessIndex {
@ -2269,8 +2231,7 @@ impl Frontend {
index: 0, index: 0,
}, },
Span::default(), Span::default(),
body, )?,
),
_ => coord, _ => coord,
}; };
@ -2283,8 +2244,7 @@ impl Frontend {
Some(ctx.add_expression( Some(ctx.add_expression(
Expression::AccessIndex { base: coord, index }, Expression::AccessIndex { base: coord, index },
Span::default(), Span::default(),
body, )?)
))
} else { } else {
None None
}; };
@ -2300,8 +2260,7 @@ impl Frontend {
Some(ctx.add_expression( Some(ctx.add_expression(
Expression::AccessIndex { base: coord, index }, Expression::AccessIndex { base: coord, index },
Span::default(), Span::default(),
body, )?)
))
} }
} }
false => None, false => None,
@ -2332,7 +2291,6 @@ impl Frontend {
/// Helper function to cast a expression holding a sampled image to a /// Helper function to cast a expression holding a sampled image to a
/// depth image. /// depth image.
pub fn sampled_to_depth( pub fn sampled_to_depth(
module: &mut Module,
ctx: &mut Context, ctx: &mut Context,
image: Handle<Expression>, image: Handle<Expression>,
meta: Span, meta: Span,
@ -2340,7 +2298,7 @@ pub fn sampled_to_depth(
) { ) {
// Get the a mutable type handle of the underlying image storage // Get the a mutable type handle of the underlying image storage
let ty = match ctx[image] { let ty = match ctx[image] {
Expression::GlobalVariable(handle) => &mut module.global_variables.get_mut(handle).ty, Expression::GlobalVariable(handle) => &mut ctx.module.global_variables.get_mut(handle).ty,
Expression::FunctionArgument(i) => { Expression::FunctionArgument(i) => {
// Mark the function argument as carrying a depth texture // Mark the function argument as carrying a depth texture
ctx.parameters_info[i as usize].depth = true; ctx.parameters_info[i as usize].depth = true;
@ -2356,7 +2314,7 @@ pub fn sampled_to_depth(
} }
}; };
match module.types[*ty].inner { match ctx.module.types[*ty].inner {
// Update the image class to depth in case it already isn't // Update the image class to depth in case it already isn't
TypeInner::Image { TypeInner::Image {
class, class,
@ -2364,7 +2322,7 @@ pub fn sampled_to_depth(
arrayed, arrayed,
} => match class { } => match class {
ImageClass::Sampled { multi, .. } => { ImageClass::Sampled { multi, .. } => {
*ty = module.types.insert( *ty = ctx.module.types.insert(
Type { Type {
name: None, name: None,
inner: TypeInner::Image { inner: TypeInner::Image {

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -175,8 +175,6 @@ pub struct Frontend {
layouter: Layouter, layouter: Layouter,
errors: Vec<Error>, errors: Vec<Error>,
module: Module,
} }
impl Frontend { impl Frontend {
@ -188,10 +186,6 @@ impl Frontend {
self.global_variables.clear(); self.global_variables.clear();
self.entry_args.clear(); self.entry_args.clear();
self.layouter.clear(); self.layouter.clear();
// This is necessary because if the last parsing errored out, the module
// wouldn't have been taken
self.module = Module::default();
} }
/// Parses a shader either outputting a shader [`Module`](Module) or a list /// Parses a shader either outputting a shader [`Module`](Module) or a list
@ -208,14 +202,18 @@ impl Frontend {
let lexer = lex::Lexer::new(source, &options.defines); let lexer = lex::Lexer::new(source, &options.defines);
let mut ctx = ParsingContext::new(lexer); let mut ctx = ParsingContext::new(lexer);
if let Err(e) = ctx.parse(self) { match ctx.parse(self) {
self.errors.push(e); Ok(module) => {
} if self.errors.is_empty() {
Ok(module)
if self.errors.is_empty() { } else {
Ok(std::mem::take(&mut self.module)) Err(std::mem::take(&mut self.errors))
} else { }
Err(std::mem::take(&mut self.errors)) }
Err(e) => {
self.errors.push(e);
Err(std::mem::take(&mut self.errors))
}
} }
} }

View file

@ -9,7 +9,7 @@ use super::{
variables::{GlobalOrConstant, VarDeclaration}, variables::{GlobalOrConstant, VarDeclaration},
Frontend, Result, Frontend, Result,
}; };
use crate::{arena::Handle, proc::U32EvalError, Block, Expression, Span, Type}; use crate::{arena::Handle, proc::U32EvalError, Expression, Module, Span, Type};
use pp_rs::token::{PreprocessorError, Token as PPToken, TokenValue as PPTokenValue}; use pp_rs::token::{PreprocessorError, Token as PPToken, TokenValue as PPTokenValue};
use std::iter::Peekable; use std::iter::Peekable;
@ -162,13 +162,14 @@ impl<'source> ParsingContext<'source> {
}) })
} }
pub fn parse(&mut self, frontend: &mut Frontend) -> Result<()> { pub fn parse(&mut self, frontend: &mut Frontend) -> Result<Module> {
let mut module = Module::default();
// Body and expression arena for global initialization // Body and expression arena for global initialization
let mut body = Block::new(); let mut ctx = Context::new(frontend, &mut module)?;
let mut ctx = Context::new(frontend, &mut body);
while self.peek(frontend).is_some() { while self.peek(frontend).is_some() {
self.parse_external_declaration(frontend, &mut ctx, &mut body)?; self.parse_external_declaration(frontend, &mut ctx)?;
} }
// Add an `EntryPoint` to `parser.module` for `main`, if a // Add an `EntryPoint` to `parser.module` for `main`, if a
@ -177,8 +178,8 @@ impl<'source> ParsingContext<'source> {
for decl in declaration.overloads.iter() { for decl in declaration.overloads.iter() {
if let FunctionKind::Call(handle) = decl.kind { if let FunctionKind::Call(handle) = decl.kind {
if decl.defined && decl.parameters.is_empty() { if decl.defined && decl.parameters.is_empty() {
frontend.add_entry_point(handle, body, ctx.expressions); frontend.add_entry_point(handle, ctx)?;
return Ok(()); return Ok(module);
} }
} }
} }
@ -190,10 +191,14 @@ impl<'source> ParsingContext<'source> {
}) })
} }
fn parse_uint_constant(&mut self, frontend: &mut Frontend) -> Result<(u32, Span)> { fn parse_uint_constant(
let (const_expr, meta) = self.parse_constant_expression(frontend)?; &mut self,
frontend: &mut Frontend,
ctx: &mut Context,
) -> Result<(u32, Span)> {
let (const_expr, meta) = self.parse_constant_expression(frontend, ctx.module)?;
let res = frontend.module.to_ctx().eval_expr_to_u32(const_expr); let res = ctx.module.to_ctx().eval_expr_to_u32(const_expr);
let int = match res { let int = match res {
Ok(value) => Ok(value), Ok(value) => Ok(value),
@ -213,16 +218,15 @@ impl<'source> ParsingContext<'source> {
fn parse_constant_expression( fn parse_constant_expression(
&mut self, &mut self,
frontend: &mut Frontend, frontend: &mut Frontend,
module: &mut Module,
) -> Result<(Handle<Expression>, Span)> { ) -> Result<(Handle<Expression>, Span)> {
let mut block = Block::new(); let mut ctx = Context::new(frontend, module)?;
let mut ctx = Context::new(frontend, &mut block);
let mut stmt_ctx = ctx.stmt_ctx(); let mut stmt_ctx = ctx.stmt_ctx();
let expr = self.parse_conditional(frontend, &mut ctx, &mut stmt_ctx, &mut block, None)?; let expr = self.parse_conditional(frontend, &mut ctx, &mut stmt_ctx, None)?;
let (root, meta) = ctx.lower_expect(stmt_ctx, frontend, expr, ExprPos::Rhs, &mut block)?; let (root, meta) = ctx.lower_expect(stmt_ctx, frontend, expr, ExprPos::Rhs)?;
Ok((frontend.solve_constant(&ctx, root, meta)?, meta)) Ok((ctx.solve_constant(root, meta)?, meta))
} }
} }
@ -387,16 +391,14 @@ impl Frontend {
} }
} }
pub struct DeclarationContext<'ctx, 'qualifiers> { pub struct DeclarationContext<'ctx, 'qualifiers, 'a> {
qualifiers: TypeQualifiers<'qualifiers>, qualifiers: TypeQualifiers<'qualifiers>,
/// Indicates a global declaration /// Indicates a global declaration
external: bool, external: bool,
ctx: &'ctx mut Context<'a>,
ctx: &'ctx mut Context,
body: &'ctx mut Block,
} }
impl<'ctx, 'qualifiers> DeclarationContext<'ctx, 'qualifiers> { impl<'ctx, 'qualifiers, 'a> DeclarationContext<'ctx, 'qualifiers, 'a> {
fn add_var( fn add_var(
&mut self, &mut self,
frontend: &mut Frontend, frontend: &mut Frontend,
@ -415,24 +417,14 @@ impl<'ctx, 'qualifiers> DeclarationContext<'ctx, 'qualifiers> {
match self.external { match self.external {
true => { true => {
let global = frontend.add_global_var(self.ctx, self.body, decl)?; let global = frontend.add_global_var(self.ctx, decl)?;
let expr = match global { let expr = match global {
GlobalOrConstant::Global(handle) => Expression::GlobalVariable(handle), GlobalOrConstant::Global(handle) => Expression::GlobalVariable(handle),
GlobalOrConstant::Constant(handle) => Expression::Constant(handle), GlobalOrConstant::Constant(handle) => Expression::Constant(handle),
}; };
Ok(self.ctx.add_expression(expr, meta, self.body)) Ok(self.ctx.add_expression(expr, meta)?)
} }
false => frontend.add_local_var(self.ctx, self.body, decl), false => frontend.add_local_var(self.ctx, decl),
} }
} }
/// Emits all the expressions captured by the emitter and starts the emitter again
///
/// Alias to [`emit_restart`] with the declaration body
///
/// [`emit_restart`]: Context::emit_restart
#[inline]
fn flush_expressions(&mut self) {
self.ctx.emit_restart(self.body);
}
} }

View file

@ -13,8 +13,8 @@ use crate::{
Error, ErrorKind, Frontend, Span, Error, ErrorKind, Frontend, Span,
}, },
proc::Alignment, proc::Alignment,
AddressSpace, Block, Expression, FunctionResult, Handle, ScalarKind, Statement, StructMember, AddressSpace, Expression, FunctionResult, Handle, ScalarKind, Statement, StructMember, Type,
Type, TypeInner, TypeInner,
}; };
use super::{DeclarationContext, ParsingContext, Result}; use super::{DeclarationContext, ParsingContext, Result};
@ -73,10 +73,9 @@ impl<'source> ParsingContext<'source> {
&mut self, &mut self,
frontend: &mut Frontend, frontend: &mut Frontend,
global_ctx: &mut Context, global_ctx: &mut Context,
global_body: &mut Block,
) -> Result<()> { ) -> Result<()> {
if self if self
.parse_declaration(frontend, global_ctx, global_body, true)? .parse_declaration(frontend, global_ctx, true)?
.is_none() .is_none()
{ {
let token = self.bump(frontend)?; let token = self.bump(frontend)?;
@ -103,7 +102,6 @@ impl<'source> ParsingContext<'source> {
frontend: &mut Frontend, frontend: &mut Frontend,
ty: Handle<Type>, ty: Handle<Type>,
ctx: &mut Context, ctx: &mut Context,
body: &mut Block,
) -> Result<(Handle<Expression>, Span)> { ) -> Result<(Handle<Expression>, Span)> {
// initializer: // initializer:
// assignment_expression // assignment_expression
@ -118,10 +116,9 @@ impl<'source> ParsingContext<'source> {
let mut components = Vec::new(); let mut components = Vec::new();
loop { loop {
// The type expected to be parsed inside the initializer list // The type expected to be parsed inside the initializer list
let new_ty = let new_ty = element_or_member_type(ty, components.len(), &mut ctx.module.types);
element_or_member_type(ty, components.len(), &mut frontend.module.types);
components.push(self.parse_initializer(frontend, new_ty, ctx, body)?.0); components.push(self.parse_initializer(frontend, new_ty, ctx)?.0);
let token = self.bump(frontend)?; let token = self.bump(frontend)?;
match token.value { match token.value {
@ -150,18 +147,17 @@ impl<'source> ParsingContext<'source> {
} }
Ok(( Ok((
ctx.add_expression(Expression::Compose { ty, components }, meta, body), ctx.add_expression(Expression::Compose { ty, components }, meta)?,
meta, meta,
)) ))
} else { } else {
let mut stmt = ctx.stmt_ctx(); let mut stmt = ctx.stmt_ctx();
let expr = self.parse_assignment(frontend, ctx, &mut stmt, body)?; let expr = self.parse_assignment(frontend, ctx, &mut stmt)?;
let (mut init, init_meta) = let (mut init, init_meta) = ctx.lower_expect(stmt, frontend, expr, ExprPos::Rhs)?;
ctx.lower_expect(stmt, frontend, expr, ExprPos::Rhs, body)?;
let scalar_components = scalar_components(&frontend.module.types[ty].inner); let scalar_components = scalar_components(&ctx.module.types[ty].inner);
if let Some((kind, width)) = scalar_components { if let Some((kind, width)) = scalar_components {
ctx.implicit_conversion(frontend, &mut init, init_meta, kind, width)?; ctx.implicit_conversion(&mut init, init_meta, kind, width)?;
} }
Ok((init, init_meta)) Ok((init, init_meta))
@ -223,18 +219,17 @@ impl<'source> ParsingContext<'source> {
// parse an array specifier if it exists // parse an array specifier if it exists
// NOTE: unlike other parse methods this one doesn't expect an array specifier and // NOTE: unlike other parse methods this one doesn't expect an array specifier and
// returns Ok(None) rather than an error if there is not one // returns Ok(None) rather than an error if there is not one
self.parse_array_specifier(frontend, &mut meta, &mut ty)?; self.parse_array_specifier(frontend, ctx.ctx, &mut meta, &mut ty)?;
let init = self let init = self
.bump_if(frontend, TokenValue::Assign) .bump_if(frontend, TokenValue::Assign)
.map::<Result<_>, _>(|_| { .map::<Result<_>, _>(|_| {
let (mut expr, init_meta) = let (mut expr, init_meta) = self.parse_initializer(frontend, ty, ctx.ctx)?;
self.parse_initializer(frontend, ty, ctx.ctx, ctx.body)?;
let scalar_components = scalar_components(&frontend.module.types[ty].inner); let scalar_components = scalar_components(&ctx.ctx.module.types[ty].inner);
if let Some((kind, width)) = scalar_components { if let Some((kind, width)) = scalar_components {
ctx.ctx ctx.ctx
.implicit_conversion(frontend, &mut expr, init_meta, kind, width)?; .implicit_conversion(&mut expr, init_meta, kind, width)?;
} }
meta.subsume(init_meta); meta.subsume(init_meta);
@ -246,7 +241,7 @@ impl<'source> ParsingContext<'source> {
let is_const = ctx.qualifiers.storage.0 == StorageQualifier::Const; let is_const = ctx.qualifiers.storage.0 == StorageQualifier::Const;
let maybe_const_expr = if ctx.external { let maybe_const_expr = if ctx.external {
if let Some((root, meta)) = init { if let Some((root, meta)) = init {
match frontend.solve_constant(ctx.ctx, root, meta) { match ctx.ctx.solve_constant(root, meta) {
Ok(res) => Some(res), Ok(res) => Some(res),
// If the declaration is external (global scope) and is constant qualified // If the declaration is external (global scope) and is constant qualified
// then the initializer must be a constant expression // then the initializer must be a constant expression
@ -263,8 +258,8 @@ impl<'source> ParsingContext<'source> {
let pointer = ctx.add_var(frontend, ty, name, maybe_const_expr, meta)?; let pointer = ctx.add_var(frontend, ty, name, maybe_const_expr, meta)?;
if let Some((value, _)) = init.filter(|_| maybe_const_expr.is_none()) { if let Some((value, _)) = init.filter(|_| maybe_const_expr.is_none()) {
ctx.flush_expressions(); ctx.ctx.emit_restart();
ctx.body.push(Statement::Store { pointer, value }, meta); ctx.ctx.body.push(Statement::Store { pointer, value }, meta);
} }
let token = self.bump(frontend)?; let token = self.bump(frontend)?;
@ -291,7 +286,6 @@ impl<'source> ParsingContext<'source> {
&mut self, &mut self,
frontend: &mut Frontend, frontend: &mut Frontend,
ctx: &mut Context, ctx: &mut Context,
body: &mut Block,
external: bool, external: bool,
) -> Result<Option<Span>> { ) -> Result<Option<Span>> {
//declaration: //declaration:
@ -307,12 +301,12 @@ impl<'source> ParsingContext<'source> {
// type_qualifier IDENTIFIER identifier_list SEMICOLON // type_qualifier IDENTIFIER identifier_list SEMICOLON
if self.peek_type_qualifier(frontend) || self.peek_type_name(frontend) { if self.peek_type_qualifier(frontend) || self.peek_type_name(frontend) {
let mut qualifiers = self.parse_type_qualifiers(frontend)?; let mut qualifiers = self.parse_type_qualifiers(frontend, ctx)?;
if self.peek_type_name(frontend) { if self.peek_type_name(frontend) {
// This branch handles variables and function prototypes and if // This branch handles variables and function prototypes and if
// external is true also function definitions // external is true also function definitions
let (ty, mut meta) = self.parse_type(frontend)?; let (ty, mut meta) = self.parse_type(frontend, ctx)?;
let token = self.bump(frontend)?; let token = self.bump(frontend)?;
let token_fallthrough = match token.value { let token_fallthrough = match token.value {
@ -322,11 +316,10 @@ impl<'source> ParsingContext<'source> {
self.bump(frontend)?; self.bump(frontend)?;
let result = ty.map(|ty| FunctionResult { ty, binding: None }); let result = ty.map(|ty| FunctionResult { ty, binding: None });
let mut body = Block::new();
let mut context = Context::new(frontend, &mut body); let mut context = Context::new(frontend, ctx.module)?;
self.parse_function_args(frontend, &mut context, &mut body)?; self.parse_function_args(frontend, &mut context)?;
let end_meta = self.expect(frontend, TokenValue::RightParen)?.meta; let end_meta = self.expect(frontend, TokenValue::RightParen)?.meta;
meta.subsume(end_meta); meta.subsume(end_meta);
@ -349,11 +342,10 @@ impl<'source> ParsingContext<'source> {
token.meta, token.meta,
frontend, frontend,
&mut context, &mut context,
&mut body,
&mut None, &mut None,
)?; )?;
frontend.add_function(context, name, result, body, meta); frontend.add_function(context, name, result, meta);
Ok(Some(meta)) Ok(Some(meta))
} }
@ -394,7 +386,6 @@ impl<'source> ParsingContext<'source> {
qualifiers, qualifiers,
external, external,
ctx, ctx,
body,
}; };
self.backtrack(token_fallthrough)?; self.backtrack(token_fallthrough)?;
@ -419,7 +410,6 @@ impl<'source> ParsingContext<'source> {
self.parse_block_declaration( self.parse_block_declaration(
frontend, frontend,
ctx, ctx,
body,
&mut qualifiers, &mut qualifiers,
ty_name, ty_name,
token.meta, token.meta,
@ -427,7 +417,7 @@ impl<'source> ParsingContext<'source> {
.map(Some) .map(Some)
} else { } else {
if qualifiers.invariant.take().is_some() { if qualifiers.invariant.take().is_some() {
frontend.make_variable_invariant(ctx, body, &ty_name, token.meta); frontend.make_variable_invariant(ctx, &ty_name, token.meta)?;
qualifiers.unused_errors(&mut frontend.errors); qualifiers.unused_errors(&mut frontend.errors);
self.expect(frontend, TokenValue::Semicolon)?; self.expect(frontend, TokenValue::Semicolon)?;
@ -500,9 +490,9 @@ impl<'source> ParsingContext<'source> {
} }
}; };
let (ty, meta) = self.parse_type_non_void(frontend)?; let (ty, meta) = self.parse_type_non_void(frontend, ctx)?;
match frontend.module.types[ty].inner { match ctx.module.types[ty].inner {
TypeInner::Scalar { TypeInner::Scalar {
kind: ScalarKind::Float | ScalarKind::Sint, kind: ScalarKind::Float | ScalarKind::Sint,
.. ..
@ -528,7 +518,6 @@ impl<'source> ParsingContext<'source> {
&mut self, &mut self,
frontend: &mut Frontend, frontend: &mut Frontend,
ctx: &mut Context, ctx: &mut Context,
body: &mut Block,
qualifiers: &mut TypeQualifiers, qualifiers: &mut TypeQualifiers,
ty_name: String, ty_name: String,
mut meta: Span, mut meta: Span,
@ -548,10 +537,10 @@ impl<'source> ParsingContext<'source> {
}; };
let mut members = Vec::new(); let mut members = Vec::new();
let span = self.parse_struct_declaration_list(frontend, &mut members, layout)?; let span = self.parse_struct_declaration_list(frontend, ctx, &mut members, layout)?;
self.expect(frontend, TokenValue::RightBrace)?; self.expect(frontend, TokenValue::RightBrace)?;
let mut ty = frontend.module.types.insert( let mut ty = ctx.module.types.insert(
Type { Type {
name: Some(ty_name), name: Some(ty_name),
inner: TypeInner::Struct { inner: TypeInner::Struct {
@ -566,7 +555,7 @@ impl<'source> ParsingContext<'source> {
let name = match token.value { let name = match token.value {
TokenValue::Semicolon => None, TokenValue::Semicolon => None,
TokenValue::Identifier(name) => { TokenValue::Identifier(name) => {
self.parse_array_specifier(frontend, &mut meta, &mut ty)?; self.parse_array_specifier(frontend, ctx, &mut meta, &mut ty)?;
self.expect(frontend, TokenValue::Semicolon)?; self.expect(frontend, TokenValue::Semicolon)?;
@ -585,7 +574,6 @@ impl<'source> ParsingContext<'source> {
let global = frontend.add_global_var( let global = frontend.add_global_var(
ctx, ctx,
body,
VarDeclaration { VarDeclaration {
qualifiers, qualifiers,
ty, ty,
@ -607,7 +595,7 @@ impl<'source> ParsingContext<'source> {
entry_arg: None, entry_arg: None,
mutable: true, mutable: true,
}; };
ctx.add_global(frontend, &k, lookup, body); ctx.add_global(&k, lookup)?;
frontend.global_variables.push((k, lookup)); frontend.global_variables.push((k, lookup));
} }
@ -619,6 +607,7 @@ impl<'source> ParsingContext<'source> {
pub fn parse_struct_declaration_list( pub fn parse_struct_declaration_list(
&mut self, &mut self,
frontend: &mut Frontend, frontend: &mut Frontend,
ctx: &mut Context,
members: &mut Vec<StructMember>, members: &mut Vec<StructMember>,
layout: StructLayout, layout: StructLayout,
) -> Result<u32> { ) -> Result<u32> {
@ -628,12 +617,12 @@ impl<'source> ParsingContext<'source> {
loop { loop {
// TODO: type_qualifier // TODO: type_qualifier
let (base_ty, mut meta) = self.parse_type_non_void(frontend)?; let (base_ty, mut meta) = self.parse_type_non_void(frontend, ctx)?;
loop { loop {
let (name, name_meta) = self.expect_ident(frontend)?; let (name, name_meta) = self.expect_ident(frontend)?;
let mut ty = base_ty; let mut ty = base_ty;
self.parse_array_specifier(frontend, &mut meta, &mut ty)?; self.parse_array_specifier(frontend, ctx, &mut meta, &mut ty)?;
meta.subsume(name_meta); meta.subsume(name_meta);
@ -641,7 +630,7 @@ impl<'source> ParsingContext<'source> {
ty, ty,
meta, meta,
layout, layout,
&mut frontend.module.types, &mut ctx.module.types,
&mut frontend.errors, &mut frontend.errors,
); );

View file

@ -9,7 +9,7 @@ use crate::{
token::{Token, TokenValue}, token::{Token, TokenValue},
Error, Frontend, Result, Span, Error, Frontend, Result, Span,
}, },
ArraySize, BinaryOperator, Block, Handle, Literal, Type, TypeInner, UnaryOperator, ArraySize, BinaryOperator, Handle, Literal, Type, TypeInner, UnaryOperator,
}; };
impl<'source> ParsingContext<'source> { impl<'source> ParsingContext<'source> {
@ -18,7 +18,6 @@ impl<'source> ParsingContext<'source> {
frontend: &mut Frontend, frontend: &mut Frontend,
ctx: &mut Context, ctx: &mut Context,
stmt: &mut StmtContext, stmt: &mut StmtContext,
body: &mut Block,
) -> Result<Handle<HirExpr>> { ) -> Result<Handle<HirExpr>> {
let mut token = self.bump(frontend)?; let mut token = self.bump(frontend)?;
@ -47,7 +46,7 @@ impl<'source> ParsingContext<'source> {
} }
TokenValue::BoolConstant(value) => Literal::Bool(value), TokenValue::BoolConstant(value) => Literal::Bool(value),
TokenValue::LeftParen => { TokenValue::LeftParen => {
let expr = self.parse_expression(frontend, ctx, stmt, body)?; let expr = self.parse_expression(frontend, ctx, stmt)?;
let meta = self.expect(frontend, TokenValue::RightParen)?.meta; let meta = self.expect(frontend, TokenValue::RightParen)?.meta;
token.meta.subsume(meta); token.meta.subsume(meta);
@ -84,7 +83,6 @@ impl<'source> ParsingContext<'source> {
frontend: &mut Frontend, frontend: &mut Frontend,
ctx: &mut Context, ctx: &mut Context,
stmt: &mut StmtContext, stmt: &mut StmtContext,
body: &mut Block,
meta: &mut Span, meta: &mut Span,
) -> Result<Vec<Handle<HirExpr>>> { ) -> Result<Vec<Handle<HirExpr>>> {
let mut args = Vec::new(); let mut args = Vec::new();
@ -92,7 +90,7 @@ impl<'source> ParsingContext<'source> {
meta.subsume(token.meta); meta.subsume(token.meta);
} else { } else {
loop { loop {
args.push(self.parse_assignment(frontend, ctx, stmt, body)?); args.push(self.parse_assignment(frontend, ctx, stmt)?);
let token = self.bump(frontend)?; let token = self.bump(frontend)?;
match token.value { match token.value {
@ -122,21 +120,20 @@ impl<'source> ParsingContext<'source> {
frontend: &mut Frontend, frontend: &mut Frontend,
ctx: &mut Context, ctx: &mut Context,
stmt: &mut StmtContext, stmt: &mut StmtContext,
body: &mut Block,
) -> Result<Handle<HirExpr>> { ) -> Result<Handle<HirExpr>> {
let mut base = if self.peek_type_name(frontend) { let mut base = if self.peek_type_name(frontend) {
let (mut handle, mut meta) = self.parse_type_non_void(frontend)?; let (mut handle, mut meta) = self.parse_type_non_void(frontend, ctx)?;
self.expect(frontend, TokenValue::LeftParen)?; self.expect(frontend, TokenValue::LeftParen)?;
let args = self.parse_function_call_args(frontend, ctx, stmt, body, &mut meta)?; let args = self.parse_function_call_args(frontend, ctx, stmt, &mut meta)?;
if let TypeInner::Array { if let TypeInner::Array {
size: ArraySize::Dynamic, size: ArraySize::Dynamic,
stride, stride,
base, base,
} = frontend.module.types[handle].inner } = ctx.module.types[handle].inner
{ {
let span = frontend.module.types.get_span(handle); let span = ctx.module.types.get_span(handle);
let size = u32::try_from(args.len()) let size = u32::try_from(args.len())
.ok() .ok()
@ -148,7 +145,7 @@ impl<'source> ParsingContext<'source> {
meta, meta,
})?; })?;
handle = frontend.module.types.insert( handle = ctx.module.types.insert(
Type { Type {
name: None, name: None,
inner: TypeInner::Array { inner: TypeInner::Array {
@ -175,7 +172,7 @@ impl<'source> ParsingContext<'source> {
let (name, mut meta) = self.expect_ident(frontend)?; let (name, mut meta) = self.expect_ident(frontend)?;
let expr = if self.bump_if(frontend, TokenValue::LeftParen).is_some() { let expr = if self.bump_if(frontend, TokenValue::LeftParen).is_some() {
let args = self.parse_function_call_args(frontend, ctx, stmt, body, &mut meta)?; let args = self.parse_function_call_args(frontend, ctx, stmt, &mut meta)?;
let kind = match frontend.lookup_type.get(&name) { let kind = match frontend.lookup_type.get(&name) {
Some(ty) => FunctionCallKind::TypeConstructor(*ty), Some(ty) => FunctionCallKind::TypeConstructor(*ty),
@ -187,7 +184,7 @@ impl<'source> ParsingContext<'source> {
meta, meta,
} }
} else { } else {
let var = match frontend.lookup_variable(ctx, body, &name, meta) { let var = match frontend.lookup_variable(ctx, &name, meta)? {
Some(var) => var, Some(var) => var,
None => { None => {
return Err(Error { return Err(Error {
@ -205,7 +202,7 @@ impl<'source> ParsingContext<'source> {
stmt.hir_exprs.append(expr, Default::default()) stmt.hir_exprs.append(expr, Default::default())
} else { } else {
self.parse_primary(frontend, ctx, stmt, body)? self.parse_primary(frontend, ctx, stmt)?
}; };
while let TokenValue::LeftBracket while let TokenValue::LeftBracket
@ -217,7 +214,7 @@ impl<'source> ParsingContext<'source> {
match value { match value {
TokenValue::LeftBracket => { TokenValue::LeftBracket => {
let index = self.parse_expression(frontend, ctx, stmt, body)?; let index = self.parse_expression(frontend, ctx, stmt)?;
let end_meta = self.expect(frontend, TokenValue::RightBracket)?.meta; let end_meta = self.expect(frontend, TokenValue::RightBracket)?.meta;
meta.subsume(end_meta); meta.subsume(end_meta);
@ -233,8 +230,7 @@ impl<'source> ParsingContext<'source> {
let (field, end_meta) = self.expect_ident(frontend)?; let (field, end_meta) = self.expect_ident(frontend)?;
if self.bump_if(frontend, TokenValue::LeftParen).is_some() { if self.bump_if(frontend, TokenValue::LeftParen).is_some() {
let args = let args = self.parse_function_call_args(frontend, ctx, stmt, &mut meta)?;
self.parse_function_call_args(frontend, ctx, stmt, body, &mut meta)?;
base = stmt.hir_exprs.append( base = stmt.hir_exprs.append(
HirExpr { HirExpr {
@ -287,13 +283,12 @@ impl<'source> ParsingContext<'source> {
frontend: &mut Frontend, frontend: &mut Frontend,
ctx: &mut Context, ctx: &mut Context,
stmt: &mut StmtContext, stmt: &mut StmtContext,
body: &mut Block,
) -> Result<Handle<HirExpr>> { ) -> Result<Handle<HirExpr>> {
Ok(match self.expect_peek(frontend)?.value { Ok(match self.expect_peek(frontend)?.value {
TokenValue::Plus | TokenValue::Dash | TokenValue::Bang | TokenValue::Tilde => { TokenValue::Plus | TokenValue::Dash | TokenValue::Bang | TokenValue::Tilde => {
let Token { value, mut meta } = self.bump(frontend)?; let Token { value, mut meta } = self.bump(frontend)?;
let expr = self.parse_unary(frontend, ctx, stmt, body)?; let expr = self.parse_unary(frontend, ctx, stmt)?;
let end_meta = stmt.hir_exprs[expr].meta; let end_meta = stmt.hir_exprs[expr].meta;
let kind = match value { let kind = match value {
@ -315,7 +310,7 @@ impl<'source> ParsingContext<'source> {
TokenValue::Increment | TokenValue::Decrement => { TokenValue::Increment | TokenValue::Decrement => {
let Token { value, meta } = self.bump(frontend)?; let Token { value, meta } = self.bump(frontend)?;
let expr = self.parse_unary(frontend, ctx, stmt, body)?; let expr = self.parse_unary(frontend, ctx, stmt)?;
stmt.hir_exprs.append( stmt.hir_exprs.append(
HirExpr { HirExpr {
@ -332,7 +327,7 @@ impl<'source> ParsingContext<'source> {
Default::default(), Default::default(),
) )
} }
_ => self.parse_postfix(frontend, ctx, stmt, body)?, _ => self.parse_postfix(frontend, ctx, stmt)?,
}) })
} }
@ -341,13 +336,12 @@ impl<'source> ParsingContext<'source> {
frontend: &mut Frontend, frontend: &mut Frontend,
ctx: &mut Context, ctx: &mut Context,
stmt: &mut StmtContext, stmt: &mut StmtContext,
body: &mut Block,
passthrough: Option<Handle<HirExpr>>, passthrough: Option<Handle<HirExpr>>,
min_bp: u8, min_bp: u8,
) -> Result<Handle<HirExpr>> { ) -> Result<Handle<HirExpr>> {
let mut left = passthrough let mut left = passthrough
.ok_or(ErrorKind::EndOfFile /* Dummy error */) .ok_or(ErrorKind::EndOfFile /* Dummy error */)
.or_else(|_| self.parse_unary(frontend, ctx, stmt, body))?; .or_else(|_| self.parse_unary(frontend, ctx, stmt))?;
let mut meta = stmt.hir_exprs[left].meta; let mut meta = stmt.hir_exprs[left].meta;
while let Some((l_bp, r_bp)) = binding_power(&self.expect_peek(frontend)?.value) { while let Some((l_bp, r_bp)) = binding_power(&self.expect_peek(frontend)?.value) {
@ -357,7 +351,7 @@ impl<'source> ParsingContext<'source> {
let Token { value, .. } = self.bump(frontend)?; let Token { value, .. } = self.bump(frontend)?;
let right = self.parse_binary(frontend, ctx, stmt, body, None, r_bp)?; let right = self.parse_binary(frontend, ctx, stmt, None, r_bp)?;
let end_meta = stmt.hir_exprs[right].meta; let end_meta = stmt.hir_exprs[right].meta;
meta.subsume(end_meta); meta.subsume(end_meta);
@ -403,16 +397,15 @@ impl<'source> ParsingContext<'source> {
frontend: &mut Frontend, frontend: &mut Frontend,
ctx: &mut Context, ctx: &mut Context,
stmt: &mut StmtContext, stmt: &mut StmtContext,
body: &mut Block,
passthrough: Option<Handle<HirExpr>>, passthrough: Option<Handle<HirExpr>>,
) -> Result<Handle<HirExpr>> { ) -> Result<Handle<HirExpr>> {
let mut condition = self.parse_binary(frontend, ctx, stmt, body, passthrough, 0)?; let mut condition = self.parse_binary(frontend, ctx, stmt, passthrough, 0)?;
let mut meta = stmt.hir_exprs[condition].meta; let mut meta = stmt.hir_exprs[condition].meta;
if self.bump_if(frontend, TokenValue::Question).is_some() { if self.bump_if(frontend, TokenValue::Question).is_some() {
let accept = self.parse_expression(frontend, ctx, stmt, body)?; let accept = self.parse_expression(frontend, ctx, stmt)?;
self.expect(frontend, TokenValue::Colon)?; self.expect(frontend, TokenValue::Colon)?;
let reject = self.parse_assignment(frontend, ctx, stmt, body)?; let reject = self.parse_assignment(frontend, ctx, stmt)?;
let end_meta = stmt.hir_exprs[reject].meta; let end_meta = stmt.hir_exprs[reject].meta;
meta.subsume(end_meta); meta.subsume(end_meta);
@ -437,15 +430,14 @@ impl<'source> ParsingContext<'source> {
frontend: &mut Frontend, frontend: &mut Frontend,
ctx: &mut Context, ctx: &mut Context,
stmt: &mut StmtContext, stmt: &mut StmtContext,
body: &mut Block,
) -> Result<Handle<HirExpr>> { ) -> Result<Handle<HirExpr>> {
let tgt = self.parse_unary(frontend, ctx, stmt, body)?; let tgt = self.parse_unary(frontend, ctx, stmt)?;
let mut meta = stmt.hir_exprs[tgt].meta; let mut meta = stmt.hir_exprs[tgt].meta;
Ok(match self.expect_peek(frontend)?.value { Ok(match self.expect_peek(frontend)?.value {
TokenValue::Assign => { TokenValue::Assign => {
self.bump(frontend)?; self.bump(frontend)?;
let value = self.parse_assignment(frontend, ctx, stmt, body)?; let value = self.parse_assignment(frontend, ctx, stmt)?;
let end_meta = stmt.hir_exprs[value].meta; let end_meta = stmt.hir_exprs[value].meta;
meta.subsume(end_meta); meta.subsume(end_meta);
@ -468,7 +460,7 @@ impl<'source> ParsingContext<'source> {
| TokenValue::RightShiftAssign | TokenValue::RightShiftAssign
| TokenValue::XorAssign => { | TokenValue::XorAssign => {
let token = self.bump(frontend)?; let token = self.bump(frontend)?;
let right = self.parse_assignment(frontend, ctx, stmt, body)?; let right = self.parse_assignment(frontend, ctx, stmt)?;
let end_meta = stmt.hir_exprs[right].meta; let end_meta = stmt.hir_exprs[right].meta;
meta.subsume(end_meta); meta.subsume(end_meta);
@ -504,7 +496,7 @@ impl<'source> ParsingContext<'source> {
Default::default(), Default::default(),
) )
} }
_ => self.parse_conditional(frontend, ctx, stmt, body, Some(tgt))?, _ => self.parse_conditional(frontend, ctx, stmt, Some(tgt))?,
}) })
} }
@ -513,13 +505,12 @@ impl<'source> ParsingContext<'source> {
frontend: &mut Frontend, frontend: &mut Frontend,
ctx: &mut Context, ctx: &mut Context,
stmt: &mut StmtContext, stmt: &mut StmtContext,
body: &mut Block,
) -> Result<Handle<HirExpr>> { ) -> Result<Handle<HirExpr>> {
let mut expr = self.parse_assignment(frontend, ctx, stmt, body)?; let mut expr = self.parse_assignment(frontend, ctx, stmt)?;
while let TokenValue::Comma = self.expect_peek(frontend)?.value { while let TokenValue::Comma = self.expect_peek(frontend)?.value {
self.bump(frontend)?; self.bump(frontend)?;
expr = self.parse_assignment(frontend, ctx, stmt, body)?; expr = self.parse_assignment(frontend, ctx, stmt)?;
} }
Ok(expr) Ok(expr)

View file

@ -40,12 +40,11 @@ impl<'source> ParsingContext<'source> {
&mut self, &mut self,
frontend: &mut Frontend, frontend: &mut Frontend,
ctx: &mut Context, ctx: &mut Context,
body: &mut Block,
terminator: &mut Option<usize>, terminator: &mut Option<usize>,
) -> Result<Option<Span>> { ) -> Result<Option<Span>> {
// Type qualifiers always identify a declaration statement // Type qualifiers always identify a declaration statement
if self.peek_type_qualifier(frontend) { if self.peek_type_qualifier(frontend) {
return self.parse_declaration(frontend, ctx, body, false); return self.parse_declaration(frontend, ctx, false);
} }
// Type names can identify either declaration statements or type constructors // Type names can identify either declaration statements or type constructors
@ -61,7 +60,7 @@ impl<'source> ParsingContext<'source> {
self.backtrack(token)?; self.backtrack(token)?;
if declaration { if declaration {
return self.parse_declaration(frontend, ctx, body, false); return self.parse_declaration(frontend, ctx, false);
} }
} }
@ -79,14 +78,14 @@ impl<'source> ParsingContext<'source> {
let meta_rest = match *value { let meta_rest = match *value {
TokenValue::Continue => { TokenValue::Continue => {
let meta = self.bump(frontend)?.meta; let meta = self.bump(frontend)?.meta;
body.push(Statement::Continue, meta); ctx.body.push(Statement::Continue, meta);
terminator.get_or_insert(body.len()); terminator.get_or_insert(ctx.body.len());
self.expect(frontend, TokenValue::Semicolon)?.meta self.expect(frontend, TokenValue::Semicolon)?.meta
} }
TokenValue::Break => { TokenValue::Break => {
let meta = self.bump(frontend)?.meta; let meta = self.bump(frontend)?.meta;
body.push(Statement::Break, meta); ctx.body.push(Statement::Break, meta);
terminator.get_or_insert(body.len()); terminator.get_or_insert(ctx.body.len());
self.expect(frontend, TokenValue::Semicolon)?.meta self.expect(frontend, TokenValue::Semicolon)?.meta
} }
TokenValue::Return => { TokenValue::Return => {
@ -96,25 +95,25 @@ impl<'source> ParsingContext<'source> {
_ => { _ => {
// TODO: Implicit conversions // TODO: Implicit conversions
let mut stmt = ctx.stmt_ctx(); let mut stmt = ctx.stmt_ctx();
let expr = self.parse_expression(frontend, ctx, &mut stmt, body)?; let expr = self.parse_expression(frontend, ctx, &mut stmt)?;
self.expect(frontend, TokenValue::Semicolon)?; self.expect(frontend, TokenValue::Semicolon)?;
let (handle, meta) = let (handle, meta) =
ctx.lower_expect(stmt, frontend, expr, ExprPos::Rhs, body)?; ctx.lower_expect(stmt, frontend, expr, ExprPos::Rhs)?;
(Some(handle), meta) (Some(handle), meta)
} }
}; };
ctx.emit_restart(body); ctx.emit_restart();
body.push(Statement::Return { value }, meta); ctx.body.push(Statement::Return { value }, meta);
terminator.get_or_insert(body.len()); terminator.get_or_insert(ctx.body.len());
meta meta
} }
TokenValue::Discard => { TokenValue::Discard => {
let meta = self.bump(frontend)?.meta; let meta = self.bump(frontend)?.meta;
body.push(Statement::Kill, meta); ctx.body.push(Statement::Kill, meta);
terminator.get_or_insert(body.len()); terminator.get_or_insert(ctx.body.len());
self.expect(frontend, TokenValue::Semicolon)?.meta self.expect(frontend, TokenValue::Semicolon)?.meta
} }
@ -124,33 +123,31 @@ impl<'source> ParsingContext<'source> {
self.expect(frontend, TokenValue::LeftParen)?; self.expect(frontend, TokenValue::LeftParen)?;
let condition = { let condition = {
let mut stmt = ctx.stmt_ctx(); let mut stmt = ctx.stmt_ctx();
let expr = self.parse_expression(frontend, ctx, &mut stmt, body)?; let expr = self.parse_expression(frontend, ctx, &mut stmt)?;
let (handle, more_meta) = let (handle, more_meta) =
ctx.lower_expect(stmt, frontend, expr, ExprPos::Rhs, body)?; ctx.lower_expect(stmt, frontend, expr, ExprPos::Rhs)?;
meta.subsume(more_meta); meta.subsume(more_meta);
handle handle
}; };
self.expect(frontend, TokenValue::RightParen)?; self.expect(frontend, TokenValue::RightParen)?;
ctx.emit_restart(body); let accept = ctx.new_body(|ctx| {
if let Some(more_meta) = self.parse_statement(frontend, ctx, &mut None)? {
let mut accept = Block::new();
if let Some(more_meta) =
self.parse_statement(frontend, ctx, &mut accept, &mut None)?
{
meta.subsume(more_meta)
}
let mut reject = Block::new();
if self.bump_if(frontend, TokenValue::Else).is_some() {
if let Some(more_meta) =
self.parse_statement(frontend, ctx, &mut reject, &mut None)?
{
meta.subsume(more_meta); meta.subsume(more_meta);
} }
} Ok(())
})?;
body.push( let reject = ctx.new_body(|ctx| {
if self.bump_if(frontend, TokenValue::Else).is_some() {
if let Some(more_meta) = self.parse_statement(frontend, ctx, &mut None)? {
meta.subsume(more_meta);
}
}
Ok(())
})?;
ctx.body.push(
Statement::If { Statement::If {
condition, condition,
accept, accept,
@ -169,17 +166,16 @@ impl<'source> ParsingContext<'source> {
let (selector, uint) = { let (selector, uint) = {
let mut stmt = ctx.stmt_ctx(); let mut stmt = ctx.stmt_ctx();
let expr = self.parse_expression(frontend, ctx, &mut stmt, body)?; let expr = self.parse_expression(frontend, ctx, &mut stmt)?;
let (root, meta) = let (root, meta) = ctx.lower_expect(stmt, frontend, expr, ExprPos::Rhs)?;
ctx.lower_expect(stmt, frontend, expr, ExprPos::Rhs, body)?; let uint = ctx.resolve_type(root, meta)?.scalar_kind()
let uint = frontend.resolve_type(ctx, root, meta)?.scalar_kind()
== Some(crate::ScalarKind::Uint); == Some(crate::ScalarKind::Uint);
(root, uint) (root, uint)
}; };
self.expect(frontend, TokenValue::RightParen)?; self.expect(frontend, TokenValue::RightParen)?;
ctx.emit_restart(body); ctx.emit_restart();
let mut cases = Vec::new(); let mut cases = Vec::new();
// Track if any default case is present in the switch statement. // Track if any default case is present in the switch statement.
@ -192,13 +188,18 @@ impl<'source> ParsingContext<'source> {
self.bump(frontend)?; self.bump(frontend)?;
let mut stmt = ctx.stmt_ctx(); let mut stmt = ctx.stmt_ctx();
let expr = self.parse_expression(frontend, ctx, &mut stmt, body)?; let expr = self.parse_expression(frontend, ctx, &mut stmt)?;
let (root, meta) = let (root, meta) =
ctx.lower_expect(stmt, frontend, expr, ExprPos::Rhs, body)?; ctx.lower_expect(stmt, frontend, expr, ExprPos::Rhs)?;
let const_expr = frontend.solve_constant(ctx, root, meta)?; let const_expr = ctx.solve_constant(root, meta)?;
match frontend.module.const_expressions[const_expr] { match ctx.module.const_expressions[const_expr] {
Expression::Literal(Literal::I32(value)) => match uint { Expression::Literal(Literal::I32(value)) => match uint {
// This unchecked cast isn't good, but since
// we only reach this code when the selector
// is unsigned but the case label is signed,
// verification will reject the module
// anyway (which also matches GLSL's rules).
true => crate::SwitchValue::U32(value as u32), true => crate::SwitchValue::U32(value as u32),
false => crate::SwitchValue::I32(value), false => crate::SwitchValue::I32(value),
}, },
@ -244,35 +245,32 @@ impl<'source> ParsingContext<'source> {
self.expect(frontend, TokenValue::Colon)?; self.expect(frontend, TokenValue::Colon)?;
let mut body = Block::new();
let mut case_terminator = None;
loop {
match self.expect_peek(frontend)?.value {
TokenValue::Case | TokenValue::Default | TokenValue::RightBrace => {
break
}
_ => {
self.parse_statement(
frontend,
ctx,
&mut body,
&mut case_terminator,
)?;
}
}
}
let mut fall_through = true; let mut fall_through = true;
if let Some(mut idx) = case_terminator { let body = ctx.new_body(|ctx| {
if let Statement::Break = body[idx - 1] { let mut case_terminator = None;
fall_through = false; loop {
idx -= 1; match self.expect_peek(frontend)?.value {
TokenValue::Case | TokenValue::Default | TokenValue::RightBrace => {
break
}
_ => {
self.parse_statement(frontend, ctx, &mut case_terminator)?;
}
}
} }
body.cull(idx..) if let Some(mut idx) = case_terminator {
} if let Statement::Break = ctx.body[idx - 1] {
fall_through = false;
idx -= 1;
}
ctx.body.cull(idx..)
}
Ok(())
})?;
cases.push(SwitchCase { cases.push(SwitchCase {
value, value,
@ -317,51 +315,48 @@ impl<'source> ParsingContext<'source> {
}) })
} }
body.push(Statement::Switch { selector, cases }, meta); ctx.body.push(Statement::Switch { selector, cases }, meta);
meta meta
} }
TokenValue::While => { TokenValue::While => {
let mut meta = self.bump(frontend)?.meta; let mut meta = self.bump(frontend)?.meta;
let mut loop_body = Block::new(); let loop_body = ctx.new_body(|ctx| {
let mut stmt = ctx.stmt_ctx();
self.expect(frontend, TokenValue::LeftParen)?;
let root = self.parse_expression(frontend, ctx, &mut stmt)?;
meta.subsume(self.expect(frontend, TokenValue::RightParen)?.meta);
let mut stmt = ctx.stmt_ctx(); let (expr, expr_meta) = ctx.lower_expect(stmt, frontend, root, ExprPos::Rhs)?;
self.expect(frontend, TokenValue::LeftParen)?; let condition = ctx.add_expression(
let root = self.parse_expression(frontend, ctx, &mut stmt, &mut loop_body)?; Expression::Unary {
meta.subsume(self.expect(frontend, TokenValue::RightParen)?.meta); op: UnaryOperator::Not,
expr,
},
expr_meta,
)?;
let (expr, expr_meta) = ctx.emit_restart();
ctx.lower_expect(stmt, frontend, root, ExprPos::Rhs, &mut loop_body)?;
let condition = ctx.add_expression(
Expression::Unary {
op: UnaryOperator::Not,
expr,
},
expr_meta,
&mut loop_body,
);
ctx.emit_restart(&mut loop_body); ctx.body.push(
Statement::If {
condition,
accept: new_break(),
reject: Block::new(),
},
crate::Span::default(),
);
loop_body.push( meta.subsume(expr_meta);
Statement::If {
condition,
accept: new_break(),
reject: Block::new(),
},
crate::Span::default(),
);
meta.subsume(expr_meta); if let Some(body_meta) = self.parse_statement(frontend, ctx, &mut None)? {
meta.subsume(body_meta);
}
Ok(())
})?;
if let Some(body_meta) = ctx.body.push(
self.parse_statement(frontend, ctx, &mut loop_body, &mut None)?
{
meta.subsume(body_meta);
}
body.push(
Statement::Loop { Statement::Loop {
body: loop_body, body: loop_body,
continuing: Block::new(), continuing: Block::new(),
@ -375,47 +370,46 @@ impl<'source> ParsingContext<'source> {
TokenValue::Do => { TokenValue::Do => {
let mut meta = self.bump(frontend)?.meta; let mut meta = self.bump(frontend)?.meta;
let mut loop_body = Block::new(); let loop_body = ctx.new_body(|ctx| {
let mut terminator = None;
self.parse_statement(frontend, ctx, &mut terminator)?;
let mut terminator = None; let mut stmt = ctx.stmt_ctx();
self.parse_statement(frontend, ctx, &mut loop_body, &mut terminator)?;
let mut stmt = ctx.stmt_ctx(); self.expect(frontend, TokenValue::While)?;
self.expect(frontend, TokenValue::LeftParen)?;
let root = self.parse_expression(frontend, ctx, &mut stmt)?;
let end_meta = self.expect(frontend, TokenValue::RightParen)?.meta;
self.expect(frontend, TokenValue::While)?; meta.subsume(end_meta);
self.expect(frontend, TokenValue::LeftParen)?;
let root = self.parse_expression(frontend, ctx, &mut stmt, &mut loop_body)?;
let end_meta = self.expect(frontend, TokenValue::RightParen)?.meta;
meta.subsume(end_meta); let (expr, expr_meta) = ctx.lower_expect(stmt, frontend, root, ExprPos::Rhs)?;
let condition = ctx.add_expression(
Expression::Unary {
op: UnaryOperator::Not,
expr,
},
expr_meta,
)?;
let (expr, expr_meta) = ctx.emit_restart();
ctx.lower_expect(stmt, frontend, root, ExprPos::Rhs, &mut loop_body)?;
let condition = ctx.add_expression(
Expression::Unary {
op: UnaryOperator::Not,
expr,
},
expr_meta,
&mut loop_body,
);
ctx.emit_restart(&mut loop_body); ctx.body.push(
Statement::If {
condition,
accept: new_break(),
reject: Block::new(),
},
crate::Span::default(),
);
loop_body.push( if let Some(idx) = terminator {
Statement::If { ctx.body.cull(idx..)
condition, }
accept: new_break(), Ok(())
reject: Block::new(), })?;
},
crate::Span::default(),
);
if let Some(idx) = terminator { ctx.body.push(
loop_body.cull(idx..)
}
body.push(
Statement::Loop { Statement::Loop {
body: loop_body, body: loop_body,
continuing: Block::new(), continuing: Block::new(),
@ -434,96 +428,98 @@ impl<'source> ParsingContext<'source> {
if self.bump_if(frontend, TokenValue::Semicolon).is_none() { if self.bump_if(frontend, TokenValue::Semicolon).is_none() {
if self.peek_type_name(frontend) || self.peek_type_qualifier(frontend) { if self.peek_type_name(frontend) || self.peek_type_qualifier(frontend) {
self.parse_declaration(frontend, ctx, body, false)?; self.parse_declaration(frontend, ctx, false)?;
} else { } else {
let mut stmt = ctx.stmt_ctx(); let mut stmt = ctx.stmt_ctx();
let expr = self.parse_expression(frontend, ctx, &mut stmt, body)?; let expr = self.parse_expression(frontend, ctx, &mut stmt)?;
ctx.lower(stmt, frontend, expr, ExprPos::Rhs, body)?; ctx.lower(stmt, frontend, expr, ExprPos::Rhs)?;
self.expect(frontend, TokenValue::Semicolon)?; self.expect(frontend, TokenValue::Semicolon)?;
} }
} }
let (mut block, mut continuing) = (Block::new(), Block::new()); let loop_body = ctx.new_body(|ctx| {
if self.bump_if(frontend, TokenValue::Semicolon).is_none() {
let (expr, expr_meta) = if self.peek_type_name(frontend)
|| self.peek_type_qualifier(frontend)
{
let mut qualifiers = self.parse_type_qualifiers(frontend, ctx)?;
let (ty, mut meta) = self.parse_type_non_void(frontend, ctx)?;
let name = self.expect_ident(frontend)?.0;
if self.bump_if(frontend, TokenValue::Semicolon).is_none() { self.expect(frontend, TokenValue::Assign)?;
let (expr, expr_meta) = if self.peek_type_name(frontend)
|| self.peek_type_qualifier(frontend)
{
let mut qualifiers = self.parse_type_qualifiers(frontend)?;
let (ty, mut meta) = self.parse_type_non_void(frontend)?;
let name = self.expect_ident(frontend)?.0;
self.expect(frontend, TokenValue::Assign)?; let (value, end_meta) = self.parse_initializer(frontend, ty, ctx)?;
meta.subsume(end_meta);
let (value, end_meta) = let decl = VarDeclaration {
self.parse_initializer(frontend, ty, ctx, &mut block)?; qualifiers: &mut qualifiers,
meta.subsume(end_meta); ty,
name: Some(name),
init: None,
meta,
};
let decl = VarDeclaration { let pointer = frontend.add_local_var(ctx, decl)?;
qualifiers: &mut qualifiers,
ty, ctx.emit_restart();
name: Some(name),
init: None, ctx.body.push(Statement::Store { pointer, value }, meta);
meta,
(value, end_meta)
} else {
let mut stmt = ctx.stmt_ctx();
let root = self.parse_expression(frontend, ctx, &mut stmt)?;
ctx.lower_expect(stmt, frontend, root, ExprPos::Rhs)?
}; };
let pointer = frontend.add_local_var(ctx, &mut block, decl)?; let condition = ctx.add_expression(
Expression::Unary {
op: UnaryOperator::Not,
expr,
},
expr_meta,
)?;
ctx.emit_restart(&mut block); ctx.emit_restart();
block.push(Statement::Store { pointer, value }, meta); ctx.body.push(
Statement::If {
condition,
accept: new_break(),
reject: Block::new(),
},
crate::Span::default(),
);
(value, end_meta) self.expect(frontend, TokenValue::Semicolon)?;
} else {
let mut stmt = ctx.stmt_ctx();
let root = self.parse_expression(frontend, ctx, &mut stmt, &mut block)?;
ctx.lower_expect(stmt, frontend, root, ExprPos::Rhs, &mut block)?
};
let condition = ctx.add_expression(
Expression::Unary {
op: UnaryOperator::Not,
expr,
},
expr_meta,
&mut block,
);
ctx.emit_restart(&mut block);
block.push(
Statement::If {
condition,
accept: new_break(),
reject: Block::new(),
},
crate::Span::default(),
);
self.expect(frontend, TokenValue::Semicolon)?;
}
match self.expect_peek(frontend)?.value {
TokenValue::RightParen => {}
_ => {
let mut stmt = ctx.stmt_ctx();
let rest =
self.parse_expression(frontend, ctx, &mut stmt, &mut continuing)?;
ctx.lower(stmt, frontend, rest, ExprPos::Rhs, &mut continuing)?;
} }
} Ok(())
})?;
let continuing = ctx.new_body(|ctx| {
match self.expect_peek(frontend)?.value {
TokenValue::RightParen => {}
_ => {
let mut stmt = ctx.stmt_ctx();
let rest = self.parse_expression(frontend, ctx, &mut stmt)?;
ctx.lower(stmt, frontend, rest, ExprPos::Rhs)?;
}
}
Ok(())
})?;
meta.subsume(self.expect(frontend, TokenValue::RightParen)?.meta); meta.subsume(self.expect(frontend, TokenValue::RightParen)?.meta);
if let Some(stmt_meta) = let loop_body = ctx.with_body(loop_body, |ctx| {
self.parse_statement(frontend, ctx, &mut block, &mut None)? if let Some(stmt_meta) = self.parse_statement(frontend, ctx, &mut None)? {
{ meta.subsume(stmt_meta);
meta.subsume(stmt_meta); }
} Ok(())
})?;
body.push( ctx.body.push(
Statement::Loop { Statement::Loop {
body: block, body: loop_body,
continuing, continuing,
break_if: None, break_if: None,
}, },
@ -535,22 +531,20 @@ impl<'source> ParsingContext<'source> {
meta meta
} }
TokenValue::LeftBrace => { TokenValue::LeftBrace => {
let meta = self.bump(frontend)?.meta; let mut meta = self.bump(frontend)?.meta;
let mut block = Block::new();
let mut block_terminator = None; let mut block_terminator = None;
let meta = self.parse_compound_statement(
meta,
frontend,
ctx,
&mut block,
&mut block_terminator,
)?;
body.push(Statement::Block(block), meta); let block = ctx.new_body(|ctx| {
let block_meta =
self.parse_compound_statement(meta, frontend, ctx, &mut block_terminator)?;
meta.subsume(block_meta);
Ok(())
})?;
ctx.body.push(Statement::Block(block), meta);
if block_terminator.is_some() { if block_terminator.is_some() {
terminator.get_or_insert(body.len()); terminator.get_or_insert(ctx.body.len());
} }
meta meta
@ -561,8 +555,8 @@ impl<'source> ParsingContext<'source> {
// tokens. Unknown or invalid tokens will be caught there and // tokens. Unknown or invalid tokens will be caught there and
// turned into an error. // turned into an error.
let mut stmt = ctx.stmt_ctx(); let mut stmt = ctx.stmt_ctx();
let expr = self.parse_expression(frontend, ctx, &mut stmt, body)?; let expr = self.parse_expression(frontend, ctx, &mut stmt)?;
ctx.lower(stmt, frontend, expr, ExprPos::Rhs, body)?; ctx.lower(stmt, frontend, expr, ExprPos::Rhs)?;
self.expect(frontend, TokenValue::Semicolon)?.meta self.expect(frontend, TokenValue::Semicolon)?.meta
} }
}; };
@ -576,7 +570,6 @@ impl<'source> ParsingContext<'source> {
mut meta: Span, mut meta: Span,
frontend: &mut Frontend, frontend: &mut Frontend,
ctx: &mut Context, ctx: &mut Context,
body: &mut Block,
terminator: &mut Option<usize>, terminator: &mut Option<usize>,
) -> Result<Span> { ) -> Result<Span> {
ctx.symbol_table.push_scope(); ctx.symbol_table.push_scope();
@ -590,7 +583,7 @@ impl<'source> ParsingContext<'source> {
break; break;
} }
let stmt = self.parse_statement(frontend, ctx, body, terminator)?; let stmt = self.parse_statement(frontend, ctx, terminator)?;
if let Some(stmt_meta) = stmt { if let Some(stmt_meta) = stmt {
meta.subsume(stmt_meta); meta.subsume(stmt_meta);
@ -598,7 +591,7 @@ impl<'source> ParsingContext<'source> {
} }
if let Some(idx) = *terminator { if let Some(idx) = *terminator {
body.cull(idx..) ctx.body.cull(idx..)
} }
ctx.symbol_table.pop_scope(); ctx.symbol_table.pop_scope();
@ -609,8 +602,7 @@ impl<'source> ParsingContext<'source> {
pub fn parse_function_args( pub fn parse_function_args(
&mut self, &mut self,
frontend: &mut Frontend, frontend: &mut Frontend,
context: &mut Context, ctx: &mut Context,
body: &mut Block,
) -> Result<()> { ) -> Result<()> {
if self.bump_if(frontend, TokenValue::Void).is_some() { if self.bump_if(frontend, TokenValue::Void).is_some() {
return Ok(()); return Ok(());
@ -619,19 +611,19 @@ impl<'source> ParsingContext<'source> {
loop { loop {
if self.peek_type_name(frontend) || self.peek_parameter_qualifier(frontend) { if self.peek_type_name(frontend) || self.peek_parameter_qualifier(frontend) {
let qualifier = self.parse_parameter_qualifier(frontend); let qualifier = self.parse_parameter_qualifier(frontend);
let mut ty = self.parse_type_non_void(frontend)?.0; let mut ty = self.parse_type_non_void(frontend, ctx)?.0;
match self.expect_peek(frontend)?.value { match self.expect_peek(frontend)?.value {
TokenValue::Comma => { TokenValue::Comma => {
self.bump(frontend)?; self.bump(frontend)?;
context.add_function_arg(frontend, body, None, ty, qualifier); ctx.add_function_arg(None, ty, qualifier)?;
continue; continue;
} }
TokenValue::Identifier(_) => { TokenValue::Identifier(_) => {
let mut name = self.expect_ident(frontend)?; let mut name = self.expect_ident(frontend)?;
self.parse_array_specifier(frontend, &mut name.1, &mut ty)?; self.parse_array_specifier(frontend, ctx, &mut name.1, &mut ty)?;
context.add_function_arg(frontend, body, Some(name), ty, qualifier); ctx.add_function_arg(Some(name), ty, qualifier)?;
if self.bump_if(frontend, TokenValue::Comma).is_some() { if self.bump_if(frontend, TokenValue::Comma).is_some() {
continue; continue;

View file

@ -3,6 +3,7 @@ use std::num::NonZeroU32;
use crate::{ use crate::{
front::glsl::{ front::glsl::{
ast::{QualifierKey, QualifierValue, StorageQualifier, StructLayout, TypeQualifiers}, ast::{QualifierKey, QualifierValue, StorageQualifier, StructLayout, TypeQualifiers},
context::Context,
error::ExpectedToken, error::ExpectedToken,
parser::ParsingContext, parser::ParsingContext,
token::{Token, TokenValue}, token::{Token, TokenValue},
@ -17,10 +18,11 @@ impl<'source> ParsingContext<'source> {
pub fn parse_array_specifier( pub fn parse_array_specifier(
&mut self, &mut self,
frontend: &mut Frontend, frontend: &mut Frontend,
ctx: &mut Context,
span: &mut Span, span: &mut Span,
ty: &mut Handle<Type>, ty: &mut Handle<Type>,
) -> Result<()> { ) -> Result<()> {
while self.parse_array_specifier_single(frontend, span, ty)? {} while self.parse_array_specifier_single(frontend, ctx, span, ty)? {}
Ok(()) Ok(())
} }
@ -28,6 +30,7 @@ impl<'source> ParsingContext<'source> {
fn parse_array_specifier_single( fn parse_array_specifier_single(
&mut self, &mut self,
frontend: &mut Frontend, frontend: &mut Frontend,
ctx: &mut Context,
span: &mut Span, span: &mut Span,
ty: &mut Handle<Type>, ty: &mut Handle<Type>,
) -> Result<bool> { ) -> Result<bool> {
@ -38,7 +41,7 @@ impl<'source> ParsingContext<'source> {
span.subsume(meta); span.subsume(meta);
ArraySize::Dynamic ArraySize::Dynamic
} else { } else {
let (value, constant_span) = self.parse_uint_constant(frontend)?; let (value, constant_span) = self.parse_uint_constant(frontend, ctx)?;
let size = NonZeroU32::new(value).ok_or(Error { let size = NonZeroU32::new(value).ok_or(Error {
kind: ErrorKind::SemanticError("Array size must be greater than zero".into()), kind: ErrorKind::SemanticError("Array size must be greater than zero".into()),
meta: constant_span, meta: constant_span,
@ -48,9 +51,9 @@ impl<'source> ParsingContext<'source> {
ArraySize::Constant(size) ArraySize::Constant(size)
}; };
frontend.layouter.update(frontend.module.to_ctx()).unwrap(); frontend.layouter.update(ctx.module.to_ctx()).unwrap();
let stride = frontend.layouter[*ty].to_stride(); let stride = frontend.layouter[*ty].to_stride();
*ty = frontend.module.types.insert( *ty = ctx.module.types.insert(
Type { Type {
name: None, name: None,
inner: TypeInner::Array { inner: TypeInner::Array {
@ -68,11 +71,15 @@ impl<'source> ParsingContext<'source> {
} }
} }
pub fn parse_type(&mut self, frontend: &mut Frontend) -> Result<(Option<Handle<Type>>, Span)> { pub fn parse_type(
&mut self,
frontend: &mut Frontend,
ctx: &mut Context,
) -> Result<(Option<Handle<Type>>, Span)> {
let token = self.bump(frontend)?; let token = self.bump(frontend)?;
let mut handle = match token.value { let mut handle = match token.value {
TokenValue::Void => return Ok((None, token.meta)), TokenValue::Void => return Ok((None, token.meta)),
TokenValue::TypeName(ty) => frontend.module.types.insert(ty, token.meta), TokenValue::TypeName(ty) => ctx.module.types.insert(ty, token.meta),
TokenValue::Struct => { TokenValue::Struct => {
let mut meta = token.meta; let mut meta = token.meta;
let ty_name = self.expect_ident(frontend)?.0; let ty_name = self.expect_ident(frontend)?.0;
@ -80,12 +87,13 @@ impl<'source> ParsingContext<'source> {
let mut members = Vec::new(); let mut members = Vec::new();
let span = self.parse_struct_declaration_list( let span = self.parse_struct_declaration_list(
frontend, frontend,
ctx,
&mut members, &mut members,
StructLayout::Std140, StructLayout::Std140,
)?; )?;
let end_meta = self.expect(frontend, TokenValue::RightBrace)?.meta; let end_meta = self.expect(frontend, TokenValue::RightBrace)?.meta;
meta.subsume(end_meta); meta.subsume(end_meta);
let ty = frontend.module.types.insert( let ty = ctx.module.types.insert(
Type { Type {
name: Some(ty_name.clone()), name: Some(ty_name.clone()),
inner: TypeInner::Struct { members, span }, inner: TypeInner::Struct { members, span },
@ -120,12 +128,16 @@ impl<'source> ParsingContext<'source> {
}; };
let mut span = token.meta; let mut span = token.meta;
self.parse_array_specifier(frontend, &mut span, &mut handle)?; self.parse_array_specifier(frontend, ctx, &mut span, &mut handle)?;
Ok((Some(handle), span)) Ok((Some(handle), span))
} }
pub fn parse_type_non_void(&mut self, frontend: &mut Frontend) -> Result<(Handle<Type>, Span)> { pub fn parse_type_non_void(
let (maybe_ty, meta) = self.parse_type(frontend)?; &mut self,
frontend: &mut Frontend,
ctx: &mut Context,
) -> Result<(Handle<Type>, Span)> {
let (maybe_ty, meta) = self.parse_type(frontend, ctx)?;
let ty = maybe_ty.ok_or_else(|| Error { let ty = maybe_ty.ok_or_else(|| Error {
kind: ErrorKind::SemanticError("Type can't be void".into()), kind: ErrorKind::SemanticError("Type can't be void".into()),
meta, meta,
@ -156,6 +168,7 @@ impl<'source> ParsingContext<'source> {
pub fn parse_type_qualifiers<'a>( pub fn parse_type_qualifiers<'a>(
&mut self, &mut self,
frontend: &mut Frontend, frontend: &mut Frontend,
ctx: &mut Context,
) -> Result<TypeQualifiers<'a>> { ) -> Result<TypeQualifiers<'a>> {
let mut qualifiers = TypeQualifiers::default(); let mut qualifiers = TypeQualifiers::default();
@ -164,7 +177,7 @@ impl<'source> ParsingContext<'source> {
// Handle layout qualifiers outside the match since this can push multiple values // Handle layout qualifiers outside the match since this can push multiple values
if token.value == TokenValue::Layout { if token.value == TokenValue::Layout {
self.parse_layout_qualifier_id_list(frontend, &mut qualifiers)?; self.parse_layout_qualifier_id_list(frontend, ctx, &mut qualifiers)?;
continue; continue;
} }
@ -287,11 +300,12 @@ impl<'source> ParsingContext<'source> {
pub fn parse_layout_qualifier_id_list( pub fn parse_layout_qualifier_id_list(
&mut self, &mut self,
frontend: &mut Frontend, frontend: &mut Frontend,
ctx: &mut Context,
qualifiers: &mut TypeQualifiers, qualifiers: &mut TypeQualifiers,
) -> Result<()> { ) -> Result<()> {
self.expect(frontend, TokenValue::LeftParen)?; self.expect(frontend, TokenValue::LeftParen)?;
loop { loop {
self.parse_layout_qualifier_id(frontend, &mut qualifiers.layout_qualifiers)?; self.parse_layout_qualifier_id(frontend, ctx, &mut qualifiers.layout_qualifiers)?;
if self.bump_if(frontend, TokenValue::Comma).is_some() { if self.bump_if(frontend, TokenValue::Comma).is_some() {
continue; continue;
@ -308,6 +322,7 @@ impl<'source> ParsingContext<'source> {
pub fn parse_layout_qualifier_id( pub fn parse_layout_qualifier_id(
&mut self, &mut self,
frontend: &mut Frontend, frontend: &mut Frontend,
ctx: &mut Context,
qualifiers: &mut crate::FastHashMap<QualifierKey, (QualifierValue, Span)>, qualifiers: &mut crate::FastHashMap<QualifierKey, (QualifierValue, Span)>,
) -> Result<()> { ) -> Result<()> {
// layout_qualifier_id: // layout_qualifier_id:
@ -332,13 +347,14 @@ impl<'source> ParsingContext<'source> {
} else { } else {
let key = QualifierKey::String(name.into()); let key = QualifierKey::String(name.into());
let value = if self.bump_if(frontend, TokenValue::Assign).is_some() { let value = if self.bump_if(frontend, TokenValue::Assign).is_some() {
let (value, end_meta) = match self.parse_uint_constant(frontend) { let (value, end_meta) =
Ok(v) => v, match self.parse_uint_constant(frontend, ctx) {
Err(e) => { Ok(v) => v,
frontend.errors.push(e); Err(e) => {
(0, Span::default()) frontend.errors.push(e);
} (0, Span::default())
}; }
};
token.meta.subsume(end_meta); token.meta.subsume(end_meta);
QualifierValue::Uint(value) QualifierValue::Uint(value)

View file

@ -1,6 +1,4 @@
use super::{ use super::{constants::ConstantSolver, context::Context, Error, ErrorKind, Result, Span};
constants::ConstantSolver, context::Context, Error, ErrorKind, Frontend, Result, Span,
};
use crate::{ use crate::{
proc::ResolveContext, Bytes, Expression, Handle, ImageClass, ImageDimension, ScalarKind, Type, proc::ResolveContext, Bytes, Expression, Handle, ImageClass, ImageDimension, ScalarKind, Type,
TypeInner, VectorSize, TypeInner, VectorSize,
@ -226,7 +224,7 @@ pub const fn type_power(kind: ScalarKind, width: Bytes) -> Option<u32> {
}) })
} }
impl Frontend { impl Context<'_> {
/// Resolves the types of the expressions until `expr` (inclusive) /// Resolves the types of the expressions until `expr` (inclusive)
/// ///
/// This needs to be done before the [`typifier`] can be queried for /// This needs to be done before the [`typifier`] can be queried for
@ -240,16 +238,11 @@ impl Frontend {
/// ///
/// [`typifier`]: Context::typifier /// [`typifier`]: Context::typifier
/// [`resolve_type`]: Self::resolve_type /// [`resolve_type`]: Self::resolve_type
pub(crate) fn typifier_grow( pub(crate) fn typifier_grow(&mut self, expr: Handle<Expression>, meta: Span) -> Result<()> {
&self, let resolve_ctx = ResolveContext::with_locals(self.module, &self.locals, &self.arguments);
ctx: &mut Context,
expr: Handle<Expression>,
meta: Span,
) -> Result<()> {
let resolve_ctx = ResolveContext::with_locals(&self.module, &ctx.locals, &ctx.arguments);
ctx.typifier self.typifier
.grow(expr, &ctx.expressions, &resolve_ctx) .grow(expr, &self.expressions, &resolve_ctx)
.map_err(|error| Error { .map_err(|error| Error {
kind: ErrorKind::SemanticError(format!("Can't resolve type: {error:?}").into()), kind: ErrorKind::SemanticError(format!("Can't resolve type: {error:?}").into()),
meta, meta,
@ -263,14 +256,13 @@ impl Frontend {
/// ///
/// [`typifier`]: Context::typifier /// [`typifier`]: Context::typifier
/// [`typifier_grow`]: Self::typifier_grow /// [`typifier_grow`]: Self::typifier_grow
pub(crate) fn resolve_type<'b>( pub(crate) fn resolve_type(
&'b self, &mut self,
ctx: &'b mut Context,
expr: Handle<Expression>, expr: Handle<Expression>,
meta: Span, meta: Span,
) -> Result<&'b TypeInner> { ) -> Result<&TypeInner> {
self.typifier_grow(ctx, expr, meta)?; self.typifier_grow(expr, meta)?;
Ok(ctx.typifier.get(expr, &self.module.types)) Ok(self.typifier.get(expr, &self.module.types))
} }
/// Gets the type handle for the result of the `expr` expression /// Gets the type handle for the result of the `expr` expression
@ -290,25 +282,23 @@ impl Frontend {
/// [`resolve_type`]: Self::resolve_type /// [`resolve_type`]: Self::resolve_type
pub(crate) fn resolve_type_handle( pub(crate) fn resolve_type_handle(
&mut self, &mut self,
ctx: &mut Context,
expr: Handle<Expression>, expr: Handle<Expression>,
meta: Span, meta: Span,
) -> Result<Handle<Type>> { ) -> Result<Handle<Type>> {
self.typifier_grow(ctx, expr, meta)?; self.typifier_grow(expr, meta)?;
Ok(ctx.typifier.register_type(expr, &mut self.module.types)) Ok(self.typifier.register_type(expr, &mut self.module.types))
} }
/// Invalidates the cached type resolution for `expr` forcing a recomputation /// Invalidates the cached type resolution for `expr` forcing a recomputation
pub(crate) fn invalidate_expression<'b>( pub(crate) fn invalidate_expression(
&'b self, &mut self,
ctx: &'b mut Context,
expr: Handle<Expression>, expr: Handle<Expression>,
meta: Span, meta: Span,
) -> Result<()> { ) -> Result<()> {
let resolve_ctx = ResolveContext::with_locals(&self.module, &ctx.locals, &ctx.arguments); let resolve_ctx = ResolveContext::with_locals(self.module, &self.locals, &self.arguments);
ctx.typifier self.typifier
.invalidate(expr, &ctx.expressions, &resolve_ctx) .invalidate(expr, &self.expressions, &resolve_ctx)
.map_err(|error| Error { .map_err(|error| Error {
kind: ErrorKind::SemanticError(format!("Can't resolve type: {error:?}").into()), kind: ErrorKind::SemanticError(format!("Can't resolve type: {error:?}").into()),
meta, meta,
@ -317,13 +307,12 @@ impl Frontend {
pub(crate) fn solve_constant( pub(crate) fn solve_constant(
&mut self, &mut self,
ctx: &Context,
root: Handle<Expression>, root: Handle<Expression>,
meta: Span, meta: Span,
) -> Result<Handle<Expression>> { ) -> Result<Handle<Expression>> {
let mut solver = ConstantSolver { let mut solver = ConstantSolver {
types: &mut self.module.types, types: &mut self.module.types,
expressions: &ctx.expressions, expressions: &self.expressions,
constants: &mut self.module.constants, constants: &mut self.module.constants,
const_expressions: &mut self.module.const_expressions, const_expressions: &mut self.module.const_expressions,
}; };

View file

@ -5,9 +5,9 @@ use super::{
Frontend, Result, Span, Frontend, Result, Span,
}; };
use crate::{ use crate::{
AddressSpace, Binding, Block, BuiltIn, Constant, Expression, GlobalVariable, Handle, AddressSpace, Binding, BuiltIn, Constant, Expression, GlobalVariable, Handle, Interpolation,
Interpolation, LocalVariable, ResourceBinding, ScalarKind, ShaderStage, SwizzleComponent, Type, LocalVariable, ResourceBinding, ScalarKind, ShaderStage, SwizzleComponent, Type, TypeInner,
TypeInner, VectorSize, VectorSize,
}; };
pub struct VarDeclaration<'a, 'key> { pub struct VarDeclaration<'a, 'key> {
@ -40,12 +40,11 @@ impl Frontend {
fn add_builtin( fn add_builtin(
&mut self, &mut self,
ctx: &mut Context, ctx: &mut Context,
body: &mut Block,
name: &str, name: &str,
data: BuiltInData, data: BuiltInData,
meta: Span, meta: Span,
) -> Option<VariableReference> { ) -> Result<Option<VariableReference>> {
let ty = self.module.types.insert( let ty = ctx.module.types.insert(
Type { Type {
name: None, name: None,
inner: data.inner, inner: data.inner,
@ -53,7 +52,7 @@ impl Frontend {
meta, meta,
); );
let handle = self.module.global_variables.append( let handle = ctx.module.global_variables.append(
GlobalVariable { GlobalVariable {
name: Some(name.into()), name: Some(name.into()),
space: AddressSpace::Private, space: AddressSpace::Private,
@ -81,7 +80,7 @@ impl Frontend {
}, },
)); ));
let expr = ctx.add_expression(Expression::GlobalVariable(handle), meta, body); let expr = ctx.add_expression(Expression::GlobalVariable(handle), meta)?;
let var = VariableReference { let var = VariableReference {
expr, expr,
@ -93,18 +92,17 @@ impl Frontend {
ctx.symbol_table.add_root(name.into(), var.clone()); ctx.symbol_table.add_root(name.into(), var.clone());
Some(var) Ok(Some(var))
} }
pub(crate) fn lookup_variable( pub(crate) fn lookup_variable(
&mut self, &mut self,
ctx: &mut Context, ctx: &mut Context,
body: &mut Block,
name: &str, name: &str,
meta: Span, meta: Span,
) -> Option<VariableReference> { ) -> Result<Option<VariableReference>> {
if let Some(var) = ctx.symbol_table.lookup(name).cloned() { if let Some(var) = ctx.symbol_table.lookup(name).cloned() {
return Some(var); return Ok(Some(var));
} }
let data = match name { let data = match name {
@ -182,7 +180,7 @@ impl Frontend {
storage: StorageQualifier::Output, storage: StorageQualifier::Output,
}, },
"gl_ClipDistance" | "gl_CullDistance" => { "gl_ClipDistance" | "gl_CullDistance" => {
let base = self.module.types.insert( let base = ctx.module.types.insert(
Type { Type {
name: None, name: None,
inner: TypeInner::Scalar { inner: TypeInner::Scalar {
@ -217,7 +215,7 @@ impl Frontend {
"gl_VertexIndex" => BuiltIn::VertexIndex, "gl_VertexIndex" => BuiltIn::VertexIndex,
"gl_SampleID" => BuiltIn::SampleIndex, "gl_SampleID" => BuiltIn::SampleIndex,
"gl_LocalInvocationIndex" => BuiltIn::LocalInvocationIndex, "gl_LocalInvocationIndex" => BuiltIn::LocalInvocationIndex,
_ => return None, _ => return Ok(None),
}; };
BuiltInData { BuiltInData {
@ -232,17 +230,16 @@ impl Frontend {
} }
}; };
self.add_builtin(ctx, body, name, data, meta) self.add_builtin(ctx, name, data, meta)
} }
pub(crate) fn make_variable_invariant( pub(crate) fn make_variable_invariant(
&mut self, &mut self,
ctx: &mut Context, ctx: &mut Context,
body: &mut Block,
name: &str, name: &str,
meta: Span, meta: Span,
) { ) -> Result<()> {
if let Some(var) = self.lookup_variable(ctx, body, name, meta) { if let Some(var) = self.lookup_variable(ctx, name, meta)? {
if let Some(index) = var.entry_arg { if let Some(index) = var.entry_arg {
if let Binding::BuiltIn(BuiltIn::Position { ref mut invariant }) = if let Binding::BuiltIn(BuiltIn::Position { ref mut invariant }) =
self.entry_args[index].binding self.entry_args[index].binding
@ -251,19 +248,19 @@ impl Frontend {
} }
} }
} }
Ok(())
} }
pub(crate) fn field_selection( pub(crate) fn field_selection(
&mut self, &mut self,
ctx: &mut Context, ctx: &mut Context,
pos: ExprPos, pos: ExprPos,
body: &mut Block,
expression: Handle<Expression>, expression: Handle<Expression>,
name: &str, name: &str,
meta: Span, meta: Span,
) -> Result<Handle<Expression>> { ) -> Result<Handle<Expression>> {
let (ty, is_pointer) = match *self.resolve_type(ctx, expression, meta)? { let (ty, is_pointer) = match *ctx.resolve_type(expression, meta)? {
TypeInner::Pointer { base, .. } => (&self.module.types[base].inner, true), TypeInner::Pointer { base, .. } => (&ctx.module.types[base].inner, true),
ref ty => (ty, false), ref ty => (ty, false),
}; };
match *ty { match *ty {
@ -281,12 +278,11 @@ impl Frontend {
index: index as u32, index: index as u32,
}, },
meta, meta,
body, )?;
);
Ok(match pos { Ok(match pos {
ExprPos::Rhs if is_pointer => { ExprPos::Rhs if is_pointer => {
ctx.add_expression(Expression::Load { pointer }, meta, body) ctx.add_expression(Expression::Load { pointer }, meta)?
} }
_ => pointer, _ => pointer,
}) })
@ -358,19 +354,17 @@ impl Frontend {
pointer: expression, pointer: expression,
}, },
meta, meta,
body, )?;
);
} }
_ => {} _ => {}
}; };
return Ok(ctx.add_expression( return ctx.add_expression(
Expression::AccessIndex { Expression::AccessIndex {
base: expression, base: expression,
index: pattern[0].index(), index: pattern[0].index(),
}, },
meta, meta,
body, );
));
} }
2 => VectorSize::Bi, 2 => VectorSize::Bi,
3 => VectorSize::Tri, 3 => VectorSize::Tri,
@ -396,8 +390,7 @@ impl Frontend {
pointer: expression, pointer: expression,
}, },
meta, meta,
body, )?;
);
} }
Ok(ctx.add_expression( Ok(ctx.add_expression(
@ -407,8 +400,7 @@ impl Frontend {
pattern, pattern,
}, },
meta, meta,
body, )?)
))
} else { } else {
Err(Error { Err(Error {
kind: ErrorKind::SemanticError( kind: ErrorKind::SemanticError(
@ -430,7 +422,6 @@ impl Frontend {
pub(crate) fn add_global_var( pub(crate) fn add_global_var(
&mut self, &mut self,
ctx: &mut Context, ctx: &mut Context,
body: &mut Block,
VarDeclaration { VarDeclaration {
qualifiers, qualifiers,
mut ty, mut ty,
@ -449,7 +440,7 @@ impl Frontend {
.uint_layout_qualifier("location", &mut self.errors) .uint_layout_qualifier("location", &mut self.errors)
.unwrap_or(0); .unwrap_or(0);
let interpolation = qualifiers.interpolation.take().map(|(i, _)| i).or_else(|| { let interpolation = qualifiers.interpolation.take().map(|(i, _)| i).or_else(|| {
let kind = self.module.types[ty].inner.scalar_kind()?; let kind = ctx.module.types[ty].inner.scalar_kind()?;
Some(match kind { Some(match kind {
ScalarKind::Float => Interpolation::Perspective, ScalarKind::Float => Interpolation::Perspective,
_ => Interpolation::Flat, _ => Interpolation::Flat,
@ -457,7 +448,7 @@ impl Frontend {
}); });
let sampling = qualifiers.sampling.take().map(|(s, _)| s); let sampling = qualifiers.sampling.take().map(|(s, _)| s);
let handle = self.module.global_variables.append( let handle = ctx.module.global_variables.append(
GlobalVariable { GlobalVariable {
name: name.clone(), name: name.clone(),
space: AddressSpace::Private, space: AddressSpace::Private,
@ -475,6 +466,7 @@ impl Frontend {
location, location,
interpolation, interpolation,
sampling, sampling,
second_blend_source: false,
}, },
handle, handle,
storage, storage,
@ -500,7 +492,7 @@ impl Frontend {
ty, ty,
init, init,
}; };
let handle = self.module.constants.fetch_or_append(constant, meta); let handle = ctx.module.constants.fetch_or_append(constant, meta);
let lookup = GlobalLookup { let lookup = GlobalLookup {
kind: GlobalLookupKind::Constant(handle, ty), kind: GlobalLookupKind::Constant(handle, ty),
@ -517,7 +509,7 @@ impl Frontend {
*access = allowed_access; *access = allowed_access;
} }
} }
AddressSpace::Uniform => match self.module.types[ty].inner { AddressSpace::Uniform => match ctx.module.types[ty].inner {
TypeInner::Image { TypeInner::Image {
class, class,
dim, dim,
@ -546,7 +538,7 @@ impl Frontend {
_ => unreachable!(), _ => unreachable!(),
} }
ty = self.module.types.insert( ty = ctx.module.types.insert(
Type { Type {
name: None, name: None,
inner: TypeInner::Image { inner: TypeInner::Image {
@ -592,7 +584,7 @@ impl Frontend {
_ => None, _ => None,
}; };
let handle = self.module.global_variables.append( let handle = ctx.module.global_variables.append(
GlobalVariable { GlobalVariable {
name: name.clone(), name: name.clone(),
space, space,
@ -614,7 +606,7 @@ impl Frontend {
}; };
if let Some(name) = name { if let Some(name) = name {
ctx.add_global(self, &name, lookup, body); ctx.add_global(&name, lookup)?;
self.global_variables.push((name, lookup)); self.global_variables.push((name, lookup));
} }
@ -627,7 +619,6 @@ impl Frontend {
pub(crate) fn add_local_var( pub(crate) fn add_local_var(
&mut self, &mut self,
ctx: &mut Context, ctx: &mut Context,
body: &mut Block,
decl: VarDeclaration, decl: VarDeclaration,
) -> Result<Handle<Expression>> { ) -> Result<Handle<Expression>> {
let storage = decl.qualifiers.storage; let storage = decl.qualifiers.storage;
@ -651,7 +642,7 @@ impl Frontend {
}, },
decl.meta, decl.meta,
); );
let expr = ctx.add_expression(Expression::LocalVariable(handle), decl.meta, body); let expr = ctx.add_expression(Expression::LocalVariable(handle), decl.meta)?;
if let Some(name) = decl.name { if let Some(name) = decl.name {
let maybe_var = ctx.add_local_var(name.clone(), expr, mutable); let maybe_var = ctx.add_local_var(name.clone(), expr, mutable);

View file

@ -43,6 +43,7 @@ impl crate::Binding {
location: _, location: _,
interpolation: ref mut interpolation @ None, interpolation: ref mut interpolation @ None,
ref mut sampling, ref mut sampling,
second_blend_source: _,
} = *self } = *self
{ {
match ty.scalar_kind() { match ty.scalar_kind() {

View file

@ -255,6 +255,7 @@ impl Decoration {
location, location,
interpolation, interpolation,
sampling, sampling,
second_blend_source: false,
}), }),
_ => Err(Error::MissingDecoration(spirv::Decoration::Location)), _ => Err(Error::MissingDecoration(spirv::Decoration::Location)),
} }

View file

@ -5,55 +5,6 @@ Type generators.
use crate::{arena::Handle, span::Span}; use crate::{arena::Handle, span::Span};
impl crate::Module { impl crate::Module {
pub fn generate_atomic_compare_exchange_result(
&mut self,
kind: crate::ScalarKind,
width: crate::Bytes,
) -> Handle<crate::Type> {
let bool_ty = self.types.insert(
crate::Type {
name: None,
inner: crate::TypeInner::Scalar {
kind: crate::ScalarKind::Bool,
width: crate::BOOL_WIDTH,
},
},
Span::UNDEFINED,
);
let scalar_ty = self.types.insert(
crate::Type {
name: None,
inner: crate::TypeInner::Scalar { kind, width },
},
Span::UNDEFINED,
);
self.types.insert(
crate::Type {
name: Some(format!(
"__atomic_compare_exchange_result<{kind:?},{width}>"
)),
inner: crate::TypeInner::Struct {
members: vec![
crate::StructMember {
name: Some("old_value".to_string()),
ty: scalar_ty,
binding: None,
offset: 0,
},
crate::StructMember {
name: Some("exchanged".to_string()),
ty: bool_ty,
binding: None,
offset: 4,
},
],
span: 8,
},
},
Span::UNDEFINED,
)
}
/// Populate this module's [`SpecialTypes::ray_desc`] type. /// Populate this module's [`SpecialTypes::ray_desc`] type.
/// ///
/// [`SpecialTypes::ray_desc`] is the type of the [`descriptor`] operand of /// [`SpecialTypes::ray_desc`] is the type of the [`descriptor`] operand of
@ -311,4 +262,203 @@ impl crate::Module {
self.special_types.ray_intersection = Some(handle); self.special_types.ray_intersection = Some(handle);
handle handle
} }
/// Populate this module's [`SpecialTypes::predeclared_types`] type and return the handle.
///
/// [`SpecialTypes::predeclared_types`]: crate::SpecialTypes::predeclared_types
pub fn generate_predeclared_type(
&mut self,
special_type: crate::PredeclaredType,
) -> Handle<crate::Type> {
use std::fmt::Write;
if let Some(value) = self.special_types.predeclared_types.get(&special_type) {
return *value;
}
let ty = match special_type {
crate::PredeclaredType::AtomicCompareExchangeWeakResult { kind, width } => {
let bool_ty = self.types.insert(
crate::Type {
name: None,
inner: crate::TypeInner::Scalar {
kind: crate::ScalarKind::Bool,
width: crate::BOOL_WIDTH,
},
},
Span::UNDEFINED,
);
let scalar_ty = self.types.insert(
crate::Type {
name: None,
inner: crate::TypeInner::Scalar { kind, width },
},
Span::UNDEFINED,
);
crate::Type {
name: Some(format!(
"__atomic_compare_exchange_result<{kind:?},{width}>"
)),
inner: crate::TypeInner::Struct {
members: vec![
crate::StructMember {
name: Some("old_value".to_string()),
ty: scalar_ty,
binding: None,
offset: 0,
},
crate::StructMember {
name: Some("exchanged".to_string()),
ty: bool_ty,
binding: None,
offset: 4,
},
],
span: 8,
},
}
}
crate::PredeclaredType::ModfResult { size, width } => {
let float_ty = self.types.insert(
crate::Type {
name: None,
inner: crate::TypeInner::Scalar {
kind: crate::ScalarKind::Float,
width,
},
},
Span::UNDEFINED,
);
let (member_ty, second_offset) = if let Some(size) = size {
let vec_ty = self.types.insert(
crate::Type {
name: None,
inner: crate::TypeInner::Vector {
size,
kind: crate::ScalarKind::Float,
width,
},
},
Span::UNDEFINED,
);
(vec_ty, size as u32 * width as u32)
} else {
(float_ty, width as u32)
};
let mut type_name = "__modf_result_".to_string();
if let Some(size) = size {
let _ = write!(type_name, "vec{}_", size as u8);
}
let _ = write!(type_name, "f{}", width * 8);
crate::Type {
name: Some(type_name),
inner: crate::TypeInner::Struct {
members: vec![
crate::StructMember {
name: Some("fract".to_string()),
ty: member_ty,
binding: None,
offset: 0,
},
crate::StructMember {
name: Some("whole".to_string()),
ty: member_ty,
binding: None,
offset: second_offset,
},
],
span: second_offset * 2,
},
}
}
crate::PredeclaredType::FrexpResult { size, width } => {
let float_ty = self.types.insert(
crate::Type {
name: None,
inner: crate::TypeInner::Scalar {
kind: crate::ScalarKind::Float,
width,
},
},
Span::UNDEFINED,
);
let int_ty = self.types.insert(
crate::Type {
name: None,
inner: crate::TypeInner::Scalar {
kind: crate::ScalarKind::Sint,
width,
},
},
Span::UNDEFINED,
);
let (fract_member_ty, exp_member_ty, second_offset) = if let Some(size) = size {
let vec_float_ty = self.types.insert(
crate::Type {
name: None,
inner: crate::TypeInner::Vector {
size,
kind: crate::ScalarKind::Float,
width,
},
},
Span::UNDEFINED,
);
let vec_int_ty = self.types.insert(
crate::Type {
name: None,
inner: crate::TypeInner::Vector {
size,
kind: crate::ScalarKind::Sint,
width,
},
},
Span::UNDEFINED,
);
(vec_float_ty, vec_int_ty, size as u32 * width as u32)
} else {
(float_ty, int_ty, width as u32)
};
let mut type_name = "__frexp_result_".to_string();
if let Some(size) = size {
let _ = write!(type_name, "vec{}_", size as u8);
}
let _ = write!(type_name, "f{}", width * 8);
crate::Type {
name: Some(type_name),
inner: crate::TypeInner::Struct {
members: vec![
crate::StructMember {
name: Some("fract".to_string()),
ty: fract_member_ty,
binding: None,
offset: 0,
},
crate::StructMember {
name: Some("exp".to_string()),
ty: exp_member_ty,
binding: None,
offset: second_offset,
},
],
span: second_offset * 2,
},
}
}
};
let handle = self.types.insert(ty, Span::UNDEFINED);
self.special_types
.predeclared_types
.insert(special_type, handle);
handle
}
} }

View file

@ -242,6 +242,7 @@ pub enum Error<'a> {
Other, Other,
ExpectedArraySize(Span), ExpectedArraySize(Span),
NonPositiveArrayLength(Span), NonPositiveArrayLength(Span),
MissingWorkgroupSize(Span),
} }
impl<'a> Error<'a> { impl<'a> Error<'a> {
@ -433,7 +434,7 @@ impl<'a> Error<'a> {
}, },
Error::RepeatedAttribute(bad_span) => ParseError { Error::RepeatedAttribute(bad_span) => ParseError {
message: format!("repeated attribute: '{}'", &source[bad_span]), message: format!("repeated attribute: '{}'", &source[bad_span]),
labels: vec![(bad_span, "repated attribute".into())], labels: vec![(bad_span, "repeated attribute".into())],
notes: vec![], notes: vec![],
}, },
Error::UnknownAttribute(bad_span) => ParseError { Error::UnknownAttribute(bad_span) => ParseError {
@ -704,6 +705,14 @@ impl<'a> Error<'a> {
labels: vec![(span, "must be greater than zero".into())], labels: vec![(span, "must be greater than zero".into())],
notes: vec![], notes: vec![],
}, },
Error::MissingWorkgroupSize(span) => ParseError {
message: "workgroup size is missing on compute shader entry point".to_string(),
labels: vec![(
span,
"must be paired with a @workgroup_size attribute".into(),
)],
notes: vec![],
},
} }
} }
} }

View file

@ -504,22 +504,29 @@ impl<'source, 'temp, 'out> ExpressionContext<'source, 'temp, 'out> {
} }
/// Insert splats, if needed by the non-'*' operations. /// Insert splats, if needed by the non-'*' operations.
///
/// See the "Binary arithmetic expressions with mixed scalar and vector operands"
/// table in the WebGPU Shading Language specification for relevant operators.
///
/// Multiply is not handled here as backends are expected to handle vec*scalar
/// operations, so inserting splats into the IR increases size needlessly.
fn binary_op_splat( fn binary_op_splat(
&mut self, &mut self,
op: crate::BinaryOperator, op: crate::BinaryOperator,
left: &mut Handle<crate::Expression>, left: &mut Handle<crate::Expression>,
right: &mut Handle<crate::Expression>, right: &mut Handle<crate::Expression>,
) -> Result<(), Error<'source>> { ) -> Result<(), Error<'source>> {
if op != crate::BinaryOperator::Multiply { if matches!(
op,
crate::BinaryOperator::Add
| crate::BinaryOperator::Subtract
| crate::BinaryOperator::Divide
| crate::BinaryOperator::Modulo
) {
self.grow_types(*left)?.grow_types(*right)?; self.grow_types(*left)?.grow_types(*right)?;
let left_size = match *self.resolved_inner(*left) { match (self.resolved_inner(*left), self.resolved_inner(*right)) {
crate::TypeInner::Vector { size, .. } => Some(size), (&crate::TypeInner::Vector { size, .. }, &crate::TypeInner::Scalar { .. }) => {
_ => None,
};
match (left_size, self.resolved_inner(*right)) {
(Some(size), &crate::TypeInner::Scalar { .. }) => {
*right = self.append_expression( *right = self.append_expression(
crate::Expression::Splat { crate::Expression::Splat {
size, size,
@ -528,7 +535,7 @@ impl<'source, 'temp, 'out> ExpressionContext<'source, 'temp, 'out> {
self.get_expression_span(*right), self.get_expression_span(*right),
); );
} }
(None, &crate::TypeInner::Vector { size, .. }) => { (&crate::TypeInner::Scalar { .. }, &crate::TypeInner::Vector { size, .. }) => {
*left = self.append_expression( *left = self.append_expression(
crate::Expression::Splat { size, value: *left }, crate::Expression::Splat { size, value: *left },
self.get_expression_span(*left), self.get_expression_span(*left),
@ -967,7 +974,7 @@ impl<'source, 'temp> Lowerer<'source, 'temp> {
name: f.name.name.to_string(), name: f.name.name.to_string(),
stage: entry.stage, stage: entry.stage,
early_depth_test: entry.early_depth_test, early_depth_test: entry.early_depth_test,
workgroup_size: entry.workgroup_size, workgroup_size: entry.workgroup_size.unwrap_or([0, 0, 0]),
function, function,
}); });
Ok(LoweredGlobalDecl::EntryPoint) Ok(LoweredGlobalDecl::EntryPoint)
@ -1689,7 +1696,26 @@ impl<'source, 'temp> Lowerer<'source, 'temp> {
let argument = self.expression(args.next()?, ctx.reborrow())?; let argument = self.expression(args.next()?, ctx.reborrow())?;
args.finish()?; args.finish()?;
crate::Expression::Relational { fun, argument } // Check for no-op all(bool) and any(bool):
let argument_unmodified = matches!(
fun,
crate::RelationalFunction::All | crate::RelationalFunction::Any
) && {
ctx.grow_types(argument)?;
matches!(
ctx.resolved_inner(argument),
&crate::TypeInner::Scalar {
kind: crate::ScalarKind::Bool,
..
}
)
};
if argument_unmodified {
return Ok(Some(argument));
} else {
crate::Expression::Relational { fun, argument }
}
} else if let Some((axis, ctrl)) = conv::map_derivative(function.name) { } else if let Some((axis, ctrl)) = conv::map_derivative(function.name) {
let mut args = ctx.prepare_args(arguments, 1, span); let mut args = ctx.prepare_args(arguments, 1, span);
let expr = self.expression(args.next()?, ctx.reborrow())?; let expr = self.expression(args.next()?, ctx.reborrow())?;
@ -1719,6 +1745,25 @@ impl<'source, 'temp> Lowerer<'source, 'temp> {
args.finish()?; args.finish()?;
if fun == crate::MathFunction::Modf || fun == crate::MathFunction::Frexp {
ctx.grow_types(arg)?;
if let Some((size, width)) = match *ctx.resolved_inner(arg) {
crate::TypeInner::Scalar { width, .. } => Some((None, width)),
crate::TypeInner::Vector { size, width, .. } => {
Some((Some(size), width))
}
_ => None,
} {
ctx.module.generate_predeclared_type(
if fun == crate::MathFunction::Modf {
crate::PredeclaredType::ModfResult { size, width }
} else {
crate::PredeclaredType::FrexpResult { size, width }
},
);
}
}
crate::Expression::Math { crate::Expression::Math {
fun, fun,
arg, arg,
@ -1854,10 +1899,12 @@ impl<'source, 'temp> Lowerer<'source, 'temp> {
let expression = match *ctx.resolved_inner(value) { let expression = match *ctx.resolved_inner(value) {
crate::TypeInner::Scalar { kind, width } => { crate::TypeInner::Scalar { kind, width } => {
crate::Expression::AtomicResult { crate::Expression::AtomicResult {
//TODO: cache this to avoid generating duplicate types ty: ctx.module.generate_predeclared_type(
ty: ctx crate::PredeclaredType::AtomicCompareExchangeWeakResult {
.module kind,
.generate_atomic_compare_exchange_result(kind, width), width,
},
),
comparison: true, comparison: true,
} }
} }

View file

@ -103,7 +103,7 @@ pub struct FunctionResult<'a> {
pub struct EntryPoint { pub struct EntryPoint {
pub stage: crate::ShaderStage, pub stage: crate::ShaderStage,
pub early_depth_test: Option<crate::EarlyDepthTest>, pub early_depth_test: Option<crate::EarlyDepthTest>,
pub workgroup_size: [u32; 3], pub workgroup_size: Option<[u32; 3]>,
} }
#[cfg(doc)] #[cfg(doc)]

View file

@ -2,7 +2,7 @@ use crate::front::wgsl::error::{Error, ExpectedToken};
use crate::front::wgsl::parse::lexer::{Lexer, Token}; use crate::front::wgsl::parse::lexer::{Lexer, Token};
use crate::front::wgsl::parse::number::Number; use crate::front::wgsl::parse::number::Number;
use crate::front::SymbolTable; use crate::front::SymbolTable;
use crate::{Arena, FastHashSet, Handle, Span}; use crate::{Arena, FastHashSet, Handle, ShaderStage, Span};
pub mod ast; pub mod ast;
pub mod conv; pub mod conv;
@ -143,6 +143,7 @@ impl<T> ParsedAttribute<T> {
#[derive(Default)] #[derive(Default)]
struct BindingParser { struct BindingParser {
location: ParsedAttribute<u32>, location: ParsedAttribute<u32>,
second_blend_source: ParsedAttribute<bool>,
built_in: ParsedAttribute<crate::BuiltIn>, built_in: ParsedAttribute<crate::BuiltIn>,
interpolation: ParsedAttribute<crate::Interpolation>, interpolation: ParsedAttribute<crate::Interpolation>,
sampling: ParsedAttribute<crate::Sampling>, sampling: ParsedAttribute<crate::Sampling>,
@ -182,6 +183,9 @@ impl BindingParser {
} }
lexer.expect(Token::Paren(')'))?; lexer.expect(Token::Paren(')'))?;
} }
"second_blend_source" => {
self.second_blend_source.set(true, name_span)?;
}
"invariant" => { "invariant" => {
self.invariant.set(true, name_span)?; self.invariant.set(true, name_span)?;
} }
@ -208,6 +212,7 @@ impl BindingParser {
location, location,
interpolation, interpolation,
sampling, sampling,
second_blend_source: self.second_blend_source.value.unwrap_or(false),
})) }))
} }
(None, Some(crate::BuiltIn::Position { .. }), None, None, invariant) => { (None, Some(crate::BuiltIn::Position { .. }), None, None, invariant) => {
@ -2158,7 +2163,8 @@ impl Parser {
// read attributes // read attributes
let mut binding = None; let mut binding = None;
let mut stage = ParsedAttribute::default(); let mut stage = ParsedAttribute::default();
let mut workgroup_size = [0u32; 3]; let mut compute_span = Span::new(0, 0);
let mut workgroup_size = ParsedAttribute::default();
let mut early_depth_test = ParsedAttribute::default(); let mut early_depth_test = ParsedAttribute::default();
let (mut bind_index, mut bind_group) = let (mut bind_index, mut bind_group) =
(ParsedAttribute::default(), ParsedAttribute::default()); (ParsedAttribute::default(), ParsedAttribute::default());
@ -2184,11 +2190,12 @@ impl Parser {
} }
("compute", name_span) => { ("compute", name_span) => {
stage.set(crate::ShaderStage::Compute, name_span)?; stage.set(crate::ShaderStage::Compute, name_span)?;
compute_span = name_span;
} }
("workgroup_size", _) => { ("workgroup_size", name_span) => {
lexer.expect(Token::Paren('('))?; lexer.expect(Token::Paren('('))?;
workgroup_size = [1u32; 3]; let mut new_workgroup_size = [1u32; 3];
for (i, size) in workgroup_size.iter_mut().enumerate() { for (i, size) in new_workgroup_size.iter_mut().enumerate() {
*size = Self::generic_non_negative_int_literal(lexer)?; *size = Self::generic_non_negative_int_literal(lexer)?;
match lexer.next() { match lexer.next() {
(Token::Paren(')'), _) => break, (Token::Paren(')'), _) => break,
@ -2201,6 +2208,7 @@ impl Parser {
} }
} }
} }
workgroup_size.set(new_workgroup_size, name_span)?;
} }
("early_depth_test", name_span) => { ("early_depth_test", name_span) => {
let conservative = if lexer.skip(Token::Paren('(')) { let conservative = if lexer.skip(Token::Paren('(')) {
@ -2281,11 +2289,18 @@ impl Parser {
(Token::Word("fn"), _) => { (Token::Word("fn"), _) => {
let function = self.function_decl(lexer, out, &mut dependencies)?; let function = self.function_decl(lexer, out, &mut dependencies)?;
Some(ast::GlobalDeclKind::Fn(ast::Function { Some(ast::GlobalDeclKind::Fn(ast::Function {
entry_point: stage.value.map(|stage| ast::EntryPoint { entry_point: if let Some(stage) = stage.value {
stage, if stage == ShaderStage::Compute && workgroup_size.value.is_none() {
early_depth_test: early_depth_test.value, return Err(Error::MissingWorkgroupSize(compute_span));
workgroup_size, }
}), Some(ast::EntryPoint {
stage,
early_depth_test: early_depth_test.value,
workgroup_size: workgroup_size.value,
})
} else {
None
},
..function ..function
})) }))
} }

View file

@ -387,13 +387,80 @@ fn parse_expressions() {
}").unwrap(); }").unwrap();
} }
#[test]
fn binary_expression_mixed_scalar_and_vector_operands() {
for (operand, expect_splat) in [
('<', false),
('>', false),
('&', false),
('|', false),
('+', true),
('-', true),
('*', false),
('/', true),
('%', true),
] {
let module = parse_str(&format!(
"
const some_vec = vec3<f32>(1.0, 1.0, 1.0);
@fragment
fn main() -> @location(0) vec4<f32> {{
if (all(1.0 {operand} some_vec)) {{
return vec4(0.0);
}}
return vec4(1.0);
}}
"
))
.unwrap();
let expressions = &&module.entry_points[0].function.expressions;
let found_expressions = expressions
.iter()
.filter(|&(_, e)| {
if let crate::Expression::Binary { left, .. } = *e {
matches!(
(expect_splat, &expressions[left]),
(false, &crate::Expression::Literal(crate::Literal::F32(..)))
| (true, &crate::Expression::Splat { .. })
)
} else {
false
}
})
.count();
assert_eq!(found_expressions, 1);
}
let module = parse_str(
"@fragment
fn main(mat: mat3x3<f32>) {
let vec = vec3<f32>(1.0, 1.0, 1.0);
let result = mat / vec;
}",
)
.unwrap();
let expressions = &&module.entry_points[0].function.expressions;
let found_splat = expressions.iter().any(|(_, e)| {
if let crate::Expression::Binary { left, .. } = *e {
matches!(&expressions[left], &crate::Expression::Splat { .. })
} else {
false
}
});
assert!(!found_splat, "'mat / vec' should not be splatted");
}
#[test] #[test]
fn parse_pointers() { fn parse_pointers() {
parse_str( parse_str(
"fn foo() { "fn foo(a: ptr<private, f32>) -> f32 { return *a; }
fn bar() {
var x: f32 = 1.0; var x: f32 = 1.0;
let px = &x; let px = &x;
let py = frexp(0.5, px); let py = foo(px);
}", }",
) )
.unwrap(); .unwrap();
@ -534,6 +601,7 @@ fn parse_repeated_attributes() {
("size(16)", template_struct), ("size(16)", template_struct),
("vertex", template_stage), ("vertex", template_stage),
("early_depth_test(less_equal)", template_resource), ("early_depth_test(less_equal)", template_resource),
("workgroup_size(1)", template_stage),
] { ] {
let shader = template.replace("__REPLACE__", &format!("@{attribute} @{attribute}")); let shader = template.replace("__REPLACE__", &format!("@{attribute} @{attribute}"));
let name_length = attribute.rfind('(').unwrap_or(attribute.len()) as u32; let name_length = attribute.rfind('(').unwrap_or(attribute.len()) as u32;
@ -548,3 +616,18 @@ fn parse_repeated_attributes() {
)); ));
} }
} }
#[test]
fn parse_missing_workgroup_size() {
use crate::{
front::wgsl::{error::Error, Frontend},
Span,
};
let shader = "@compute fn vs() -> vec4<f32> { return vec4<f32>(0.0); }";
let result = Frontend::new().inner(shader);
assert!(matches!(
result.unwrap_err(),
Error::MissingWorkgroupSize(span) if span == Span::new(1, 8)
));
}

View file

@ -922,6 +922,8 @@ pub enum Binding {
/// [`Fragment`]: crate::ShaderStage::Fragment /// [`Fragment`]: crate::ShaderStage::Fragment
Location { Location {
location: u32, location: u32,
/// Indicates the 2nd input to the blender when dual-source blending.
second_blend_source: bool,
interpolation: Option<Interpolation>, interpolation: Option<Interpolation>,
sampling: Option<Sampling>, sampling: Option<Sampling>,
}, },
@ -1943,6 +1945,31 @@ pub struct EntryPoint {
pub function: Function, pub function: Function,
} }
/// Return types predeclared for the frexp, modf, and atomicCompareExchangeWeak built-in functions.
///
/// These cannot be spelled in WGSL source.
///
/// Stored in [`SpecialTypes::predeclared_types`] and created by [`Module::generate_predeclared_type`].
#[derive(Debug, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "clone", derive(Clone))]
#[cfg_attr(feature = "serialize", derive(Serialize))]
#[cfg_attr(feature = "deserialize", derive(Deserialize))]
#[cfg_attr(feature = "arbitrary", derive(Arbitrary))]
pub enum PredeclaredType {
AtomicCompareExchangeWeakResult {
kind: ScalarKind,
width: Bytes,
},
ModfResult {
size: Option<VectorSize>,
width: Bytes,
},
FrexpResult {
size: Option<VectorSize>,
width: Bytes,
},
}
/// Set of special types that can be optionally generated by the frontends. /// Set of special types that can be optionally generated by the frontends.
#[derive(Debug, Default)] #[derive(Debug, Default)]
#[cfg_attr(feature = "clone", derive(Clone))] #[cfg_attr(feature = "clone", derive(Clone))]
@ -1961,6 +1988,12 @@ pub struct SpecialTypes {
/// Call [`Module::generate_ray_intersection_type`] to populate /// Call [`Module::generate_ray_intersection_type`] to populate
/// this if needed and return the handle. /// this if needed and return the handle.
pub ray_intersection: Option<Handle<Type>>, pub ray_intersection: Option<Handle<Type>>,
/// Types for predeclared wgsl types instantiated on demand.
///
/// Call [`Module::generate_predeclared_type`] to populate this if
/// needed and return the handle.
pub predeclared_types: indexmap::IndexMap<PredeclaredType, Handle<Type>>,
} }
/// Shader module. /// Shader module.

View file

@ -375,8 +375,8 @@ impl super::MathFunction {
Self::Round => 1, Self::Round => 1,
Self::Fract => 1, Self::Fract => 1,
Self::Trunc => 1, Self::Trunc => 1,
Self::Modf => 2, Self::Modf => 1,
Self::Frexp => 2, Self::Frexp => 1,
Self::Ldexp => 2, Self::Ldexp => 2,
// exponent // exponent
Self::Exp => 1, Self::Exp => 1,

View file

@ -706,8 +706,6 @@ impl<'a> ResolveContext<'a> {
Mf::Round | Mf::Round |
Mf::Fract | Mf::Fract |
Mf::Trunc | Mf::Trunc |
Mf::Modf |
Mf::Frexp |
Mf::Ldexp | Mf::Ldexp |
// exponent // exponent
Mf::Exp | Mf::Exp |
@ -715,6 +713,31 @@ impl<'a> ResolveContext<'a> {
Mf::Log | Mf::Log |
Mf::Log2 | Mf::Log2 |
Mf::Pow => res_arg.clone(), Mf::Pow => res_arg.clone(),
Mf::Modf | Mf::Frexp => {
let (size, width) = match res_arg.inner_with(types) {
&Ti::Scalar {
kind: crate::ScalarKind::Float,
width,
} => (None, width),
&Ti::Vector {
kind: crate::ScalarKind::Float,
size,
width,
} => (Some(size), width),
ref other =>
return Err(ResolveError::IncompatibleOperands(format!("{fun:?}({other:?}, _)")))
};
let result = self
.special_types
.predeclared_types
.get(&if fun == Mf::Modf {
crate::PredeclaredType::ModfResult { size, width }
} else {
crate::PredeclaredType::FrexpResult { size, width }
})
.ok_or(ResolveError::MissingSpecialType)?;
TypeResolution::Handle(*result)
},
// geometry // geometry
Mf::Dot => match *res_arg.inner_with(types) { Mf::Dot => match *res_arg.inner_with(types) {
Ti::Vector { Ti::Vector {

View file

@ -256,6 +256,9 @@ pub struct FunctionInfo {
/// ///
/// [`GlobalVariable`]: crate::GlobalVariable /// [`GlobalVariable`]: crate::GlobalVariable
sampling: crate::FastHashSet<Sampling>, sampling: crate::FastHashSet<Sampling>,
/// Indicates that the function is using dual source blending.
pub dual_source_blending: bool,
} }
impl FunctionInfo { impl FunctionInfo {
@ -999,6 +1002,7 @@ impl ModuleInfo {
global_uses: vec![GlobalUse::empty(); module.global_variables.len()].into_boxed_slice(), global_uses: vec![GlobalUse::empty(); module.global_variables.len()].into_boxed_slice(),
expressions: vec![ExpressionInfo::new(); fun.expressions.len()].into_boxed_slice(), expressions: vec![ExpressionInfo::new(); fun.expressions.len()].into_boxed_slice(),
sampling: crate::FastHashSet::default(), sampling: crate::FastHashSet::default(),
dual_source_blending: false,
}; };
let resolve_context = let resolve_context =
ResolveContext::with_locals(module, &fun.local_variables, &fun.arguments); ResolveContext::with_locals(module, &fun.local_variables, &fun.arguments);
@ -1108,6 +1112,7 @@ fn uniform_control_flow() {
global_uses: vec![GlobalUse::empty(); global_var_arena.len()].into_boxed_slice(), global_uses: vec![GlobalUse::empty(); global_var_arena.len()].into_boxed_slice(),
expressions: vec![ExpressionInfo::new(); expressions.len()].into_boxed_slice(), expressions: vec![ExpressionInfo::new(); expressions.len()].into_boxed_slice(),
sampling: crate::FastHashSet::default(), sampling: crate::FastHashSet::default(),
dual_source_blending: false,
}; };
let resolve_context = ResolveContext { let resolve_context = ResolveContext {
constants: &Arena::new(), constants: &Arena::new(),

View file

@ -893,7 +893,7 @@ impl super::Validator {
let arg3_ty = arg3.map(resolve); let arg3_ty = arg3.map(resolve);
match fun { match fun {
Mf::Abs => { Mf::Abs => {
if arg1_ty.is_some() | arg2_ty.is_some() | arg3_ty.is_some() { if arg1_ty.is_some() || arg2_ty.is_some() || arg3_ty.is_some() {
return Err(ExpressionError::WrongArgumentCount(fun)); return Err(ExpressionError::WrongArgumentCount(fun));
} }
let good = match *arg_ty { let good = match *arg_ty {
@ -976,10 +976,9 @@ impl super::Validator {
| Mf::Log | Mf::Log
| Mf::Log2 | Mf::Log2
| Mf::Length | Mf::Length
| Mf::Sign
| Mf::Sqrt | Mf::Sqrt
| Mf::InverseSqrt => { | Mf::InverseSqrt => {
if arg1_ty.is_some() | arg2_ty.is_some() | arg3_ty.is_some() { if arg1_ty.is_some() || arg2_ty.is_some() || arg3_ty.is_some() {
return Err(ExpressionError::WrongArgumentCount(fun)); return Err(ExpressionError::WrongArgumentCount(fun));
} }
match *arg_ty { match *arg_ty {
@ -992,6 +991,22 @@ impl super::Validator {
_ => return Err(ExpressionError::InvalidArgumentType(fun, 0, arg)), _ => return Err(ExpressionError::InvalidArgumentType(fun, 0, arg)),
} }
} }
Mf::Sign => {
if arg1_ty.is_some() || arg2_ty.is_some() || arg3_ty.is_some() {
return Err(ExpressionError::WrongArgumentCount(fun));
}
match *arg_ty {
Ti::Scalar {
kind: Sk::Float | Sk::Sint,
..
}
| Ti::Vector {
kind: Sk::Float | Sk::Sint,
..
} => {}
_ => return Err(ExpressionError::InvalidArgumentType(fun, 0, arg)),
}
}
Mf::Atan2 | Mf::Pow | Mf::Distance | Mf::Step => { Mf::Atan2 | Mf::Pow | Mf::Distance | Mf::Step => {
let arg1_ty = match (arg1_ty, arg2_ty, arg3_ty) { let arg1_ty = match (arg1_ty, arg2_ty, arg3_ty) {
(Some(ty1), None, None) => ty1, (Some(ty1), None, None) => ty1,
@ -1014,31 +1029,48 @@ impl super::Validator {
)); ));
} }
} }
Mf::Modf | Mf::Frexp | Mf::Ldexp => { Mf::Modf | Mf::Frexp => {
if arg1_ty.is_some() || arg2_ty.is_some() || arg3_ty.is_some() {
return Err(ExpressionError::WrongArgumentCount(fun));
}
if !matches!(
*arg_ty,
Ti::Scalar {
kind: Sk::Float,
..
} | Ti::Vector {
kind: Sk::Float,
..
},
) {
return Err(ExpressionError::InvalidArgumentType(fun, 1, arg));
}
}
Mf::Ldexp => {
let arg1_ty = match (arg1_ty, arg2_ty, arg3_ty) { let arg1_ty = match (arg1_ty, arg2_ty, arg3_ty) {
(Some(ty1), None, None) => ty1, (Some(ty1), None, None) => ty1,
_ => return Err(ExpressionError::WrongArgumentCount(fun)), _ => return Err(ExpressionError::WrongArgumentCount(fun)),
}; };
let (size0, width0) = match *arg_ty { let size0 = match *arg_ty {
Ti::Scalar { Ti::Scalar {
kind: Sk::Float, kind: Sk::Float, ..
width, } => None,
} => (None, width),
Ti::Vector { Ti::Vector {
kind: Sk::Float, kind: Sk::Float,
size, size,
width, ..
} => (Some(size), width), } => Some(size),
_ => return Err(ExpressionError::InvalidArgumentType(fun, 0, arg)), _ => {
return Err(ExpressionError::InvalidArgumentType(fun, 0, arg));
}
}; };
let good = match *arg1_ty { let good = match *arg1_ty {
Ti::Pointer { base, space: _ } => module.types[base].inner == *arg_ty, Ti::Scalar { kind: Sk::Sint, .. } if size0.is_none() => true,
Ti::ValuePointer { Ti::Vector {
size, size,
kind: Sk::Float, kind: Sk::Sint,
width, ..
space: _, } if Some(size) == size0 => true,
} => size == size0 && width == width0,
_ => false, _ => false,
}; };
if !good { if !good {
@ -1130,7 +1162,7 @@ impl super::Validator {
} }
} }
Mf::Normalize => { Mf::Normalize => {
if arg1_ty.is_some() | arg2_ty.is_some() | arg3_ty.is_some() { if arg1_ty.is_some() || arg2_ty.is_some() || arg3_ty.is_some() {
return Err(ExpressionError::WrongArgumentCount(fun)); return Err(ExpressionError::WrongArgumentCount(fun));
} }
match *arg_ty { match *arg_ty {
@ -1210,7 +1242,7 @@ impl super::Validator {
} }
} }
Mf::Inverse | Mf::Determinant => { Mf::Inverse | Mf::Determinant => {
if arg1_ty.is_some() | arg2_ty.is_some() | arg3_ty.is_some() { if arg1_ty.is_some() || arg2_ty.is_some() || arg3_ty.is_some() {
return Err(ExpressionError::WrongArgumentCount(fun)); return Err(ExpressionError::WrongArgumentCount(fun));
} }
let good = match *arg_ty { let good = match *arg_ty {
@ -1222,7 +1254,7 @@ impl super::Validator {
} }
} }
Mf::Transpose => { Mf::Transpose => {
if arg1_ty.is_some() | arg2_ty.is_some() | arg3_ty.is_some() { if arg1_ty.is_some() || arg2_ty.is_some() || arg3_ty.is_some() {
return Err(ExpressionError::WrongArgumentCount(fun)); return Err(ExpressionError::WrongArgumentCount(fun));
} }
match *arg_ty { match *arg_ty {
@ -1236,7 +1268,7 @@ impl super::Validator {
| Mf::ReverseBits | Mf::ReverseBits
| Mf::FindLsb | Mf::FindLsb
| Mf::FindMsb => { | Mf::FindMsb => {
if arg1_ty.is_some() | arg2_ty.is_some() | arg3_ty.is_some() { if arg1_ty.is_some() || arg2_ty.is_some() || arg3_ty.is_some() {
return Err(ExpressionError::WrongArgumentCount(fun)); return Err(ExpressionError::WrongArgumentCount(fun));
} }
match *arg_ty { match *arg_ty {
@ -1333,7 +1365,7 @@ impl super::Validator {
} }
} }
Mf::Pack2x16unorm | Mf::Pack2x16snorm | Mf::Pack2x16float => { Mf::Pack2x16unorm | Mf::Pack2x16snorm | Mf::Pack2x16float => {
if arg1_ty.is_some() | arg2_ty.is_some() | arg3_ty.is_some() { if arg1_ty.is_some() || arg2_ty.is_some() || arg3_ty.is_some() {
return Err(ExpressionError::WrongArgumentCount(fun)); return Err(ExpressionError::WrongArgumentCount(fun));
} }
match *arg_ty { match *arg_ty {
@ -1346,7 +1378,7 @@ impl super::Validator {
} }
} }
Mf::Pack4x8snorm | Mf::Pack4x8unorm => { Mf::Pack4x8snorm | Mf::Pack4x8unorm => {
if arg1_ty.is_some() | arg2_ty.is_some() | arg3_ty.is_some() { if arg1_ty.is_some() || arg2_ty.is_some() || arg3_ty.is_some() {
return Err(ExpressionError::WrongArgumentCount(fun)); return Err(ExpressionError::WrongArgumentCount(fun));
} }
match *arg_ty { match *arg_ty {
@ -1363,7 +1395,7 @@ impl super::Validator {
| Mf::Unpack2x16unorm | Mf::Unpack2x16unorm
| Mf::Unpack4x8snorm | Mf::Unpack4x8snorm
| Mf::Unpack4x8unorm => { | Mf::Unpack4x8unorm => {
if arg1_ty.is_some() | arg2_ty.is_some() | arg3_ty.is_some() { if arg1_ty.is_some() || arg2_ty.is_some() || arg3_ty.is_some() {
return Err(ExpressionError::WrongArgumentCount(fun)); return Err(ExpressionError::WrongArgumentCount(fun));
} }
match *arg_ty { match *arg_ty {

View file

@ -61,6 +61,17 @@ pub enum VaryingError {
DuplicateBuiltIn(crate::BuiltIn), DuplicateBuiltIn(crate::BuiltIn),
#[error("Capability {0:?} is not supported")] #[error("Capability {0:?} is not supported")]
UnsupportedCapability(Capabilities), UnsupportedCapability(Capabilities),
#[error("The attribute {0:?} is only valid as an output for stage {1:?}")]
InvalidInputAttributeInStage(&'static str, crate::ShaderStage),
#[error("The attribute {0:?} is not valid for stage {1:?}")]
InvalidAttributeInStage(&'static str, crate::ShaderStage),
#[error(
"The location index {location} cannot be used together with the attribute {attribute:?}"
)]
InvalidLocationAttributeCombination {
location: u32,
attribute: &'static str,
},
} }
#[derive(Clone, Debug, thiserror::Error)] #[derive(Clone, Debug, thiserror::Error)]
@ -89,6 +100,10 @@ pub enum EntryPointError {
InvalidIntegerInterpolation { location: u32 }, InvalidIntegerInterpolation { location: u32 },
#[error(transparent)] #[error(transparent)]
Function(#[from] FunctionError), Function(#[from] FunctionError),
#[error(
"Invalid locations {location_mask:?} are set while dual source blending. Only location 0 may be set."
)]
InvalidLocationsWhileDualSourceBlending { location_mask: BitSet },
} }
#[cfg(feature = "validate")] #[cfg(feature = "validate")]
@ -106,6 +121,7 @@ fn storage_usage(access: crate::StorageAccess) -> GlobalUse {
struct VaryingContext<'a> { struct VaryingContext<'a> {
stage: crate::ShaderStage, stage: crate::ShaderStage,
output: bool, output: bool,
second_blend_source: bool,
types: &'a UniqueArena<crate::Type>, types: &'a UniqueArena<crate::Type>,
type_info: &'a Vec<super::r#type::TypeInfo>, type_info: &'a Vec<super::r#type::TypeInfo>,
location_mask: &'a mut BitSet, location_mask: &'a mut BitSet,
@ -293,6 +309,7 @@ impl VaryingContext<'_> {
location, location,
interpolation, interpolation,
sampling, sampling,
second_blend_source,
} => { } => {
// Only IO-shareable types may be stored in locations. // Only IO-shareable types may be stored in locations.
if !self.type_info[ty.index()] if !self.type_info[ty.index()]
@ -301,7 +318,37 @@ impl VaryingContext<'_> {
{ {
return Err(VaryingError::NotIOShareableType(ty)); return Err(VaryingError::NotIOShareableType(ty));
} }
if !self.location_mask.insert(location as usize) {
if second_blend_source {
if !self
.capabilities
.contains(Capabilities::DUAL_SOURCE_BLENDING)
{
return Err(VaryingError::UnsupportedCapability(
Capabilities::DUAL_SOURCE_BLENDING,
));
}
if self.stage != crate::ShaderStage::Fragment {
return Err(VaryingError::InvalidAttributeInStage(
"second_blend_source",
self.stage,
));
}
if !self.output {
return Err(VaryingError::InvalidInputAttributeInStage(
"second_blend_source",
self.stage,
));
}
if location != 0 {
return Err(VaryingError::InvalidLocationAttributeCombination {
location,
attribute: "second_blend_source",
});
}
self.second_blend_source = true;
} else if !self.location_mask.insert(location as usize) {
#[cfg(feature = "validate")] #[cfg(feature = "validate")]
if self.flags.contains(super::ValidationFlags::BINDINGS) { if self.flags.contains(super::ValidationFlags::BINDINGS) {
return Err(VaryingError::BindingCollision { location }); return Err(VaryingError::BindingCollision { location });
@ -567,7 +614,8 @@ impl super::Validator {
return Err(EntryPointError::UnexpectedWorkgroupSize.with_span()); return Err(EntryPointError::UnexpectedWorkgroupSize.with_span());
} }
let info = self #[cfg_attr(not(feature = "validate"), allow(unused_mut))]
let mut info = self
.validate_function(&ep.function, module, mod_info, true) .validate_function(&ep.function, module, mod_info, true)
.map_err(WithSpan::into_other)?; .map_err(WithSpan::into_other)?;
@ -593,6 +641,7 @@ impl super::Validator {
let mut ctx = VaryingContext { let mut ctx = VaryingContext {
stage: ep.stage, stage: ep.stage,
output: false, output: false,
second_blend_source: false,
types: &module.types, types: &module.types,
type_info: &self.types, type_info: &self.types,
location_mask: &mut self.location_mask, location_mask: &mut self.location_mask,
@ -612,6 +661,7 @@ impl super::Validator {
let mut ctx = VaryingContext { let mut ctx = VaryingContext {
stage: ep.stage, stage: ep.stage,
output: true, output: true,
second_blend_source: false,
types: &module.types, types: &module.types,
type_info: &self.types, type_info: &self.types,
location_mask: &mut self.location_mask, location_mask: &mut self.location_mask,
@ -623,6 +673,18 @@ impl super::Validator {
}; };
ctx.validate(fr.ty, fr.binding.as_ref()) ctx.validate(fr.ty, fr.binding.as_ref())
.map_err_inner(|e| EntryPointError::Result(e).with_span())?; .map_err_inner(|e| EntryPointError::Result(e).with_span())?;
#[cfg(feature = "validate")]
if ctx.second_blend_source {
// Only the first location may be used whhen dual source blending
if ctx.location_mask.len() == 1 && ctx.location_mask.contains(0) {
info.dual_source_blending = true;
} else {
return Err(EntryPointError::InvalidLocationsWhileDualSourceBlending {
location_mask: self.location_mask.clone(),
}
.with_span());
}
}
#[cfg(feature = "validate")] #[cfg(feature = "validate")]
if ep.stage == crate::ShaderStage::Vertex if ep.stage == crate::ShaderStage::Vertex

View file

@ -112,6 +112,8 @@ bitflags::bitflags! {
const MULTISAMPLED_SHADING = 0x800; const MULTISAMPLED_SHADING = 0x800;
/// Support for ray queries and acceleration structures. /// Support for ray queries and acceleration structures.
const RAY_QUERY = 0x1000; const RAY_QUERY = 0x1000;
/// Support for generating two sources for blending from fragement shaders
const DUAL_SOURCE_BLENDING = 0x2000;
} }
} }

View file

@ -1 +1 @@
{"files":{"Cargo.toml":"dd1ecc9d26c0bac60aafeb9e404fd076025cc5f4a5bc405adfa08d2ca38608ac","LICENSE.APACHE":"a6cba85bc92e0cff7a450b1d873c0eaa2e9fc96bf472df0247a26bec77bf3ff9","LICENSE.MIT":"c7fea58d1cfe49634cd92e54fc10a9d871f4b275321a4cd8c09e449122caaeb4","src/binding_model.rs":"3322f03854b92abeb4aeb65dda4ef776094713bce1277047fe85be73a9bc7b4e","src/command/bind.rs":"c243a4448b87e9b7274b10873b7091b385413ec0a4ea93cccd6d612214ec9abb","src/command/bundle.rs":"b26eb6cb877a19d203e9d2b8ac3b10e81f6a94b8b68617eac97a3b861cbe102b","src/command/clear.rs":"418ac36738d782ab72e8700aabef802638cdef1c873f4b036aa8a4c521cb9caf","src/command/compute.rs":"0f8492bdfddb58c413282b2640b1f2a2b934ee256106f661241ac144bfd63d28","src/command/draw.rs":"92facdd0e3fd553af590ecbc0de3491f212e237ea66494ff99f67dbf090d10df","src/command/memory_init.rs":"b50d3d20dbf659052f19da2e79469ba6435e06370f19d6ef45e1b1128d9900b7","src/command/mod.rs":"a63937a5b8a441b714329a5e1b0b4dd4b70ae2f39b52da633fd79edbe183edc6","src/command/query.rs":"d39e1b8cb6a054fd31333a916da5d79a6671a724212c90c490c13e55043a1685","src/command/render.rs":"e98d109e2fe3651a96f74cf1c40a2cc9c752bcf9ea8e893285d7fcad634724dc","src/command/transfer.rs":"c777c6e51afb459d2b5416e31071f24e4215c66f456fee3bd8f7395f9d1c5db1","src/conv.rs":"da95b36b7680ae74ebf810ad8f1decf01bd3eeaff44b3c5af1d4b3c3f0e2059a","src/device/global.rs":"5d7e852465ed0060f2cc5e6aee5831f9e7619cca492be73de1523fba4a83e668","src/device/life.rs":"4afecaf3602e23a4d8c795c29b9e5e148866e728b008884d55f658de29af4fe9","src/device/mod.rs":"dbb98ce046651fd70a26df2030913f7224557eceb5f84198daf433c266356c7c","src/device/queue.rs":"d2692b5c12d05dfaf325d437b5bffe069cfff43228ce40f25147cec2cda428ba","src/device/resource.rs":"ef5ef881a2cf1037d6f5b6bf3b497a9826707ceee00421873c4cf32f0e5c4510","src/device/trace.rs":"21408dfd2c99e3ce36a77d08ba86cf52f32bb376ed82690bbbf74937bfd42cbe","src/error.rs":"ca37282283985e2b7d184b2ab7ca6f53f726432d920f8d8477bfff6fab9b34e2","src/global.rs":"cf551de97c3eb5acd0c2710da09ebd92cc863ad0bb0f53c0fd4911bf8cd3ad97","src/hal_api.rs":"92a2f0cb80f192693530ed61048919bbad446742c2370bf0944c44b1c5df8362","src/hub.rs":"48ccada54672a88169c23975ddc3758cd32ed5de577c55ca4f665d0ed17d3233","src/id.rs":"f6245d024586c7fe63ded13b3cb926b940c191bbee56aedc655e8cef74bdd66b","src/identity.rs":"3ce6a3b57c7c4fc0808d13cd342d928c214f32368e45c79d8e2bbf8df887f97f","src/init_tracker/buffer.rs":"a0ebf54a1e6d269c7b4aa0ac7bb8b04fd2cea3221a1d058ff33cb683b2aea3e9","src/init_tracker/mod.rs":"0867f79f83555390d0982d1dc6dcf0d4340e10cb89aa633d3c3ecc45deb3c78c","src/init_tracker/texture.rs":"37b6584aaca11c407d91f77002dcbb48d8a4876e27edd1b71b7929ef966f901d","src/instance.rs":"18c53b6ff12237b794dd6d6e5ff9c6f879dcc7db9dce535b3b64862090b6ef55","src/lib.rs":"27ff8dd787d41cf412e90d0c4674aa70db59e608f9eb3be485c0bd18e9f13369","src/pipeline.rs":"669219add15448fdf5fe8bc5e03fd6fd1ada2b45b07047fd8c0a9bbbcdecad8b","src/present.rs":"afbc762a6103d1076670ab2ad1270f5a2b8448c89196ecf36348c7ab6929e1ee","src/registry.rs":"4098413de7f48e9ff15d0246793be47a0d54c95b4c8594baf9fafd222a90ba84","src/resource.rs":"7d1d841dd185a1a857814cab424a8b892aa8731dddf3373ea9436fa619d655b7","src/storage.rs":"bc70689ba299e9b4d9f4992c4d3f4dd36b1d8e71327595094981fdfd624f811a","src/track/buffer.rs":"dd6f632c6f31b15807148d705c516a8a1a8d72d02b137dd3b9d7c939447917cb","src/track/metadata.rs":"a80bd086ce825f7484ce6318a586c482d06fea0efc9c76bfa0124e480cc8b75e","src/track/mod.rs":"42b791d9a41eb6e62f6d79cae7abb5ab523eeb9e6030b0f95bbb0e26d56ad0ec","src/track/range.rs":"5bbfed6e103b3234d9de8e42057022da6d628c2cc1db6bb51b88f87f2d8adf8b","src/track/stateless.rs":"1d786b5e9558672243ba7d913736561065ef2bd5c6105c935e982486d10841f0","src/track/texture.rs":"7d60dc81ba7f7e2c2819525b90e6e6c7760cb0920e36aeefe98e76cedd49d26e","src/validation.rs":"6be9229cc34ef293ae99cfa520c6ad22bab39b83a53880a4aca36c42c53d40c3"},"package":null} {"files":{"Cargo.toml":"7e5c00225497db0d65e2d91d19645c708402165126f655d26b77375ed70077af","LICENSE.APACHE":"a6cba85bc92e0cff7a450b1d873c0eaa2e9fc96bf472df0247a26bec77bf3ff9","LICENSE.MIT":"c7fea58d1cfe49634cd92e54fc10a9d871f4b275321a4cd8c09e449122caaeb4","src/binding_model.rs":"3322f03854b92abeb4aeb65dda4ef776094713bce1277047fe85be73a9bc7b4e","src/command/bind.rs":"c243a4448b87e9b7274b10873b7091b385413ec0a4ea93cccd6d612214ec9abb","src/command/bundle.rs":"b26eb6cb877a19d203e9d2b8ac3b10e81f6a94b8b68617eac97a3b861cbe102b","src/command/clear.rs":"418ac36738d782ab72e8700aabef802638cdef1c873f4b036aa8a4c521cb9caf","src/command/compute.rs":"0f8492bdfddb58c413282b2640b1f2a2b934ee256106f661241ac144bfd63d28","src/command/draw.rs":"92facdd0e3fd553af590ecbc0de3491f212e237ea66494ff99f67dbf090d10df","src/command/memory_init.rs":"b50d3d20dbf659052f19da2e79469ba6435e06370f19d6ef45e1b1128d9900b7","src/command/mod.rs":"a63937a5b8a441b714329a5e1b0b4dd4b70ae2f39b52da633fd79edbe183edc6","src/command/query.rs":"d39e1b8cb6a054fd31333a916da5d79a6671a724212c90c490c13e55043a1685","src/command/render.rs":"e98d109e2fe3651a96f74cf1c40a2cc9c752bcf9ea8e893285d7fcad634724dc","src/command/transfer.rs":"c777c6e51afb459d2b5416e31071f24e4215c66f456fee3bd8f7395f9d1c5db1","src/conv.rs":"da95b36b7680ae74ebf810ad8f1decf01bd3eeaff44b3c5af1d4b3c3f0e2059a","src/device/global.rs":"84ca385802eba1c119b571a30885c59ae1a4056438d6f69b13a837e06b2cd584","src/device/life.rs":"4afecaf3602e23a4d8c795c29b9e5e148866e728b008884d55f658de29af4fe9","src/device/mod.rs":"5ac7886bc8b2054c52dfb5955fe7b72fc9bf725b08b5e39121d6a952aa4f8f2b","src/device/queue.rs":"d2692b5c12d05dfaf325d437b5bffe069cfff43228ce40f25147cec2cda428ba","src/device/resource.rs":"ef5ef881a2cf1037d6f5b6bf3b497a9826707ceee00421873c4cf32f0e5c4510","src/device/trace.rs":"21408dfd2c99e3ce36a77d08ba86cf52f32bb376ed82690bbbf74937bfd42cbe","src/error.rs":"ca37282283985e2b7d184b2ab7ca6f53f726432d920f8d8477bfff6fab9b34e2","src/global.rs":"cf551de97c3eb5acd0c2710da09ebd92cc863ad0bb0f53c0fd4911bf8cd3ad97","src/hal_api.rs":"92a2f0cb80f192693530ed61048919bbad446742c2370bf0944c44b1c5df8362","src/hub.rs":"48ccada54672a88169c23975ddc3758cd32ed5de577c55ca4f665d0ed17d3233","src/id.rs":"f6245d024586c7fe63ded13b3cb926b940c191bbee56aedc655e8cef74bdd66b","src/identity.rs":"3ce6a3b57c7c4fc0808d13cd342d928c214f32368e45c79d8e2bbf8df887f97f","src/init_tracker/buffer.rs":"a0ebf54a1e6d269c7b4aa0ac7bb8b04fd2cea3221a1d058ff33cb683b2aea3e9","src/init_tracker/mod.rs":"0867f79f83555390d0982d1dc6dcf0d4340e10cb89aa633d3c3ecc45deb3c78c","src/init_tracker/texture.rs":"37b6584aaca11c407d91f77002dcbb48d8a4876e27edd1b71b7929ef966f901d","src/instance.rs":"2798b83772dd82a5461901cfca0b91c8b101bfcdf96095091dc59d1bd4b69cc1","src/lib.rs":"27ff8dd787d41cf412e90d0c4674aa70db59e608f9eb3be485c0bd18e9f13369","src/pipeline.rs":"669219add15448fdf5fe8bc5e03fd6fd1ada2b45b07047fd8c0a9bbbcdecad8b","src/present.rs":"b17400ba823e2fce9438947d3dfbfd34045f959666c6718286112a3f3925e387","src/registry.rs":"4098413de7f48e9ff15d0246793be47a0d54c95b4c8594baf9fafd222a90ba84","src/resource.rs":"7d1d841dd185a1a857814cab424a8b892aa8731dddf3373ea9436fa619d655b7","src/storage.rs":"bc70689ba299e9b4d9f4992c4d3f4dd36b1d8e71327595094981fdfd624f811a","src/track/buffer.rs":"dd6f632c6f31b15807148d705c516a8a1a8d72d02b137dd3b9d7c939447917cb","src/track/metadata.rs":"a80bd086ce825f7484ce6318a586c482d06fea0efc9c76bfa0124e480cc8b75e","src/track/mod.rs":"42b791d9a41eb6e62f6d79cae7abb5ab523eeb9e6030b0f95bbb0e26d56ad0ec","src/track/range.rs":"5bbfed6e103b3234d9de8e42057022da6d628c2cc1db6bb51b88f87f2d8adf8b","src/track/stateless.rs":"1d786b5e9558672243ba7d913736561065ef2bd5c6105c935e982486d10841f0","src/track/texture.rs":"7d60dc81ba7f7e2c2819525b90e6e6c7760cb0920e36aeefe98e76cedd49d26e","src/validation.rs":"66ee194d095b7fc54e0545f8b848120eb42d458a50beea67301ff3e5c88a4b3c"},"package":null}

View file

@ -55,7 +55,7 @@ package = "wgpu-hal"
[dependencies.naga] [dependencies.naga]
version = "0.13.0" version = "0.13.0"
git = "https://github.com/gfx-rs/naga" git = "https://github.com/gfx-rs/naga"
rev = "7a19f3af909202c7eafd36633b5584bfbb353ecb" rev = "cc87b8f9eb30bb55d0735b89d3df3e099e1a6e7c"
features = [ features = [
"clone", "clone",
"span", "span",

View file

@ -2134,7 +2134,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
let (mut surface_guard, mut token) = self.surfaces.write(&mut token); let (mut surface_guard, mut token) = self.surfaces.write(&mut token);
let (adapter_guard, mut token) = hub.adapters.read(&mut token); let (adapter_guard, mut token) = hub.adapters.read(&mut token);
let (device_guard, _token) = hub.devices.read(&mut token); let (device_guard, mut token) = hub.devices.read(&mut token);
let error = 'outer: loop { let error = 'outer: loop {
let device = match device_guard.get(device_id) { let device = match device_guard.get(device_id) {
@ -2207,6 +2207,24 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
break error; break error;
} }
// Wait for all work to finish before configuring the surface.
if let Err(e) = device.maintain(hub, wgt::Maintain::Wait, &mut token) {
break e.into();
}
// All textures must be destroyed before the surface can be re-configured.
if let Some(present) = surface.presentation.take() {
if present.acquired_texture.is_some() {
break E::PreviousOutputExists;
}
}
// TODO: Texture views may still be alive that point to the texture.
// this will allow the user to render to the surface texture, long after
// it has been removed.
//
// https://github.com/gfx-rs/wgpu/issues/4105
match unsafe { match unsafe {
A::get_surface_mut(surface) A::get_surface_mut(surface)
.unwrap() .unwrap()
@ -2226,12 +2244,6 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
} }
} }
if let Some(present) = surface.presentation.take() {
if present.acquired_texture.is_some() {
break E::PreviousOutputExists;
}
}
surface.presentation = Some(present::Presentation { surface.presentation = Some(present::Presentation {
device_id: Stored { device_id: Stored {
value: id::Valid(device_id), value: id::Valid(device_id),

View file

@ -1,6 +1,5 @@
use crate::{ use crate::{
binding_model, binding_model,
device::life::WaitIdleError,
hal_api::HalApi, hal_api::HalApi,
hub::Hub, hub::Hub,
id, id,
@ -24,7 +23,7 @@ pub mod queue;
pub mod resource; pub mod resource;
#[cfg(any(feature = "trace", feature = "replay"))] #[cfg(any(feature = "trace", feature = "replay"))]
pub mod trace; pub mod trace;
pub use resource::Device; pub use {life::WaitIdleError, resource::Device};
pub const SHADER_STAGE_COUNT: usize = 3; pub const SHADER_STAGE_COUNT: usize = 3;
// Should be large enough for the largest possible texture row. This // Should be large enough for the largest possible texture row. This

View file

@ -84,8 +84,22 @@ impl Instance {
dx12_shader_compiler: instance_desc.dx12_shader_compiler.clone(), dx12_shader_compiler: instance_desc.dx12_shader_compiler.clone(),
gles_minor_version: instance_desc.gles_minor_version, gles_minor_version: instance_desc.gles_minor_version,
}; };
unsafe { hal::Instance::init(&hal_desc).ok() } match unsafe { hal::Instance::init(&hal_desc) } {
Ok(instance) => {
log::debug!("Instance::new: created {:?} backend", A::VARIANT);
Some(instance)
}
Err(err) => {
log::debug!(
"Instance::new: failed to create {:?} backend: {:?}",
A::VARIANT,
err
);
None
}
}
} else { } else {
log::trace!("Instance::new: backend {:?} not requested", A::VARIANT);
None None
} }
} }

View file

@ -15,7 +15,7 @@ use std::borrow::Borrow;
use crate::device::trace::Action; use crate::device::trace::Action;
use crate::{ use crate::{
conv, conv,
device::{DeviceError, MissingDownlevelFlags}, device::{DeviceError, MissingDownlevelFlags, WaitIdleError},
global::Global, global::Global,
hal_api::HalApi, hal_api::HalApi,
hub::Token, hub::Token,
@ -96,6 +96,18 @@ pub enum ConfigureSurfaceError {
}, },
#[error("Requested usage is not supported")] #[error("Requested usage is not supported")]
UnsupportedUsage, UnsupportedUsage,
#[error("Gpu got stuck :(")]
StuckGpu,
}
impl From<WaitIdleError> for ConfigureSurfaceError {
fn from(e: WaitIdleError) -> Self {
match e {
WaitIdleError::Device(d) => ConfigureSurfaceError::Device(d),
WaitIdleError::WrongSubmissionIndex(..) => unreachable!(),
WaitIdleError::StuckGpu => ConfigureSurfaceError::StuckGpu,
}
}
} }
#[repr(C)] #[repr(C)]

View file

@ -812,6 +812,7 @@ impl Interface {
location, location,
interpolation, interpolation,
sampling, sampling,
.. // second_blend_source
}) => Varying::Local { }) => Varying::Local {
location, location,
iv: InterfaceVar { iv: InterfaceVar {

File diff suppressed because one or more lines are too long

View file

@ -64,7 +64,7 @@ optional = true
[dependencies.naga] [dependencies.naga]
version = "0.13.0" version = "0.13.0"
git = "https://github.com/gfx-rs/naga" git = "https://github.com/gfx-rs/naga"
rev = "7a19f3af909202c7eafd36633b5584bfbb353ecb" rev = "cc87b8f9eb30bb55d0735b89d3df3e099e1a6e7c"
features = ["clone"] features = ["clone"]
[dependencies.profiling] [dependencies.profiling]
@ -83,7 +83,7 @@ env_logger = "0.10"
[dev-dependencies.naga] [dev-dependencies.naga]
version = "0.13.0" version = "0.13.0"
git = "https://github.com/gfx-rs/naga" git = "https://github.com/gfx-rs/naga"
rev = "7a19f3af909202c7eafd36633b5584bfbb353ecb" rev = "cc87b8f9eb30bb55d0735b89d3df3e099e1a6e7c"
features = ["wgsl-in"] features = ["wgsl-in"]
[dev-dependencies.winit] [dev-dependencies.winit]

View file

@ -181,7 +181,10 @@ impl super::Device {
}) })
} }
pub(super) unsafe fn wait_idle(&self) -> Result<(), crate::DeviceError> { // Blocks until the dedicated present queue is finished with all of its work.
//
// Once this method completes, the surface is able to be resized or deleted.
pub(super) unsafe fn wait_for_present_queue_idle(&self) -> Result<(), crate::DeviceError> {
let cur_value = self.idler.fence.get_value(); let cur_value = self.idler.fence.get_value();
if cur_value == !0 { if cur_value == !0 {
return Err(crate::DeviceError::Lost); return Err(crate::DeviceError::Lost);

View file

@ -613,19 +613,23 @@ impl crate::Surface<Api> for Surface {
let mut flags = dxgi::DXGI_SWAP_CHAIN_FLAG_FRAME_LATENCY_WAITABLE_OBJECT; let mut flags = dxgi::DXGI_SWAP_CHAIN_FLAG_FRAME_LATENCY_WAITABLE_OBJECT;
// We always set ALLOW_TEARING on the swapchain no matter // We always set ALLOW_TEARING on the swapchain no matter
// what kind of swapchain we want because ResizeBuffers // what kind of swapchain we want because ResizeBuffers
// cannot change if ALLOW_TEARING is applied to the swapchain. // cannot change the swapchain's ALLOW_TEARING flag.
//
// This does not change the behavior of the swapchain, just
// allow present calls to use tearing.
if self.supports_allow_tearing { if self.supports_allow_tearing {
flags |= dxgi::DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING; flags |= dxgi::DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING;
} }
// While `configure`s contract ensures that no work on the GPU's main queues
// are in flight, we still need to wait for the present queue to be idle.
unsafe { device.wait_for_present_queue_idle() }?;
let non_srgb_format = auxil::dxgi::conv::map_texture_format_nosrgb(config.format); let non_srgb_format = auxil::dxgi::conv::map_texture_format_nosrgb(config.format);
let swap_chain = match self.swap_chain.take() { let swap_chain = match self.swap_chain.take() {
//Note: this path doesn't properly re-initialize all of the things //Note: this path doesn't properly re-initialize all of the things
Some(sc) => { Some(sc) => {
// can't have image resources in flight used by GPU
let _ = unsafe { device.wait_idle() };
let raw = unsafe { sc.release_resources() }; let raw = unsafe { sc.release_resources() };
let result = unsafe { let result = unsafe {
raw.ResizeBuffers( raw.ResizeBuffers(
@ -773,12 +777,16 @@ impl crate::Surface<Api> for Surface {
} }
unsafe fn unconfigure(&mut self, device: &Device) { unsafe fn unconfigure(&mut self, device: &Device) {
if let Some(mut sc) = self.swap_chain.take() { if let Some(sc) = self.swap_chain.take() {
unsafe { unsafe {
let _ = sc.wait(None); // While `unconfigure`s contract ensures that no work on the GPU's main queues
//TODO: this shouldn't be needed, // are in flight, we still need to wait for the present queue to be idle.
// but it complains that the queue is still used otherwise
let _ = device.wait_idle(); // The major failure mode of this function is device loss,
// which if we have lost the device, we should just continue
// cleaning up, without error.
let _ = device.wait_for_present_queue_idle();
let _raw = sc.release_resources(); let _raw = sc.release_resources();
} }
} }
@ -837,6 +845,13 @@ impl crate::Queue<Api> for Queue {
.signal(&fence.raw, value) .signal(&fence.raw, value)
.into_device_result("Signal fence")?; .into_device_result("Signal fence")?;
} }
// Note the lack of synchronization here between the main Direct queue
// and the dedicated presentation queue. This is automatically handled
// by the D3D runtime by detecting uses of resources derived from the
// swapchain. This automatic detection is why you cannot use a swapchain
// as an UAV in D3D12.
Ok(()) Ok(())
} }
unsafe fn present( unsafe fn present(

View file

@ -227,12 +227,28 @@ pub trait Instance<A: Api>: Sized + WasmNotSend + WasmNotSync {
} }
pub trait Surface<A: Api>: WasmNotSend + WasmNotSync { pub trait Surface<A: Api>: WasmNotSend + WasmNotSync {
/// Configures the surface to use the given device.
///
/// # Safety
///
/// - All gpu work that uses the surface must have been completed.
/// - All [`AcquiredSurfaceTexture`]s must have been destroyed.
/// - All [`Api::TextureView`]s derived from the [`AcquiredSurfaceTexture`]s must have been destroyed.
/// - All surfaces created using other devices must have been unconfigured before this call.
unsafe fn configure( unsafe fn configure(
&mut self, &mut self,
device: &A::Device, device: &A::Device,
config: &SurfaceConfiguration, config: &SurfaceConfiguration,
) -> Result<(), SurfaceError>; ) -> Result<(), SurfaceError>;
/// Unconfigures the surface on the given device.
///
/// # Safety
///
/// - All gpu work that uses the surface must have been completed.
/// - All [`AcquiredSurfaceTexture`]s must have been destroyed.
/// - All [`Api::TextureView`]s derived from the [`AcquiredSurfaceTexture`]s must have been destroyed.
/// - The surface must have been configured on the given device.
unsafe fn unconfigure(&mut self, device: &A::Device); unsafe fn unconfigure(&mut self, device: &A::Device);
/// Returns the next texture to be presented by the swapchain for drawing /// Returns the next texture to be presented by the swapchain for drawing

View file

@ -1143,7 +1143,7 @@ impl crate::Device<super::Api> for super::Device {
} }
if desc.anisotropy_clamp != 1 { if desc.anisotropy_clamp != 1 {
// We only enable anisotropy if it is supported, and wgpu-hal interface guarentees // We only enable anisotropy if it is supported, and wgpu-hal interface guarantees
// the clamp is in the range [1, 16] which is always supported if anisotropy is. // the clamp is in the range [1, 16] which is always supported if anisotropy is.
vk_info = vk_info vk_info = vk_info
.anisotropy_enable(true) .anisotropy_enable(true)

View file

@ -152,12 +152,11 @@ unsafe extern "system" fn debug_utils_messenger_callback(
} }
impl super::Swapchain { impl super::Swapchain {
/// # Safety
///
/// - The device must have been made idle before calling this function.
unsafe fn release_resources(self, device: &ash::Device) -> Self { unsafe fn release_resources(self, device: &ash::Device) -> Self {
profiling::scope!("Swapchain::release_resources"); profiling::scope!("Swapchain::release_resources");
{
profiling::scope!("vkDeviceWaitIdle");
let _ = unsafe { device.device_wait_idle() };
};
unsafe { device.destroy_fence(self.fence, None) }; unsafe { device.destroy_fence(self.fence, None) };
self self
} }
@ -186,7 +185,20 @@ impl super::Instance {
&self.shared &self.shared
} }
pub fn required_extensions( /// Return the instance extension names wgpu would like to enable.
///
/// Return a vector of the names of instance extensions actually available
/// on `entry` that wgpu would like to enable.
///
/// The `driver_api_version` argument should be the instance's Vulkan API
/// version, as obtained from `vkEnumerateInstanceVersion`. This is the same
/// space of values as the `VK_API_VERSION` constants.
///
/// Note that wgpu can function without many of these extensions (for
/// example, `VK_KHR_wayland_surface` is certainly not going to be available
/// everywhere), but if one of these extensions is available at all, wgpu
/// assumes that it has been enabled.
pub fn desired_extensions(
entry: &ash::Entry, entry: &ash::Entry,
_driver_api_version: u32, _driver_api_version: u32,
flags: crate::InstanceFlags, flags: crate::InstanceFlags,
@ -265,7 +277,7 @@ impl super::Instance {
/// ///
/// - `raw_instance` must be created from `entry` /// - `raw_instance` must be created from `entry`
/// - `raw_instance` must be created respecting `driver_api_version`, `extensions` and `flags` /// - `raw_instance` must be created respecting `driver_api_version`, `extensions` and `flags`
/// - `extensions` must be a superset of `required_extensions()` and must be created from the /// - `extensions` must be a superset of `desired_extensions()` and must be created from the
/// same entry, driver_api_version and flags. /// same entry, driver_api_version and flags.
/// - `android_sdk_version` is ignored and can be `0` for all platforms besides Android /// - `android_sdk_version` is ignored and can be `0` for all platforms besides Android
/// ///
@ -592,7 +604,7 @@ impl crate::Instance<super::Api> for super::Instance {
}, },
); );
let extensions = Self::required_extensions(&entry, driver_api_version, desc.flags)?; let extensions = Self::desired_extensions(&entry, driver_api_version, desc.flags)?;
let instance_layers = entry.enumerate_instance_layer_properties().map_err(|e| { let instance_layers = entry.enumerate_instance_layer_properties().map_err(|e| {
log::info!("enumerate_instance_layer_properties: {:?}", e); log::info!("enumerate_instance_layer_properties: {:?}", e);
@ -786,13 +798,22 @@ impl crate::Instance<super::Api> for super::Instance {
if exposed.info.device_type == wgt::DeviceType::IntegratedGpu if exposed.info.device_type == wgt::DeviceType::IntegratedGpu
&& exposed.info.vendor == db::intel::VENDOR && exposed.info.vendor == db::intel::VENDOR
{ {
// See https://gitlab.freedesktop.org/mesa/mesa/-/issues/4688 // Check if mesa driver and version less than 21.2
log::warn!( if let Some(version) = exposed.info.driver_info.split_once("Mesa ").map(|s| {
"Disabling presentation on '{}' (id {:?}) because of NV Optimus (on Linux)", s.1.rsplit_once('.')
exposed.info.name, .map(|v| v.0.parse::<f32>().unwrap_or_default())
exposed.adapter.raw .unwrap_or_default()
); }) {
exposed.adapter.private_caps.can_present = false; if version < 21.2 {
// See https://gitlab.freedesktop.org/mesa/mesa/-/issues/4688
log::warn!(
"Disabling presentation on '{}' (id {:?}) due to NV Optimus and Intel Mesa < v21.2",
exposed.info.name,
exposed.adapter.raw
);
exposed.adapter.private_caps.can_present = false;
}
}
} }
} }
} }
@ -807,6 +828,7 @@ impl crate::Surface<super::Api> for super::Surface {
device: &super::Device, device: &super::Device,
config: &crate::SurfaceConfiguration, config: &crate::SurfaceConfiguration,
) -> Result<(), crate::SurfaceError> { ) -> Result<(), crate::SurfaceError> {
// Safety: `configure`'s contract guarantees there are no resources derived from the swapchain in use.
let old = self let old = self
.swapchain .swapchain
.take() .take()
@ -820,6 +842,7 @@ impl crate::Surface<super::Api> for super::Surface {
unsafe fn unconfigure(&mut self, device: &super::Device) { unsafe fn unconfigure(&mut self, device: &super::Device) {
if let Some(sc) = self.swapchain.take() { if let Some(sc) = self.swapchain.take() {
// Safety: `unconfigure`'s contract guarantees there are no resources derived from the swapchain in use.
let swapchain = unsafe { sc.release_resources(&device.shared.raw) }; let swapchain = unsafe { sc.release_resources(&device.shared.raw) };
unsafe { swapchain.functor.destroy_swapchain(swapchain.raw, None) }; unsafe { swapchain.functor.destroy_swapchain(swapchain.raw, None) };
} }

View file

@ -1 +1 @@
{"files":{"Cargo.toml":"77707bfd609ceefcb527b361673fc735670cfd54db55b010386d90f2b9dac5d5","LICENSE.APACHE":"a6cba85bc92e0cff7a450b1d873c0eaa2e9fc96bf472df0247a26bec77bf3ff9","LICENSE.MIT":"c7fea58d1cfe49634cd92e54fc10a9d871f4b275321a4cd8c09e449122caaeb4","src/assertions.rs":"3fe98027aa73970c8ab7874a3e13dbfd6faa87df2081beb5c83aeec4c60f372f","src/lib.rs":"d65636e17a80dd44eb06b7bee735020869a7165b63505f11b126ab1eb76a5107","src/math.rs":"4d03039736dd6926feb139bc68734cb59df34ede310427bbf059e5c925e0af3b"},"package":null} {"files":{"Cargo.toml":"9f4a8846579ca480d493e80ad1488dcd18feb08c79aa833e2733808f0473d79b","LICENSE.APACHE":"a6cba85bc92e0cff7a450b1d873c0eaa2e9fc96bf472df0247a26bec77bf3ff9","LICENSE.MIT":"c7fea58d1cfe49634cd92e54fc10a9d871f4b275321a4cd8c09e449122caaeb4","src/assertions.rs":"3fe98027aa73970c8ab7874a3e13dbfd6faa87df2081beb5c83aeec4c60f372f","src/lib.rs":"d65636e17a80dd44eb06b7bee735020869a7165b63505f11b126ab1eb76a5107","src/math.rs":"4d03039736dd6926feb139bc68734cb59df34ede310427bbf059e5c925e0af3b"},"package":null}

View file

@ -44,7 +44,7 @@ features = ["serde_derive"]
optional = true optional = true
[dev-dependencies] [dev-dependencies]
serde_json = "1.0.105" serde_json = "1.0.106"
[dev-dependencies.serde] [dev-dependencies.serde]
version = "1" version = "1"