more-optimizations: first batch of optimizations at the levelrenderer

level
This commit is contained in:
JuiceyDev 2026-04-05 20:01:53 +02:00
parent 1f928fd28a
commit 8d4dd1a1d0
7 changed files with 369 additions and 188 deletions

View file

@ -13,11 +13,10 @@
#include <utility>
#include <vector>
#include "platform/sdl2/Render.h"
#include "LevelRenderer.h"
#include "app/linux/Stubs/winapi_stubs.h"
#include "app/include/FrameProfiler.h"
#include "TileRenderer.h"
#include "app/include/FrameProfiler.h"
#include "app/linux/Stubs/winapi_stubs.h"
#include "minecraft/client/renderer/Tesselator.h"
#include "minecraft/client/renderer/culling/Culler.h"
#include "minecraft/client/renderer/tileentity/TileEntityRenderDispatcher.h"
@ -29,6 +28,7 @@
#include "minecraft/world/level/tile/Tile.h"
#include "minecraft/world/level/tile/entity/TileEntity.h"
#include "minecraft/world/phys/AABB.h"
#include "platform/sdl2/Render.h"
int Chunk::updates = 0;
@ -174,6 +174,11 @@ void Chunk::setPos(int x, int y, int z) {
levelRenderer->m_csDirtyChunks);
unsigned char refCount =
levelRenderer->incGlobalChunkRefCount(x, y, z, level);
if ((clipChunk->globalIdx >= 0) &&
((size_t)clipChunk->globalIdx <
levelRenderer->m_globalClipChunks.size())) {
levelRenderer->m_globalClipChunks[clipChunk->globalIdx] = clipChunk;
}
// printf("\t\t [inc] refcount %d at %d, %d, %d\n",refCount,x,y,z);
// int idx = levelRenderer->getGlobalIndexForChunk(x, y, z, level);
@ -223,8 +228,15 @@ void Chunk::makeCopyForRebuild(Chunk* source) {
this->globalRenderableTileEntities = source->globalRenderableTileEntities;
this->globalRenderableTileEntities_cs =
source->globalRenderableTileEntities_cs;
this->buildContext = source->buildContext;
}
void Chunk::setBuildContext(const ChunkBuildContext& ctx) {
buildContext = ctx;
}
const ChunkBuildContext& Chunk::getBuildContext() const { return buildContext; }
void Chunk::rebuild() {
// if (!dirty) return;
@ -285,8 +297,8 @@ void Chunk::rebuild() {
LevelSource* region =
new Region(level, x0 - r, y0 - r, z0 - r, x1 + r, y1 + r, z1 + r, r);
TileRenderer* tileRenderer =
new TileRenderer(region, this->x, this->y, this->z, tileIds);
TileRenderer* tileRenderer = new TileRenderer(
region, this->x, this->y, this->z, tileIds, &buildContext);
// AP - added a caching system for Chunk::rebuild to take advantage of
// Basically we're storing of copy of the tileIDs array inside the region so
@ -542,10 +554,14 @@ void Chunk::rebuild() {
bb = {bounds.boundingBox[0], bounds.boundingBox[1], bounds.boundingBox[2],
bounds.boundingBox[3], bounds.boundingBox[4], bounds.boundingBox[5]};
uint64_t conn = computeConnectivity(tileIds); // pass tileIds
int globalIdx =
levelRenderer->getGlobalIndexForChunk(this->x, this->y, this->z, level);
#if defined(OCCLUSION_MODE_BFS)
uint64_t conn = computeConnectivity(tileIds); // pass tileIds
levelRenderer->setGlobalChunkConnectivity(globalIdx, conn);
#else
levelRenderer->setGlobalChunkConnectivity(globalIdx, ~0ULL);
#endif
delete tileRenderer;
delete region;
@ -590,6 +606,7 @@ float Chunk::squishedDistanceToSqr(std::shared_ptr<Entity> player) {
return xd * xd + yd * yd + zd * zd;
}
#if defined(OCCLUSION_MODE_BFS)
uint64_t Chunk::computeConnectivity(const uint8_t* tileIds) {
const int W = 16;
const int H = 16;
@ -726,6 +743,7 @@ uint64_t Chunk::computeConnectivity(const uint8_t* tileIds) {
return result;
}
#endif
void Chunk::reset() {
if (assigned) {
int oldKey = -1;
@ -742,6 +760,9 @@ void Chunk::reset() {
//%d\n",refCount,x,y,z);
if (refCount == 0 && oldKey != -1) {
retireRenderableTileEntities = true;
if ((size_t)oldKey < levelRenderer->m_globalClipChunks.size()) {
levelRenderer->m_globalClipChunks[oldKey] = nullptr;
}
int lists = oldKey * 2;
if (lists >= 0) {
lists += levelRenderer->chunkLists;

View file

@ -17,6 +17,14 @@ class Entity;
class Chunk;
class Culler;
struct ChunkBuildContext {
int64_t nowMs = 0;
uint32_t batchId = 0;
uint16_t veryNearCount = 0;
bool onlyRebuild = false;
bool deferredAtomic = false;
};
class ClipChunk {
public:
Chunk* chunk;
@ -57,7 +65,9 @@ public:
int xm, ym, zm;
AABB bb;
ClipChunk* clipChunk;
#if defined(OCCLUSION_MODE_BFS)
uint64_t computeConnectivity(const uint8_t* tileIds);
#endif
int id;
// public:
// std::vector<std::shared_ptr<TileEntity> > renderableTileEntities;
@ -67,6 +77,7 @@ private:
LevelRenderer::rteMap* globalRenderableTileEntities;
std::mutex* globalRenderableTileEntities_cs;
bool assigned;
ChunkBuildContext buildContext;
public:
Chunk(Level* level, LevelRenderer::rteMap& globalRenderableTileEntities,
@ -84,6 +95,8 @@ private:
public:
void makeCopyForRebuild(Chunk* source);
void setBuildContext(const ChunkBuildContext& ctx);
const ChunkBuildContext& getBuildContext() const;
void rebuild();
float distanceToSqr(std::shared_ptr<Entity> player) const;
float squishedDistanceToSqr(std::shared_ptr<Entity> player);

View file

@ -6,24 +6,18 @@
#include <cmath>
#include <numbers>
#include "platform/PlatformTypes.h"
#include "platform/sdl2/Input.h"
#include "platform/sdl2/Render.h"
#include "BossMobGuiInfo.h"
#include "Chunk.h"
#include "ItemInHandRenderer.h"
#include "LevelRenderer.h"
#include "Tesselator.h"
#include "app/common/App_enums.h"
#include "platform/ShutdownManager.h"
#include "app/common/src/Colours/ColourTable.h"
#include "app/linux/LinuxGame.h"
#include "app/linux/Stubs/winapi_stubs.h"
#include "app/include/BufferedImage.h"
#include "app/include/FrameProfiler.h"
#include "app/include/stubs.h"
#include "Tesselator.h"
#include "minecraft/world/level/storage/ConsoleSaveFileIO/compression.h"
#include "app/linux/LinuxGame.h"
#include "app/linux/Stubs/winapi_stubs.h"
#include "java/Class.h"
#include "java/FloatBuffer.h"
#include "java/JavaMath.h"
@ -78,10 +72,15 @@
#include "minecraft/world/level/chunk/SparseLightStorage.h"
#include "minecraft/world/level/dimension/Dimension.h"
#include "minecraft/world/level/material/Material.h"
#include "minecraft/world/level/storage/ConsoleSaveFileIO/compression.h"
#include "minecraft/world/level/tile/Tile.h"
#include "minecraft/world/phys/AABB.h"
#include "minecraft/world/phys/HitResult.h"
#include "minecraft/world/phys/Vec3.h"
#include "platform/PlatformTypes.h"
#include "platform/ShutdownManager.h"
#include "platform/sdl2/Input.h"
#include "platform/sdl2/Render.h"
bool GameRenderer::anaglyph3d = false;
int GameRenderer::anaglyphPass = 0;
@ -1115,6 +1114,7 @@ void GameRenderer::FinishedReassigning() { m_csDeleteStack.unlock(); }
int GameRenderer::runUpdate(void* lpParam) {
Minecraft* minecraft = Minecraft::GetInstance();
int64_t updatePassToken = 0;
Tesselator::CreateNewThreadStorage(1024 * 1024);
Compression::UseDefaultThreadStorage();
RenderManager.InitialiseContext();
@ -1150,8 +1150,10 @@ int GameRenderer::runUpdate(void* lpParam) {
int count = 0;
static const int MAX_DEFERRED_UPDATES = 10;
bool shouldContinue = false;
const int64_t tickNowMs = ++updatePassToken;
do {
shouldContinue = minecraft->levelRenderer->updateDirtyChunks();
shouldContinue =
minecraft->levelRenderer->updateDirtyChunks(tickNowMs);
count++;
} while (shouldContinue && count < MAX_DEFERRED_UPDATES);
@ -1421,7 +1423,8 @@ void GameRenderer::renderLevel(float a, int64_t until) {
int visibleWaterChunks =
levelRenderer->render(cameraEntity, 1, a, updateChunks);
RenderManager.StateSetBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
RenderManager.StateSetBlendFunc(GL_SRC_ALPHA,
GL_ONE_MINUS_SRC_ALPHA);
if (visibleWaterChunks > 0) {
levelRenderer->render(

View file

@ -11,27 +11,24 @@
#include <algorithm>
#include <array>
#include <atomic>
#include <cmath>
#include <mutex>
#include <numbers>
#include <ranges>
#include <utility>
#include "platform/PlatformTypes.h"
#include "platform/sdl2/Input.h"
#include "platform/sdl2/Render.h"
#include "Chunk.h"
#include "GameRenderer.h"
#include "Tesselator.h"
#include "app/common/App_enums.h"
#include "app/common/src/Audio/SoundEngine.h"
#include "app/common/src/Colours/ColourTable.h"
#include "app/common/src/Console_Debug_enum.h"
#include "app/linux/LinuxGame.h"
#include "app/include/FrameProfiler.h"
#include "app/include/MobSkinMemTextureProcessor.h"
#include "app/include/stubs.h"
#include "Tesselator.h"
#include "util/StringHelpers.h"
#include "app/linux/LinuxGame.h"
#include "java/Class.h"
#include "java/JavaMath.h"
#include "java/Random.h"
@ -111,10 +108,19 @@
#include "minecraft/world/phys/AABB.h"
#include "minecraft/world/phys/HitResult.h"
#include "minecraft/world/phys/Vec3.h"
#include "platform/PlatformTypes.h"
#include "platform/sdl2/Input.h"
#include "platform/sdl2/Render.h"
#include "util/StringHelpers.h"
class Icon;
class ItemInstance;
namespace {
std::atomic<uint32_t> s_chunkBuildBatchCounter{0};
std::atomic<int64_t> s_dirtyChunkPassToken{0};
} // namespace
// #define DISABLE_SPU_CODE
ResourceLocation LevelRenderer::MOON_LOCATION =
@ -216,6 +222,7 @@ LevelRenderer::LevelRenderer(Minecraft* mc, Textures* textures) {
dirtyChunkPresent = false;
lastDirtyChunkFound = 0;
m_dirtyChunksRequireFullScan = false;
this->mc = mc;
this->textures = textures;
@ -230,6 +237,8 @@ LevelRenderer::LevelRenderer(Minecraft* mc, Textures* textures) {
globalChunkConnectivity = new uint64_t[getGlobalChunkCount()];
memset(globalChunkConnectivity, 0xFF,
getGlobalChunkCount() * sizeof(uint64_t)); // 0xFF >> Fully open
m_globalClipChunks =
std::vector<ClipChunk*>(getGlobalChunkCount(), nullptr);
starList = MemoryTracker::genLists(4);
@ -1699,7 +1708,14 @@ void LevelRenderer::renderAdvancedClouds(float alpha) {
RenderManager.StateSetEnableViewportClipPlanes(false);
}
bool LevelRenderer::updateDirtyChunks() {
bool LevelRenderer::updateDirtyChunks(int64_t cachedNowMs) {
const int64_t nowMs =
(cachedNowMs >= 0)
? cachedNowMs
: (s_dirtyChunkPassToken.fetch_add(1, std::memory_order_relaxed) +
1);
const uint32_t chunkBuildBatchId =
s_chunkBuildBatchCounter.fetch_add(1, std::memory_order_relaxed) + 1;
#if defined(_LARGE_WORLDS)
struct NearestClipChunkSet {
std::array<std::pair<ClipChunk*, int>, MAX_CONCURRENT_CHUNK_REBUILDS>
@ -1740,6 +1756,7 @@ bool LevelRenderer::updateDirtyChunks() {
ClipChunk* nearChunk = nullptr; // Nearest chunk that is dirty
int veryNearCount = 0;
int minDistSq = 0x7fffffff; // Distances to this chunk
bool onlyRebuild = false;
std::unique_lock<std::recursive_mutex> dirtyChunksLock(m_csDirtyChunks);
// Set a flag if we should only rebuild existing chunks, not create anything
@ -1756,11 +1773,165 @@ bool LevelRenderer::updateDirtyChunks() {
}
throttle++;
*/
bool onlyRebuild = (memAlloc >= MAX_COMMANDBUFFER_ALLOCATIONS);
onlyRebuild = (memAlloc >= MAX_COMMANDBUFFER_ALLOCATIONS);
// Move any dirty chunks stored in the lock free stack into global flags
int index = 0;
bool hasNonStackDirtySignal = false;
std::vector<int> poppedDirtyIndices;
// We want to pop all the dirty chunk indices from the stack
auto evaluateSparseDirtyClipChunk = [&](ClipChunk* pClipChunk) {
if (pClipChunk == nullptr) return;
if (pClipChunk->globalIdx < 0) return;
unsigned char flags = globalChunkFlags[pClipChunk->globalIdx];
if (!(flags & CHUNK_FLAG_DIRTY)) return;
Chunk* candidateChunk = pClipChunk->chunk;
if ((candidateChunk == nullptr) ||
(candidateChunk->level == nullptr))
return;
int bestDistSq = INT_MAX;
int bestDistWeighted = INT_MAX;
bool hasRelevantPlayer = false;
for (int p = 0; p < XUSER_MAX_COUNT; ++p) {
std::shared_ptr<LocalPlayer> player = mc->localplayers[p];
if (player == nullptr) continue;
if (level[p] != candidateChunk->level) continue;
hasRelevantPlayer = true;
int xd = candidateChunk->xm - (int)player->x;
int yd = candidateChunk->ym - (int)player->y;
int zd = candidateChunk->zm - (int)player->z;
int distSq = xd * xd + yd * yd + zd * zd;
int distSqWeighted = xd * xd + yd * yd * 4 + zd * zd;
if (distSq < bestDistSq) bestDistSq = distSq;
if (distSqWeighted < bestDistWeighted)
bestDistWeighted = distSqWeighted;
}
if (!hasRelevantPlayer) return;
if ((!onlyRebuild) || (flags & CHUNK_FLAG_COMPILED) ||
(bestDistSq < 20 * 20)) {
#if defined(_LARGE_WORLDS)
bool isNearer = nearestClipChunks.wouldAccept(bestDistWeighted);
#else
bool isNearer = bestDistWeighted < minDistSq;
#endif
#if defined(_CRITICAL_CHUNKS)
bool isCritical = (globalChunkFlags[pClipChunk->globalIdx] &
CHUNK_FLAG_CRITICAL) != 0;
if ((!veryNearCount && isNearer) ||
((bestDistSq < 20 * 20) && isCritical))
#else
if (isNearer)
#endif
{
int chunkY = candidateChunk->y / CHUNK_SIZE;
if ((chunkY >= 0) && (chunkY < CHUNK_Y_COUNT)) {
LevelChunk* lc = candidateChunk->level->getChunkAt(
candidateChunk->x, candidateChunk->z);
if (!lc->isRenderChunkEmpty(chunkY * CHUNK_SIZE)) {
nearChunk = pClipChunk;
minDistSq = bestDistWeighted;
#if defined(_LARGE_WORLDS)
nearestClipChunks.insert(nearChunk, minDistSq);
#endif
} else {
candidateChunk->clearDirty();
globalChunkFlags[pClipChunk->globalIdx] |=
CHUNK_FLAG_EMPTYBOTH;
}
}
}
#if defined(_CRITICAL_CHUNKS)
if ((bestDistSq < 20 * 20) &&
(globalChunkFlags[pClipChunk->globalIdx] &
CHUNK_FLAG_CRITICAL))
#else
if (bestDistSq < 20 * 20)
#endif
{
veryNearCount++;
}
}
};
auto evaluateDirtyClipChunkForPlayer = [&](ClipChunk* pClipChunk,
int px, int py, int pz) {
if (pClipChunk == nullptr) return;
if (pClipChunk->globalIdx < 0) return;
unsigned char flags = globalChunkFlags[pClipChunk->globalIdx];
if (!(flags & CHUNK_FLAG_DIRTY)) return;
Chunk* candidateChunk = pClipChunk->chunk;
if ((candidateChunk == nullptr) ||
(candidateChunk->level == nullptr))
return;
int xd = candidateChunk->xm - px;
int yd = candidateChunk->ym - py;
int zd = candidateChunk->zm - pz;
int distSq = xd * xd + yd * yd + zd * zd;
int distSqWeighted = xd * xd + yd * yd * 4 + zd * zd;
if ((!onlyRebuild) || (flags & CHUNK_FLAG_COMPILED) ||
(distSq < 20 * 20)) {
#if defined(_LARGE_WORLDS)
bool isNearer = nearestClipChunks.wouldAccept(distSqWeighted);
#else
bool isNearer = distSqWeighted < minDistSq;
#endif
#if defined(_CRITICAL_CHUNKS)
if ((!veryNearCount && isNearer) ||
(distSq < 20 * 20 &&
(globalChunkFlags[pClipChunk->globalIdx] &
CHUNK_FLAG_CRITICAL)))
#else
if (isNearer)
#endif
{
int chunkY = candidateChunk->y / CHUNK_SIZE;
if ((chunkY >= 0) && (chunkY < CHUNK_Y_COUNT)) {
LevelChunk* lc = candidateChunk->level->getChunkAt(
candidateChunk->x, candidateChunk->z);
if (!lc->isRenderChunkEmpty(chunkY * CHUNK_SIZE)) {
nearChunk = pClipChunk;
minDistSq = distSqWeighted;
#if defined(_LARGE_WORLDS)
nearestClipChunks.insert(nearChunk, minDistSq);
#endif
} else {
candidateChunk->clearDirty();
globalChunkFlags[pClipChunk->globalIdx] |=
CHUNK_FLAG_EMPTYBOTH;
}
}
}
#if defined(_CRITICAL_CHUNKS)
if (distSq < 20 * 20 &&
((globalChunkFlags[pClipChunk->globalIdx] &
CHUNK_FLAG_CRITICAL)))
#else
if (distSq < 20 * 20)
#endif
{
veryNearCount++;
}
}
};
do {
// See comment on dirtyChunksLockFreeStack.Push() regarding details
// of this casting/subtracting -2.
@ -1775,7 +1946,10 @@ bool LevelRenderer::updateDirtyChunks() {
true; // 1 is a special value passed to let this thread
// know that a chunk which isn't on this stack has
// been set to dirty
else if (index > 1) {
if (index == 1) {
hasNonStackDirtySignal = true;
m_dirtyChunksRequireFullScan = true;
} else if (index > 1) {
int i2 = index - 2;
if (i2 >= DIMENSION_OFFSETS[2]) {
i2 -= DIMENSION_OFFSETS[2];
@ -1798,148 +1972,69 @@ bool LevelRenderer::updateDirtyChunks() {
#endif
dirtyChunkPresent = true;
poppedDirtyIndices.push_back(index - 2);
}
} while (index);
// Only bother searching round all the chunks if we have some dirty
// chunk(s)
if (dirtyChunkPresent) {
lastDirtyChunkFound = System::currentTimeMillis();
lastDirtyChunkFound = nowMs;
// Find nearest chunk that is dirty
for (int p = 0; p < XUSER_MAX_COUNT; p++) {
// It's possible that the localplayers member can be set to
// nullptr on the main thread when a player chooses to exit the
// game So take a reference to the player object now. As it is a
// shared_ptr it should live as long as we need it
std::shared_ptr<LocalPlayer> player = mc->localplayers[p];
if (player == nullptr) continue;
if (chunks[p].empty()) continue;
if (level[p] == nullptr) continue;
if (chunks[p].size() != xChunks * zChunks * CHUNK_Y_COUNT)
continue;
int px = (int)player->x;
int py = (int)player->y;
int pz = (int)player->z;
bool useSparsePath = (!m_dirtyChunksRequireFullScan) &&
(!hasNonStackDirtySignal) &&
(!poppedDirtyIndices.empty());
// app.DebugPrintf("!! %d %d %d, %d %d %d
//{%d,%d}
//",px,py,pz,stackChunkDirty,nonStackChunkDirty,onlyRebuild,
// xChunks, zChunks);
if (useSparsePath) {
std::sort(poppedDirtyIndices.begin(), poppedDirtyIndices.end());
poppedDirtyIndices.erase(std::unique(poppedDirtyIndices.begin(),
poppedDirtyIndices.end()),
poppedDirtyIndices.end());
int considered = 0;
int wouldBeNearButEmpty = 0;
for (int x = 0; x < xChunks; x++) {
for (int z = 0; z < zChunks; z++) {
for (int y = 0; y < CHUNK_Y_COUNT; y++) {
ClipChunk* pClipChunk =
&chunks[p][(z * yChunks + y) * xChunks + x];
// Get distance to this chunk - deliberately not
// calling the chunk's method of doing this to avoid
// overheads (passing entitie, type conversion etc.)
// that this involves
int xd = pClipChunk->xm - px;
int yd = pClipChunk->ym - py;
int zd = pClipChunk->zm - pz;
int distSq = xd * xd + yd * yd + zd * zd;
int distSqWeighted =
xd * xd + yd * yd * 4 +
zd * zd; // Weighting against y to prioritise
// things in same x/z plane as player
// first
for (int dirtyIdx : poppedDirtyIndices) {
if (dirtyIdx < 0) continue;
if ((size_t)dirtyIdx >= m_globalClipChunks.size()) continue;
evaluateSparseDirtyClipChunk(m_globalClipChunks[dirtyIdx]);
}
} else {
bool sawAnyDirtyFlag = false;
for (int p = 0; p < XUSER_MAX_COUNT; p++) {
std::shared_ptr<LocalPlayer> player = mc->localplayers[p];
if (player == nullptr) continue;
if (chunks[p].empty()) continue;
if (level[p] == nullptr) continue;
if (chunks[p].size() != xChunks * zChunks * CHUNK_Y_COUNT)
continue;
if (globalChunkFlags[pClipChunk->globalIdx] &
CHUNK_FLAG_DIRTY) {
if ((!onlyRebuild) ||
globalChunkFlags[pClipChunk->globalIdx] &
CHUNK_FLAG_COMPILED ||
(distSq <
20 * 20)) // Always rebuild really near
// things or else building (say)
// at tower up into empty blocks
// when we are low on memory
// will not create render data
{
considered++;
// Is this chunk nearer than our nearest?
#if defined(_LARGE_WORLDS)
bool isNearer =
nearestClipChunks.wouldAccept(
distSqWeighted);
#else
bool isNearer = distSqWeighted < minDistSq;
#endif
const int px = (int)player->x;
const int py = (int)player->y;
const int pz = (int)player->z;
#if defined(_CRITICAL_CHUNKS)
// AP - this will make sure that if a
// deferred grouping has started, only
// critical chunks go into that grouping,
// even if a non-critical chunk is closer.
if ((!veryNearCount && isNearer) ||
(distSq < 20 * 20 &&
(globalChunkFlags[pClipChunk
->globalIdx] &
CHUNK_FLAG_CRITICAL)))
#else
if (isNearer)
#endif
{
// At this point we've got a chunk that
// we would like to consider for
// rendering, at least based on its
// proximity to the player(s). Its
// *quite* quick to generate empty
// render data for render chunks, but if
// we let the rebuilding do that then
// the after rebuilding we will have to
// start searching for the next nearest
// chunk from scratch again. Instead,
// its better to detect empty chunks at
// this stage, flag them up as not dirty
// (and empty), and carry on. The
// levelchunk's isRenderChunkEmpty
// method can be quite optimal as it can
// make use of the chunk's data
// compression to detect emptiness
// without actually testing as many data
// items as uncompressed data would.
Chunk* chunk = pClipChunk->chunk;
LevelChunk* lc = level[p]->getChunkAt(
chunk->x, chunk->z);
if (!lc->isRenderChunkEmpty(y * 16)) {
nearChunk = pClipChunk;
minDistSq = distSqWeighted;
#if defined(_LARGE_WORLDS)
nearestClipChunks.insert(nearChunk,
minDistSq);
#endif
} else {
chunk->clearDirty();
globalChunkFlags[pClipChunk
->globalIdx] |=
CHUNK_FLAG_EMPTYBOTH;
wouldBeNearButEmpty++;
}
}
// app.DebugPrintf("!! %d %d %d, %d %d %d
//{%d,%d}
//",px,py,pz,stackChunkDirty,nonStackChunkDirty,onlyRebuild,
// xChunks, zChunks);
#if defined(_CRITICAL_CHUNKS)
// AP - is the chunk near and also critical
if (distSq < 20 * 20 &&
((globalChunkFlags[pClipChunk
->globalIdx] &
CHUNK_FLAG_CRITICAL)))
#else
if (distSq < 20 * 20)
#endif
{
veryNearCount++;
}
for (int x = 0; x < xChunks; x++) {
for (int z = 0; z < zChunks; z++) {
for (int y = 0; y < CHUNK_Y_COUNT; y++) {
ClipChunk* pClipChunk =
&chunks[p][(z * yChunks + y) * xChunks + x];
if (pClipChunk->globalIdx < 0) continue;
if (globalChunkFlags[pClipChunk->globalIdx] &
CHUNK_FLAG_DIRTY) {
sawAnyDirtyFlag = true;
}
evaluateDirtyClipChunkForPlayer(pClipChunk, px,
py, pz);
}
}
}
}
// app.DebugPrintf("[%d,%d,%d]\n",nearestClipChunks.empty(),considered,wouldBeNearButEmpty);
if (!sawAnyDirtyFlag) {
m_dirtyChunksRequireFullScan = false;
}
}
}
}
@ -1973,7 +2068,15 @@ bool LevelRenderer::updateDirtyChunks() {
// whilst we are doing this copy. The copy will then be
// guaranteed to be consistent whilst rebuilding takes place
// outside of that lock.
const ChunkBuildContext buildContext = {
nowMs,
chunkBuildBatchId,
static_cast<uint16_t>(std::min(veryNearCount, 0xffff)),
onlyRebuild,
veryNearCount > 0,
};
permaChunk[index].makeCopyForRebuild(chunk);
permaChunk[index].setBuildContext(buildContext);
++index;
}
dirtyChunksLock.unlock();
@ -2064,7 +2167,15 @@ bool LevelRenderer::updateDirtyChunks() {
// means that any chunks can't be repositioned whilst we are doing
// this copy. The copy will then be guaranteed to be consistent
// whilst rebuilding takes place outside of that lock.
const ChunkBuildContext buildContext = {
nowMs,
chunkBuildBatchId,
static_cast<uint16_t>(std::min(veryNearCount, 0xffff)),
onlyRebuild,
veryNearCount > 0,
};
permaChunk.makeCopyForRebuild(chunk);
permaChunk.setBuildContext(buildContext);
dirtyChunksLock.unlock();
}
// static int64_t totalTime = 0;
@ -2086,8 +2197,8 @@ bool LevelRenderer::updateDirtyChunks() {
// Nothing to do - clear flags that there are things to process, unless
// it's been a while since we found any dirty chunks in which case force
// a check next time through
if ((System::currentTimeMillis() - lastDirtyChunkFound) >
FORCE_DIRTY_CHUNK_CHECK_PERIOD_MS) {
if ((nowMs - lastDirtyChunkFound) >
FORCE_DIRTY_CHUNK_CHECK_PERIOD_PASSES) {
dirtyChunkPresent = true;
} else {
dirtyChunkPresent = false;
@ -2323,6 +2434,12 @@ void LevelRenderer::setDirty(int x0, int y0, int z0, int x1, int y1, int z1,
int _y1 = Mth::intFloorDiv(y1, CHUNK_SIZE);
int _z1 = Mth::intFloorDiv(z1, CHUNK_XZSIZE);
#if !defined(_CRITICAL_CHUNKS)
std::vector<intptr_t> pendingDirtyIndices;
pendingDirtyIndices.reserve(
((_x1 - _x0 + 1) * (_y1 - _y0 + 1) * (_z1 - _z0 + 1)));
#endif
for (int x = _x0; x <= _x1; x++) {
for (int y = _y0; y <= _y1; y++) {
for (int z = _z0; z <= _z1; z++) {
@ -2393,8 +2510,8 @@ void LevelRenderer::setDirty(int x0, int y0, int z0, int x1, int y1, int z1,
dirtyChunksLockFreeStack.Push((int*)(index));
#else
dirtyChunksLockFreeStack.Push(
(int*)(intptr_t)(uintptr_t)(index + 2));
pendingDirtyIndices.push_back(
(intptr_t)(uintptr_t)(index + 2));
#endif
}
// setGlobalChunkFlag(x * 16, y *
@ -2402,6 +2519,19 @@ void LevelRenderer::setDirty(int x0, int y0, int z0, int x1, int y1, int z1,
}
}
}
#if !defined(_CRITICAL_CHUNKS)
if (!pendingDirtyIndices.empty()) {
std::sort(pendingDirtyIndices.begin(), pendingDirtyIndices.end());
pendingDirtyIndices.erase(
std::unique(pendingDirtyIndices.begin(), pendingDirtyIndices.end()),
pendingDirtyIndices.end());
for (intptr_t encodedIndex : pendingDirtyIndices) {
dirtyChunksLockFreeStack.Push((int*)encodedIndex);
}
}
#endif
}
void LevelRenderer::tileChanged(int x, int y, int z) {

View file

@ -1,15 +1,15 @@
#pragma once
#include "OffsettedRenderList.h"
#include "app/include/NetTypes.h"
#include "app/include/SkinBox.h"
#include "app/include/XboxStubs.h"
#include "OffsettedRenderList.h"
#include "platform/C4JThread.h"
#include "util/Definitions.h"
#include "java/JavaIntHash.h"
#include "minecraft/core/particles/ParticleTypes.h"
#include "minecraft/world/level/Level.h"
#include "minecraft/world/level/LevelListener.h"
#include "minecraft/world/phys/AABB.h"
#include "platform/C4JThread.h"
#include "util/Definitions.h"
class ClipChunk;
class HitResult;
@ -119,7 +119,7 @@ public:
void renderClouds(float alpha);
bool isInCloud(double x, double y, double z, float alpha);
void renderAdvancedClouds(float alpha);
bool updateDirtyChunks();
bool updateDirtyChunks(int64_t cachedNowMs = -1);
public:
void renderHit(std::shared_ptr<Player> player, HitResult* h, int mode,
@ -225,6 +225,8 @@ private:
uint64_t getGlobalChunkConnectivity(int index);
std::vector<ClipChunk*> m_bfsGrid;
std::vector<uint8_t> m_bfsVisitedFaces[4];
std::vector<ClipChunk*> m_globalClipChunks;
bool m_dirtyChunksRequireFullScan;
std::unordered_map<int, BlockDestructionProgress*> destroyingBlocks;
Icon** breakingTextures;
@ -355,7 +357,7 @@ public:
bool dirtyChunkPresent;
int64_t lastDirtyChunkFound;
static const int FORCE_DIRTY_CHUNK_CHECK_PERIOD_MS = 250;
static const int FORCE_DIRTY_CHUNK_CHECK_PERIOD_PASSES = 64;
#if defined(_LARGE_WORLDS)
static const int MAX_CONCURRENT_CHUNK_REBUILDS = 4;

View file

@ -13,13 +13,13 @@
#include <cmath>
#include <numbers>
#include "platform/sdl2/Render.h"
#include "Chunk.h"
#include "EntityTileRenderer.h"
#include "GameRenderer.h"
#include "Tesselator.h"
#include "app/common/App_enums.h"
#include "app/common/src/Colours/ColourTable.h"
#include "app/include/FrameProfiler.h"
#include "Tesselator.h"
#include "minecraft/Direction.h"
#include "minecraft/Facing.h"
#include "minecraft/SharedConstants.h"
@ -69,6 +69,7 @@
#include "minecraft/world/level/tile/piston/PistonBaseTile.h"
#include "minecraft/world/level/tile/piston/PistonExtensionTile.h"
#include "minecraft/world/phys/Vec3.h"
#include "platform/sdl2/Render.h"
bool TileRenderer::fancy = true;
@ -101,6 +102,7 @@ void TileRenderer::_init() {
yMin = 0;
zMin = 0;
cache = nullptr;
buildContext = nullptr;
}
bool TileRenderer::isTranslucentAt(LevelSource* level, int x, int y, int z) {
@ -204,7 +206,8 @@ int TileRenderer::getLightColor(Tile* tt, LevelSource* level, int x, int y,
}
TileRenderer::TileRenderer(LevelSource* level, int xMin, int yMin, int zMin,
unsigned char* tileIds) {
unsigned char* tileIds,
const ChunkBuildContext* buildContext) {
this->level = level;
_init();
this->xMin = xMin;
@ -214,6 +217,7 @@ TileRenderer::TileRenderer(LevelSource* level, int xMin, int yMin, int zMin,
this->yMin2 = yMin - 2;
this->zMin2 = zMin - 2;
this->tileIds = tileIds;
this->buildContext = buildContext;
cache = new unsigned int[32 * 32 * 32];
memset(cache, 0, 32 * 32 * 32 * sizeof(unsigned int));
}
@ -232,6 +236,10 @@ TileRenderer::TileRenderer() {
_init();
}
void TileRenderer::setBuildContext(const ChunkBuildContext* buildContext) {
this->buildContext = buildContext;
}
void TileRenderer::setFixedTexture(Icon* fixedTexture) {
this->fixedTexture = fixedTexture;
}
@ -5254,10 +5262,10 @@ bool TileRenderer::tesselateBlockInWorldWithAmbienceOcclusionTexLighting(
ll0yZ = getShadeBrightness(tt, level, pX, pY, pZ + 1);
llXy0 = getShadeBrightness(tt, level, pX + 1, pY, pZ);
bool llTransXy0 = Tile::transculent[level->getTile(pX + 1, pY - 1, pZ)];
bool llTransxy0 = Tile::transculent[level->getTile(pX - 1, pY - 1, pZ)];
bool llTrans0yZ = Tile::transculent[level->getTile(pX, pY - 1, pZ + 1)];
bool llTrans0yz = Tile::transculent[level->getTile(pX, pY - 1, pZ - 1)];
bool llTransXy0 = isTranslucentAt(level, pX + 1, pY - 1, pZ);
bool llTransxy0 = isTranslucentAt(level, pX - 1, pY - 1, pZ);
bool llTrans0yZ = isTranslucentAt(level, pX, pY - 1, pZ + 1);
bool llTrans0yz = isTranslucentAt(level, pX, pY - 1, pZ - 1);
if (llTrans0yz || llTransxy0) {
llxyz = getShadeBrightness(tt, level, pX - 1, pY, pZ - 1);
@ -5346,10 +5354,10 @@ bool TileRenderer::tesselateBlockInWorldWithAmbienceOcclusionTexLighting(
ll0Yz = getShadeBrightness(tt, level, pX, pY, pZ - 1);
ll0YZ = getShadeBrightness(tt, level, pX, pY, pZ + 1);
bool llTransXY0 = Tile::transculent[level->getTile(pX + 1, pY + 1, pZ)];
bool llTransxY0 = Tile::transculent[level->getTile(pX - 1, pY + 1, pZ)];
bool llTrans0YZ = Tile::transculent[level->getTile(pX, pY + 1, pZ + 1)];
bool llTrans0Yz = Tile::transculent[level->getTile(pX, pY + 1, pZ - 1)];
bool llTransXY0 = isTranslucentAt(level, pX + 1, pY + 1, pZ);
bool llTransxY0 = isTranslucentAt(level, pX - 1, pY + 1, pZ);
bool llTrans0YZ = isTranslucentAt(level, pX, pY + 1, pZ + 1);
bool llTrans0Yz = isTranslucentAt(level, pX, pY + 1, pZ - 1);
if (llTrans0Yz || llTransxY0) {
llxYz = getShadeBrightness(tt, level, pX - 1, pY, pZ - 1);
@ -5429,10 +5437,10 @@ bool TileRenderer::tesselateBlockInWorldWithAmbienceOcclusionTexLighting(
cc0Yz = getLightColor(tt, level, pX, pY + 1, pZ);
ccX0z = getLightColor(tt, level, pX + 1, pY, pZ);
bool llTransX0z = Tile::transculent[level->getTile(pX + 1, pY, pZ - 1)];
bool llTransx0z = Tile::transculent[level->getTile(pX - 1, pY, pZ - 1)];
bool llTrans0Yz = Tile::transculent[level->getTile(pX, pY + 1, pZ - 1)];
bool llTrans0yz = Tile::transculent[level->getTile(pX, pY - 1, pZ - 1)];
bool llTransX0z = isTranslucentAt(level, pX + 1, pY, pZ - 1);
bool llTransx0z = isTranslucentAt(level, pX - 1, pY, pZ - 1);
bool llTrans0Yz = isTranslucentAt(level, pX, pY + 1, pZ - 1);
bool llTrans0yz = isTranslucentAt(level, pX, pY - 1, pZ - 1);
if (llTransx0z || llTrans0yz) {
llxyz = getShadeBrightness(tt, level, pX - 1, pY - 1, pZ);
@ -5597,10 +5605,10 @@ bool TileRenderer::tesselateBlockInWorldWithAmbienceOcclusionTexLighting(
cc0yZ = getLightColor(tt, level, pX, pY - 1, pZ);
cc0YZ = getLightColor(tt, level, pX, pY + 1, pZ);
bool llTransX0Z = Tile::transculent[level->getTile(pX + 1, pY, pZ + 1)];
bool llTransx0Z = Tile::transculent[level->getTile(pX - 1, pY, pZ + 1)];
bool llTrans0YZ = Tile::transculent[level->getTile(pX, pY + 1, pZ + 1)];
bool llTrans0yZ = Tile::transculent[level->getTile(pX, pY - 1, pZ + 1)];
bool llTransX0Z = isTranslucentAt(level, pX + 1, pY, pZ + 1);
bool llTransx0Z = isTranslucentAt(level, pX - 1, pY, pZ + 1);
bool llTrans0YZ = isTranslucentAt(level, pX, pY + 1, pZ + 1);
bool llTrans0yZ = isTranslucentAt(level, pX, pY - 1, pZ + 1);
if (llTransx0Z || llTrans0yZ) {
llxyZ = getShadeBrightness(tt, level, pX - 1, pY - 1, pZ);
@ -5765,10 +5773,10 @@ bool TileRenderer::tesselateBlockInWorldWithAmbienceOcclusionTexLighting(
ccx0Z = getLightColor(tt, level, pX, pY, pZ + 1);
ccxY0 = getLightColor(tt, level, pX, pY + 1, pZ);
bool llTransxY0 = Tile::transculent[level->getTile(pX - 1, pY + 1, pZ)];
bool llTransxy0 = Tile::transculent[level->getTile(pX - 1, pY - 1, pZ)];
bool llTransx0z = Tile::transculent[level->getTile(pX - 1, pY, pZ - 1)];
bool llTransx0Z = Tile::transculent[level->getTile(pX - 1, pY, pZ + 1)];
bool llTransxY0 = isTranslucentAt(level, pX - 1, pY + 1, pZ);
bool llTransxy0 = isTranslucentAt(level, pX - 1, pY - 1, pZ);
bool llTransx0z = isTranslucentAt(level, pX - 1, pY, pZ - 1);
bool llTransx0Z = isTranslucentAt(level, pX - 1, pY, pZ + 1);
if (llTransx0z || llTransxy0) {
llxyz = getShadeBrightness(tt, level, pX, pY - 1, pZ - 1);
@ -5929,10 +5937,10 @@ bool TileRenderer::tesselateBlockInWorldWithAmbienceOcclusionTexLighting(
ccX0Z = getLightColor(tt, level, pX, pY, pZ + 1);
ccXY0 = getLightColor(tt, level, pX, pY + 1, pZ);
bool llTransXY0 = Tile::transculent[level->getTile(pX + 1, pY + 1, pZ)];
bool llTransXy0 = Tile::transculent[level->getTile(pX + 1, pY - 1, pZ)];
bool llTransX0Z = Tile::transculent[level->getTile(pX + 1, pY, pZ + 1)];
bool llTransX0z = Tile::transculent[level->getTile(pX + 1, pY, pZ - 1)];
bool llTransXY0 = isTranslucentAt(level, pX + 1, pY + 1, pZ);
bool llTransXy0 = isTranslucentAt(level, pX + 1, pY - 1, pZ);
bool llTransX0Z = isTranslucentAt(level, pX + 1, pY, pZ + 1);
bool llTransX0z = isTranslucentAt(level, pX + 1, pY, pZ - 1);
if (llTransXy0 || llTransX0z) {
llXyz = getShadeBrightness(tt, level, pX, pY - 1, pZ - 1);

View file

@ -30,6 +30,7 @@ class BeaconTile;
class HopperTile;
class Icon;
class Minecraft;
struct ChunkBuildContext;
class TileRenderer {
friend class FallingTileRenderer;
@ -62,6 +63,7 @@ public:
bool isTranslucentAt(LevelSource* level, int x, int y, int z);
unsigned int* cache;
unsigned char* tileIds;
const ChunkBuildContext* buildContext;
static const unsigned int cache_getLightColor_valid = 0x80000000;
static const unsigned int cache_isTranslucentAt_valid = 0x40000000;
static const unsigned int cache_isSolidBlockingTile_valid = 0x20000000;
@ -74,10 +76,12 @@ public:
public:
TileRenderer(LevelSource* level, int xMin, int yMin, int zMin,
unsigned char* tileIds);
unsigned char* tileIds,
const ChunkBuildContext* buildContext = nullptr);
TileRenderer(LevelSource* level);
TileRenderer();
~TileRenderer();
void setBuildContext(const ChunkBuildContext* buildContext);
void setFixedTexture(Icon* fixedTexture);
void clearFixedTexture();
bool hasFixedTexture();