interleaved.rs
@@ -0,0 +1,187 @@
+//! Flat forward drawing pass that mimics a blit.
+
+use crate::pass::{
+ encoded_2d::*,
+ encoder::{EncodingBuffer, Flat2DData},
+ util::*,
+};
+use amethyst::{
+ assets::AssetStorage,
+ core::{
+ specs::prelude::{Read, ReadStorage, Write},
+ transform::GlobalTransform,
+ },
+ renderer::{
+ error::Result,
+ get_camera,
+ pipe::{
+ pass::{Pass, PassData},
+ DepthMode, Effect, NewEffect,
+ },
+ ActiveCamera, Attributes, Camera, Encoder, Factory, Query, Texture, VertexFormat,
+ },
+};
+use derivative::*;
+use gfx::pso::buffer::ElemStride;
+use gfx_core::state::{Blend, ColorMask};
+use glsl_layout::Uniform;
+
+/// Draws sprites on a 2D quad.
+#[derive(Derivative, Clone, Debug)]
+#[derivative(Default(bound = "Self: Pass"))]
+pub struct DrawFlat2DEncoded {
+ transparency: Option<(ColorMask, Blend, Option<DepthMode>)>,
+ instance_data: Vec<f32>,
+}
+
+impl DrawFlat2DEncoded
+where
+ Self: Pass,
+{
+ /// Create instance of `DrawFlat2DEncoded` pass
+ pub fn new() -> Self {
+ Self {
+ transparency: None,
+ instance_data: Vec::with_capacity(1024),
+ }
+ }
+
+ /// Enable transparency
+ pub fn with_transparency(
+ mut self,
+ mask: ColorMask,
+ blend: Blend,
+ depth: Option<DepthMode>,
+ ) -> Self {
+ self.transparency = Some((mask, blend, depth));
+ self
+ }
+
+ fn attributes() -> Attributes<'static> {
+ <SpriteInstance as Query<(DirX, DirY, Pos, OffsetU, OffsetV, Depth)>>::QUERIED_ATTRIBUTES
+ }
+}
+
+impl<'a> PassData<'a> for DrawFlat2DEncoded {
+ type Data = (
+ Option<Read<'a, ActiveCamera>>,
+ ReadStorage<'a, Camera>,
+ ReadStorage<'a, GlobalTransform>,
+ Write<'a, EncodingBuffer<Flat2DData>>,
+ Read<'a, AssetStorage<Texture>>,
+ );
+}
+
+impl Pass for DrawFlat2DEncoded {
+ fn compile(&mut self, effect: NewEffect<'_>) -> Result<Effect> {
+ use std::mem;
+
+ let mut builder = effect.simple(VERT_SRC, FRAG_SRC);
+ builder
+ .without_back_face_culling()
+ .with_raw_constant_buffer(
+ "ViewArgs",
+ mem::size_of::<<ViewArgs as Uniform>::Std140>(),
+ 1,
+ )
+ .with_raw_vertex_buffer(Self::attributes(), SpriteInstance::size() as ElemStride, 1);
+ setup_textures(&mut builder, &TEXTURES);
+ match self.transparency {
+ Some((mask, blend, depth)) => builder.with_blended_output("color", mask, blend, depth),
+ None => builder.with_output("color", Some(DepthMode::LessEqualWrite)),
+ };
+ builder.build()
+ }
+
+ fn apply<'a, 'b: 'a>(
+ &'a mut self,
+ encoder: &mut Encoder,
+ effect: &mut Effect,
+ mut factory: Factory,
+ (active, camera, global, mut buffer, tex_storage): <Self as PassData<'a>>::Data,
+ ) {
+ let camera = get_camera(active, &camera, &global);
+
+ use gfx::{
+ buffer,
+ memory::{Bind, Typed},
+ Factory,
+ };
+
+ // Sprite vertex shader
+ set_view_args(effect, encoder, camera);
+
+ // We might be able to improve performance here if we
+ // preallocate the maximum needed capacity. We need to
+ // iterate over the sprites though to find out the longest
+ // chain of sprites with the same texture, so we would need
+ // to check if it actually results in an improvement over just
+ // doing the allocations.
+ let instance_data = &mut self.instance_data;
+ let mut num_instances = 0;
+ let num_quads = buffer.vec.len();
+ let mut current_tex_id = buffer.vec.first().map(|d| d.texture.id()).unwrap_or(0);
+
+ for (i, quad) in buffer.vec.iter().enumerate() {
+ let Flat2DData {
+ dir_x,
+ dir_y,
+ pos,
+ uv_left,
+ uv_right,
+ uv_bottom,
+ uv_top,
+ texture,
+ ..
+ } = quad;
+ instance_data.extend(&[
+ dir_x.x, dir_x.y, dir_y.x, dir_y.y, pos.x, pos.y, *uv_left, *uv_right, *uv_bottom,
+ *uv_top, pos.z,
+ ]);
+
+ num_instances += 1;
+
+ // Need to flush outstanding draw calls due to state switch (texture).
+ //
+ // 1. We are at the last sprite and want to submit all pending work.
+ // 2. The next sprite will use a different texture triggering a flush.
+ let need_flush = i >= num_quads - 1 || current_tex_id != texture.id();
+ current_tex_id = texture.id();
+
+ if need_flush {
+ if let Some(texture) = tex_storage.get(texture) {
+ add_texture(effect, texture);
+
+ let vbuf = factory
+ .create_buffer_immutable(
+ &instance_data,
+ buffer::Role::Vertex,
+ Bind::empty(),
+ )
+ .expect("Unable to create immutable buffer for `TextureBatch`");
+
+ for _ in DrawFlat2DEncoded::attributes() {
+ effect.data.vertex_bufs.push(vbuf.raw().clone());
+ }
+
+ effect.draw(
+ &Slice {
+ start: 0,
+ end: 6,
+ base_vertex: 0,
+ instances: Some((num_instances, 0)),
+ buffer: Default::default(),
+ },
+ encoder,
+ );
+
+ effect.clear();
+ }
+
+ num_instances = 0;
+ instance_data.clear();
+ }
+ }
+ buffer.vec.clear();
+ }
+}