///|
fn WgslIrEmitter::stage_attribute(
self : WgslIrEmitter,
stage : ShaderStage,
) -> String raise WgslIrEmitError {
ignore(self)
match stage {
Vertex => "@vertex"
Task => "@task"
Fragment => "@fragment"
Compute => "@compute"
_ => raise Unsupported("shader stage")
}
}
///|
fn WgslIrEmitter::emit_binding_attribute_prefix(
self : WgslIrEmitter,
out : StringBuilder,
binding : Binding?,
ty : Handle?,
) -> Unit raise WgslIrEmitError {
match binding {
Some(BuiltIn(builtin)) =>
out.write_string("@builtin(\{self.builtin_name(builtin)}) ")
Some(Location(location, interpolation, sampling, blend_src, per_primitive)) => {
if per_primitive {
out.write_string("@per_primitive ")
}
out.write_string("@location(\{location}) ")
match blend_src {
Some(value) => out.write_string("@blend_src(\{value}) ")
None => ()
}
match interpolation {
Some(value) => {
out.write_string("@interpolate(\{self.interpolation_name(value)}")
match sampling {
Some(sampling_value) =>
out.write_string(", \{self.sampling_name(sampling_value)}")
None => ()
}
out.write_string(") ")
}
None =>
if self.binding_type_uses_implicit_flat_interpolation(ty) {
out.write_string("@interpolate(flat) ")
}
}
}
None => ()
}
}
///|
fn WgslIrEmitter::binding_type_uses_implicit_flat_interpolation(
self : WgslIrEmitter,
ty : Handle?,
) -> Bool {
if !self.options.emit_implicit_flat_interpolation() {
return false
}
guard ty is Some(ty_handle) else { return false }
match self.type_component_scalar(ty_handle) {
Some({ kind: Sint | Uint, .. }) => true
_ => false
}
}
///|
fn WgslIrEmitter::builtin_name(
self : WgslIrEmitter,
builtin : BuiltIn,
) -> String raise WgslIrEmitError {
ignore(self)
match builtin {
Position(_) => "position"
ViewIndex => "view_index"
ClipDistances => "clip_distances"
PrimitiveIndex => "primitive_index"
VertexIndex => "vertex_index"
InstanceIndex => "instance_index"
FrontFacing => "front_facing"
Barycentric(true) => "barycentric"
Barycentric(false) => "barycentric_no_perspective"
FragDepth => "frag_depth"
SampleIndex => "sample_index"
SampleMask => "sample_mask"
GlobalInvocationId => "global_invocation_id"
LocalInvocationId => "local_invocation_id"
LocalInvocationIndex => "local_invocation_index"
WorkGroupId => "workgroup_id"
NumWorkGroups => "num_workgroups"
NumSubgroups => "num_subgroups"
SubgroupId => "subgroup_id"
SubgroupSize => "subgroup_size"
SubgroupInvocationId => "subgroup_invocation_id"
MeshTaskSize => "mesh_task_size"
CullPrimitive => "cull_primitive"
PointIndex => "point_index"
LineIndices => "line_indices"
TriangleIndices => "triangle_indices"
VertexCount => "vertex_count"
Vertices => "vertices"
PrimitiveCount => "primitive_count"
Primitives => "primitives"
_ => raise Unsupported("builtin")
}
}
///|
fn WgslIrEmitter::interpolation_name(
self : WgslIrEmitter,
interpolation : Interpolation,
) -> String {
ignore(self)
match interpolation {
Perspective => "perspective"
Linear => "linear"
Flat => "flat"
PerVertex => "per_vertex"
}
}
///|
fn WgslIrEmitter::sampling_name(
self : WgslIrEmitter,
sampling : Sampling,
) -> String {
ignore(self)
match sampling {
Center => "center"
Centroid => "centroid"
Sample => "sample"
First => "first"
Either => "either"
}
}
///|
fn WgslIrEmitter::literal(self : WgslIrEmitter, literal : Literal) -> String {
let numeric_policy = self.options.numeric_literal_policy()
match literal {
F64(value) => wgsl_ir_emit_abstract_float_literal_text("\{value}")
F32(value) => self.f32_literal(value, numeric_policy)
F32Exact(value) => wgsl_ir_emit_exact_f32_literal(value, numeric_policy)
F16(value) => wgsl_ir_emit_f16_literal_text("\{value}")
U16(value) => "\{value}u"
I16(value) => "\{value}"
U32(value) => wgsl_ir_emit_unsigned_literal(value, numeric_policy)
I32(value) => wgsl_ir_emit_signed_literal(value, numeric_policy)
U64(value) => "\{value}u"
I64(value) => "\{value}"
Bool(value) => if value { "true" } else { "false" }
AbstractInt(value) => "\{value}"
AbstractFloat(value) =>
if self.options.preserve_abstract_float_literals() {
wgsl_ir_emit_abstract_float_literal_text("\{value}")
} else {
wgsl_ir_emit_f32_literal(Float::from_double(value), numeric_policy)
}
}
}
///|
fn wgsl_ir_emit_exact_f32_literal(
value : Float,
policy : WgslIrNumericLiteralBytePolicy,
) -> String {
if wgsl_ir_f32_is_negative_zero(value) {
return "-0f"
}
match policy {
CompatNumericLiteralSpelling =>
wgsl_ir_emit_concrete_float_literal_text(
wgsl_ir_shortest_f32_literal_text(value),
)
RuntimeNumericLiteralSpelling =>
wgsl_ir_emit_concrete_float_literal_text(
wgsl_ir_shortest_f32_literal_text(value),
)
}
}
///|
fn wgsl_ir_emit_f32_literal(
value : Float,
policy : WgslIrNumericLiteralBytePolicy,
) -> String {
if wgsl_ir_f32_is_negative_zero(value) {
return "-0f"
}
match policy {
CompatNumericLiteralSpelling | RuntimeNumericLiteralSpelling =>
wgsl_ir_emit_concrete_float_literal_text(
wgsl_ir_shortest_f32_literal_text(value),
)
}
}
///|
fn WgslIrEmitter::f32_literal(
self : WgslIrEmitter,
value : Float,
policy : WgslIrNumericLiteralBytePolicy,
) -> String {
match policy {
CompatNumericLiteralSpelling =>
if value == Float::from_int(0) {
if wgsl_ir_f32_is_negative_zero(value) {
self.compat_negative_zero_seen = true
return "-0f"
}
if self.compat_negative_zero_seen {
return "-0f"
}
}
RuntimeNumericLiteralSpelling => ()
}
wgsl_ir_emit_f32_literal(value, policy)
}
///|
fn wgsl_ir_f32_is_negative_zero(value : Float) -> Bool {
value == Float::from_int(0) && value.reinterpret_as_int() < 0
}
///|
fn wgsl_ir_shortest_f32_literal_text(value : Float) -> String {
let original = "\{value}"
if original.contains("e") || original.contains("E") {
match wgsl_ir_shortest_large_fixed_f32_literal_text(value, original) {
Some(fixed) => return fixed
None => ()
}
match wgsl_ir_shortest_fixed_f32_literal_text(value, original) {
Some(fixed) => return fixed
None => ()
}
return original
}
guard original.contains(".") else { return original }
for precision in 0..<13 {
match wgsl_ir_round_fixed_decimal_text(original, precision) {
Some(candidate) =>
if wgsl_ir_f32_literal_round_trips(value, candidate) {
return candidate
}
None => ()
}
}
original
}
///|
fn wgsl_ir_shortest_large_fixed_f32_literal_text(
value : Float,
scientific : String,
) -> String? {
let exp_index = wgsl_ir_find_scientific_exponent_index(scientific)
guard exp_index >= 0 else { return None }
let mantissa = scientific[0:exp_index].to_owned()
let exponent_text = scientific[exp_index + 1:scientific.length()].to_owned()
let exponent : Int = @strconv.from_str(exponent_text) catch {
_ => return None
}
let negative = mantissa.has_prefix("-")
let mantissa_body = if negative {
mantissa[1:mantissa.length()].to_owned()
} else {
mantissa
}
let dot = wgsl_ir_find_ascii_char(mantissa_body, 46)
let decimal_pos = if dot >= 0 { dot } else { mantissa_body.length() }
let digits = wgsl_ir_decimal_digits_without_dot(mantissa_body)
guard digits.length() > 0 else { return None }
let shifted_decimal_pos = decimal_pos + exponent
guard shifted_decimal_pos > 18 else { return None }
let integer_digits = if shifted_decimal_pos >= digits.length() {
"\{digits}\{wgsl_ir_repeat_ascii_zero(shifted_decimal_pos - digits.length())}"
} else {
digits[0:shifted_decimal_pos].to_owned()
}
let max_keep = if digits.length() < integer_digits.length() {
digits.length()
} else {
integer_digits.length()
}
for keep in 1..<=max_keep {
match
wgsl_ir_round_decimal_integer_to_significant_digits(integer_digits, keep) {
Some(candidate) => {
let text = if negative { "-\{candidate}" } else { candidate }
if wgsl_ir_f32_literal_round_trips(value, text) {
return Some(text)
}
}
None => ()
}
}
None
}
///|
fn wgsl_ir_shortest_fixed_f32_literal_text(
value : Float,
scientific : String,
) -> String? {
guard wgsl_ir_scientific_decimal_text(scientific) is Some(decimal) else {
return None
}
let dot = wgsl_ir_find_ascii_char(decimal, 46)
guard dot >= 0 else { return None }
for precision in 0..<19 {
match wgsl_ir_round_fixed_decimal_text(decimal, precision) {
Some(candidate) =>
if candidate != "0" &&
candidate != "-0" &&
wgsl_ir_f32_literal_round_trips(value, candidate) {
return Some(candidate)
}
None => ()
}
}
None
}
///|
fn wgsl_ir_scientific_decimal_text(text : String) -> String? {
let exp_index = wgsl_ir_find_scientific_exponent_index(text)
guard exp_index >= 0 else { return None }
let mantissa = text[0:exp_index].to_owned()
let exponent_text = text[exp_index + 1:text.length()].to_owned()
let exponent : Int = @strconv.from_str(exponent_text) catch {
_ => return None
}
let negative = mantissa.has_prefix("-")
let mantissa_body = if negative {
mantissa[1:mantissa.length()].to_owned()
} else {
mantissa
}
let dot = wgsl_ir_find_ascii_char(mantissa_body, 46)
let decimal_pos = if dot >= 0 { dot } else { mantissa_body.length() }
let digits = wgsl_ir_decimal_digits_without_dot(mantissa_body)
guard digits.length() > 0 else { return None }
let shifted_decimal_pos = decimal_pos + exponent
let unsigned = if shifted_decimal_pos <= 0 {
"0.\{wgsl_ir_repeat_ascii_zero(-shifted_decimal_pos)}\{digits}"
} else if shifted_decimal_pos >= digits.length() {
"\{digits}\{wgsl_ir_repeat_ascii_zero(shifted_decimal_pos - digits.length())}.0"
} else {
"\{digits[0:shifted_decimal_pos]}.\{digits[shifted_decimal_pos:digits.length()]}"
}
if negative {
Some("-\{unsigned}")
} else {
Some(unsigned)
}
}
///|
fn wgsl_ir_find_scientific_exponent_index(text : String) -> Int {
for index in 0.. String {
let out = StringBuilder::new()
for index in 0..= 48 && code <= 57 {
out.write_string(text[index:index + 1].to_owned())
}
}
out.to_string()
}
///|
fn wgsl_ir_repeat_ascii_zero(count : Int) -> String {
let out = StringBuilder::new()
for _ in 0.. String? {
guard keep > 0 && keep <= digits.length() else { return None }
let prefix = digits[0:keep].to_owned()
let next_digit = if keep < digits.length() {
let code = digits.code_unit_at(keep).to_int()
if code < 48 || code > 57 {
return None
}
code - 48
} else {
0
}
let rounded = if next_digit >= 5 {
wgsl_ir_increment_decimal_digit_string(prefix)
} else {
prefix
}
let zero_count = digits.length() - rounded.length()
if zero_count > 0 {
Some("\{rounded}\{wgsl_ir_repeat_ascii_zero(zero_count)}")
} else {
Some(rounded)
}
}
///|
fn wgsl_ir_increment_decimal_digit_string(text : String) -> String {
let digits : Array[Int] = []
for index in 0..= 0 && carry {
if digits[index] == 57 {
digits[index] = 48
index = index - 1
} else {
digits[index] = digits[index] + 1
carry = false
}
}
let out = StringBuilder::new()
if carry {
out.write_string("1")
}
for code in digits {
out.write_char(code.unsafe_to_char())
}
out.to_string()
}
///|
fn wgsl_ir_f32_literal_round_trips(value : Float, text : String) -> Bool {
let parsed : Double = @strconv.from_str(text) catch { _ => return false }
Float::from_double(parsed) == value
}
///|
fn wgsl_ir_round_fixed_decimal_text(text : String, precision : Int) -> String? {
let dot = wgsl_ir_find_ascii_char(text, 46)
guard dot >= 0 else { return None }
let negative = text.has_prefix("-")
let body_start = if negative { 1 } else { 0 }
let int_part = text[body_start:dot].to_owned()
let frac_part = text[dot + 1:text.length()].to_owned()
if int_part.length() + precision > 18 {
return None
}
let integer = match wgsl_ir_parse_unsigned_decimal_int64(int_part) {
Some(value) => value
None => return None
}
let scale = wgsl_ir_pow10_int64(precision)
let frac = match wgsl_ir_parse_fraction_prefix_int64(frac_part, precision) {
Some(value) => value
None => return None
}
let next_digit = if precision < frac_part.length() {
let code = frac_part.code_unit_at(precision).to_int()
if code < 48 || code > 57 {
return None
}
code - 48
} else {
0
}
let carry = if next_digit >= 5 {
Int64::from_int(1)
} else {
Int64::from_int(0)
}
let scaled = integer * scale + frac + carry
let abs_text = wgsl_ir_scaled_fixed_decimal_text(scaled, precision)
if negative && scaled != Int64::from_int(0) {
Some("-\{abs_text}")
} else {
Some(abs_text)
}
}
///|
fn wgsl_ir_find_ascii_char(text : String, code : Int) -> Int {
for index in 0.. Int64? {
if text.length() == 0 {
return Some(Int64::from_int(0))
}
let mut value : Int64 = 0
for index in 0.. 57 {
return None
}
value = value * 10 + Int64::from_int(code - 48)
}
Some(value)
}
///|
fn wgsl_ir_parse_fraction_prefix_int64(
text : String,
precision : Int,
) -> Int64? {
let mut value : Int64 = 0
for index in 0.. 57 {
return None
}
value = value + Int64::from_int(code - 48)
}
}
Some(value)
}
///|
fn wgsl_ir_pow10_int64(power : Int) -> Int64 {
let mut value : Int64 = 1
for _ in 0.. String {
if precision == 0 {
return "\{value}"
}
let mut digits = "\{value}"
while digits.length() <= precision {
digits = "0\{digits}"
}
let split = digits.length() - precision
"\{digits[0:split]}.\{digits[split:digits.length()]}"
}
///|
fn wgsl_ir_emit_normalized_float_literal_text(text : String) -> String {
if text.contains(".") || text.contains("e") || text.contains("E") {
text
} else {
"\{text}.0"
}
}
///|
fn wgsl_ir_emit_concrete_float_literal_text(text : String) -> String {
let normalized = wgsl_ir_emit_normalized_float_literal_text(text)
if normalized.has_suffix(".0") {
"\{normalized[0:normalized.length() - 2]}f"
} else {
"\{normalized}f"
}
}
///|
fn wgsl_ir_emit_f16_literal_text(text : String) -> String {
let normalized = wgsl_ir_emit_normalized_float_literal_text(text)
if normalized.has_suffix(".0") {
"\{normalized[0:normalized.length() - 2]}h"
} else {
"\{normalized}h"
}
}
///|
fn wgsl_ir_emit_abstract_float_literal_text(text : String) -> String {
wgsl_ir_emit_normalized_float_literal_text(text)
}