extract resources into renderer_resources
This commit is contained in:
parent
69039ba3c1
commit
a173cf18ff
|
|
@ -12,7 +12,7 @@ members = [
|
||||||
"crates/window",
|
"crates/window",
|
||||||
"crates/game",
|
"crates/game",
|
||||||
"crates/text",
|
"crates/text",
|
||||||
"crates/bevy_vulkan_render", "crates/renderer_device",
|
"crates/bevy_vulkan_render", "crates/renderer_device", "crates/renderer_resources",
|
||||||
]
|
]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
|
@ -57,6 +57,7 @@ ahash = "0.8"
|
||||||
md-5 = "0.11.0"
|
md-5 = "0.11.0"
|
||||||
|
|
||||||
parking_lot = "0.12.3"
|
parking_lot = "0.12.3"
|
||||||
|
bytemuck = { version = "1.21.0", features = ["derive"] }
|
||||||
|
|
||||||
# tokio = "1.42"
|
# tokio = "1.42"
|
||||||
# futures = "0.3"
|
# futures = "0.3"
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ edition = "2024"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
renderer_device = { path = "../renderer_device" }
|
renderer_device = { path = "../renderer_device" }
|
||||||
|
renderer_resources = { path = "../renderer_resources" }
|
||||||
renderer_macros = { path = "renderer_macros" }
|
renderer_macros = { path = "renderer_macros" }
|
||||||
arrayvec = { workspace = true }
|
arrayvec = { workspace = true }
|
||||||
indexmap = { workspace = true }
|
indexmap = { workspace = true }
|
||||||
|
|
|
||||||
|
|
@ -11,18 +11,17 @@ use raw_window_handle::{RawDisplayHandle, RawWindowHandle};
|
||||||
|
|
||||||
pub use ash;
|
pub use ash;
|
||||||
|
|
||||||
mod buffers;
|
|
||||||
pub mod commands;
|
pub mod commands;
|
||||||
#[path = "egui.rs"]
|
#[path = "egui.rs"]
|
||||||
mod egui_pass;
|
mod egui_pass;
|
||||||
mod images;
|
|
||||||
mod pipeline;
|
|
||||||
pub mod render_graph;
|
pub mod render_graph;
|
||||||
pub mod rendering;
|
pub mod rendering;
|
||||||
pub mod swapchain;
|
pub mod swapchain;
|
||||||
mod text;
|
mod text;
|
||||||
pub mod util;
|
pub mod util;
|
||||||
|
|
||||||
|
pub(crate) use renderer_resources::{buffers, images, pipeline};
|
||||||
|
|
||||||
pub use renderer_device::{device, instance, queue, sync};
|
pub use renderer_device::{device, instance, queue, sync};
|
||||||
pub use renderer_device::{
|
pub use renderer_device::{
|
||||||
compatible_extension_properties, extension_intersection,
|
compatible_extension_properties, extension_intersection,
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,6 @@ use std::sync::Arc;
|
||||||
use ash::vk;
|
use ash::vk;
|
||||||
use glam::{
|
use glam::{
|
||||||
camera::rh::{proj, view},
|
camera::rh::{proj, view},
|
||||||
f32::Mat4,
|
|
||||||
vec3,
|
vec3,
|
||||||
};
|
};
|
||||||
use gpu_allocator::MemoryLocation;
|
use gpu_allocator::MemoryLocation;
|
||||||
|
|
@ -165,11 +164,7 @@ impl Wireframe {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
let future = cmd.submit_async(
|
let future = cmd.submit_async(None, None, Arc::new(sync::Fence::from_pool(&dev, None)?))?;
|
||||||
None,
|
|
||||||
None,
|
|
||||||
Arc::new(sync::Fence::from_pool(&dev, None)?),
|
|
||||||
)?;
|
|
||||||
|
|
||||||
let pipeline = Self::create_pipeline(dev.clone())?;
|
let pipeline = Self::create_pipeline(dev.clone())?;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -562,7 +562,17 @@ impl Swapchain {
|
||||||
|
|
||||||
let idx = idx as usize;
|
let idx = idx as usize;
|
||||||
let image = self.images[idx];
|
let image = self.images[idx];
|
||||||
let image = Arc::new(images::Image::from_swapchain_image(image, &self));
|
let image = Arc::new(images::Image::from_swapchain_raw(
|
||||||
|
image,
|
||||||
|
self.swapchain.device().clone(),
|
||||||
|
self.config.format,
|
||||||
|
vk::Extent3D {
|
||||||
|
width: self.config.extent.width,
|
||||||
|
height: self.config.extent.height,
|
||||||
|
depth: 1,
|
||||||
|
},
|
||||||
|
self.config.usage,
|
||||||
|
));
|
||||||
let view = image.create_view(ImageViewDesc {
|
let view = image.create_view(ImageViewDesc {
|
||||||
name: Some(format!("swapchain-{:x}-image-view-{idx}", self.raw().as_raw()).into()),
|
name: Some(format!("swapchain-{:x}-image-view-{idx}", self.raw().as_raw()).into()),
|
||||||
kind: vk::ImageViewType::TYPE_2D,
|
kind: vk::ImageViewType::TYPE_2D,
|
||||||
|
|
|
||||||
|
|
@ -6,75 +6,7 @@ use std::{
|
||||||
use ash::vk;
|
use ash::vk;
|
||||||
use bytemuck::{Pod, Zeroable};
|
use bytemuck::{Pod, Zeroable};
|
||||||
|
|
||||||
pub(crate) mod weak_vec {
|
pub use renderer_resources::util::{F32, eq_f32, hash_f32, ord_f32};
|
||||||
#![allow(dead_code)]
|
|
||||||
//! Module containing the [`WeakVec`] API.
|
|
||||||
|
|
||||||
use std::{sync::Weak, vec::Vec};
|
|
||||||
|
|
||||||
/// An optimized container for `Weak` references of `T` that minimizes reallocations by
|
|
||||||
/// dropping older elements that no longer have strong references to them.
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub(crate) struct WeakVec<T> {
|
|
||||||
inner: Vec<Weak<T>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> Default for WeakVec<T> {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self {
|
|
||||||
inner: Default::default(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> WeakVec<T> {
|
|
||||||
pub(crate) fn new() -> Self {
|
|
||||||
Self { inner: Vec::new() }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Pushes a new element to this collection.
|
|
||||||
///
|
|
||||||
/// If the inner Vec needs to be reallocated, we will first drop older elements that
|
|
||||||
/// no longer have strong references to them.
|
|
||||||
pub(crate) fn push(&mut self, value: Weak<T>) {
|
|
||||||
if self.inner.len() == self.inner.capacity() {
|
|
||||||
// Iterating backwards has the advantage that we don't do more work than we have to.
|
|
||||||
for i in (0..self.inner.len()).rev() {
|
|
||||||
if self.inner[i].strong_count() == 0 {
|
|
||||||
self.inner.swap_remove(i);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Make sure our capacity is twice the number of live elements.
|
|
||||||
// Leaving some spare capacity ensures that we won't re-scan immediately.
|
|
||||||
self.inner.reserve_exact(self.inner.len());
|
|
||||||
}
|
|
||||||
|
|
||||||
self.inner.push(value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) struct WeakVecIter<T> {
|
|
||||||
inner: std::vec::IntoIter<Weak<T>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> Iterator for WeakVecIter<T> {
|
|
||||||
type Item = Weak<T>;
|
|
||||||
fn next(&mut self) -> Option<Self::Item> {
|
|
||||||
self.inner.next()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> IntoIterator for WeakVec<T> {
|
|
||||||
type Item = Weak<T>;
|
|
||||||
type IntoIter = WeakVecIter<T>;
|
|
||||||
fn into_iter(self) -> Self::IntoIter {
|
|
||||||
WeakVecIter {
|
|
||||||
inner: self.inner.into_iter(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(derive_more::Debug)]
|
#[derive(derive_more::Debug)]
|
||||||
pub(crate) struct DropGuard(#[debug(skip)] ManuallyDrop<Box<dyn FnOnce()>>);
|
pub(crate) struct DropGuard(#[debug(skip)] ManuallyDrop<Box<dyn FnOnce()>>);
|
||||||
|
|
@ -419,104 +351,6 @@ impl Rect2D {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn eq_f32(lhs: f32, rhs: f32) -> bool {
|
|
||||||
use core::num::FpCategory::*;
|
|
||||||
match (lhs.classify(), rhs.classify()) {
|
|
||||||
(Zero, Zero) | (Nan, Nan) => true,
|
|
||||||
(Infinite, Infinite) => lhs.signum() == rhs.signum(),
|
|
||||||
_ => lhs == rhs,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub fn ord_f32(lhs: f32, rhs: f32) -> core::cmp::Ordering {
|
|
||||||
use core::cmp::Ordering;
|
|
||||||
use core::num::FpCategory::*;
|
|
||||||
match (lhs.classify(), rhs.classify()) {
|
|
||||||
(Zero, Zero) | (Nan, Nan) => Ordering::Equal,
|
|
||||||
(Infinite, Infinite) => {
|
|
||||||
core::cmp::PartialOrd::partial_cmp(&lhs.signum(), &rhs.signum()).unwrap()
|
|
||||||
}
|
|
||||||
_ => core::cmp::PartialOrd::partial_cmp(&lhs, &rhs).unwrap(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn hash_f32<H: std::hash::Hasher>(state: &mut H, f: f32) {
|
|
||||||
use std::hash::Hash;
|
|
||||||
let classify = f.classify();
|
|
||||||
match classify {
|
|
||||||
std::num::FpCategory::Nan => (classify as u8).hash(state),
|
|
||||||
std::num::FpCategory::Infinite | std::num::FpCategory::Zero => {
|
|
||||||
(classify as u8, f.signum() as i8).hash(state)
|
|
||||||
}
|
|
||||||
std::num::FpCategory::Subnormal | std::num::FpCategory::Normal => f.to_bits().hash(state),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[repr(transparent)]
|
|
||||||
#[derive(Debug, Clone, Copy)]
|
|
||||||
pub struct F32(f32);
|
|
||||||
|
|
||||||
impl F32 {
|
|
||||||
#[inline]
|
|
||||||
pub fn from_bits(bits: u32) -> Self {
|
|
||||||
Self(f32::from_bits(bits))
|
|
||||||
}
|
|
||||||
#[inline]
|
|
||||||
pub fn into_bits(self) -> u32 {
|
|
||||||
self.0.to_bits()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<f32> for F32 {
|
|
||||||
fn from(value: f32) -> Self {
|
|
||||||
Self(value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl From<F32> for f32 {
|
|
||||||
fn from(value: F32) -> Self {
|
|
||||||
value.0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Deref for F32 {
|
|
||||||
type Target = f32;
|
|
||||||
|
|
||||||
fn deref(&self) -> &Self::Target {
|
|
||||||
&self.0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl DerefMut for F32 {
|
|
||||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
|
||||||
&mut self.0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PartialEq for F32 {
|
|
||||||
fn eq(&self, other: &Self) -> bool {
|
|
||||||
eq_f32(self.0, other.0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Eq for F32 {}
|
|
||||||
|
|
||||||
impl PartialOrd for F32 {
|
|
||||||
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
|
|
||||||
Some(ord_f32(self.0, other.0))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Ord for F32 {
|
|
||||||
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
|
|
||||||
ord_f32(self.0, other.0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl core::hash::Hash for F32 {
|
|
||||||
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
|
|
||||||
hash_f32(state, self.0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn timed<T, F: FnOnce() -> T>(label: &str, f: F) -> T {
|
pub fn timed<T, F: FnOnce() -> T>(label: &str, f: F) -> T {
|
||||||
let now = std::time::Instant::now();
|
let now = std::time::Instant::now();
|
||||||
let out = f();
|
let out = f();
|
||||||
|
|
|
||||||
13
crates/renderer_resources/Cargo.toml
Normal file
13
crates/renderer_resources/Cargo.toml
Normal file
|
|
@ -0,0 +1,13 @@
|
||||||
|
[package]
|
||||||
|
name = "renderer_resources"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2024"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
renderer_device = { path = "../renderer_device" }
|
||||||
|
ash = { workspace = true }
|
||||||
|
gpu-allocator = { workspace = true }
|
||||||
|
parking_lot = { workspace = true }
|
||||||
|
bytemuck = { workspace = true }
|
||||||
|
tracing = { workspace = true }
|
||||||
|
ash-formats = { git = "https://github.com/janis-bhm/ash-formats", branch = "main" }
|
||||||
|
|
@ -49,7 +49,6 @@ impl PartialEq for BufferDesc {
|
||||||
fn eq(&self, other: &Self) -> bool {
|
fn eq(&self, other: &Self) -> bool {
|
||||||
self.flags == other.flags
|
self.flags == other.flags
|
||||||
// for hashmaps, `Eq` may be more strict than `Hash`
|
// for hashmaps, `Eq` may be more strict than `Hash`
|
||||||
&& self.name == other.name
|
|
||||||
&& self.size == other.size
|
&& self.size == other.size
|
||||||
&& self.usage == other.usage
|
&& self.usage == other.usage
|
||||||
&& self.queue_families == other.queue_families
|
&& self.queue_families == other.queue_families
|
||||||
|
|
@ -6,11 +6,8 @@ use std::{
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
device::{
|
device::{Allocation, AllocationStrategy, DeviceObject, QueueFlags},
|
||||||
Allocation, AllocationStrategy, DeviceObject, QueueFlags,
|
util::WeakVec,
|
||||||
},
|
|
||||||
swapchain::Swapchain,
|
|
||||||
util::weak_vec::WeakVec,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::Device;
|
use super::Device;
|
||||||
|
|
@ -75,7 +72,6 @@ impl Eq for ImageDesc {}
|
||||||
impl PartialEq for ImageDesc {
|
impl PartialEq for ImageDesc {
|
||||||
fn eq(&self, other: &Self) -> bool {
|
fn eq(&self, other: &Self) -> bool {
|
||||||
self.flags == other.flags
|
self.flags == other.flags
|
||||||
&& self.name == other.name
|
|
||||||
&& self.format == other.format
|
&& self.format == other.format
|
||||||
&& self.kind == other.kind
|
&& self.kind == other.kind
|
||||||
&& self.mip_levels == other.mip_levels
|
&& self.mip_levels == other.mip_levels
|
||||||
|
|
@ -90,7 +86,7 @@ impl PartialEq for ImageDesc {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> std::fmt::Debug for ImageDesc {
|
impl std::fmt::Debug for ImageDesc {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
f.debug_struct("ImageDesc")
|
f.debug_struct("ImageDesc")
|
||||||
.field("flags", &self.flags)
|
.field("flags", &self.flags)
|
||||||
|
|
@ -255,22 +251,24 @@ impl Image {
|
||||||
Self::new_with_allocation_unchecked(device, image, allocation, desc)
|
Self::new_with_allocation_unchecked(device, image, allocation, desc)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn from_swapchain_image(image: vk::Image, swapchain: &Swapchain) -> Self {
|
pub fn from_swapchain_raw(
|
||||||
|
image: vk::Image,
|
||||||
|
device: Device,
|
||||||
|
format: vk::Format,
|
||||||
|
extent: vk::Extent3D,
|
||||||
|
usage: vk::ImageUsageFlags,
|
||||||
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
image: ImageInner::Swapchain(image, swapchain.swapchain.device().clone()),
|
image: ImageInner::Swapchain(image, device),
|
||||||
desc: ImageDesc {
|
desc: ImageDesc {
|
||||||
format: swapchain.config.format,
|
format,
|
||||||
kind: vk::ImageType::TYPE_2D,
|
kind: vk::ImageType::TYPE_2D,
|
||||||
mip_levels: 1,
|
mip_levels: 1,
|
||||||
array_layers: 1,
|
array_layers: 1,
|
||||||
samples: vk::SampleCountFlags::TYPE_1,
|
samples: vk::SampleCountFlags::TYPE_1,
|
||||||
extent: vk::Extent3D {
|
extent,
|
||||||
width: swapchain.config.extent.width,
|
|
||||||
height: swapchain.config.extent.height,
|
|
||||||
depth: 1,
|
|
||||||
},
|
|
||||||
tiling: vk::ImageTiling::OPTIMAL,
|
tiling: vk::ImageTiling::OPTIMAL,
|
||||||
usage: swapchain.config.usage,
|
usage,
|
||||||
queue_families: QueueFlags::PRESENT,
|
queue_families: QueueFlags::PRESENT,
|
||||||
layout: vk::ImageLayout::UNDEFINED,
|
layout: vk::ImageLayout::UNDEFINED,
|
||||||
mem_location: gpu_allocator::MemoryLocation::GpuOnly,
|
mem_location: gpu_allocator::MemoryLocation::GpuOnly,
|
||||||
6
crates/renderer_resources/src/lib.rs
Normal file
6
crates/renderer_resources/src/lib.rs
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
pub use renderer_device::{Device, Error, Result, device, make_extension};
|
||||||
|
|
||||||
|
pub mod buffers;
|
||||||
|
pub mod images;
|
||||||
|
pub mod pipeline;
|
||||||
|
pub mod util;
|
||||||
170
crates/renderer_resources/src/util.rs
Normal file
170
crates/renderer_resources/src/util.rs
Normal file
|
|
@ -0,0 +1,170 @@
|
||||||
|
pub(crate) mod weak_vec {
|
||||||
|
#![allow(dead_code)]
|
||||||
|
//! Module containing the [`WeakVec`] API.
|
||||||
|
|
||||||
|
use std::{sync::Weak, vec::Vec};
|
||||||
|
|
||||||
|
/// An optimized container for `Weak` references of `T` that minimizes reallocations by
|
||||||
|
/// dropping older elements that no longer have strong references to them.
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub(crate) struct WeakVec<T> {
|
||||||
|
inner: Vec<Weak<T>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Default for WeakVec<T> {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
inner: Default::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> WeakVec<T> {
|
||||||
|
pub(crate) fn new() -> Self {
|
||||||
|
Self { inner: Vec::new() }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Pushes a new element to this collection.
|
||||||
|
///
|
||||||
|
/// If the inner Vec needs to be reallocated, we will first drop older elements that
|
||||||
|
/// no longer have strong references to them.
|
||||||
|
pub(crate) fn push(&mut self, value: Weak<T>) {
|
||||||
|
if self.inner.len() == self.inner.capacity() {
|
||||||
|
// Iterating backwards has the advantage that we don't do more work than we have to.
|
||||||
|
for i in (0..self.inner.len()).rev() {
|
||||||
|
if self.inner[i].strong_count() == 0 {
|
||||||
|
self.inner.swap_remove(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make sure our capacity is twice the number of live elements.
|
||||||
|
// Leaving some spare capacity ensures that we won't re-scan immediately.
|
||||||
|
self.inner.reserve_exact(self.inner.len());
|
||||||
|
}
|
||||||
|
|
||||||
|
self.inner.push(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) struct WeakVecIter<T> {
|
||||||
|
inner: std::vec::IntoIter<Weak<T>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Iterator for WeakVecIter<T> {
|
||||||
|
type Item = Weak<T>;
|
||||||
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
|
self.inner.next()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> IntoIterator for WeakVec<T> {
|
||||||
|
type Item = Weak<T>;
|
||||||
|
type IntoIter = WeakVecIter<T>;
|
||||||
|
fn into_iter(self) -> Self::IntoIter {
|
||||||
|
WeakVecIter {
|
||||||
|
inner: self.inner.into_iter(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) use weak_vec::WeakVec;
|
||||||
|
|
||||||
|
pub fn eq_f32(lhs: f32, rhs: f32) -> bool {
|
||||||
|
use core::num::FpCategory::*;
|
||||||
|
match (lhs.classify(), rhs.classify()) {
|
||||||
|
(Zero, Zero) | (Nan, Nan) => true,
|
||||||
|
(Infinite, Infinite) => lhs.signum() == rhs.signum(),
|
||||||
|
_ => lhs == rhs,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn ord_f32(lhs: f32, rhs: f32) -> core::cmp::Ordering {
|
||||||
|
use core::cmp::Ordering;
|
||||||
|
use core::num::FpCategory::*;
|
||||||
|
match (lhs.classify(), rhs.classify()) {
|
||||||
|
(Zero, Zero) | (Nan, Nan) => Ordering::Equal,
|
||||||
|
(Infinite, Infinite) => {
|
||||||
|
core::cmp::PartialOrd::partial_cmp(&lhs.signum(), &rhs.signum()).unwrap()
|
||||||
|
}
|
||||||
|
_ => core::cmp::PartialOrd::partial_cmp(&lhs, &rhs).unwrap(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn hash_f32<H: std::hash::Hasher>(state: &mut H, f: f32) {
|
||||||
|
use std::hash::Hash;
|
||||||
|
let classify = f.classify();
|
||||||
|
match classify {
|
||||||
|
std::num::FpCategory::Nan => (classify as u8).hash(state),
|
||||||
|
std::num::FpCategory::Infinite | std::num::FpCategory::Zero => {
|
||||||
|
(classify as u8, f.signum() as i8).hash(state)
|
||||||
|
}
|
||||||
|
std::num::FpCategory::Subnormal | std::num::FpCategory::Normal => f.to_bits().hash(state),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(transparent)]
|
||||||
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
pub struct F32(f32);
|
||||||
|
|
||||||
|
impl F32 {
|
||||||
|
#[inline]
|
||||||
|
pub fn from_bits(bits: u32) -> Self {
|
||||||
|
Self(f32::from_bits(bits))
|
||||||
|
}
|
||||||
|
#[inline]
|
||||||
|
pub fn into_bits(self) -> u32 {
|
||||||
|
self.0.to_bits()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<f32> for F32 {
|
||||||
|
fn from(value: f32) -> Self {
|
||||||
|
Self(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl From<F32> for f32 {
|
||||||
|
fn from(value: F32) -> Self {
|
||||||
|
value.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::ops::Deref for F32 {
|
||||||
|
type Target = f32;
|
||||||
|
|
||||||
|
fn deref(&self) -> &Self::Target {
|
||||||
|
&self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::ops::DerefMut for F32 {
|
||||||
|
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||||
|
&mut self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PartialEq for F32 {
|
||||||
|
fn eq(&self, other: &Self) -> bool {
|
||||||
|
eq_f32(self.0, other.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Eq for F32 {}
|
||||||
|
|
||||||
|
impl PartialOrd for F32 {
|
||||||
|
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
|
||||||
|
Some(ord_f32(self.0, other.0))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Ord for F32 {
|
||||||
|
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
|
||||||
|
ord_f32(self.0, other.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl core::hash::Hash for F32 {
|
||||||
|
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
|
||||||
|
hash_f32(state, self.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in a new issue