Skip to content

Commit f283caf

Browse files
perf: batch of optimisations
significant chunk has been implemented from a MCLE pr MCLCE/MinecraftConsoles#978
1 parent 6cc0098 commit f283caf

8 files changed

Lines changed: 325 additions & 340 deletions

File tree

targets/minecraft/client/renderer/Chunk.cpp

Lines changed: 75 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -28,15 +28,13 @@
2828
int Chunk::updates = 0;
2929

3030
#if defined(_LARGE_WORLDS)
31-
thread_local uint8_t* Chunk::m_tlsTileIds = nullptr;
31+
static thread_local unsigned char s_tlsTileIds[16 * 16 * Level::maxBuildHeight];
3232

33-
void Chunk::CreateNewThreadStorage() {
34-
m_tlsTileIds = new unsigned char[16 * 16 * Level::maxBuildHeight];
35-
}
33+
void Chunk::CreateNewThreadStorage() {}
3634

37-
void Chunk::ReleaseThreadStorage() { delete m_tlsTileIds; }
35+
void Chunk::ReleaseThreadStorage() {}
3836

39-
uint8_t* Chunk::GetTileIdsStorage() { return m_tlsTileIds; }
37+
uint8_t* Chunk::GetTileIdsStorage() { return s_tlsTileIds; }
4038
#else
4139
// 4J Stu - Don't want this when multi-threaded
4240
Tesselator* Chunk::t = Tesselator::getInstance();
@@ -272,19 +270,33 @@ void Chunk::rebuild() {
272270
static unsigned char tileIds[16 * 16 * Level::maxBuildHeight];
273271
#endif
274272
std::vector<uint8_t> tileArray(16 * 16 * Level::maxBuildHeight);
275-
level->getChunkAt(x, z)->getBlockData(tileArray);
273+
LevelChunk* sourceChunk = level->getChunkAt(x, z);
274+
275+
if (sourceChunk == nullptr) {
276+
// Level chunk not loaded yet - treat as empty
277+
for (int currentLayer = 0; currentLayer < 2; currentLayer++) {
278+
levelRenderer->setGlobalChunkFlag(this->x, this->y, this->z, level,
279+
LevelRenderer::CHUNK_FLAG_EMPTY0,
280+
currentLayer);
281+
PlatformRenderer.CBuffClear(lists + currentLayer);
282+
}
283+
284+
levelRenderer->setGlobalChunkFlag(this->x, this->y, this->z, level,
285+
LevelRenderer::CHUNK_FLAG_NOTSKYLIT);
286+
levelRenderer->setGlobalChunkFlag(this->x, this->y, this->z, level,
287+
LevelRenderer::CHUNK_FLAG_COMPILED);
288+
289+
return;
290+
}
291+
292+
sourceChunk->getBlockData(tileArray);
276293
memcpy(
277294
tileIds, tileArray.data(),
278295
16 * 16 * Level::maxBuildHeight); // 4J - TODO - now our data has been
279296
// re-arranged, we could just extra
280297
// the vertical slice of this chunk
281298
// rather than the whole thing
282299

283-
LevelSource* region =
284-
new Region(level, x0 - r, y0 - r, z0 - r, x1 + r, y1 + r, z1 + r, r);
285-
TileRenderer* tileRenderer =
286-
new TileRenderer(region, this->x, this->y, this->z, tileIds);
287-
288300
// AP - added a caching system for Chunk::rebuild to take advantage of
289301
// Basically we're storing of copy of the tileIDs array inside the region so
290302
// that calls to Region::getTile can grab data more quickly from this array
@@ -299,6 +311,16 @@ void Chunk::rebuild() {
299311
// tesselateInWorld in the unoptimised version of this function fall
300312
// into this category. By far the largest category of these are tiles in
301313
// solid regions of rock.
314+
static thread_local auto isOccluder = [] {
315+
std::array<bool, 256> table{};
316+
for (int id = 1; id < 256; id++) {
317+
Tile* tile = Tile::tiles[id];
318+
if (tile != nullptr && tile->isSolidRender()) table[id] = true;
319+
}
320+
table[255] = true; // already-marked-invisible
321+
return table;
322+
}();
323+
302324
bool empty = true;
303325
{
304326
FRAME_PROFILE_SCOPE(ChunkPrepass);
@@ -319,50 +341,37 @@ void Chunk::rebuild() {
319341
(indexY + 0))];
320342
if (tileId > 0) empty = false;
321343

322-
// Don't bother trying to work out neighbours for this tile
323-
// if we are at the edge of the chunk - apart from the very
324-
// bottom of the world where we shouldn't ever be able to
325-
// see
344+
// Don't bother trying to work out neighbours for this
345+
// tile if we are at the edge of the chunk - apart from
346+
// the very bottom of the world where we shouldn't ever
347+
// be able to see
326348
if (yy == (Level::maxBuildHeight - 1)) continue;
327349
if ((xx == 0) || (xx == 15)) continue;
328350
if ((zz == 0) || (zz == 15)) continue;
329351

330-
// Establish whether this tile and its neighbours are all
331-
// made of rock, dirt, unbreakable tiles, or have already
332-
// been determined to meet this criteria themselves and have
333-
// a tile of 255 set.
334-
if (!((tileId == Tile::stone_Id) ||
335-
(tileId == Tile::dirt_Id) ||
336-
(tileId == Tile::unbreakable_Id) || (tileId == 255)))
337-
continue;
338-
tileId = tileIds[offset + (((xx - 1) << 11) |
339-
((zz + 0) << 7) | (indexY + 0))];
340-
if (!((tileId == Tile::stone_Id) ||
341-
(tileId == Tile::dirt_Id) ||
342-
(tileId == Tile::unbreakable_Id) || (tileId == 255)))
352+
// Establish whether this tile and its neighbours are
353+
// all occluders using lookup table
354+
if (!isOccluder[tileId]) continue;
355+
if (!isOccluder[tileIds[offset + (((xx - 1) << 11) |
356+
((zz + 0) << 7) |
357+
(indexY + 0))]])
343358
continue;
344-
tileId = tileIds[offset + (((xx + 1) << 11) |
345-
((zz + 0) << 7) | (indexY + 0))];
346-
if (!((tileId == Tile::stone_Id) ||
347-
(tileId == Tile::dirt_Id) ||
348-
(tileId == Tile::unbreakable_Id) || (tileId == 255)))
359+
if (!isOccluder[tileIds[offset + (((xx + 1) << 11) |
360+
((zz + 0) << 7) |
361+
(indexY + 0))]])
349362
continue;
350-
tileId = tileIds[offset + (((xx + 0) << 11) |
351-
((zz - 1) << 7) | (indexY + 0))];
352-
if (!((tileId == Tile::stone_Id) ||
353-
(tileId == Tile::dirt_Id) ||
354-
(tileId == Tile::unbreakable_Id) || (tileId == 255)))
363+
if (!isOccluder[tileIds[offset + (((xx + 0) << 11) |
364+
((zz - 1) << 7) |
365+
(indexY + 0))]])
355366
continue;
356-
tileId = tileIds[offset + (((xx + 0) << 11) |
357-
((zz + 1) << 7) | (indexY + 0))];
358-
if (!((tileId == Tile::stone_Id) ||
359-
(tileId == Tile::dirt_Id) ||
360-
(tileId == Tile::unbreakable_Id) || (tileId == 255)))
367+
if (!isOccluder[tileIds[offset + (((xx + 0) << 11) |
368+
((zz + 1) << 7) |
369+
(indexY + 0))]])
361370
continue;
362-
// Treat the bottom of the world differently - we shouldn't
363-
// ever be able to look up at this, so consider tiles as
364-
// invisible if they are surrounded on sides other than the
365-
// bottom
371+
// Treat the bottom of the world differently - we
372+
// shouldn't ever be able to look up at this, so
373+
// consider tiles as invisible if they are surrounded on
374+
// sides other than the bottom
366375
if (yy > 0) {
367376
int indexYMinusOne = yy - 1;
368377
int yMinusOneOffset = 0;
@@ -373,13 +382,10 @@ void Chunk::rebuild() {
373382
yMinusOneOffset =
374383
Level::COMPRESSED_CHUNK_SECTION_TILES;
375384
}
376-
tileId = tileIds[yMinusOneOffset + (((xx + 0) << 11) |
377-
((zz + 0) << 7) |
378-
indexYMinusOne)];
379-
if (!((tileId == Tile::stone_Id) ||
380-
(tileId == Tile::dirt_Id) ||
381-
(tileId == Tile::unbreakable_Id) ||
382-
(tileId == 255)))
385+
if (!isOccluder[tileIds[yMinusOneOffset +
386+
(((xx + 0) << 11) |
387+
((zz + 0) << 7) |
388+
indexYMinusOne)]])
383389
continue;
384390
}
385391
int indexYPlusOne = yy + 1;
@@ -389,16 +395,13 @@ void Chunk::rebuild() {
389395
indexYPlusOne -= Level::COMPRESSED_CHUNK_SECTION_HEIGHT;
390396
yPlusOneOffset = Level::COMPRESSED_CHUNK_SECTION_TILES;
391397
}
392-
tileId = tileIds[yPlusOneOffset + (((xx + 0) << 11) |
393-
((zz + 0) << 7) |
394-
indexYPlusOne)];
395-
if (!((tileId == Tile::stone_Id) ||
396-
(tileId == Tile::dirt_Id) ||
397-
(tileId == Tile::unbreakable_Id) || (tileId == 255)))
398+
if (!isOccluder[tileIds[yPlusOneOffset + (((xx + 0) << 11) |
399+
((zz + 0) << 7) |
400+
indexYPlusOne)]])
398401
continue;
399402

400-
// This tile is surrounded. Flag it as not requiring to be
401-
// rendered by setting its id to 255.
403+
// This tile is surrounded. Flag it as not requiring to
404+
// be rendered by setting its id to 255.
402405
tileIds[offset + (((xx + 0) << 11) | ((zz + 0) << 7) |
403406
(indexY + 0))] = 0xff;
404407
}
@@ -424,13 +427,14 @@ void Chunk::rebuild() {
424427
LevelRenderer::CHUNK_FLAG_COMPILED);
425428
#endif
426429

427-
delete region;
428-
delete tileRenderer;
429430
return;
430431
}
431432
// 4J - optimisation ends
432433
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
433434

435+
Region region(level, x0 - r, y0 - r, z0 - r, x1 + r, y1 + r, z1 + r, r);
436+
TileRenderer tileRenderer(&region, this->x, this->y, this->z, tileIds);
437+
434438
Tesselator::Bounds bounds; // 4J MGH - added
435439
{
436440
// this was the old default clip bounds for the chunk, set in
@@ -465,14 +469,15 @@ void Chunk::rebuild() {
465469

466470
// 4J - get tile from those copied into our local array in
467471
// earlier optimisation
468-
unsigned char tileId =
472+
const unsigned char tileId =
469473
tileIds[offset +
470474
(((x - x0) << 11) | ((z - z0) << 7) | indexY)];
471475
// If flagged as not visible, drop out straight away
472-
if (tileId == 0xff) continue;
476+
if (tileId == 0xff) [[unlikely]]
477+
continue;
473478
// int tileId =
474479
// region->getTile(x,y,z);
475-
if (tileId > 0) {
480+
if (tileId > 0) [[unlikely]] {
476481
if (!started) {
477482
started = true;
478483

@@ -487,7 +492,7 @@ void Chunk::rebuild() {
487492
Tile* tile = Tile::tiles[tileId];
488493
if (currentLayer == 0 && tile->isEntityTile()) {
489494
std::shared_ptr<TileEntity> et =
490-
region->getTileEntity(x, y, z);
495+
region.getTileEntity(x, y, z);
491496
if (TileEntityRenderDispatcher::instance
492497
->hasRenderer(et)) {
493498
renderableTileEntities.push_back(et);
@@ -499,7 +504,7 @@ void Chunk::rebuild() {
499504
renderNextLayer = true;
500505
} else if (renderLayer == currentLayer) {
501506
rendered |=
502-
tileRenderer->tesselateInWorld(tile, x, y, z);
507+
tileRenderer.tesselateInWorld(tile, x, y, z);
503508
}
504509
}
505510
}
@@ -548,9 +553,6 @@ void Chunk::rebuild() {
548553
levelRenderer->setGlobalChunkConnectivity(globalIdx, conn);
549554
#endif
550555

551-
delete tileRenderer;
552-
delete region;
553-
554556
// 4J - have rewritten the way that tile entities are stored globally to
555557
// make it work more easily with split screen. Chunks are now stored
556558
// globally in the levelrenderer, in a hashmap with a special key made up

0 commit comments

Comments
 (0)