use super::{utils::*, Color};
use crate::data::{DebugTriangleData, VxDraw};
use cgmath::Rad;
#[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, Adapter, Backend, Primitive};
use std::{io::Cursor, mem::ManuallyDrop};
#[derive(Debug)]
pub struct Handle(usize);
#[derive(Clone, Copy, Debug)]
pub struct DebugTriangle {
origin: [(f32, f32); 3],
colors_rgba: [(u8, u8, u8, u8); 3],
translation: (f32, f32),
rotation: f32,
scale: f32,
}
impl DebugTriangle {
pub fn new() -> Self {
Self::default()
}
pub fn vertices(mut self, vertices: [(f32, f32); 3]) -> Self {
self.origin = vertices;
self
}
pub fn colors(mut self, colors: [Color; 3]) -> Self {
for (idx, color) in colors.iter().enumerate() {
self.colors_rgba[idx] = color.into();
}
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 radius(&self) -> f32 {
(self.origin[0].0.powi(2) + self.origin[0].1.powi(2))
.sqrt()
.max(
(self.origin[1].0.powi(2) + self.origin[1].1.powi(2))
.sqrt()
.max((self.origin[2].0.powi(2) + self.origin[2].1.powi(2)).sqrt()),
)
* self.scale
}
}
impl From<[f32; 6]> for DebugTriangle {
fn from(array: [f32; 6]) -> Self {
let mut tri = Self::default();
tri.origin[0].0 = array[0];
tri.origin[0].1 = array[1];
tri.origin[1].0 = array[2];
tri.origin[1].1 = array[3];
tri.origin[2].0 = array[4];
tri.origin[2].1 = array[5];
tri
}
}
impl Default for DebugTriangle {
fn default() -> Self {
let origin = make_centered_equilateral_triangle();
DebugTriangle {
origin: [
(origin[0], origin[1]),
(origin[2], origin[3]),
(origin[4], origin[5]),
],
colors_rgba: [(255, 0, 0, 255), (0, 255, 0, 255), (0, 0, 255, 255)],
rotation: 0f32,
translation: (0f32, 0f32),
scale: 1f32,
}
}
}
pub struct Debtri<'a> {
vx: &'a mut VxDraw,
}
impl<'a> Debtri<'a> {
fn check_health(&self) {
let imgcnt = self.vx.swapconfig.image_count as usize;
debug_assert![self.vx.debtris.posbuf.len() == imgcnt];
debug_assert![self.vx.debtris.colbuf.len() == imgcnt];
debug_assert![self.vx.debtris.tranbuf.len() == imgcnt];
debug_assert![self.vx.debtris.rotbuf.len() == imgcnt];
debug_assert![self.vx.debtris.scalebuf.len() == imgcnt];
let imgcnt = self.vx.swapconfig.image_count;
debug_assert![self.vx.debtris.posbuf_touch <= imgcnt];
debug_assert![self.vx.debtris.colbuf_touch <= imgcnt];
debug_assert![self.vx.debtris.tranbuf_touch <= imgcnt];
debug_assert![self.vx.debtris.rotbuf_touch <= imgcnt];
debug_assert![self.vx.debtris.scalebuf_touch <= imgcnt];
}
pub fn new(vx: &'a mut VxDraw) -> Self {
let debtris = Self { vx };
debtris.check_health();
debtris
}
pub fn compare_draw_order(&self, left: &Handle, right: &Handle) -> std::cmp::Ordering {
left.0.cmp(&right.0)
}
pub fn swap_draw_order(&mut self, left: &mut Handle, right: &mut Handle) {
let debtris = &mut self.vx.debtris;
debtris.posbuffer.swap(left.0, right.0);
debtris.colbuffer.swap(left.0, right.0);
debtris.tranbuffer.swap(left.0, right.0);
debtris.rotbuffer.swap(left.0, right.0);
debtris.scalebuffer.swap(left.0, right.0);
debtris.posbuf_touch = self.vx.swapconfig.image_count;
debtris.colbuf_touch = self.vx.swapconfig.image_count;
debtris.tranbuf_touch = self.vx.swapconfig.image_count;
debtris.rotbuf_touch = self.vx.swapconfig.image_count;
debtris.scalebuf_touch = self.vx.swapconfig.image_count;
std::mem::swap(&mut left.0, &mut right.0);
}
pub fn show(&mut self) {
self.vx.debtris.hidden = false;
}
pub fn hide(&mut self) {
self.vx.debtris.hidden = true;
}
pub fn count(&mut self) -> usize {
self.vx.debtris.posbuffer.len()
}
pub fn add(&mut self, triangle: DebugTriangle) -> Handle {
let debtris = &mut self.vx.debtris;
let handle = if let Some(hole) = debtris.holes.pop() {
debtris.posbuffer[hole].copy_from_slice(&[
triangle.origin[0].0,
triangle.origin[0].1,
triangle.origin[1].0,
triangle.origin[1].1,
triangle.origin[2].0,
triangle.origin[2].1,
]);
debtris.colbuffer[hole].copy_from_slice(&[
triangle.colors_rgba[0].0,
triangle.colors_rgba[0].1,
triangle.colors_rgba[0].2,
triangle.colors_rgba[0].3,
triangle.colors_rgba[1].0,
triangle.colors_rgba[1].1,
triangle.colors_rgba[1].2,
triangle.colors_rgba[1].3,
triangle.colors_rgba[2].0,
triangle.colors_rgba[2].1,
triangle.colors_rgba[2].2,
triangle.colors_rgba[2].3,
]);
debtris.tranbuffer[hole].copy_from_slice(&[
triangle.translation.0,
triangle.translation.1,
triangle.translation.0,
triangle.translation.1,
triangle.translation.0,
triangle.translation.1,
]);
debtris.rotbuffer[hole].copy_from_slice(&[
triangle.rotation,
triangle.rotation,
triangle.rotation,
]);
debtris.scalebuffer[hole].copy_from_slice(&[
triangle.scale,
triangle.scale,
triangle.scale,
]);
Handle(hole)
} else {
debtris.posbuffer.push([
triangle.origin[0].0,
triangle.origin[0].1,
triangle.origin[1].0,
triangle.origin[1].1,
triangle.origin[2].0,
triangle.origin[2].1,
]);
debtris.colbuffer.push([
triangle.colors_rgba[0].0,
triangle.colors_rgba[0].1,
triangle.colors_rgba[0].2,
triangle.colors_rgba[0].3,
triangle.colors_rgba[1].0,
triangle.colors_rgba[1].1,
triangle.colors_rgba[1].2,
triangle.colors_rgba[1].3,
triangle.colors_rgba[2].0,
triangle.colors_rgba[2].1,
triangle.colors_rgba[2].2,
triangle.colors_rgba[2].3,
]);
debtris.tranbuffer.push([
triangle.translation.0,
triangle.translation.1,
triangle.translation.0,
triangle.translation.1,
triangle.translation.0,
triangle.translation.1,
]);
debtris
.rotbuffer
.push([triangle.rotation, triangle.rotation, triangle.rotation]);
debtris
.scalebuffer
.push([triangle.scale, triangle.scale, triangle.scale]);
Handle(debtris.posbuffer.len() - 1)
};
debtris.posbuf_touch = self.vx.swapconfig.image_count;
debtris.colbuf_touch = self.vx.swapconfig.image_count;
debtris.tranbuf_touch = self.vx.swapconfig.image_count;
debtris.rotbuf_touch = self.vx.swapconfig.image_count;
debtris.scalebuf_touch = self.vx.swapconfig.image_count;
handle
}
pub fn pop(&mut self) {
let debtris = &mut self.vx.debtris;
debtris.posbuffer.pop();
debtris.colbuffer.pop();
debtris.tranbuffer.pop();
debtris.rotbuffer.pop();
debtris.scalebuffer.pop();
}
pub fn pop_many(&mut self, n: usize) {
let end = self.vx.debtris.posbuffer.len();
let begin = end.checked_sub(n).unwrap_or(0);
let debtris = &mut self.vx.debtris;
debtris.posbuffer.drain(begin..end);
debtris.colbuffer.drain(begin..end);
debtris.tranbuffer.drain(begin..end);
debtris.rotbuffer.drain(begin..end);
debtris.scalebuffer.drain(begin..end);
}
pub fn remove(&mut self, handle: Handle) {
self.vx.debtris.holes.push(handle.0);
self.set_scale(&handle, 0.0);
}
pub fn set_deform(&mut self, handle: &Handle, points: [(f32, f32); 3]) {
self.vx.debtris.posbuf_touch = self.vx.swapconfig.image_count;
let vertex = &mut self.vx.debtris.posbuffer[handle.0];
vertex[0] = points[0].0;
vertex[1] = points[0].1;
vertex[2] = points[1].0;
vertex[3] = points[1].1;
vertex[4] = points[2].0;
vertex[5] = points[2].1;
}
pub fn set_color(&mut self, handle: &Handle, rgba: Color) {
self.vx.debtris.colbuf_touch = self.vx.swapconfig.image_count;
for vtx in 0..3 {
let Color::Rgba(r, g, b, a) = rgba;
self.vx.debtris.colbuffer[handle.0][vtx * 4] = r;
self.vx.debtris.colbuffer[handle.0][vtx * 4 + 1] = g;
self.vx.debtris.colbuffer[handle.0][vtx * 4 + 2] = b;
self.vx.debtris.colbuffer[handle.0][vtx * 4 + 3] = a;
}
}
pub fn set_translation(&mut self, handle: &Handle, pos: (f32, f32)) {
self.vx.debtris.tranbuf_touch = self.vx.swapconfig.image_count;
for vtx in 0..3 {
self.vx.debtris.tranbuffer[handle.0][vtx * 2] = pos.0;
self.vx.debtris.tranbuffer[handle.0][vtx * 2 + 1] = pos.1;
}
}
pub fn set_rotation<T: Copy + Into<Rad<f32>>>(&mut self, handle: &Handle, deg: T) {
let angle = deg.into().0;
self.vx.debtris.rotbuf_touch = self.vx.swapconfig.image_count;
self.vx.debtris.rotbuffer[handle.0].copy_from_slice(&[angle, angle, angle]);
}
pub fn set_scale(&mut self, handle: &Handle, scale: f32) {
self.vx.debtris.scalebuf_touch = self.vx.swapconfig.image_count;
for sc in self.vx.debtris.scalebuffer[handle.0].iter_mut() {
*sc = scale;
}
}
pub fn deform(&mut self, handle: &Handle, delta: [(f32, f32); 3]) {
self.vx.debtris.posbuf_touch = self.vx.swapconfig.image_count;
let trn = &mut self.vx.debtris.posbuffer[handle.0];
trn[0] += delta[0].0;
trn[1] += delta[0].1;
trn[2] += delta[1].0;
trn[3] += delta[1].1;
trn[4] += delta[2].0;
trn[5] += delta[2].1;
}
pub fn color(&mut self, handle: &Handle, color: [i16; 4]) {
self.vx.debtris.tranbuf_touch = self.vx.swapconfig.image_count;
for cols in self.vx.debtris.colbuffer[handle.0].chunks_exact_mut(4) {
for (idx, color) in color.iter().enumerate() {
let excol = i16::from(cols[idx]);
cols[idx] = (excol + *color).min(255).max(0) as u8;
}
}
}
pub fn translate(&mut self, handle: &Handle, delta: (f32, f32)) {
self.vx.debtris.tranbuf_touch = self.vx.swapconfig.image_count;
for stride in 0..3 {
self.vx.debtris.tranbuffer[handle.0][stride * 2] += delta.0;
self.vx.debtris.tranbuffer[handle.0][stride * 2 + 1] += delta.1;
}
}
pub fn rotate<T: Copy + Into<Rad<f32>>>(&mut self, handle: &Handle, deg: T) {
self.vx.debtris.rotbuf_touch = self.vx.swapconfig.image_count;
for rot in &mut self.vx.debtris.rotbuffer[handle.0] {
*rot += deg.into().0;
}
}
pub fn scale(&mut self, handle: &Handle, scale: f32) {
self.vx.debtris.scalebuf_touch = self.vx.swapconfig.image_count;
for sc in self.vx.debtris.scalebuffer[handle.0].iter_mut() {
*sc *= scale;
}
}
pub fn deform_all(&mut self, mut delta: impl FnMut(usize) -> [(f32, f32); 3]) {
self.vx.debtris.posbuf_touch = self.vx.swapconfig.image_count;
for (idx, trn) in self.vx.debtris.posbuffer.iter_mut().enumerate() {
let delta = delta(idx);
trn[0] += delta[0].0;
trn[1] += delta[0].1;
trn[2] += delta[1].0;
trn[3] += delta[1].1;
trn[4] += delta[2].0;
trn[5] += delta[2].1;
}
}
pub fn color_all(&mut self, mut delta: impl FnMut(usize) -> [i16; 4]) {
self.vx.debtris.colbuf_touch = self.vx.swapconfig.image_count;
for (idx, cols) in self.vx.debtris.colbuffer.iter_mut().enumerate() {
let delta = delta(idx);
for (idx, color) in delta.iter().enumerate() {
for idy in 0..3 {
let excol = i16::from(cols[idx + idy * 4]);
cols[idx + idy * 4] = (excol + *color).min(255).max(0) as u8;
}
}
}
}
pub fn translate_all(&mut self, mut delta: impl FnMut(usize) -> (f32, f32)) {
self.vx.debtris.tranbuf_touch = self.vx.swapconfig.image_count;
for (idx, trns) in self.vx.debtris.tranbuffer.iter_mut().enumerate() {
let delta = delta(idx);
for trn in trns.chunks_exact_mut(2) {
trn[0] += delta.0;
trn[1] += delta.1;
}
}
}
pub fn rotate_all<T: Copy + Into<Rad<f32>>>(&mut self, mut delta: impl FnMut(usize) -> T) {
self.vx.debtris.rotbuf_touch = self.vx.swapconfig.image_count;
for (idx, rots) in &mut self.vx.debtris.rotbuffer.iter_mut().enumerate() {
let delta = delta(idx).into().0;
for rot in rots.iter_mut() {
*rot += delta;
}
}
}
pub fn scale_all(&mut self, mut delta: impl FnMut(usize) -> f32) {
self.vx.debtris.scalebuf_touch = self.vx.swapconfig.image_count;
for (idx, scs) in &mut self.vx.debtris.scalebuffer.iter_mut().enumerate() {
let delta = delta(idx);
for sc in scs.iter_mut() {
*sc *= delta;
}
}
}
pub fn set_deform_all(&mut self, mut delta: impl FnMut(usize) -> [(f32, f32); 3]) {
self.vx.debtris.posbuf_touch = self.vx.swapconfig.image_count;
for (idx, trn) in self.vx.debtris.posbuffer.iter_mut().enumerate() {
let delta = delta(idx);
trn[0] = delta[0].0;
trn[1] = delta[0].1;
trn[2] = delta[1].0;
trn[3] = delta[1].1;
trn[4] = delta[2].0;
trn[5] = delta[2].1;
}
}
pub fn set_color_all(&mut self, mut delta: impl FnMut(usize) -> Color) {
self.vx.debtris.colbuf_touch = self.vx.swapconfig.image_count;
for (idx, cols) in self.vx.debtris.colbuffer.iter_mut().enumerate() {
let Color::Rgba(r, g, b, a) = delta(idx);
for col in cols.chunks_exact_mut(4) {
col.copy_from_slice(&[r, g, b, a]);
}
}
}
pub fn set_translation_all(&mut self, mut delta: impl FnMut(usize) -> (f32, f32)) {
self.vx.debtris.tranbuf_touch = self.vx.swapconfig.image_count;
for (idx, trns) in self.vx.debtris.tranbuffer.iter_mut().enumerate() {
let delta = delta(idx);
trns.copy_from_slice(&[delta.0, delta.1, delta.0, delta.1, delta.0, delta.1]);
}
}
pub fn set_rotation_all<T: Copy + Into<Rad<f32>>>(
&mut self,
mut delta: impl FnMut(usize) -> T,
) {
self.vx.debtris.rotbuf_touch = self.vx.swapconfig.image_count;
for (idx, rots) in &mut self.vx.debtris.rotbuffer.iter_mut().enumerate() {
let delta = delta(idx).into().0;
for rot in rots.iter_mut() {
*rot = delta;
}
}
}
pub fn set_scale_all(&mut self, mut delta: impl FnMut(usize) -> f32) {
self.vx.debtris.scalebuf_touch = self.vx.swapconfig.image_count;
for (idx, scs) in &mut self.vx.debtris.scalebuffer.iter_mut().enumerate() {
let delta = delta(idx);
for sc in scs.iter_mut() {
*sc = delta;
}
}
}
}
pub(crate) fn create_debug_triangle(
device: &back::Device,
adapter: &Adapter<back::Backend>,
format: format::Format,
image_count: usize,
) -> DebugTriangleData {
pub const VERTEX_SOURCE: &[u8] = include_bytes!["../target/spirv/debtri.vert.spirv"];
pub const FRAGMENT_SOURCE: &[u8] = include_bytes!["../target/spirv/debtri.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 { device.create_shader_module(&vertex_source) }.unwrap() };
let fs_module = { unsafe { 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 {
binding: 0,
stride: 8,
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::CounterClockwise,
depth_bias: None,
conservative: false,
};
let depth_stencil = pso::DepthStencilDesc {
depth: None,
depth_bounds: false,
stencil: None,
};
let blender = {
let blend_state = pso::BlendState {
color: pso::BlendOp::Add {
src: pso::Factor::One,
dst: pso::Factor::Zero,
},
alpha: pso::BlendOp::Add {
src: pso::Factor::One,
dst: pso::Factor::Zero,
},
};
pso::BlendDesc {
logic_op: Some(pso::LogicOp::Copy),
targets: vec![pso::ColorBlendDesc {
mask: pso::ColorMask::ALL,
blend: Some(blend_state),
}],
}
};
let triangle_render_pass = {
let attachment = pass::Attachment {
format: Some(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 { 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 triangle_descriptor_set_layouts: Vec<<back::Backend as Backend>::DescriptorSetLayout> =
vec![unsafe {
device
.create_descriptor_set_layout(bindings, immutable_samplers)
.expect("Couldn't make a DescriptorSetLayout")
}];
let mut push_constants = Vec::<(pso::ShaderStageFlags, core::ops::Range<u32>)>::new();
push_constants.push((pso::ShaderStageFlags::VERTEX, 0..1));
let triangle_pipeline_layout = unsafe {
device
.create_pipeline_layout(&triangle_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: &triangle_pipeline_layout,
subpass: pass::Subpass {
index: 0,
main_pass: &triangle_render_pass,
},
flags: pso::PipelineCreationFlags::empty(),
parent: pso::BasePipeline::None,
};
let triangle_pipeline = unsafe {
device
.create_graphics_pipeline(&pipeline_desc, None)
.expect("Couldn't create a graphics pipeline!")
};
unsafe {
device.destroy_shader_module(vs_module);
device.destroy_shader_module(fs_module);
}
let posbuf = (0..image_count)
.map(|_| super::utils::ResizBuf::new(&device, &adapter))
.collect::<Vec<_>>();
let colbuf = (0..image_count)
.map(|_| super::utils::ResizBuf::new(&device, &adapter))
.collect::<Vec<_>>();
let tranbuf = (0..image_count)
.map(|_| super::utils::ResizBuf::new(&device, &adapter))
.collect::<Vec<_>>();
let rotbuf = (0..image_count)
.map(|_| super::utils::ResizBuf::new(&device, &adapter))
.collect::<Vec<_>>();
let scalebuf = (0..image_count)
.map(|_| super::utils::ResizBuf::new(&device, &adapter))
.collect::<Vec<_>>();
DebugTriangleData {
hidden: false,
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,
descriptor_set: triangle_descriptor_set_layouts,
pipeline: ManuallyDrop::new(triangle_pipeline),
pipeline_layout: ManuallyDrop::new(triangle_pipeline_layout),
render_pass: ManuallyDrop::new(triangle_render_pass),
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::*;
use cgmath::Deg;
use fast_logger::{Generic, GenericLogger, Logger};
use test::{black_box, Bencher};
#[test]
fn simple_triangle() {
let logger = Logger::<Generic>::spawn_void().to_compatibility();
let mut vx = VxDraw::new(logger, ShowWindow::Headless1k);
let tri = DebugTriangle::default();
vx.debtri().add(tri);
utils::add_4_screencorners(&mut vx);
let img = vx.draw_frame_copy_framebuffer();
utils::assert_swapchain_eq(&mut vx, "simple_triangle", img);
}
#[test]
fn simple_triangle_pop() {
let logger = Logger::<Generic>::spawn_void().to_compatibility();
let mut vx = VxDraw::new(logger, ShowWindow::Headless1k);
let tri = DebugTriangle::default();
vx.debtri().add(tri);
utils::add_4_screencorners(&mut vx);
for _ in 0..4 {
vx.debtri().pop();
}
let img = vx.draw_frame_copy_framebuffer();
utils::assert_swapchain_eq(&mut vx, "simple_triangle_middle", img);
}
#[test]
fn simple_triangle_color() {
let logger = Logger::<Generic>::spawn_void().to_compatibility();
let mut vx = VxDraw::new(logger, ShowWindow::Headless1k);
let tri = DebugTriangle::default();
let triangle = vx.debtri().add(tri);
vx.debtri().color(&triangle, [-255, 0, 0, 0]);
let img = vx.draw_frame_copy_framebuffer();
utils::assert_swapchain_eq(&mut vx, "simple_triangle_color", img);
vx.debtri().color_all(|_| [0, 0, -128, 0]);
let img = vx.draw_frame_copy_framebuffer();
utils::assert_swapchain_eq(&mut vx, "simple_triangle_color_opacity", img);
}
#[test]
fn test_single_triangle_api() {
let logger = Logger::<Generic>::spawn_void().to_compatibility();
let mut vx = VxDraw::new(logger, ShowWindow::Headless1k);
let tri = DebugTriangle::default();
let mut debtri = vx.debtri();
let handle = debtri.add(tri);
debtri.set_scale(&handle, 0.1);
debtri.scale(&handle, 1.0);
debtri.set_rotation(&handle, Deg(25.0));
debtri.set_translation(&handle, (0.05, 0.4));
debtri.translate(&handle, (0.2, 0.1));
debtri.rotate(&handle, Deg(5.0));
let img = vx.draw_frame_copy_framebuffer();
utils::assert_swapchain_eq(&mut vx, "test_single_triangle_api", img);
}
#[test]
fn remove_middle_triangle() {
let logger = Logger::<Generic>::spawn_void().to_compatibility();
let mut vx = VxDraw::new(logger, ShowWindow::Headless1k);
let tri = DebugTriangle::default();
let mut debtri = vx.debtri();
let left = debtri.add(tri);
debtri.set_translation(&left, (-0.25, 0.0));
debtri.set_color(&left, Color::Rgba(255, 0, 0, 255));
let middle = debtri.add(tri);
debtri.set_color(&middle, Color::Rgba(0, 255, 0, 255));
let right = debtri.add(tri);
debtri.set_translation(&right, (0.25, 0.0));
debtri.set_color(&right, Color::Rgba(0, 0, 255, 255));
debtri.remove(middle);
let img = vx.draw_frame_copy_framebuffer();
utils::assert_swapchain_eq(&mut vx, "remove_middle_triangle", img);
}
#[test]
fn fill_remove_hole() {
let logger = Logger::<Generic>::spawn_void().to_compatibility();
let mut vx = VxDraw::new(logger, ShowWindow::Headless1k);
let tri = DebugTriangle::default();
let mut debtri = vx.debtri();
let left = debtri.add(tri);
debtri.set_translation(&left, (-0.25, 0.0));
debtri.set_color(&left, Color::Rgba(255, 0, 0, 255));
let middle = debtri.add(tri);
debtri.set_color(&middle, Color::Rgba(0, 255, 0, 255));
let right = debtri.add(tri);
debtri.set_translation(&right, (0.25, 0.0));
debtri.set_color(&right, Color::Rgba(0, 0, 255, 255));
debtri.remove(middle);
let middle = debtri.add(tri);
debtri.set_rotation(&middle, Deg(60.0));
let img = vx.draw_frame_copy_framebuffer();
utils::assert_swapchain_eq(&mut vx, "fill_remove_hole", img);
}
#[test]
fn removing_iterates_minus_one() {
let logger = Logger::<Generic>::spawn_void().to_compatibility();
let mut vx = VxDraw::new(logger, ShowWindow::Headless1k);
let tri = DebugTriangle::default();
let mut debtri = vx.debtri();
let mut count = 0;
debtri.translate_all(|_| {
count += 1;
(0.0, 0.0)
});
assert_eq![0, count];
assert_eq![0, debtri.count()];
debtri.add(tri);
let middle = debtri.add(tri);
debtri.add(tri);
let mut count = 0;
debtri.rotate_all(|_| {
count += 1;
Deg(0.0)
});
assert_eq![3, count];
assert_eq![3, debtri.count()];
debtri.remove(middle);
let mut count = 0;
debtri.scale_all(|_| {
count += 1;
1.0
});
assert_eq![3, count];
assert_eq![3, debtri.count()];
}
#[test]
fn swap_triangles() {
let logger = Logger::<Generic>::spawn_void().to_compatibility();
let mut vx = VxDraw::new(logger, ShowWindow::Headless1k);
let tri = DebugTriangle::default();
let mut debtri = vx.debtri();
let mut left = debtri.add(tri);
debtri.set_translation(&left, (-0.25, 0.0));
debtri.set_color(&left, Color::Rgba(255, 0, 0, 255));
let mut right = debtri.add(tri);
debtri.set_translation(&right, (0.25, 0.0));
debtri.set_color(&right, Color::Rgba(0, 0, 255, 255));
assert_eq![
std::cmp::Ordering::Less,
debtri.compare_draw_order(&left, &right)
];
debtri.swap_draw_order(&mut left, &mut right);
assert_eq![
std::cmp::Ordering::Greater,
debtri.compare_draw_order(&left, &right)
];
let img = vx.draw_frame_copy_framebuffer();
utils::assert_swapchain_eq(&mut vx, "swap_triangles", img);
}
#[test]
fn deform_triangles() {
let logger = Logger::<Generic>::spawn_void().to_compatibility();
let mut vx = VxDraw::new(logger, ShowWindow::Headless1k);
let tri = DebugTriangle::default();
let mut debtri = vx.debtri();
let left = debtri.add(tri);
debtri.set_translation(&left, (-0.25, 0.0));
debtri.set_color(&left, Color::Rgba(255, 0, 0, 255));
let right = debtri.add(tri);
debtri.set_translation(&right, (0.25, 0.0));
debtri.set_color(&right, Color::Rgba(0, 0, 255, 255));
debtri.set_deform(&right, [(0.0, 0.0), (1.0, 0.0), (1.0, 1.0)]);
debtri.deform(&left, [(0.0, 0.0), (1.0, 0.0), (0.0, 1.0)]);
debtri.deform_all(|_| [(-1.0, 0.0), (0.0, 0.0), (0.0, 0.0)]);
let img = vx.draw_frame_copy_framebuffer();
utils::assert_swapchain_eq(&mut vx, "deform_triangles", img);
}
#[test]
fn simple_triangle_change_color() {
let logger = Logger::<Generic>::spawn_void().to_compatibility();
let mut vx = VxDraw::new(logger, ShowWindow::Headless1k);
let tri = DebugTriangle::default();
let mut debtri = vx.debtri();
let idx = debtri.add(tri);
debtri.set_color(&idx, Color::Rgba(255, 0, 255, 255));
let img = vx.draw_frame_copy_framebuffer();
utils::assert_swapchain_eq(&mut vx, "simple_triangle_change_color", img);
}
#[test]
fn debug_triangle_corners_widescreen() {
let logger = Logger::<Generic>::spawn_void().to_compatibility();
let mut vx = VxDraw::new(logger, ShowWindow::Headless2x1k);
for i in [-1f32, 1f32].iter() {
for j in [-1f32, 1f32].iter() {
let mut tri = DebugTriangle::default();
tri.translation = (*i, *j);
let _idx = vx.debtri().add(tri);
}
}
let img = vx.draw_frame_copy_framebuffer();
utils::assert_swapchain_eq(&mut vx, "debug_triangle_corners_widescreen", img);
}
#[test]
fn debug_triangle_corners_tallscreen() {
let logger = Logger::<Generic>::spawn_void().to_compatibility();
let mut vx = VxDraw::new(logger, ShowWindow::Headless1x2k);
for i in [-1f32, 1f32].iter() {
for j in [-1f32, 1f32].iter() {
let mut tri = DebugTriangle::default();
tri.translation = (*i, *j);
let _idx = vx.debtri().add(tri);
}
}
let img = vx.draw_frame_copy_framebuffer();
utils::assert_swapchain_eq(&mut vx, "debug_triangle_corners_tallscreen", img);
}
#[test]
fn circle_of_triangles() {
let logger = Logger::<Generic>::spawn_void().to_compatibility();
let mut vx = VxDraw::new(logger, ShowWindow::Headless2x1k);
for i in 0..360 {
let mut tri = DebugTriangle::default();
tri.translation = ((i as f32).cos(), (i as f32).sin());
tri.scale = 0.1f32;
let _idx = vx.debtri().add(tri);
}
let img = vx.draw_frame_copy_framebuffer();
utils::assert_swapchain_eq(&mut vx, "circle_of_triangles", img);
}
#[test]
fn triangle_in_corner() {
let logger = Logger::<Generic>::spawn_void().to_compatibility();
let mut vx = VxDraw::new(logger, ShowWindow::Headless1k);
let mut tri = DebugTriangle::default();
tri.scale = 0.1f32;
let radi = tri.radius();
let trans = -1f32 + radi;
for j in 0..31 {
for i in 0..31 {
tri.translation = (trans + i as f32 * 2.0 * radi, trans + j as f32 * 2.0 * radi);
vx.debtri().add(tri);
}
}
let img = vx.draw_frame_copy_framebuffer();
utils::assert_swapchain_eq(&mut vx, "triangle_in_corner", img);
}
#[test]
fn a_bunch_of_quads() {
let logger = Logger::<Generic>::spawn_void().to_compatibility();
let mut vx = VxDraw::new(logger, ShowWindow::Headless1k);
let mut topright = debtri::DebugTriangle::from([-1.0, -1.0, 1.0, 1.0, 1.0, -1.0]);
let mut bottomleft = debtri::DebugTriangle::from([-1.0, -1.0, -1.0, 1.0, 1.0, 1.0]);
topright.scale = 0.1;
bottomleft.scale = 0.1;
let radi = 0.1;
let trans = -1f32 + radi;
for j in 0..10 {
for i in 0..10 {
topright.translation =
(trans + i as f32 * 2.0 * radi, trans + j as f32 * 2.0 * radi);
bottomleft.translation =
(trans + i as f32 * 2.0 * radi, trans + j as f32 * 2.0 * radi);
vx.debtri().add(topright);
vx.debtri().add(bottomleft);
}
}
let img = vx.draw_frame_copy_framebuffer();
utils::assert_swapchain_eq(&mut vx, "a_bunch_of_quads", img);
}
#[test]
fn windmills() {
let logger = Logger::<Generic>::spawn_void().to_compatibility();
let mut vx = VxDraw::new(logger, ShowWindow::Headless1k);
utils::add_windmills(&mut vx, false);
let img = vx.draw_frame_copy_framebuffer();
utils::assert_swapchain_eq(&mut vx, "windmills", img);
}
#[test]
fn windmills_mass_edits() {
let logger = Logger::<Generic>::spawn_void().to_compatibility();
let mut vx = VxDraw::new(logger, ShowWindow::Headless1k);
utils::add_windmills(&mut vx, false);
let mut debtri = vx.debtri();
debtri.translate_all(|_| (1.0, 0.5));
debtri.rotate_all(|_| Deg(90.0));
debtri.scale_all(|_| 2.0);
let img = vx.draw_frame_copy_framebuffer();
utils::assert_swapchain_eq(&mut vx, "windmills_mass_edits", img);
}
#[test]
fn windmills_hidden() {
let logger = Logger::<Generic>::spawn_void().to_compatibility();
let mut vx = VxDraw::new(logger, ShowWindow::Headless1k);
utils::add_windmills(&mut vx, false);
vx.debtri().hide();
let img = vx.draw_frame_copy_framebuffer();
utils::assert_swapchain_eq(&mut vx, "windmills_hidden", img);
vx.debtri().show();
let img = vx.draw_frame_copy_framebuffer();
utils::assert_swapchain_eq(&mut vx, "windmills_hidden_now_shown", img);
}
#[test]
fn windmills_ignore_perspective() {
let logger = Logger::<Generic>::spawn_void().to_compatibility();
let mut vx = VxDraw::new(logger, ShowWindow::Headless2x1k);
utils::add_windmills(&mut vx, false);
let img = vx.draw_frame_copy_framebuffer();
utils::assert_swapchain_eq(&mut vx, "windmills_ignore_perspective", img);
}
#[test]
fn windmills_change_color() {
let logger = Logger::<Generic>::spawn_void().to_compatibility();
let mut vx = VxDraw::new(logger, ShowWindow::Headless1k);
let handles = utils::add_windmills(&mut vx, false);
let mut debtri = vx.debtri();
debtri.set_color(&handles[0], Color::Rgba(255, 0, 0, 255));
debtri.set_color(&handles[249], Color::Rgba(0, 255, 0, 255));
debtri.set_color(&handles[499], Color::Rgba(0, 0, 255, 255));
debtri.set_color(&handles[999], Color::Rgba(0, 0, 0, 255));
let img = vx.draw_frame_copy_framebuffer();
utils::assert_swapchain_eq(&mut vx, "windmills_change_color", img);
}
#[test]
fn rotating_windmills_drawing_invariant() {
let logger = Logger::<Generic>::spawn_void().to_compatibility();
let mut vx = VxDraw::new(logger, ShowWindow::Headless1k);
utils::add_windmills(&mut vx, false);
for _ in 0..30 {
vx.debtri().rotate_all(|_| Deg(-1.0f32));
}
let img = vx.draw_frame_copy_framebuffer();
utils::assert_swapchain_eq(&mut vx, "rotating_windmills_drawing_invariant", img);
utils::remove_windmills(&mut vx);
utils::add_windmills(&mut vx, false);
for _ in 0..30 {
vx.debtri().rotate_all(|_| Deg(-1.0f32));
vx.draw_frame();
}
let img = vx.draw_frame_copy_framebuffer();
utils::assert_swapchain_eq(&mut vx, "rotating_windmills_drawing_invariant", img);
}
#[test]
fn windmills_given_initial_rotation() {
let logger = Logger::<Generic>::spawn_void().to_compatibility();
let mut vx = VxDraw::new(logger, ShowWindow::Headless1k);
utils::add_windmills(&mut vx, true);
let img = vx.draw_frame_copy_framebuffer();
utils::assert_swapchain_eq(&mut vx, "windmills_given_initial_rotation", img);
}
#[bench]
fn bench_simple_triangle(b: &mut Bencher) {
let logger = Logger::<Generic>::spawn_void().to_compatibility();
let mut vx = VxDraw::new(logger, ShowWindow::Headless1k);
vx.debtri().add(DebugTriangle::default());
utils::add_4_screencorners(&mut vx);
b.iter(|| {
vx.draw_frame();
});
}
#[bench]
fn bench_still_windmills(b: &mut Bencher) {
let logger = Logger::<Generic>::spawn_void().to_compatibility();
let mut vx = VxDraw::new(logger, ShowWindow::Headless1k);
utils::add_windmills(&mut vx, false);
b.iter(|| {
vx.draw_frame();
});
}
#[bench]
fn bench_windmills_set_color(b: &mut Bencher) {
let logger = Logger::<Generic>::spawn_void().to_compatibility();
let mut vx = VxDraw::new(logger, ShowWindow::Headless1k);
let handles = utils::add_windmills(&mut vx, false);
b.iter(|| {
vx.debtri()
.set_color(&handles[0], black_box(Color::Rgba(0, 0, 0, 255)));
});
}
#[bench]
fn bench_rotating_windmills_only(b: &mut Bencher) {
let logger = Logger::<Generic>::spawn_void().to_compatibility();
let mut vx = VxDraw::new(logger, ShowWindow::Headless1k);
utils::add_windmills(&mut vx, false);
b.iter(|| {
vx.debtri().rotate_all(|_| Deg(1.0f32));
vx.draw_frame();
});
}
#[bench]
fn bench_rotating_windmills_set_color(b: &mut Bencher) {
let logger = Logger::<Generic>::spawn_void().to_compatibility();
let mut vx = VxDraw::new(logger, ShowWindow::Headless1k);
let last = utils::add_windmills(&mut vx, false).pop().unwrap();
b.iter(|| {
vx.debtri().rotate_all(|_| Deg(1.0f32));
vx.debtri().set_color(&last, Color::Rgba(255, 0, 255, 255));
vx.draw_frame();
});
}
#[bench]
fn bench_rotating_windmills_no_render(b: &mut Bencher) {
let logger = Logger::<Generic>::spawn_void().to_compatibility();
let mut vx = VxDraw::new(logger, ShowWindow::Headless1k);
utils::add_windmills(&mut vx, false);
b.iter(|| {
vx.debtri().rotate_all(|_| Deg(1.0f32));
});
}
}