![]() # Objective Certain classes of games, usually those with enormous worlds, require some amount of support for double-precision. Libraries like `big_space` exist to allow for large worlds while integrating cleanly with Bevy's primarily single-precision ecosystem, but even then, games will often still work directly in double-precision throughout the part of the pipeline that feeds into the Bevy interface. Currently, working with double-precision types in Bevy is a pain. `glam` provides types like `DVec3`, but Bevy doesn't provide double-precision analogs for `glam` wrappers like `Dir3`. This is mostly because doing so involves one of: - code duplication - generics - templates (like `glam` uses) - macros Each of these has issues that are enough to be deal-breakers as far as maintainability, usability or readability. To work around this, I'm putting together `bevy_dmath`, a crate that duplicates `bevy_math` types and functionality to allow downstream users to enjoy the ergonomics and power of `bevy_math` in double-precision. For the most part, it's a smooth process, but in order to fully integrate, there are some necessary changes that can only be made in `bevy_math`. ## Solution This PR addresses the first and easiest issue with downstream double-precision math support: `VectorSpace` currently can only represent vector spaces over `f32`. This automatically closes the door to double-precision curves, among other things. This restriction can be easily lifted by allowing vector spaces to specify the underlying scalar field. This PR adds a new trait `ScalarField` that satisfies the properties of a scalar field (the ones that can be upheld statically) and adds a new associated type `type Scalar: ScalarField` to `VectorSpace`. It's mostly an unintrusive change. The biggest annoyances are: - it touches a lot of curve code - `bevy_math::ops` doesn't support `f64`, so there are some annoying workarounds As far as curves code, I wanted to make this change unintrusive and bite-sized, so I'm trying to touch as little code as possible. To prove to myself it can be done, I went ahead and (*not* in this PR) migrated most of the curves API to support different `ScalarField`s and it went really smoothly! The ugliest thing was adding `P::Scalar: From<usize>` in several places. There's an argument to be made here that we should be using `num-traits`, but that's not immediately relevant. The point is that for now, the smallest change I could make was to go into every curve impl and make them generic over `VectorSpace<Scalar = f32>`. Curves work exactly like before and don't change the user API at all. # Follow-up - **Extend `bevy_math::ops` to work with `f64`.** `bevy_math::ops` is used all over, and if curves are ever going to support different `ScalarField` types, we'll need to be able to use the correct `std` or `libm` ops for `f64` types as well. Adding an `ops64` mod turned out to be really ugly, but I'll point out the maintenance burden is low because we're not going to be adding new floating-point ops anytime soon. Another solution is to build a floating-point trait that calls the right op variant and impl it for `f32` and `f64`. This reduces maintenance burden because on the off chance we ever *do* want to go modify it, it's all tied together: you can't change the interface on one without changing the trait, which forces you to update the other. A third option is to use `num-traits`, which is basically option 2 but someone else did the work for us. They already support `no_std` using `libm`, so it would be more or less a drop-in replacement. They're missing a couple floating-point ops like `floor` and `ceil`, but we could make our own floating-point traits for those (there's even the potential for upstreaming them into `num-traits`). - **Tweak curves to accept vector spaces over any `ScalarField`.** Curves are ready to support custom scalar types as soon as the bullet above is addressed. I will admit that the code is not as fun to look at: `P::Scalar` instead of `f32` everywhere. We could consider an alternate design where we use `f32` even to interpolate something like a `DVec3`, but personally I think that's a worse solution than parameterizing curves over the vector space's scalar type. At the end of the day, it's not really bad to deal with in my opinion... `ScalarType` supports enough operations that working with them is almost like working with raw float types, and it unlocks a whole ecosystem for games that want to use double-precision. |
||
---|---|---|
.. | ||
benches | ||
src | ||
Cargo.toml | ||
README.md |
Bevy Benchmarks
This is a crate with a collection of benchmarks for Bevy.
Running benchmarks
Benchmarks can be run through Cargo:
# Run all benchmarks. (This will take a while!)
cargo bench -p benches
# Just compile the benchmarks, do not run them.
cargo bench -p benches --no-run
# Run the benchmarks for a specific crate. (See `Cargo.toml` for a complete list of crates
# tracked.)
cargo bench -p benches --bench ecs
# Filter which benchmarks are run based on the name. This will only run benchmarks whose name
# contains "name_fragment".
cargo bench -p benches -- name_fragment
# List all available benchmarks.
cargo bench -p benches -- --list
# Save a baseline to be compared against later.
cargo bench -p benches -- --save-baseline before
# Compare the current benchmarks against a baseline to find performance gains and regressions.
cargo bench -p benches -- --baseline before
Criterion
Bevy's benchmarks use Criterion. If you want to learn more about using Criterion for comparing performance against a baseline or generating detailed reports, you can read the Criterion.rs documentation.