Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 9 additions & 1 deletion naga/src/ir/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2411,6 +2411,12 @@ pub struct FunctionResult {
#[cfg_attr(feature = "arbitrary", derive(Arbitrary))]
pub struct Function {
/// Name of the function, if any.
///
/// Unlike WGSL, Naga IR allows a module to have multiple functions with the
/// same name. Since functions are generally identified by handle, the name
/// is mostly needed for diagnostics and as a hint to [`Namer`].
///
/// [`Namer`]: crate::proc::Namer
pub name: Option<String>,
/// Information about function argument.
pub arguments: Vec<FunctionArgument>,
Expand Down Expand Up @@ -2502,7 +2508,9 @@ pub struct Function {
pub struct EntryPoint {
/// Name of this entry point, visible externally.
///
/// Entry point names for a given `stage` must be distinct within a module.
/// Unlike WGSL, Naga IR allows a module to have multiple entry points with
/// the same name, as long as they are for different shader stages. That is,
/// `(name, stage)` pairs must be distinct within a module.
pub name: String,
/// Shader stage.
pub stage: ShaderStage,
Expand Down
147 changes: 145 additions & 2 deletions naga/tests/naga/validation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,8 +62,11 @@ fn expect_validation_error_with_span(
)
}

/// Validation should fail if `AtomicResult` expressions are not
/// populated by `Atomic` statements.
/// Validation should fail if [`AtomicResult` expressions] are not
/// populated by [`Atomic` statements].
///
/// [`AtomicResult` expressions]: Expression::AtomicResult
/// [`Atomic` statements]: naga::Statement::Atomic
#[test]
fn populate_atomic_result() {
use naga::{Module, Type, TypeInner};
Expand Down Expand Up @@ -1753,3 +1756,143 @@ fn memory_decorations_require_storage_address_space() {
}
));
}

/// Unlike WGSL, Naga IR permits multiple entry points to have the
/// same name, as long as they are for distinct stages.
#[test]
fn entry_points_distinguished_by_stage() {
let mut test_spans = TestSpanGenerator::default();
let mut module = Module::default();

let ty_vec4f = module.types.insert(
ir::Type {
name: Some("vec4f".to_string()),
inner: ir::TypeInner::Vector {
size: ir::VectorSize::Quad,
scalar: ir::Scalar::F32,
},
},
test_spans.next(),
);

let vertex_function = ir::Function {
name: Some("non_unique_name".into()),
result: Some(ir::FunctionResult {
ty: ty_vec4f,
binding: Some(ir::Binding::BuiltIn(ir::BuiltIn::Position {
invariant: false,
})),
}),
..ir::Function::default()
};
module.entry_points.push(ir::EntryPoint {
name: "non_unique_name".into(),
stage: ir::ShaderStage::Vertex,
early_depth_test: None,
workgroup_size: [0, 0, 0],
workgroup_size_overrides: None,
function: vertex_function,
mesh_info: None,
task_payload: None,
incoming_ray_payload: None,
});

module.entry_points.push(ir::EntryPoint {
name: "non_unique_name".into(),
stage: ir::ShaderStage::Compute,
early_depth_test: None,
workgroup_size: [1, 1, 1],
workgroup_size_overrides: None,
function: ir::Function::default(),
mesh_info: None,
task_payload: None,
incoming_ray_payload: None,
});

valid::Validator::new(
valid::ValidationFlags::default(),
valid::Capabilities::default(),
)
.validate(&module)
.expect("module should be valid");
}

/// It is not permitted for a [`Module`] to have multiple entry points with the
/// same name and the same stage.
#[test]
fn entry_points_share_name() {
let mut module = Module::default();

module.entry_points.push(ir::EntryPoint {
name: "non_unique_name".into(),
stage: ir::ShaderStage::Compute,
early_depth_test: None,
workgroup_size: [1, 1, 1],
workgroup_size_overrides: None,
function: ir::Function::default(),
mesh_info: None,
task_payload: None,
incoming_ray_payload: None,
});

module.entry_points.push(ir::EntryPoint {
name: "non_unique_name".into(),
stage: ir::ShaderStage::Compute,
early_depth_test: None,
workgroup_size: [1, 1, 1],
workgroup_size_overrides: None,
function: ir::Function::default(),
mesh_info: None,
task_payload: None,
incoming_ray_payload: None,
});

let err = valid::Validator::new(
valid::ValidationFlags::default(),
valid::Capabilities::default(),
)
.validate(&module)
.expect_err("module should be invalid");

assert!(matches!(
err.into_inner(),
valid::ValidationError::EntryPoint {
source: valid::EntryPointError::Conflict,
..
}
));
}

/// Unlike WGSL, Naga IR permits a [`Module`] to have multiple non-entry-point
/// functions with the same name. (Calls identify callees by handle, so the
/// names shouldn't affect validation.)
#[test]
fn functions_share_name() {
let mut test_spans = TestSpanGenerator::default();
let mut module = Module::default();

module.functions.append(
ir::Function {
name: Some("non_unique_name".into()),
result: None,
..ir::Function::default()
},
test_spans.next(),
);

module.functions.append(
ir::Function {
name: Some("non_unique_name".into()),
result: None,
..ir::Function::default()
},
test_spans.next(),
);

valid::Validator::new(
valid::ValidationFlags::default(),
valid::Capabilities::default(),
)
.validate(&module)
.expect("module should be valid");
}