Skip to content

Commit a4a94e7

Browse files
committed
[naga] Test validation of function/entry point name collision.
Unlike WGSL, Naga IR permits functions to have conflicting names, and entry points must only have distinct names if they are for the same shader stage. Document and test this.
1 parent 7a65558 commit a4a94e7

2 files changed

Lines changed: 149 additions & 1 deletion

File tree

naga/src/ir/mod.rs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2411,6 +2411,12 @@ pub struct FunctionResult {
24112411
#[cfg_attr(feature = "arbitrary", derive(Arbitrary))]
24122412
pub struct Function {
24132413
/// Name of the function, if any.
2414+
///
2415+
/// Unlike WGSL, Naga IR allows a module to have multiple functions with the
2416+
/// same name. Since functions are generally identified by handle, the name
2417+
/// is mostly needed for diagnostics and as a hint to [`Namer`].
2418+
///
2419+
/// [`Namer`]: crate::proc::Namer
24142420
pub name: Option<String>,
24152421
/// Information about function argument.
24162422
pub arguments: Vec<FunctionArgument>,
@@ -2502,7 +2508,9 @@ pub struct Function {
25022508
pub struct EntryPoint {
25032509
/// Name of this entry point, visible externally.
25042510
///
2505-
/// Entry point names for a given `stage` must be distinct within a module.
2511+
/// Unlike WGSL, Naga IR allows a module to have multiple entry points with
2512+
/// the same name, as long as they are for different shader stages. That is,
2513+
/// `(name, stage)` pairs must be distinct within a module.
25062514
pub name: String,
25072515
/// Shader stage.
25082516
pub stage: ShaderStage,

naga/tests/naga/validation.rs

Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1753,3 +1753,143 @@ fn memory_decorations_require_storage_address_space() {
17531753
}
17541754
));
17551755
}
1756+
1757+
/// Unlike WGSL, Naga IR permits multiple entry points to have the
1758+
/// same name, as long as they are for distinct stages.
1759+
#[test]
1760+
fn entry_points_distinguished_by_stage() {
1761+
let mut test_spans = TestSpanGenerator::default();
1762+
let mut module = Module::default();
1763+
1764+
let ty_vec4f = module.types.insert(
1765+
ir::Type {
1766+
name: Some("vec4f".to_string()),
1767+
inner: ir::TypeInner::Vector {
1768+
size: ir::VectorSize::Quad,
1769+
scalar: ir::Scalar::F32,
1770+
},
1771+
},
1772+
test_spans.next(),
1773+
);
1774+
1775+
let vertex_function = ir::Function {
1776+
name: Some("non_unique_name".into()),
1777+
result: Some(ir::FunctionResult {
1778+
ty: ty_vec4f,
1779+
binding: Some(ir::Binding::BuiltIn(ir::BuiltIn::Position {
1780+
invariant: false,
1781+
})),
1782+
}),
1783+
..ir::Function::default()
1784+
};
1785+
module.entry_points.push(ir::EntryPoint {
1786+
name: "non_unique_name".into(),
1787+
stage: ir::ShaderStage::Vertex,
1788+
early_depth_test: None,
1789+
workgroup_size: [0, 0, 0],
1790+
workgroup_size_overrides: None,
1791+
function: vertex_function,
1792+
mesh_info: None,
1793+
task_payload: None,
1794+
incoming_ray_payload: None,
1795+
});
1796+
1797+
module.entry_points.push(ir::EntryPoint {
1798+
name: "non_unique_name".into(),
1799+
stage: ir::ShaderStage::Compute,
1800+
early_depth_test: None,
1801+
workgroup_size: [1, 1, 1],
1802+
workgroup_size_overrides: None,
1803+
function: ir::Function::default(),
1804+
mesh_info: None,
1805+
task_payload: None,
1806+
incoming_ray_payload: None,
1807+
});
1808+
1809+
valid::Validator::new(
1810+
valid::ValidationFlags::default(),
1811+
valid::Capabilities::default(),
1812+
)
1813+
.validate(&module)
1814+
.expect("module should be valid");
1815+
}
1816+
1817+
/// It is not permitted for a `Module` to have multiple entry points with the
1818+
/// same name and the same stage.
1819+
#[test]
1820+
fn entry_points_share_name() {
1821+
let mut module = Module::default();
1822+
1823+
module.entry_points.push(ir::EntryPoint {
1824+
name: "non_unique_name".into(),
1825+
stage: ir::ShaderStage::Compute,
1826+
early_depth_test: None,
1827+
workgroup_size: [1, 1, 1],
1828+
workgroup_size_overrides: None,
1829+
function: ir::Function::default(),
1830+
mesh_info: None,
1831+
task_payload: None,
1832+
incoming_ray_payload: None,
1833+
});
1834+
1835+
module.entry_points.push(ir::EntryPoint {
1836+
name: "non_unique_name".into(),
1837+
stage: ir::ShaderStage::Compute,
1838+
early_depth_test: None,
1839+
workgroup_size: [1, 1, 1],
1840+
workgroup_size_overrides: None,
1841+
function: ir::Function::default(),
1842+
mesh_info: None,
1843+
task_payload: None,
1844+
incoming_ray_payload: None,
1845+
});
1846+
1847+
let err = valid::Validator::new(
1848+
valid::ValidationFlags::default(),
1849+
valid::Capabilities::default(),
1850+
)
1851+
.validate(&module)
1852+
.expect_err("module should be invalid");
1853+
1854+
assert!(matches!(
1855+
err.into_inner(),
1856+
valid::ValidationError::EntryPoint {
1857+
source: valid::EntryPointError::Conflict,
1858+
..
1859+
}
1860+
));
1861+
}
1862+
1863+
/// Unlike WGSL, Naga IR permits a `Module` to have multiple non-entry-point
1864+
/// functions with the same name. (Calls identify callees by handle, so the
1865+
/// names aren't dispositive.)
1866+
#[test]
1867+
fn functions_share_name() {
1868+
let mut test_spans = TestSpanGenerator::default();
1869+
let mut module = Module::default();
1870+
1871+
module.functions.append(
1872+
ir::Function {
1873+
name: Some("non_unique_name".into()),
1874+
result: None,
1875+
..ir::Function::default()
1876+
},
1877+
test_spans.next(),
1878+
);
1879+
1880+
module.functions.append(
1881+
ir::Function {
1882+
name: Some("non_unique_name".into()),
1883+
result: None,
1884+
..ir::Function::default()
1885+
},
1886+
test_spans.next(),
1887+
);
1888+
1889+
valid::Validator::new(
1890+
valid::ValidationFlags::default(),
1891+
valid::Capabilities::default(),
1892+
)
1893+
.validate(&module)
1894+
.expect("module should be valid");
1895+
}

0 commit comments

Comments
 (0)