a performance disaster, part 1

This commit is contained in:
Anuken
2020-05-01 14:36:33 -04:00
parent cdc8d2bfe4
commit 64bacea3c7
13 changed files with 262 additions and 44 deletions

View File

@ -14,6 +14,15 @@ public class GroundAI extends AIController{
@Override @Override
public void update(){ public void update(){
//TODO test
if(true){
unit.controlWeapons(true, true);
unit.aimLook(unit.x(), unit.y() + 10);
return;
}
if(Units.invalidateTarget(target, unit.team(), unit.x(), unit.y(), Float.MAX_VALUE)){ if(Units.invalidateTarget(target, unit.team(), unit.x(), unit.y(), Float.MAX_VALUE)){
target = null; target = null;

View File

@ -8,7 +8,7 @@ import java.util.concurrent.*;
public class AsyncLogic{ public class AsyncLogic{
//all processes to be executed each frame //all processes to be executed each frame
private Array<AsyncProcess> processes = Array.with(new PhysicsProcess()); private Array<AsyncProcess> processes = Array.with(new PhysicsProcess(), new CollisionProcess());
//futures to be awaited //futures to be awaited
private Array<Future<?>> futures = new Array<>(); private Array<Future<?>> futures = new Array<>();

View File

@ -0,0 +1,200 @@
package mindustry.async;
import arc.*;
import arc.box2d.*;
import arc.box2d.BodyDef.*;
import arc.math.geom.*;
import arc.struct.*;
import arc.util.*;
import arc.util.pooling.*;
import arc.util.pooling.Pool.*;
import mindustry.entities.*;
import mindustry.gen.*;
/**
* Processes collisions.
*
* Each entity is assigned a final filter layer, then given a body and inserted into a physics world.
*
* Async:
* The body's position is set to its entity position, and the body velocity is set to the entity delta.
* Collisions are resolved and stored in a list, then processed synchronously.
*
*/
public class CollisionProcess implements AsyncProcess, ContactListener, ContactFilter{
private Pool<CollisionRef> pool = Pools.get(CollisionRef.class, CollisionRef::new);
private Physics physics;
private Array<CollisionRef> refs = new Array<>(false);
private BodyDef def;
private FixtureDef fdef;
private EntityGroup<? extends Collisionc> group = Groups.collision;
private Array<Collisionc> collisions = new Array<>(Collisionc.class);
public CollisionProcess(){
def = new BodyDef();
def.type = BodyType.DynamicBody;
fdef = new FixtureDef();
fdef.density = 1;
fdef.isSensor = true;
}
@Override
public void begin(){
if(physics == null) return;
//remove stale entities
refs.removeAll(ref -> {
if(!ref.entity.isAdded()){
physics.destroyBody(ref.body);
pool.free(ref);
return true;
}
return false;
});
collisions.clear();
//find entities without bodies and assign them
for(Collisionc entity : group){
if(entity.colref() == null){
//add bodies to entities that have none
fdef.shape = new CircleShape(entity.hitSize() / 2f);
def.position.set(entity);
Body body = physics.createBody(def);
body.createFixture(fdef);
CollisionRef ref = pool.obtain().set(entity, body);
refs.add(ref);
body.setUserData(ref);
entity.colref(ref);
}
//save last position
CollisionRef ref = entity.colref();
ref.position.set(entity);
}
}
@Override
public void process(){
if(physics == null) return;
collisions.clear();
Time.mark();
//get last position vectors before step
for(CollisionRef ref : refs){
//force set target position
ref.body.setPosition(ref.position.x, ref.position.y);
//write velocity
ref.body.setLinearVelocity(ref.velocity);
}
physics.step(Core.graphics.getDeltaTime(), 2, 2);
}
@Override
public void end(){
if(physics == null) return;
//processes anything that happened
for(int i = 0; i < collisions.size; i += 2){
Collisionc a = collisions.items[i];
Collisionc b = collisions.items[i + 1];
//TODO incorrect
float cx = (a.x() + b.x())/2f, cy = (a.y() + b.y())/2f;
a.collision(b, cx, cy);
b.collision(a, cx, cy);
}
//update velocity state based on frame movement
for(CollisionRef ref : refs){
ref.velocity.set(ref.entity).sub(ref.position);
}
}
@Override
public void reset(){
if(physics != null){
pool.freeAll(refs);
refs.clear();
physics.dispose();
physics = null;
}
}
@Override
public void init(){
reset();
physics = new Physics(new Vec2(), true);
physics.setContactListener(this);
physics.setContactFilter(this);
}
@Override
public void beginContact(Contact contact){
CollisionRef a = contact.getFixtureA().getBody().getUserData();
CollisionRef b = contact.getFixtureB().getBody().getUserData();
//save collision
collisions.add(a.entity, b.entity);
}
@Override
public void endContact(Contact contact){
}
@Override
public void preSolve(Contact contact, Manifold oldManifold){
}
@Override
public void postSolve(Contact contact, ContactImpulse impulse){
}
@Override
public boolean shouldCollide(Fixture fixtureA, Fixture fixtureB){
CollisionRef a = fixtureA.getBody().getUserData();
CollisionRef b = fixtureB.getBody().getUserData();
//note that this method is called in a different thread, but for simple collision checks state doesn't matter too much
return a != b && a.entity.collides(b.entity) && b.entity.collides(a.entity);
}
public static class CollisionRef implements Poolable{
Collisionc entity;
Body body;
Vec2 velocity = new Vec2(), position = new Vec2();
public CollisionRef set(Collisionc entity, Body body){
this.entity = entity;
this.body = body;
position.set(entity);
return this;
}
@Override
public void reset(){
entity = null;
body = null;
velocity.setZero();
position.setZero();
}
}
}

View File

@ -13,7 +13,7 @@ public class PhysicsProcess implements AsyncProcess{
private Array<PhysicRef> refs = new Array<>(false); private Array<PhysicRef> refs = new Array<>(false);
private BodyDef def; private BodyDef def;
private EntityGroup<Unitc> group; private EntityGroup<? extends Physicsc> group;
private Filter flying = new Filter(){{ private Filter flying = new Filter(){{
maskBits = categoryBits = 2; maskBits = categoryBits = 2;
}}, ground = new Filter(){{ }}, ground = new Filter(){{
@ -22,8 +22,6 @@ public class PhysicsProcess implements AsyncProcess{
public PhysicsProcess(){ public PhysicsProcess(){
def = new BodyDef(); def = new BodyDef();
def.allowSleep = true;
def.bullet = false;
def.type = BodyType.DynamicBody; def.type = BodyType.DynamicBody;
//currently only enabled for units //currently only enabled for units
@ -44,17 +42,15 @@ public class PhysicsProcess implements AsyncProcess{
}); });
//find entities without bodies and assign them //find entities without bodies and assign them
for(Unitc entity : group){ for(Physicsc entity : group){
boolean grounded = entity.isGrounded(); boolean grounded = entity.isGrounded();
if(entity.body() == null){ if(entity.physref() == null){
//add bodies to entities that have none //add bodies to entities that have none
CircleShape shape = new CircleShape();
shape.setRadius(entity.hitSize() / 2f);
FixtureDef fd = new FixtureDef(); FixtureDef fd = new FixtureDef();
fd.shape = shape; fd.shape = new CircleShape(entity.hitSize() / 2f);
fd.density = 5.0f * entity.mass(); //TODO does this scale well?
fd.density = 5f; //5.0f * entity.mass();
fd.restitution = 0.05f; fd.restitution = 0.05f;
fd.filter.maskBits = fd.filter.categoryBits = (grounded ? ground : flying).maskBits; fd.filter.maskBits = fd.filter.categoryBits = (grounded ? ground : flying).maskBits;
@ -62,16 +58,15 @@ public class PhysicsProcess implements AsyncProcess{
Body body = physics.createBody(def); Body body = physics.createBody(def);
body.createFixture(fd); body.createFixture(fd);
body.setUserData(entity);
PhysicRef ref = new PhysicRef(entity, body); PhysicRef ref = new PhysicRef(entity, body);
refs.add(ref); refs.add(ref);
entity.body(ref); entity.physref(ref);
} }
//save last position //save last position
PhysicRef ref = entity.body(); PhysicRef ref = entity.physref();
if(ref.wasGround != grounded){ if(ref.wasGround != grounded){
//set correct filter //set correct filter
@ -115,15 +110,9 @@ public class PhysicsProcess implements AsyncProcess{
//move entities //move entities
for(PhysicRef ref : refs){ for(PhysicRef ref : refs){
Hitboxc entity = ref.entity; Physicsc entity = ref.entity;
if(entity instanceof Velc){ entity.move(ref.delta.x, ref.delta.y);
//move using velocity component move method TODO hack
((Velc)entity).move(ref.delta.x, ref.delta.y);
}else{
//move directly
entity.trns(ref.delta.x, ref.delta.y);
}
//save last position //save last position
ref.position.set(entity); ref.position.set(entity);
@ -147,12 +136,12 @@ public class PhysicsProcess implements AsyncProcess{
} }
public static class PhysicRef{ public static class PhysicRef{
Hitboxc entity; Physicsc entity;
Body body; Body body;
boolean wasGround = true; boolean wasGround = true;
Vec2 lastPosition = new Vec2(), delta = new Vec2(), velocity = new Vec2(), position = new Vec2(); Vec2 lastPosition = new Vec2(), delta = new Vec2(), velocity = new Vec2(), position = new Vec2();
public PhysicRef(Hitboxc entity, Body body){ public PhysicRef(Physicsc entity, Body body){
this.entity = entity; this.entity = entity;
this.body = body; this.body = body;
} }

View File

@ -15,11 +15,16 @@ class AllDefs{
} }
@GroupDef(value = Bulletc.class, spatial = true, collide = {gunit.class}) @GroupDef(value = Bulletc.class, spatial = true)
class gbullet{ class gbullet{
} }
@GroupDef(value = Collisionc.class)
class gcollision{
}
@GroupDef(value = Unitc.class, spatial = true, mapping = true) @GroupDef(value = Unitc.class, spatial = true, mapping = true)
class gunit{ class gunit{

View File

@ -114,7 +114,7 @@ public class EntityCollisions{
group.each(s -> { group.each(s -> {
s.updateLastPosition(); s.updateLastPosition();
tree.insert(s); //tree.insert(s);
}); });
} }

View File

@ -12,7 +12,7 @@ import static mindustry.Vars.*;
@EntityDef(value = {Bulletc.class}, pooled = true) @EntityDef(value = {Bulletc.class}, pooled = true)
@Component @Component
abstract class BulletComp implements Timedc, Damagec, Hitboxc, Teamc, Posc, Drawc, Shielderc, Ownerc, Velc, Bulletc, Timerc{ abstract class BulletComp implements Timedc, Damagec, Collisionc, Teamc, Posc, Drawc, Shielderc, Ownerc, Velc, Bulletc, Timerc{
Object data; Object data;
BulletType type; BulletType type;
float damage; float damage;

View File

@ -0,0 +1,12 @@
package mindustry.entities.def;
import mindustry.annotations.Annotations.*;
import mindustry.async.CollisionProcess.*;
import mindustry.gen.*;
/** Can be collided with. Collision elibility depends on team.
* TODO merge with hitboxcomp?*/
@Component
abstract class CollisionComp implements Hitboxc{
transient CollisionRef colref;
}

View File

@ -3,7 +3,6 @@ package mindustry.entities.def;
import arc.math.geom.QuadTree.*; import arc.math.geom.QuadTree.*;
import arc.math.geom.*; import arc.math.geom.*;
import mindustry.annotations.Annotations.*; import mindustry.annotations.Annotations.*;
import mindustry.async.PhysicsProcess.*;
import mindustry.gen.*; import mindustry.gen.*;
@Component @Component
@ -11,7 +10,6 @@ abstract class HitboxComp implements Posc, QuadTreeObject{
@Import float x, y; @Import float x, y;
transient float lastX, lastY, hitSize; transient float lastX, lastY, hitSize;
transient PhysicRef body;
@Override @Override
public void update(){ public void update(){

View File

@ -1,13 +0,0 @@
package mindustry.entities.def;
import mindustry.annotations.Annotations.*;
import mindustry.gen.*;
@Component
abstract class MassComp implements Velc{
transient float mass = 1f;
public void impulse(float x, float y){
vel().add(x / mass, y / mass);
}
}

View File

@ -0,0 +1,18 @@
package mindustry.entities.def;
import mindustry.annotations.Annotations.*;
import mindustry.async.PhysicsProcess.*;
import mindustry.gen.*;
/** Affected by physics.
* Will bounce off of other objects that are at similar elevations.
* Has mass.*/
@Component
abstract class PhysicsComp implements Velc, Hitboxc, Flyingc{
transient PhysicRef physref;
transient float mass = 1f;
public void impulse(float x, float y){
vel().add(x / mass, y / mass);
}
}

View File

@ -18,7 +18,7 @@ import mindustry.world.blocks.environment.*;
import static mindustry.Vars.*; import static mindustry.Vars.*;
@Component @Component
abstract class UnitComp implements Healthc, Velc, Statusc, Teamc, Itemsc, Hitboxc, Rotc, Massc, Unitc, Weaponsc, Drawc, Boundedc, Syncc, Shieldc{ abstract class UnitComp implements Healthc, Physicsc, Collisionc, Statusc, Teamc, Itemsc, Rotc, Unitc, Weaponsc, Drawc, Boundedc, Syncc, Shieldc{
@Import float x, y, rotation, elevation, maxHealth; @Import float x, y, rotation, elevation, maxHealth;
private UnitController controller; private UnitController controller;

View File

@ -1,3 +1,3 @@
org.gradle.daemon=true org.gradle.daemon=true
org.gradle.jvmargs=-Xms256m -Xmx1024m org.gradle.jvmargs=-Xms256m -Xmx1024m
archash=ae2bb7c28ac26b26e549cdc42b0fb5ecfaa0f916 archash=00ccb60840294db20d561feda12fa739edaaf601