scena 1.7.2

A Rust-native scene-graph renderer with typed scene state, glTF assets, and explicit prepare/render lifecycles.
Documentation
use super::super::super::RasterTarget;
use super::super::material_bindings::MaterialTextureBindingMode;
use super::super::pipeline::{GPU_COLOR_FORMAT, create_unlit_pipeline_set};
use super::types::PostResources;
use super::{blit, bloom, bloom_fxaa, dof, fxaa, ssao, ssr};

pub(super) const POST_COLOR_FORMAT: wgpu::TextureFormat = wgpu::TextureFormat::Rgba8Unorm;
const POST_UNIFORM_BYTE_LEN: u64 = 32;

#[allow(clippy::too_many_arguments)]
pub(in crate::render::gpu) fn create_resources(
    device: &wgpu::Device,
    target: RasterTarget,
    output_bind_group_layout: &wgpu::BindGroupLayout,
    material_bind_group_layout: &wgpu::BindGroupLayout,
    draw_bind_group_layout: &wgpu::BindGroupLayout,
    texture_binding_mode: MaterialTextureBindingMode,
    depth_compare: Option<wgpu::CompareFunction>,
    surface_format: Option<wgpu::TextureFormat>,
) -> PostResources {
    let scene = create_post_texture(device, target, "scena.gpu_post.scene_encoded_srgb");
    let ping = create_post_texture(device, target, "scena.gpu_post.ping");
    let pong = create_post_texture(device, target, "scena.gpu_post.pong");
    let uniform = device.create_buffer(&wgpu::BufferDescriptor {
        label: Some("scena.gpu_post.uniform"),
        size: POST_UNIFORM_BYTE_LEN,
        usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
        mapped_at_creation: false,
    });
    let texture_bind_group_layout =
        device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
            label: Some("scena.gpu_post.texture_bind_group_layout"),
            entries: &[
                wgpu::BindGroupLayoutEntry {
                    binding: 0,
                    visibility: wgpu::ShaderStages::FRAGMENT,
                    ty: wgpu::BindingType::Texture {
                        sample_type: wgpu::TextureSampleType::Float { filterable: false },
                        view_dimension: wgpu::TextureViewDimension::D2,
                        multisampled: false,
                    },
                    count: None,
                },
                wgpu::BindGroupLayoutEntry {
                    binding: 1,
                    visibility: wgpu::ShaderStages::FRAGMENT,
                    ty: wgpu::BindingType::Buffer {
                        ty: wgpu::BufferBindingType::Uniform,
                        has_dynamic_offset: false,
                        min_binding_size: None,
                    },
                    count: None,
                },
            ],
        });
    let ssao_bind_group_layout =
        device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
            label: Some("scena.gpu_post.ssao_bind_group_layout"),
            entries: &[
                wgpu::BindGroupLayoutEntry {
                    binding: 0,
                    visibility: wgpu::ShaderStages::FRAGMENT,
                    ty: wgpu::BindingType::Texture {
                        sample_type: wgpu::TextureSampleType::Float { filterable: false },
                        view_dimension: wgpu::TextureViewDimension::D2,
                        multisampled: false,
                    },
                    count: None,
                },
                wgpu::BindGroupLayoutEntry {
                    binding: 1,
                    visibility: wgpu::ShaderStages::FRAGMENT,
                    ty: wgpu::BindingType::Buffer {
                        ty: wgpu::BufferBindingType::Uniform,
                        has_dynamic_offset: false,
                        min_binding_size: None,
                    },
                    count: None,
                },
                wgpu::BindGroupLayoutEntry {
                    binding: 2,
                    visibility: wgpu::ShaderStages::FRAGMENT,
                    ty: wgpu::BindingType::Texture {
                        sample_type: wgpu::TextureSampleType::Float { filterable: false },
                        view_dimension: wgpu::TextureViewDimension::D2,
                        multisampled: false,
                    },
                    count: None,
                },
            ],
        });
    let texture_bind_groups = [
        create_texture_bind_group(
            device,
            &texture_bind_group_layout,
            &scene.1,
            &uniform,
            "scena.gpu_post.scene_bind_group",
        ),
        create_texture_bind_group(
            device,
            &texture_bind_group_layout,
            &ping.1,
            &uniform,
            "scena.gpu_post.ping_bind_group",
        ),
        create_texture_bind_group(
            device,
            &texture_bind_group_layout,
            &pong.1,
            &uniform,
            "scena.gpu_post.pong_bind_group",
        ),
    ];
    let scene_pipelines = create_unlit_pipeline_set(
        device,
        POST_COLOR_FORMAT,
        output_bind_group_layout,
        material_bind_group_layout,
        draw_bind_group_layout,
        texture_binding_mode,
        depth_compare,
        1,
    );
    let scene_msaa4_pipelines = create_unlit_pipeline_set(
        device,
        POST_COLOR_FORMAT,
        output_bind_group_layout,
        material_bind_group_layout,
        draw_bind_group_layout,
        texture_binding_mode,
        depth_compare,
        4,
    );
    let output_blit_pipeline =
        blit::create_srgb_pipeline(device, &texture_bind_group_layout, GPU_COLOR_FORMAT);
    let surface_blit_pipeline = surface_format
        .map(|format| blit::create_surface_pipeline(device, &texture_bind_group_layout, format));
    let surface_fxaa_pipeline = surface_format
        .map(|format| fxaa::create_surface_pipeline(device, &texture_bind_group_layout, format));
    let surface_bloom_fxaa_pipeline = surface_format.map(|format| {
        bloom_fxaa::create_surface_pipeline(device, &texture_bind_group_layout, format)
    });
    let fxaa_pipeline = fxaa::create_pipeline(device, &texture_bind_group_layout);
    let ssr_pipeline = ssr::create_pipeline(device, &texture_bind_group_layout);
    let bloom_pipeline = bloom::create_pipeline(device, &texture_bind_group_layout);
    let ssao_pipeline = ssao::create_pipeline(device, &ssao_bind_group_layout);
    let depth_of_field_pipeline = dof::create_pipeline(device, &ssao_bind_group_layout);

    PostResources {
        target,
        scene_texture: scene.0,
        scene_view: scene.1,
        ping_texture: ping.0,
        ping_view: ping.1,
        pong_texture: pong.0,
        pong_view: pong.1,
        uniform,
        ssao_bind_group_layout,
        texture_bind_groups,
        scene_pipelines,
        scene_msaa4_pipelines,
        scene_msaa8_pipelines: None,
        output_blit_pipeline,
        surface_blit_pipeline,
        surface_fxaa_pipeline,
        surface_bloom_fxaa_pipeline,
        fxaa_pipeline,
        ssr_pipeline,
        bloom_pipeline,
        ssao_pipeline,
        depth_of_field_pipeline,
    }
}

fn create_post_texture(
    device: &wgpu::Device,
    target: RasterTarget,
    label: &'static str,
) -> (wgpu::Texture, wgpu::TextureView) {
    let texture = device.create_texture(&wgpu::TextureDescriptor {
        label: Some(label),
        size: wgpu::Extent3d {
            width: target.width,
            height: target.height,
            depth_or_array_layers: 1,
        },
        mip_level_count: 1,
        sample_count: 1,
        dimension: wgpu::TextureDimension::D2,
        format: POST_COLOR_FORMAT,
        usage: wgpu::TextureUsages::RENDER_ATTACHMENT
            | wgpu::TextureUsages::TEXTURE_BINDING
            | wgpu::TextureUsages::COPY_SRC,
        view_formats: &[],
    });
    let view = texture.create_view(&wgpu::TextureViewDescriptor::default());
    (texture, view)
}

fn create_texture_bind_group(
    device: &wgpu::Device,
    layout: &wgpu::BindGroupLayout,
    source_view: &wgpu::TextureView,
    uniform: &wgpu::Buffer,
    label: &'static str,
) -> wgpu::BindGroup {
    device.create_bind_group(&wgpu::BindGroupDescriptor {
        label: Some(label),
        layout,
        entries: &[
            wgpu::BindGroupEntry {
                binding: 0,
                resource: wgpu::BindingResource::TextureView(source_view),
            },
            wgpu::BindGroupEntry {
                binding: 1,
                resource: uniform.as_entire_binding(),
            },
        ],
    })
}