euv-engine 0.28.21

A high-performance 2D game engine built on the euv framework, featuring ECS, fixed-timestep game loop, canvas rendering, physics, collision detection, sprite animation, and audio.
Documentation
use super::*;

fn rendered<T: Debug>(value: &T) -> String {
    format!("{value:?}")
}

#[test]
fn a_non_filtering_sampler_differs_from_a_filtering_one() {
    let filtering: BindGroupLayoutEntry = BindGroupLayoutEntry::sampler(0, ShaderStage::Fragment);
    let non_filtering: BindGroupLayoutEntry =
        BindGroupLayoutEntry::sampler_non_filtering(0, ShaderStage::Fragment);
    assert_ne!(
        rendered(&filtering),
        rendered(&non_filtering),
        "nearest and linear sampling are different looks; collapsing them would \
         make `non_filtering` a lie"
    );
}

#[test]
fn a_comparison_sampler_differs_from_a_plain_one() {
    let plain: BindGroupLayoutEntry = BindGroupLayoutEntry::sampler(1, ShaderStage::Compute);
    let comparison: BindGroupLayoutEntry =
        BindGroupLayoutEntry::sampler_comparison(1, ShaderStage::Compute);
    assert_ne!(
        rendered(&plain),
        rendered(&comparison),
        "a comparison sampler feeds shadow maps; a plain one must not stand in for it"
    );
}

#[test]
fn every_bind_group_entry_records_the_binding_it_was_given() {
    let entries: [BindGroupLayoutEntry; 4] = [
        BindGroupLayoutEntry::sampler(0, ShaderStage::Fragment),
        BindGroupLayoutEntry::sampler_non_filtering(1, ShaderStage::Fragment),
        BindGroupLayoutEntry::sampler_comparison(2, ShaderStage::Fragment),
        BindGroupLayoutEntry::sampler(3, ShaderStage::Vertex),
    ];
    for (index, entry) in entries.iter().enumerate() {
        let text: String = rendered(entry);
        assert!(
            text.contains(&format!("binding: {index}")),
            "the binding index is how the shader finds the slot, got: {text}"
        );
    }
}

#[test]
fn the_two_bind_group_entry_type_families_stay_distinct() {
    let a: BindGroupLayoutEntry = BindGroupLayoutEntry::sampler(0, ShaderStage::Fragment);
    let b: BindGroupLayoutEntry =
        BindGroupLayoutEntry::texture_multisampled(0, ShaderStage::Fragment, "float");
    assert_ne!(rendered(&a), rendered(&b));
    let _sampler_kind: BindGroupEntryType = BindGroupEntryType::Sampler {
        comparison: false,
        filtering: true,
    };
    let _texture_kind: BindGroupEntryType = BindGroupEntryType::SampledTexture {
        sample_type: String::from("float"),
        multisampled: false,
    };
}

#[test]
fn a_color_target_keeps_the_format_it_was_built_for() {
    let target: ColorTargetState = ColorTargetState::for_format(GpuTextureFormat::Rgba8Unorm);
    let text: String = rendered(&target);
    assert!(
        text.contains("Rgba8Unorm"),
        "a colour attachment's format is the whole point of the call, got: {text}"
    );
    let other: ColorTargetState = ColorTargetState::for_format(GpuTextureFormat::Bgra8Unorm);
    assert_ne!(
        text,
        rendered(&other),
        "two formats must not produce the same attachment"
    );
}

#[test]
fn a_multisample_state_of_one_carries_the_full_sample_mask() {
    let single: MultisampleState = MultisampleState::with_sample_count(1);
    let four: MultisampleState = MultisampleState::with_sample_count(4);
    let single_text: String = rendered(&single);
    assert!(single_text.contains("count: 1"), "got: {single_text}");
    assert!(
        single_text.replace("count: 1", "") == rendered(&four).replace("count: 4", ""),
        "only the count may differ between sample counts; the mask must not: \
         {single_text} vs {}",
        rendered(&four)
    );
}

#[test]
fn a_default_texture_descriptor_has_a_single_mip_and_no_multisampling() {
    let texture: Texture2DDescriptor =
        Texture2DDescriptor::default_for(1920, 1080, GpuTextureFormat::Rgba8Unorm);
    let text: String = rendered(&texture);
    assert!(text.contains("width: 1920"), "got: {text}");
    assert!(text.contains("height: 1080"), "got: {text}");
    assert!(
        text.contains("mip_level_count: 1") && text.contains("sample_count: 1"),
        "a default texture is neither mipmapped nor multisampled, got: {text}"
    );
}

#[test]
fn a_full_texture_view_selects_everything_from_level_zero() {
    let view: TextureViewDescriptor = TextureViewDescriptor::full();
    let text: String = rendered(&view);
    assert!(
        text.contains("base_mip_level: 0"),
        "a full view starts at the base level, got: {text}"
    );
    for field in [
        "mip_level_count: 0",
        "base_array_layer: 0",
        "array_layer_count: 0",
    ] {
        assert!(
            text.contains(field),
            "a full view leaves {field} to the implementation, got: {text}"
        );
    }
}

#[test]
fn a_single_mip_view_starts_at_the_requested_level() {
    let view: TextureViewDescriptor = TextureViewDescriptor::mip(3);
    let text: String = rendered(&view);
    assert!(
        text.contains("base_mip_level: 3"),
        "asking for mip 3 must actually look at mip 3, got: {text}"
    );
}

#[test]
fn a_depth_only_view_starts_at_the_base_level() {
    let view: TextureViewDescriptor = TextureViewDescriptor::depth_only();
    let text: String = rendered(&view);
    assert!(
        text.contains("base_mip_level: 0"),
        "a depth attachment has no mip chain to pick from, got: {text}"
    );
    assert_ne!(
        text,
        rendered(&TextureViewDescriptor::full()),
        "a depth-only view is a different view, not a synonym for the full one"
    );
}

#[test]
fn a_uniform_binding_differs_from_a_storage_binding() {
    let uniform: BindGroupLayoutEntry = BindGroupLayoutEntry::uniform(0, ShaderStage::Vertex);
    let storage: BindGroupLayoutEntry =
        BindGroupLayoutEntry::storage(0, ShaderStage::Vertex, false);
    assert_ne!(
        rendered(&uniform),
        rendered(&storage),
        "read-write storage is a different shader contract than a uniform buffer"
    );
}

#[test]
fn a_read_only_storage_binding_differs_from_a_writable_one() {
    let read_only: BindGroupLayoutEntry =
        BindGroupLayoutEntry::storage(0, ShaderStage::Compute, true);
    let writable: BindGroupLayoutEntry =
        BindGroupLayoutEntry::storage(0, ShaderStage::Compute, false);
    assert_ne!(
        rendered(&read_only),
        rendered(&writable),
        "the read-only flag is the whole difference and must survive into the layout"
    );
}

#[test]
fn a_single_sampled_texture_differs_from_a_multisampled_one() {
    let single: BindGroupLayoutEntry =
        BindGroupLayoutEntry::texture(0, ShaderStage::Fragment, "float");
    let multi: BindGroupLayoutEntry =
        BindGroupLayoutEntry::texture_multisampled(0, ShaderStage::Fragment, "float");
    assert_ne!(
        rendered(&single),
        rendered(&multi),
        "a resolve target and a multisample target are different layouts"
    );
}

#[test]
fn a_write_only_storage_texture_differs_from_a_read_write_one() {
    let read_only: BindGroupLayoutEntry =
        BindGroupLayoutEntry::storage_texture(0, ShaderStage::Compute, "r32float", true);
    let writable: BindGroupLayoutEntry =
        BindGroupLayoutEntry::storage_texture(0, ShaderStage::Compute, "r32float", false);
    assert_ne!(rendered(&read_only), rendered(&writable));
}

#[test]
fn the_nearest_clamp_sampler_filters_nothing_and_clamps_every_edge() {
    let sampler: SamplerDescriptor = SamplerDescriptor::nearest_clamp();
    let text: String = rendered(&sampler);
    assert!(
        text.contains("Nearest"),
        "the cheapest sampler must not blur, got: {text}"
    );
    assert!(
        text.contains("ClampToEdge"),
        "clamp-to-edge is what stops a border texel from wrapping, got: {text}"
    );
}

#[test]
fn a_whole_stream_draw_covers_every_vertex_of_every_instance() {
    let args: DrawArgs = DrawArgs::whole_stream(36, 2);
    let text: String = rendered(&args);
    assert!(text.contains("vertex_count: 36"), "got: {text}");
    assert!(text.contains("instance_count: 2"), "got: {text}");
    assert!(text.contains("first_vertex: 0"), "got: {text}");
    assert!(text.contains("first_instance: 0"), "got: {text}");
}

#[test]
fn a_whole_indexed_draw_covers_every_index_with_no_base_vertex() {
    let args: DrawIndexedArgs = DrawIndexedArgs::whole_buffer(6, 1);
    let text: String = rendered(&args);
    assert!(text.contains("index_count: 6"), "got: {text}");
    assert!(text.contains("first_index: 0"), "got: {text}");
    assert!(
        text.contains("base_vertex: 0"),
        "a non-zero base vertex would silently re-index the whole buffer, got: {text}"
    );
}

#[test]
fn a_dispatch_covers_the_whole_three_dimensional_grid() {
    let args: DispatchArgs = DispatchArgs::grid(8, 4, 2);
    let text: String = rendered(&args);
    for field in ["x: 8", "y: 4", "z: 2"] {
        assert!(
            text.contains(field),
            "the workgroup {field} must survive, got: {text}"
        );
    }
}

#[test]
fn a_sampler_entry_keeps_its_filtering_and_comparison_flags_apart() {
    let plain: BindGroupLayoutEntry = BindGroupLayoutEntry::sampler(0, ShaderStage::Fragment);
    let non_filtering: BindGroupLayoutEntry =
        BindGroupLayoutEntry::sampler_non_filtering(0, ShaderStage::Fragment);
    let comparison: BindGroupLayoutEntry =
        BindGroupLayoutEntry::sampler_comparison(0, ShaderStage::Fragment);
    let shapes: Vec<String> = vec![
        rendered(&plain),
        rendered(&non_filtering),
        rendered(&comparison),
    ];
    for i in 0..shapes.len() {
        for j in (i + 1)..shapes.len() {
            assert_ne!(
                shapes[i], shapes[j],
                "three sampler flavours collapsed into one layout"
            );
        }
    }
}