// Write function return type and name let ret_ty = func_ctx.resolve_type(expr_handle, &module.types); self.write_value_type(module, ret_ty)?;
write!(self.out, " ")?; self.write_wrapped_image_query_function_name(wiq)?;
// Write function parameters
write!(self.out, "(")?; // Texture always first parameter self.write_image_type(wiq.dim, wiq.arrayed, wiq.class)?;
write!(self.out, " {ARGUMENT_VARIABLE_NAME}")?; // Mipmap is a second parameter if exists iflet ImageQuery::SizeLevel = wiq.query {
write!(self.out, ", uint {MIP_LEVEL_PARAM}")?;
}
writeln!(self.out, ")")?;
// Write function body
writeln!(self.out, "{{")?;
let array_coords = usize::from(wiq.arrayed); // extra parameter is the mip level count or the sample count let extra_coords = match wiq.class { crate::ImageClass::Storage { .. } => 0, crate::ImageClass::Sampled { .. } | crate::ImageClass::Depth { .. } => 1,
};
for component in COMPONENTS[..number_of_params - 1].iter() {
write!(self.out, "{RETURN_VARIABLE_NAME}.{component}, ")?;
}
// write last parameter without comma and space for last parameter
write!( self.out, "{}.{}",
RETURN_VARIABLE_NAME,
COMPONENTS[number_of_params - 1]
)?;
writeln!(self.out, ");")?;
// Write return value
writeln!( self.out, "{INDENT}return {RETURN_VARIABLE_NAME}.{ret_swizzle};"
)?;
// End of function body
writeln!(self.out, "}}")?; // Write extra new line
writeln!(self.out)?;
/// Helper function that write wrapped function for `Expression::Compose` for structures. fn write_wrapped_constructor_function(
&mutself,
module: &crate::Module,
constructor: WrappedConstructor,
) -> BackendResult { usecrate::back::INDENT;
match module.types[constructor.ty].inner { crate::TypeInner::Struct { ref members, .. } => { for (i, member) in members.iter().enumerate() {
write_arg(i, member.ty)?;
}
} crate::TypeInner::Array {
base,
size: crate::ArraySize::Constant(size),
..
} => { for i in0..size.get() as usize {
write_arg(i, base)?;
}
}
_ => unreachable!(),
};
write!(self.out, ")")?;
// Write function body
writeln!(self.out, " {{")?;
match module.types[constructor.ty].inner { crate::TypeInner::Struct { ref members, .. } => { let struct_name = &self.names[&NameKey::Type(constructor.ty)];
writeln!( self.out, "{INDENT}{struct_name} {RETURN_VARIABLE_NAME} = ({struct_name})0;"
)?; for (i, member) in members.iter().enumerate() { let field_name = &self.names[&NameKey::StructMember(constructor.ty, i as u32)];
match module.types[member.ty].inner { crate::TypeInner::Matrix {
columns,
rows: crate::VectorSize::Bi,
..
} if member.binding.is_none() => { for j in0..columns as u8 {
writeln!( self.out, "{INDENT}{RETURN_VARIABLE_NAME}.{field_name}_{j} = {ARGUMENT_VARIABLE_NAME}{i}[{j}];"
)?;
}
} ref other => { // We cast arrays of native HLSL `floatCx2`s to arrays of `matCx2`s // (where the inner matrix is represented by a struct with C `float2` members). // See the module-level block comment in mod.rs for details. iflet Some(super::writer::MatrixType {
columns,
rows: crate::VectorSize::Bi,
width: 4,
}) = super::writer::get_inner_matrix_data(module, member.ty)
{
write!( self.out, "{}{}.{} = (__mat{}x2",
INDENT, RETURN_VARIABLE_NAME, field_name, columns as u8
)?; ifletcrate::TypeInner::Array { base, size, .. } = *other { self.write_array_size(module, base, size)?;
}
writeln!(self.out, "){ARGUMENT_VARIABLE_NAME}{i};",)?;
} else {
writeln!( self.out, "{INDENT}{RETURN_VARIABLE_NAME}.{field_name} = {ARGUMENT_VARIABLE_NAME}{i};",
)?;
}
}
}
}
} crate::TypeInner::Array {
base,
size: crate::ArraySize::Constant(size),
..
} => {
write!(self.out, "{INDENT}")?; self.write_type(module, base)?;
write!(self.out, " {RETURN_VARIABLE_NAME}")?; self.write_array_size(module, base, crate::ArraySize::Constant(size))?;
write!(self.out, " = {{ ")?; for i in0..size.get() { if i != 0 {
write!(self.out, ", ")?;
}
write!(self.out, "{ARGUMENT_VARIABLE_NAME}{i}")?;
}
writeln!(self.out, " }};",)?;
}
_ => unreachable!(),
}
// Write return value
writeln!(self.out, "{INDENT}return {RETURN_VARIABLE_NAME};")?;
// End of function body
writeln!(self.out, "}}")?; // Write extra new line
writeln!(self.out)?;
Ok(())
}
pub(super) fn write_wrapped_struct_matrix_get_function_name(
&mutself,
access: WrappedStructMatrixAccess,
) -> BackendResult { let name = &self.names[&NameKey::Type(access.ty)]; let field_name = &self.names[&NameKey::StructMember(access.ty, access.index)];
write!(self.out, "GetMat{field_name}On{name}")?;
Ok(())
}
/// Writes a function used to get a matCx2 from within a structure. pub(super) fn write_wrapped_struct_matrix_get_function(
&mutself,
module: &crate::Module,
access: WrappedStructMatrixAccess,
) -> BackendResult { usecrate::back::INDENT;
// Write function return type and name let member = match module.types[access.ty].inner { crate::TypeInner::Struct { ref members, .. } => &members[access.index as usize],
_ => unreachable!(),
}; let ret_ty = &module.types[member.ty].inner; self.write_value_type(module, ret_ty)?;
write!(self.out, " ")?; self.write_wrapped_struct_matrix_get_function_name(access)?;
// Write function parameters
write!(self.out, "(")?; let struct_name = &self.names[&NameKey::Type(access.ty)];
write!(self.out, "{struct_name} {STRUCT_ARGUMENT_VARIABLE_NAME}")?;
// Write function body
writeln!(self.out, ") {{")?;
// Write return value
write!(self.out, "{INDENT}return ")?; self.write_value_type(module, ret_ty)?;
write!(self.out, "(")?; let field_name = &self.names[&NameKey::StructMember(access.ty, access.index)]; match module.types[member.ty].inner { crate::TypeInner::Matrix { columns, .. } => { for i in0..columns as u8 { if i != 0 {
write!(self.out, ", ")?;
}
write!(self.out, "{STRUCT_ARGUMENT_VARIABLE_NAME}.{field_name}_{i}")?;
}
}
_ => unreachable!(),
}
writeln!(self.out, ");")?;
// End of function body
writeln!(self.out, "}}")?; // Write extra new line
writeln!(self.out)?;
Ok(())
}
pub(super) fn write_wrapped_struct_matrix_set_function_name(
&mutself,
access: WrappedStructMatrixAccess,
) -> BackendResult { let name = &self.names[&NameKey::Type(access.ty)]; let field_name = &self.names[&NameKey::StructMember(access.ty, access.index)];
write!(self.out, "SetMat{field_name}On{name}")?;
Ok(())
}
/// Writes a function used to set a matCx2 from within a structure. pub(super) fn write_wrapped_struct_matrix_set_function(
&mutself,
module: &crate::Module,
access: WrappedStructMatrixAccess,
) -> BackendResult { usecrate::back::INDENT;
// Write function return type and name
write!(self.out, "void ")?; self.write_wrapped_struct_matrix_set_function_name(access)?;
// Write function parameters
write!(self.out, "(")?; let struct_name = &self.names[&NameKey::Type(access.ty)];
write!(self.out, "{struct_name} {STRUCT_ARGUMENT_VARIABLE_NAME}, ")?; let member = match module.types[access.ty].inner { crate::TypeInner::Struct { ref members, .. } => &members[access.index as usize],
_ => unreachable!(),
}; self.write_type(module, member.ty)?;
write!(self.out, " {MATRIX_ARGUMENT_VARIABLE_NAME}")?; // Write function body
writeln!(self.out, ") {{")?;
let field_name = &self.names[&NameKey::StructMember(access.ty, access.index)];
match module.types[member.ty].inner { crate::TypeInner::Matrix { columns, .. } => { for i in0..columns as u8 {
writeln!( self.out, "{INDENT}{STRUCT_ARGUMENT_VARIABLE_NAME}.{field_name}_{i} = {MATRIX_ARGUMENT_VARIABLE_NAME}[{i}];"
)?;
}
}
_ => unreachable!(),
}
// End of function body
writeln!(self.out, "}}")?; // Write extra new line
writeln!(self.out)?;
Ok(())
}
pub(super) fn write_wrapped_struct_matrix_set_vec_function_name(
&mutself,
access: WrappedStructMatrixAccess,
) -> BackendResult { let name = &self.names[&NameKey::Type(access.ty)]; let field_name = &self.names[&NameKey::StructMember(access.ty, access.index)];
write!(self.out, "SetMatVec{field_name}On{name}")?;
Ok(())
}
/// Writes a function used to set a vec2 on a matCx2 from within a structure. pub(super) fn write_wrapped_struct_matrix_set_vec_function(
&mutself,
module: &crate::Module,
access: WrappedStructMatrixAccess,
) -> BackendResult { usecrate::back::INDENT;
let field_name = &self.names[&NameKey::StructMember(access.ty, access.index)];
match module.types[member.ty].inner { crate::TypeInner::Matrix { columns, .. } => { for i in0..columns as u8 {
writeln!( self.out, "{INDENT}case {i}: {{ {STRUCT_ARGUMENT_VARIABLE_NAME}.{field_name}_{i} = {VECTOR_ARGUMENT_VARIABLE_NAME}; break; }}"
)?;
}
}
_ => unreachable!(),
}
writeln!(self.out, "{INDENT}}}")?;
// End of function body
writeln!(self.out, "}}")?; // Write extra new line
writeln!(self.out)?;
Ok(())
}
pub(super) fn write_wrapped_struct_matrix_set_scalar_function_name(
&mutself,
access: WrappedStructMatrixAccess,
) -> BackendResult { let name = &self.names[&NameKey::Type(access.ty)]; let field_name = &self.names[&NameKey::StructMember(access.ty, access.index)];
write!(self.out, "SetMatScalar{field_name}On{name}")?;
Ok(())
}
/// Writes a function used to set a float on a matCx2 from within a structure. pub(super) fn write_wrapped_struct_matrix_set_scalar_function(
&mutself,
module: &crate::Module,
access: WrappedStructMatrixAccess,
) -> BackendResult { usecrate::back::INDENT;
/// Helper function that writes compose wrapped functions pub(super) fn write_wrapped_compose_functions(
&mutself,
module: &crate::Module,
expressions: &crate::Arena<crate::Expression>,
) -> BackendResult { for (handle, _) in expressions.iter() { ifletcrate::Expression::Compose { ty, .. } = expressions[handle] { match module.types[ty].inner { crate::TypeInner::Struct { .. } | crate::TypeInner::Array { .. } => { let constructor = WrappedConstructor { ty }; ifself.wrapped.constructors.insert(constructor) { self.write_wrapped_constructor_function(module, constructor)?;
}
}
_ => {}
};
}
}
Ok(())
}
// TODO: we could merge this with iteration in write_wrapped_compose_functions... // /// Helper function that writes zero value wrapped functions pub(super) fn write_wrapped_zero_value_functions(
&mutself,
module: &crate::Module,
expressions: &crate::Arena<crate::Expression>,
) -> BackendResult { for (handle, _) in expressions.iter() { ifletcrate::Expression::ZeroValue(ty) = expressions[handle] { let zero_value = WrappedZeroValue { ty }; ifself.wrapped.zero_values.insert(zero_value) { self.write_wrapped_zero_value_function(module, zero_value)?;
}
}
}
Ok(())
}
pub(super) fn write_wrapped_math_functions(
&mutself,
module: &crate::Module,
func_ctx: &FunctionCtx,
) -> BackendResult { for (_, expression) in func_ctx.expressions.iter() { ifletcrate::Expression::Math {
fun,
arg,
arg1: _arg1,
arg2: _arg2,
arg3: _arg3,
} = *expression
{ match fun { crate::MathFunction::ExtractBits => { // The behavior of our extractBits polyfill is undefined if offset + count > bit_width. We need // to first sanitize the offset and count first. If we don't do this, we will get out-of-spec // values if the extracted range is not within the bit width. // // This encodes the exact formula specified by the wgsl spec: // https://gpuweb.github.io/gpuweb/wgsl/#extractBits-unsigned-builtin // // w = sizeof(x) * 8 // o = min(offset, w) // c = min(count, w - o) // // bitfieldExtract(x, o, c) let arg_ty = func_ctx.resolve_type(arg, &module.types); let scalar = arg_ty.scalar().unwrap(); let components = arg_ty.components();
let wrapped = WrappedMath {
fun,
scalar,
components,
};
if !self.wrapped.math.insert(wrapped) { continue;
}
// Write return type self.write_value_type(module, arg_ty)?;
let scalar_width: u8 = scalar.width * 8;
// Write function name and parameters
writeln!(self.out, " {EXTRACT_BITS_FUNCTION}(")?;
write!(self.out, " ")?; self.write_value_type(module, arg_ty)?;
writeln!(self.out, " e,")?;
writeln!(self.out, " uint offset,")?;
writeln!(self.out, " uint count")?;
writeln!(self.out, ") {{")?;
// Write function body
writeln!(self.out, " uint w = {scalar_width};")?;
writeln!(self.out, " uint o = min(offset, w);")?;
writeln!(self.out, " uint c = min(count, w - o);")?;
writeln!( self.out, " return (c == 0 ? 0 : (e << (w - c - o)) >> (w - c));"
)?;
// End of function body
writeln!(self.out, "}}")?;
} crate::MathFunction::InsertBits => { // The behavior of our insertBits polyfill has the same constraints as the extractBits polyfill.
let arg_ty = func_ctx.resolve_type(arg, &module.types); let scalar = arg_ty.scalar().unwrap(); let components = arg_ty.components();
let wrapped = WrappedMath {
fun,
scalar,
components,
};
if !self.wrapped.math.insert(wrapped) { continue;
}
// Write return type self.write_value_type(module, arg_ty)?;
let scalar_width: u8 = scalar.width * 8; let scalar_max: u64 = match scalar.width { 1 => 0xFF, 2 => 0xFFFF, 4 => 0xFFFFFFFF, 8 => 0xFFFFFFFFFFFFFFFF,
_ => unreachable!(),
};
// Write function body
writeln!(self.out, " uint w = {scalar_width}u;")?;
writeln!(self.out, " uint o = min(offset, w);")?;
writeln!(self.out, " uint c = min(count, w - o);")?;
// The `u` suffix on the literals is _extremely_ important. Otherwise it will use // i32 shifting instead of the intended u32 shifting.
writeln!( self.out, " uint mask = (({scalar_max}u >> ({scalar_width}u - c)) << o);"
)?;
writeln!( self.out, " return (c == 0 ? e : ((e & ~mask) | ((newbits << o) & mask)));"
)?;
// End of function body
writeln!(self.out, "}}")?;
}
_ => {}
}
}
}
Ok(())
}
/// Helper function that writes various wrapped functions pub(super) fn write_wrapped_functions(
&mutself,
module: &crate::Module,
func_ctx: &FunctionCtx,
) -> BackendResult { self.write_wrapped_math_functions(module, func_ctx)?; self.write_wrapped_compose_functions(module, func_ctx.expressions)?; self.write_wrapped_zero_value_functions(module, func_ctx.expressions)?;
for (handle, _) in func_ctx.expressions.iter() { match func_ctx.expressions[handle] { crate::Expression::ArrayLength(expr) => { let global_expr = match func_ctx.expressions[expr] { crate::Expression::GlobalVariable(_) => expr, crate::Expression::AccessIndex { base, index: _ } => base, ref other => unreachable!("Array length of {:?}", other),
}; let global_var = match func_ctx.expressions[global_expr] { crate::Expression::GlobalVariable(var_handle) => {
&module.global_variables[var_handle]
} ref other => { return Err(super::Error::Unimplemented(format!( "Array length of base {other:?}"
)))
}
}; let storage_access = match global_var.space { crate::AddressSpace::Storage { access } => access,
_ => crate::StorageAccess::default(),
}; let wal = WrappedArrayLength {
writable: storage_access.contains(crate::StorageAccess::STORE),
};
ifself.wrapped.image_queries.insert(wiq) { self.write_wrapped_image_query_function(module, wiq, handle, func_ctx)?;
}
} // Write `WrappedConstructor` for structs that are loaded from `AddressSpace::Storage` // since they will later be used by the fn `write_storage_load` crate::Expression::Load { pointer } => { let pointer_space = func_ctx
.resolve_type(pointer, &module.types)
.pointer_space();
fn write_wrapped_constructor<W: Write>(
writer: &mutsuper::Writer<'_, W>,
ty: Handle<crate::Type>,
module: &crate::Module,
) -> BackendResult { match module.types[ty].inner { crate::TypeInner::Struct { ref members, .. } => { for member in members {
write_wrapped_constructor(writer, member.ty, module)?;
}
let constructor = WrappedConstructor { ty }; if writer.wrapped.constructors.insert(constructor) {
writer
.write_wrapped_constructor_function(module, constructor)?;
}
} crate::TypeInner::Array { base, .. } => {
write_wrapped_constructor(writer, base, module)?;
let constructor = WrappedConstructor { ty }; if writer.wrapped.constructors.insert(constructor) {
writer
.write_wrapped_constructor_function(module, constructor)?;
}
}
_ => {}
};
Ok(())
}
} // We treat matrices of the form `matCx2` as a sequence of C `vec2`s // (see top level module docs for details). // // The functions injected here are required to get the matrix accesses working. crate::Expression::AccessIndex { base, index } => { let base_ty_res = &func_ctx.info[base].ty; letmut resolved = base_ty_res.inner_with(&module.types); let base_ty_handle = match *resolved { crate::TypeInner::Pointer { base, .. } => {
resolved = &module.types[base].inner;
Some(base)
}
_ => base_ty_res.handle(),
}; ifletcrate::TypeInner::Struct { ref members, .. } = *resolved { let member = &members[index as usize];
match module.types[member.ty].inner { crate::TypeInner::Matrix {
rows: crate::VectorSize::Bi,
..
} if member.binding.is_none() => { let ty = base_ty_handle.unwrap(); let access = WrappedStructMatrixAccess { ty, index };
// typedef
write!(self.out, "typedef struct {{ ")?; for i in0..columns as u8 {
write!(self.out, "float2 _{i}; ")?;
}
writeln!(self.out, "}} __mat{}x2;", columns as u8)?;
// __get_col_of_mat
writeln!( self.out, "float2 __get_col_of_mat{}x2(__mat{}x2 mat, uint idx) {{",
columns as u8, columns as u8
)?;
writeln!(self.out, "{INDENT}switch(idx) {{")?; for i in0..columns as u8 {
writeln!(self.out, "{INDENT}case {i}: {{ return mat._{i}; }}")?;
}
writeln!(self.out, "{INDENT}default: {{ return (float2)0; }}")?;
writeln!(self.out, "{INDENT}}}")?;
writeln!(self.out, "}}")?;
// __set_col_of_mat
writeln!( self.out, "void __set_col_of_mat{}x2(__mat{}x2 mat, uint idx, float2 value) {{",
columns as u8, columns as u8
)?;
writeln!(self.out, "{INDENT}switch(idx) {{")?; for i in0..columns as u8 {
writeln!(self.out, "{INDENT}case {i}: {{ mat._{i} = value; break; }}")?;
}
writeln!(self.out, "{INDENT}}}")?;
writeln!(self.out, "}}")?;
// __set_el_of_mat
writeln!( self.out, "void __set_el_of_mat{}x2(__mat{}x2 mat, uint idx, uint vec_idx, float value) {{",
columns as u8, columns as u8
)?;
writeln!(self.out, "{INDENT}switch(idx) {{")?; for i in0..columns as u8 {
writeln!( self.out, "{INDENT}case {i}: {{ mat._{i}[vec_idx] = value; break; }}"
)?;
}
writeln!(self.out, "{INDENT}}}")?;
writeln!(self.out, "}}")?;
writeln!(self.out)?;
Ok(())
}
pub(super) fn write_all_mat_cx2_typedefs_and_functions(
&mutself,
module: &crate::Module,
) -> BackendResult { for (handle, _) in module.global_variables.iter() { let global = &module.global_variables[handle];
/// Helper function that write wrapped function for `Expression::ZeroValue` /// /// This is necessary since we might have a member access after the zero value expression, e.g. /// `.y` (in practice this can come up when consuming SPIRV that's been produced by glslc). /// /// So we can't just write `(float4)0` since `(float4)0.y` won't parse correctly. /// /// Parenthesizing the expression like `((float4)0).y` would work... except DXC can't handle /// cases like: /// /// ```text /// tests\out\hlsl\access.hlsl:183:41: error: cannot compile this l-value expression yet /// t_1.am = (__mat4x2[2])((float4x2[2])0); /// ^ /// ``` fn write_wrapped_zero_value_function(
&mutself,
module: &crate::Module,
zero_value: WrappedZeroValue,
) -> BackendResult { usecrate::back::INDENT;
const RETURN_VARIABLE_NAME: &str = "ret";
// Write function return type and name ifletcrate::TypeInner::Array { base, size, .. } = module.types[zero_value.ty].inner {
write!(self.out, "typedef ")?; self.write_type(module, zero_value.ty)?;
write!(self.out, " ret_")?; self.write_wrapped_zero_value_function_name(module, zero_value)?; self.write_array_size(module, base, size)?;
writeln!(self.out, ";")?;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.