struct VertexOutput { @builtin(position) position: vec4, @location(0) fragment_position: vec2, } alias fakebool = u32; struct GlobalState { center: vec2, rectangle: vec2, max_iterations: u32, use_fixed_real: fakebool, } struct FixedReal { int: u32, frac: u32, }; @group(0) @binding(0) var global_state: GlobalState; @vertex fn vertex_main(@builtin(vertex_index) vertexIndex: u32) -> VertexOutput { var output: VertexOutput; let positions = array, 4>( vec2(1.0, -1.0), vec2(1.0, 1.0), vec2(-1.0, -1.0), vec2(-1.0, 1.0), ); let position_2D = positions[vertexIndex]; output.position = vec4(position_2D, 0.0, 1.0); output.fragment_position = position_2D; return output; } fn fixed_add(a: FixedReal, b: FixedReal) -> FixedReal { var result: FixedReal; let a_sign: u32 = a.int >> 0x1F; let a_int: u32 = a.int & 0x7FFFFFFF; let b_sign: u32 = b.int >> 0x1F; let b_int: u32 = b.int & 0x7FFFFFFF; var int: u32 = 0; var frac: u32 = 0; if a_sign == b_sign { int = a_int + b_int; if a_sign == 1 { int |= 0x80000000u; } { let tmp: u32 = (a.frac >> 1) + (b.frac >> 1); frac = tmp << 1; int += tmp >> 0x1F; } } else { int = a_int - b_int; let tmp: u32 = (a.frac >> 1) - (b.frac >> 1); frac = tmp << 1; int -= tmp >> 0x1F; if (int >> 0x1F) == 1 { int = ~int; if bool(b_sign) { int |= 0x80000000u; } frac = (~frac) >> 1; frac += 1u; int += (frac >> 0x1F); frac <<= 1u; } else { if bool(a_sign) { int |= 0x80000000u; } } } result.int = int; result.frac = frac; return result; } fn fixed_add_01(a: FixedReal, b: FixedReal) -> FixedReal { var result: FixedReal; let a_sign: u32 = a.int >> 0x1F; let a_int: u32 = a.int & 0x7FFFFFFF; let b_sign: u32 = b.int >> 0x1F; let b_int: u32 = b.int & 0x7FFFFFFF; var int: u32 = 0; var frac: u32 = 0; if a_sign == b_sign { int = a_int + b_int; if a_sign == 1 { int |= 0x80000000u; } { let tmp: u32 = (a.frac >> 1) + (b.frac >> 1); frac = tmp << 1; int += tmp >> 0x1F; } } else { int = a_int - b_int; let tmp: u32 = (a.frac >> 1) - (b.frac >> 1); frac = tmp << 1; int -= tmp >> 0x1F; if (int >> 0x1F) == 1 { int = ~int; int |= 0x80000000u; frac = (~frac) >> 1; frac += 1u; int += (frac >> 0x1F); frac <<= 1u; } } result.int = int; result.frac = frac; return result; } fn fixed_subtract(a: FixedReal, b: FixedReal) -> FixedReal { let b_int: u32 = (~b.int & 0x80000000u) | (b.int & 0x7FFFFFFF); return fixed_add(a, FixedReal(b_int, b.frac)); } fn fixed_multiply(a: FixedReal, b: FixedReal) -> FixedReal { var result: FixedReal; let a_sign: u32 = a.int >> 0x1F; let a_int: u32 = a.int & 0x7FFFFFFF; let b_sign: u32 = b.int >> 0x1F; let b_int: u32 = b.int & 0x7FFFFFFF; let ai1: u32 = a_int >> 0x10; let ai2: u32 = a_int & 0xFFFF; let af1: u32 = a.frac >> 0x10; let af2: u32 = a.frac & 0xFFFF; let bi1: u32 = b_int >> 0x10; let bi2: u32 = b_int & 0xFFFF; let bf1: u32 = b.frac >> 0x10; let bf2: u32 = b.frac & 0xFFFF; var int: u32 = 0; var frac: u32 = 0; { // a.int * b.int: int = a_int * b_int; } { // a.int * b.frac: int += ai1 * bf1; { let tmp = ai1 * bf2; int += (tmp >> 0x10); frac += (tmp << 0x10) >> 3; } { let tmp = ai2 * bf1; int += (tmp >> 0x10); frac += (tmp << 0x10) >> 3; } frac += (ai2 * bf2) >> 3; } { // a.frac * b.int: int += af1 * bi1; { let tmp = af1 * bi2; int += (tmp >> 0x10); frac += (tmp << 0x10) >> 3; } { let tmp = af2 * bi1; int += (tmp >> 0x10); frac += (tmp << 0x10) >> 3; } frac += (af2 * bi2) >> 3; } { // a.frac * b.frac: frac += (af1 * bf1) >> 3; { let tmp = af1 * bf2; frac += (tmp >> 0x10) >> 3; } { let tmp = af2 * bf1; frac += (tmp >> 0x10) >> 3; } } int += frac >> 0x1D; frac <<= 3u; int &= 0x7FFFFFFFu; if a_sign != b_sign { int |= 0x80000000u; } return FixedReal(int, frac); } fn float_to_fixed_real(float: f32) -> FixedReal { var result: FixedReal; result.int = u32(abs(float)) & 0x7FFFFFFF; if float < 0.0 { result.int |= 0x80000000u; } result.frac = u32(fract(abs(float)) * 0x100000000.0); return result; } fn calculate_pixel_native(input: VertexOutput) -> vec4 { let c: vec2 = global_state.center + input.fragment_position * global_state.rectangle; let max_iterations: u32 = global_state.max_iterations; var z: vec2 = c; var z_squared_scalars: vec2; var iteration: u32 = 0u; for (; iteration < max_iterations; iteration++) { z_squared_scalars = z * z; if z_squared_scalars.x + z_squared_scalars.y > 4.0 { break; } z.y = 2.0 * z.x * z.y + c.y; z.x = z_squared_scalars.x - z_squared_scalars.y + c.x; } if iteration < max_iterations { return vec4( sin(f32(iteration) / f32(max_iterations) * 5.0), sin(f32(iteration) / f32(max_iterations) * 10.0), sin(f32(iteration) / f32(max_iterations) * 15.0), // // f32(abs((i32(iteration) % 20) - 10)) / 10, // // f32(abs((i32(iteration) % 20) - 10)) / 10, // // f32(abs((i32(iteration) % 20) - 10)) / 10, // 1.0 / f32(iteration), // f32(abs((i32(iteration) % 20) - 10)) / 10, // sin(f32(iteration) / f32(max_iterations) * 5.0), 1.0, ); } else { return vec4(0.0, 0.0, 0.0, 1.0); } } fn calculate_pixel_fixed_real(input: VertexOutput) -> vec4 { var cx: FixedReal = float_to_fixed_real(global_state.center.x + input.fragment_position.x * global_state.rectangle.x); var cy: FixedReal = float_to_fixed_real(global_state.center.y + input.fragment_position.y * global_state.rectangle.y); var zx: FixedReal = cx; var zy: FixedReal = cy; var iteration: u32 = 0u; let max_iterations: u32 = global_state.max_iterations; var zx2: FixedReal; var zy2: FixedReal; for (; iteration < max_iterations; iteration++) { zx2 = fixed_multiply(zx, zx); zy2 = fixed_multiply(zy, zy); if fixed_add(zx2, zy2).int >= 4 { break; } zy = fixed_add(fixed_multiply(fixed_multiply(FixedReal(2, 0), zx), zy), cy); zx = fixed_add(fixed_subtract(zx2, zy2), cx); } if iteration < max_iterations { return vec4( sin(f32(iteration) / f32(max_iterations) * 5.0), sin(f32(iteration) / f32(max_iterations) * 10.0), sin(f32(iteration) / f32(max_iterations) * 15.0), 1.0, ); } else { return vec4(0.0, 0.0, 0.0, 1.0); } } @fragment fn fragment_main(input: VertexOutput) -> @location(0) vec4 { if bool(global_state.use_fixed_real) { return calculate_pixel_fixed_real(input); } else { return calculate_pixel_native(input); } }