lumen-engine 0.2.2

Core composition model and rendering orchestration for Lumen.
Documentation
struct ResizeParams {
    size: vec2<u32>,
    mode: u32,
    sampling: u32,
}

@group(0) @binding(0) var input_tex: texture_2d<f32>;
@group(0) @binding(1) var<uniform> params: ResizeParams;
@group(0) @binding(2) var output_tex: texture_storage_2d<rgba8unorm, write>;

fn read_pixel(coord: vec2<i32>) -> vec4<f32> {
    let size = textureDimensions(input_tex);
    if (coord.x < 0 || coord.y < 0 || coord.x >= i32(size.x) || coord.y >= i32(size.y)) {
        return vec4<f32>(0.0);
    }
    return textureLoad(input_tex, coord, 0);
}

fn sample_nearest(coord: vec2<f32>) -> vec4<f32> {
    return read_pixel(vec2<i32>(floor(coord + vec2<f32>(0.5))));
}

fn sample_linear(coord: vec2<f32>) -> vec4<f32> {
    let base = floor(coord - vec2<f32>(0.5));
    let frac = coord - vec2<f32>(0.5) - base;
    let p0 = vec2<i32>(base);
    let c00 = read_pixel(p0);
    let c10 = read_pixel(p0 + vec2<i32>(1, 0));
    let c01 = read_pixel(p0 + vec2<i32>(0, 1));
    let c11 = read_pixel(p0 + vec2<i32>(1, 1));
    return mix(mix(c00, c10, frac.x), mix(c01, c11, frac.x), frac.y);
}

@compute @workgroup_size(8, 8, 1)
fn cs_main(@builtin(global_invocation_id) id: vec3<u32>) {
    let output_size = textureDimensions(output_tex);
    if (id.x >= output_size.x || id.y >= output_size.y) {
        return;
    }
    if (id.x >= params.size.x || id.y >= params.size.y) {
        textureStore(output_tex, vec2<i32>(id.xy), vec4<f32>(0.0));
        return;
    }

    let input_size_u = textureDimensions(input_tex);
    let input_size = vec2<f32>(input_size_u);
    let dest_size = vec2<f32>(max(params.size, vec2<u32>(1u)));
    var source_coord = (vec2<f32>(id.xy) + vec2<f32>(0.5)) * input_size / dest_size - vec2<f32>(0.5);
    var transparent = false;

    if (params.mode == 1u) {
        let scale = min(dest_size.x / input_size.x, dest_size.y / input_size.y);
        let scaled = input_size * scale;
        let offset = (dest_size - scaled) * 0.5;
        let local = vec2<f32>(id.xy) + vec2<f32>(0.5) - offset;
        transparent = local.x < 0.0 || local.y < 0.0 || local.x >= scaled.x || local.y >= scaled.y;
        source_coord = local / scale - vec2<f32>(0.5);
    } else if (params.mode == 2u) {
        let scale = max(dest_size.x / input_size.x, dest_size.y / input_size.y);
        let crop_size = dest_size / scale;
        let crop_origin = (input_size - crop_size) * 0.5;
        source_coord = crop_origin + ((vec2<f32>(id.xy) + vec2<f32>(0.5)) / scale) - vec2<f32>(0.5);
    }

    if (transparent) {
        textureStore(output_tex, vec2<i32>(id.xy), vec4<f32>(0.0));
        return;
    }

    let color = select(sample_linear(source_coord), sample_nearest(source_coord), params.sampling == 0u);
    textureStore(output_tex, vec2<i32>(id.xy), color);
}