MaikKlein / rlsl

Rust to SPIR-V compiler
Apache License 2.0
557 stars 14 forks source link

Fully implement structured control flow #8

Open MaikKlein opened 6 years ago

MaikKlein commented 6 years ago
#[spirv(fragment)]
fn color_frag(
    frag: Fragment,
    uv: Input<N0, Vec2<f32>>,
    time: Descriptor<N2, N0, f32>,
) -> Output<N0, Vec4<f32>> {
    let uv = uv.data;
    // let mut time = time.data;
    let offset = Vec3::new(0.0f32, 2.0, 4.0);
    let right = uv.x > 0.5;
    let top = uv.y > 0.5;

    let color: Vec3<f32> = match (right, top) {
        (true, true) => Vec3::new(0.0, 0.0, 1.0),
        (true, false) => Vec3::new(0.0, 1.0, 0.0),
        (false, true) => Vec3::new(1.0, 0.0, 0.0),
        (false, false) => Vec3::new(1.0, 1.0, 1.0),
    };

    Output::new(color.extend(1.0))
}

This results in the following CFG cfg

Three branches which all converge into bb7. The dashed lines here are the merge blocks. While 30 is the "correct" merge block, in spir-v all merge blocks have to be unique.

To address this issue we need to generate new blocks for:

bb1, bb2 -> bb8 -> bb7  

bb3, bb4 -> bb9 -> bb7 

bb0 -> bb10 -> bb7

And the new control flow node should be

bb5 => bb8

bb6 => bb9

bb0 => bb10
MaikKlein commented 6 years ago

cfg https://github.com/MaikKlein/rlsl/commit/76c1d3026e6c8f645fbd8f0cd63cbb88a18bc251

MaikKlein commented 6 years ago

A more complex example

MaikKlein commented 6 years ago

Interestingly a very simple branch doesn't work.


#[spirv(fragment)]
fn color_frag(
    frag: Fragment,
    uv: Input<N0, Vec2<f32>>,
    time: Descriptor<N2, N0, f32>,
) -> Output<N0, Vec4<f32>> {
    let uv = uv.data;
    let mut color = Vec3::new(1.0, 1.0, 0.0);
    if uv.x > 0.5 {
        color.x = 0.5;
    }

    Output::new(color.extend(1.0))
}

It crashes in Vulkan. Adding the else branch fixes it.

#[spirv(fragment)]
fn color_frag(
    frag: Fragment,
    uv: Input<N0, Vec2<f32>>,
    time: Descriptor<N2, N0, f32>,
) -> Output<N0, Vec4<f32>> {
    let uv = uv.data;
    let mut color = Vec3::new(1.0, 1.0, 0.0);
    if uv.x > 0.5 {
        color.x = 0.5;
    }
    } else {
        color.x = 0.5;
    }
    Output::new(color.extend(1.0))
}

I am not yet sure which rule I am violating.

MaikKlein commented 6 years ago

After some testing it seems the problem is the OpSwitchinstruction. Switching to OpBranchConditional fixes the problem. This might be a driver problem.

MaikKlein commented 6 years ago

The current implementation seems to break for the ? operator on options. It produces where weird looking cfgs

MaikKlein commented 6 years ago

It seems rlsl doesn't correctly handle unwind and resume.