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"
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"
rev = "7a19f3af909202c7eafd36633b5584bfbb353ecb"
rev = "cc87b8f9eb30bb55d0735b89d3df3e099e1a6e7c"
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"
rev = "332cd0325da52675432830870584ec9766679c34"
rev = "7fea9e934efd8d5dc03b9aa3e06b775c1ac4a23e"
replace-with = "vendored-sources"
[source."git+https://github.com/glandium/prost?rev=95964e9d33df3c2a9c3f14285e262867cab6f96b"]

8
Cargo.lock generated
View file

@ -3724,7 +3724,7 @@ checksum = "a2983372caf4480544083767bf2d27defafe32af49ab4df3a0b7fc90793a3664"
[[package]]
name = "naga"
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 = [
"bit-set",
"bitflags 2.999.999",
@ -6301,7 +6301,7 @@ dependencies = [
[[package]]
name = "wgpu-core"
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 = [
"arrayvec",
"bit-vec",
@ -6324,7 +6324,7 @@ dependencies = [
[[package]]
name = "wgpu-hal"
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 = [
"android_system_properties",
"arrayvec",
@ -6360,7 +6360,7 @@ dependencies = [
[[package]]
name = "wgpu-types"
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 = [
"bitflags 2.999.999",
"js-sys",

View file

@ -17,7 +17,7 @@ default = []
[dependencies.wgc]
package = "wgpu-core"
git = "https://github.com/gfx-rs/wgpu"
rev = "332cd0325da52675432830870584ec9766679c34"
rev = "7fea9e934efd8d5dc03b9aa3e06b775c1ac4a23e"
#Note: "replay" shouldn't ideally be needed,
# but it allows us to serialize everything across IPC.
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]
package = "wgpu-core"
git = "https://github.com/gfx-rs/wgpu"
rev = "332cd0325da52675432830870584ec9766679c34"
rev = "7fea9e934efd8d5dc03b9aa3e06b775c1ac4a23e"
features = ["metal"]
# We want the wgpu-core Direct3D backends on Windows.
[target.'cfg(windows)'.dependencies.wgc]
package = "wgpu-core"
git = "https://github.com/gfx-rs/wgpu"
rev = "332cd0325da52675432830870584ec9766679c34"
rev = "7fea9e934efd8d5dc03b9aa3e06b775c1ac4a23e"
features = ["dx11", "dx12"]
# 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]
package = "wgpu-core"
git = "https://github.com/gfx-rs/wgpu"
rev = "332cd0325da52675432830870584ec9766679c34"
rev = "7fea9e934efd8d5dc03b9aa3e06b775c1ac4a23e"
features = ["vulkan"]
[dependencies.wgt]
package = "wgpu-types"
git = "https://github.com/gfx-rs/wgpu"
rev = "332cd0325da52675432830870584ec9766679c34"
rev = "7fea9e934efd8d5dc03b9aa3e06b775c1ac4a23e"
[dependencies.wgh]
package = "wgpu-hal"
git = "https://github.com/gfx-rs/wgpu"
rev = "332cd0325da52675432830870584ec9766679c34"
rev = "7fea9e934efd8d5dc03b9aa3e06b775c1ac4a23e"
[target.'cfg(windows)'.dependencies]
d3d12 = "0.7.0"

View file

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

View file

@ -2283,6 +2283,11 @@ who = "Teodor Tanasoaia <ttanasoaia@mozilla.com>"
criteria = "safe-to-deploy"
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]]
who = "Erich Gubler <egubler@mozilla.com"
criteria = "safe-to-deploy"
@ -3992,6 +3997,11 @@ who = "Erich Gubler <erichdongubler@gmail.com>"
criteria = "safe-to-deploy"
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]]
who = "Erich Gubler <erichdongubler@gmail.com>"
criteria = "safe-to-deploy"
@ -4100,6 +4110,11 @@ who = "Erich Gubler <erichdongubler@gmail.com>"
criteria = "safe-to-deploy"
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]]
who = "Erich Gubler <erichdongubler@gmail.com>"
criteria = "safe-to-deploy"
@ -4208,6 +4223,11 @@ who = "Erich Gubler <erichdongubler@gmail.com>"
criteria = "safe-to-deploy"
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]]
who = "Erich Gubler <erichdongubler@gmail.com>"
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;
/// Image size query
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!(SAMPLE_VARIABLES, 400, 300);
check_feature!(DYNAMIC_ARRAY_SIZE, 430, 310);
check_feature!(DUAL_SOURCE_BLENDING, 330, 300 /* with extension */);
match version {
Version::Embedded { is_webgl: true, .. } => check_feature!(MULTI_VIEW, 140, 300),
_ => 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
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(())
}
@ -497,6 +504,7 @@ impl<'a, W> Writer<'a, W> {
location: _,
interpolation,
sampling,
second_blend_source,
} => {
if interpolation == Some(Interpolation::Linear) {
self.features.request(Features::NOPERSPECTIVE_QUALIFIER);
@ -504,6 +512,9 @@ impl<'a, W> Writer<'a, W> {
if sampling == Some(Sampling::Sample) {
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)
//
"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`
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.
pub type BindingMap = std::collections::BTreeMap<crate::ResourceBinding, u8>;
@ -333,6 +336,12 @@ struct VaryingName<'a> {
impl fmt::Display for VaryingName<'_> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self.binding {
crate::Binding::Location {
second_blend_source: true,
..
} => {
write!(f, "_fs2p_location1",)
}
crate::Binding::Location { location, .. } => {
let prefix = match (self.stage, self.output) {
(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
let mut constants = self
.module
@ -1235,12 +1291,13 @@ impl<'a, W: Write> Writer<'a, W> {
Some(binding) => binding,
};
let (location, interpolation, sampling) = match *binding {
let (location, interpolation, sampling, second_blend_source) = match *binding {
crate::Binding::Location {
location,
interpolation,
sampling,
} => (location, interpolation, sampling),
second_blend_source,
} => (location, interpolation, sampling, second_blend_source),
crate::Binding::BuiltIn(built_in) => {
if let crate::BuiltIn::Position { invariant: true } = built_in {
match (self.options.version, self.entry_point.stage) {
@ -1281,8 +1338,12 @@ impl<'a, W: Write> Writer<'a, W> {
// Write the I/O locations, if allowed
if self.options.version.supports_explicit_locations() || !emit_interpolation_and_auxiliary {
if second_blend_source {
write!(self.out, "layout(location = {location}, index = 1) ")?;
} else {
write!(self.out, "layout(location = {location}) ")?;
}
}
// Write the interpolation qualifier.
if let Some(interp) = interpolation {
@ -1318,6 +1379,7 @@ impl<'a, W: Write> Writer<'a, W> {
location,
interpolation: None,
sampling: None,
second_blend_source,
},
stage: self.entry_point.stage,
output,
@ -2985,8 +3047,8 @@ impl<'a, W: Write> Writer<'a, W> {
Mf::Round => "roundEven",
Mf::Fract => "fract",
Mf::Trunc => "trunc",
Mf::Modf => "modf",
Mf::Frexp => "frexp",
Mf::Modf => MODF_FUNCTION,
Mf::Frexp => FREXP_FUNCTION,
Mf::Ldexp => "ldexp",
// exponent
Mf::Exp => "exp",

View file

@ -781,6 +781,59 @@ impl<'a, W: Write> super::Writer<'a, W> {
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
pub(super) fn write_wrapped_compose_functions(
&mut self,

View file

@ -814,6 +814,9 @@ pub const RESERVED: &[&str] = &[
"TextureBuffer",
"ConstantBuffer",
"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

View file

@ -17,6 +17,9 @@ const SPECIAL_BASE_VERTEX: &str = "base_vertex";
const SPECIAL_BASE_INSTANCE: &str = "base_instance";
const SPECIAL_OTHER: &str = "other";
pub(crate) const MODF_FUNCTION: &str = "naga_modf";
pub(crate) const FREXP_FUNCTION: &str = "naga_frexp";
struct EpStructMember {
name: String,
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)?;
// Write all named constants
@ -416,7 +421,17 @@ impl<'a, W: fmt::Write> super::Writer<'a, W> {
let builtin_str = builtin.to_hlsl_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)) {
write!(self.out, " : SV_Target{location}")?;
} else {
@ -2665,8 +2680,8 @@ impl<'a, W: fmt::Write> super::Writer<'a, W> {
Mf::Round => Function::Regular("round"),
Mf::Fract => Function::Regular("frac"),
Mf::Trunc => Function::Regular("trunc"),
Mf::Modf => Function::Regular("modf"),
Mf::Frexp => Function::Regular("frexp"),
Mf::Modf => Function::Regular(MODF_FUNCTION),
Mf::Frexp => Function::Regular(FREXP_FUNCTION),
Mf::Ldexp => Function::Regular("ldexp"),
// exponent
Mf::Exp => Function::Regular("exp"),

View file

@ -214,4 +214,6 @@ pub const RESERVED: &[&str] = &[
// Naga utilities
"DefaultConstructible",
"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 {
BuiltIn(crate::BuiltIn),
Attribute(u32),
Color(u32),
Color {
location: u32,
second_blend_source: bool,
},
User {
prefix: &'static str,
index: u32,
@ -245,9 +248,20 @@ impl Options {
location,
interpolation,
sampling,
second_blend_source,
} => match mode {
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 => {
Ok(ResolvedBinding::User {
prefix: if self.spirv_cross_compatibility {
@ -404,7 +418,16 @@ impl ResolvedBinding {
write!(out, "{name}")?;
}
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 {
prefix,
index,

View file

@ -32,6 +32,9 @@ const RAY_QUERY_FIELD_INTERSECTION: &str = "intersection";
const RAY_QUERY_FIELD_READY: &str = "ready";
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.
///
/// The `sizes` slice determines whether this function writes a
@ -1181,6 +1184,31 @@ impl<W: Write> Writer<W> {
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(
&mut self,
expr_handle: Handle<crate::Expression>,
@ -1644,8 +1672,9 @@ impl<W: Write> Writer<W> {
} => {
use crate::MathFunction as Mf;
let scalar_argument = match *context.resolve_type(arg) {
crate::TypeInner::Scalar { .. } => true,
let arg_type = context.resolve_type(arg);
let scalar_argument = match arg_type {
&crate::TypeInner::Scalar { .. } => true,
_ => false,
};
@ -1678,8 +1707,8 @@ impl<W: Write> Writer<W> {
Mf::Round => "rint",
Mf::Fract => "fract",
Mf::Trunc => "trunc",
Mf::Modf => "modf",
Mf::Frexp => "frexp",
Mf::Modf => MODF_FUNCTION,
Mf::Frexp => FREXP_FUNCTION,
Mf::Ldexp => "ldexp",
// exponent
Mf::Exp => "exp",
@ -1710,7 +1739,12 @@ impl<W: Write> Writer<W> {
Mf::Reflect => "reflect",
Mf::Refract => "refract",
// 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::Mix => "mix",
Mf::Step => "step",
@ -1813,6 +1847,9 @@ impl<W: Write> Writer<W> {
write!(self.out, "((")?;
self.put_expression(arg, context, false)?;
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 {
write!(self.out, "{NAMESPACE}::{fun_name}")?;
self.put_call_parameters(
@ -2417,6 +2454,16 @@ impl<W: Write> Writer<W> {
crate::MathFunction::FindMsb => {
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(())
}

View file

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

View file

@ -1434,6 +1434,7 @@ impl Writer {
location,
interpolation,
sampling,
second_blend_source,
} => {
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) => {
use crate::BuiltIn as Bi;

View file

@ -17,6 +17,7 @@ enum Attribute {
Invariant,
Interpolate(Option<crate::Interpolation>, Option<crate::Sampling>),
Location(u32),
SecondBlendSource,
Stage(ShaderStage),
WorkGroupSize([u32; 3]),
}
@ -96,6 +97,14 @@ impl<W: Write> Writer<W> {
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 {
self.reset(module);
@ -108,15 +117,15 @@ impl<W: Write> Writer<W> {
// Write all structs
for (handle, ty) in module.types.iter() {
if let TypeInner::Struct {
ref members,
span: _,
} = ty.inner
if let TypeInner::Struct { ref members, .. } = ty.inner {
{
if !self.is_builtin_wgsl_struct(module, handle) {
self.write_struct(module, handle, members)?;
writeln!(self.out)?;
}
}
}
}
// Write all named constants
let mut constants = module
@ -319,6 +328,7 @@ impl<W: Write> Writer<W> {
for attribute in attributes {
match *attribute {
Attribute::Location(id) => write!(self.out, "@location({id}) ")?,
Attribute::SecondBlendSource => write!(self.out, "@second_blend_source ")?,
Attribute::BuiltIn(builtin_attrib) => {
let builtin = builtin_str(builtin_attrib)?;
write!(self.out, "@builtin({builtin}) ")?;
@ -1917,9 +1927,20 @@ fn map_binding_to_attribute(binding: &crate::Binding) -> Vec<Attribute> {
location,
interpolation,
sampling,
second_blend_source: false,
} => vec![
Attribute::Location(location),
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,
};
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,
SampleLevel, ScalarKind as Sk, Span, Type, TypeInner, UnaryOperator, VectorSize,
};
@ -1280,14 +1280,14 @@ fn inject_common_builtin(
0b10 => Some(VectorSize::Tri),
_ => Some(VectorSize::Quad),
};
let ty = || match size {
let ty = |kind| match size {
Some(size) => TypeInner::Vector {
size,
kind: Sk::Float,
kind,
width: float_width,
},
None => TypeInner::Scalar {
kind: Sk::Float,
kind,
width: float_width,
},
};
@ -1300,9 +1300,15 @@ fn inject_common_builtin(
_ => unreachable!(),
};
let second_kind = if fun == MacroCall::MathFunction(MathFunction::Ldexp) {
Sk::Sint
} else {
Sk::Float
};
declaration
.overloads
.push(module.add_builtin(vec![ty(), ty()], fun))
.push(module.add_builtin(vec![ty(Sk::Float), ty(second_kind)], fun))
}
}
"transpose" => {
@ -1678,7 +1684,6 @@ impl MacroCall {
&self,
frontend: &mut Frontend,
ctx: &mut Context,
body: &mut Block,
args: &mut [Handle<Expression>],
meta: Span,
) -> Result<Option<Handle<Expression>>> {
@ -1688,14 +1693,8 @@ impl MacroCall {
args[0]
}
MacroCall::SamplerShadow => {
sampled_to_depth(
&mut frontend.module,
ctx,
args[0],
meta,
&mut frontend.errors,
);
frontend.invalidate_expression(ctx, args[0], meta)?;
sampled_to_depth(ctx, args[0], meta, &mut frontend.errors);
ctx.invalidate_expression(args[0], meta)?;
ctx.samplers.insert(args[0], args[1]);
args[0]
}
@ -1708,7 +1707,7 @@ impl MacroCall {
let mut coords = args[1];
if proj {
let size = match *frontend.resolve_type(ctx, coords, meta)? {
let size = match *ctx.resolve_type(coords, meta)? {
TypeInner::Vector { size, .. } => size,
_ => unreachable!(),
};
@ -1718,8 +1717,7 @@ impl MacroCall {
index: size as u32 - 1,
},
Span::default(),
body,
);
)?;
let left = if let VectorSize::Bi = size {
ctx.add_expression(
Expression::AccessIndex {
@ -1727,8 +1725,7 @@ impl MacroCall {
index: 0,
},
Span::default(),
body,
)
)?
} else {
let size = match size {
VectorSize::Tri => VectorSize::Bi,
@ -1737,9 +1734,8 @@ impl MacroCall {
right = ctx.add_expression(
Expression::Splat { size, value: right },
Span::default(),
body,
);
ctx.vector_resize(size, coords, Span::default(), body)
)?;
ctx.vector_resize(size, coords, Span::default())?
};
coords = ctx.add_expression(
Expression::Binary {
@ -1748,13 +1744,11 @@ impl MacroCall {
right,
},
Span::default(),
body,
);
)?;
}
let extra = args.get(2).copied();
let comps =
frontend.coordinate_components(ctx, args[0], coords, extra, meta, body)?;
let comps = frontend.coordinate_components(ctx, args[0], coords, extra, meta)?;
let mut num_args = 2;
@ -1801,7 +1795,7 @@ impl MacroCall {
true => {
let offset_arg = args[num_args];
num_args += 1;
match frontend.solve_constant(ctx, offset_arg, meta) {
match ctx.solve_constant(offset_arg, meta) {
Ok(v) => Some(v),
Err(e) => {
frontend.errors.push(e);
@ -1820,7 +1814,7 @@ impl MacroCall {
.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 } => {
@ -1832,20 +1826,18 @@ impl MacroCall {
},
},
Span::default(),
body,
);
)?;
if arrayed {
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, .. } => {
for index in 0..(ori_size as u32) {
components.push(ctx.add_expression(
Expression::AccessIndex { base: expr, index },
Span::default(),
body,
))
)?)
}
match ori_size {
@ -1865,10 +1857,9 @@ impl MacroCall {
query: ImageQuery::NumLayers,
},
Span::default(),
body,
));
)?);
let ty = frontend.module.types.insert(
let ty = ctx.module.types.insert(
Type {
name: None,
inner: TypeInner::Vector {
@ -1880,7 +1871,7 @@ impl MacroCall {
Span::default(),
);
expr = ctx.add_expression(Expression::Compose { components, ty }, meta, body)
expr = ctx.add_expression(Expression::Compose { components, ty }, meta)?
}
ctx.add_expression(
@ -1890,12 +1881,10 @@ impl MacroCall {
convert: Some(4),
},
Span::default(),
body,
)
)?
}
MacroCall::ImageLoad { multi } => {
let comps =
frontend.coordinate_components(ctx, args[0], args[1], None, meta, body)?;
let comps = frontend.coordinate_components(ctx, args[0], args[1], None, meta)?;
let (sample, level) = match (multi, args.get(2)) {
(_, None) => (None, None),
(true, Some(&arg)) => (Some(arg), None),
@ -1910,14 +1899,12 @@ impl MacroCall {
level,
},
Span::default(),
body,
)
)?
}
MacroCall::ImageStore => {
let comps =
frontend.coordinate_components(ctx, args[0], args[1], None, meta, body)?;
ctx.emit_restart(body);
body.push(
let comps = frontend.coordinate_components(ctx, args[0], args[1], None, meta)?;
ctx.emit_restart();
ctx.body.push(
crate::Statement::ImageStore {
image: args[0],
coordinate: comps.coordinate,
@ -1937,8 +1924,7 @@ impl MacroCall {
arg3: args.get(3).copied(),
},
Span::default(),
body,
),
)?,
mc @ (MacroCall::FindLsbUint | MacroCall::FindMsbUint) => {
let fun = match mc {
MacroCall::FindLsbUint => MathFunction::FindLsb,
@ -1954,8 +1940,7 @@ impl MacroCall {
arg3: None,
},
Span::default(),
body,
);
)?;
ctx.add_expression(
Expression::As {
expr: res,
@ -1963,8 +1948,7 @@ impl MacroCall {
convert: Some(4),
},
Span::default(),
body,
)
)?
}
MacroCall::BitfieldInsert => {
let conv_arg_2 = ctx.add_expression(
@ -1974,8 +1958,7 @@ impl MacroCall {
convert: Some(4),
},
Span::default(),
body,
);
)?;
let conv_arg_3 = ctx.add_expression(
Expression::As {
expr: args[3],
@ -1983,8 +1966,7 @@ impl MacroCall {
convert: Some(4),
},
Span::default(),
body,
);
)?;
ctx.add_expression(
Expression::Math {
fun: MathFunction::InsertBits,
@ -1994,8 +1976,7 @@ impl MacroCall {
arg3: Some(conv_arg_3),
},
Span::default(),
body,
)
)?
}
MacroCall::BitfieldExtract => {
let conv_arg_1 = ctx.add_expression(
@ -2005,8 +1986,7 @@ impl MacroCall {
convert: Some(4),
},
Span::default(),
body,
);
)?;
let conv_arg_2 = ctx.add_expression(
Expression::As {
expr: args[2],
@ -2014,8 +1994,7 @@ impl MacroCall {
convert: Some(4),
},
Span::default(),
body,
);
)?;
ctx.add_expression(
Expression::Math {
fun: MathFunction::ExtractBits,
@ -2025,8 +2004,7 @@ impl MacroCall {
arg3: None,
},
Span::default(),
body,
)
)?
}
MacroCall::Relational(fun) => ctx.add_expression(
Expression::Relational {
@ -2034,13 +2012,10 @@ impl MacroCall {
argument: args[0],
},
Span::default(),
body,
),
MacroCall::Unary(op) => ctx.add_expression(
Expression::Unary { op, expr: args[0] },
Span::default(),
body,
),
)?,
MacroCall::Unary(op) => {
ctx.add_expression(Expression::Unary { op, expr: args[0] }, Span::default())?
}
MacroCall::Binary(op) => ctx.add_expression(
Expression::Binary {
op,
@ -2048,10 +2023,9 @@ impl MacroCall {
right: args[1],
},
Span::default(),
body,
),
)?,
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)
@ -2062,8 +2036,7 @@ impl MacroCall {
right: args[1],
},
Span::default(),
body,
);
)?;
let floor = ctx.add_expression(
Expression::Math {
fun: MathFunction::Floor,
@ -2073,8 +2046,7 @@ impl MacroCall {
arg3: None,
},
Span::default(),
body,
);
)?;
let mult = ctx.add_expression(
Expression::Binary {
op: BinaryOperator::Multiply,
@ -2082,8 +2054,7 @@ impl MacroCall {
right: args[1],
},
Span::default(),
body,
);
)?;
ctx.add_expression(
Expression::Binary {
op: BinaryOperator::Subtract,
@ -2091,11 +2062,10 @@ impl MacroCall {
right: mult,
},
Span::default(),
body,
)
)?
}
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(
Expression::Math {
@ -2106,8 +2076,7 @@ impl MacroCall {
arg3: args.get(3).copied(),
},
Span::default(),
body,
)
)?
}
MacroCall::MixBoolean => ctx.add_expression(
Expression::Select {
@ -2116,11 +2085,10 @@ impl MacroCall {
reject: args[0],
},
Span::default(),
body,
),
)?,
MacroCall::Clamp(size) => {
ctx.implicit_splat(frontend, &mut args[1], meta, size)?;
ctx.implicit_splat(frontend, &mut args[2], meta, size)?;
ctx.implicit_splat(&mut args[1], meta, size)?;
ctx.implicit_splat(&mut args[2], meta, size)?;
ctx.add_expression(
Expression::Math {
@ -2131,8 +2099,7 @@ impl MacroCall {
arg3: args.get(3).copied(),
},
Span::default(),
body,
)
)?
}
MacroCall::BitCast(kind) => ctx.add_expression(
Expression::As {
@ -2141,8 +2108,7 @@ impl MacroCall {
convert: None,
},
Span::default(),
body,
),
)?,
MacroCall::Derivate(axis, ctrl) => ctx.add_expression(
Expression::Derivative {
axis,
@ -2150,16 +2116,16 @@ impl MacroCall {
expr: args[0],
},
Span::default(),
body,
),
)?,
MacroCall::Barrier => {
ctx.emit_restart(body);
body.push(crate::Statement::Barrier(crate::Barrier::all()), meta);
ctx.emit_restart();
ctx.body
.push(crate::Statement::Barrier(crate::Barrier::all()), meta);
return Ok(None);
}
MacroCall::SmoothStep { splatted } => {
ctx.implicit_splat(frontend, &mut args[0], meta, splatted)?;
ctx.implicit_splat(frontend, &mut args[1], meta, splatted)?;
ctx.implicit_splat(&mut args[0], meta, splatted)?;
ctx.implicit_splat(&mut args[1], meta, splatted)?;
ctx.add_expression(
Expression::Math {
@ -2170,8 +2136,7 @@ impl MacroCall {
arg3: None,
},
Span::default(),
body,
)
)?
}
}))
}
@ -2183,7 +2148,6 @@ fn texture_call(
level: SampleLevel,
comps: CoordComponents,
offset: Option<Handle<Expression>>,
body: &mut Block,
meta: Span,
) -> Result<Handle<Expression>> {
if let Some(sampler) = ctx.samplers.get(&image).copied() {
@ -2205,8 +2169,7 @@ fn texture_call(
depth_ref: comps.depth_ref,
},
meta,
body,
))
)?)
} else {
Err(Error {
kind: ErrorKind::SemanticError("Bad call".into()),
@ -2235,13 +2198,12 @@ impl Frontend {
coord: Handle<Expression>,
extra: Option<Handle<Expression>>,
meta: Span,
body: &mut Block,
) -> Result<CoordComponents> {
if let TypeInner::Image {
dim,
arrayed,
class,
} = *self.resolve_type(ctx, image, meta)?
} = *ctx.resolve_type(image, meta)?
{
let image_size = match dim {
Dim::D1 => None,
@ -2249,7 +2211,7 @@ impl Frontend {
Dim::D3 => 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),
_ => None,
};
@ -2261,7 +2223,7 @@ impl Frontend {
let coordinate = match (image_size, coord_size) {
(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(
Expression::AccessIndex {
@ -2269,8 +2231,7 @@ impl Frontend {
index: 0,
},
Span::default(),
body,
),
)?,
_ => coord,
};
@ -2283,8 +2244,7 @@ impl Frontend {
Some(ctx.add_expression(
Expression::AccessIndex { base: coord, index },
Span::default(),
body,
))
)?)
} else {
None
};
@ -2300,8 +2260,7 @@ impl Frontend {
Some(ctx.add_expression(
Expression::AccessIndex { base: coord, index },
Span::default(),
body,
))
)?)
}
}
false => None,
@ -2332,7 +2291,6 @@ impl Frontend {
/// Helper function to cast a expression holding a sampled image to a
/// depth image.
pub fn sampled_to_depth(
module: &mut Module,
ctx: &mut Context,
image: Handle<Expression>,
meta: Span,
@ -2340,7 +2298,7 @@ pub fn sampled_to_depth(
) {
// Get the a mutable type handle of the underlying image storage
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) => {
// Mark the function argument as carrying a depth texture
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
TypeInner::Image {
class,
@ -2364,7 +2322,7 @@ pub fn sampled_to_depth(
arrayed,
} => match class {
ImageClass::Sampled { multi, .. } => {
*ty = module.types.insert(
*ty = ctx.module.types.insert(
Type {
name: None,
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,
errors: Vec<Error>,
module: Module,
}
impl Frontend {
@ -188,10 +186,6 @@ impl Frontend {
self.global_variables.clear();
self.entry_args.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
@ -208,16 +202,20 @@ impl Frontend {
let lexer = lex::Lexer::new(source, &options.defines);
let mut ctx = ParsingContext::new(lexer);
if let Err(e) = ctx.parse(self) {
self.errors.push(e);
}
match ctx.parse(self) {
Ok(module) => {
if self.errors.is_empty() {
Ok(std::mem::take(&mut self.module))
Ok(module)
} else {
Err(std::mem::take(&mut self.errors))
}
}
Err(e) => {
self.errors.push(e);
Err(std::mem::take(&mut self.errors))
}
}
}
/// Returns additional information about the parsed shader which might not be
/// stored in the [`Module`](Module), see the documentation for

View file

@ -9,7 +9,7 @@ use super::{
variables::{GlobalOrConstant, VarDeclaration},
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 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
let mut body = Block::new();
let mut ctx = Context::new(frontend, &mut body);
let mut ctx = Context::new(frontend, &mut module)?;
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
@ -177,8 +178,8 @@ impl<'source> ParsingContext<'source> {
for decl in declaration.overloads.iter() {
if let FunctionKind::Call(handle) = decl.kind {
if decl.defined && decl.parameters.is_empty() {
frontend.add_entry_point(handle, body, ctx.expressions);
return Ok(());
frontend.add_entry_point(handle, ctx)?;
return Ok(module);
}
}
}
@ -190,10 +191,14 @@ impl<'source> ParsingContext<'source> {
})
}
fn parse_uint_constant(&mut self, frontend: &mut Frontend) -> Result<(u32, Span)> {
let (const_expr, meta) = self.parse_constant_expression(frontend)?;
fn parse_uint_constant(
&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 {
Ok(value) => Ok(value),
@ -213,16 +218,15 @@ impl<'source> ParsingContext<'source> {
fn parse_constant_expression(
&mut self,
frontend: &mut Frontend,
module: &mut Module,
) -> Result<(Handle<Expression>, Span)> {
let mut block = Block::new();
let mut ctx = Context::new(frontend, &mut block);
let mut ctx = Context::new(frontend, module)?;
let mut stmt_ctx = ctx.stmt_ctx();
let expr = self.parse_conditional(frontend, &mut ctx, &mut stmt_ctx, &mut block, None)?;
let (root, meta) = ctx.lower_expect(stmt_ctx, frontend, expr, ExprPos::Rhs, &mut block)?;
let expr = self.parse_conditional(frontend, &mut ctx, &mut stmt_ctx, None)?;
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>,
/// Indicates a global declaration
external: bool,
ctx: &'ctx mut Context,
body: &'ctx mut Block,
ctx: &'ctx mut Context<'a>,
}
impl<'ctx, 'qualifiers> DeclarationContext<'ctx, 'qualifiers> {
impl<'ctx, 'qualifiers, 'a> DeclarationContext<'ctx, 'qualifiers, 'a> {
fn add_var(
&mut self,
frontend: &mut Frontend,
@ -415,24 +417,14 @@ impl<'ctx, 'qualifiers> DeclarationContext<'ctx, 'qualifiers> {
match self.external {
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 {
GlobalOrConstant::Global(handle) => Expression::GlobalVariable(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,
},
proc::Alignment,
AddressSpace, Block, Expression, FunctionResult, Handle, ScalarKind, Statement, StructMember,
Type, TypeInner,
AddressSpace, Expression, FunctionResult, Handle, ScalarKind, Statement, StructMember, Type,
TypeInner,
};
use super::{DeclarationContext, ParsingContext, Result};
@ -73,10 +73,9 @@ impl<'source> ParsingContext<'source> {
&mut self,
frontend: &mut Frontend,
global_ctx: &mut Context,
global_body: &mut Block,
) -> Result<()> {
if self
.parse_declaration(frontend, global_ctx, global_body, true)?
.parse_declaration(frontend, global_ctx, true)?
.is_none()
{
let token = self.bump(frontend)?;
@ -103,7 +102,6 @@ impl<'source> ParsingContext<'source> {
frontend: &mut Frontend,
ty: Handle<Type>,
ctx: &mut Context,
body: &mut Block,
) -> Result<(Handle<Expression>, Span)> {
// initializer:
// assignment_expression
@ -118,10 +116,9 @@ impl<'source> ParsingContext<'source> {
let mut components = Vec::new();
loop {
// The type expected to be parsed inside the initializer list
let new_ty =
element_or_member_type(ty, components.len(), &mut frontend.module.types);
let new_ty = element_or_member_type(ty, components.len(), &mut ctx.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)?;
match token.value {
@ -150,18 +147,17 @@ impl<'source> ParsingContext<'source> {
}
Ok((
ctx.add_expression(Expression::Compose { ty, components }, meta, body),
ctx.add_expression(Expression::Compose { ty, components }, meta)?,
meta,
))
} else {
let mut stmt = ctx.stmt_ctx();
let expr = self.parse_assignment(frontend, ctx, &mut stmt, body)?;
let (mut init, init_meta) =
ctx.lower_expect(stmt, frontend, expr, ExprPos::Rhs, body)?;
let expr = self.parse_assignment(frontend, ctx, &mut stmt)?;
let (mut init, init_meta) = ctx.lower_expect(stmt, frontend, expr, ExprPos::Rhs)?;
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 {
ctx.implicit_conversion(frontend, &mut init, init_meta, kind, width)?;
ctx.implicit_conversion(&mut init, init_meta, kind, width)?;
}
Ok((init, init_meta))
@ -223,18 +219,17 @@ impl<'source> ParsingContext<'source> {
// parse an array specifier if it exists
// 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
self.parse_array_specifier(frontend, &mut meta, &mut ty)?;
self.parse_array_specifier(frontend, ctx.ctx, &mut meta, &mut ty)?;
let init = self
.bump_if(frontend, TokenValue::Assign)
.map::<Result<_>, _>(|_| {
let (mut expr, init_meta) =
self.parse_initializer(frontend, ty, ctx.ctx, ctx.body)?;
let (mut expr, init_meta) = self.parse_initializer(frontend, ty, ctx.ctx)?;
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 {
ctx.ctx
.implicit_conversion(frontend, &mut expr, init_meta, kind, width)?;
.implicit_conversion(&mut expr, init_meta, kind, width)?;
}
meta.subsume(init_meta);
@ -246,7 +241,7 @@ impl<'source> ParsingContext<'source> {
let is_const = ctx.qualifiers.storage.0 == StorageQualifier::Const;
let maybe_const_expr = if ctx.external {
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),
// If the declaration is external (global scope) and is constant qualified
// 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)?;
if let Some((value, _)) = init.filter(|_| maybe_const_expr.is_none()) {
ctx.flush_expressions();
ctx.body.push(Statement::Store { pointer, value }, meta);
ctx.ctx.emit_restart();
ctx.ctx.body.push(Statement::Store { pointer, value }, meta);
}
let token = self.bump(frontend)?;
@ -291,7 +286,6 @@ impl<'source> ParsingContext<'source> {
&mut self,
frontend: &mut Frontend,
ctx: &mut Context,
body: &mut Block,
external: bool,
) -> Result<Option<Span>> {
//declaration:
@ -307,12 +301,12 @@ impl<'source> ParsingContext<'source> {
// type_qualifier IDENTIFIER identifier_list SEMICOLON
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) {
// This branch handles variables and function prototypes and if
// 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_fallthrough = match token.value {
@ -322,11 +316,10 @@ impl<'source> ParsingContext<'source> {
self.bump(frontend)?;
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;
meta.subsume(end_meta);
@ -349,11 +342,10 @@ impl<'source> ParsingContext<'source> {
token.meta,
frontend,
&mut context,
&mut body,
&mut None,
)?;
frontend.add_function(context, name, result, body, meta);
frontend.add_function(context, name, result, meta);
Ok(Some(meta))
}
@ -394,7 +386,6 @@ impl<'source> ParsingContext<'source> {
qualifiers,
external,
ctx,
body,
};
self.backtrack(token_fallthrough)?;
@ -419,7 +410,6 @@ impl<'source> ParsingContext<'source> {
self.parse_block_declaration(
frontend,
ctx,
body,
&mut qualifiers,
ty_name,
token.meta,
@ -427,7 +417,7 @@ impl<'source> ParsingContext<'source> {
.map(Some)
} else {
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);
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 {
kind: ScalarKind::Float | ScalarKind::Sint,
..
@ -528,7 +518,6 @@ impl<'source> ParsingContext<'source> {
&mut self,
frontend: &mut Frontend,
ctx: &mut Context,
body: &mut Block,
qualifiers: &mut TypeQualifiers,
ty_name: String,
mut meta: Span,
@ -548,10 +537,10 @@ impl<'source> ParsingContext<'source> {
};
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)?;
let mut ty = frontend.module.types.insert(
let mut ty = ctx.module.types.insert(
Type {
name: Some(ty_name),
inner: TypeInner::Struct {
@ -566,7 +555,7 @@ impl<'source> ParsingContext<'source> {
let name = match token.value {
TokenValue::Semicolon => None,
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)?;
@ -585,7 +574,6 @@ impl<'source> ParsingContext<'source> {
let global = frontend.add_global_var(
ctx,
body,
VarDeclaration {
qualifiers,
ty,
@ -607,7 +595,7 @@ impl<'source> ParsingContext<'source> {
entry_arg: None,
mutable: true,
};
ctx.add_global(frontend, &k, lookup, body);
ctx.add_global(&k, lookup)?;
frontend.global_variables.push((k, lookup));
}
@ -619,6 +607,7 @@ impl<'source> ParsingContext<'source> {
pub fn parse_struct_declaration_list(
&mut self,
frontend: &mut Frontend,
ctx: &mut Context,
members: &mut Vec<StructMember>,
layout: StructLayout,
) -> Result<u32> {
@ -628,12 +617,12 @@ impl<'source> ParsingContext<'source> {
loop {
// 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 {
let (name, name_meta) = self.expect_ident(frontend)?;
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);
@ -641,7 +630,7 @@ impl<'source> ParsingContext<'source> {
ty,
meta,
layout,
&mut frontend.module.types,
&mut ctx.module.types,
&mut frontend.errors,
);

View file

@ -9,7 +9,7 @@ use crate::{
token::{Token, TokenValue},
Error, Frontend, Result, Span,
},
ArraySize, BinaryOperator, Block, Handle, Literal, Type, TypeInner, UnaryOperator,
ArraySize, BinaryOperator, Handle, Literal, Type, TypeInner, UnaryOperator,
};
impl<'source> ParsingContext<'source> {
@ -18,7 +18,6 @@ impl<'source> ParsingContext<'source> {
frontend: &mut Frontend,
ctx: &mut Context,
stmt: &mut StmtContext,
body: &mut Block,
) -> Result<Handle<HirExpr>> {
let mut token = self.bump(frontend)?;
@ -47,7 +46,7 @@ impl<'source> ParsingContext<'source> {
}
TokenValue::BoolConstant(value) => Literal::Bool(value),
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;
token.meta.subsume(meta);
@ -84,7 +83,6 @@ impl<'source> ParsingContext<'source> {
frontend: &mut Frontend,
ctx: &mut Context,
stmt: &mut StmtContext,
body: &mut Block,
meta: &mut Span,
) -> Result<Vec<Handle<HirExpr>>> {
let mut args = Vec::new();
@ -92,7 +90,7 @@ impl<'source> ParsingContext<'source> {
meta.subsume(token.meta);
} else {
loop {
args.push(self.parse_assignment(frontend, ctx, stmt, body)?);
args.push(self.parse_assignment(frontend, ctx, stmt)?);
let token = self.bump(frontend)?;
match token.value {
@ -122,21 +120,20 @@ impl<'source> ParsingContext<'source> {
frontend: &mut Frontend,
ctx: &mut Context,
stmt: &mut StmtContext,
body: &mut Block,
) -> Result<Handle<HirExpr>> {
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)?;
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 {
size: ArraySize::Dynamic,
stride,
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())
.ok()
@ -148,7 +145,7 @@ impl<'source> ParsingContext<'source> {
meta,
})?;
handle = frontend.module.types.insert(
handle = ctx.module.types.insert(
Type {
name: None,
inner: TypeInner::Array {
@ -175,7 +172,7 @@ impl<'source> ParsingContext<'source> {
let (name, mut meta) = self.expect_ident(frontend)?;
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) {
Some(ty) => FunctionCallKind::TypeConstructor(*ty),
@ -187,7 +184,7 @@ impl<'source> ParsingContext<'source> {
meta,
}
} else {
let var = match frontend.lookup_variable(ctx, body, &name, meta) {
let var = match frontend.lookup_variable(ctx, &name, meta)? {
Some(var) => var,
None => {
return Err(Error {
@ -205,7 +202,7 @@ impl<'source> ParsingContext<'source> {
stmt.hir_exprs.append(expr, Default::default())
} else {
self.parse_primary(frontend, ctx, stmt, body)?
self.parse_primary(frontend, ctx, stmt)?
};
while let TokenValue::LeftBracket
@ -217,7 +214,7 @@ impl<'source> ParsingContext<'source> {
match value {
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;
meta.subsume(end_meta);
@ -233,8 +230,7 @@ impl<'source> ParsingContext<'source> {
let (field, end_meta) = self.expect_ident(frontend)?;
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)?;
base = stmt.hir_exprs.append(
HirExpr {
@ -287,13 +283,12 @@ impl<'source> ParsingContext<'source> {
frontend: &mut Frontend,
ctx: &mut Context,
stmt: &mut StmtContext,
body: &mut Block,
) -> Result<Handle<HirExpr>> {
Ok(match self.expect_peek(frontend)?.value {
TokenValue::Plus | TokenValue::Dash | TokenValue::Bang | TokenValue::Tilde => {
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 kind = match value {
@ -315,7 +310,7 @@ impl<'source> ParsingContext<'source> {
TokenValue::Increment | TokenValue::Decrement => {
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(
HirExpr {
@ -332,7 +327,7 @@ impl<'source> ParsingContext<'source> {
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,
ctx: &mut Context,
stmt: &mut StmtContext,
body: &mut Block,
passthrough: Option<Handle<HirExpr>>,
min_bp: u8,
) -> Result<Handle<HirExpr>> {
let mut left = passthrough
.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;
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 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;
meta.subsume(end_meta);
@ -403,16 +397,15 @@ impl<'source> ParsingContext<'source> {
frontend: &mut Frontend,
ctx: &mut Context,
stmt: &mut StmtContext,
body: &mut Block,
passthrough: Option<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;
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)?;
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;
meta.subsume(end_meta);
@ -437,15 +430,14 @@ impl<'source> ParsingContext<'source> {
frontend: &mut Frontend,
ctx: &mut Context,
stmt: &mut StmtContext,
body: &mut Block,
) -> 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;
Ok(match self.expect_peek(frontend)?.value {
TokenValue::Assign => {
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;
meta.subsume(end_meta);
@ -468,7 +460,7 @@ impl<'source> ParsingContext<'source> {
| TokenValue::RightShiftAssign
| TokenValue::XorAssign => {
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;
meta.subsume(end_meta);
@ -504,7 +496,7 @@ impl<'source> ParsingContext<'source> {
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,
ctx: &mut Context,
stmt: &mut StmtContext,
body: &mut Block,
) -> 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 {
self.bump(frontend)?;
expr = self.parse_assignment(frontend, ctx, stmt, body)?;
expr = self.parse_assignment(frontend, ctx, stmt)?;
}
Ok(expr)

View file

@ -40,12 +40,11 @@ impl<'source> ParsingContext<'source> {
&mut self,
frontend: &mut Frontend,
ctx: &mut Context,
body: &mut Block,
terminator: &mut Option<usize>,
) -> Result<Option<Span>> {
// Type qualifiers always identify a declaration statement
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
@ -61,7 +60,7 @@ impl<'source> ParsingContext<'source> {
self.backtrack(token)?;
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 {
TokenValue::Continue => {
let meta = self.bump(frontend)?.meta;
body.push(Statement::Continue, meta);
terminator.get_or_insert(body.len());
ctx.body.push(Statement::Continue, meta);
terminator.get_or_insert(ctx.body.len());
self.expect(frontend, TokenValue::Semicolon)?.meta
}
TokenValue::Break => {
let meta = self.bump(frontend)?.meta;
body.push(Statement::Break, meta);
terminator.get_or_insert(body.len());
ctx.body.push(Statement::Break, meta);
terminator.get_or_insert(ctx.body.len());
self.expect(frontend, TokenValue::Semicolon)?.meta
}
TokenValue::Return => {
@ -96,25 +95,25 @@ impl<'source> ParsingContext<'source> {
_ => {
// TODO: Implicit conversions
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)?;
let (handle, meta) =
ctx.lower_expect(stmt, frontend, expr, ExprPos::Rhs, body)?;
ctx.lower_expect(stmt, frontend, expr, ExprPos::Rhs)?;
(Some(handle), meta)
}
};
ctx.emit_restart(body);
ctx.emit_restart();
body.push(Statement::Return { value }, meta);
terminator.get_or_insert(body.len());
ctx.body.push(Statement::Return { value }, meta);
terminator.get_or_insert(ctx.body.len());
meta
}
TokenValue::Discard => {
let meta = self.bump(frontend)?.meta;
body.push(Statement::Kill, meta);
terminator.get_or_insert(body.len());
ctx.body.push(Statement::Kill, meta);
terminator.get_or_insert(ctx.body.len());
self.expect(frontend, TokenValue::Semicolon)?.meta
}
@ -124,33 +123,31 @@ impl<'source> ParsingContext<'source> {
self.expect(frontend, TokenValue::LeftParen)?;
let condition = {
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) =
ctx.lower_expect(stmt, frontend, expr, ExprPos::Rhs, body)?;
ctx.lower_expect(stmt, frontend, expr, ExprPos::Rhs)?;
meta.subsume(more_meta);
handle
};
self.expect(frontend, TokenValue::RightParen)?;
ctx.emit_restart(body);
let mut accept = Block::new();
if let Some(more_meta) =
self.parse_statement(frontend, ctx, &mut accept, &mut None)?
{
meta.subsume(more_meta)
let accept = ctx.new_body(|ctx| {
if let Some(more_meta) = self.parse_statement(frontend, ctx, &mut None)? {
meta.subsume(more_meta);
}
Ok(())
})?;
let mut reject = Block::new();
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 reject, &mut None)?
{
if let Some(more_meta) = self.parse_statement(frontend, ctx, &mut None)? {
meta.subsume(more_meta);
}
}
Ok(())
})?;
body.push(
ctx.body.push(
Statement::If {
condition,
accept,
@ -169,17 +166,16 @@ impl<'source> ParsingContext<'source> {
let (selector, uint) = {
let mut stmt = ctx.stmt_ctx();
let expr = self.parse_expression(frontend, ctx, &mut stmt, body)?;
let (root, meta) =
ctx.lower_expect(stmt, frontend, expr, ExprPos::Rhs, body)?;
let uint = frontend.resolve_type(ctx, root, meta)?.scalar_kind()
let expr = self.parse_expression(frontend, ctx, &mut stmt)?;
let (root, meta) = ctx.lower_expect(stmt, frontend, expr, ExprPos::Rhs)?;
let uint = ctx.resolve_type(root, meta)?.scalar_kind()
== Some(crate::ScalarKind::Uint);
(root, uint)
};
self.expect(frontend, TokenValue::RightParen)?;
ctx.emit_restart(body);
ctx.emit_restart();
let mut cases = Vec::new();
// Track if any default case is present in the switch statement.
@ -192,13 +188,18 @@ impl<'source> ParsingContext<'source> {
self.bump(frontend)?;
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) =
ctx.lower_expect(stmt, frontend, expr, ExprPos::Rhs, body)?;
let const_expr = frontend.solve_constant(ctx, root, meta)?;
ctx.lower_expect(stmt, frontend, expr, ExprPos::Rhs)?;
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 {
// 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),
false => crate::SwitchValue::I32(value),
},
@ -244,8 +245,9 @@ impl<'source> ParsingContext<'source> {
self.expect(frontend, TokenValue::Colon)?;
let mut body = Block::new();
let mut fall_through = true;
let body = ctx.new_body(|ctx| {
let mut case_terminator = None;
loop {
match self.expect_peek(frontend)?.value {
@ -253,27 +255,23 @@ impl<'source> ParsingContext<'source> {
break
}
_ => {
self.parse_statement(
frontend,
ctx,
&mut body,
&mut case_terminator,
)?;
self.parse_statement(frontend, ctx, &mut case_terminator)?;
}
}
}
let mut fall_through = true;
if let Some(mut idx) = case_terminator {
if let Statement::Break = body[idx - 1] {
if let Statement::Break = ctx.body[idx - 1] {
fall_through = false;
idx -= 1;
}
body.cull(idx..)
ctx.body.cull(idx..)
}
Ok(())
})?;
cases.push(SwitchCase {
value,
body,
@ -317,34 +315,31 @@ impl<'source> ParsingContext<'source> {
})
}
body.push(Statement::Switch { selector, cases }, meta);
ctx.body.push(Statement::Switch { selector, cases }, meta);
meta
}
TokenValue::While => {
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, &mut loop_body)?;
let root = self.parse_expression(frontend, ctx, &mut stmt)?;
meta.subsume(self.expect(frontend, TokenValue::RightParen)?.meta);
let (expr, expr_meta) =
ctx.lower_expect(stmt, frontend, root, ExprPos::Rhs, &mut loop_body)?;
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,
&mut loop_body,
);
)?;
ctx.emit_restart(&mut loop_body);
ctx.emit_restart();
loop_body.push(
ctx.body.push(
Statement::If {
condition,
accept: new_break(),
@ -355,13 +350,13 @@ impl<'source> ParsingContext<'source> {
meta.subsume(expr_meta);
if let Some(body_meta) =
self.parse_statement(frontend, ctx, &mut loop_body, &mut None)?
{
if let Some(body_meta) = self.parse_statement(frontend, ctx, &mut None)? {
meta.subsume(body_meta);
}
Ok(())
})?;
body.push(
ctx.body.push(
Statement::Loop {
body: loop_body,
continuing: Block::new(),
@ -375,34 +370,31 @@ impl<'source> ParsingContext<'source> {
TokenValue::Do => {
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 loop_body, &mut terminator)?;
self.parse_statement(frontend, ctx, &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, &mut loop_body)?;
let root = self.parse_expression(frontend, ctx, &mut stmt)?;
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, &mut loop_body)?;
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,
&mut loop_body,
);
)?;
ctx.emit_restart(&mut loop_body);
ctx.emit_restart();
loop_body.push(
ctx.body.push(
Statement::If {
condition,
accept: new_break(),
@ -412,10 +404,12 @@ impl<'source> ParsingContext<'source> {
);
if let Some(idx) = terminator {
loop_body.cull(idx..)
ctx.body.cull(idx..)
}
Ok(())
})?;
body.push(
ctx.body.push(
Statement::Loop {
body: loop_body,
continuing: Block::new(),
@ -434,29 +428,27 @@ impl<'source> ParsingContext<'source> {
if self.bump_if(frontend, TokenValue::Semicolon).is_none() {
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 {
let mut stmt = ctx.stmt_ctx();
let expr = self.parse_expression(frontend, ctx, &mut stmt, body)?;
ctx.lower(stmt, frontend, expr, ExprPos::Rhs, body)?;
let expr = self.parse_expression(frontend, ctx, &mut stmt)?;
ctx.lower(stmt, frontend, expr, ExprPos::Rhs)?;
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)?;
let (ty, mut meta) = self.parse_type_non_void(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;
self.expect(frontend, TokenValue::Assign)?;
let (value, end_meta) =
self.parse_initializer(frontend, ty, ctx, &mut block)?;
let (value, end_meta) = self.parse_initializer(frontend, ty, ctx)?;
meta.subsume(end_meta);
let decl = VarDeclaration {
@ -467,17 +459,17 @@ impl<'source> ParsingContext<'source> {
meta,
};
let pointer = frontend.add_local_var(ctx, &mut block, decl)?;
let pointer = frontend.add_local_var(ctx, decl)?;
ctx.emit_restart(&mut block);
ctx.emit_restart();
block.push(Statement::Store { pointer, value }, meta);
ctx.body.push(Statement::Store { pointer, value }, meta);
(value, end_meta)
} 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 root = self.parse_expression(frontend, ctx, &mut stmt)?;
ctx.lower_expect(stmt, frontend, root, ExprPos::Rhs)?
};
let condition = ctx.add_expression(
@ -486,12 +478,11 @@ impl<'source> ParsingContext<'source> {
expr,
},
expr_meta,
&mut block,
);
)?;
ctx.emit_restart(&mut block);
ctx.emit_restart();
block.push(
ctx.body.push(
Statement::If {
condition,
accept: new_break(),
@ -502,28 +493,33 @@ impl<'source> ParsingContext<'source> {
self.expect(frontend, TokenValue::Semicolon)?;
}
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, &mut continuing)?;
ctx.lower(stmt, frontend, rest, ExprPos::Rhs, &mut continuing)?;
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);
if let Some(stmt_meta) =
self.parse_statement(frontend, ctx, &mut block, &mut None)?
{
let loop_body = ctx.with_body(loop_body, |ctx| {
if let Some(stmt_meta) = self.parse_statement(frontend, ctx, &mut None)? {
meta.subsume(stmt_meta);
}
Ok(())
})?;
body.push(
ctx.body.push(
Statement::Loop {
body: block,
body: loop_body,
continuing,
break_if: None,
},
@ -535,22 +531,20 @@ impl<'source> ParsingContext<'source> {
meta
}
TokenValue::LeftBrace => {
let meta = self.bump(frontend)?.meta;
let mut block = Block::new();
let mut meta = self.bump(frontend)?.meta;
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() {
terminator.get_or_insert(body.len());
terminator.get_or_insert(ctx.body.len());
}
meta
@ -561,8 +555,8 @@ impl<'source> ParsingContext<'source> {
// tokens. Unknown or invalid tokens will be caught there and
// turned into an error.
let mut stmt = ctx.stmt_ctx();
let expr = self.parse_expression(frontend, ctx, &mut stmt, body)?;
ctx.lower(stmt, frontend, expr, ExprPos::Rhs, body)?;
let expr = self.parse_expression(frontend, ctx, &mut stmt)?;
ctx.lower(stmt, frontend, expr, ExprPos::Rhs)?;
self.expect(frontend, TokenValue::Semicolon)?.meta
}
};
@ -576,7 +570,6 @@ impl<'source> ParsingContext<'source> {
mut meta: Span,
frontend: &mut Frontend,
ctx: &mut Context,
body: &mut Block,
terminator: &mut Option<usize>,
) -> Result<Span> {
ctx.symbol_table.push_scope();
@ -590,7 +583,7 @@ impl<'source> ParsingContext<'source> {
break;
}
let stmt = self.parse_statement(frontend, ctx, body, terminator)?;
let stmt = self.parse_statement(frontend, ctx, terminator)?;
if let Some(stmt_meta) = stmt {
meta.subsume(stmt_meta);
@ -598,7 +591,7 @@ impl<'source> ParsingContext<'source> {
}
if let Some(idx) = *terminator {
body.cull(idx..)
ctx.body.cull(idx..)
}
ctx.symbol_table.pop_scope();
@ -609,8 +602,7 @@ impl<'source> ParsingContext<'source> {
pub fn parse_function_args(
&mut self,
frontend: &mut Frontend,
context: &mut Context,
body: &mut Block,
ctx: &mut Context,
) -> Result<()> {
if self.bump_if(frontend, TokenValue::Void).is_some() {
return Ok(());
@ -619,19 +611,19 @@ impl<'source> ParsingContext<'source> {
loop {
if self.peek_type_name(frontend) || self.peek_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 {
TokenValue::Comma => {
self.bump(frontend)?;
context.add_function_arg(frontend, body, None, ty, qualifier);
ctx.add_function_arg(None, ty, qualifier)?;
continue;
}
TokenValue::Identifier(_) => {
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() {
continue;

View file

@ -3,6 +3,7 @@ use std::num::NonZeroU32;
use crate::{
front::glsl::{
ast::{QualifierKey, QualifierValue, StorageQualifier, StructLayout, TypeQualifiers},
context::Context,
error::ExpectedToken,
parser::ParsingContext,
token::{Token, TokenValue},
@ -17,10 +18,11 @@ impl<'source> ParsingContext<'source> {
pub fn parse_array_specifier(
&mut self,
frontend: &mut Frontend,
ctx: &mut Context,
span: &mut Span,
ty: &mut Handle<Type>,
) -> Result<()> {
while self.parse_array_specifier_single(frontend, span, ty)? {}
while self.parse_array_specifier_single(frontend, ctx, span, ty)? {}
Ok(())
}
@ -28,6 +30,7 @@ impl<'source> ParsingContext<'source> {
fn parse_array_specifier_single(
&mut self,
frontend: &mut Frontend,
ctx: &mut Context,
span: &mut Span,
ty: &mut Handle<Type>,
) -> Result<bool> {
@ -38,7 +41,7 @@ impl<'source> ParsingContext<'source> {
span.subsume(meta);
ArraySize::Dynamic
} 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 {
kind: ErrorKind::SemanticError("Array size must be greater than zero".into()),
meta: constant_span,
@ -48,9 +51,9 @@ impl<'source> ParsingContext<'source> {
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();
*ty = frontend.module.types.insert(
*ty = ctx.module.types.insert(
Type {
name: None,
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 mut handle = match token.value {
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 => {
let mut meta = token.meta;
let ty_name = self.expect_ident(frontend)?.0;
@ -80,12 +87,13 @@ impl<'source> ParsingContext<'source> {
let mut members = Vec::new();
let span = self.parse_struct_declaration_list(
frontend,
ctx,
&mut members,
StructLayout::Std140,
)?;
let end_meta = self.expect(frontend, TokenValue::RightBrace)?.meta;
meta.subsume(end_meta);
let ty = frontend.module.types.insert(
let ty = ctx.module.types.insert(
Type {
name: Some(ty_name.clone()),
inner: TypeInner::Struct { members, span },
@ -120,12 +128,16 @@ impl<'source> ParsingContext<'source> {
};
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))
}
pub fn parse_type_non_void(&mut self, frontend: &mut Frontend) -> Result<(Handle<Type>, Span)> {
let (maybe_ty, meta) = self.parse_type(frontend)?;
pub fn parse_type_non_void(
&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 {
kind: ErrorKind::SemanticError("Type can't be void".into()),
meta,
@ -156,6 +168,7 @@ impl<'source> ParsingContext<'source> {
pub fn parse_type_qualifiers<'a>(
&mut self,
frontend: &mut Frontend,
ctx: &mut Context,
) -> Result<TypeQualifiers<'a>> {
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
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;
}
@ -287,11 +300,12 @@ impl<'source> ParsingContext<'source> {
pub fn parse_layout_qualifier_id_list(
&mut self,
frontend: &mut Frontend,
ctx: &mut Context,
qualifiers: &mut TypeQualifiers,
) -> Result<()> {
self.expect(frontend, TokenValue::LeftParen)?;
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() {
continue;
@ -308,6 +322,7 @@ impl<'source> ParsingContext<'source> {
pub fn parse_layout_qualifier_id(
&mut self,
frontend: &mut Frontend,
ctx: &mut Context,
qualifiers: &mut crate::FastHashMap<QualifierKey, (QualifierValue, Span)>,
) -> Result<()> {
// layout_qualifier_id:
@ -332,7 +347,8 @@ impl<'source> ParsingContext<'source> {
} else {
let key = QualifierKey::String(name.into());
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) =
match self.parse_uint_constant(frontend, ctx) {
Ok(v) => v,
Err(e) => {
frontend.errors.push(e);

View file

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

View file

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

View file

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

View file

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

View file

@ -5,55 +5,6 @@ Type generators.
use crate::{arena::Handle, span::Span};
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.
///
/// [`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);
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,
ExpectedArraySize(Span),
NonPositiveArrayLength(Span),
MissingWorkgroupSize(Span),
}
impl<'a> Error<'a> {
@ -433,7 +434,7 @@ impl<'a> Error<'a> {
},
Error::RepeatedAttribute(bad_span) => ParseError {
message: format!("repeated attribute: '{}'", &source[bad_span]),
labels: vec![(bad_span, "repated attribute".into())],
labels: vec![(bad_span, "repeated attribute".into())],
notes: vec![],
},
Error::UnknownAttribute(bad_span) => ParseError {
@ -704,6 +705,14 @@ impl<'a> Error<'a> {
labels: vec![(span, "must be greater than zero".into())],
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.
///
/// 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(
&mut self,
op: crate::BinaryOperator,
left: &mut Handle<crate::Expression>,
right: &mut Handle<crate::Expression>,
) -> 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)?;
let left_size = match *self.resolved_inner(*left) {
crate::TypeInner::Vector { size, .. } => Some(size),
_ => None,
};
match (left_size, self.resolved_inner(*right)) {
(Some(size), &crate::TypeInner::Scalar { .. }) => {
match (self.resolved_inner(*left), self.resolved_inner(*right)) {
(&crate::TypeInner::Vector { size, .. }, &crate::TypeInner::Scalar { .. }) => {
*right = self.append_expression(
crate::Expression::Splat {
size,
@ -528,7 +535,7 @@ impl<'source, 'temp, 'out> ExpressionContext<'source, 'temp, 'out> {
self.get_expression_span(*right),
);
}
(None, &crate::TypeInner::Vector { size, .. }) => {
(&crate::TypeInner::Scalar { .. }, &crate::TypeInner::Vector { size, .. }) => {
*left = self.append_expression(
crate::Expression::Splat { size, value: *left },
self.get_expression_span(*left),
@ -967,7 +974,7 @@ impl<'source, 'temp> Lowerer<'source, 'temp> {
name: f.name.name.to_string(),
stage: entry.stage,
early_depth_test: entry.early_depth_test,
workgroup_size: entry.workgroup_size,
workgroup_size: entry.workgroup_size.unwrap_or([0, 0, 0]),
function,
});
Ok(LoweredGlobalDecl::EntryPoint)
@ -1689,7 +1696,26 @@ impl<'source, 'temp> Lowerer<'source, 'temp> {
let argument = self.expression(args.next()?, ctx.reborrow())?;
args.finish()?;
// 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) {
let mut args = ctx.prepare_args(arguments, 1, span);
let expr = self.expression(args.next()?, ctx.reborrow())?;
@ -1719,6 +1745,25 @@ impl<'source, 'temp> Lowerer<'source, 'temp> {
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 {
fun,
arg,
@ -1854,10 +1899,12 @@ impl<'source, 'temp> Lowerer<'source, 'temp> {
let expression = match *ctx.resolved_inner(value) {
crate::TypeInner::Scalar { kind, width } => {
crate::Expression::AtomicResult {
//TODO: cache this to avoid generating duplicate types
ty: ctx
.module
.generate_atomic_compare_exchange_result(kind, width),
ty: ctx.module.generate_predeclared_type(
crate::PredeclaredType::AtomicCompareExchangeWeakResult {
kind,
width,
},
),
comparison: true,
}
}

View file

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

View file

@ -387,13 +387,80 @@ fn parse_expressions() {
}").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]
fn parse_pointers() {
parse_str(
"fn foo() {
"fn foo(a: ptr<private, f32>) -> f32 { return *a; }
fn bar() {
var x: f32 = 1.0;
let px = &x;
let py = frexp(0.5, px);
let py = foo(px);
}",
)
.unwrap();
@ -534,6 +601,7 @@ fn parse_repeated_attributes() {
("size(16)", template_struct),
("vertex", template_stage),
("early_depth_test(less_equal)", template_resource),
("workgroup_size(1)", template_stage),
] {
let shader = template.replace("__REPLACE__", &format!("@{attribute} @{attribute}"));
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
Location {
location: u32,
/// Indicates the 2nd input to the blender when dual-source blending.
second_blend_source: bool,
interpolation: Option<Interpolation>,
sampling: Option<Sampling>,
},
@ -1943,6 +1945,31 @@ pub struct EntryPoint {
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.
#[derive(Debug, Default)]
#[cfg_attr(feature = "clone", derive(Clone))]
@ -1961,6 +1988,12 @@ pub struct SpecialTypes {
/// Call [`Module::generate_ray_intersection_type`] to populate
/// this if needed and return the handle.
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.

View file

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

View file

@ -706,8 +706,6 @@ impl<'a> ResolveContext<'a> {
Mf::Round |
Mf::Fract |
Mf::Trunc |
Mf::Modf |
Mf::Frexp |
Mf::Ldexp |
// exponent
Mf::Exp |
@ -715,6 +713,31 @@ impl<'a> ResolveContext<'a> {
Mf::Log |
Mf::Log2 |
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
Mf::Dot => match *res_arg.inner_with(types) {
Ti::Vector {

View file

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

View file

@ -893,7 +893,7 @@ impl super::Validator {
let arg3_ty = arg3.map(resolve);
match fun {
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));
}
let good = match *arg_ty {
@ -976,10 +976,9 @@ impl super::Validator {
| Mf::Log
| Mf::Log2
| Mf::Length
| Mf::Sign
| Mf::Sqrt
| 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));
}
match *arg_ty {
@ -992,6 +991,22 @@ impl super::Validator {
_ => 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 => {
let arg1_ty = match (arg1_ty, arg2_ty, arg3_ty) {
(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) {
(Some(ty1), None, None) => ty1,
_ => return Err(ExpressionError::WrongArgumentCount(fun)),
};
let (size0, width0) = match *arg_ty {
let size0 = match *arg_ty {
Ti::Scalar {
kind: Sk::Float,
width,
} => (None, width),
kind: Sk::Float, ..
} => None,
Ti::Vector {
kind: Sk::Float,
size,
width,
} => (Some(size), width),
_ => return Err(ExpressionError::InvalidArgumentType(fun, 0, arg)),
..
} => Some(size),
_ => {
return Err(ExpressionError::InvalidArgumentType(fun, 0, arg));
}
};
let good = match *arg1_ty {
Ti::Pointer { base, space: _ } => module.types[base].inner == *arg_ty,
Ti::ValuePointer {
Ti::Scalar { kind: Sk::Sint, .. } if size0.is_none() => true,
Ti::Vector {
size,
kind: Sk::Float,
width,
space: _,
} => size == size0 && width == width0,
kind: Sk::Sint,
..
} if Some(size) == size0 => true,
_ => false,
};
if !good {
@ -1130,7 +1162,7 @@ impl super::Validator {
}
}
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));
}
match *arg_ty {
@ -1210,7 +1242,7 @@ impl super::Validator {
}
}
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));
}
let good = match *arg_ty {
@ -1222,7 +1254,7 @@ impl super::Validator {
}
}
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));
}
match *arg_ty {
@ -1236,7 +1268,7 @@ impl super::Validator {
| Mf::ReverseBits
| Mf::FindLsb
| 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));
}
match *arg_ty {
@ -1333,7 +1365,7 @@ impl super::Validator {
}
}
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));
}
match *arg_ty {
@ -1346,7 +1378,7 @@ impl super::Validator {
}
}
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));
}
match *arg_ty {
@ -1363,7 +1395,7 @@ impl super::Validator {
| Mf::Unpack2x16unorm
| Mf::Unpack4x8snorm
| 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));
}
match *arg_ty {

View file

@ -61,6 +61,17 @@ pub enum VaryingError {
DuplicateBuiltIn(crate::BuiltIn),
#[error("Capability {0:?} is not supported")]
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)]
@ -89,6 +100,10 @@ pub enum EntryPointError {
InvalidIntegerInterpolation { location: u32 },
#[error(transparent)]
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")]
@ -106,6 +121,7 @@ fn storage_usage(access: crate::StorageAccess) -> GlobalUse {
struct VaryingContext<'a> {
stage: crate::ShaderStage,
output: bool,
second_blend_source: bool,
types: &'a UniqueArena<crate::Type>,
type_info: &'a Vec<super::r#type::TypeInfo>,
location_mask: &'a mut BitSet,
@ -293,6 +309,7 @@ impl VaryingContext<'_> {
location,
interpolation,
sampling,
second_blend_source,
} => {
// Only IO-shareable types may be stored in locations.
if !self.type_info[ty.index()]
@ -301,7 +318,37 @@ impl VaryingContext<'_> {
{
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")]
if self.flags.contains(super::ValidationFlags::BINDINGS) {
return Err(VaryingError::BindingCollision { location });
@ -567,7 +614,8 @@ impl super::Validator {
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)
.map_err(WithSpan::into_other)?;
@ -593,6 +641,7 @@ impl super::Validator {
let mut ctx = VaryingContext {
stage: ep.stage,
output: false,
second_blend_source: false,
types: &module.types,
type_info: &self.types,
location_mask: &mut self.location_mask,
@ -612,6 +661,7 @@ impl super::Validator {
let mut ctx = VaryingContext {
stage: ep.stage,
output: true,
second_blend_source: false,
types: &module.types,
type_info: &self.types,
location_mask: &mut self.location_mask,
@ -623,6 +673,18 @@ impl super::Validator {
};
ctx.validate(fr.ty, fr.binding.as_ref())
.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")]
if ep.stage == crate::ShaderStage::Vertex

View file

@ -112,6 +112,8 @@ bitflags::bitflags! {
const MULTISAMPLED_SHADING = 0x800;
/// Support for ray queries and acceleration structures.
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]
version = "0.13.0"
git = "https://github.com/gfx-rs/naga"
rev = "7a19f3af909202c7eafd36633b5584bfbb353ecb"
rev = "cc87b8f9eb30bb55d0735b89d3df3e099e1a6e7c"
features = [
"clone",
"span",

View file

@ -2134,7 +2134,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
let (mut surface_guard, mut token) = self.surfaces.write(&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 device = match device_guard.get(device_id) {
@ -2207,6 +2207,24 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
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 {
A::get_surface_mut(surface)
.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 {
device_id: Stored {
value: id::Valid(device_id),

View file

@ -1,6 +1,5 @@
use crate::{
binding_model,
device::life::WaitIdleError,
hal_api::HalApi,
hub::Hub,
id,
@ -24,7 +23,7 @@ pub mod queue;
pub mod resource;
#[cfg(any(feature = "trace", feature = "replay"))]
pub mod trace;
pub use resource::Device;
pub use {life::WaitIdleError, resource::Device};
pub const SHADER_STAGE_COUNT: usize = 3;
// 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(),
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 {
log::trace!("Instance::new: backend {:?} not requested", A::VARIANT);
None
}
}

View file

@ -15,7 +15,7 @@ use std::borrow::Borrow;
use crate::device::trace::Action;
use crate::{
conv,
device::{DeviceError, MissingDownlevelFlags},
device::{DeviceError, MissingDownlevelFlags, WaitIdleError},
global::Global,
hal_api::HalApi,
hub::Token,
@ -96,6 +96,18 @@ pub enum ConfigureSurfaceError {
},
#[error("Requested usage is not supported")]
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)]

View file

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

File diff suppressed because one or more lines are too long

View file

@ -64,7 +64,7 @@ optional = true
[dependencies.naga]
version = "0.13.0"
git = "https://github.com/gfx-rs/naga"
rev = "7a19f3af909202c7eafd36633b5584bfbb353ecb"
rev = "cc87b8f9eb30bb55d0735b89d3df3e099e1a6e7c"
features = ["clone"]
[dependencies.profiling]
@ -83,7 +83,7 @@ env_logger = "0.10"
[dev-dependencies.naga]
version = "0.13.0"
git = "https://github.com/gfx-rs/naga"
rev = "7a19f3af909202c7eafd36633b5584bfbb353ecb"
rev = "cc87b8f9eb30bb55d0735b89d3df3e099e1a6e7c"
features = ["wgsl-in"]
[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();
if cur_value == !0 {
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;
// We always set ALLOW_TEARING on the swapchain no matter
// 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 {
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 swap_chain = match self.swap_chain.take() {
//Note: this path doesn't properly re-initialize all of the things
Some(sc) => {
// can't have image resources in flight used by GPU
let _ = unsafe { device.wait_idle() };
let raw = unsafe { sc.release_resources() };
let result = unsafe {
raw.ResizeBuffers(
@ -773,12 +777,16 @@ impl crate::Surface<Api> for Surface {
}
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 {
let _ = sc.wait(None);
//TODO: this shouldn't be needed,
// but it complains that the queue is still used otherwise
let _ = device.wait_idle();
// While `unconfigure`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.
// 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();
}
}
@ -837,6 +845,13 @@ impl crate::Queue<Api> for Queue {
.signal(&fence.raw, value)
.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(())
}
unsafe fn present(

View file

@ -227,12 +227,28 @@ pub trait Instance<A: Api>: Sized + 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(
&mut self,
device: &A::Device,
config: &SurfaceConfiguration,
) -> 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);
/// 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 {
// 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.
vk_info = vk_info
.anisotropy_enable(true)

View file

@ -152,12 +152,11 @@ unsafe extern "system" fn debug_utils_messenger_callback(
}
impl super::Swapchain {
/// # Safety
///
/// - The device must have been made idle before calling this function.
unsafe fn release_resources(self, device: &ash::Device) -> Self {
profiling::scope!("Swapchain::release_resources");
{
profiling::scope!("vkDeviceWaitIdle");
let _ = unsafe { device.device_wait_idle() };
};
unsafe { device.destroy_fence(self.fence, None) };
self
}
@ -186,7 +185,20 @@ impl super::Instance {
&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,
_driver_api_version: u32,
flags: crate::InstanceFlags,
@ -265,7 +277,7 @@ impl super::Instance {
///
/// - `raw_instance` must be created from `entry`
/// - `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.
/// - `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| {
log::info!("enumerate_instance_layer_properties: {:?}", e);
@ -786,9 +798,16 @@ impl crate::Instance<super::Api> for super::Instance {
if exposed.info.device_type == wgt::DeviceType::IntegratedGpu
&& exposed.info.vendor == db::intel::VENDOR
{
// Check if mesa driver and version less than 21.2
if let Some(version) = exposed.info.driver_info.split_once("Mesa ").map(|s| {
s.1.rsplit_once('.')
.map(|v| v.0.parse::<f32>().unwrap_or_default())
.unwrap_or_default()
}) {
if version < 21.2 {
// See https://gitlab.freedesktop.org/mesa/mesa/-/issues/4688
log::warn!(
"Disabling presentation on '{}' (id {:?}) because of NV Optimus (on Linux)",
"Disabling presentation on '{}' (id {:?}) due to NV Optimus and Intel Mesa < v21.2",
exposed.info.name,
exposed.adapter.raw
);
@ -796,6 +815,8 @@ impl crate::Instance<super::Api> for super::Instance {
}
}
}
}
}
exposed_adapters
}
@ -807,6 +828,7 @@ impl crate::Surface<super::Api> for super::Surface {
device: &super::Device,
config: &crate::SurfaceConfiguration,
) -> Result<(), crate::SurfaceError> {
// Safety: `configure`'s contract guarantees there are no resources derived from the swapchain in use.
let old = self
.swapchain
.take()
@ -820,6 +842,7 @@ impl crate::Surface<super::Api> for super::Surface {
unsafe fn unconfigure(&mut self, device: &super::Device) {
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) };
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
[dev-dependencies]
serde_json = "1.0.105"
serde_json = "1.0.106"
[dev-dependencies.serde]
version = "1"