use crate::data::VxDraw;
use fast_logger::error;
#[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::{
adapter::{MemoryTypeId, PhysicalDevice},
command,
device::Device,
format, image, memory,
memory::Properties,
pso, Adapter, Backend,
};
use std::f32::consts::PI;
use std::iter::once;
use std::mem::ManuallyDrop;
pub trait Layerable {
fn get_layer(&self, vx: &VxDraw) -> usize;
}
pub(crate) fn find_memory_type_id<B: gfx_hal::Backend>(
adap: &Adapter<B>,
reqs: memory::Requirements,
prop: memory::Properties,
) -> MemoryTypeId {
adap.physical_device
.memory_properties()
.memory_types
.iter()
.enumerate()
.find(|&(id, memory_type)| {
reqs.type_mask & (1 << id) != 0 && memory_type.properties.contains(prop)
})
.map(|(id, _)| MemoryTypeId(id))
.expect("Unable to find memory type id")
}
pub(crate) fn make_vertex_buffer_with_data(
s: &mut VxDraw,
data: &[f32],
) -> (
<back::Backend as Backend>::Buffer,
<back::Backend as Backend>::Memory,
memory::Requirements,
) {
let device = &s.device;
let (buffer, memory, requirements) = unsafe {
let buffer_size: u64 = (std::mem::size_of::<f32>() * data.len()) as u64;
let mut buffer = device
.create_buffer(buffer_size, gfx_hal::buffer::Usage::VERTEX)
.expect("cant make bf");
let requirements = device.get_buffer_requirements(&buffer);
let memory_type_id = find_memory_type_id(
&s.adapter,
requirements,
Properties::CPU_VISIBLE | Properties::COHERENT,
);
let memory = device
.allocate_memory(memory_type_id, requirements.size)
.expect("Couldn't allocate vertex buffer memory");
device
.bind_buffer_memory(&memory, 0, &mut buffer)
.expect("Couldn't bind the buffer memory!");
(buffer, memory, requirements)
};
unsafe {
let mut data_target = device
.acquire_mapping_writer(&memory, 0..requirements.size)
.expect("Failed to acquire a memory writer!");
data_target[..data.len()].copy_from_slice(data);
device
.release_mapping_writer(data_target)
.expect("Couldn't release the mapping writer!");
}
(buffer, memory, requirements)
}
#[derive(Debug)]
pub(crate) struct ResizBufIdx4 {
buffer: ManuallyDrop<<back::Backend as Backend>::Buffer>,
memory: ManuallyDrop<<back::Backend as Backend>::Memory>,
capacity: usize,
}
impl ResizBufIdx4 {
pub(crate) fn new(device: &back::Device, adapter: &Adapter<back::Backend>) -> Self {
Self::with_capacity(device, adapter, 1)
}
pub(crate) fn with_capacity(
device: &back::Device,
adapter: &Adapter<back::Backend>,
capacity: usize,
) -> Self {
let (buffer, memory, requirements) = unsafe {
let buffer_size: u64 = (capacity * 6 * std::mem::size_of::<u32>()) as u64;
let mut buffer = device
.create_buffer(buffer_size, gfx_hal::buffer::Usage::INDEX)
.expect("cant make bf");
let requirements = device.get_buffer_requirements(&buffer);
let memory_type_id = find_memory_type_id(
adapter,
requirements,
Properties::COHERENT | Properties::CPU_VISIBLE | Properties::DEVICE_LOCAL,
);
let memory = device
.allocate_memory(memory_type_id, requirements.size)
.expect("Couldn't allocate vertex buffer memory");
device
.bind_buffer_memory(&memory, 0, &mut buffer)
.expect("Couldn't bind the buffer memory!");
(buffer, memory, requirements)
};
unsafe {
let mut data_target = device
.acquire_mapping_writer(&memory, 0..requirements.size)
.expect("Failed to acquire a memory writer!");
for index in 0..capacity {
let ver = (index * 6) as u32;
let ind = (index * 4) as u32;
data_target[ver as usize..(ver + 6) as usize].copy_from_slice(&[
ind,
ind + 1,
ind + 2,
ind + 2,
ind + 3,
ind,
]);
}
device
.release_mapping_writer(data_target)
.expect("Couldn't release the mapping writer!");
}
Self {
buffer: ManuallyDrop::new(buffer),
memory: ManuallyDrop::new(memory),
capacity,
}
}
pub(crate) fn buffer(&self) -> &<back::Backend as Backend>::Buffer {
&self.buffer
}
fn resize(&mut self, device: &back::Device, adapter: &Adapter<back::Backend>, capacity: usize) {
let mut new_resizbuf = Self::with_capacity(device, adapter, capacity);
std::mem::swap(&mut self.buffer, &mut new_resizbuf.buffer);
std::mem::swap(&mut self.memory, &mut new_resizbuf.memory);
std::mem::swap(&mut self.capacity, &mut new_resizbuf.capacity);
new_resizbuf.destroy(device);
}
pub(crate) fn ensure_capacity(
&mut self,
device: &back::Device,
adapter: &Adapter<back::Backend>,
capacity: usize,
) {
static SHRINK_TRESHOLD: usize = 2;
let capacity = capacity.max(1);
if self.capacity >= capacity * SHRINK_TRESHOLD {
self.resize(device, adapter, (self.capacity / 2).max(capacity));
} else if self.capacity >= capacity {
} else {
self.resize(device, adapter, (self.capacity * 2).max(capacity));
}
}
pub(crate) fn destroy(&mut self, device: &back::Device) {
use core::ptr::read;
unsafe {
device.destroy_buffer(ManuallyDrop::into_inner(read(&self.buffer)));
device.free_memory(ManuallyDrop::into_inner(read(&self.memory)));
}
}
}
#[derive(Debug)]
pub(crate) struct ResizBuf {
buffer: ManuallyDrop<<back::Backend as Backend>::Buffer>,
memory: ManuallyDrop<<back::Backend as Backend>::Memory>,
requirements: memory::Requirements,
capacity_in_bytes: usize,
}
impl ResizBuf {
pub(crate) fn new(device: &back::Device, adapter: &Adapter<back::Backend>) -> Self {
Self::with_capacity(device, adapter, 1)
}
pub(crate) fn with_capacity(
device: &back::Device,
adapter: &Adapter<back::Backend>,
capacity_in_bytes: usize,
) -> Self {
let (buffer, memory, requirements) = unsafe {
let buffer_size: u64 = capacity_in_bytes as u64;
let mut buffer = device
.create_buffer(buffer_size, gfx_hal::buffer::Usage::VERTEX)
.expect("cant make bf");
let requirements = device.get_buffer_requirements(&buffer);
let memory_type_id = find_memory_type_id(
adapter,
requirements,
Properties::COHERENT | Properties::CPU_VISIBLE | Properties::DEVICE_LOCAL,
);
let memory = device
.allocate_memory(memory_type_id, requirements.size)
.expect("Couldn't allocate vertex buffer memory");
device
.bind_buffer_memory(&memory, 0, &mut buffer)
.expect("Couldn't bind the buffer memory!");
(buffer, memory, requirements)
};
Self {
buffer: ManuallyDrop::new(buffer),
memory: ManuallyDrop::new(memory),
requirements,
capacity_in_bytes,
}
}
pub(crate) fn buffer(&self) -> &<back::Backend as Backend>::Buffer {
&self.buffer
}
fn resize(
&mut self,
device: &back::Device,
adapter: &Adapter<back::Backend>,
capacity_in_bytes: usize,
) {
let mut new_resizbuf = Self::with_capacity(device, adapter, capacity_in_bytes);
std::mem::swap(self, &mut new_resizbuf);
new_resizbuf.destroy(device);
}
pub(crate) fn copy_from_slice_and_maybe_resize<T: Copy>(
&mut self,
device: &back::Device,
adapter: &Adapter<back::Backend>,
slice: &[T],
) {
static SHRINK_TRESHOLD: u64 = 2;
let bytes_in_slice = (slice.len() * std::mem::size_of::<T>()).max(1) as u64;
if self.capacity_in_bytes as u64 >= bytes_in_slice * SHRINK_TRESHOLD {
self.resize(
device,
adapter,
(self.capacity_in_bytes as usize / 2).max(bytes_in_slice as usize),
);
self.copy_from_slice_and_maybe_resize(device, adapter, slice);
} else if self.capacity_in_bytes as u64 >= bytes_in_slice {
unsafe {
let mut data_target = device
.acquire_mapping_writer(&self.memory, 0..self.requirements.size)
.expect("Failed to acquire a memory writer!");
data_target[..slice.len()].copy_from_slice(slice);
device
.release_mapping_writer(data_target)
.expect("Couldn't release the mapping writer!");
}
} else {
self.resize(
device,
adapter,
(self.capacity_in_bytes as usize * 2).max(bytes_in_slice as usize),
);
self.copy_from_slice_and_maybe_resize(device, adapter, slice);
}
}
pub(crate) fn destroy(&mut self, device: &back::Device) {
use core::ptr::read;
unsafe {
device.destroy_buffer(ManuallyDrop::into_inner(read(&self.buffer)));
device.free_memory(ManuallyDrop::into_inner(read(&self.memory)));
}
}
}
pub(crate) fn make_transfer_buffer_of_size(
s: &mut VxDraw,
size: u64,
) -> (
<back::Backend as Backend>::Buffer,
<back::Backend as Backend>::Memory,
memory::Requirements,
) {
let device = &s.device;
let (buffer, memory, requirements) = unsafe {
let mut buffer = device
.create_buffer(size, gfx_hal::buffer::Usage::TRANSFER_DST)
.expect("cant make bf");
let requirements = device.get_buffer_requirements(&buffer);
let memory_type_id = find_memory_type_id(
&s.adapter,
requirements,
Properties::CPU_VISIBLE | Properties::COHERENT,
);
let memory = device
.allocate_memory(memory_type_id, requirements.size)
.expect("Couldn't allocate vertex buffer memory");
device
.bind_buffer_memory(&memory, 0, &mut buffer)
.expect("Couldn't bind the buffer memory!");
(buffer, memory, requirements)
};
(buffer, memory, requirements)
}
pub(crate) fn make_transfer_img_of_size(
s: &mut VxDraw,
w: u32,
h: u32,
) -> (
<back::Backend as Backend>::Image,
<back::Backend as Backend>::Memory,
memory::Requirements,
) {
let device = &s.device;
let (buffer, memory, requirements) = unsafe {
if s.adapter
.physical_device
.image_format_properties(
format::Format::Rgba8Unorm,
2,
image::Tiling::Linear,
image::Usage::TRANSFER_SRC | image::Usage::TRANSFER_DST,
image::ViewCapabilities::empty(),
)
.is_none()
{
const MSG: &str = "Device does not support VK_FORMAT_R8G8B8A8_UNORM transfer image";
error![s.log, "{}", MSG];
panic![MSG];
}
if !s
.adapter
.physical_device
.format_properties(Some(format::Format::Rgba8Unorm))
.linear_tiling
.contains(format::ImageFeature::BLIT_DST)
{
const MSG: &str =
"Device does not support VK_FORMAT_R8G8B8A8_UNORM as blit destination";
error![s.log, "{}", MSG];
panic![MSG];
}
let mut buffer = device
.create_image(
image::Kind::D2(w, h, 1, 1),
1,
format::Format::Rgba8Unorm,
image::Tiling::Linear,
image::Usage::TRANSFER_DST | image::Usage::TRANSFER_SRC,
image::ViewCapabilities::empty(),
)
.expect("cant make bf");
let requirements = device.get_image_requirements(&buffer);
let memory_type_id = find_memory_type_id(
&s.adapter,
requirements,
Properties::CPU_VISIBLE | Properties::COHERENT,
);
let memory = device
.allocate_memory(memory_type_id, requirements.size)
.expect("Couldn't allocate image buffer memory");
device
.bind_image_memory(&memory, 0, &mut buffer)
.expect("Couldn't bind the buffer memory!");
(buffer, memory, requirements)
};
(buffer, memory, requirements)
}
#[cfg(test)]
pub(crate) fn make_vertex_buffer_with_data_on_gpu(
s: &mut VxDraw,
data: &[f32],
) -> (
<back::Backend as Backend>::Buffer,
<back::Backend as Backend>::Memory,
memory::Requirements,
) {
let device = &s.device;
let (buffer, memory, requirements) = unsafe {
let buffer_size: u64 = (std::mem::size_of::<f32>() * data.len()) as u64;
let mut buffer = device
.create_buffer(buffer_size, gfx_hal::buffer::Usage::TRANSFER_SRC)
.expect("cant make bf");
let requirements = device.get_buffer_requirements(&buffer);
let memory_type_id = find_memory_type_id(
&s.adapter,
requirements,
Properties::CPU_VISIBLE | Properties::COHERENT,
);
let memory = device
.allocate_memory(memory_type_id, requirements.size)
.expect("Couldn't allocate vertex buffer memory");
device
.bind_buffer_memory(&memory, 0, &mut buffer)
.expect("Couldn't bind the buffer memory!");
(buffer, memory, requirements)
};
unsafe {
let mut data_target = device
.acquire_mapping_writer(&memory, 0..requirements.size)
.expect("Failed to acquire a memory writer!");
data_target[..data.len()].copy_from_slice(data);
device
.release_mapping_writer(data_target)
.expect("Couldn't release the mapping writer!");
}
let (buffer_gpu, memory_gpu, memory_gpu_requirements) = unsafe {
let buffer_size: u64 = (std::mem::size_of::<f32>() * data.len()) as u64;
let mut buffer = device
.create_buffer(
buffer_size,
gfx_hal::buffer::Usage::TRANSFER_DST | gfx_hal::buffer::Usage::VERTEX,
)
.expect("cant make bf");
let requirements = device.get_buffer_requirements(&buffer);
let memory_type_id =
find_memory_type_id(&s.adapter, requirements, Properties::DEVICE_LOCAL);
let memory = device
.allocate_memory(memory_type_id, requirements.size)
.expect("Couldn't allocate vertex buffer memory");
device
.bind_buffer_memory(&memory, 0, &mut buffer)
.expect("Couldn't bind the buffer memory!");
(buffer, memory, requirements)
};
let buffer_size: u64 = (std::mem::size_of::<f32>() * data.len()) as u64;
let mut cmd_buffer = s
.command_pool
.acquire_command_buffer::<gfx_hal::command::OneShot>();
unsafe {
cmd_buffer.begin();
let buffer_barrier = gfx_hal::memory::Barrier::Buffer {
families: None,
range: None..None,
states: gfx_hal::buffer::Access::empty()..gfx_hal::buffer::Access::TRANSFER_WRITE,
target: &buffer_gpu,
};
cmd_buffer.pipeline_barrier(
pso::PipelineStage::TOP_OF_PIPE..pso::PipelineStage::TRANSFER,
gfx_hal::memory::Dependencies::empty(),
&[buffer_barrier],
);
let copy = once(command::BufferCopy {
src: 0,
dst: 0,
size: buffer_size,
});
cmd_buffer.copy_buffer(&buffer, &buffer_gpu, copy);
let buffer_barrier = gfx_hal::memory::Barrier::Buffer {
families: None,
range: None..None,
states: gfx_hal::buffer::Access::TRANSFER_WRITE..gfx_hal::buffer::Access::SHADER_READ,
target: &buffer_gpu,
};
cmd_buffer.pipeline_barrier(
pso::PipelineStage::TRANSFER..pso::PipelineStage::FRAGMENT_SHADER,
gfx_hal::memory::Dependencies::empty(),
&[buffer_barrier],
);
cmd_buffer.finish();
let upload_fence = device
.create_fence(false)
.expect("Couldn't create an upload fence!");
s.queue_group.queues[0].submit_without_semaphores(Some(&cmd_buffer), Some(&upload_fence));
device
.wait_for_fence(&upload_fence, core::u64::MAX)
.expect("Couldn't wait for the fence!");
device.destroy_fence(upload_fence);
device.destroy_buffer(buffer);
device.free_memory(memory);
}
(buffer_gpu, memory_gpu, memory_gpu_requirements)
}
pub(crate) fn make_centered_equilateral_triangle() -> [f32; 6] {
let mut tri = [0.0f32; 6];
tri[2] = 1.0f32 * (60.0f32 / 180.0f32 * PI).cos();
tri[3] = -1.0f32 * (60.0f32 / 180.0f32 * PI).sin();
tri[4] = 1.0f32;
let avg_x = (tri[0] + tri[2] + tri[4]) / 3.0f32;
let avg_y = (tri[1] + tri[3] + tri[5]) / 3.0f32;
tri[0] -= avg_x;
tri[1] -= avg_y;
tri[2] -= avg_x;
tri[3] -= avg_y;
tri[4] -= avg_x;
tri[5] -= avg_y;
tri
}
pub(crate) fn copy_image_to_rgb(
s: &mut VxDraw,
image_index: gfx_hal::window::SwapImageIndex,
) -> Vec<u8> {
let width = s.swapconfig.extent.width;
let height = s.swapconfig.extent.height;
let (buffer, memory, requirements) =
make_transfer_buffer_of_size(s, u64::from(width * height * 4));
let (imgbuf, imgmem, _imgreq) = make_transfer_img_of_size(s, width, height);
let images = &s.images;
unsafe {
s.device
.wait_for_fence(
&s.frames_in_flight_fences[s.current_frame],
u64::max_value(),
)
.expect("Unable to wait for fence");
}
unsafe {
let mut cmd_buffer = s
.command_pool
.acquire_command_buffer::<gfx_hal::command::OneShot>();
cmd_buffer.begin();
let image_barrier = gfx_hal::memory::Barrier::Image {
states: (gfx_hal::image::Access::empty(), image::Layout::Present)
..(
gfx_hal::image::Access::TRANSFER_READ,
image::Layout::TransferSrcOptimal,
),
target: &images[image_index as usize],
families: None,
range: image::SubresourceRange {
aspects: format::Aspects::COLOR,
levels: 0..1,
layers: 0..1,
},
};
let dstbarrier = gfx_hal::memory::Barrier::Image {
states: (gfx_hal::image::Access::empty(), image::Layout::Undefined)
..(
gfx_hal::image::Access::TRANSFER_WRITE,
image::Layout::General,
),
target: &imgbuf,
families: None,
range: image::SubresourceRange {
aspects: format::Aspects::COLOR,
levels: 0..1,
layers: 0..1,
},
};
cmd_buffer.pipeline_barrier(
pso::PipelineStage::TOP_OF_PIPE..pso::PipelineStage::TRANSFER,
gfx_hal::memory::Dependencies::empty(),
&[image_barrier, dstbarrier],
);
cmd_buffer.blit_image(
&images[image_index as usize],
image::Layout::TransferSrcOptimal,
&imgbuf,
image::Layout::General,
image::Filter::Nearest,
once(command::ImageBlit {
src_subresource: image::SubresourceLayers {
aspects: format::Aspects::COLOR,
level: 0,
layers: 0..1,
},
src_bounds: image::Offset { x: 0, y: 0, z: 0 }..image::Offset {
x: width as i32,
y: height as i32,
z: 1,
},
dst_subresource: image::SubresourceLayers {
aspects: format::Aspects::COLOR,
level: 0,
layers: 0..1,
},
dst_bounds: image::Offset { x: 0, y: 0, z: 0 }..image::Offset {
x: width as i32,
y: height as i32,
z: 1,
},
}),
);
let image_barrier = gfx_hal::memory::Barrier::Image {
states: (
gfx_hal::image::Access::TRANSFER_READ,
image::Layout::TransferSrcOptimal,
)..(gfx_hal::image::Access::empty(), image::Layout::Present),
target: &images[image_index as usize],
families: None,
range: image::SubresourceRange {
aspects: format::Aspects::COLOR,
levels: 0..1,
layers: 0..1,
},
};
cmd_buffer.pipeline_barrier(
pso::PipelineStage::TRANSFER..pso::PipelineStage::BOTTOM_OF_PIPE,
gfx_hal::memory::Dependencies::empty(),
&[image_barrier],
);
cmd_buffer.finish();
let the_command_queue = &mut s.queue_group.queues[0];
let fence = s
.device
.create_fence(false)
.expect("Unable to create fence");
the_command_queue.submit_without_semaphores(once(&cmd_buffer), Some(&fence));
s.device
.wait_for_fence(&fence, u64::max_value())
.expect("unable to wait for fence");
s.device.destroy_fence(fence);
}
unsafe {
let mut cmd_buffer = s
.command_pool
.acquire_command_buffer::<gfx_hal::command::OneShot>();
cmd_buffer.begin();
let image_barrier = gfx_hal::memory::Barrier::Image {
states: (gfx_hal::image::Access::empty(), image::Layout::Undefined)
..(
gfx_hal::image::Access::TRANSFER_READ,
image::Layout::TransferSrcOptimal,
),
target: &imgbuf,
families: None,
range: image::SubresourceRange {
aspects: format::Aspects::COLOR,
levels: 0..1,
layers: 0..1,
},
};
cmd_buffer.pipeline_barrier(
pso::PipelineStage::TOP_OF_PIPE..pso::PipelineStage::TRANSFER,
gfx_hal::memory::Dependencies::empty(),
&[image_barrier],
);
cmd_buffer.copy_image_to_buffer(
&imgbuf,
image::Layout::TransferSrcOptimal,
&buffer,
once(command::BufferImageCopy {
buffer_offset: 0,
buffer_width: width,
buffer_height: height,
image_layers: image::SubresourceLayers {
aspects: format::Aspects::COLOR,
level: 0,
layers: 0..1,
},
image_offset: image::Offset { x: 0, y: 0, z: 0 },
image_extent: image::Extent {
width,
height,
depth: 1,
},
}),
);
cmd_buffer.finish();
let the_command_queue = &mut s.queue_group.queues[0];
let fence = s
.device
.create_fence(false)
.expect("Unable to create fence");
the_command_queue.submit_without_semaphores(once(&cmd_buffer), Some(&fence));
s.device
.wait_for_fence(&fence, u64::max_value())
.expect("unable to wait for fence");
s.device.destroy_fence(fence);
s.command_pool.free(once(cmd_buffer));
}
unsafe {
let reader = s
.device
.acquire_mapping_reader::<u8>(&memory, 0..requirements.size as u64)
.expect("Unable to open reader");
assert![u64::from(4 * width * height) <= requirements.size];
let result = reader
.iter()
.take((4 * width * height) as usize)
.cloned()
.collect::<Vec<_>>();
s.device.release_mapping_reader(reader);
s.device.destroy_buffer(buffer);
s.device.free_memory(memory);
s.device.destroy_image(imgbuf);
s.device.free_memory(imgmem);
result
}
}
#[cfg(test)]
pub(crate) fn assert_swapchain_eq(vx: &mut VxDraw, name: &str, rgb: Vec<u8>) {
use ::image as load_image;
use load_image::ImageDecoder;
let rgb = {
let mut tmp = vec![];
for rgba in rgb.chunks_exact(4) {
tmp.extend(&rgba[0..3]);
}
tmp
};
std::fs::create_dir_all("target/vxdraw_results").expect("Unable to create directories");
let genname = String::from("target/vxdraw_results/") + name + ".png";
let correctname = String::from("tests/vxdraw/") + name + ".png";
let diffname = String::from("target/vxdraw_results/") + name + "#diff.png";
let appendname = String::from("target/vxdraw_results/") + name + "#sum.png";
let store_generated_image = || {
let file = std::fs::File::create(&genname).expect("Unable to create file");
let enc = load_image::png::PNGEncoder::new(file);
enc.encode(
&rgb,
vx.swapconfig.extent.width,
vx.swapconfig.extent.height,
load_image::ColorType::RGB(8),
)
.expect("Unable to encode PNG file");
};
let correct = match std::fs::File::open(&correctname) {
Ok(x) => x,
Err(err) => {
store_generated_image();
std::process::Command::new("feh")
.args(&[genname])
.output()
.expect("Failed to execute process");
panic!["Unable to open reference file: {}", err]
}
};
let dec = load_image::png::PNGDecoder::new(correct)
.expect("Unable to read PNG file (does it exist?)");
if (
u64::from(vx.swapconfig.extent.width),
u64::from(vx.swapconfig.extent.height),
) != dec.dimensions()
{
store_generated_image();
assert_eq![
(
u64::from(vx.swapconfig.extent.width),
u64::from(vx.swapconfig.extent.height),
),
dec.dimensions(),
"The swapchain image and the preset correct image MUST be of the exact same size"
];
}
assert_eq![
load_image::ColorType::RGB(8),
dec.colortype(),
"Both images MUST have the RGB(8) format"
];
let correct_bytes = dec.read_image().expect("Unable to read image");
fn absdiff(lhs: u8, rhs: u8) -> u8 {
if let Some(newbyte) = lhs.checked_sub(rhs) {
newbyte
} else {
rhs - lhs
}
}
if correct_bytes != rgb {
#[cfg(not(feature = "exact"))]
{
let mut sum_diff: u64 = 0;
for (idx, byte) in correct_bytes.iter().enumerate() {
sum_diff += absdiff(*byte, rgb[idx]) as u64;
}
let width = vx.swapconfig.extent.width as f64;
let height = vx.swapconfig.extent.height as f64;
let diff_coeff = sum_diff as f64 / 255.0 / width / height / 3.0;
const DIFF_COEFF: f64 = 0.003;
dbg![diff_coeff];
if diff_coeff > DIFF_COEFF {
} else {
dbg!["WARNING: Images were NOT exact, but difference is below treshold"];
return;
}
}
store_generated_image();
let mut diff = Vec::with_capacity(correct_bytes.len());
for (idx, byte) in correct_bytes.iter().enumerate() {
diff.push(absdiff(*byte, rgb[idx]));
}
let file = std::fs::File::create(&diffname).expect("Unable to create file");
let enc = load_image::png::PNGEncoder::new(file);
enc.encode(
&diff,
vx.swapconfig.extent.width,
vx.swapconfig.extent.height,
load_image::ColorType::RGB(8),
)
.expect("Unable to encode PNG file");
std::process::Command::new("convert")
.args(&[
"-bordercolor".into(),
"black".into(),
"-border".into(),
"20".into(),
correctname,
genname,
diffname,
"+append".into(),
appendname.clone(),
])
.output()
.expect("Failed to execute process");
#[cfg(not(feature = "no-test-preview"))]
std::process::Command::new("feh")
.args(&[appendname])
.output()
.expect("Failed to execute process");
panic!["Images were NOT the same!"];
}
}
#[cfg(test)]
pub(crate) fn add_windmills(vx: &mut VxDraw, rand_rotat: bool) -> Vec<super::debtri::Handle> {
use cgmath::Rad;
use rand::Rng;
use rand_pcg::Pcg64Mcg as random;
let mut rng = random::new(0);
let mut debtris = Vec::with_capacity(1000);
for _ in 0..1000 {
let tri = super::debtri::DebugTriangle::new();
let (dx, dy) = (
rng.gen_range(-1.0f32, 1.0f32),
rng.gen_range(-1.0f32, 1.0f32),
);
let scale = rng.gen_range(0.03f32, 0.1f32);
let tri = if rand_rotat {
tri.rotation(Rad(rng.gen_range(-PI, PI)))
} else {
tri
};
let tri = tri.scale(scale).translation((dx, dy));
debtris.push(vx.debtri().add(tri));
}
debtris
}
#[cfg(test)]
pub(crate) fn remove_windmills(vx: &mut VxDraw) {
vx.debtri().pop_many(1000);
}
#[cfg(test)]
pub(crate) fn add_4_screencorners(vx: &mut VxDraw) {
vx.debtri().add(super::debtri::DebugTriangle::from([
-1.0f32, -1.0, 0.0, -1.0, -1.0, 0.0,
]));
vx.debtri().add(super::debtri::DebugTriangle::from([
-1.0f32, 1.0, 0.0, 1.0, -1.0, 0.0,
]));
vx.debtri().add(super::debtri::DebugTriangle::from([
1.0f32, -1.0, 0.0, -1.0, 1.0, 0.0,
]));
vx.debtri().add(super::debtri::DebugTriangle::from([
1.0f32, 1.0, 0.0, 1.0, 1.0, 0.0,
]));
}