1 Commits

Author SHA1 Message Date
JuiceyDev 8d4dd1a1d0 more-optimizations: first batch of optimizations at the levelrenderer
level
2026-04-05 20:01:53 +02:00
7 changed files with 369 additions and 188 deletions
+27 -6
View File
@@ -13,11 +13,10 @@
#include <utility> #include <utility>
#include <vector> #include <vector>
#include "platform/sdl2/Render.h"
#include "LevelRenderer.h" #include "LevelRenderer.h"
#include "app/linux/Stubs/winapi_stubs.h"
#include "app/include/FrameProfiler.h"
#include "TileRenderer.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/Tesselator.h"
#include "minecraft/client/renderer/culling/Culler.h" #include "minecraft/client/renderer/culling/Culler.h"
#include "minecraft/client/renderer/tileentity/TileEntityRenderDispatcher.h" #include "minecraft/client/renderer/tileentity/TileEntityRenderDispatcher.h"
@@ -29,6 +28,7 @@
#include "minecraft/world/level/tile/Tile.h" #include "minecraft/world/level/tile/Tile.h"
#include "minecraft/world/level/tile/entity/TileEntity.h" #include "minecraft/world/level/tile/entity/TileEntity.h"
#include "minecraft/world/phys/AABB.h" #include "minecraft/world/phys/AABB.h"
#include "platform/sdl2/Render.h"
int Chunk::updates = 0; int Chunk::updates = 0;
@@ -174,6 +174,11 @@ void Chunk::setPos(int x, int y, int z) {
levelRenderer->m_csDirtyChunks); levelRenderer->m_csDirtyChunks);
unsigned char refCount = unsigned char refCount =
levelRenderer->incGlobalChunkRefCount(x, y, z, level); 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); // printf("\t\t [inc] refcount %d at %d, %d, %d\n",refCount,x,y,z);
// int idx = levelRenderer->getGlobalIndexForChunk(x, y, z, level); // int idx = levelRenderer->getGlobalIndexForChunk(x, y, z, level);
@@ -223,8 +228,15 @@ void Chunk::makeCopyForRebuild(Chunk* source) {
this->globalRenderableTileEntities = source->globalRenderableTileEntities; this->globalRenderableTileEntities = source->globalRenderableTileEntities;
this->globalRenderableTileEntities_cs = this->globalRenderableTileEntities_cs =
source->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() { void Chunk::rebuild() {
// if (!dirty) return; // if (!dirty) return;
@@ -285,8 +297,8 @@ void Chunk::rebuild() {
LevelSource* region = LevelSource* region =
new Region(level, x0 - r, y0 - r, z0 - r, x1 + r, y1 + r, z1 + r, r); new Region(level, x0 - r, y0 - r, z0 - r, x1 + r, y1 + r, z1 + r, r);
TileRenderer* tileRenderer = TileRenderer* tileRenderer = new TileRenderer(
new TileRenderer(region, this->x, this->y, this->z, tileIds); region, this->x, this->y, this->z, tileIds, &buildContext);
// AP - added a caching system for Chunk::rebuild to take advantage of // 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 // 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], bb = {bounds.boundingBox[0], bounds.boundingBox[1], bounds.boundingBox[2],
bounds.boundingBox[3], bounds.boundingBox[4], bounds.boundingBox[5]}; bounds.boundingBox[3], bounds.boundingBox[4], bounds.boundingBox[5]};
uint64_t conn = computeConnectivity(tileIds); // pass tileIds
int globalIdx = int globalIdx =
levelRenderer->getGlobalIndexForChunk(this->x, this->y, this->z, level); 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); levelRenderer->setGlobalChunkConnectivity(globalIdx, conn);
#else
levelRenderer->setGlobalChunkConnectivity(globalIdx, ~0ULL);
#endif
delete tileRenderer; delete tileRenderer;
delete region; delete region;
@@ -590,6 +606,7 @@ float Chunk::squishedDistanceToSqr(std::shared_ptr<Entity> player) {
return xd * xd + yd * yd + zd * zd; return xd * xd + yd * yd + zd * zd;
} }
#if defined(OCCLUSION_MODE_BFS)
uint64_t Chunk::computeConnectivity(const uint8_t* tileIds) { uint64_t Chunk::computeConnectivity(const uint8_t* tileIds) {
const int W = 16; const int W = 16;
const int H = 16; const int H = 16;
@@ -726,6 +743,7 @@ uint64_t Chunk::computeConnectivity(const uint8_t* tileIds) {
return result; return result;
} }
#endif
void Chunk::reset() { void Chunk::reset() {
if (assigned) { if (assigned) {
int oldKey = -1; int oldKey = -1;
@@ -742,6 +760,9 @@ void Chunk::reset() {
//%d\n",refCount,x,y,z); //%d\n",refCount,x,y,z);
if (refCount == 0 && oldKey != -1) { if (refCount == 0 && oldKey != -1) {
retireRenderableTileEntities = true; retireRenderableTileEntities = true;
if ((size_t)oldKey < levelRenderer->m_globalClipChunks.size()) {
levelRenderer->m_globalClipChunks[oldKey] = nullptr;
}
int lists = oldKey * 2; int lists = oldKey * 2;
if (lists >= 0) { if (lists >= 0) {
lists += levelRenderer->chunkLists; lists += levelRenderer->chunkLists;
+13
View File
@@ -17,6 +17,14 @@ class Entity;
class Chunk; class Chunk;
class Culler; class Culler;
struct ChunkBuildContext {
int64_t nowMs = 0;
uint32_t batchId = 0;
uint16_t veryNearCount = 0;
bool onlyRebuild = false;
bool deferredAtomic = false;
};
class ClipChunk { class ClipChunk {
public: public:
Chunk* chunk; Chunk* chunk;
@@ -57,7 +65,9 @@ public:
int xm, ym, zm; int xm, ym, zm;
AABB bb; AABB bb;
ClipChunk* clipChunk; ClipChunk* clipChunk;
#if defined(OCCLUSION_MODE_BFS)
uint64_t computeConnectivity(const uint8_t* tileIds); uint64_t computeConnectivity(const uint8_t* tileIds);
#endif
int id; int id;
// public: // public:
// std::vector<std::shared_ptr<TileEntity> > renderableTileEntities; // std::vector<std::shared_ptr<TileEntity> > renderableTileEntities;
@@ -67,6 +77,7 @@ private:
LevelRenderer::rteMap* globalRenderableTileEntities; LevelRenderer::rteMap* globalRenderableTileEntities;
std::mutex* globalRenderableTileEntities_cs; std::mutex* globalRenderableTileEntities_cs;
bool assigned; bool assigned;
ChunkBuildContext buildContext;
public: public:
Chunk(Level* level, LevelRenderer::rteMap& globalRenderableTileEntities, Chunk(Level* level, LevelRenderer::rteMap& globalRenderableTileEntities,
@@ -84,6 +95,8 @@ private:
public: public:
void makeCopyForRebuild(Chunk* source); void makeCopyForRebuild(Chunk* source);
void setBuildContext(const ChunkBuildContext& ctx);
const ChunkBuildContext& getBuildContext() const;
void rebuild(); void rebuild();
float distanceToSqr(std::shared_ptr<Entity> player) const; float distanceToSqr(std::shared_ptr<Entity> player) const;
float squishedDistanceToSqr(std::shared_ptr<Entity> player); float squishedDistanceToSqr(std::shared_ptr<Entity> player);
@@ -6,24 +6,18 @@
#include <cmath> #include <cmath>
#include <numbers> #include <numbers>
#include "platform/PlatformTypes.h"
#include "platform/sdl2/Input.h"
#include "platform/sdl2/Render.h"
#include "BossMobGuiInfo.h" #include "BossMobGuiInfo.h"
#include "Chunk.h" #include "Chunk.h"
#include "ItemInHandRenderer.h" #include "ItemInHandRenderer.h"
#include "LevelRenderer.h" #include "LevelRenderer.h"
#include "Tesselator.h"
#include "app/common/App_enums.h" #include "app/common/App_enums.h"
#include "platform/ShutdownManager.h"
#include "app/common/src/Colours/ColourTable.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/BufferedImage.h"
#include "app/include/FrameProfiler.h" #include "app/include/FrameProfiler.h"
#include "app/include/stubs.h" #include "app/include/stubs.h"
#include "Tesselator.h" #include "app/linux/LinuxGame.h"
#include "minecraft/world/level/storage/ConsoleSaveFileIO/compression.h" #include "app/linux/Stubs/winapi_stubs.h"
#include "java/Class.h" #include "java/Class.h"
#include "java/FloatBuffer.h" #include "java/FloatBuffer.h"
#include "java/JavaMath.h" #include "java/JavaMath.h"
@@ -78,10 +72,15 @@
#include "minecraft/world/level/chunk/SparseLightStorage.h" #include "minecraft/world/level/chunk/SparseLightStorage.h"
#include "minecraft/world/level/dimension/Dimension.h" #include "minecraft/world/level/dimension/Dimension.h"
#include "minecraft/world/level/material/Material.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/level/tile/Tile.h"
#include "minecraft/world/phys/AABB.h" #include "minecraft/world/phys/AABB.h"
#include "minecraft/world/phys/HitResult.h" #include "minecraft/world/phys/HitResult.h"
#include "minecraft/world/phys/Vec3.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; bool GameRenderer::anaglyph3d = false;
int GameRenderer::anaglyphPass = 0; int GameRenderer::anaglyphPass = 0;
@@ -1115,6 +1114,7 @@ void GameRenderer::FinishedReassigning() { m_csDeleteStack.unlock(); }
int GameRenderer::runUpdate(void* lpParam) { int GameRenderer::runUpdate(void* lpParam) {
Minecraft* minecraft = Minecraft::GetInstance(); Minecraft* minecraft = Minecraft::GetInstance();
int64_t updatePassToken = 0;
Tesselator::CreateNewThreadStorage(1024 * 1024); Tesselator::CreateNewThreadStorage(1024 * 1024);
Compression::UseDefaultThreadStorage(); Compression::UseDefaultThreadStorage();
RenderManager.InitialiseContext(); RenderManager.InitialiseContext();
@@ -1150,8 +1150,10 @@ int GameRenderer::runUpdate(void* lpParam) {
int count = 0; int count = 0;
static const int MAX_DEFERRED_UPDATES = 10; static const int MAX_DEFERRED_UPDATES = 10;
bool shouldContinue = false; bool shouldContinue = false;
const int64_t tickNowMs = ++updatePassToken;
do { do {
shouldContinue = minecraft->levelRenderer->updateDirtyChunks(); shouldContinue =
minecraft->levelRenderer->updateDirtyChunks(tickNowMs);
count++; count++;
} while (shouldContinue && count < MAX_DEFERRED_UPDATES); } while (shouldContinue && count < MAX_DEFERRED_UPDATES);
@@ -1421,7 +1423,8 @@ void GameRenderer::renderLevel(float a, int64_t until) {
int visibleWaterChunks = int visibleWaterChunks =
levelRenderer->render(cameraEntity, 1, a, updateChunks); 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) { if (visibleWaterChunks > 0) {
levelRenderer->render( levelRenderer->render(
@@ -11,27 +11,24 @@
#include <algorithm> #include <algorithm>
#include <array> #include <array>
#include <atomic>
#include <cmath> #include <cmath>
#include <mutex> #include <mutex>
#include <numbers> #include <numbers>
#include <ranges> #include <ranges>
#include <utility> #include <utility>
#include "platform/PlatformTypes.h"
#include "platform/sdl2/Input.h"
#include "platform/sdl2/Render.h"
#include "Chunk.h" #include "Chunk.h"
#include "GameRenderer.h" #include "GameRenderer.h"
#include "Tesselator.h"
#include "app/common/App_enums.h" #include "app/common/App_enums.h"
#include "app/common/src/Audio/SoundEngine.h" #include "app/common/src/Audio/SoundEngine.h"
#include "app/common/src/Colours/ColourTable.h" #include "app/common/src/Colours/ColourTable.h"
#include "app/common/src/Console_Debug_enum.h" #include "app/common/src/Console_Debug_enum.h"
#include "app/linux/LinuxGame.h"
#include "app/include/FrameProfiler.h" #include "app/include/FrameProfiler.h"
#include "app/include/MobSkinMemTextureProcessor.h" #include "app/include/MobSkinMemTextureProcessor.h"
#include "app/include/stubs.h" #include "app/include/stubs.h"
#include "Tesselator.h" #include "app/linux/LinuxGame.h"
#include "util/StringHelpers.h"
#include "java/Class.h" #include "java/Class.h"
#include "java/JavaMath.h" #include "java/JavaMath.h"
#include "java/Random.h" #include "java/Random.h"
@@ -111,10 +108,19 @@
#include "minecraft/world/phys/AABB.h" #include "minecraft/world/phys/AABB.h"
#include "minecraft/world/phys/HitResult.h" #include "minecraft/world/phys/HitResult.h"
#include "minecraft/world/phys/Vec3.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 Icon;
class ItemInstance; class ItemInstance;
namespace {
std::atomic<uint32_t> s_chunkBuildBatchCounter{0};
std::atomic<int64_t> s_dirtyChunkPassToken{0};
} // namespace
// #define DISABLE_SPU_CODE // #define DISABLE_SPU_CODE
ResourceLocation LevelRenderer::MOON_LOCATION = ResourceLocation LevelRenderer::MOON_LOCATION =
@@ -216,6 +222,7 @@ LevelRenderer::LevelRenderer(Minecraft* mc, Textures* textures) {
dirtyChunkPresent = false; dirtyChunkPresent = false;
lastDirtyChunkFound = 0; lastDirtyChunkFound = 0;
m_dirtyChunksRequireFullScan = false;
this->mc = mc; this->mc = mc;
this->textures = textures; this->textures = textures;
@@ -230,6 +237,8 @@ LevelRenderer::LevelRenderer(Minecraft* mc, Textures* textures) {
globalChunkConnectivity = new uint64_t[getGlobalChunkCount()]; globalChunkConnectivity = new uint64_t[getGlobalChunkCount()];
memset(globalChunkConnectivity, 0xFF, memset(globalChunkConnectivity, 0xFF,
getGlobalChunkCount() * sizeof(uint64_t)); // 0xFF >> Fully open getGlobalChunkCount() * sizeof(uint64_t)); // 0xFF >> Fully open
m_globalClipChunks =
std::vector<ClipChunk*>(getGlobalChunkCount(), nullptr);
starList = MemoryTracker::genLists(4); starList = MemoryTracker::genLists(4);
@@ -1699,7 +1708,14 @@ void LevelRenderer::renderAdvancedClouds(float alpha) {
RenderManager.StateSetEnableViewportClipPlanes(false); 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) #if defined(_LARGE_WORLDS)
struct NearestClipChunkSet { struct NearestClipChunkSet {
std::array<std::pair<ClipChunk*, int>, MAX_CONCURRENT_CHUNK_REBUILDS> 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 ClipChunk* nearChunk = nullptr; // Nearest chunk that is dirty
int veryNearCount = 0; int veryNearCount = 0;
int minDistSq = 0x7fffffff; // Distances to this chunk int minDistSq = 0x7fffffff; // Distances to this chunk
bool onlyRebuild = false;
std::unique_lock<std::recursive_mutex> dirtyChunksLock(m_csDirtyChunks); std::unique_lock<std::recursive_mutex> dirtyChunksLock(m_csDirtyChunks);
// Set a flag if we should only rebuild existing chunks, not create anything // Set a flag if we should only rebuild existing chunks, not create anything
@@ -1756,11 +1773,165 @@ bool LevelRenderer::updateDirtyChunks() {
} }
throttle++; 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 // Move any dirty chunks stored in the lock free stack into global flags
int index = 0; 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 { do {
// See comment on dirtyChunksLockFreeStack.Push() regarding details // See comment on dirtyChunksLockFreeStack.Push() regarding details
// of this casting/subtracting -2. // of this casting/subtracting -2.
@@ -1775,7 +1946,10 @@ bool LevelRenderer::updateDirtyChunks() {
true; // 1 is a special value passed to let this thread true; // 1 is a special value passed to let this thread
// know that a chunk which isn't on this stack has // know that a chunk which isn't on this stack has
// been set to dirty // been set to dirty
else if (index > 1) { if (index == 1) {
hasNonStackDirtySignal = true;
m_dirtyChunksRequireFullScan = true;
} else if (index > 1) {
int i2 = index - 2; int i2 = index - 2;
if (i2 >= DIMENSION_OFFSETS[2]) { if (i2 >= DIMENSION_OFFSETS[2]) {
i2 -= DIMENSION_OFFSETS[2]; i2 -= DIMENSION_OFFSETS[2];
@@ -1798,148 +1972,69 @@ bool LevelRenderer::updateDirtyChunks() {
#endif #endif
dirtyChunkPresent = true; dirtyChunkPresent = true;
poppedDirtyIndices.push_back(index - 2);
} }
} while (index); } while (index);
// Only bother searching round all the chunks if we have some dirty // Only bother searching round all the chunks if we have some dirty
// chunk(s) // chunk(s)
if (dirtyChunkPresent) { if (dirtyChunkPresent) {
lastDirtyChunkFound = System::currentTimeMillis(); lastDirtyChunkFound = nowMs;
// Find nearest chunk that is dirty bool useSparsePath = (!m_dirtyChunksRequireFullScan) &&
for (int p = 0; p < XUSER_MAX_COUNT; p++) { (!hasNonStackDirtySignal) &&
// It's possible that the localplayers member can be set to (!poppedDirtyIndices.empty());
// 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;
// app.DebugPrintf("!! %d %d %d, %d %d %d if (useSparsePath) {
//{%d,%d} std::sort(poppedDirtyIndices.begin(), poppedDirtyIndices.end());
//",px,py,pz,stackChunkDirty,nonStackChunkDirty,onlyRebuild, poppedDirtyIndices.erase(std::unique(poppedDirtyIndices.begin(),
// xChunks, zChunks); poppedDirtyIndices.end()),
poppedDirtyIndices.end());
int considered = 0; for (int dirtyIdx : poppedDirtyIndices) {
int wouldBeNearButEmpty = 0; if (dirtyIdx < 0) continue;
for (int x = 0; x < xChunks; x++) { if ((size_t)dirtyIdx >= m_globalClipChunks.size()) continue;
for (int z = 0; z < zChunks; z++) { evaluateSparseDirtyClipChunk(m_globalClipChunks[dirtyIdx]);
for (int y = 0; y < CHUNK_Y_COUNT; y++) { }
ClipChunk* pClipChunk = } else {
&chunks[p][(z * yChunks + y) * xChunks + x]; bool sawAnyDirtyFlag = false;
// Get distance to this chunk - deliberately not for (int p = 0; p < XUSER_MAX_COUNT; p++) {
// calling the chunk's method of doing this to avoid std::shared_ptr<LocalPlayer> player = mc->localplayers[p];
// overheads (passing entitie, type conversion etc.) if (player == nullptr) continue;
// that this involves if (chunks[p].empty()) continue;
int xd = pClipChunk->xm - px; if (level[p] == nullptr) continue;
int yd = pClipChunk->ym - py; if (chunks[p].size() != xChunks * zChunks * CHUNK_Y_COUNT)
int zd = pClipChunk->zm - pz; continue;
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
if (globalChunkFlags[pClipChunk->globalIdx] & const int px = (int)player->x;
CHUNK_FLAG_DIRTY) { const int py = (int)player->y;
if ((!onlyRebuild) || const int pz = (int)player->z;
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
#if defined(_CRITICAL_CHUNKS) // app.DebugPrintf("!! %d %d %d, %d %d %d
// AP - this will make sure that if a //{%d,%d}
// deferred grouping has started, only //",px,py,pz,stackChunkDirty,nonStackChunkDirty,onlyRebuild,
// critical chunks go into that grouping, // xChunks, zChunks);
// 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++;
}
}
#if defined(_CRITICAL_CHUNKS) for (int x = 0; x < xChunks; x++) {
// AP - is the chunk near and also critical for (int z = 0; z < zChunks; z++) {
if (distSq < 20 * 20 && for (int y = 0; y < CHUNK_Y_COUNT; y++) {
((globalChunkFlags[pClipChunk ClipChunk* pClipChunk =
->globalIdx] & &chunks[p][(z * yChunks + y) * xChunks + x];
CHUNK_FLAG_CRITICAL))) if (pClipChunk->globalIdx < 0) continue;
#else if (globalChunkFlags[pClipChunk->globalIdx] &
if (distSq < 20 * 20) CHUNK_FLAG_DIRTY) {
#endif sawAnyDirtyFlag = true;
{
veryNearCount++;
}
} }
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 // whilst we are doing this copy. The copy will then be
// guaranteed to be consistent whilst rebuilding takes place // guaranteed to be consistent whilst rebuilding takes place
// outside of that lock. // 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].makeCopyForRebuild(chunk);
permaChunk[index].setBuildContext(buildContext);
++index; ++index;
} }
dirtyChunksLock.unlock(); dirtyChunksLock.unlock();
@@ -2064,7 +2167,15 @@ bool LevelRenderer::updateDirtyChunks() {
// means that any chunks can't be repositioned whilst we are doing // means that any chunks can't be repositioned whilst we are doing
// this copy. The copy will then be guaranteed to be consistent // this copy. The copy will then be guaranteed to be consistent
// whilst rebuilding takes place outside of that lock. // 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.makeCopyForRebuild(chunk);
permaChunk.setBuildContext(buildContext);
dirtyChunksLock.unlock(); dirtyChunksLock.unlock();
} }
// static int64_t totalTime = 0; // static int64_t totalTime = 0;
@@ -2086,8 +2197,8 @@ bool LevelRenderer::updateDirtyChunks() {
// Nothing to do - clear flags that there are things to process, unless // 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 // it's been a while since we found any dirty chunks in which case force
// a check next time through // a check next time through
if ((System::currentTimeMillis() - lastDirtyChunkFound) > if ((nowMs - lastDirtyChunkFound) >
FORCE_DIRTY_CHUNK_CHECK_PERIOD_MS) { FORCE_DIRTY_CHUNK_CHECK_PERIOD_PASSES) {
dirtyChunkPresent = true; dirtyChunkPresent = true;
} else { } else {
dirtyChunkPresent = false; 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 _y1 = Mth::intFloorDiv(y1, CHUNK_SIZE);
int _z1 = Mth::intFloorDiv(z1, CHUNK_XZSIZE); 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 x = _x0; x <= _x1; x++) {
for (int y = _y0; y <= _y1; y++) { for (int y = _y0; y <= _y1; y++) {
for (int z = _z0; z <= _z1; z++) { 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)); dirtyChunksLockFreeStack.Push((int*)(index));
#else #else
dirtyChunksLockFreeStack.Push( pendingDirtyIndices.push_back(
(int*)(intptr_t)(uintptr_t)(index + 2)); (intptr_t)(uintptr_t)(index + 2));
#endif #endif
} }
// setGlobalChunkFlag(x * 16, y * // 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) { void LevelRenderer::tileChanged(int x, int y, int z) {
@@ -1,15 +1,15 @@
#pragma once #pragma once
#include "OffsettedRenderList.h"
#include "app/include/NetTypes.h" #include "app/include/NetTypes.h"
#include "app/include/SkinBox.h" #include "app/include/SkinBox.h"
#include "app/include/XboxStubs.h" #include "app/include/XboxStubs.h"
#include "OffsettedRenderList.h"
#include "platform/C4JThread.h"
#include "util/Definitions.h"
#include "java/JavaIntHash.h" #include "java/JavaIntHash.h"
#include "minecraft/core/particles/ParticleTypes.h" #include "minecraft/core/particles/ParticleTypes.h"
#include "minecraft/world/level/Level.h" #include "minecraft/world/level/Level.h"
#include "minecraft/world/level/LevelListener.h" #include "minecraft/world/level/LevelListener.h"
#include "minecraft/world/phys/AABB.h" #include "minecraft/world/phys/AABB.h"
#include "platform/C4JThread.h"
#include "util/Definitions.h"
class ClipChunk; class ClipChunk;
class HitResult; class HitResult;
@@ -119,7 +119,7 @@ public:
void renderClouds(float alpha); void renderClouds(float alpha);
bool isInCloud(double x, double y, double z, float alpha); bool isInCloud(double x, double y, double z, float alpha);
void renderAdvancedClouds(float alpha); void renderAdvancedClouds(float alpha);
bool updateDirtyChunks(); bool updateDirtyChunks(int64_t cachedNowMs = -1);
public: public:
void renderHit(std::shared_ptr<Player> player, HitResult* h, int mode, void renderHit(std::shared_ptr<Player> player, HitResult* h, int mode,
@@ -225,6 +225,8 @@ private:
uint64_t getGlobalChunkConnectivity(int index); uint64_t getGlobalChunkConnectivity(int index);
std::vector<ClipChunk*> m_bfsGrid; std::vector<ClipChunk*> m_bfsGrid;
std::vector<uint8_t> m_bfsVisitedFaces[4]; std::vector<uint8_t> m_bfsVisitedFaces[4];
std::vector<ClipChunk*> m_globalClipChunks;
bool m_dirtyChunksRequireFullScan;
std::unordered_map<int, BlockDestructionProgress*> destroyingBlocks; std::unordered_map<int, BlockDestructionProgress*> destroyingBlocks;
Icon** breakingTextures; Icon** breakingTextures;
@@ -355,7 +357,7 @@ public:
bool dirtyChunkPresent; bool dirtyChunkPresent;
int64_t lastDirtyChunkFound; 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) #if defined(_LARGE_WORLDS)
static const int MAX_CONCURRENT_CHUNK_REBUILDS = 4; static const int MAX_CONCURRENT_CHUNK_REBUILDS = 4;
@@ -13,13 +13,13 @@
#include <cmath> #include <cmath>
#include <numbers> #include <numbers>
#include "platform/sdl2/Render.h" #include "Chunk.h"
#include "EntityTileRenderer.h" #include "EntityTileRenderer.h"
#include "GameRenderer.h" #include "GameRenderer.h"
#include "Tesselator.h"
#include "app/common/App_enums.h" #include "app/common/App_enums.h"
#include "app/common/src/Colours/ColourTable.h" #include "app/common/src/Colours/ColourTable.h"
#include "app/include/FrameProfiler.h" #include "app/include/FrameProfiler.h"
#include "Tesselator.h"
#include "minecraft/Direction.h" #include "minecraft/Direction.h"
#include "minecraft/Facing.h" #include "minecraft/Facing.h"
#include "minecraft/SharedConstants.h" #include "minecraft/SharedConstants.h"
@@ -69,6 +69,7 @@
#include "minecraft/world/level/tile/piston/PistonBaseTile.h" #include "minecraft/world/level/tile/piston/PistonBaseTile.h"
#include "minecraft/world/level/tile/piston/PistonExtensionTile.h" #include "minecraft/world/level/tile/piston/PistonExtensionTile.h"
#include "minecraft/world/phys/Vec3.h" #include "minecraft/world/phys/Vec3.h"
#include "platform/sdl2/Render.h"
bool TileRenderer::fancy = true; bool TileRenderer::fancy = true;
@@ -101,6 +102,7 @@ void TileRenderer::_init() {
yMin = 0; yMin = 0;
zMin = 0; zMin = 0;
cache = nullptr; cache = nullptr;
buildContext = nullptr;
} }
bool TileRenderer::isTranslucentAt(LevelSource* level, int x, int y, int z) { 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, TileRenderer::TileRenderer(LevelSource* level, int xMin, int yMin, int zMin,
unsigned char* tileIds) { unsigned char* tileIds,
const ChunkBuildContext* buildContext) {
this->level = level; this->level = level;
_init(); _init();
this->xMin = xMin; this->xMin = xMin;
@@ -214,6 +217,7 @@ TileRenderer::TileRenderer(LevelSource* level, int xMin, int yMin, int zMin,
this->yMin2 = yMin - 2; this->yMin2 = yMin - 2;
this->zMin2 = zMin - 2; this->zMin2 = zMin - 2;
this->tileIds = tileIds; this->tileIds = tileIds;
this->buildContext = buildContext;
cache = new unsigned int[32 * 32 * 32]; cache = new unsigned int[32 * 32 * 32];
memset(cache, 0, 32 * 32 * 32 * sizeof(unsigned int)); memset(cache, 0, 32 * 32 * 32 * sizeof(unsigned int));
} }
@@ -232,6 +236,10 @@ TileRenderer::TileRenderer() {
_init(); _init();
} }
void TileRenderer::setBuildContext(const ChunkBuildContext* buildContext) {
this->buildContext = buildContext;
}
void TileRenderer::setFixedTexture(Icon* fixedTexture) { void TileRenderer::setFixedTexture(Icon* fixedTexture) {
this->fixedTexture = fixedTexture; this->fixedTexture = fixedTexture;
} }
@@ -5254,10 +5262,10 @@ bool TileRenderer::tesselateBlockInWorldWithAmbienceOcclusionTexLighting(
ll0yZ = getShadeBrightness(tt, level, pX, pY, pZ + 1); ll0yZ = getShadeBrightness(tt, level, pX, pY, pZ + 1);
llXy0 = getShadeBrightness(tt, level, pX + 1, pY, pZ); llXy0 = getShadeBrightness(tt, level, pX + 1, pY, pZ);
bool llTransXy0 = Tile::transculent[level->getTile(pX + 1, pY - 1, pZ)]; bool llTransXy0 = isTranslucentAt(level, pX + 1, pY - 1, pZ);
bool llTransxy0 = Tile::transculent[level->getTile(pX - 1, pY - 1, pZ)]; bool llTransxy0 = isTranslucentAt(level, pX - 1, pY - 1, pZ);
bool llTrans0yZ = Tile::transculent[level->getTile(pX, pY - 1, pZ + 1)]; bool llTrans0yZ = isTranslucentAt(level, pX, pY - 1, pZ + 1);
bool llTrans0yz = Tile::transculent[level->getTile(pX, pY - 1, pZ - 1)]; bool llTrans0yz = isTranslucentAt(level, pX, pY - 1, pZ - 1);
if (llTrans0yz || llTransxy0) { if (llTrans0yz || llTransxy0) {
llxyz = getShadeBrightness(tt, level, pX - 1, pY, pZ - 1); 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);
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 = isTranslucentAt(level, pX + 1, pY + 1, pZ);
bool llTransxY0 = Tile::transculent[level->getTile(pX - 1, pY + 1, pZ)]; bool llTransxY0 = isTranslucentAt(level, pX - 1, pY + 1, pZ);
bool llTrans0YZ = Tile::transculent[level->getTile(pX, pY + 1, pZ + 1)]; bool llTrans0YZ = isTranslucentAt(level, pX, pY + 1, pZ + 1);
bool llTrans0Yz = Tile::transculent[level->getTile(pX, pY + 1, pZ - 1)]; bool llTrans0Yz = isTranslucentAt(level, pX, pY + 1, pZ - 1);
if (llTrans0Yz || llTransxY0) { if (llTrans0Yz || llTransxY0) {
llxYz = getShadeBrightness(tt, level, pX - 1, pY, pZ - 1); llxYz = getShadeBrightness(tt, level, pX - 1, pY, pZ - 1);
@@ -5429,10 +5437,10 @@ bool TileRenderer::tesselateBlockInWorldWithAmbienceOcclusionTexLighting(
cc0Yz = getLightColor(tt, level, pX, pY + 1, pZ); cc0Yz = getLightColor(tt, level, pX, pY + 1, pZ);
ccX0z = getLightColor(tt, level, pX + 1, pY, pZ); ccX0z = getLightColor(tt, level, pX + 1, pY, pZ);
bool llTransX0z = Tile::transculent[level->getTile(pX + 1, pY, pZ - 1)]; bool llTransX0z = isTranslucentAt(level, pX + 1, pY, pZ - 1);
bool llTransx0z = Tile::transculent[level->getTile(pX - 1, pY, pZ - 1)]; bool llTransx0z = isTranslucentAt(level, pX - 1, pY, pZ - 1);
bool llTrans0Yz = Tile::transculent[level->getTile(pX, pY + 1, pZ - 1)]; bool llTrans0Yz = isTranslucentAt(level, pX, pY + 1, pZ - 1);
bool llTrans0yz = Tile::transculent[level->getTile(pX, pY - 1, pZ - 1)]; bool llTrans0yz = isTranslucentAt(level, pX, pY - 1, pZ - 1);
if (llTransx0z || llTrans0yz) { if (llTransx0z || llTrans0yz) {
llxyz = getShadeBrightness(tt, level, pX - 1, pY - 1, pZ); 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);
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 = isTranslucentAt(level, pX + 1, pY, pZ + 1);
bool llTransx0Z = Tile::transculent[level->getTile(pX - 1, pY, pZ + 1)]; bool llTransx0Z = isTranslucentAt(level, pX - 1, pY, pZ + 1);
bool llTrans0YZ = Tile::transculent[level->getTile(pX, pY + 1, pZ + 1)]; bool llTrans0YZ = isTranslucentAt(level, pX, pY + 1, pZ + 1);
bool llTrans0yZ = Tile::transculent[level->getTile(pX, pY - 1, pZ + 1)]; bool llTrans0yZ = isTranslucentAt(level, pX, pY - 1, pZ + 1);
if (llTransx0Z || llTrans0yZ) { if (llTransx0Z || llTrans0yZ) {
llxyZ = getShadeBrightness(tt, level, pX - 1, pY - 1, pZ); llxyZ = getShadeBrightness(tt, level, pX - 1, pY - 1, pZ);
@@ -5765,10 +5773,10 @@ bool TileRenderer::tesselateBlockInWorldWithAmbienceOcclusionTexLighting(
ccx0Z = getLightColor(tt, level, pX, pY, pZ + 1); ccx0Z = getLightColor(tt, level, pX, pY, pZ + 1);
ccxY0 = getLightColor(tt, level, pX, pY + 1, pZ); ccxY0 = getLightColor(tt, level, pX, pY + 1, pZ);
bool llTransxY0 = Tile::transculent[level->getTile(pX - 1, pY + 1, pZ)]; bool llTransxY0 = isTranslucentAt(level, pX - 1, pY + 1, pZ);
bool llTransxy0 = Tile::transculent[level->getTile(pX - 1, pY - 1, pZ)]; bool llTransxy0 = isTranslucentAt(level, pX - 1, pY - 1, pZ);
bool llTransx0z = Tile::transculent[level->getTile(pX - 1, pY, pZ - 1)]; bool llTransx0z = isTranslucentAt(level, pX - 1, pY, pZ - 1);
bool llTransx0Z = Tile::transculent[level->getTile(pX - 1, pY, pZ + 1)]; bool llTransx0Z = isTranslucentAt(level, pX - 1, pY, pZ + 1);
if (llTransx0z || llTransxy0) { if (llTransx0z || llTransxy0) {
llxyz = getShadeBrightness(tt, level, pX, pY - 1, pZ - 1); llxyz = getShadeBrightness(tt, level, pX, pY - 1, pZ - 1);
@@ -5929,10 +5937,10 @@ bool TileRenderer::tesselateBlockInWorldWithAmbienceOcclusionTexLighting(
ccX0Z = getLightColor(tt, level, pX, pY, pZ + 1); ccX0Z = getLightColor(tt, level, pX, pY, pZ + 1);
ccXY0 = getLightColor(tt, level, pX, pY + 1, pZ); ccXY0 = getLightColor(tt, level, pX, pY + 1, pZ);
bool llTransXY0 = Tile::transculent[level->getTile(pX + 1, pY + 1, pZ)]; bool llTransXY0 = isTranslucentAt(level, pX + 1, pY + 1, pZ);
bool llTransXy0 = Tile::transculent[level->getTile(pX + 1, pY - 1, pZ)]; bool llTransXy0 = isTranslucentAt(level, pX + 1, pY - 1, pZ);
bool llTransX0Z = Tile::transculent[level->getTile(pX + 1, pY, pZ + 1)]; bool llTransX0Z = isTranslucentAt(level, pX + 1, pY, pZ + 1);
bool llTransX0z = Tile::transculent[level->getTile(pX + 1, pY, pZ - 1)]; bool llTransX0z = isTranslucentAt(level, pX + 1, pY, pZ - 1);
if (llTransXy0 || llTransX0z) { if (llTransXy0 || llTransX0z) {
llXyz = getShadeBrightness(tt, level, pX, pY - 1, pZ - 1); llXyz = getShadeBrightness(tt, level, pX, pY - 1, pZ - 1);
@@ -30,6 +30,7 @@ class BeaconTile;
class HopperTile; class HopperTile;
class Icon; class Icon;
class Minecraft; class Minecraft;
struct ChunkBuildContext;
class TileRenderer { class TileRenderer {
friend class FallingTileRenderer; friend class FallingTileRenderer;
@@ -62,6 +63,7 @@ public:
bool isTranslucentAt(LevelSource* level, int x, int y, int z); bool isTranslucentAt(LevelSource* level, int x, int y, int z);
unsigned int* cache; unsigned int* cache;
unsigned char* tileIds; unsigned char* tileIds;
const ChunkBuildContext* buildContext;
static const unsigned int cache_getLightColor_valid = 0x80000000; static const unsigned int cache_getLightColor_valid = 0x80000000;
static const unsigned int cache_isTranslucentAt_valid = 0x40000000; static const unsigned int cache_isTranslucentAt_valid = 0x40000000;
static const unsigned int cache_isSolidBlockingTile_valid = 0x20000000; static const unsigned int cache_isSolidBlockingTile_valid = 0x20000000;
@@ -74,10 +76,12 @@ public:
public: public:
TileRenderer(LevelSource* level, int xMin, int yMin, int zMin, TileRenderer(LevelSource* level, int xMin, int yMin, int zMin,
unsigned char* tileIds); unsigned char* tileIds,
const ChunkBuildContext* buildContext = nullptr);
TileRenderer(LevelSource* level); TileRenderer(LevelSource* level);
TileRenderer(); TileRenderer();
~TileRenderer(); ~TileRenderer();
void setBuildContext(const ChunkBuildContext* buildContext);
void setFixedTexture(Icon* fixedTexture); void setFixedTexture(Icon* fixedTexture);
void clearFixedTexture(); void clearFixedTexture();
bool hasFixedTexture(); bool hasFixedTexture();