diff --git a/source/common/objects/dobjgc.cpp b/source/common/objects/dobjgc.cpp index 91cfafaef..59c5002d7 100644 --- a/source/common/objects/dobjgc.cpp +++ b/source/common/objects/dobjgc.cpp @@ -195,6 +195,7 @@ static DObject **SweepList(DObject **p, size_t count, size_t *finalize_count) // However, this can happen during deletion of the thinker list while cleaning up // from a savegame error so we can't assume that any thinker that gets here is an error. + assert(!curr->IsKindOf("CoreActor")); curr->Destroy(); } diff --git a/source/common/objects/dobjgc.h b/source/common/objects/dobjgc.h index cbfe758f0..125070a54 100644 --- a/source/common/objects/dobjgc.h +++ b/source/common/objects/dobjgc.h @@ -186,6 +186,8 @@ class TObjPtr public: TObjPtr() = default; + TObjPtr(const TObjPtr &q) = delete; + TObjPtr(T q) noexcept : pp(q) { diff --git a/source/core/actorlist.cpp b/source/core/actorlist.cpp index 75ff3b5a7..28c3fa66d 100644 --- a/source/core/actorlist.cpp +++ b/source/core/actorlist.cpp @@ -32,7 +32,6 @@ ** */ -#include #include "build.h" #include "coreactor.h" #include "gamefuncs.h" @@ -40,7 +39,6 @@ // Doubly linked ring list of Actors -std::deque freeList; // only needed until we can get rid of the sprite array. ActorStatList statList[MAXSTATUS]; //========================================================================== @@ -100,8 +98,8 @@ static void AddStatTail(DCoreActor* actor, int statnum) assert(ValidateStatList(statnum)); actor->s().statnum = statnum; actor->link_stat = statnum; - //GC::WriteBarrier(tail); - //GC::WriteBarrier(actor); + GC::WriteBarrier(actor); + GC::WriteBarrier(tail); } //========================================================================== @@ -124,8 +122,8 @@ static void AddStatHead(DCoreActor* actor, int statnum) statList[statnum].firstEntry = actor; actor->s().statnum = statnum; actor->link_stat = statnum; - //GC::WriteBarrier(head); - //GC::WriteBarrier(actor); + GC::WriteBarrier(actor); + GC::WriteBarrier(head); } @@ -155,10 +153,8 @@ static void RemoveActorStat(DCoreActor* actor) actor->nextStat = actor->prevStat = nullptr; actor->s().statnum = MAXSTATUS; actor->link_stat = MAXSTATUS; - /* - GC::WriteBarrier(prev, next); - GC::WriteBarrier(next, prev); - */ + GC::WriteBarrier(prev); + GC::WriteBarrier(next); } //========================================================================== @@ -239,8 +235,8 @@ static void AddSectTail(DCoreActor *actor, sectortype* sect) assert(ValidateSectList(sect)); actor->s().setsector(sect); actor->link_sector = sect; - //GC::WriteBarrier(tail); - //GC::WriteBarrier(actor); + GC::WriteBarrier(actor); + GC::WriteBarrier(tail); } //========================================================================== @@ -263,8 +259,8 @@ static void AddSectHead(DCoreActor *actor, sectortype* sect) assert(ValidateSectList(sect)); actor->s().sectnum = sectnum(sect); actor->link_sector = sect; - //GC::WriteBarrier(head); - //GC::WriteBarrier(actor); + GC::WriteBarrier(actor); + GC::WriteBarrier(head); } //========================================================================== @@ -299,10 +295,8 @@ static void RemoveActorSect(DCoreActor* actor) actor->nextSect = actor->prevSect = nullptr; actor->s().setsector(nullptr); actor->link_sector = nullptr; - /* - GC::WriteBarrier(prev, next); - GC::WriteBarrier(next, prev); - */ + GC::WriteBarrier(prev); + GC::WriteBarrier(next); } //========================================================================== @@ -344,16 +338,12 @@ void ChangeActorSect(DCoreActor* actor, sectortype* sect, bool tail) // //========================================================================== -DCoreActor* InsertActor(sectortype* sector, int stat, bool tail) +DCoreActor* InsertActor(PClass* type, sectortype* sector, int stat, bool tail) { - if (freeList.empty()) - { - I_Error("Out of sprites!"); // we cannot deal with this - and most of the calling code never checks... - return nullptr; - } + assert(type->IsDescendantOf(RUNTIME_CLASS(DCoreActor))); - auto actor = freeList.back(); - freeList.pop_back(); + auto actor = static_cast(type->CreateNew()); + GC::WriteBarrier(actor); spritetype* pSprite = &actor->s(); pSprite->clear(); @@ -372,27 +362,23 @@ DCoreActor* InsertActor(sectortype* sector, int stat, bool tail) // //========================================================================== -int DeleteActor(DCoreActor* actor) +void DCoreActor::OnDestroy() { - auto sp = &actor->s(); - assert(sp->statnum >= 0 && sp->statnum < MAXSTATUS); + assert(spr.statnum >= 0 && spr.statnum < MAXSTATUS); - int stat = actor->link_stat; - RemoveActorStat(actor); + int stat = link_stat; + RemoveActorStat(this); - auto sect = actor->link_sector; + auto sect = link_sector; if (sect) { - RemoveActorSect(actor); + RemoveActorSect(this); } else { - assert(actor->prevSect == nullptr && actor->nextSect == nullptr); + assert(prevSect == nullptr && nextSect == nullptr); } Numsprites--; - actor->ClearContent(); - freeList.push_front(actor); - return 0; } //========================================================================== @@ -401,10 +387,19 @@ int DeleteActor(DCoreActor* actor) // we can use real DObject life cycle management. // //========================================================================== -static DCoreActor* actorArray[16384]; void InitSpriteLists() { + // Do not mass-destroy from the iterator. This may fail if destroying one actor results in further destructions. + TArray allActors; + TSpriteIterator it; + while (auto actor = it.Next()) + allActors.Push(actor); + for (auto& act : allActors) + { + if (!(act->ObjectFlags & OF_EuthanizeMe)) + act->Destroy(); + } for (auto& stat : statList) { stat.firstEntry = stat.lastEntry = nullptr; @@ -413,12 +408,6 @@ void InitSpriteLists() { sect.firstEntry = sect.lastEntry = nullptr; } - freeList.clear(); - for(auto& actor : actorArray) - { - actor->ClearContent(); - freeList.push_front(actor); - } Numsprites = 0; } @@ -457,16 +446,6 @@ size_t DCoreActor::PropagateMark() GC::Mark(nextStat); GC::Mark(prevSect); GC::Mark(nextSect); - return 4 + Super::PropagateMark(); + return Super::PropagateMark(); } -void SetupActors(PClass* clstype) -{ - // this is temporary until we have added proper tracking to all pointers in the games. - // Until then we have to keep a static array of actors to avoid stale references to deallocated memory. - for (int i = 0; i < 16384; i++) - { - actorArray[i] = static_cast(clstype->CreateNew()); - actorArray[i]->Release(); // no GC for this static array. - } -} \ No newline at end of file diff --git a/source/core/coreactor.h b/source/core/coreactor.h index f19ac91a8..0c0b4feca 100644 --- a/source/core/coreactor.h +++ b/source/core/coreactor.h @@ -35,6 +35,8 @@ public: spr = {}; } + virtual void BeginPlay() {} + void OnDestroy() override; size_t PropagateMark() override; bool exists() const @@ -338,12 +340,10 @@ public: using CoreSectIterator = TSectIterator; -DCoreActor* InsertActor(sectortype* sector, int stat, bool forcetail = false); -int DeleteActor(DCoreActor* actor); +DCoreActor* InsertActor(PClass* type, sectortype* sector, int stat, bool forcetail = false); void ChangeActorSect(DCoreActor* actor, sectortype* sector, bool forcetail = false); int ChangeActorStat(DCoreActor* actor, int nStatus, bool forcetail = false); void InitSpriteLists(); -void SetupActors(PClass* clstype); void SetActorZ(DCoreActor* actor, const vec3_t* newpos); diff --git a/source/core/mainloop.cpp b/source/core/mainloop.cpp index 0e9f3a651..3234bff29 100644 --- a/source/core/mainloop.cpp +++ b/source/core/mainloop.cpp @@ -373,6 +373,19 @@ static void GameTicker() break; } + + GC::CheckGC(); + // Do some more aggressive GC maintenance when the game ticker is inactive. + if ((gamestate != GS_LEVEL && gamestate != GS_TITLELEVEL) || paused) + { + size_t ac = max(10, GC::AllocCount); + for (size_t i = 0; i < ac; i++) + { + if (!GC::CheckGC()) break; + } + } + + } diff --git a/source/core/maploader.cpp b/source/core/maploader.cpp index d5b7b2321..fcf59eb0c 100644 --- a/source/core/maploader.cpp +++ b/source/core/maploader.cpp @@ -529,6 +529,11 @@ void setWallSectors() void MarkMap() { + for (auto& stat : statList) + { + GC::Mark(stat.firstEntry); + GC::Mark(stat.lastEntry); + } for (auto& sect : sectors()) { GC::Mark(sect.firstEntry); diff --git a/source/games/blood/src/blood.cpp b/source/games/blood/src/blood.cpp index b207580ba..3dd821f24 100644 --- a/source/games/blood/src/blood.cpp +++ b/source/games/blood/src/blood.cpp @@ -75,7 +75,7 @@ size_t DBloodActor::PropagateMark() { condition[0].Mark(); condition[1].Mark(); - return 2 + Super::PropagateMark(); + return Super::PropagateMark(); } static void markgcroots() @@ -535,7 +535,6 @@ void GameInterface::loadPalette(void) void GameInterface::app_init() { - SetupActors(RUNTIME_CLASS(DBloodActor)); GC::AddMarkerFunc(markgcroots); InitCheats(); memcpy(&gGameOptions, &gSingleGameOptions, sizeof(GAMEOPTIONS)); diff --git a/source/games/blood/src/db.cpp b/source/games/blood/src/db.cpp index 7a3e0179c..80e8594e1 100644 --- a/source/games/blood/src/db.cpp +++ b/source/games/blood/src/db.cpp @@ -40,7 +40,7 @@ BEGIN_BLD_NS DBloodActor* InsertSprite(sectortype* pSector, int nStat) { - auto act = static_cast(::InsertActor(pSector, nStat)); + auto act = static_cast(::InsertActor(RUNTIME_CLASS(DBloodActor), pSector, nStat)); auto pSprite = &act->s(); pSprite->cstat = 128; pSprite->clipdist = 32; @@ -58,7 +58,7 @@ int DeleteSprite(DBloodActor* actor) for (auto& ctrl : gPlayerCtrl) if (ctrl.qavScene.initiator == actor) ctrl.qavScene.initiator = nullptr; #endif - ::DeleteActor(actor); + actor->Destroy(); return 0; } diff --git a/source/games/duke/src/actors.cpp b/source/games/duke/src/actors.cpp index b09680995..786ad4949 100644 --- a/source/games/duke/src/actors.cpp +++ b/source/games/duke/src/actors.cpp @@ -78,7 +78,7 @@ void deletesprite(DDukeActor *const actor) S_StopSound(actor->s->lotag, actor); else S_RelinkActorSound(actor, nullptr); - ::DeleteActor(actor); + actor->Destroy(); } //--------------------------------------------------------------------------- diff --git a/source/games/duke/src/game.cpp b/source/games/duke/src/game.cpp index 7472281c4..a4cef69ee 100644 --- a/source/games/duke/src/game.cpp +++ b/source/games/duke/src/game.cpp @@ -67,7 +67,7 @@ size_t DDukeActor::PropagateMark() { var.Mark(); } - return uservars.Size() + Super::PropagateMark(); + return Super::PropagateMark(); } static void markgcroots() @@ -334,7 +334,6 @@ int GameInterface::GetCurrentSkill() void GameInterface::app_init() { - SetupActors(RUNTIME_CLASS(DDukeActor)); GC::AddMarkerFunc(markgcroots); if (isRR()) C_SetNotifyFontScale(0.5); diff --git a/source/games/duke/src/premap.cpp b/source/games/duke/src/premap.cpp index c7163f4dc..d9e58b955 100644 --- a/source/games/duke/src/premap.cpp +++ b/source/games/duke/src/premap.cpp @@ -947,7 +947,7 @@ static TArray spawnactors(SpawnSpriteDef& sprites) continue; } auto sprt = &sprites.sprites[i]; - auto actor = static_cast(InsertActor(sprt->sector(), sprt->statnum)); + auto actor = static_cast(InsertActor(RUNTIME_CLASS(DDukeActor), sprt->sector(), sprt->statnum)); spawns[j++] = actor; actor->spr = sprites.sprites[i]; actor->s = &actor->spr; diff --git a/source/games/duke/src/spawn.cpp b/source/games/duke/src/spawn.cpp index dacd5cbc0..56772d3ea 100644 --- a/source/games/duke/src/spawn.cpp +++ b/source/games/duke/src/spawn.cpp @@ -53,7 +53,7 @@ DDukeActor* EGS(sectortype* whatsectp, int s_x, int s_y, int s_z, int s_pn, int8 { // sector pointer must be strictly validated here or the engine will crash. if (whatsectp == nullptr || !validSectorIndex(sectnum(whatsectp))) return nullptr; - auto act = static_cast(::InsertActor(whatsectp, s_ss)); + auto act = static_cast(::InsertActor(RUNTIME_CLASS(DDukeActor), whatsectp, s_ss)); if (act == nullptr) return nullptr; act->s = &act->spr; diff --git a/source/games/exhumed/src/exhumed.cpp b/source/games/exhumed/src/exhumed.cpp index b88a93994..2c57b15b7 100644 --- a/source/games/exhumed/src/exhumed.cpp +++ b/source/games/exhumed/src/exhumed.cpp @@ -74,23 +74,19 @@ size_t MarkRunlist(); static void markgcroots() { - size_t num = MarkMove(); - num += MarkBullets(); - num += MarkInput(); - num += MarkItems(); - num += MarkLighting(); - num += MarkObjects(); - num += MarkPlayers(); - num += MarkQueen(); - num += MarkRa(); - num += MarkSnake(); - num += MarkRunlist(); + MarkBullets(); + MarkInput(); + MarkItems(); + MarkLighting(); + MarkObjects(); + MarkPlayers(); + MarkQueen(); + MarkRa(); + MarkSnake(); + MarkRunlist(); GC::Mark(bestTarget); GC::Mark(pSpiritSprite); - num += 2; - - Printf("%d objects marked\n", num); } static MapRecord* NextMap; @@ -502,7 +498,6 @@ static void SetTileNames() void GameInterface::app_init() { - SetupActors(RUNTIME_CLASS(DExhumedActor)); GC::AddMarkerFunc(markgcroots); @@ -544,7 +539,7 @@ void DeleteActor(DExhumedActor* actor) bestTarget = nullptr; } - ::DeleteActor(actor); + actor->Destroy(); } diff --git a/source/games/exhumed/src/move.cpp b/source/games/exhumed/src/move.cpp index ec0d534dc..5989c934c 100644 --- a/source/games/exhumed/src/move.cpp +++ b/source/games/exhumed/src/move.cpp @@ -485,7 +485,7 @@ int GetActorHeight(DExhumedActor* actor) DExhumedActor* insertActor(sectortype* s, int st) { - return static_cast(::InsertActor(s, st)); + return static_cast(::InsertActor(RUNTIME_CLASS(DExhumedActor), s, st)); } diff --git a/source/games/sw/src/draw.cpp b/source/games/sw/src/draw.cpp index c97cffc96..dec5e3cd9 100644 --- a/source/games/sw/src/draw.cpp +++ b/source/games/sw/src/draw.cpp @@ -1279,7 +1279,7 @@ PostDraw(void) while (auto actor = it.Next()) { actor->clearUser(); - ::DeleteActor(actor); + actor->Destroy(); } } diff --git a/source/games/sw/src/game.cpp b/source/games/sw/src/game.cpp index 4d151affe..1e0fd53e7 100644 --- a/source/games/sw/src/game.cpp +++ b/source/games/sw/src/game.cpp @@ -233,7 +233,6 @@ void GameInterface::LoadGameTextures() void GameInterface::app_init() { - SetupActors(RUNTIME_CLASS(DSWActor)); GC::AddMarkerFunc(markgcroots); GameTicRate = TICS_PER_SEC / synctics; diff --git a/source/games/sw/src/rooms.cpp b/source/games/sw/src/rooms.cpp index 033a57605..38d9664c2 100644 --- a/source/games/sw/src/rooms.cpp +++ b/source/games/sw/src/rooms.cpp @@ -56,7 +56,7 @@ bool FAF_DebugView = false; DSWActor* insertActor(sectortype* sect, int statnum) { - auto pActor = static_cast(::InsertActor(sect, statnum)); + auto pActor = static_cast(::InsertActor(RUNTIME_CLASS(DSWActor), sect, statnum)); auto pSprite = &pActor->s(); pSprite->x = pSprite->y = pSprite->z = 0; diff --git a/source/games/sw/src/sprite.cpp b/source/games/sw/src/sprite.cpp index 357e9e8f0..3d2e15db6 100644 --- a/source/games/sw/src/sprite.cpp +++ b/source/games/sw/src/sprite.cpp @@ -770,7 +770,7 @@ void KillActor(DSWActor* actor) FVector3 pos = GetSoundPos(&actor->s().pos); soundEngine->RelinkSound(SOURCE_Actor, &actor->s(), nullptr, &pos); - ::DeleteActor(actor); + actor->Destroy(); // shred your garbage sp->clear();