monstertruck-render 0.3.2

Shape and polygon mesh visualization
Documentation
#![allow(dead_code)]

use monstertruck_gpu::*;
use rayon::prelude::*;
use std::io::Write;
use std::sync::Arc;
use wgpu::*;

#[derive(Clone, Debug)]
pub struct Plane<'a> {
    pub shader: &'a str,
    pub vs_entpt: &'a str,
    pub fs_entpt: &'a str,
    pub id: RenderId,
}

#[macro_export]
macro_rules! new_plane {
    ($shader: expr, $vs_endpt: expr, $fs_endpt: expr) => {
        Plane {
            shader: include_str!($shader),
            vs_entpt: $vs_endpt,
            fs_entpt: $fs_endpt,
            id: RenderId::generate(),
        }
    };
}

impl Rendered for Plane<'_> {
    impl_render_id!(id);
    fn vertex_buffer(
        &self,
        handler: &DeviceHandler,
    ) -> (Arc<BufferHandler>, Option<Arc<BufferHandler>>) {
        writeln!(&mut std::io::stderr(), "create vertex buffer").unwrap();
        let vertex_buffer =
            BufferHandler::from_slice(&[0, 1, 2, 3], handler.device(), BufferUsages::VERTEX);
        let index_buffer =
            BufferHandler::from_slice(&[0, 1, 2, 2, 1, 3], handler.device(), BufferUsages::INDEX);
        (Arc::new(vertex_buffer), Some(Arc::new(index_buffer)))
    }
    fn bind_group_layout(&self, handler: &DeviceHandler) -> Arc<BindGroupLayout> {
        writeln!(&mut std::io::stderr(), "create bind group layout").unwrap();
        Arc::new(bind_group_util::create_bind_group_layout(
            handler.device(),
            &[],
        ))
    }
    fn bind_group(&self, handler: &DeviceHandler, layout: &BindGroupLayout) -> Arc<BindGroup> {
        writeln!(&mut std::io::stderr(), "create bind group").unwrap();
        Arc::new(handler.device().create_bind_group(&BindGroupDescriptor {
            label: None,
            layout,
            entries: &[],
        }))
    }
    fn pipeline(
        &self,
        handler: &DeviceHandler,
        layout: &PipelineLayout,
        scene_desc: &SceneDescriptor,
    ) -> Arc<RenderPipeline> {
        writeln!(&mut std::io::stderr(), "create pipeline").unwrap();
        let device = handler.device();
        let source = ShaderSource::Wgsl(self.shader.into());
        let module = device.create_shader_module(ShaderModuleDescriptor {
            source,
            label: None,
        });
        Arc::new(
            handler
                .device()
                .create_render_pipeline(&RenderPipelineDescriptor {
                    layout: Some(layout),
                    vertex: VertexState {
                        module: &module,
                        entry_point: Some(self.vs_entpt),
                        buffers: &[VertexBufferLayout {
                            array_stride: std::mem::size_of::<u32>() as BufferAddress,
                            step_mode: VertexStepMode::Vertex,
                            attributes: &[VertexAttribute {
                                format: VertexFormat::Uint32,
                                offset: 0,
                                shader_location: 0,
                            }],
                        }],
                        compilation_options: Default::default(),
                    },
                    fragment: Some(FragmentState {
                        module: &module,
                        entry_point: Some(self.fs_entpt),
                        targets: &[Some(ColorTargetState {
                            format: scene_desc.render_texture.format,
                            blend: Some(BlendState::REPLACE),
                            write_mask: ColorWrites::ALL,
                        })],
                        compilation_options: Default::default(),
                    }),
                    primitive: PrimitiveState {
                        topology: PrimitiveTopology::TriangleList,
                        front_face: FrontFace::Ccw,
                        cull_mode: None,
                        polygon_mode: PolygonMode::Fill,
                        ..Default::default()
                    },
                    depth_stencil: Some(DepthStencilState {
                        format: TextureFormat::Depth32Float,
                        depth_write_enabled: Some(true),
                        depth_compare: Some(wgpu::CompareFunction::Less),
                        stencil: Default::default(),
                        bias: Default::default(),
                    }),
                    multisample: MultisampleState {
                        count: scene_desc.backend_buffer.sample_count,
                        mask: !0,
                        alpha_to_coverage_enabled: false,
                    },
                    label: None,
                    multiview_mask: None,
                    cache: None,
                }),
        )
    }
}

pub fn render_one<R: Rendered>(scene: &mut Scene, object: &R) -> Vec<u8> {
    scene.add_object(object);
    let res = pollster::block_on(scene.render_to_buffer());
    scene.remove_object(object);
    res
}

pub fn render_ones<'a, R: 'a + Rendered, I: IntoIterator<Item = &'a R>>(
    scene: &mut Scene,
    texture: &Texture,
    object: I,
) {
    scene.add_objects(object);
    scene.render(&texture.create_view(&Default::default()));
    scene.clear_objects();
}

pub fn nontex_answer_texture(scene: &mut Scene) -> Vec<u8> {
    let plane = new_plane!("shaders/plane.wgsl", "vs_main", "unicolor");
    render_one(scene, &plane)
}

pub fn random_texture(scene: &mut Scene) -> Vec<u8> {
    let plane = new_plane!("shaders/plane.wgsl", "vs_main", "random_texture");
    render_one(scene, &plane)
}

pub fn gradation_texture(scene: &mut Scene) -> Vec<u8> {
    let plane = new_plane!("shaders/plane.wgsl", "vs_main", "gradation_texture");
    render_one(scene, &plane)
}

pub fn init_device(instance: &Instance) -> DeviceHandler {
    pollster::block_on(async {
        let adapter = instance
            .request_adapter(&RequestAdapterOptions {
                power_preference: PowerPreference::HighPerformance,
                compatible_surface: None,
                force_fallback_adapter: false,
            })
            .await
            .unwrap();
        writeln!(&mut std::io::stderr(), "{:?}", adapter.get_info()).unwrap();
        let (device, queue) = adapter
            .request_device(&DeviceDescriptor {
                required_features: Default::default(),
                required_limits: Limits::default(),
                memory_hints: Default::default(),
                trace: Default::default(),
                experimental_features: ExperimentalFeatures::disabled(),
                label: None,
            })
            .await
            .unwrap();
        DeviceHandler::new(adapter, device, queue)
    })
}

pub fn swap_chain_descriptor(size: (u32, u32)) -> SurfaceConfiguration {
    SurfaceConfiguration {
        usage: TextureUsages::RENDER_ATTACHMENT,
        format: TextureFormat::Rgba8Unorm,
        width: size.0,
        height: size.1,
        present_mode: PresentMode::Mailbox,
        view_formats: Vec::new(),
        alpha_mode: CompositeAlphaMode::Auto,
        desired_maximum_frame_latency: 2,
    }
}

pub fn texture_descriptor(config: &RenderTextureConfig) -> TextureDescriptor<'static> {
    TextureDescriptor {
        label: None,
        size: Extent3d {
            width: config.canvas_size.0,
            height: config.canvas_size.1,
            depth_or_array_layers: 1,
        },
        mip_level_count: 1,
        sample_count: 1,
        dimension: TextureDimension::D2,
        format: config.format,
        view_formats: &[],
        usage: TextureUsages::RENDER_ATTACHMENT | TextureUsages::COPY_SRC,
    }
}

pub fn save_buffer<P: AsRef<std::path::Path>>(path: P, vec: &[u8], size: (u32, u32)) {
    image::save_buffer(path, vec, size.0, size.1, image::ColorType::Rgba8).unwrap();
}

pub fn same_buffer(vec0: &[u8], vec1: &[u8]) -> bool {
    vec0.par_iter()
        .zip(vec1)
        .all(move |(i, j)| std::cmp::max(i, j) - std::cmp::min(i, j) < 3)
}

pub fn count_difference(vec0: &[u8], vec1: &[u8]) -> usize {
    vec0.par_iter()
        .zip(vec1)
        .filter(move |(i, j)| *std::cmp::max(i, j) - *std::cmp::min(i, j) > 2)
        .count()
}

pub fn os_alt_exec_test<F: Fn(Backends, &str)>(test: F) {
    let _ = env_logger::try_init();
    if cfg!(target_os = "windows") {
        test(Backends::VULKAN, "output/vulkan/");
        test(Backends::DX12, "output/dx12/");
    } else if cfg!(target_os = "macos") {
        test(Backends::METAL, "output/");
    } else {
        test(Backends::VULKAN, "output/");
    }
}