Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Binary Operators

Demonstrates all supported binary operators in five fragment shaders: arithmetic (+ - * / %), comparison (== != < <= > >=), logical (&& ||), bitwise (& | ^ << >>), and mixed-precedence expressions (see Precedence & Parentheses).

Rust Source

#![allow(unused)]
fn main() {
#[wgsl]
pub mod binary_ops_example {
    //! Demonstrates all supported binary operators including:
    //! - Arithmetic: + - * / %
    //! - Comparison: == != < <= > >=
    //! - Logical: && ||
    //! - Bitwise: & | ^ << >>

    use wgsl_rs::std::*;

    // Demonstrates arithmetic operators including remainder.
    #[fragment]
    pub fn test_arithmetic() -> Vec4f {
        let a = vec3f(10.0, 11.0, 12.0);
        let b = 3.0;
        let add = a + b;
        let sub = a - b;
        let mul = a * b;
        let div = a / b;
        let rem = a % b;
        vec4f(add.x(), sub.y(), (mul * div).z(), rem.z())
    }

    // Demonstrates comparison operators.
    // All comparison operators return bool (or vecN<bool> for vectors).
    #[fragment]
    pub fn test_comparison() -> Vec4f {
        let a = 5;
        let b = 10;

        // Comparison operators
        let lt = a < b;
        let le = a <= b;
        let _gt = a > b;
        let ge = a >= b;
        let eq = a == b;
        let ne = a != b;

        // Use the booleans in a calculation
        // In WGSL, we use select() to convert bool to numeric
        let lt_val = select(0.0, 1.0, lt);
        let eq_val = select(0.0, 1.0, eq);
        let ne_val = select(0.0, 1.0, ne);
        let combined = select(0.0, 1.0, le && ge);

        vec4f(lt_val, eq_val, ne_val, combined)
    }

    // Demonstrates logical operators (short-circuit and/or).
    #[fragment]
    pub fn test_logical() -> Vec4f {
        let a = true;
        let b = false;

        // Logical operators (short-circuit evaluation)
        let and_result = a && b;
        let or_result = a || b;
        let complex = (a && b) || (!a && !b);

        let and_val = select(0.0, 1.0, and_result);
        let or_val = select(0.0, 1.0, or_result);
        let complex_val = select(0.0, 1.0, complex);

        vec4f(and_val, or_val, complex_val, 1.0)
    }

    // Demonstrates bitwise operators.
    #[fragment]
    pub fn test_bitwise() -> Vec4f {
        let a: u32 = 0xFF00;
        let b: u32 = 0x0F0F;

        // Bitwise operators
        let and_result = a & b;
        let or_result = a | b;
        let xor_result = a ^ b;

        // Shift operators
        let shl_result = a << 4u32;
        let shr_result = a >> 4u32;

        // Convert to floats for output (normalized)
        let and_f = f32(and_result) / 65535.0;
        let or_f = f32(or_result) / 65535.0;
        let xor_f = f32(xor_result) / 65535.0;
        let shift_f = f32(shl_result ^ shr_result) / 65535.0;

        vec4f(and_f, or_f, xor_f, shift_f)
    }

    // Demonstrates mixed-precedence expressions (wgsl-rs#159): Rust binds
    // `& ^ |` and shifts tighter than comparisons, WGSL binds comparisons
    // tighter. wgsl-rs parenthesizes so the WGSL evaluates exactly as the
    // Rust does — `flags & mask == flags` is `(flags & mask) == flags`,
    // never `flags & (mask == flags)`.
    #[fragment]
    pub fn test_precedence() -> Vec4f {
        // From the issue: `0 & 0 == 0` parses as `(0 & 0) == 0` in Rust; a
        // flat WGSL rendering would re-parse as `0 & (0 == 0)`, a type
        // error.
        let flags: u32 = 12;
        let mask: u32 = 3;
        let eq_after_and = flags & mask == flags;
        let cmp_after_or = flags | 1 == 13;
        let cmp_after_xor = flags ^ 2 != 14;
        let shift_vs_lt = 1u32 << 2u32 < 8u32;

        vec4f(
            select(0.0, 1.0, eq_after_and),
            select(0.0, 1.0, cmp_after_or),
            select(0.0, 1.0, cmp_after_xor),
            select(0.0, 1.0, shift_vs_lt),
        )
    }
}
}

Generated WGSL

@fragment fn test_arithmetic() -> @location(0) vec4f {
    let a = vec3f(10.0, 11.0, 12.0);
    let b = 3.0;
    let add = (a + b);
    let sub = (a - b);
    let mul = (a * b);
    let div = (a / b);
    let rem = (a % b);
    return vec4f(add.x, sub.y, (mul * div).z, rem.z);
}

@fragment fn test_comparison() -> @location(0) vec4f {
    let a = 5;
    let b = 10;
    let lt = (a < b);
    let le = (a <= b);
    let _gt = (a > b);
    let ge = (a >= b);
    let eq = (a == b);
    let ne = (a != b);
    let lt_val = select(0.0, 1.0, lt);
    let eq_val = select(0.0, 1.0, eq);
    let ne_val = select(0.0, 1.0, ne);
    let combined = select(0.0, 1.0, (le && ge));
    return vec4f(lt_val, eq_val, ne_val, combined);
}

@fragment fn test_logical() -> @location(0) vec4f {
    let a = true;
    let b = false;
    let and_result = (a && b);
    let or_result = (a || b);
    let complex = ((a && b) || (!a && !b));
    let and_val = select(0.0, 1.0, and_result);
    let or_val = select(0.0, 1.0, or_result);
    let complex_val = select(0.0, 1.0, complex);
    return vec4f(and_val, or_val, complex_val, 1.0);
}

@fragment fn test_bitwise() -> @location(0) vec4f {
    let a: u32 = 65280;
    let b: u32 = 3855;
    let and_result = (a & b);
    let or_result = (a | b);
    let xor_result = (a ^ b);
    let shl_result = (a << 4u);
    let shr_result = (a >> 4u);
    let and_f = (f32(and_result) / 65535.0);
    let or_f = (f32(or_result) / 65535.0);
    let xor_f = (f32(xor_result) / 65535.0);
    let shift_f = (f32((shl_result ^ shr_result)) / 65535.0);
    return vec4f(and_f, or_f, xor_f, shift_f);
}

@fragment fn test_precedence() -> @location(0) vec4f {
    let flags: u32 = 12;
    let mask: u32 = 3;
    let eq_after_and = ((flags & mask) == flags);
    let cmp_after_or = ((flags | 1) == 13);
    let cmp_after_xor = ((flags ^ 2) != 14);
    let shift_vs_lt = ((1u << 2u) < 8u);
    return vec4f(select(0.0, 1.0, eq_after_and), select(0.0, 1.0, cmp_after_or), select(0.0, 1.0, cmp_after_xor), select(0.0, 1.0, shift_vs_lt));
}

Notes

  • Hex literals like 0xFF00 are emitted as decimal WGSL literals.
  • Short-circuit &&/|| are preserved in the raw output; the naga-validated form lowers them to if/else because WGSL has no short-circuit logical operators.
  • Binary expressions render parenthesized — (a + b), not a + b — so Rust’s evaluation structure survives WGSL’s different precedence rules (see Precedence & Parentheses; wgsl-rs#159).