Only copy the live portion of parts when SRT is enabled

One major downside of SRT is that a RenderableSimulation has to be copied between the main thread and the rendering thread, which may actually take more time than to render on the main thread. This commit reduces the impact of this copy by copying only the subset of RenderableSimulation::parts that actually matters.
This commit is contained in:
Tamás Bálint Misius
2024-12-20 17:14:35 +01:00
parent 2f2d24b838
commit c645269c86
10 changed files with 59 additions and 37 deletions

View File

@@ -18,7 +18,7 @@ void DebugParts::Draw()
Graphics * g = ui::Engine::Ref().g;
int x = 0, y = 0, lpx = 0, lpy = 0;
String info = String::Build(sim->parts_lastActiveIndex, "/", NPART, " (", Format::Precision((float)sim->parts_lastActiveIndex/(NPART)*100.0f, 2), "%)");
String info = String::Build(sim->parts.lastActiveIndex, "/", NPART, " (", Format::Precision((float)sim->parts.lastActiveIndex/(NPART)*100.0f, 2), "%)");
for (int i = 0; i < NPART; i++)
{
if (sim->parts[i].type)
@@ -26,7 +26,7 @@ void DebugParts::Draw()
else
g->AddPixel({ x, y }, 0x000000_rgb .WithAlpha(180));
if (i == sim->parts_lastActiveIndex)
if (i == sim->parts.lastActiveIndex)
{
lpx = x;
lpy = y;

View File

@@ -249,7 +249,7 @@ void Renderer::render_parts()
int orbd[4] = {0, 0, 0, 0}, orbl[4] = {0, 0, 0, 0};
int drawing_budget = 1000000; //Serves as an upper bound for costly effects such as SPARK, FLARE and LFLARE
auto *parts = sim->parts;
auto &parts = sim->parts;
if (gridSize)//draws the grid
{
for (ny=0; ny<YRES; ny++)
@@ -262,7 +262,7 @@ void Renderer::render_parts()
}
}
foundParticles = 0;
for(i = 0; i<=sim->parts_lastActiveIndex; i++) {
for(i = 0; i<=sim->parts.lastActiveIndex; i++) {
if (sim->parts[i].type && sim->parts[i].type >= 0 && sim->parts[i].type < PT_NUM) {
t = sim->parts[i].type;
@@ -801,7 +801,7 @@ void Renderer::render_parts()
type = PT_PRTO;
else if (type == PT_PRTO)
type = PT_PRTI;
for (int z = 0; z <= sim->parts_lastActiveIndex; z++)
for (int z = 0; z <= sim->parts.lastActiveIndex; z++)
{
if (parts[z].type == type)
{

View File

@@ -862,7 +862,7 @@ static int resetTemp(lua_State *L)
auto &sd = SimulationData::CRef();
auto &elements = sd.elements;
bool onlyConductors = luaL_optint(L, 1, 0);
for (int i = 0; i < sim->parts_lastActiveIndex; i++)
for (int i = 0; i < sim->parts.lastActiveIndex; i++)
{
if (sim->parts[i].type && (elements[sim->parts[i].type].HeatConduct || !onlyConductors))
{
@@ -1286,7 +1286,7 @@ static int brush(lua_State *L)
static int partsClosure(lua_State *L)
{
auto *lsi = GetLSI();
for (int i = lua_tointeger(L, lua_upvalueindex(1)); i <= lsi->sim->parts_lastActiveIndex; ++i)
for (int i = lua_tointeger(L, lua_upvalueindex(1)); i <= lsi->sim->parts.lastActiveIndex; ++i)
{
if (lsi->sim->parts[i].type)
{

View File

@@ -418,7 +418,7 @@ void Air::ApproximateBlockAirMaps()
{
auto &sd = SimulationData::CRef();
auto &elements = sd.elements;
for (int i = 0; i <= sim.parts_lastActiveIndex; i++)
for (int i = 0; i <= sim.parts.lastActiveIndex; i++)
{
int type = sim.parts[i].type;
if (!type)

View File

@@ -25,7 +25,7 @@ std::unique_ptr<Snapshot> Simulation::CreateSnapshot() const
snap->BlockAirH .insert (snap->BlockAirH .begin(), &air->bmap_blockairh[0][0], &air->bmap_blockairh[0][0] + NCELL);
snap->FanVelocityX .insert (snap->FanVelocityX .begin(), &fvx [0][0] , &fvx [0][0] + NCELL);
snap->FanVelocityY .insert (snap->FanVelocityY .begin(), &fvy [0][0] , &fvy [0][0] + NCELL);
snap->Particles .insert (snap->Particles .begin(), &parts [0] , &parts [0] + parts_lastActiveIndex + 1);
snap->Particles .insert (snap->Particles .begin(), &parts [0] , &parts [0] + parts.lastActiveIndex + 1);
snap->PortalParticles.insert (snap->PortalParticles.begin(), &portalp[0][0][0], &portalp[0][0][0] + CHANNELS * 8 * 80);
snap->WirelessData .insert (snap->WirelessData .begin(), &wireless[0][0] , &wireless[0][0] + CHANNELS * 2);
snap->stickmen .insert (snap->stickmen .begin(), &fighters[0] , &fighters[0] + MAX_FIGHTERS);
@@ -45,7 +45,7 @@ void Simulation::Restore(const Snapshot &snap)
std::fill(elementCount, elementCount + PT_NUM, 0);
elementRecount = true;
force_stacking_check = true;
for (auto &part : parts)
for (auto &part : parts.data)
{
part.type = 0;
}
@@ -78,7 +78,7 @@ void Simulation::Restore(const Snapshot &snap)
signs = snap.signs;
frameCount = snap.FrameCount;
rng.state(snap.RngState);
parts_lastActiveIndex = NPART - 1;
parts.lastActiveIndex = NPART - 1;
RecalcFreeParticles(false);
}
@@ -90,7 +90,7 @@ void Simulation::clear_area(int area_x, int area_y, int area_w, int area_h)
area_w = intersection.size.X;
area_h = intersection.size.Y;
float fx = area_x-.5f, fy = area_y-.5f;
for (int i = 0; i <= parts_lastActiveIndex; i++)
for (int i = 0; i <= parts.lastActiveIndex; i++)
{
if (parts[i].type)
if (parts[i].x >= fx && parts[i].x <= fx+area_w+1 && parts[i].y >= fy && parts[i].y <= fy+area_h+1)

View File

@@ -39,7 +39,7 @@ constexpr auto FLAG_MOVABLE = UINT32_C(0x00000008); // compatibility with
constexpr auto FLAG_PHOTDECO = UINT32_C(0x00000008); // compatibility with old saves (decorated photons), only applies to PHOT. Having the same value as FLAG_MOVABLE is fine because they apply to different elements, and this saves space for future flags,
#define UPDATE_FUNC_ARGS Simulation* sim, int i, int x, int y, int surround_space, int nt, Particle *parts, int pmap[YRES][XRES]
#define UPDATE_FUNC_ARGS Simulation* sim, int i, int x, int y, int surround_space, int nt, Parts &parts, int pmap[YRES][XRES]
#define UPDATE_FUNC_SUBCALL_ARGS sim, i, x, y, surround_space, nt, parts, pmap
#define GRAPHICS_FUNC_ARGS GraphicsFuncContext &gfctx, const Particle *cpart, int nx, int ny, int *pixel_mode, int* cola, int *colr, int *colg, int *colb, int *firea, int *firer, int *fireg, int *fireb
@@ -83,3 +83,4 @@ constexpr bool InBounds(int x, int y)
}
struct playerst;
struct Parts;

View File

@@ -39,7 +39,7 @@ void Simulation::Load(const GameSave *save, bool includePressure, Vec2<int> bloc
};
std::vector<ExistingParticle> existingParticles;
auto pasteArea = RES.OriginRect() & RectSized(partP, save->blockSize * CELL);
for (int i = 0; i <= parts_lastActiveIndex; i++)
for (int i = 0; i <= parts.lastActiveIndex; i++)
{
if (parts[i].type)
{
@@ -132,8 +132,8 @@ void Simulation::Load(const GameSave *save, bool includePressure, Vec2<int> bloc
auto i = pfree;
pfree = parts[i].life;
NUM_PARTS += 1;
if (i > parts_lastActiveIndex)
parts_lastActiveIndex = i;
if (i > parts.lastActiveIndex)
parts.lastActiveIndex = i;
parts[i] = tempPart;
elementCount[tempPart.type]++;
@@ -219,7 +219,7 @@ void Simulation::Load(const GameSave *save, bool includePressure, Vec2<int> bloc
parts[i].tmp3 = 0;
}
}
parts_lastActiveIndex = NPART-1;
parts.lastActiveIndex = NPART-1;
force_stacking_check = true;
Element_PPIP_ppip_changed = 1;
@@ -992,7 +992,7 @@ void Simulation::clear_sim(void)
parts[NPART-1].life = -1;
pfree = 0;
NUM_PARTS = 0;
parts_lastActiveIndex = 0;
parts.lastActiveIndex = 0;
memset(pmap, 0, sizeof(pmap));
memset(fvx, 0, sizeof(fvx));
memset(fvy, 0, sizeof(fvy));
@@ -1890,7 +1890,7 @@ int Simulation::create_part(int p, int x, int y, int t, int v)
i = p;
}
if (i>parts_lastActiveIndex) parts_lastActiveIndex = i;
if (i>parts.lastActiveIndex) parts.lastActiveIndex = i;
parts[i] = elements[t].DefaultProperties;
parts[i].type = t;
@@ -1993,7 +1993,7 @@ void Simulation::create_gain_photon(int pp)//photons from PHOT going through GLO
pfree = parts[i].life;
NUM_PARTS += 1;
if (i>parts_lastActiveIndex) parts_lastActiveIndex = i;
if (i>parts.lastActiveIndex) parts.lastActiveIndex = i;
parts[i].type = PT_PHOT;
parts[i].life = 680;
@@ -2032,7 +2032,7 @@ void Simulation::create_cherenkov_photon(int pp)//photons from NEUT going throug
pfree = parts[i].life;
NUM_PARTS += 1;
if (i>parts_lastActiveIndex) parts_lastActiveIndex = i;
if (i>parts.lastActiveIndex) parts.lastActiveIndex = i;
lr = rng.between(0, 1);
@@ -2210,7 +2210,7 @@ void Simulation::UpdateParticles(int start, int end)
//the main particle loop function, goes over all particles.
auto &sd = SimulationData::CRef();
auto &elements = sd.elements;
for (auto i = start; i < end && i <= parts_lastActiveIndex; i++)
for (auto i = start; i < end && i <= parts.lastActiveIndex; i++)
{
if (parts[i].type)
{
@@ -3363,7 +3363,7 @@ void Simulation::RecalcFreeParticles(bool do_life_dec)
auto &sd = SimulationData::CRef();
auto &elements = sd.elements;
//the particle loop that resets the pmap/photon maps every frame, to update them.
for (int i = 0; i <= parts_lastActiveIndex; i++)
for (int i = 0; i <= parts.lastActiveIndex; i++)
{
if (parts[i].type)
{
@@ -3431,13 +3431,13 @@ void Simulation::RecalcFreeParticles(bool do_life_dec)
}
if (lastPartUnused == -1)
{
pfree = (parts_lastActiveIndex>=(NPART-1)) ? -1 : parts_lastActiveIndex+1;
pfree = (parts.lastActiveIndex>=(NPART-1)) ? -1 : parts.lastActiveIndex+1;
}
else
{
parts[lastPartUnused].life = (parts_lastActiveIndex>=(NPART-1)) ? -1 : parts_lastActiveIndex+1;
parts[lastPartUnused].life = (parts.lastActiveIndex>=(NPART-1)) ? -1 : parts.lastActiveIndex+1;
}
parts_lastActiveIndex = lastPartUsed;
parts.lastActiveIndex = lastPartUsed;
if (elementRecount)
elementRecount = false;
}
@@ -3446,7 +3446,7 @@ void Simulation::SimulateGoL()
{
auto &builtinGol = SimulationData::builtinGol;
CGOL = 0;
for (int i = 0; i <= parts_lastActiveIndex; ++i)
for (int i = 0; i <= parts.lastActiveIndex; ++i)
{
auto &part = parts[i];
if (part.type != PT_LIFE)
@@ -3652,7 +3652,7 @@ void Simulation::CheckStacking()
}
if (excessive_stacking_found)
{
for (int i = 0; i <= parts_lastActiveIndex; i++)
for (int i = 0; i <= parts.lastActiveIndex; i++)
{
if (parts[i].type)
{
@@ -3877,7 +3877,7 @@ void Simulation::BeforeSim()
// update PPIP tmp?
if (Element_PPIP_ppip_changed)
{
for (int i = 0; i <= parts_lastActiveIndex; i++)
for (int i = 0; i <= parts.lastActiveIndex; i++)
{
if (parts[i].type==PT_PPIP)
{

View File

@@ -34,6 +34,30 @@ class Renderer;
class Air;
class GameSave;
struct Parts
{
std::array<Particle, NPART> data;
// initialized in clear_sim
int lastActiveIndex;
operator const Particle *() const
{
return data.data();
}
operator Particle *()
{
return data.data();
}
Parts &operator =(const Parts &other)
{
std::copy(other.data.begin(), other.data.begin() + lastActiveIndex + 1, data.begin());
lastActiveIndex = other.lastActiveIndex;
return *this;
}
};
struct RenderableSimulation
{
GravityInput gravIn;
@@ -56,15 +80,12 @@ struct RenderableSimulation
unsigned char bmap[YCELLS][XCELLS];
unsigned char emap[YCELLS][XCELLS];
Particle parts[NPART];
Parts parts;
int pmap[YRES][XRES];
int photons[YRES][XRES];
int aheat_enable = 0;
// initialized in clear_sim
int parts_lastActiveIndex;
bool useLuaCallbacks = false;
};

View File

@@ -105,7 +105,7 @@ void Element_EMP_Trigger(Simulation *sim, int triggerCount)
float prob_randDLAY = Probability::binomial_gte1(triggerCount, 1.0f/70);
for (int r = 0; r <=sim->parts_lastActiveIndex; r++)
for (int r = 0; r <=sim->parts.lastActiveIndex; r++)
{
int t = parts[r].type;
auto rx = int(parts[r].x);

View File

@@ -115,7 +115,7 @@ int Element_ETRD_nearestSparkablePart(Simulation *sim, int targetId)
// If the simulation contains lots of particles, check near the target position first since going through all particles will be slow.
// Threshold = number of positions checked, *2 because it's likely to access memory all over the place (less cache friendly) and there's extra logic needed
// TODO: probably not optimal if excessive stacking is used
if (sim->parts_lastActiveIndex > (int)deltaPos.size()*2)
if (sim->parts.lastActiveIndex > (int)deltaPos.size()*2)
{
for (std::vector<ETRD_deltaWithLength>::iterator iter = deltaPos.begin(), end = deltaPos.end(); iter != end; ++iter)
{
@@ -145,7 +145,7 @@ int Element_ETRD_nearestSparkablePart(Simulation *sim, int targetId)
// If neighbor search didn't find a suitable particle, search all particles
if (foundI < 0)
{
for (int i = 0; i <= sim->parts_lastActiveIndex; i++)
for (int i = 0; i <= sim->parts.lastActiveIndex; i++)
{
if (parts[i].type == PT_ETRD && !parts[i].life)
{
@@ -164,7 +164,7 @@ int Element_ETRD_nearestSparkablePart(Simulation *sim, int targetId)
{
// Recalculate countLife0, and search for the closest suitable particle
int countLife0 = 0;
for (int i = 0; i <= sim->parts_lastActiveIndex; i++)
for (int i = 0; i <= sim->parts.lastActiveIndex; i++)
{
if (parts[i].type == PT_ETRD && !parts[i].life)
{