The test app simply adds boxes until the frame rate (calculated from 1-second samples) drops below 30 per second.
I run each test 3 times and report the extremes.
There’s no plotting involved.
The test app simply adds boxes until the frame rate (calculated from 1-second samples) drops below 30 per second.
I run each test 3 times and report the extremes.
There’s no plotting involved.
Jolt-jni v0.9.6 was released today.
It adds V-HACD, which can be used to generate efficient collision shapes from meshes. Here’s some example code:
ProgressListener listener = (double overallPercent, double sp,
double op, String sn, String on) -> {
progressPercent = overallPercent;
};
Decomposer decomposer = new Decomposer().addProgressListener(listener);
// Decompose the triangle mesh into convex hulls:
Parameters parameters = new Parameters();
Collection<ConvexHull> hulls
= decomposer.decompose(locationArray, indexArray, parameters);
// Construct shape- and body settings:
StaticCompoundShapeSettings compoundSettings
= new StaticCompoundShapeSettings();
compoundSettings.addHulls(hulls);
BodyCreationSettings bcs = new BodyCreationSettings(
compoundSettings, RVec3.sZero(), Quat.sIdentity(),
EMotionType.Dynamic, Layers.MOVING);
Support for soft bodies has also been improved, and I’ve begun work on a tutorial.
I re-tested KK Physics performance with Jolt-jni v0.9.6 and JME v3.8.0-alpha3:
(b) KK Physics with 10 worker threads: 3793 – 3838 boxes (more is better)
I suspect most of the 2.8% – 4.4% improvement came from changing the compiler from GCC to LLVM/Clang.
Further improvements can be achieved using ISA extensions such as Fused Multiply-Add (FMA) and Advanced Vector Extensions (AVX). However, not all x86_64 hardware supports these instructions. Before deploying natives with ISA extensions, I need a software mechanism to test for support.
To motivate this effort, I kludged together a performance test with JPH_USE_AVX2
and JPH_USE_FMADD
enabled:
(b) KK Physics with 10 worker threads: 3920 – 3983 boxes
In other words, another 2.2% – 5% improvement relative to the new Jolt-jni release.
Results will vary (of course) based on the workload, software, hardware, and other factors.
Jolt-jni v0.9.7 was released today. It adds alternative native libraries for Linux and Windows systems with extended instruction sets, increasing the number of platforms from 6 to 8.
I verified KK physics performance using Jolt v0.9.7. The TestManyBoxes
app achieved the expected numbers of boxes, both with and without the new natives.
To determine the best native library to load, KK Physics now uses the OSHI library. This change introduced dependencies on both OSHI and Apache Log4j.
A brief progress report on Jolt JNI…
Recently most of my development time has gone into creating a simple OpenGL graphics engine for Jolt JNI, very similar to the SPORT engine I created for Libbulletjme. That’s mostly working now. I created a GitHub repo for that project but haven’t committed any code yet. I hope to publish a release of the graphics engine before the end of March.
The graphics engine will be used to execute code samples in the forthcoming Jolt-JNI tutorial. The tutorial will be patterned after those I created for Minie and Libbulletjme. Once the tutorial reaches a useful state, I’ll declare the API to be stable and release Jolt JNI v1.0.0.
I hope some you are as excited about that milestone as I am!
The tutorial should be useful both to JME developers and those wishing to use Jolt JNI with other JVM-based game engines.
In other news, Jolt JNI can now be configured for Linuxes running on the Loongson 64-bit ISA. At this time, however, I don’t have any plans to distribute pre-built native libraries for LoongArch (nor for FreeBSD, PPC, RISC-V, or Windows-on-ARM). My focus, you see, is on platforms that JME supports.
Awesome work! Thank you so much for this effort!
Thanks to time zones, I made it. Barely. Sport-Jolt v0.9.0 was released tonight.
Next I’ll start work on the tutorial.
You might want to inform people on the enginedev channel on the lwjgl discord. Thats where lots of active engine devs are.
Good to know! Thanks for the hint.
I haven’t put much effort into promoting/publicizing Jolt JNI because I don’t have great confidence in it … yet. Someday soon, Jolt JNI will (I hope) become the physics engine I recommend for all new projects.
I imagine there’s a software proverb that says, “March testing and April documentation bring forth May publicity.”
Since the API of Jolt JNI isn’t frozen, now would be a good time for feedback on usability. If anyone besides @danielp is using Jolt JNI or KK Physics in a project, please let me know.
I am going to use jolt jni in my engine, but i am already far behind my schedule and i have to complete 4 more points on my checklist before basic physics shows up. 3 are mearly cleaning up/ testing but one of the points on the list is serialization/assetManagement where i have not yet made a prototype and don’t know nothing about the pros/cons of various system. So that might take a bit.
Sounds good. When you’re ready for physics, I’ll give you my best customer support.
For integrating physics into a game engine, I suspect the key question is how do you combine physics and graphics together. For instance, Minie and jme3-jbullet use scene-graph controls, and Sport extends the Geometry
class using inheritance. Do you have a particular approach in mind?
I am deliberately pushing such hard decisions down the list as far i can. I am definitely separating data from logic in some way, not sure yet if i want to go full component based or not. It would offer the the most flexibility, but at least for me it is not user friendly. If i go the control way i would probably make it as hard as possible to get access to the world beside it’s spatial and childs. (at least for editable data that is)
The very first experiments are going to be on how to integrate physics in the update loop.
Is the result deterministic if i run the physics part at 30/60/144/240hz what about variable update rates?
Best way to implement the interpolation? Should physics run one step ahead (input lag?)
Separating the physics update completely from the graphics update loop?
Not yet a very long bullet list in the physics section, but i am sure this list gets bigger once i start on it.
Jolt Physics is carefully tested for determinism. Even so, achieving determinism in a real-time simulation remains difficult.
If you can live without determinism, I suggest you do so.
To view the current WIP: https://stephengold.github.io/jolt-jni-docs
One thing that is not 100% clear to me after reading the “Add to Project” and “Rigid bodies” tutorial is how the sensors works exactly.
The doc stats that contact response is disabled when setting the object as sensor, but gravity still applies if i have the body configured as dynamic object (i guess?)
What about damping? is that still active or not?
I assume i can set each of the body types (static/kinematic/dynamic) as a sensor?
The collision layers probably requires some more in-depth explaining since i guess these are going to be the primary point of interest for optimization.
Without having written a single line of code, is there a logical system behind on when to edit values on the Body or the BodyInterface? (Gravity and Restitution as an example from the tutorial)
contact response is disabled when setting the object as sensor, but gravity still applies if i have the body configured as dynamic object (i guess?)
What about damping? is that still active or not?
I haven’t tested, but I expect gravity and damping still apply to sensors. If you find differently, please let me know!
i can set each of the body types (static/kinematic/dynamic) as a sensor?
I expect you can.
collision layers probably requires some more in-depth explaining
Thanks for that feedback.
is there a logical system behind on when to edit values on the Body or the BodyInterface?
I admit I don’t see any logic to which body attributes are directly accessible and which have to go through BodyInterface
.
One thing to keep in mind is that BodyInterface
gives you flexibility to use locking or not. I admit that so far I haven’t taken advantage of that flexibility.
Here’s the Jolt Physics documentation on sensors: https://jrouwe.github.io/JoltPhysics/index.html#sensors
Here’s what I found in the Jolt Physics docs regarding layers, in the section on Collision Detection:
The broad phase is divided in layers (BroadPhaseLayer), each broad phase layer has an AABB quad tree associated with it. A standard setup would be to have at least 2 broad phase layers: One for all static bodies (which is infrequently updated but is expensive to update since it usually contains most bodies) and one for all dynamic bodies (which is updated every simulation step but cheaper to update since it contains fewer objects). In general you should only have a few broad phase layers as there is overhead in querying and maintaining many different broad phase trees.
Also:
Each Body is in an ObjectLayer. If two object layers don’t collide, the bodies inside those layers cannot collide. You can define object layers in any way you like, it could be a simple number from 0 to N or it could be a bitmask. Jolt supports 16 or 32 bit ObjectLayers through the JPH_OBJECT_LAYER_BITS define and you’re free to define as many as you like as they don’t incur any overhead in the system.
When constructing the PhysicsSystem you need to provide a number of filtering interfaces:
- BroadPhaseLayerInterface: This class defines a mapping from ObjectLayer to BroadPhaseLayer through the BroadPhaseLayerInterface::GetBroadPhaseLayer function. Each Body can only be in 1 BroadPhaseLayer so an ObjectLayer maps to 1 BroadphaseLayer. In general there will be multiple ObjectLayers mapping to the same BroadPhaseLayer (because each broad phase layer comes at a cost). If there are multiple object layers in a single broad phase layer, they are stored in the same tree. When a query visits the tree it will visit all objects whose AABB overlaps with the query and only when the overlap is detected, the actual object layer will be checked. This means that you should carefully design which object layers end up in which broad phase layer, balancing the requirement of having few broad phase layers with the number of needless objects that are visited because multiple object layers share the same broad phase layer. You can define JPH_TRACK_BROADPHASE_STATS to let Jolt print out some statistics about the query patterns your application is using. In general it is wise to start with only 2 broad phase layers as listed in the Broad Phase section.
- ObjectVsBroadPhaseLayerFilter: This class defines a ObjectVsBroadPhaseLayerFilter::ShouldCollide function that checks if an ObjectLayer collides with objects that reside in a particular BroadPhaseLayer. ObjectLayers can collide with as many BroadPhaseLayers as needed, so it is possible for a collision query to visit multiple broad phase trees.
- ObjectLayerPairFilter: This class defines a ObjectLayerPairFilter::ShouldCollide function that checks if an ObjectLayer collides with another ObjectLayer.
As an example we will use a simple enum as ObjectLayer:
- NON_MOVING - Layer for all static objects.
- MOVING - Layer for all regular dynamic bodies.
- DEBRIS - Layer for all debris dynamic bodies, we want to test these only against the static geometry because we want to save some simulation cost.
- BULLET - Layer for high detail collision bodies that we co-locate with regular dynamic bodies. These are bodies that are not used for simulation but are moved to follow the dynamic bodies and provide more precise geometry for ray tests to simulate shooting bullets. > See Level of Detail for more information.
- WEAPON - This is a query layer so we don’t create any bodies with this layer but we use it when doing ray cast querying for our weapon system.
We define the following object layers to collide:
- MOVING vs NON_MOVING, MOVING vs MOVING - These are for our regular dynamic objects that need to collide with the static world and with each other.
- DEBRIS vs NON_MOVING - As said, we only want debris to collide with the static world and not with anything else.
- WEAPON vs BULLET, WEAPON vs NON_MOVING - We want our weapon ray cast to hit the high detail BULLET collision instead of the normal MOVING collision and we want bullets to be blocked by the static world (obviously the static world could also have a high detail version, but not in this example).
This means that we need to implement a ObjectLayerPairFilter::ShouldCollide that returns true for the permutations listed above. Note that if ShouldCollide(A, B) returns true, ShouldCollide(B, A) should return true too.
We define the following broad phase layers:
- BP_NON_MOVING - For everything static (contains object layer: NON_MOVING).
- BP_MOVING - The default layer for dynamic objects (contains object layers: MOVING, BULLET).
- BP_DEBRIS - An extra layer that contains only debris (contains object layers: DEBRIS).
This means we now implement a BroadPhaseLayerInterface::GetBroadPhaseLayer that maps: NON_MOVING → BP_NON_MOVING, MOVING → BP_MOVING, BULLET → BP_MOVING and DEBRIS → BP_DEBRIS. We can map WEAPON to anything as we won’t create any objects with this layer.
We also need to implement a ObjectVsBroadPhaseLayerFilter::ShouldCollide that determines which object layer should collide with what broad phase layers, these can be deduced from the two lists above:
- NON_MOVING: BP_MOVING, BP_DEBRIS
- MOVING: BP_NON_MOVING, BP_MOVING
- DEBRIS: BP_NON_MOVING
- BULLET: None (these are not simulated so need no collision with other objects)
- WEAPON: BP_NON_MOVING, BP_MOVING
So you can see now that when we simulate DEBRIS we only need to visit a single broad phase tree to check for collision, we did this because in our example we know that there are going to be 1000s of debris objects so it is important that their queries are as fast as possible. We could have moved the BULLET layer to its own broad phase layer too because now BP_MOVING contains a lot of bodies that WEAPON is not interested in, but in this example we didn’t because we know that there are not enough of these objects for this to be a performance problem.
For convenience two filtering implementations are provided:
- ObjectLayerPairFilterTable, ObjectVsBroadPhaseLayerFilterTable and BroadPhaseLayerInterfaceTable: These three implement collision layers as a simple table. You construct ObjectLayerPairFilterTable with a fixed number of object layers and then call ObjectLayerPairFilterTable::EnableCollision or ObjectLayerPairFilterTable::DisableCollision to selectively enable or disable collisions between layers. BroadPhaseLayerInterfaceTable is constructed with a number of broad phase layers. You can then map each object layer to a broad phase layer through BroadPhaseLayerInterfaceTable::MapObjectToBroadPhaseLayer.
- ObjectLayerPairFilterMask, ObjectVsBroadPhaseLayerFilterMask and BroadPhaseLayerInterfaceMask: These split an ObjectLayer in an equal amount of bits for group and mask. Two objects collide if (object1.group & object2.mask) != 0 && (object2.group & object1.mask) != 0. This behavior is similar to e.g. Bullet. In order to map groups to broad phase layers, you call BroadPhaseLayerInterfaceMask::ConfigureLayer for each broad phase layer. You determine which groups can be put in that layer and which group must be excluded from that layer. E.g. a broad phase layer could include everything that has the STATIC group but should exclude everything that has the SENSOR group, so that if an object has both STATIC and SENSOR bits set, this broad phase layer will not be used. The broad phase layers are checked one by one and the first one that meets the condition is the one that the body will be put in. If you use this implementation, consider setting the cmake option OBJECT_LAYER_BITS to 32 to get a 32-bit ObjectLayer instead of a 16-bit one.
Ultimately, I think this’ll need to be its own page in the tutorial.
I have created an issue on the sport-jolt repository:
Low FPS and freeze every second · Issue #1 · stephengold/sport-jolt
i was able to track the issue down, but the remaining allocation is inside jolt-jni. not sure if it is possible to cache the bodyid somehow to remove the native allocations.
I am handing that part over to you
As small follow up. A slightly modified TestManyBoxes (use instancing/emit 10 boxes each frame) from kk-physics
Warning: assertions are enabled.
Jolt JNI v0.9.9 initializing
maxSubSteps = 1, numSolvers = 28
numBoxes = 24031
Warning: assertions are enabled.
Jolt JNI v0.9.8 initializing
maxSubSteps = 1, numSolvers = 28
numBoxes = 21111
@sgold thanks for bringing jolt to JME. just a question is it now production ready. and when we will get it will SDK ? with 3.8 or later ?