diff --git a/Minecraft.Client/Chunk.cpp b/Minecraft.Client/Chunk.cpp index 99933db32..5c32a5c4c 100644 --- a/Minecraft.Client/Chunk.cpp +++ b/Minecraft.Client/Chunk.cpp @@ -78,6 +78,7 @@ void Chunk::setPos(int x, int y, int z) clipChunk->zm = zm; clipChunk->globalIdx = LevelRenderer::getGlobalIndexForChunk(x, y, z, level); + int globalIdx = clipChunk->globalIdx; #if 1 // 4J - we're not using offsetted renderlists anymore, so just set the full position of this chunk into x/y/zRenderOffs where @@ -126,6 +127,11 @@ void Chunk::setPos(int x, int y, int z) // If we're the first thing to be referencing this, mark it up as dirty to get rebuilt if( refCount == 1 ) { + int listBase = levelRenderer->allocateListBaseForGlobalIdx(globalIdx); + if (listBase < 0) + { + __debugbreak(); + } // printf("Setting %d %d %d dirty [%d]\n",x,y,z, idx); // Chunks being made dirty in this way can be very numerous (eg the full visible area of the world at start up, or a whole edge of the world when moving). // On account of this, don't want to stick them into our lock free queue that we would normally use for letting the render update thread know about this chunk. @@ -211,8 +217,14 @@ void Chunk::rebuild() int r = 1; - int lists = levelRenderer->getGlobalIndexForChunk(this->x,this->y,this->z,level) * 2; - lists += levelRenderer->chunkLists; + int globalIdx = levelRenderer->getGlobalIndexForChunk(this->x, this->y, this->z, level); + int listBase = levelRenderer->getListBaseForGlobalIdx(globalIdx); + if (listBase < 0) + { + __debugbreak(); + PIXEndNamedEvent(); + return; + } PIXEndNamedEvent(); @@ -324,7 +336,7 @@ void Chunk::rebuild() for (int currentLayer = 0; currentLayer < 2; currentLayer++) { levelRenderer->setGlobalChunkFlag(this->x, this->y, this->z, level, LevelRenderer::CHUNK_FLAG_EMPTY0, currentLayer); - RenderManager.CBuffClear(lists + currentLayer); + RenderManager.CBuffClear(listBase + currentLayer); } delete region; @@ -381,7 +393,7 @@ void Chunk::rebuild() started = true; MemSect(31); - glNewList(lists + currentLayer, GL_COMPILE); + glNewList(listBase + currentLayer, GL_COMPILE); MemSect(0); glPushMatrix(); glDepthMask(true); // 4J added @@ -459,12 +471,12 @@ void Chunk::rebuild() { // 4J - added - clear any renderer data associated with this unused list levelRenderer->setGlobalChunkFlag(this->x, this->y, this->z, level, LevelRenderer::CHUNK_FLAG_EMPTY0, currentLayer); - RenderManager.CBuffClear(lists + currentLayer); + RenderManager.CBuffClear(listBase + currentLayer); } if((currentLayer==0)&&(!renderNextLayer)) { levelRenderer->setGlobalChunkFlag(this->x, this->y, this->z, level, LevelRenderer::CHUNK_FLAG_EMPTY1); - RenderManager.CBuffClear(lists + 1); + RenderManager.CBuffClear(listBase + 1); break; } } @@ -670,8 +682,12 @@ void Chunk::rebuild_SPU() Region region(level, x0 - r, y0 - r, z0 - r, x1 + r, y1 + r, z1 + r); TileRenderer tileRenderer(®ion); - int lists = levelRenderer->getGlobalIndexForChunk(this->x,this->y,this->z,level) * 2; - lists += levelRenderer->chunkLists; + int globalIdx = levelRenderer->getGlobalIndexForChunk(this->x, this->y, this->z, level); + int listBase = levelRenderer->getListBaseForGlobalIdx(globalIdx); + if (listBase < 0) + { + return; + } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // 4J - optimisation begins. @@ -700,7 +716,7 @@ void Chunk::rebuild_SPU() bool rendered = false; { - glNewList(lists + currentLayer, GL_COMPILE); + glNewList(listBase + currentLayer, GL_COMPILE); MemSect(0); glPushMatrix(); glDepthMask(true); // 4J added @@ -796,7 +812,7 @@ void Chunk::rebuild_SPU() { // 4J - added - clear any renderer data associated with this unused list levelRenderer->setGlobalChunkFlag(this->x, this->y, this->z, level, LevelRenderer::CHUNK_FLAG_EMPTY0, currentLayer); - RenderManager.CBuffClear(lists + currentLayer); + RenderManager.CBuffClear(listBase + currentLayer); } } @@ -965,18 +981,13 @@ void Chunk::reset() 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 ) + if (refCount == 0) { - int lists = levelRenderer->getGlobalIndexForChunk(x, y, z, level) * 2; - if(lists >= 0) + int globalIdx = levelRenderer->getGlobalIndexForChunk(x, y, z, level); + if (globalIdx >= 0) { - lists += levelRenderer->chunkLists; - for (int i = 0; i < 2; i++) - { - // 4J - added - clear any renderer data associated with this unused list - RenderManager.CBuffClear(lists + i); - } levelRenderer->setGlobalChunkFlags(x, y, z, level, 0); + levelRenderer->freeListBaseForGlobalIdx(globalIdx); } } LeaveCriticalSection(&levelRenderer->m_csDirtyChunks); @@ -995,11 +1006,12 @@ int Chunk::getList(int layer) { if (!clipChunk->visible) return -1; - int lists = levelRenderer->getGlobalIndexForChunk(x, y, z,level) * 2; - lists += levelRenderer->chunkLists; + int globalIdx = levelRenderer->getGlobalIndexForChunk(x, y, z, level); + int listBase = levelRenderer->getListBaseForGlobalIdx(globalIdx); + if (listBase < 0) return -1; - bool empty = levelRenderer->getGlobalChunkFlag(x, y, z, level, LevelRenderer::CHUNK_FLAG_EMPTY0, layer); - if (!empty) return lists + layer; + bool empty = levelRenderer->getGlobalChunkFlag(x, y, z, level, LevelRenderer::CHUNK_FLAG_EMPTY0, layer); + if (!empty) return listBase + layer; return -1; } diff --git a/Minecraft.Client/LevelRenderer.cpp b/Minecraft.Client/LevelRenderer.cpp index 983600783..e3758de58 100644 --- a/Minecraft.Client/LevelRenderer.cpp +++ b/Minecraft.Client/LevelRenderer.cpp @@ -77,6 +77,9 @@ C4JThread *LevelRenderer::rebuildThreads[MAX_CHUNK_REBUILD_THREADS]; C4JThread::EventArray *LevelRenderer::s_rebuildCompleteEvents; C4JThread::Event *LevelRenderer::s_activationEventA[MAX_CHUNK_REBUILD_THREADS]; +unordered_map m_globalIdxToListBase; +vector m_freeListBases; + // This defines the maximum size of renderable level, must be big enough to cope with actual size of level + view distance at each side // so that we can render the "infinite" sea at the edges. Currently defined as: const int overworldSize = LEVEL_MAX_WIDTH + LevelRenderer::PLAYER_VIEW_DISTANCE + LevelRenderer::PLAYER_VIEW_DISTANCE; @@ -149,10 +152,18 @@ LevelRenderer::LevelRenderer(Minecraft *mc, Textures *textures) this->mc = mc; this->textures = textures; - chunkLists = MemoryTracker::genLists(getGlobalChunkCount()*2); // *2 here is because there is one renderlist per chunk here for each of the opaque & transparent layers + chunkLists = MemoryTracker::genLists(MAX_RENDER_CHUNK_SLOTS * 2); // *2 here is because there is one renderlist per chunk here for each of the opaque & transparent layers globalChunkFlags = new unsigned char[getGlobalChunkCount()]; memset(globalChunkFlags, 0, getGlobalChunkCount()); + m_globalIdxToListBase.clear(); + m_freeListBases.clear(); + m_freeListBases.reserve(MAX_RENDER_CHUNK_SLOTS); + for (int i = 0; i < MAX_RENDER_CHUNK_SLOTS; ++i) + { + m_freeListBases.push_back(chunkLists + i * 2); + } + starList = MemoryTracker::genLists(4); glPushMatrix(); @@ -250,6 +261,43 @@ LevelRenderer::LevelRenderer(Minecraft *mc, Textures *textures) #endif // __PS3__ } +int LevelRenderer::allocateListBaseForGlobalIdx(int globalIdx) +{ + AUTO_VAR(it, m_globalIdxToListBase.find(globalIdx)); + if (it != m_globalIdxToListBase.end()) + return it->second; + + if (m_freeListBases.empty()) + return -1; + + int listBase = m_freeListBases.back(); + m_freeListBases.pop_back(); + m_globalIdxToListBase[globalIdx] = listBase; + return listBase; +} + +void LevelRenderer::freeListBaseForGlobalIdx(int globalIdx) +{ + AUTO_VAR(it, m_globalIdxToListBase.find(globalIdx)); + if (it == m_globalIdxToListBase.end()) + return; + + int listBase = it->second; + RenderManager.CBuffClear(listBase + 0); + RenderManager.CBuffClear(listBase + 1); + + m_freeListBases.push_back(listBase); + m_globalIdxToListBase.erase(it); +} + +int LevelRenderer::getListBaseForGlobalIdx(int globalIdx) const +{ + AUTO_VAR(it, m_globalIdxToListBase.find(globalIdx)); + if (it == m_globalIdxToListBase.end()) + return -1; + return it->second; +} + void LevelRenderer::renderStars() { Random random = Random(10842); @@ -787,8 +835,10 @@ int LevelRenderer::renderChunks(int from, int to, int layer, double alpha) if( ( globalChunkFlags[pClipChunk->globalIdx] & emptyFlag ) == emptyFlag ) continue; // Check that this particular layer isn't empty // List can be calculated directly from the chunk's global idex - int list = pClipChunk->globalIdx * 2 + layer; - list += chunkLists; + int listBase = getListBaseForGlobalIdx(pClipChunk->globalIdx); + if (listBase < 0) + continue; + int list = listBase + layer; if(RenderManager.CBuffCall(list, first)) { diff --git a/Minecraft.Client/LevelRenderer.h b/Minecraft.Client/LevelRenderer.h index c0a98bba6..8e20e912f 100644 --- a/Minecraft.Client/LevelRenderer.h +++ b/Minecraft.Client/LevelRenderer.h @@ -210,6 +210,9 @@ class LevelRenderer : public LevelListener #ifdef _LARGE_WORLDS static const int PLAYER_VIEW_DISTANCE = 18; // Straight line distance from centre to extent of visible world static const int PLAYER_RENDER_AREA = (PLAYER_VIEW_DISTANCE * PLAYER_VIEW_DISTANCE * 4); + + static const int MAX_CHUNKS_PER_PLAYER = PLAYER_RENDER_AREA * CHUNK_Y_COUNT; + static const int MAX_RENDER_CHUNK_SLOTS = MAX_CHUNKS_PER_PLAYER * 4; #else static const int PLAYER_RENDER_AREA = 400; #endif @@ -271,6 +274,10 @@ class LevelRenderer : public LevelListener static void staticCtor(); static int rebuildChunkThreadProc(LPVOID lpParam); + int allocateListBaseForGlobalIdx(int globalIdx); + void freeListBaseForGlobalIdx(int globalIdx); + int getListBaseForGlobalIdx(int globalIdx) const; + CRITICAL_SECTION m_csChunkFlags; #endif void nonStackDirtyChunksAdded(); diff --git a/Minecraft.World/ChunkSource.h b/Minecraft.World/ChunkSource.h index 242f30a0f..399448f0b 100644 --- a/Minecraft.World/ChunkSource.h +++ b/Minecraft.World/ChunkSource.h @@ -7,7 +7,7 @@ class TilePos; // The maximum number of chunks that we can store #ifdef _LARGE_WORLDS // 4J Stu - Our default map (at zoom level 3) is 1024x1024 blocks (or 64 chunks) -#define LEVEL_MAX_WIDTH (5*64) //(6*54) +#define LEVEL_MAX_WIDTH (64*64) //(6*54) I have tested up to 512x64, but the game was using 16gb of ram to do so, so probably don't set it this high. I am leaving this at 64*64 for now, but feel free to change it lol #else #define LEVEL_MAX_WIDTH 54 #endif diff --git a/Minecraft.World/Dimension.cpp b/Minecraft.World/Dimension.cpp index 35e66698b..607425a95 100644 --- a/Minecraft.World/Dimension.cpp +++ b/Minecraft.World/Dimension.cpp @@ -67,29 +67,42 @@ Dimension::~Dimension() delete biomeSource; } -ChunkSource *Dimension::createRandomLevelSource() const +ChunkSource* Dimension::createRandomLevelSource() const { + ChunkSource* src = NULL; + #ifdef _OVERRIDE_HEIGHTMAP - // 4J Stu - Added to enable overriding the heightmap from a loaded in data file - if(app.DebugSettingsOn() && app.GetGameSettingsDebugMask(ProfileManager.GetPrimaryPad())&(1L<getSeed(), level->getLevelData()->isGenerateMapFeatures()); + src = new CustomLevelSource(level, level->getSeed(), level->getLevelData()->isGenerateMapFeatures()); } else #endif - if (levelType == LevelType::lvl_flat) + if (levelType == LevelType::lvl_flat) + { + src = new FlatLevelSource(level, level->getSeed(), level->getLevelData()->isGenerateMapFeatures()); + } + else + { + src = new RandomLevelSource(level, level->getSeed(), level->getLevelData()->isGenerateMapFeatures()); + } + + if (src != NULL) { - return new FlatLevelSource(level, level->getSeed(), level->getLevelData()->isGenerateMapFeatures()); - } - else - { - return new RandomLevelSource(level, level->getSeed(), level->getLevelData()->isGenerateMapFeatures()); + src->m_XZSize = level->getLevelData()->getXZSize(); } + + return src; } -ChunkSource *Dimension::createFlatLevelSource() const +ChunkSource* Dimension::createFlatLevelSource() const { - return new FlatLevelSource(level, level->getSeed(), level->getLevelData()->isGenerateMapFeatures()); + ChunkSource* src = new FlatLevelSource(level, level->getSeed(), level->getLevelData()->isGenerateMapFeatures()); + if (src != NULL) + { + src->m_XZSize = level->getLevelData()->getXZSize(); + } + return src; } ChunkStorage *Dimension::createStorage(File dir) diff --git a/Minecraft.World/RandomLevelSource.cpp b/Minecraft.World/RandomLevelSource.cpp index 4e26134a1..aa518e1a8 100644 --- a/Minecraft.World/RandomLevelSource.cpp +++ b/Minecraft.World/RandomLevelSource.cpp @@ -573,9 +573,12 @@ doubleArray RandomLevelSource::getHeights(doubleArray buffer, int x, int y, int } -bool RandomLevelSource::hasChunk(int x, int y) +bool RandomLevelSource::hasChunk(int x, int z) { - return true; + const int half = m_XZSize / 2; + + return (x >= -half && x < half && + z >= -half && z < half); } void RandomLevelSource::calcWaterDepths(ChunkSource *parent, int xt, int zt)