diff --git a/Minecraft.Client/Rendering/Chunk.cpp b/Minecraft.Client/Rendering/Chunk.cpp index ff0550717..9b294d5e8 100644 --- a/Minecraft.Client/Rendering/Chunk.cpp +++ b/Minecraft.Client/Rendering/Chunk.cpp @@ -8,6 +8,7 @@ #include "../../Minecraft.World/Headers/net.minecraft.world.level.tile.entity.h" #include "LevelRenderer.h" #include "../Utils/FrameProfiler.h" +#include #ifdef __PS3__ #include "../Platform/PS3/SPU_Tasks/ChunkUpdate/ChunkRebuildData.h" @@ -36,6 +37,66 @@ Tesselator* Chunk::t = Tesselator::getInstance(); #endif LevelRenderer* Chunk::levelRenderer; +void Chunk::reconcileRenderableTileEntities( + const std::vector >& renderableTileEntities) { + int key = + levelRenderer->getGlobalIndexForChunk(this->x, this->y, this->z, level); + AUTO_VAR(it, globalRenderableTileEntities->find(key)); + if (!renderableTileEntities.empty()) { + std::unordered_set currentRenderableTileEntitySet; + currentRenderableTileEntitySet.reserve(renderableTileEntities.size()); + for (size_t i = 0; i < renderableTileEntities.size(); i++) { + currentRenderableTileEntitySet.insert(renderableTileEntities[i].get()); + } + + if (it != globalRenderableTileEntities->end()) { + LevelRenderer::RenderableTileEntityBucket& existingBucket = + it->second; + + for (AUTO_VAR(it2, existingBucket.tiles.begin()); + it2 != existingBucket.tiles.end(); it2++) { + TileEntity* tileEntity = (*it2).get(); + if (currentRenderableTileEntitySet.find(tileEntity) == + currentRenderableTileEntitySet.end()) { + (*it2)->setRenderRemoveStage( + TileEntity::e_RenderRemoveStageFlaggedAtChunk); + levelRenderer->queueRenderableTileEntityForRemoval_Locked( + key, tileEntity); + } else { + (*it2)->setRenderRemoveStage( + TileEntity::e_RenderRemoveStageKeep); + } + } + + for (size_t i = 0; i < renderableTileEntities.size(); i++) { + renderableTileEntities[i]->setRenderRemoveStage( + TileEntity::e_RenderRemoveStageKeep); + if (existingBucket.indexByTile.find(renderableTileEntities[i].get()) == + existingBucket.indexByTile.end()) { + levelRenderer->addRenderableTileEntity_Locked( + key, renderableTileEntities[i]); + } + } + } else { + for (size_t i = 0; i < renderableTileEntities.size(); i++) { + renderableTileEntities[i]->setRenderRemoveStage( + TileEntity::e_RenderRemoveStageKeep); + levelRenderer->addRenderableTileEntity_Locked( + key, renderableTileEntities[i]); + } + } + } else if (it != globalRenderableTileEntities->end()) { + for (AUTO_VAR(it2, it->second.tiles.begin()); + it2 != it->second.tiles.end(); + it2++) { + (*it2)->setRenderRemoveStage( + TileEntity::e_RenderRemoveStageFlaggedAtChunk); + levelRenderer->queueRenderableTileEntityForRemoval_Locked(key, + (*it2).get()); + } + } +} + // TODO - 4J see how input entity vector is set up and decide what way is best // to pass this to the function Chunk::Chunk(Level* level, LevelRenderer::rteMap& globalRenderableTileEntities, @@ -519,57 +580,8 @@ void Chunk::rebuild() { // from the dimension and chunk position (using same index as is used for // global flags) #if 1 - int key = - levelRenderer->getGlobalIndexForChunk(this->x, this->y, this->z, level); EnterCriticalSection(globalRenderableTileEntities_cs); - if (renderableTileEntities.size()) { - AUTO_VAR(it, globalRenderableTileEntities->find(key)); - if (it != globalRenderableTileEntities->end()) { - // We've got some renderable tile entities that we want associated - // with this chunk, and an existing list of things that used to be. - // We need to flag any that we don't need any more to be removed, - // keep those that we do, and add any new ones - - // First pass - flag everything already existing to be removed - for (AUTO_VAR(it2, it->second.begin()); it2 != it->second.end(); - it2++) { - (*it2)->setRenderRemoveStage( - TileEntity::e_RenderRemoveStageFlaggedAtChunk); - } - - // Now go through the current list. If these are already in the - // list, then unflag the remove flag. If they aren't, then add - for (int i = 0; i < renderableTileEntities.size(); i++) { - AUTO_VAR(it2, find(it->second.begin(), it->second.end(), - renderableTileEntities[i])); - if (it2 == it->second.end()) { - (*globalRenderableTileEntities)[key].push_back( - renderableTileEntities[i]); - } else { - (*it2)->setRenderRemoveStage( - TileEntity::e_RenderRemoveStageKeep); - } - } - } else { - // Easy case - nothing already existing for this chunk. Add them all - // in. - for (int i = 0; i < renderableTileEntities.size(); i++) { - (*globalRenderableTileEntities)[key].push_back( - renderableTileEntities[i]); - } - } - } else { - // Another easy case - we don't want any renderable tile entities - // associated with this chunk. Flag all to be removed. - AUTO_VAR(it, globalRenderableTileEntities->find(key)); - if (it != globalRenderableTileEntities->end()) { - for (AUTO_VAR(it2, it->second.begin()); it2 != it->second.end(); - it2++) { - (*it2)->setRenderRemoveStage( - TileEntity::e_RenderRemoveStageFlaggedAtChunk); - } - } - } + reconcileRenderableTileEntities(renderableTileEntities); LeaveCriticalSection(globalRenderableTileEntities_cs); PIXEndNamedEvent(); #else @@ -831,57 +843,8 @@ void Chunk::rebuild_SPU() { // from the dimension and chunk position (using same index as is used for // global flags) #if 1 - int key = - levelRenderer->getGlobalIndexForChunk(this->x, this->y, this->z, level); EnterCriticalSection(globalRenderableTileEntities_cs); - if (renderableTileEntities.size()) { - AUTO_VAR(it, globalRenderableTileEntities->find(key)); - if (it != globalRenderableTileEntities->end()) { - // We've got some renderable tile entities that we want associated - // with this chunk, and an existing list of things that used to be. - // We need to flag any that we don't need any more to be removed, - // keep those that we do, and add any new ones - - // First pass - flag everything already existing to be removed - for (AUTO_VAR(it2, it->second.begin()); it2 != it->second.end(); - it2++) { - (*it2)->setRenderRemoveStage( - TileEntity::e_RenderRemoveStageFlaggedAtChunk); - } - - // Now go through the current list. If these are already in the - // list, then unflag the remove flag. If they aren't, then add - for (int i = 0; i < renderableTileEntities.size(); i++) { - AUTO_VAR(it2, find(it->second.begin(), it->second.end(), - renderableTileEntities[i])); - if (it2 == it->second.end()) { - (*globalRenderableTileEntities)[key].push_back( - renderableTileEntities[i]); - } else { - (*it2)->setRenderRemoveStage( - TileEntity::e_RenderRemoveStageKeep); - } - } - } else { - // Easy case - nothing already existing for this chunk. Add them all - // in. - for (int i = 0; i < renderableTileEntities.size(); i++) { - (*globalRenderableTileEntities)[key].push_back( - renderableTileEntities[i]); - } - } - } else { - // Another easy case - we don't want any renderable tile entities - // associated with this chunk. Flag all to be removed. - AUTO_VAR(it, globalRenderableTileEntities->find(key)); - if (it != globalRenderableTileEntities->end()) { - for (AUTO_VAR(it2, it->second.begin()); it2 != it->second.end(); - it2++) { - (*it2)->setRenderRemoveStage( - TileEntity::e_RenderRemoveStageFlaggedAtChunk); - } - } - } + reconcileRenderableTileEntities(renderableTileEntities); LeaveCriticalSection(globalRenderableTileEntities_cs); #else // Find the removed ones: @@ -1108,15 +1071,19 @@ uint64_t Chunk::computeConnectivity(const uint8_t* tileIds) { } void Chunk::reset() { if (assigned) { + int oldKey = -1; + bool retireRenderableTileEntities = false; + EnterCriticalSection(&levelRenderer->m_csDirtyChunks); + oldKey = levelRenderer->getGlobalIndexForChunk(x, y, z, level); unsigned char refCount = levelRenderer->decGlobalChunkRefCount(x, y, z, level); assigned = false; // printf("\t\t [dec] refcount %d at %d, %d, //%d\n",refCount,x,y,z); - if (refCount == 0) { - int lists = - levelRenderer->getGlobalIndexForChunk(x, y, z, level) * 2; + if (refCount == 0 && oldKey != -1) { + retireRenderableTileEntities = true; + int lists = oldKey * 2; if (lists >= 0) { lists += levelRenderer->chunkLists; for (int i = 0; i < 2; i++) { @@ -1128,6 +1095,10 @@ void Chunk::reset() { } } LeaveCriticalSection(&levelRenderer->m_csDirtyChunks); + + if (retireRenderableTileEntities) { + levelRenderer->retireRenderableTileEntitiesForChunkKey(oldKey); + } } clipChunk->visible = false; diff --git a/Minecraft.Client/Rendering/Chunk.h b/Minecraft.Client/Rendering/Chunk.h index c524c69ce..fded430cf 100644 --- a/Minecraft.Client/Rendering/Chunk.h +++ b/Minecraft.Client/Rendering/Chunk.h @@ -69,6 +69,8 @@ public: private: void translateToPos(); + void reconcileRenderableTileEntities( + const std::vector >& renderableTileEntities); public: void makeCopyForRebuild(Chunk* source); diff --git a/Minecraft.Client/Rendering/LevelRenderer.cpp b/Minecraft.Client/Rendering/LevelRenderer.cpp index a91df96dd..763611cca 100644 --- a/Minecraft.Client/Rendering/LevelRenderer.cpp +++ b/Minecraft.Client/Rendering/LevelRenderer.cpp @@ -410,8 +410,12 @@ void LevelRenderer::setLevel(int playerIndex, MultiPlayerLevel* level) { // tile entities in the world dissappear We should only do this when // actually exiting the game, so only when the primary player sets there // level to NULL - if (playerIndex == ProfileManager.GetPrimaryPad()) + if (playerIndex == ProfileManager.GetPrimaryPad()) { + EnterCriticalSection(&m_csRenderableTileEntities); renderableTileEntities.clear(); + m_renderableTileEntitiesPendingRemoval.clear(); + LeaveCriticalSection(&m_csRenderableTileEntities); + } } } @@ -641,34 +645,13 @@ void LevelRenderer::renderEntities(Vec3* cam, Culler* culler, float a) { // Don't render if it isn't in the same dimension as this player if (!isGlobalIndexInSameDimension(idx, level[playerIndex])) continue; - for (AUTO_VAR(it2, it->second.begin()); it2 != it->second.end(); + for (AUTO_VAR(it2, it->second.tiles.begin()); + it2 != it->second.tiles.end(); it2++) { TileEntityRenderDispatcher::instance->render(*it2, a); } } - // Now consider if any of these renderable tile entities have been flagged - // for removal, and if so, remove - for (AUTO_VAR(it, renderableTileEntities.begin()); - it != renderableTileEntities.end();) { - int idx = it->first; - - for (AUTO_VAR(it2, it->second.begin()); it2 != it->second.end();) { - // If it has been flagged for removal, remove - if ((*it2)->shouldRemoveForRender()) { - it2 = it->second.erase(it2); - } else { - it2++; - } - } - - // If there aren't any entities left for this key, then delete the key - if (it->second.size() == 0) { - it = renderableTileEntities.erase(it); - } else { - it++; - } - } LeaveCriticalSection(&m_csRenderableTileEntities); @@ -4181,16 +4164,87 @@ unsigned char LevelRenderer::decGlobalChunkRefCount(int x, int y, int z, } } +void LevelRenderer::queueRenderableTileEntityForRemoval_Locked( + int key, TileEntity* tileEntity) { + m_renderableTileEntitiesPendingRemoval[key].insert(tileEntity); +} + +void LevelRenderer::addRenderableTileEntity_Locked( + int key, const std::shared_ptr& tileEntity) { + RenderableTileEntityBucket& bucket = renderableTileEntities[key]; + TileEntity* tileEntityPtr = tileEntity.get(); + if (bucket.indexByTile.find(tileEntityPtr) != bucket.indexByTile.end()) { + return; + } + + size_t index = bucket.tiles.size(); + bucket.tiles.push_back(tileEntity); + bucket.indexByTile.insert(std::make_pair(tileEntityPtr, index)); +} + +void LevelRenderer::eraseRenderableTileEntity_Locked( + RenderableTileEntityBucket& bucket, TileEntity* tileEntity) { + auto it = bucket.indexByTile.find(tileEntity); + if (it == bucket.indexByTile.end()) { + return; + } + + size_t index = it->second; + size_t lastIndex = bucket.tiles.size() - 1; + if (index != lastIndex) { + std::shared_ptr moved = bucket.tiles[lastIndex]; + bucket.tiles[index] = moved; + bucket.indexByTile[moved.get()] = index; + } + + bucket.tiles.pop_back(); + bucket.indexByTile.erase(it); +} + +void LevelRenderer::retireRenderableTileEntitiesForChunkKey(int key) { + if (key == -1) return; + + EnterCriticalSection(&m_csRenderableTileEntities); + renderableTileEntities.erase(key); + m_renderableTileEntitiesPendingRemoval.erase(key); + LeaveCriticalSection(&m_csRenderableTileEntities); +} + // 4J added void LevelRenderer::fullyFlagRenderableTileEntitiesToBeRemoved() { + FRAME_PROFILE_SCOPE(RenderableTileEntityCleanup); + EnterCriticalSection(&m_csRenderableTileEntities); - AUTO_VAR(itChunkEnd, renderableTileEntities.end()); - for (AUTO_VAR(it, renderableTileEntities.begin()); it != itChunkEnd; it++) { - AUTO_VAR(itTEEnd, it->second.end()); - for (AUTO_VAR(it2, it->second.begin()); it2 != itTEEnd; it2++) { - (*it2)->upgradeRenderRemoveStage(); + if (m_renderableTileEntitiesPendingRemoval.empty()) { + LeaveCriticalSection(&m_csRenderableTileEntities); + return; + } + + auto itKeyEnd = m_renderableTileEntitiesPendingRemoval.end(); + for (auto itKey = m_renderableTileEntitiesPendingRemoval.begin(); + itKey != itKeyEnd; itKey++) { + auto itChunk = renderableTileEntities.find(itKey->first); + if (itChunk == renderableTileEntities.end()) continue; + + RenderableTileEntityBucket& bucket = itChunk->second; + for (AUTO_VAR(itPending, itKey->second.begin()); + itPending != itKey->second.end(); itPending++) { + if (bucket.indexByTile.find(*itPending) == bucket.indexByTile.end()) { + continue; + } + + if (!(*itPending)->finalizeRenderRemoveStage()) { + continue; + } + + eraseRenderableTileEntity_Locked(bucket, *itPending); + } + + if (bucket.tiles.empty()) { + renderableTileEntities.erase(itChunk); } } + m_renderableTileEntitiesPendingRemoval.clear(); LeaveCriticalSection(&m_csRenderableTileEntities); } diff --git a/Minecraft.Client/Rendering/LevelRenderer.h b/Minecraft.Client/Rendering/LevelRenderer.h index 081aa98b9..fbafa7ce8 100644 --- a/Minecraft.Client/Rendering/LevelRenderer.h +++ b/Minecraft.Client/Rendering/LevelRenderer.h @@ -10,6 +10,7 @@ #ifdef __PS3__ #include "../Platform/PS3/PS3Extras/C4JSpursJob.h" #endif +#include class MultiPlayerLevel; class Textures; class Chunk; @@ -163,8 +164,13 @@ public: void destroyTileProgress(int id, int x, int y, int z, int progress); void registerTextures(IconRegister* iconRegister); - typedef std::unordered_map >, - IntKeyHash, IntKeyEq> + struct RenderableTileEntityBucket { + std::vector > tiles; + std::unordered_map indexByTile; + }; + + typedef std::unordered_map rteMap; private: @@ -176,6 +182,10 @@ private: rteMap renderableTileEntities; // 4J - changed - was // std::vector, // now hashed by chunk so we can find them + typedef std::unordered_set rtePendingRemovalSet; + typedef std::unordered_map + rtePendingRemovalMap; + rtePendingRemovalMap m_renderableTileEntitiesPendingRemoval; CRITICAL_SECTION m_csRenderableTileEntities; MultiPlayerLevel* level[4]; // 4J - now one per player Textures* textures; @@ -214,6 +224,14 @@ private: std::unordered_map destroyingBlocks; Icon** breakingTextures; + void addRenderableTileEntity_Locked( + int key, const std::shared_ptr& tileEntity); + void eraseRenderableTileEntity_Locked( + RenderableTileEntityBucket& bucket, TileEntity* tileEntity); + void queueRenderableTileEntityForRemoval_Locked(int key, + TileEntity* tileEntity); + void retireRenderableTileEntitiesForChunkKey(int key); + public: void fullyFlagRenderableTileEntitiesToBeRemoved(); // 4J added diff --git a/Minecraft.Client/Utils/FrameProfiler.cpp b/Minecraft.Client/Utils/FrameProfiler.cpp index 89db484eb..53ddaa42f 100644 --- a/Minecraft.Client/Utils/FrameProfiler.cpp +++ b/Minecraft.Client/Utils/FrameProfiler.cpp @@ -44,6 +44,8 @@ constexpr std::array {Bucket::ChunkBlockFaceCull, "chunkBlockFaceCull"}, {Bucket::ChunkBlockLighting, "chunkBlockLighting"}, {Bucket::ChunkBlockEmit, "chunkBlockEmit"}, + {Bucket::RenderableTileEntityCleanup, "renderableTileEntityCleanup"}, + {Bucket::TileEntityUnloadCleanup, "tileEntityUnloadCleanup"}, {Bucket::Entity, "entities"}, {Bucket::Particle, "particles"}, {Bucket::WeatherSky, "weather"}, @@ -133,8 +135,7 @@ inline void recordWorkerBucket(Bucket bucket, std::uint64_t elapsedNs) noexcept return t_threadState.frameScopeDepth != 0; } -FRAME_PROFILER_NOINLINE -[[nodiscard]] bool computeEnabled() noexcept { +FRAME_PROFILER_NOINLINE bool computeEnabled() noexcept { const char* const envValue = std::getenv("C4J_FRAME_PROFILER"); if (envValue == nullptr) return true; return !envSaysDisabled(envValue); diff --git a/Minecraft.Client/Utils/FrameProfiler.h b/Minecraft.Client/Utils/FrameProfiler.h index 7abf56255..c22351f69 100644 --- a/Minecraft.Client/Utils/FrameProfiler.h +++ b/Minecraft.Client/Utils/FrameProfiler.h @@ -23,6 +23,8 @@ public: ChunkBlockFaceCull, ChunkBlockLighting, ChunkBlockEmit, + RenderableTileEntityCleanup, + TileEntityUnloadCleanup, Entity, Particle, WeatherSky, diff --git a/Minecraft.World/Blocks/TileEntities/TileEntity.cpp b/Minecraft.World/Blocks/TileEntities/TileEntity.cpp index 24986046f..66ae1afac 100644 --- a/Minecraft.World/Blocks/TileEntities/TileEntity.cpp +++ b/Minecraft.World/Blocks/TileEntities/TileEntity.cpp @@ -187,6 +187,15 @@ void TileEntity::upgradeRenderRemoveStage() { } } +bool TileEntity::finalizeRenderRemoveStage() { + if (renderRemoveStage == e_RenderRemoveStageFlaggedAtChunk) { + renderRemoveStage = e_RenderRemoveStageRemove; + return true; + } + + return renderRemoveStage == e_RenderRemoveStageRemove; +} + // 4J Added void TileEntity::clone(std::shared_ptr tileEntity) { tileEntity->level = this->level; @@ -195,4 +204,4 @@ void TileEntity::clone(std::shared_ptr tileEntity) { tileEntity->z = this->z; tileEntity->data = this->data; tileEntity->tile = this->tile; -} \ No newline at end of file +} diff --git a/Minecraft.World/Blocks/TileEntities/TileEntity.h b/Minecraft.World/Blocks/TileEntities/TileEntity.h index e640ae2e4..fc382b673 100644 --- a/Minecraft.World/Blocks/TileEntities/TileEntity.h +++ b/Minecraft.World/Blocks/TileEntities/TileEntity.h @@ -49,6 +49,7 @@ public: void setRenderRemoveStage(unsigned char stage); // 4J added void upgradeRenderRemoveStage(); // 4J added + bool finalizeRenderRemoveStage(); // 4J added bool shouldRemoveForRender(); // 4J added virtual Level* getLevel(); @@ -77,4 +78,4 @@ public: protected: void clone(std::shared_ptr tileEntity); -}; \ No newline at end of file +}; diff --git a/Minecraft.World/Level/Level.cpp b/Minecraft.World/Level/Level.cpp index ab251a833..06c99a2ae 100644 --- a/Minecraft.World/Level/Level.cpp +++ b/Minecraft.World/Level/Level.cpp @@ -38,6 +38,7 @@ #include "../../Minecraft.Client/Platform/Common/DLC/DLCPack.h" #include "../../Minecraft.Client/Platform/PS3/PS3Extras/ShutdownManager.h" #include "../../Minecraft.Client/MinecraftServer.h" +#include "../../Minecraft.Client/Utils/FrameProfiler.h" #include #include #include @@ -2206,19 +2207,11 @@ void Level::tickEntities() { // 4J-PB - Stuart - check this is correct here if (!tileEntitiesToUnload.empty()) { - // tileEntityList.removeAll(tileEntitiesToUnload); + FRAME_PROFILE_SCOPE(TileEntityUnloadCleanup); for (AUTO_VAR(it, tileEntityList.begin()); it != tileEntityList.end();) { - bool found = false; - for (AUTO_VAR(it2, tileEntitiesToUnload.begin()); - it2 != tileEntitiesToUnload.end(); it2++) { - if ((*it) == (*it2)) { - found = true; - break; - } - } - if (found) { + if (tileEntitiesToUnload.find(*it) != tileEntitiesToUnload.end()) { if (isClientSide) { __debugbreak(); } @@ -2731,7 +2724,9 @@ void Level::removeTileEntity(int x, int y, int z) { } void Level::markForRemoval(std::shared_ptr entity) { - tileEntitiesToUnload.push_back(entity); + EnterCriticalSection(&m_tileEntityListCS); + tileEntitiesToUnload.insert(entity); + LeaveCriticalSection(&m_tileEntityListCS); } bool Level::isSolidRenderTile(int x, int y, int z) { diff --git a/Minecraft.World/Level/Level.h b/Minecraft.World/Level/Level.h index d28d1cda5..50910ada0 100644 --- a/Minecraft.World/Level/Level.h +++ b/Minecraft.World/Level/Level.h @@ -10,6 +10,7 @@ #include "../WorldGen/Biomes/Biome.h" #include "../Util/C4JThread.h" #include +#include #ifdef __PSVITA__ #include "../../Minecraft.Client/Platform/PSVita/PSVitaExtras/CustomSet.h" @@ -118,7 +119,7 @@ public: private: std::vector > pendingTileEntities; - std::vector > tileEntitiesToUnload; + std::unordered_set > tileEntitiesToUnload; bool updatingTileEntities; public: @@ -665,3 +666,4 @@ public: bool canCreateMore(eINSTANCEOF type, ESPAWN_TYPE spawnType); }; +#include diff --git a/Minecraft.World/Level/LevelChunk.cpp b/Minecraft.World/Level/LevelChunk.cpp index 5f95fd9d2..52193665f 100644 --- a/Minecraft.World/Level/LevelChunk.cpp +++ b/Minecraft.World/Level/LevelChunk.cpp @@ -1558,13 +1558,19 @@ void LevelChunk::unload(bool unloadTileEntities) // 4J - added parameter { loaded = false; if (unloadTileEntities) { + std::vector > tileEntitiesToRemove; EnterCriticalSection(&m_csTileEntities); for (AUTO_VAR(it, tileEntities.begin()); it != tileEntities.end(); it++) { - // 4J-PB -m 1.7.3 was it->second->setRemoved(); - level->markForRemoval(it->second); + tileEntitiesToRemove.push_back(it->second); } LeaveCriticalSection(&m_csTileEntities); + + AUTO_VAR(itEnd, tileEntitiesToRemove.end()); + for (AUTO_VAR(it, tileEntitiesToRemove.begin()); it != itEnd; it++) { + // 4J-PB -m 1.7.3 was it->second->setRemoved(); + level->markForRemoval(*it); + } } #ifdef _ENTITIES_RW_SECTION