Skip to content

Commit

Permalink
Improve performance of projectiles (FAForever#3718)
Browse files Browse the repository at this point in the history
Improves the performance of all projectiles and specifically the projectiles of the Zthuee. Changes across the hierarchy that includes the upvalue strategy, better memory management and a reduction of engine calls.

Part of a Youtube series where we look at code and improve the performance by rewriting it.
  • Loading branch information
Garanas authored Mar 25, 2022
1 parent 80dcbf0 commit 4c3848d
Show file tree
Hide file tree
Showing 9 changed files with 327 additions and 159 deletions.
1 change: 1 addition & 0 deletions engine/Sim/Projectile.lua
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ end

---
-- Projectile:CreateChildProjectile(blueprint)
-- Internally calls import to find the projectile
function Projectile:CreateChildProjectile(blueprint)
end

Expand Down
3 changes: 1 addition & 2 deletions lua/proptree.lua
Original file line number Diff line number Diff line change
Expand Up @@ -84,11 +84,10 @@ Tree = Class(Prop) {
local canBurn = (not self.Burning) and (not self.NoBurn)

if type == 'Disintegrate' or type == "Reclaimed" then
LOG("Desintegrated!")
-- we just got obliterated
EntityDestroy(self)

elseif type == 'Force' then
elseif type == 'Force' or type == "KnockTree" then
if canFall then
-- change internal state
self.NoBurn = true
Expand Down
114 changes: 103 additions & 11 deletions lua/sim/DefaultProjectiles.lua
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
-- Summary : Script for default projectiles
-- Copyright © 2005 Gas Powered Games, Inc. All rights reserved.
-----------------------------------------------------------------

local Projectile = import('/lua/sim/Projectile.lua').Projectile
local DummyProjectile = import('/lua/sim/Projectile.lua').DummyProjectile
local UnitsInSphere = import('/lua/utilities.lua').GetTrueEnemyUnitsInSphere
Expand All @@ -13,6 +14,20 @@ local OCProjectiles = {}
-- shared between sim and ui
local OverchargeShared = import('/lua/shared/overcharge.lua')

-- upvalue globals for performance
local Random = Random
local CreateTrail = CreateTrail
local CreateEmitterOnEntity = CreateEmitterOnEntity
local CreateBeamEmitterOnEntity = CreateBeamEmitterOnEntity

local TableGetn = table.getn

local MathFloor = math.floor

-- upvalue moho functions for performance
local IEffectScaleEmitter = _G.moho.IEffect.ScaleEmitter
local IEffectOffsetEmitter = _G.moho.IEffect.OffsetEmitter

-----------------------------------------------------------------
-- Null Shell
-----------------------------------------------------------------
Expand All @@ -21,15 +36,27 @@ NullShell = Class(Projectile) {}
-----------------------------------------------------------------
-- PROJECTILE WITH ATTACHED EFFECT EMITTERS
-----------------------------------------------------------------

EmitterProjectile = Class(Projectile) {
FxTrails = {'/effects/emitters/missile_munition_trail_01_emit.bp',},
FxTrailScale = 1,
FxTrailOffset = 0,

OnCreate = function(self)
Projectile.OnCreate(self)

local effect
for i in self.FxTrails do
CreateEmitterOnEntity(self, self.Army, self.FxTrails[i]):ScaleEmitter(self.FxTrailScale):OffsetEmitter(0, 0, self.FxTrailOffset)
effect = CreateEmitterOnEntity(self, self.Army, self.FxTrails[i])

-- only do these engine calls when they matter
if self.FxTrailScale ~= 1 then
IEffectScaleEmitter(effect, self.FxTrailScale)
end

if self.FxTrailOffset ~= 1 then
IEffectOffsetEmitter(effect, 0, 0, self.FxTrailOffset)
end
end
end,
}
Expand All @@ -44,6 +71,7 @@ SingleBeamProjectile = Class(EmitterProjectile) {

OnCreate = function(self)
EmitterProjectile.OnCreate(self)

if self.BeamName then
CreateBeamEmitterOnEntity(self, -1, self.Army, self.BeamName)
end
Expand All @@ -57,6 +85,7 @@ MultiBeamProjectile = Class(EmitterProjectile) {

OnCreate = function(self)
EmitterProjectile.OnCreate(self)

local beam = nil
for k, v in self.Beams do
CreateBeamEmitterOnEntity(self, -1, self.Army, v)
Expand Down Expand Up @@ -187,12 +216,21 @@ SinglePolyTrailProjectile = Class(EmitterProjectile) {

OnCreate = function(self)
EmitterProjectile.OnCreate(self)

if self.PolyTrail ~= '' then
CreateTrail(self, -1, self.Army, self.PolyTrail):OffsetEmitter(0, 0, self.PolyTrailOffset)
local effect = CreateTrail(self, -1, self.Army, self.PolyTrail)

-- only do these engine calls when they matter
if self.PolyTrailOffset ~= 0 then
IEffectOffsetEmitter(effect, 0, 0, self.PolyTrailOffset)
end
end
end,
}

-- upvalue for performance


MultiPolyTrailProjectile = Class(EmitterProjectile) {

PolyTrails = {'/effects/emitters/test_missile_trail_emit.bp'},
Expand All @@ -202,18 +240,31 @@ MultiPolyTrailProjectile = Class(EmitterProjectile) {

OnCreate = function(self)
EmitterProjectile.OnCreate(self)

if self.PolyTrails then
local NumPolyTrails = table.getn(self.PolyTrails)
local effect
local army = self.Army
local NumPolyTrails = TableGetn(self.PolyTrails)

if self.RandomPolyTrails ~= 0 then
local index = nil
local index
for i = 1, self.RandomPolyTrails do
index = math.floor(Random(1, NumPolyTrails))
CreateTrail(self, -1, self.Army, self.PolyTrails[index]):OffsetEmitter(0, 0, self.PolyTrailOffset[index])
index = Random(1, NumPolyTrails)
effect = CreateTrail(self, -1, army, self.PolyTrails[index])

-- only do these engine calls when they matter
if self.PolyTrailOffset[index] ~= 0 then
IEffectOffsetEmitter(effect, 0, 0, self.PolyTrailOffset[index])
end
end
else
for i = 1, NumPolyTrails do
CreateTrail(self, -1, self.Army, self.PolyTrails[i]):OffsetEmitter(0, 0, self.PolyTrailOffset[i])
effect = CreateTrail(self, -1, army, self.PolyTrails[i])

-- only do these engine calls when they matter
if self.PolyTrailOffset[i] ~= 0 then
IEffectOffsetEmitter(effect, 0, 0, self.PolyTrailOffset[i])
end
end
end
end
Expand All @@ -234,6 +285,7 @@ SingleCompositeEmitterProjectile = Class(SinglePolyTrailProjectile) {

OnCreate = function(self)
SinglePolyTrailProjectile.OnCreate(self)

if self.BeamName ~= '' then
CreateBeamEmitterOnEntity(self, -1, self.Army, self.BeamName)
end
Expand All @@ -251,6 +303,7 @@ MultiCompositeEmitterProjectile = Class(MultiPolyTrailProjectile) {

OnCreate = function(self)
MultiPolyTrailProjectile.OnCreate(self)

local beam = nil
for k, v in self.Beams do
CreateBeamEmitterOnEntity(self, -1, self.Army, v)
Expand All @@ -272,23 +325,61 @@ OnWaterEntryEmitterProjectile = Class(Projectile) {

OnCreate = function(self, inWater)
Projectile.OnCreate(self, inWater)

if inWater then

local effect
local army = self.Army

for i in self.FxTrails do
CreateEmitterOnEntity(self, self.Army, self.FxTrails[i]):ScaleEmitter(self.FxTrailScale):OffsetEmitter(0, 0, self.FxTrailOffset)
effect = CreateEmitterOnEntity(self, army, self.FxTrails[i])

-- only do these engine calls when they matter
if self.FxTrailScale ~= 1 then
IEffectScaleEmitter(effect, self.FxTrailScale)
end

if self.FxTrailOffset ~= 1 then
IEffectOffsetEmitter(effect, 0, 0, self.FxTrailOffset)
end
end

if self.PolyTrail ~= '' then
CreateTrail(self, -1, self.Army, self.PolyTrail):OffsetEmitter(0, 0, self.PolyTrailOffset)
effect = CreateTrail(self, -1, army, self.PolyTrail)

-- only do these engine calls when they matter
if self.PolyTrailOffset ~= 0 then
IEffectOffsetEmitter(effect, 0, 0, self.PolyTrailOffset)
end
end
end
end,

EnterWaterThread = function(self)
WaitTicks(self.TrailDelay)

local effect
local army = self.Army

for i in self.FxTrails do
CreateEmitterOnEntity(self, self.Army, self.FxTrails[i]):ScaleEmitter(self.FxTrailScale):OffsetEmitter(0, 0, self.FxTrailOffset)
effect = CreateEmitterOnEntity(self, army, self.FxTrails[i])

-- only do these engine calls when they matter
if self.FxTrailScale ~= 1 then
IEffectScaleEmitter(effect, self.FxTrailScale)
end

if self.FxTrailOffset ~= 1 then
IEffectOffsetEmitter(effect, 0, 0, self.FxTrailOffset)
end
end
if self.PolyTrail ~= '' then
CreateTrail(self, -1, self.Army, self.PolyTrail):OffsetEmitter(0, 0, self.PolyTrailOffset)
local effect = CreateTrail(self, -1, army, self.PolyTrail)

-- only do these engine calls when they matter
if self.PolyTrailOffset ~= 0 then
IEffectOffsetEmitter(effect, 0, 0, self.PolyTrailOffset)
end
end
end,

Expand Down Expand Up @@ -387,6 +478,7 @@ BaseGenericDebris = Class(DummyProjectile){
-----------------------------------------------------------
-- PROJECTILE THAT ADJUSTS DAMAGE AND ENERGY COST ON IMPACT
-----------------------------------------------------------

OverchargeProjectile = Class() {
OnImpact = function(self, targetType, targetEntity)
-- Stop us doing blueprint damage in the other OnImpact call if we ditch this one without resetting self.DamageData
Expand Down
12 changes: 12 additions & 0 deletions lua/sim/Profiler.lua
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ local thread = false
--- Data that we send over to the UI
local data = CreateEmptyProfilerTable()

local traces = { }

--- Toggles the profiler on / off
function ToggleProfiler(army, forceEnable)

Expand Down Expand Up @@ -103,6 +105,16 @@ function ToggleProfiler(army, forceEnable)
scope = "other"
end

if name == "lambda" then
local trace = repr(debug.traceback())
if not traces[trace] or traces[trace] == 500 then
traces[trace] = traces[trace] or 0
LOG(tostring(traces[trace]) .. ": " .. trace)
end

traces[trace] = traces[trace] + 1
end

-- keep track
local count = data[source][scope][name]
if not count then
Expand Down
Loading

0 comments on commit 4c3848d

Please sign in to comment.