truck-rendimpl 0.2.0

visualization of shape and polygon mesh based on platform
Documentation
#![allow(dead_code)]

use glsl_to_spirv::ShaderType;
use rayon::prelude::*;
use std::io::{Read, Write};
use std::sync::Arc;
use truck_platform::*;
use wgpu::*;

#[derive(Clone, Debug)]
pub struct Plane<'a> {
    pub vertex_shader: &'a str,
    pub fragment_shader: &'a str,
    pub id: RenderID,
}

#[macro_export]
macro_rules! new_plane {
    ($vertex_shader: expr, $fragment_shader: expr) => {
        Plane {
            vertex_shader: include_str!($vertex_shader),
            fragment_shader: include_str!($fragment_shader),
            id: RenderID::gen(),
        }
    };
}

impl<'a> Rendered for Plane<'a> {
    impl_render_id!(id);
    fn vertex_buffer(
        &self,
        handler: &DeviceHandler,
    ) -> (Arc<BufferHandler>, Option<Arc<BufferHandler>>) {
        let vertex_buffer = BufferHandler::from_slice(
            &[0 as u32, 1, 2, 2, 1, 3],
            handler.device(),
            BufferUsage::VERTEX,
        );
        (Arc::new(vertex_buffer), None)
    }
    fn bind_group_layout(&self, handler: &DeviceHandler) -> Arc<BindGroupLayout> {
        Arc::new(bind_group_util::create_bind_group_layout(
            handler.device(),
            &[],
        ))
    }
    fn bind_group(&self, handler: &DeviceHandler, layout: &BindGroupLayout) -> Arc<BindGroup> {
        Arc::new(handler.device().create_bind_group(&BindGroupDescriptor {
            label: None,
            layout,
            entries: &[],
        }))
    }
    fn pipeline(
        &self,
        handler: &DeviceHandler,
        layout: &PipelineLayout,
        sample_count: u32,
    ) -> Arc<RenderPipeline> {
        let (device, sc_desc) = (handler.device(), handler.sc_desc());
        let vertex_spirv = compile_shader(self.vertex_shader, ShaderType::Vertex);
        let vertex_module = device.create_shader_module(wgpu::util::make_spirv(&vertex_spirv));
        let fragment_spirv = compile_shader(self.fragment_shader, ShaderType::Fragment);
        let fragment_module = device.create_shader_module(wgpu::util::make_spirv(&fragment_spirv));
        Arc::new(
            handler
                .device()
                .create_render_pipeline(&RenderPipelineDescriptor {
                    layout: Some(layout),
                    vertex_stage: ProgrammableStageDescriptor {
                        module: &vertex_module,
                        entry_point: "main",
                    },
                    fragment_stage: Some(ProgrammableStageDescriptor {
                        module: &fragment_module,
                        entry_point: "main",
                    }),
                    rasterization_state: Some(RasterizationStateDescriptor {
                        front_face: FrontFace::Ccw,
                        cull_mode: CullMode::None,
                        depth_bias: 0,
                        depth_bias_slope_scale: 0.0,
                        depth_bias_clamp: 0.0,
                        clamp_depth: false,
                    }),
                    primitive_topology: PrimitiveTopology::TriangleList,
                    color_states: &[ColorStateDescriptor {
                        format: sc_desc.format,
                        color_blend: BlendDescriptor::REPLACE,
                        alpha_blend: BlendDescriptor::REPLACE,
                        write_mask: ColorWrite::ALL,
                    }],
                    depth_stencil_state: Some(DepthStencilStateDescriptor {
                        format: TextureFormat::Depth32Float,
                        depth_write_enabled: true,
                        depth_compare: wgpu::CompareFunction::Less,
                        stencil: StencilStateDescriptor {
                            front: StencilStateFaceDescriptor::IGNORE,
                            back: StencilStateFaceDescriptor::IGNORE,
                            read_mask: 0,
                            write_mask: 0,
                        },
                    }),
                    vertex_state: VertexStateDescriptor {
                        index_format: IndexFormat::Uint32,
                        vertex_buffers: &[VertexBufferDescriptor {
                            stride: std::mem::size_of::<u32>() as BufferAddress,
                            step_mode: InputStepMode::Vertex,
                            attributes: &[VertexAttributeDescriptor {
                                format: VertexFormat::Uint,
                                offset: 0,
                                shader_location: 0,
                            }],
                        }],
                    },
                    sample_count,
                    sample_mask: !0,
                    alpha_to_coverage_enabled: false,
                    label: None,
                }),
        )
    }
}

pub fn render_one<R: Rendered>(scene: &mut Scene, texture: &Texture, object: &R) {
    scene.add_object(object);
    scene.render_scene(&texture.create_view(&Default::default()));
    scene.remove_object(object);
}

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_scene(&texture.create_view(&Default::default()));
    scene.clear_objects();
}

pub fn compile_shader(code: &str, shadertype: ShaderType) -> Vec<u8> {
    let mut spirv = glsl_to_spirv::compile(&code, shadertype).unwrap();
    let mut compiled = Vec::new();
    spirv.read_to_end(&mut compiled).unwrap();
    compiled
}

pub fn nontex_answer_texture(scene: &mut Scene) -> Texture {
    let sc_desc = scene.sc_desc();
    let tex_desc = texture_descriptor(&sc_desc);
    let texture = scene.device().create_texture(&tex_desc);
    let mut plane = new_plane!("shaders/plane.vert", "shaders/unicolor.frag");
    render_one(scene, &texture, &mut plane);
    texture
}

pub fn random_texture(scene: &mut Scene) -> Texture {
    let sc_desc = scene.sc_desc();
    let tex_desc = texture_descriptor(&sc_desc);
    let texture = scene.device().create_texture(&tex_desc);
    let mut plane = new_plane!("shaders/plane.vert", "shaders/random.frag");
    render_one(scene, &texture, &mut plane);
    texture
}

pub fn gradation_texture(scene: &mut Scene) -> Texture {
    let sc_desc = scene.sc_desc();
    let tex_desc = texture_descriptor(&sc_desc);
    let texture = scene.device().create_texture(&tex_desc);
    let mut plane = new_plane!("shaders/plane.vert", "shaders/gradation.frag");
    render_one(scene, &texture, &mut plane);
    texture
}

pub fn init_device(instance: &Instance) -> (Arc<Device>, Arc<Queue>) {
    futures::executor::block_on(async {
        let adapter = instance
            .request_adapter(&RequestAdapterOptions {
                power_preference: PowerPreference::Default,
                compatible_surface: None,
            })
            .await
            .unwrap();
        writeln!(&mut std::io::stderr(), "{:?}", adapter.get_info()).unwrap();
        let (device, queue) = adapter
            .request_device(
                &DeviceDescriptor {
                    features: Default::default(),
                    limits: Limits::default(),
                    shader_validation: true,
                },
                None,
            )
            .await
            .unwrap();
        (Arc::new(device), Arc::new(queue))
    })
}

pub fn swap_chain_descriptor(size: (u32, u32)) -> SwapChainDescriptor {
    SwapChainDescriptor {
        usage: TextureUsage::OUTPUT_ATTACHMENT,
        format: TextureFormat::Rgba8Unorm,
        width: size.0,
        height: size.1,
        present_mode: PresentMode::Mailbox,
    }
}

pub fn texture_descriptor(sc_desc: &SwapChainDescriptor) -> TextureDescriptor<'static> {
    TextureDescriptor {
        label: None,
        size: Extent3d {
            width: sc_desc.width,
            height: sc_desc.height,
            depth: 1,
        },
        mip_level_count: 1,
        sample_count: 1,
        dimension: TextureDimension::D2,
        format: sc_desc.format,
        usage: TextureUsage::OUTPUT_ATTACHMENT | TextureUsage::COPY_SRC,
    }
}

pub fn texture_copy_view<'a>(texture: &'a Texture) -> TextureCopyView<'a> {
    TextureCopyView {
        texture: &texture,
        mip_level: 0,
        origin: Origin3d::ZERO,
    }
}

pub fn buffer_copy_view<'a>(buffer: &'a Buffer, size: (u32, u32)) -> BufferCopyView<'a> {
    BufferCopyView {
        buffer: &buffer,
        layout: TextureDataLayout {
            offset: 0,
            bytes_per_row: size.0 * 4,
            rows_per_image: size.1,
        },
    }
}

pub fn read_buffer(device: &Device, buffer: &Buffer) -> Vec<u8> {
    let buffer_slice = buffer.slice(..);
    let buffer_future = buffer_slice.map_async(MapMode::Read);
    device.poll(Maintain::Wait);
    futures::executor::block_on(async {
        match buffer_future.await {
            Ok(_) => buffer_slice.get_mapped_range().iter().map(|b| *b).collect(),
            Err(_) => panic!("failed to run compute on gpu!"),
        }
    })
}

pub fn read_texture(handler: &DeviceHandler, texture: &Texture) -> Vec<u8> {
    let (device, queue, sc_desc) = (handler.device(), handler.queue(), handler.sc_desc());
    let size = (sc_desc.width * sc_desc.height * 4) as u64;
    let buffer = device.create_buffer(&BufferDescriptor {
        label: None,
        mapped_at_creation: false,
        usage: BufferUsage::COPY_DST | BufferUsage::MAP_READ,
        size,
    });
    let mut encoder = device.create_command_encoder(&CommandEncoderDescriptor { label: None });
    encoder.copy_texture_to_buffer(
        texture_copy_view(&texture),
        buffer_copy_view(&buffer, (sc_desc.width, sc_desc.height)),
        Extent3d {
            width: sc_desc.width,
            height: sc_desc.height,
            depth: 1,
        },
    );
    queue.submit(Some(encoder.finish()));
    read_buffer(device, &buffer)
}

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

pub fn same_buffer(vec0: &Vec<u8>, vec1: &Vec<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: &Vec<u8>, vec1: &Vec<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(BackendBit, &str)>(test: F) {
    if cfg!(target_os = "windows") {
        test(BackendBit::VULKAN, "output/vulkan/");
        test(BackendBit::DX12, "output/dx12/");
    } else if cfg!(target_os = "macos") {
        test(BackendBit::METAL, "output/");
    } else {
        test(BackendBit::VULKAN, "output/");
    }
}