prototty_graphical_gfx 0.29.0

Prototty context which renders with gfx
Documentation
use gfx::{self, *};

use crate::formats::*;
use prototty_render::grid_2d::CoordIter;
use prototty_render::Size;

const QUAD_INDICES: [u16; 6] = [0, 1, 2, 2, 3, 0];
const QUAD_COORDS: [[f32; 2]; 4] = [[0.0, 0.0], [0.0, 1.0], [1.0, 1.0], [1.0, 0.0]];

gfx_vertex_struct!(Vertex {
    corner_multiplier: [f32; 2] = "a_CornerMultiplier",
});

gfx_vertex_struct!(Instance {
    top_left_corner_pix_pos: [f32; 2] = "a_TopLeftCornerPixPos",
    background_colour: [f32; 4] = "a_BackgroundColour",
    foreground_colour: [f32; 4] = "a_ForegroundColour",
    underline: u32 = "a_Underline",
});

gfx_constant_struct!(Properties {
    cell_pix_size: [f32; 2] = "u_CellPixSize",
    window_pix_size: [f32; 2] = "u_WindowPixSize",
    underline_pix_width: f32 = "u_UnderlinePixWidth",
    underline_pix_pos: f32 = "u_UnderlinePixPos",
});

gfx_pipeline!( pipe {
    properties: gfx::ConstantBuffer<Properties> = "Properties",
    vertex: gfx::VertexBuffer<Vertex> = (),
    instance: gfx::InstanceBuffer<Instance> = (),
    out_colour: gfx::BlendTarget<ColourFormat> = ("Target0", gfx::state::ColorMask::all(), gfx::preset::blend::ALPHA),
});

pub struct BackgroundRenderer<R: gfx::Resources> {
    bundle: gfx::pso::bundle::Bundle<R, pipe::Data<R>>,
    instance_upload: gfx::handle::Buffer<R, Instance>,
    num_instances: usize,
    cell_width: f32,
    cell_height: f32,
    properties: Properties,
}

pub struct BackgroundRendererArgs<'a, R, F, C>
where
    R: gfx::Resources,
    F: gfx::Factory<R> + gfx::traits::FactoryExt<R>,
    C: gfx::CommandBuffer<R>,
{
    pub window_width: u32,
    pub window_height: u32,
    pub cell_width: f32,
    pub cell_height: f32,
    pub size: Size,
    pub underline_width: f32,
    pub underline_position: f32,
    pub rtv: gfx::handle::RenderTargetView<R, ColourFormat>,
    pub factory: &'a mut F,
    pub encoder: &'a mut gfx::Encoder<R, C>,
}

impl<R: gfx::Resources> BackgroundRenderer<R> {
    pub fn new<F, C>(
        BackgroundRendererArgs {
            window_width,
            window_height,
            cell_width,
            cell_height,
            size,
            underline_width,
            underline_position,
            rtv,
            factory,
            encoder,
        }: BackgroundRendererArgs<R, F, C>,
    ) -> Self
    where
        F: gfx::Factory<R> + gfx::traits::FactoryExt<R>,
        C: gfx::CommandBuffer<R>,
    {
        let pso = factory
            .create_pipeline_simple(
                include_bytes!("shaders/background.150.vert"),
                include_bytes!("shaders/background.150.frag"),
                pipe::new(),
            )
            .expect("Failed to create pipeline");

        let vertex_data: Vec<Vertex> = QUAD_COORDS.iter().map(|v| Vertex { corner_multiplier: *v }).collect();

        let (vertex_buffer, mut slice) = factory.create_vertex_buffer_with_slice(&vertex_data, &QUAD_INDICES[..]);

        let (num_instances, instance_buffer, instance_upload) =
            Self::create_instance_buffer(cell_width, cell_height, size, factory);

        slice.instances = Some((num_instances as u32, 0));

        let data = pipe::Data {
            properties: factory.create_constant_buffer(1),
            vertex: vertex_buffer,
            instance: instance_buffer,
            out_colour: rtv,
        };

        let properties = Properties {
            cell_pix_size: [cell_width, cell_height],
            window_pix_size: [window_width as f32, window_height as f32],
            underline_pix_width: underline_width,
            underline_pix_pos: underline_position,
        };

        encoder.update_constant_buffer(&data.properties, &properties);

        Self {
            bundle: gfx::pso::bundle::Bundle::new(slice, pso, data),
            instance_upload,
            num_instances,
            cell_width,
            cell_height,
            properties,
        }
    }

    pub fn map_cells<F: gfx::Factory<R>>(&mut self, factory: &mut F) -> gfx::mapping::Writer<R, Instance> {
        factory
            .write_mapping(&self.instance_upload)
            .expect("Failed to map instance upload buffer")
    }

    pub fn draw<C>(&self, encoder: &mut gfx::Encoder<R, C>)
    where
        C: gfx::CommandBuffer<R>,
    {
        encoder
            .copy_buffer(
                &self.instance_upload,
                &self.bundle.data.instance,
                0,
                0,
                self.num_instances,
            )
            .expect("Failed to copy instances");
        encoder.draw(&self.bundle.slice, &self.bundle.pso, &self.bundle.data);
    }

    fn create_instance_buffer<F>(
        cell_width: f32,
        cell_height: f32,
        size: Size,
        factory: &mut F,
    ) -> (
        usize,
        gfx::handle::Buffer<R, Instance>,
        gfx::handle::Buffer<R, Instance>,
    )
    where
        F: gfx::Factory<R> + gfx::traits::FactoryExt<R>,
    {
        let num_instances = size.count();

        let instance_buffer = factory
            .create_buffer(
                num_instances,
                gfx::buffer::Role::Vertex,
                gfx::memory::Usage::Data,
                gfx::memory::Bind::TRANSFER_DST,
            )
            .expect("Failed to create instance buffer");

        let instance_upload: gfx::handle::Buffer<R, Instance> = factory
            .create_upload_buffer(num_instances)
            .expect("Failed to create instance upload buffer");

        for (coord, instance) in CoordIter::new(size).zip(
            factory
                .write_mapping(&instance_upload)
                .expect("Failed to map instance upload buffer")
                .iter_mut(),
        ) {
            let x = coord.x as f32 * cell_width;
            let y = coord.y as f32 * cell_height;
            instance.top_left_corner_pix_pos = [x, y];
        }

        (num_instances, instance_buffer, instance_upload)
    }

    pub fn handle_resize<F, C>(
        &mut self,
        window_width: f32,
        window_height: f32,
        size: Size,
        rtv: gfx::handle::RenderTargetView<R, ColourFormat>,
        factory: &mut F,
        encoder: &mut gfx::Encoder<R, C>,
    ) where
        F: gfx::Factory<R> + gfx::traits::FactoryExt<R>,
        C: gfx::CommandBuffer<R>,
    {
        let (num_instances, instance_buffer, instance_upload) =
            Self::create_instance_buffer(self.cell_width, self.cell_height, size, factory);

        self.num_instances = num_instances;
        self.instance_upload = instance_upload;
        self.bundle.data.instance = instance_buffer;
        self.bundle.data.out_colour = rtv;
        self.bundle.slice.instances = Some((num_instances as u32, 0));

        self.properties.window_pix_size = [window_width as f32, window_height as f32];
        encoder.update_constant_buffer(&self.bundle.data.properties, &self.properties);
    }
}