Working on an example.

Would be easier if I had vectors!
5 files changed, 324 insertions(+), 3 deletions(-)

M Cargo.toml
A => examples/rendy.rs
A => examples/shader.frag
A => examples/shader.vert
M src/main.rs
M Cargo.toml +6 -0
@@ 28,3 28,9 @@ structopt = "0.2"
 
 [dev-dependencies]
 assert_matches = "1"
+rendy = {version = "0.3", features = ["vulkan"]}
+lazy_static = "1"
+failure = "0.1"
+
+[[example]]
+name = "rendy"

          
A => examples/rendy.rs +293 -0
@@ 0,0 1,293 @@ 
+//! The triangle example from the `rendy` crate,
+//! but with a Chrysanthemum-compiled shader.
+#![cfg_attr(
+    not(any(feature = "dx12", feature = "metal", feature = "vulkan")),
+    allow(unused)
+)]
+
+use chrysanthemum as ch;
+use failure;
+use rendy::{
+    command::{Families, QueueId, RenderPassEncoder},
+    factory::{Config, Factory},
+    graph::{render::*, Graph, GraphBuilder, GraphContext, NodeBuffer, NodeImage},
+    hal,
+    memory::Dynamic,
+    mesh::PosColor,
+    resource::{Buffer, BufferInfo, DescriptorSetLayout, Escape, Handle},
+    shader::{ShaderKind, ShaderSet, SourceLanguage, SourceShaderInfo, SpirvShader},
+    wsi::winit::{EventsLoop, WindowBuilder},
+};
+
+fn compile_shader() -> Vec<u8> {
+    use ch::ast::*;
+    let vert = Decl::Function(FunctionDecl {
+        name: String::from("vertex"),
+        params: vec![Param {
+            name: "i".into(),
+            typ: Type("F32".into()),
+        }],
+        returns: Type("F32".into()),
+        body: vec![Expr::Var("i".into())],
+    });
+    let frag = Decl::Function(FunctionDecl {
+        name: String::from("fragment"),
+        params: vec![Param {
+            name: "input".into(),
+            typ: Type("F32".into()),
+        }],
+        returns: Type("F32".into()),
+        //body: vec![Expr::Var("input".into())],
+        body: vec![Expr::Literal(Lit::F32(0.5))],
+    });
+    let program = vec![vert, frag];
+    let ctx = ch::verify::verify(program).unwrap();
+    let cc = ch::compile::compile(&ctx).unwrap();
+
+    ch::words_to_bytes_le(&cc.output())
+}
+
+use rendy::mesh::AsVertex;
+
+#[cfg(feature = "dx12")]
+type Backend = rendy::dx12::Backend;
+
+#[cfg(feature = "metal")]
+type Backend = rendy::metal::Backend;
+
+#[cfg(feature = "vulkan")]
+type Backend = rendy::vulkan::Backend;
+
+lazy_static::lazy_static! {
+    static ref SHADER: Vec<u8> = compile_shader();
+    //static ref VERTEX: SpirvShader = SpirvShader::new(SHADER.clone(), hal::pso::ShaderStageFlags::VERTEX, "_vertex_entry");
+
+    static ref VERTEX: SpirvShader = SourceShaderInfo::new(
+        include_str!("shader.vert"),
+        concat!(env!("CARGO_MANIFEST_DIR"), "/examples/sprite/shader.vert").into(),
+        ShaderKind::Vertex,
+        SourceLanguage::GLSL,
+        "main",
+    ).precompile().unwrap();
+    //static ref FRAGMENT: SpirvShader = SpirvShader::new(SHADER.clone(), hal::pso::ShaderStageFlags::FRAGMENT, "_fragment_entry");
+
+    static ref FRAGMENT: SpirvShader = SourceShaderInfo::new(
+        include_str!("shader.frag"),
+        concat!(env!("CARGO_MANIFEST_DIR"), "/examples/sprite/shader.frag").into(),
+        ShaderKind::Fragment,
+        SourceLanguage::GLSL,
+        "main",
+    ).precompile().unwrap();
+
+    static ref SHADERS: rendy::shader::ShaderSetBuilder = rendy::shader::ShaderSetBuilder::default()
+        .with_vertex(&*VERTEX).unwrap()
+        .with_fragment(&*FRAGMENT).unwrap();
+}
+
+#[derive(Debug, Default)]
+struct TriangleRenderPipelineDesc;
+
+#[derive(Debug)]
+struct TriangleRenderPipeline<B: hal::Backend> {
+    vertex: Option<Escape<Buffer<B>>>,
+}
+
+impl<B, T> SimpleGraphicsPipelineDesc<B, T> for TriangleRenderPipelineDesc
+where
+    B: hal::Backend,
+    T: ?Sized,
+{
+    type Pipeline = TriangleRenderPipeline<B>;
+
+    fn depth_stencil(&self) -> Option<hal::pso::DepthStencilDesc> {
+        None
+    }
+
+    fn load_shader_set(&self, factory: &mut Factory<B>, _aux: &T) -> ShaderSet<B> {
+        SHADERS.build(factory, Default::default()).unwrap()
+    }
+
+    fn vertices(
+        &self,
+    ) -> Vec<(
+        Vec<hal::pso::Element<hal::format::Format>>,
+        hal::pso::ElemStride,
+        hal::pso::VertexInputRate,
+    )> {
+        return vec![PosColor::vertex().gfx_vertex_input_desc(hal::pso::VertexInputRate::Vertex)];
+    }
+
+    fn build<'a>(
+        self,
+        _ctx: &GraphContext<B>,
+        _factory: &mut Factory<B>,
+        _queue: QueueId,
+        _aux: &T,
+        buffers: Vec<NodeBuffer>,
+        images: Vec<NodeImage>,
+        set_layouts: &[Handle<DescriptorSetLayout<B>>],
+    ) -> Result<TriangleRenderPipeline<B>, failure::Error> {
+        assert!(buffers.is_empty());
+        assert!(images.is_empty());
+        assert!(set_layouts.is_empty());
+
+        Ok(TriangleRenderPipeline { vertex: None })
+    }
+}
+
+impl<B, T> SimpleGraphicsPipeline<B, T> for TriangleRenderPipeline<B>
+where
+    B: hal::Backend,
+    T: ?Sized,
+{
+    type Desc = TriangleRenderPipelineDesc;
+
+    fn prepare(
+        &mut self,
+        factory: &Factory<B>,
+        _queue: QueueId,
+        _set_layouts: &[Handle<DescriptorSetLayout<B>>],
+        _index: usize,
+        _aux: &T,
+    ) -> PrepareResult {
+        if self.vertex.is_none() {
+            let vbuf_size = PosColor::vertex().stride as u64 * 3;
+
+            let mut vbuf = factory
+                .create_buffer(
+                    BufferInfo {
+                        size: vbuf_size,
+                        usage: hal::buffer::Usage::VERTEX,
+                    },
+                    Dynamic,
+                )
+                .unwrap();
+
+            unsafe {
+                // Fresh buffer.
+                factory
+                    .upload_visible_buffer(
+                        &mut vbuf,
+                        0,
+                        &[
+                            PosColor {
+                                position: [0.0, -0.5, 0.0].into(),
+                                color: [1.0, 0.0, 0.0, 1.0].into(),
+                            },
+                            PosColor {
+                                position: [0.5, 0.5, 0.0].into(),
+                                color: [0.0, 1.0, 0.0, 1.0].into(),
+                            },
+                            PosColor {
+                                position: [-0.5, 0.5, 0.0].into(),
+                                color: [0.0, 0.0, 1.0, 1.0].into(),
+                            },
+                        ],
+                    )
+                    .unwrap();
+            }
+
+            self.vertex = Some(vbuf);
+        }
+
+        PrepareResult::DrawReuse
+    }
+
+    fn draw(
+        &mut self,
+        _layout: &B::PipelineLayout,
+        mut encoder: RenderPassEncoder<'_, B>,
+        _index: usize,
+        _aux: &T,
+    ) {
+        let vbuf = self.vertex.as_ref().unwrap();
+        unsafe {
+            encoder.bind_vertex_buffers(0, Some((vbuf.raw(), 0)));
+            encoder.draw(0..3, 0..1);
+        }
+    }
+
+    fn dispose(self, _factory: &mut Factory<B>, _aux: &T) {}
+}
+
+#[cfg(any(feature = "dx12", feature = "metal", feature = "vulkan"))]
+fn run(
+    event_loop: &mut EventsLoop,
+    factory: &mut Factory<Backend>,
+    families: &mut Families<Backend>,
+    mut graph: Graph<Backend, ()>,
+) -> Result<(), failure::Error> {
+    let started = std::time::Instant::now();
+
+    let mut frames = 0u64..;
+    let mut elapsed = started.elapsed();
+
+    for _ in &mut frames {
+        factory.maintain(families);
+        event_loop.poll_events(|_| ());
+        graph.run(factory, families, &());
+
+        elapsed = started.elapsed();
+        if elapsed >= std::time::Duration::new(5, 0) {
+            break;
+        }
+    }
+
+    let elapsed_ns = elapsed.as_secs() * 1_000_000_000 + elapsed.subsec_nanos() as u64;
+
+    log::info!(
+        "Elapsed: {:?}. Frames: {}. FPS: {}",
+        elapsed,
+        frames.start,
+        frames.start * 1_000_000_000 / elapsed_ns
+    );
+
+    graph.dispose(factory, &());
+    Ok(())
+}
+
+#[cfg(any(feature = "dx12", feature = "metal", feature = "vulkan"))]
+fn main() {
+    env_logger::Builder::from_default_env()
+        .filter_module("triangle", log::LevelFilter::Trace)
+        .init();
+
+    let config: Config = Default::default();
+
+    let (mut factory, mut families): (Factory<Backend>, _) = rendy::factory::init(config).unwrap();
+
+    let mut event_loop = EventsLoop::new();
+
+    let window = WindowBuilder::new()
+        .with_title("Rendy example")
+        .build(&event_loop)
+        .unwrap();
+
+    event_loop.poll_events(|_| ());
+
+    let surface = factory.create_surface(&window);
+
+    let mut graph_builder = GraphBuilder::<Backend, ()>::new();
+
+    graph_builder.add_node(
+        TriangleRenderPipeline::builder()
+            .into_subpass()
+            .with_color_surface()
+            .into_pass()
+            .with_surface(
+                surface,
+                Some(hal::command::ClearValue::Color([1.0, 1.0, 1.0, 1.0].into())),
+            ),
+    );
+
+    let graph = graph_builder
+        .build(&mut factory, &mut families, &())
+        .unwrap();
+
+    run(&mut event_loop, &mut factory, &mut families, graph).unwrap();
+}
+
+#[cfg(not(any(feature = "dx12", feature = "metal", feature = "vulkan")))]
+fn main() {
+    panic!("Specify feature: { dx12, metal, vulkan }");
+}

          
A => examples/shader.frag +11 -0
@@ 0,0 1,11 @@ 
+#version 450
+#extension GL_ARB_separate_shader_objects : enable
+
+layout(early_fragment_tests) in;
+
+layout(location = 0) in vec4 frag_color;
+layout(location = 0) out vec4 color;
+
+void main() {
+    color = frag_color;
+}

          
A => examples/shader.vert +11 -0
@@ 0,0 1,11 @@ 
+#version 450
+#extension GL_ARB_separate_shader_objects : enable
+
+layout(location = 0) in vec3 pos;
+layout(location = 1) in vec4 color;
+layout(location = 0) out vec4 frag_color;
+
+void main() {
+    frag_color = color;
+    gl_Position = vec4(pos, 1.0);
+}

          
M src/main.rs +3 -3
@@ 1,4 1,4 @@ 
-use chrysanthemum as c;
+use chrysanthemum as ch;
 use structopt::StructOpt;
 
 use std::fs;

          
@@ 18,8 18,8 @@ fn main() {
     let opt = Opt::from_args();
     let contents = fs::read_to_string(&opt.input).expect("Could not read input file");
 
-    let output = c::compile(&contents);
-    let output_bytes = c::words_to_bytes_le(&output);
+    let output = ch::compile(&contents);
+    let output_bytes = ch::words_to_bytes_le(&output);
 
     let mut output_path = opt.input.clone();
     output_path.set_extension("spv");