use super::{blender, utils::*, Color};
use crate::data::{DrawType, QuadsData, VxDraw};
use cgmath::{Matrix4, Rad};
use core::ptr::read;
#[cfg(feature = "dx12")]
use gfx_backend_dx12 as back;
#[cfg(feature = "gl")]
use gfx_backend_gl as back;
#[cfg(feature = "metal")]
use gfx_backend_metal as back;
#[cfg(feature = "vulkan")]
use gfx_backend_vulkan as back;
use gfx_hal::{device::Device, format, image, pass, pso, Backend, Primitive};
use std::{io::Cursor, mem::ManuallyDrop};
#[derive(Debug)]
pub struct Handle(usize, usize);
#[derive(Debug)]
pub struct Layer(usize);
impl Layerable for Layer {
fn get_layer(&self, vx: &VxDraw) -> usize {
for (idx, ord) in vx.draw_order.iter().enumerate() {
match ord {
DrawType::Quad { id } if *id == self.0 => {
return idx;
}
_ => {}
}
}
panic!["Unable to get layer"]
}
}
#[derive(Debug)]
pub struct LayerOptions {
depth_test: bool,
hide: bool,
blend: blender::Blender,
fixed_perspective: Option<Matrix4<f32>>,
}
impl Default for LayerOptions {
fn default() -> Self {
Self {
depth_test: false,
hide: false,
blend: blender::Blender::default(),
fixed_perspective: None,
}
}
}
impl LayerOptions {
pub fn new() -> Self {
Self::default()
}
pub fn hide(mut self) -> Self {
self.hide = true;
self
}
pub fn show(mut self) -> Self {
self.hide = false;
self
}
pub fn fixed_perspective(mut self, mat: Matrix4<f32>) -> Self {
self.fixed_perspective = Some(mat);
self
}
pub fn blend(mut self, blend_setter: impl Fn(blender::Blender) -> blender::Blender) -> Self {
self.blend = blend_setter(self.blend);
self
}
}
#[derive(Clone, Copy, Debug)]
pub struct Quad {
width: f32,
height: f32,
depth: f32,
colors: [(u8, u8, u8, u8); 4],
translation: (f32, f32),
rotation: f32,
scale: f32,
origin: (f32, f32),
}
impl Quad {
pub fn new() -> Self {
Self::default()
}
pub fn width(mut self, width: f32) -> Self {
self.width = width;
self
}
pub fn height(mut self, height: f32) -> Self {
self.height = height;
self
}
pub fn colors(mut self, colors: [(u8, u8, u8, u8); 4]) -> Self {
self.colors = colors;
self
}
pub fn translation(mut self, trn: (f32, f32)) -> Self {
self.translation = trn;
self
}
pub fn rotation<T: Copy + Into<Rad<f32>>>(mut self, angle: T) -> Self {
self.rotation = angle.into().0;
self
}
pub fn scale(mut self, scale: f32) -> Self {
self.scale = scale;
self
}
pub fn origin(mut self, origin: (f32, f32)) -> Self {
self.origin = origin;
self
}
}
impl Default for Quad {
fn default() -> Self {
Quad {
width: 2.0,
height: 2.0,
depth: 0.0,
colors: [(0, 0, 0, 255); 4],
translation: (0.0, 0.0),
rotation: 0.0,
scale: 1.0,
origin: (0.0, 0.0),
}
}
}
pub struct Quads<'a> {
vx: &'a mut VxDraw,
}
impl<'a> Quads<'a> {
pub fn new(vx: &'a mut VxDraw) -> Self {
Self { vx }
}
pub fn compare_draw_order(&self, left: &Handle, right: &Handle) -> std::cmp::Ordering {
let layer_ordering = left.0.cmp(&right.0);
if layer_ordering == std::cmp::Ordering::Equal {
left.1.cmp(&right.1)
} else {
layer_ordering
}
}
pub fn swap_draw_order(&mut self, left: &mut Handle, right: &mut Handle) {
let q1d = self.vx.quads[left.0].posbuffer[left.1];
let q2d = self.vx.quads[right.0].posbuffer[right.1];
self.vx.quads[left.0].posbuffer[left.1] = q2d;
self.vx.quads[right.0].posbuffer[right.1] = q1d;
let q1d = self.vx.quads[left.0].colbuffer[left.1];
let q2d = self.vx.quads[right.0].colbuffer[right.1];
self.vx.quads[left.0].colbuffer[left.1] = q2d;
self.vx.quads[right.0].colbuffer[right.1] = q1d;
let q1d = self.vx.quads[left.0].tranbuffer[left.1];
let q2d = self.vx.quads[right.0].tranbuffer[right.1];
self.vx.quads[left.0].tranbuffer[left.1] = q2d;
self.vx.quads[right.0].tranbuffer[right.1] = q1d;
let q1d = self.vx.quads[left.0].rotbuffer[left.1];
let q2d = self.vx.quads[right.0].rotbuffer[right.1];
self.vx.quads[left.0].rotbuffer[left.1] = q2d;
self.vx.quads[right.0].rotbuffer[right.1] = q1d;
let q1d = self.vx.quads[left.0].scalebuffer[left.1];
let q2d = self.vx.quads[right.0].scalebuffer[right.1];
self.vx.quads[left.0].scalebuffer[left.1] = q2d;
self.vx.quads[right.0].scalebuffer[right.1] = q1d;
self.vx.quads[left.0].posbuf_touch = self.vx.swapconfig.image_count;
self.vx.quads[left.0].colbuf_touch = self.vx.swapconfig.image_count;
self.vx.quads[left.0].tranbuf_touch = self.vx.swapconfig.image_count;
self.vx.quads[left.0].rotbuf_touch = self.vx.swapconfig.image_count;
self.vx.quads[left.0].scalebuf_touch = self.vx.swapconfig.image_count;
self.vx.quads[right.0].posbuf_touch = self.vx.swapconfig.image_count;
self.vx.quads[right.0].colbuf_touch = self.vx.swapconfig.image_count;
self.vx.quads[right.0].tranbuf_touch = self.vx.swapconfig.image_count;
self.vx.quads[right.0].rotbuf_touch = self.vx.swapconfig.image_count;
self.vx.quads[right.0].scalebuf_touch = self.vx.swapconfig.image_count;
std::mem::swap(&mut left.0, &mut right.0);
std::mem::swap(&mut left.1, &mut right.1);
}
pub fn add_layer(&mut self, options: &LayerOptions) -> Layer {
let s = &mut *self.vx;
pub const VERTEX_SOURCE: &[u8] = include_bytes!["../target/spirv/quads.vert.spirv"];
pub const FRAGMENT_SOURCE: &[u8] = include_bytes!["../target/spirv/quads.frag.spirv"];
let vertex_source = pso::read_spirv(Cursor::new(VERTEX_SOURCE)).unwrap();
let fragment_source = pso::read_spirv(Cursor::new(FRAGMENT_SOURCE)).unwrap();
let vs_module = { unsafe { s.device.create_shader_module(&vertex_source) }.unwrap() };
let fs_module = { unsafe { s.device.create_shader_module(&fragment_source) }.unwrap() };
const ENTRY_NAME: &str = "main";
let vs_module: <back::Backend as Backend>::ShaderModule = vs_module;
let (vs_entry, fs_entry) = (
pso::EntryPoint {
entry: ENTRY_NAME,
module: &vs_module,
specialization: pso::Specialization::default(),
},
pso::EntryPoint {
entry: ENTRY_NAME,
module: &fs_module,
specialization: pso::Specialization::default(),
},
);
let shader_entries = pso::GraphicsShaderSet {
vertex: vs_entry,
hull: None,
domain: None,
geometry: None,
fragment: Some(fs_entry),
};
let input_assembler = pso::InputAssemblerDesc::new(Primitive::TriangleList);
let vertex_buffers: Vec<pso::VertexBufferDesc> = vec![
pso::VertexBufferDesc {
binding: 0,
stride: 2 * 4,
rate: pso::VertexInputRate::Vertex,
},
pso::VertexBufferDesc {
binding: 1,
stride: 4,
rate: pso::VertexInputRate::Vertex,
},
pso::VertexBufferDesc {
binding: 2,
stride: 8,
rate: pso::VertexInputRate::Vertex,
},
pso::VertexBufferDesc {
binding: 3,
stride: 4,
rate: pso::VertexInputRate::Vertex,
},
pso::VertexBufferDesc {
binding: 4,
stride: 4,
rate: pso::VertexInputRate::Vertex,
},
];
let attributes: Vec<pso::AttributeDesc> = vec![
pso::AttributeDesc {
location: 0,
binding: 0,
element: pso::Element {
format: format::Format::Rg32Sfloat,
offset: 0,
},
},
pso::AttributeDesc {
location: 1,
binding: 1,
element: pso::Element {
format: format::Format::Rgba8Unorm,
offset: 0,
},
},
pso::AttributeDesc {
location: 2,
binding: 2,
element: pso::Element {
format: format::Format::Rg32Sfloat,
offset: 0,
},
},
pso::AttributeDesc {
location: 3,
binding: 3,
element: pso::Element {
format: format::Format::R32Sfloat,
offset: 0,
},
},
pso::AttributeDesc {
location: 4,
binding: 4,
element: pso::Element {
format: format::Format::R32Sfloat,
offset: 0,
},
},
];
let rasterizer = pso::Rasterizer {
depth_clamping: false,
polygon_mode: pso::PolygonMode::Fill,
cull_face: pso::Face::NONE,
front_face: pso::FrontFace::Clockwise,
depth_bias: None,
conservative: false,
};
let depth_stencil = pso::DepthStencilDesc {
depth: if options.depth_test {
Some(pso::DepthTest {
fun: pso::Comparison::LessEqual,
write: true,
})
} else {
None
},
depth_bounds: false,
stencil: None,
};
let blender = options.blend.clone().into_gfx_blender();
let quad_render_pass = {
let attachment = pass::Attachment {
format: Some(s.format),
samples: 1,
ops: pass::AttachmentOps::new(
pass::AttachmentLoadOp::Clear,
pass::AttachmentStoreOp::Store,
),
stencil_ops: pass::AttachmentOps::DONT_CARE,
layouts: image::Layout::Undefined..image::Layout::Present,
};
let depth = pass::Attachment {
format: Some(format::Format::D32Sfloat),
samples: 1,
ops: pass::AttachmentOps::new(
pass::AttachmentLoadOp::Clear,
pass::AttachmentStoreOp::Store,
),
stencil_ops: pass::AttachmentOps::DONT_CARE,
layouts: image::Layout::Undefined..image::Layout::DepthStencilAttachmentOptimal,
};
let subpass = pass::SubpassDesc {
colors: &[(0, image::Layout::ColorAttachmentOptimal)],
depth_stencil: Some(&(1, image::Layout::DepthStencilAttachmentOptimal)),
inputs: &[],
resolves: &[],
preserves: &[],
};
unsafe {
s.device
.create_render_pass(&[attachment, depth], &[subpass], &[])
}
.expect("Can't create render pass")
};
let baked_states = pso::BakedStates {
viewport: None,
scissor: None,
blend_color: None,
depth_bounds: None,
};
let bindings = Vec::<pso::DescriptorSetLayoutBinding>::new();
let immutable_samplers = Vec::<<back::Backend as Backend>::Sampler>::new();
let quad_descriptor_set_layouts: Vec<<back::Backend as Backend>::DescriptorSetLayout> =
vec![unsafe {
s.device
.create_descriptor_set_layout(bindings, immutable_samplers)
.expect("Couldn't make a DescriptorSetLayout")
}];
let mut push_constants = Vec::<(pso::ShaderStageFlags, std::ops::Range<u32>)>::new();
push_constants.push((pso::ShaderStageFlags::VERTEX, 0..16));
let quad_pipeline_layout = unsafe {
s.device
.create_pipeline_layout(&quad_descriptor_set_layouts, push_constants)
.expect("Couldn't create a pipeline layout")
};
let pipeline_desc = pso::GraphicsPipelineDesc {
shaders: shader_entries,
rasterizer,
vertex_buffers,
attributes,
input_assembler,
blender,
depth_stencil,
multisampling: None,
baked_states,
layout: &quad_pipeline_layout,
subpass: pass::Subpass {
index: 0,
main_pass: &quad_render_pass,
},
flags: pso::PipelineCreationFlags::empty(),
parent: pso::BasePipeline::None,
};
let quad_pipeline = unsafe {
s.device
.create_graphics_pipeline(&pipeline_desc, None)
.expect("Couldn't create a graphics pipeline!")
};
unsafe {
s.device.destroy_shader_module(vs_module);
s.device.destroy_shader_module(fs_module);
}
let image_count = s.swapconfig.image_count;
let posbuf = (0..image_count)
.map(|_| super::utils::ResizBuf::new(&s.device, &s.adapter))
.collect::<Vec<_>>();
let colbuf = (0..image_count)
.map(|_| super::utils::ResizBuf::new(&s.device, &s.adapter))
.collect::<Vec<_>>();
let tranbuf = (0..image_count)
.map(|_| super::utils::ResizBuf::new(&s.device, &s.adapter))
.collect::<Vec<_>>();
let rotbuf = (0..image_count)
.map(|_| super::utils::ResizBuf::new(&s.device, &s.adapter))
.collect::<Vec<_>>();
let scalebuf = (0..image_count)
.map(|_| super::utils::ResizBuf::new(&s.device, &s.adapter))
.collect::<Vec<_>>();
let indices = (0..image_count)
.map(|_| super::utils::ResizBufIdx4::new(&s.device, &s.adapter))
.collect::<Vec<_>>();
let quads = QuadsData {
hidden: options.hide,
fixed_perspective: options.fixed_perspective,
holes: vec![],
posbuf_touch: 0,
colbuf_touch: 0,
tranbuf_touch: 0,
rotbuf_touch: 0,
scalebuf_touch: 0,
posbuffer: vec![],
colbuffer: vec![],
tranbuffer: vec![],
rotbuffer: vec![],
scalebuffer: vec![],
posbuf,
colbuf,
tranbuf,
rotbuf,
scalebuf,
indices,
descriptor_set: quad_descriptor_set_layouts,
pipeline: ManuallyDrop::new(quad_pipeline),
pipeline_layout: ManuallyDrop::new(quad_pipeline_layout),
render_pass: ManuallyDrop::new(quad_render_pass),
};
let prev_layer = s.layer_holes.find_available(|x| match x {
DrawType::Quad { .. } => true,
_ => false,
});
if let Some(prev_layer) = prev_layer {
match prev_layer {
DrawType::Quad { id } => {
let old_quad = std::mem::replace(&mut s.quads[id], quads);
destroy_layer(s, old_quad);
s.draw_order.push(DrawType::Quad { id });
Layer(id)
}
_ => panic!["Got a non-quads drawtype, should be impossible!"],
}
} else {
s.quads.push(quads);
s.draw_order.push(DrawType::Quad {
id: s.quads.len() - 1,
});
Layer(s.quads.len() - 1)
}
}
pub fn layer_count(&self) -> usize {
self.vx.quads.len()
}
pub fn hide(&mut self, layer: &Layer) {
self.vx.quads[layer.0].hidden = true;
}
pub fn show(&mut self, layer: &Layer) {
self.vx.quads[layer.0].hidden = false;
}
pub fn add(&mut self, layer: &Layer, quad: Quad) -> Handle {
let width = quad.width;
let height = quad.height;
let topleft = (
-width / 2f32 - quad.origin.0,
-height / 2f32 - quad.origin.1,
);
let topright = (width / 2f32 - quad.origin.0, -height / 2f32 - quad.origin.1);
let bottomleft = (-width / 2f32 - quad.origin.0, height / 2f32 - quad.origin.1);
let bottomright = (width / 2f32 - quad.origin.0, height / 2f32 - quad.origin.1);
let replace = self.vx.quads.get(layer.0).map(|x| !x.holes.is_empty());
if replace.is_none() {
panic!["Layer does not exist"];
}
let handle = if replace.unwrap() {
let hole = self.vx.quads.get_mut(layer.0).unwrap().holes.pop().unwrap();
let handle = Handle(layer.0, hole);
self.set_deform(
&handle,
[
(topleft.0, topleft.1),
(bottomleft.0, bottomleft.1),
(bottomright.0, bottomright.1),
(topright.0, topright.1),
],
);
self.set_color(
&handle,
[
Color::Rgba(
quad.colors[0].0,
quad.colors[0].1,
quad.colors[0].2,
quad.colors[0].3,
),
Color::Rgba(
quad.colors[1].0,
quad.colors[1].1,
quad.colors[1].2,
quad.colors[1].3,
),
Color::Rgba(
quad.colors[2].0,
quad.colors[2].1,
quad.colors[2].2,
quad.colors[2].3,
),
Color::Rgba(
quad.colors[3].0,
quad.colors[3].1,
quad.colors[3].2,
quad.colors[3].3,
),
],
);
self.set_translation(&handle, (quad.translation.0, quad.translation.1));
self.set_rotation(&handle, Rad(quad.rotation));
self.set_scale(&handle, quad.scale);
handle
} else {
let quads = self.vx.quads.get_mut(layer.0).unwrap();
quads.posbuffer.push([
topleft.0,
topleft.1,
bottomleft.0,
bottomleft.1,
bottomright.0,
bottomright.1,
topright.0,
topright.1,
]);
quads.colbuffer.push([
quad.colors[0].0,
quad.colors[0].1,
quad.colors[0].2,
quad.colors[0].3,
quad.colors[1].0,
quad.colors[1].1,
quad.colors[1].2,
quad.colors[1].3,
quad.colors[2].0,
quad.colors[2].1,
quad.colors[2].2,
quad.colors[2].3,
quad.colors[3].0,
quad.colors[3].1,
quad.colors[3].2,
quad.colors[3].3,
]);
quads.tranbuffer.push([
quad.translation.0,
quad.translation.1,
quad.translation.0,
quad.translation.1,
quad.translation.0,
quad.translation.1,
quad.translation.0,
quad.translation.1,
]);
quads
.rotbuffer
.push([quad.rotation, quad.rotation, quad.rotation, quad.rotation]);
quads
.scalebuffer
.push([quad.scale, quad.scale, quad.scale, quad.scale]);
Handle(layer.0, quads.posbuffer.len() - 1)
};
let quads = self.vx.quads.get_mut(layer.0).unwrap();
quads.posbuf_touch = self.vx.swapconfig.image_count;
quads.colbuf_touch = self.vx.swapconfig.image_count;
quads.tranbuf_touch = self.vx.swapconfig.image_count;
quads.rotbuf_touch = self.vx.swapconfig.image_count;
quads.scalebuf_touch = self.vx.swapconfig.image_count;
handle
}
pub fn remove_layer(&mut self, layer: Layer) {
let s = &mut *self.vx;
let mut index = None;
for (idx, x) in s.draw_order.iter().enumerate() {
match x {
DrawType::Quad { id } if *id == layer.0 => {
index = Some(idx);
break;
}
_ => {}
}
}
if let Some(idx) = index {
let draw_type = s.draw_order.remove(idx);
s.layer_holes.push(draw_type);
}
}
pub fn remove(&mut self, handle: Handle) {
self.vx.quads[handle.0].holes.push(handle.1);
self.set_scale(&handle, 0.0);
}
pub fn set_deform(&mut self, handle: &Handle, points: [(f32, f32); 4]) {
self.vx.quads[handle.0].posbuf_touch = self.vx.swapconfig.image_count;
let vertex = &mut self.vx.quads[handle.0].posbuffer[handle.1];
for (idx, point) in points.iter().enumerate() {
vertex[idx * 2] = point.0;
vertex[idx * 2 + 1] = point.1;
}
}
pub fn set_solid_color(&mut self, handle: &Handle, rgba: Color) {
self.vx.quads[handle.0].colbuf_touch = self.vx.swapconfig.image_count;
for idx in 0..4 {
let Color::Rgba(r, g, b, a) = rgba;
self.vx.quads[handle.0].colbuffer[handle.1][idx * 4..(idx + 1) * 4]
.copy_from_slice(&[r, g, b, a]);
}
}
pub fn set_color(&mut self, handle: &Handle, rgba: [Color; 4]) {
self.vx.quads[handle.0].colbuf_touch = self.vx.swapconfig.image_count;
for (idx, dt) in rgba.iter().enumerate() {
let Color::Rgba(r, g, b, a) = dt;
self.vx.quads[handle.0].colbuffer[handle.1][idx * 4..(idx + 1) * 4]
.copy_from_slice(&[*r, *g, *b, *a]);
}
}
pub fn set_translation(&mut self, handle: &Handle, position: (f32, f32)) {
self.vx.quads[handle.0].tranbuf_touch = self.vx.swapconfig.image_count;
for idx in 0..4 {
self.vx.quads[handle.0].tranbuffer[handle.1][idx * 2] = position.0;
self.vx.quads[handle.0].tranbuffer[handle.1][idx * 2 + 1] = position.1;
}
}
pub fn set_rotation<T: Copy + Into<Rad<f32>>>(&mut self, handle: &Handle, angle: T) {
let angle = angle.into().0;
self.vx.quads[handle.0].rotbuf_touch = self.vx.swapconfig.image_count;
self.vx.quads[handle.0].rotbuffer[handle.1].copy_from_slice(&[angle, angle, angle, angle]);
}
pub fn set_scale(&mut self, handle: &Handle, scale: f32) {
self.vx.quads[handle.0].scalebuf_touch = self.vx.swapconfig.image_count;
for sc in &mut self.vx.quads[handle.0].scalebuffer[handle.1] {
*sc = scale;
}
}
pub fn deform(&mut self, handle: &Handle, delta: [(f32, f32); 4]) {
self.vx.quads[handle.0].posbuf_touch = self.vx.swapconfig.image_count;
let points = &mut self.vx.quads[handle.0].posbuffer[handle.1];
points[0] += delta[0].0;
points[1] += delta[0].1;
points[2] += delta[1].0;
points[3] += delta[1].1;
points[4] += delta[2].0;
points[5] += delta[2].1;
points[6] += delta[3].0;
points[7] += delta[3].1;
}
pub fn translate(&mut self, handle: &Handle, movement: (f32, f32)) {
self.vx.quads[handle.0].tranbuf_touch = self.vx.swapconfig.image_count;
for idx in 0..4 {
self.vx.quads[handle.0].tranbuffer[handle.1][idx * 2] += movement.0;
self.vx.quads[handle.0].tranbuffer[handle.1][idx * 2 + 1] += movement.1;
}
}
pub fn rotate<T: Copy + Into<Rad<f32>>>(&mut self, handle: &Handle, deg: T) {
self.vx.quads[handle.0].rotbuf_touch = self.vx.swapconfig.image_count;
for rot in &mut self.vx.quads[handle.0].rotbuffer[handle.1] {
*rot += deg.into().0;
}
}
pub fn scale(&mut self, handle: &Handle, scale: f32) {
self.vx.quads[handle.0].scalebuf_touch = self.vx.swapconfig.image_count;
for sc in &mut self.vx.quads[handle.0].scalebuffer[handle.1] {
*sc *= scale;
}
}
pub fn deform_all(&mut self, layer: &Layer, mut delta: impl FnMut(usize) -> [(f32, f32); 4]) {
self.vx.quads[layer.0].posbuf_touch = self.vx.swapconfig.image_count;
for (idx, quad) in self.vx.quads[layer.0].posbuffer.iter_mut().enumerate() {
let delta = delta(idx);
quad[0] += delta[0].0;
quad[1] += delta[0].1;
quad[2] += delta[1].0;
quad[3] += delta[1].1;
quad[4] += delta[2].0;
quad[5] += delta[2].1;
quad[6] += delta[3].0;
quad[7] += delta[3].1;
}
}
pub fn translate_all(&mut self, layer: &Layer, mut delta: impl FnMut(usize) -> (f32, f32)) {
self.vx.quads[layer.0].tranbuf_touch = self.vx.swapconfig.image_count;
for (idx, quad) in self.vx.quads[layer.0].tranbuffer.iter_mut().enumerate() {
let delta = delta(idx);
for idx in 0..4 {
quad[idx * 2] += delta.0;
quad[idx * 2 + 1] += delta.1;
}
}
}
pub fn rotate_all<T: Copy + Into<Rad<f32>>>(
&mut self,
layer: &Layer,
mut delta: impl FnMut(usize) -> T,
) {
self.vx.quads[layer.0].rotbuf_touch = self.vx.swapconfig.image_count;
for (idx, quad) in self.vx.quads[layer.0].rotbuffer.iter_mut().enumerate() {
let delta = delta(idx).into().0;
for rotation in quad.iter_mut() {
*rotation += delta;
}
}
}
pub fn scale_all(&mut self, layer: &Layer, mut delta: impl FnMut(usize) -> f32) {
self.vx.quads[layer.0].scalebuf_touch = self.vx.swapconfig.image_count;
for (idx, quad) in self.vx.quads[layer.0].scalebuffer.iter_mut().enumerate() {
let delta = delta(idx);
for scale in quad.iter_mut() {
*scale *= delta;
}
}
}
pub fn set_deform_all(
&mut self,
layer: &Layer,
mut delta: impl FnMut(usize) -> [(f32, f32); 4],
) {
self.vx.quads[layer.0].posbuf_touch = self.vx.swapconfig.image_count;
for (idx, quad) in self.vx.quads[layer.0].posbuffer.iter_mut().enumerate() {
let delta = delta(idx);
quad[0] = delta[0].0;
quad[1] = delta[0].1;
quad[2] = delta[1].0;
quad[3] = delta[1].1;
quad[4] = delta[2].0;
quad[5] = delta[2].1;
quad[6] = delta[3].0;
quad[7] = delta[3].1;
}
}
pub fn set_solid_color_all(&mut self, layer: &Layer, mut delta: impl FnMut(usize) -> Color) {
self.vx.quads[layer.0].colbuf_touch = self.vx.swapconfig.image_count;
for (idx, quad) in self.vx.quads[layer.0].colbuffer.iter_mut().enumerate() {
let delta = delta(idx);
for idx in 0..4 {
let Color::Rgba(r, g, b, a) = delta;
quad[idx * 4..(idx + 1) * 4].copy_from_slice(&[r, g, b, a]);
}
}
}
pub fn set_color_all(&mut self, layer: &Layer, mut delta: impl FnMut(usize) -> [Color; 4]) {
self.vx.quads[layer.0].colbuf_touch = self.vx.swapconfig.image_count;
for (idx, quad) in self.vx.quads[layer.0].colbuffer.iter_mut().enumerate() {
let delta = delta(idx);
for idx in 0..4 {
let Color::Rgba(r, g, b, a) = delta[idx];
quad[idx * 4..(idx + 1) * 4].copy_from_slice(&[r, g, b, a]);
}
}
}
pub fn set_translation_all(
&mut self,
layer: &Layer,
mut delta: impl FnMut(usize) -> (f32, f32),
) {
self.vx.quads[layer.0].tranbuf_touch = self.vx.swapconfig.image_count;
for (idx, quad) in self.vx.quads[layer.0].tranbuffer.iter_mut().enumerate() {
let delta = delta(idx);
for idx in 0..4 {
quad[idx * 2] = delta.0;
quad[idx * 2 + 1] = delta.1;
}
}
}
pub fn set_rotation_all<T: Copy + Into<Rad<f32>>>(
&mut self,
layer: &Layer,
mut delta: impl FnMut(usize) -> T,
) {
self.vx.quads[layer.0].rotbuf_touch = self.vx.swapconfig.image_count;
for (idx, quad) in self.vx.quads[layer.0].rotbuffer.iter_mut().enumerate() {
let delta = delta(idx).into().0;
quad.copy_from_slice(&[delta; 4]);
}
}
pub fn set_scale_all(&mut self, layer: &Layer, mut delta: impl FnMut(usize) -> f32) {
self.vx.quads[layer.0].scalebuf_touch = self.vx.swapconfig.image_count;
for (idx, quad) in self.vx.quads[layer.0].scalebuffer.iter_mut().enumerate() {
let delta = delta(idx);
quad.copy_from_slice(&[delta; 4]);
}
}
}
fn destroy_layer(s: &mut VxDraw, mut quad: QuadsData) {
unsafe {
for mut indices in quad.indices.drain(..) {
indices.destroy(&s.device);
}
for mut posbuf in quad.posbuf.drain(..) {
posbuf.destroy(&s.device);
}
for mut colbuf in quad.colbuf.drain(..) {
colbuf.destroy(&s.device);
}
for mut tranbuf in quad.tranbuf.drain(..) {
tranbuf.destroy(&s.device);
}
for mut rotbuf in quad.rotbuf.drain(..) {
rotbuf.destroy(&s.device);
}
for mut scalebuf in quad.scalebuf.drain(..) {
scalebuf.destroy(&s.device);
}
for dsl in quad.descriptor_set.drain(..) {
s.device.destroy_descriptor_set_layout(dsl);
}
s.device
.destroy_graphics_pipeline(ManuallyDrop::into_inner(read(&quad.pipeline)));
s.device
.destroy_pipeline_layout(ManuallyDrop::into_inner(read(&quad.pipeline_layout)));
s.device
.destroy_render_pass(ManuallyDrop::into_inner(read(&quad.render_pass)));
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::*;
use cgmath::Deg;
use fast_logger::{Generic, GenericLogger, Logger};
#[test]
fn simple_quad() {
let logger = Logger::<Generic>::spawn_void().to_compatibility();
let mut vx = VxDraw::new(logger, ShowWindow::Headless1k);
let mut quad = quads::Quad::new();
quad.colors[0].1 = 255;
quad.colors[3].1 = 255;
let layer = vx.quads().add_layer(&LayerOptions::new());
vx.quads().add(&layer, quad);
let img = vx.draw_frame_copy_framebuffer();
utils::assert_swapchain_eq(&mut vx, "simple_quad", img);
}
#[test]
fn simple_quad_hide() {
let logger = Logger::<Generic>::spawn_void().to_compatibility();
let mut vx = VxDraw::new(logger, ShowWindow::Headless1k);
let quad = quads::Quad::new();
let layer = vx.quads().add_layer(&LayerOptions::new());
vx.quads().add(&layer, quad);
vx.quads().hide(&layer);
let img = vx.draw_frame_copy_framebuffer();
utils::assert_swapchain_eq(&mut vx, "simple_quad_hide", img);
}
#[test]
fn simple_quad_translated() {
let logger = Logger::<Generic>::spawn_void().to_compatibility();
let mut vx = VxDraw::new(logger, ShowWindow::Headless1k);
let mut quad = quads::Quad::new();
quad.colors[0].1 = 255;
quad.colors[3].1 = 255;
let mut quads = vx.quads();
let layer = quads.add_layer(&LayerOptions::new());
let handle = quads.add(&layer, quad);
quads.translate(&handle, (0.25, 0.4));
let img = vx.draw_frame_copy_framebuffer();
utils::assert_swapchain_eq(&mut vx, "simple_quad_translated", img);
}
#[test]
fn swapping_quad_draw_order() {
let logger = Logger::<Generic>::spawn_void().to_compatibility();
let mut vx = VxDraw::new(logger, ShowWindow::Headless1k);
let quad = quads::Quad::new();
let layer = vx.quads().add_layer(&LayerOptions::new());
let mut q1 = vx.quads().add(&layer, quad);
let mut q2 = vx.quads().add(&layer, quad);
let mut quads = vx.quads();
quads.translate(&q1, (-0.5, -0.5));
quads.set_solid_color(&q1, Color::Rgba(255, 0, 255, 255));
quads.translate(&q2, (0.5, 0.5));
quads.set_solid_color(&q2, Color::Rgba(0, 255, 255, 128));
assert_eq![std::cmp::Ordering::Less, quads.compare_draw_order(&q1, &q2)];
quads.swap_draw_order(&mut q1, &mut q2);
assert_eq![
std::cmp::Ordering::Greater,
quads.compare_draw_order(&q1, &q2)
];
let img = vx.draw_frame_copy_framebuffer();
utils::assert_swapchain_eq(&mut vx, "swapping_quad_draw_order", img);
}
#[test]
fn swapping_quad_draw_order_different_layers() {
let logger = Logger::<Generic>::spawn_void().to_compatibility();
let mut vx = VxDraw::new(logger, ShowWindow::Headless1k);
let quad = quads::Quad::new();
let layer1 = vx.quads().add_layer(&LayerOptions::new());
let layer2 = vx.quads().add_layer(&LayerOptions::new());
let mut q1 = vx.quads().add(&layer1, quad);
let mut q2 = vx.quads().add(&layer2, quad);
let mut quads = vx.quads();
quads.translate(&q1, (-0.5, -0.5));
quads.set_solid_color(&q1, Color::Rgba(255, 0, 255, 255));
quads.translate(&q2, (0.5, 0.5));
quads.set_solid_color(&q2, Color::Rgba(0, 255, 255, 128));
assert_eq![std::cmp::Ordering::Less, quads.compare_draw_order(&q1, &q2)];
quads.swap_draw_order(&mut q1, &mut q2);
assert_eq![
std::cmp::Ordering::Greater,
quads.compare_draw_order(&q1, &q2)
];
let img = vx.draw_frame_copy_framebuffer();
utils::assert_swapchain_eq(&mut vx, "swapping_quad_draw_order_different_layers", img);
}
#[test]
fn three_quads_add_remove() {
let logger = Logger::<Generic>::spawn_void().to_compatibility();
let mut vx = VxDraw::new(logger, ShowWindow::Headless1k);
let mut quad = quads::Quad::new();
quad.colors[0].1 = 255;
quad.colors[3].1 = 255;
let mut quads = vx.quads();
let layer = quads.add_layer(&LayerOptions::new());
let _q1 = quads.add(&layer, quad);
let q2 = quads.add(&layer, quad);
let q3 = quads.add(&layer, quad);
quads.translate(&q2, (0.25, 0.4));
quads.set_solid_color(&q2, Color::Rgba(0, 0, 255, 128));
quads.translate(&q3, (0.35, 0.8));
quads.remove(q2);
let img = vx.draw_frame_copy_framebuffer();
utils::assert_swapchain_eq(&mut vx, "three_quads_add_remove", img);
}
#[test]
fn three_quads_add_remove_layer() {
let logger = Logger::<Generic>::spawn_void().to_compatibility();
let mut vx = VxDraw::new(logger, ShowWindow::Headless1k);
let mut quad = quads::Quad::new();
quad.colors[0].1 = 255;
quad.colors[3].1 = 255;
let mut quads = vx.quads();
let layer1 = quads.add_layer(&LayerOptions::new());
let _q1 = quads.add(&layer1, quad);
let layer2 = quads.add_layer(&LayerOptions::new());
let q2 = quads.add(&layer2, quad);
let layer3 = quads.add_layer(&LayerOptions::new());
let q3 = quads.add(&layer3, quad);
quads.translate(&q2, (0.25, 0.4));
quads.set_solid_color(&q2, Color::Rgba(0, 0, 255, 128));
quads.translate(&q3, (0.35, 0.8));
quads.remove_layer(layer2);
let img = vx.draw_frame_copy_framebuffer();
utils::assert_swapchain_eq(&mut vx, "three_quads_add_remove_layer", img);
}
#[test]
fn simple_quad_set_position() {
let logger = Logger::<Generic>::spawn_void().to_compatibility();
let mut vx = VxDraw::new(logger, ShowWindow::Headless1k);
let mut quad = quads::Quad::new();
quad.colors[0].1 = 255;
quad.colors[3].1 = 255;
let mut quads = vx.quads();
let layer = quads.add_layer(&LayerOptions::new());
let handle = quads.add(&layer, quad);
quads.set_translation(&handle, (0.25, 0.4));
let img = vx.draw_frame_copy_framebuffer();
utils::assert_swapchain_eq(&mut vx, "simple_quad_set_position", img);
}
#[test]
fn simple_quad_scale() {
let logger = Logger::<Generic>::spawn_void().to_compatibility();
let mut vx = VxDraw::new(logger, ShowWindow::Headless1k);
let mut quad = quads::Quad::new();
quad.colors[0].1 = 255;
quad.colors[2].2 = 255;
let mut quads = vx.quads();
let layer = quads.add_layer(&LayerOptions::new());
let handle = quads.add(&layer, quad);
quads.set_scale(&handle, 0.5);
let img = vx.draw_frame_copy_framebuffer();
utils::assert_swapchain_eq(&mut vx, "simple_quad_scale", img);
}
#[test]
fn simple_quad_deform() {
let logger = Logger::<Generic>::spawn_void().to_compatibility();
let mut vx = VxDraw::new(logger, ShowWindow::Headless1k);
let mut quad = quads::Quad::new();
quad.colors[0].1 = 255;
quad.colors[2].2 = 255;
let mut quads = vx.quads();
let layer = quads.add_layer(&LayerOptions::new());
let handle = quads.add(&layer, quad);
quads.scale(&handle, 0.5);
quads.deform(&handle, [(-0.5, 0.0), (0.0, 0.0), (0.0, 0.0), (0.5, 0.1)]);
let img = vx.draw_frame_copy_framebuffer();
utils::assert_swapchain_eq(&mut vx, "simple_quad_deform", img);
}
#[test]
fn set_color_all() {
let logger = Logger::<Generic>::spawn_void().to_compatibility();
let mut vx = VxDraw::new(logger, ShowWindow::Headless1k);
let quad = quads::Quad::new();
let mut quads = vx.quads();
let layer = quads.add_layer(&LayerOptions::new());
quads.add(&layer, quad.scale(0.5).translation((-0.5, 0.0)));
quads.add(&layer, quad.scale(0.5).translation((0.5, 0.0)));
quads.set_color_all(&layer, |idx| match idx {
0 => [
Color::Rgba(0, 0, 0, 255),
Color::Rgba(255, 0, 0, 255),
Color::Rgba(0, 255, 0, 255),
Color::Rgba(0, 0, 255, 255),
],
1 => [
Color::Rgba(0, 0, 0, 0),
Color::Rgba(255, 255, 0, 255),
Color::Rgba(255, 0, 255, 255),
Color::Rgba(0, 255, 255, 255),
],
_ => panic!["There should only be 2 quads"],
});
let img = vx.draw_frame_copy_framebuffer();
utils::assert_swapchain_eq(&mut vx, "set_color_all", img);
}
#[test]
fn simple_quad_set_position_after_initial() {
let logger = Logger::<Generic>::spawn_void().to_compatibility();
let mut vx = VxDraw::new(logger, ShowWindow::Headless1k);
let mut quad = quads::Quad::new();
quad.colors[0].1 = 255;
quad.colors[3].1 = 255;
let mut quads = vx.quads();
let layer = quads.add_layer(&LayerOptions::new());
let handle = quads.add(&layer, quad);
for _ in 0..3 {
vx.draw_frame();
}
vx.quads().set_translation(&handle, (0.25, 0.4));
let img = vx.draw_frame_copy_framebuffer();
utils::assert_swapchain_eq(&mut vx, "simple_quad_set_position_after_initial", img);
}
#[test]
fn simple_quad_rotated_with_exotic_origin() {
let logger = Logger::<Generic>::spawn_void().to_compatibility();
let mut vx = VxDraw::new(logger, ShowWindow::Headless1k);
let mut quad = quads::Quad::new();
quad.scale = 0.2;
quad.colors[0].0 = 255;
quad.colors[3].0 = 255;
let layer = vx.quads().add_layer(&LayerOptions::new());
vx.quads().add(&layer, quad);
let mut quad = quads::Quad::new();
quad.scale = 0.2;
quad.origin = (-1.0, -1.0);
quad.colors[0].1 = 255;
quad.colors[3].1 = 255;
let mut quads = vx.quads();
quads.add(&layer, quad);
quads.rotate_all(&layer, |_| Deg(30.0));
let img = vx.draw_frame_copy_framebuffer();
utils::assert_swapchain_eq(&mut vx, "simple_quad_rotated_with_exotic_origin", img);
}
#[test]
fn quad_layering() {
let logger = Logger::<Generic>::spawn_void().to_compatibility();
let mut vx = VxDraw::new(logger, ShowWindow::Headless1k);
let mut quad = quads::Quad {
scale: 0.5,
..quads::Quad::new()
};
for i in 0..4 {
quad.colors[i] = (0, 255, 0, 255);
}
quad.depth = 0.0;
quad.translation = (0.25, 0.25);
let layer1 = vx.quads().add_layer(&LayerOptions::new());
let layer2 = vx.quads().add_layer(&LayerOptions::new());
vx.quads().add(&layer2, quad);
quad.scale = 0.6;
for i in 0..4 {
quad.colors[i] = (0, 0, 255, 255);
}
vx.quads().add(&layer1, quad);
let img = vx.draw_frame_copy_framebuffer();
utils::assert_swapchain_eq(&mut vx, "quad_layering", img);
}
#[test]
fn quad_mass_manip() {
let logger = Logger::<Generic>::spawn_void().to_compatibility();
let mut vx = VxDraw::new(logger, ShowWindow::Headless1k);
let layer = vx.quads().add_layer(&LayerOptions::new());
use rand::Rng;
use rand_pcg::Pcg64Mcg as random;
let mut rng = random::new(0);
let quad = quads::Quad::new();
for _ in 0..1000 {
vx.quads().add(&layer, quad);
}
for _ in 0..vx.buffer_count() {
vx.draw_frame();
}
vx.quads().set_translation_all(&layer, |idx| {
if idx < 500 {
(
rng.gen_range(-1.0f32, 0.0f32),
rng.gen_range(-1.0f32, 1.0f32),
)
} else {
(
rng.gen_range(0.0f32, 1.0f32),
rng.gen_range(-1.0f32, 1.0f32),
)
}
});
vx.quads()
.set_scale_all(&layer, |idx| if idx < 500 { 0.01 } else { 0.02 });
vx.quads().set_solid_color_all(&layer, |idx| {
if idx < 250 {
Color::Rgba(0, 255, 255, 128)
} else if idx < 500 {
Color::Rgba(0, 255, 0, 128)
} else if idx < 750 {
Color::Rgba(0, 0, 255, 128)
} else {
Color::Rgba(255, 255, 255, 128)
}
});
vx.quads()
.set_rotation_all(&layer, |idx| if idx < 500 { Deg(0.0) } else { Deg(30.0) });
let img = vx.draw_frame_copy_framebuffer();
utils::assert_swapchain_eq(&mut vx, "quad_mass_manip", img);
}
#[test]
fn rapidly_add_remove_layer() {
let logger = Logger::<Generic>::spawn_void().to_compatibility();
let mut vx = VxDraw::new(logger, ShowWindow::Headless1k);
let options = &LayerOptions::new();
for _ in 0..10 {
let mut quads = vx.quads();
let layer = quads.add_layer(options);
quads.add(&layer, Quad::new());
vx.draw_frame();
vx.quads().remove_layer(layer);
assert![vx.swapconfig.image_count + 1 >= vx.quads().layer_count() as u32];
assert![0 < vx.quads().layer_count()];
}
}
}