vulkano-framing 0.3.0

Uploading images to and downloading frames from the GPU.
Documentation
//! Puts the image in a triangle and overlays a silly-looking gradient.

#[macro_use] extern crate vulkano;
#[macro_use] extern crate vulkano_shader_derive;
extern crate vulkano_framing;
extern crate png_framing;
extern crate framing;

use vulkano::buffer::{BufferUsage, CpuAccessibleBuffer};
use vulkano::command_buffer::{
    AutoCommandBufferBuilder,
    CommandBuffer,
    DynamicState
};
use vulkano::device::{Device, DeviceExtensions};
use vulkano::format::Format;
use vulkano::framebuffer::{Framebuffer, Subpass};
use vulkano::image::AttachmentImage;
use vulkano::instance::{Instance, InstanceExtensions, PhysicalDevice, Features};
use vulkano::pipeline::GraphicsPipeline;
use vulkano::pipeline::viewport::Viewport;
use vulkano::sampler::{Filter, MipmapMode, SamplerAddressMode, Sampler};
use vulkano::sync::GpuFuture;
use vulkano::descriptor::descriptor_set::PersistentDescriptorSet;

use png_framing as png;
use framing::Chunky;
use vulkano_framing::Buffer;
use std::sync::Arc;

#[derive(Clone, Debug)]
struct Vertex {
    pos: [f32; 3],
    tex: [f32; 2]
}
impl_vertex!(Vertex, pos, tex);

fn main() {
    let image = png::decode(include_bytes!("todoroki.png")).unwrap();

    let format = Format::R8G8B8A8Unorm;
    let (w, h) = (1920, 1200);
    let instance_ext = InstanceExtensions::none();
    let device_ext = DeviceExtensions::none();
    let features = Features::none();

    let instance = Instance::new(None, &instance_ext, None).unwrap();
    let phy = PhysicalDevice::enumerate(&instance).next().unwrap();

    let queue_family =
        phy.queue_families()
            .find(|&q| q.supports_graphics())
            .unwrap();

    let (device, mut queues) =
        Device::new(
            phy,
            &features,
            &device_ext,
            [(queue_family, 0.5)].iter().cloned()
        ).unwrap();

    let queue = queues.next().unwrap();

    let (texture, texture_future) =
        vulkano_framing::upload::<[u8; 4], _, _>(
            queue.clone(),
            format,
            image
        ).unwrap();

    texture_future.then_signal_fence_and_flush()
        .unwrap()
        .wait(None)
        .unwrap();

    let output: Arc<CpuAccessibleBuffer<[[u8; 4]]>> = unsafe {
        CpuAccessibleBuffer::uninitialized_array(
            device.clone(),
            w * h,
            BufferUsage::all(),
        ).unwrap()
    };

    let output = Buffer::new(output, w, h).unwrap();

    macro_rules! vertices {
        ($($pos:expr, $tex:expr),*) => {
            [$( Vertex { pos: $pos, tex: $tex } ),*]
        }
    }

    let vertices = CpuAccessibleBuffer::from_iter(
        device.clone(),
        BufferUsage::all(),
        vertices![
            [-0.8,  0.8, 0.0], [0.0, 1.0],
            [ 0.8,  0.8, 0.0], [1.0, 1.0],
            [ 0.0, -0.8, 0.0], [0.5, 0.0]
        ].iter().cloned()
    ).unwrap();

    let vs = VertexShader::load(device.clone()).unwrap();
    let fs = FragmentShader::load(device.clone()).unwrap();

    let render_pass = Arc::new(single_pass_renderpass!(
        device.clone(),
        attachments: {
            color: {
                load: DontCare,
                store: Store,
                format: format,
                samples: 1,
            }
        },
        pass: {
            color: [color],
            depth_stencil: {}
        }
    ).unwrap());

    let staging = Arc::new(
        AttachmentImage::new(
            device.clone(),
            [w as u32, h as u32],
            format
        ).unwrap()
    );

    let framebuffer = Arc::new(
        Framebuffer::start(render_pass.clone())
            .add(staging.clone())
            .unwrap()
            .build()
            .unwrap()
    );

    let pipeline = Arc::new(
        GraphicsPipeline::start()
            .vertex_input_single_buffer::<Vertex>()
            .vertex_shader(vs.main_entry_point(), ())
            .triangle_list()
            .viewports(Some(Viewport {
                origin: [0.0, 0.0],
                depth_range: 0.0..1.0,
                dimensions: [w as f32, h as f32],
            }))
            .fragment_shader(fs.main_entry_point(), ())
            .render_pass(Subpass::from(render_pass.clone(), 0).unwrap())
            .build(device.clone())
            .unwrap()
    );

    let sampler = Sampler::new(
        device.clone(),
        Filter::Linear,
        Filter::Linear,
        MipmapMode::Nearest,
        SamplerAddressMode::Repeat,
        SamplerAddressMode::Repeat,
        SamplerAddressMode::Repeat,
        0.0, 1.0, 0.0, 0.0
    ).unwrap();

    let set = Arc::new(
        PersistentDescriptorSet::start(pipeline.clone(), 0)
            .add_sampled_image(texture.clone(), sampler.clone())
            .unwrap()
            .build()
            .unwrap()
    );

    let commands =
        AutoCommandBufferBuilder::primary(device.clone(), queue.family())
            .unwrap()

            .begin_render_pass(
                framebuffer.clone(),
                false,
                vec![[0.0, 1.0, 0.0, 1.0].into()]
            )
            .unwrap()

            .draw(
                pipeline.clone(),
                DynamicState::none(),
                vertices.clone(),
                set.clone(),
                ()
            )
            .unwrap()

            .end_render_pass()
            .unwrap()

            .copy_image_to_buffer(staging.clone(), output.buffer().clone())
            .unwrap()

            .build()
            .unwrap();

    commands.execute(queue)
        .unwrap()
        .then_signal_fence_and_flush()
        .unwrap()
        .wait(None)
        .unwrap();

    png::save(
        &Chunky::new(framing::map(Into::into, output.read().unwrap())),
        "output.png"
    ).unwrap();

    println!("Saved image to `output.png`!");
}

macro_rules! shaders {
    ($(#[ kind = $kind:expr ] $name:ident $code:expr)*) => {
        #[doc(hidden)]
        mod shaders {$(
            #[allow(dead_code, non_snake_case)]
            pub mod $name {
                #[derive(VulkanoShader)]
                #[src = $code]
                #[ty = $kind]
                struct Dummy;
            }
        )*}

        $(use self::shaders::$name::Shader as $name;)*
    }
}

shaders! {
    #[kind = "vertex"]
    VertexShader "
        #version 450

        layout(location = 0) in vec3 pos;
        layout(location = 1) in vec2 tex;

        layout(location = 0) out vec2 tex_coords;

        void main() {
            gl_Position = vec4(pos, 1.0);
            tex_coords = tex;
        }
    "

    #[kind = "fragment"]
    FragmentShader "
        #version 450

        layout(location = 0) in vec2 tex_coords;
        layout(location = 0) out vec4 color;

        layout(set = 0, binding = 0) uniform sampler2D tex;

        void main() {
            color = texture(tex, tex_coords) + vec4(tex_coords, 0.0, 1.0);
        }
    "
}