Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/main/java/rs117/hd/HdPlugin.java
Original file line number Diff line number Diff line change
Expand Up @@ -1935,7 +1935,7 @@ public float getGammaCorrection() {
}

public int getExpandedMapLoadingChunks() {
if (useLowMemoryMode)
if (useLowMemoryMode || sceneManager.isExpandedMapLoadingChunksOverridden())
return 0;
return config.expandedMapLoadingChunks();
}
Expand Down
4 changes: 2 additions & 2 deletions src/main/java/rs117/hd/renderer/legacy/LegacyModelPusher.java
Original file line number Diff line number Diff line change
Expand Up @@ -387,7 +387,7 @@ public void pushModel(
sceneContext.modelPusherResults[1] = texturedFaceCount;
}

private void getNormalDataForFace(SceneContext sceneContext, Model model, @Nonnull ModelOverride modelOverride, int face) {
private void getNormalDataForFace(LegacySceneContext sceneContext, Model model, @Nonnull ModelOverride modelOverride, int face) {
assert packTerrainData(false, 0, WaterType.NONE, 0) == 0;
if (modelOverride.flatNormals || !plugin.configPreserveVanillaNormals && model.getFaceColors3()[face] == -1) {
Arrays.fill(sceneContext.modelFaceNormals, 0);
Expand Down Expand Up @@ -424,7 +424,7 @@ private void getNormalDataForFace(SceneContext sceneContext, Model model, @Nonnu

@SuppressWarnings({ "ReassignedVariable" })
private int[] getFaceVertices(
SceneContext sceneContext,
LegacySceneContext sceneContext,
Tile tile,
int uuid,
Model model,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,12 @@ public class LegacySceneContext extends SceneContext {
public GpuFloatBuffer stagingBufferUvs;
public GpuFloatBuffer stagingBufferNormals;

// Model pusher arrays, to avoid simultaneous usage from different threads
public final int[] modelFaceVertices = new int[12];
public final float[] modelFaceUvs = new float[12];
public final float[] modelFaceNormals = new float[12];
public final int[] modelPusherResults = new int[2];

public LegacySceneContext(
Client client,
Scene scene,
Expand Down
78 changes: 71 additions & 7 deletions src/main/java/rs117/hd/renderer/zone/SceneManager.java
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@
import static rs117.hd.HdPlugin.checkGLErrors;
import static rs117.hd.renderer.zone.WorldViewContext.DYNAMIC_MODEL_VAO_POOL;
import static rs117.hd.renderer.zone.WorldViewContext.DYNAMIC_MODEL_VAO_STAGING_POOL;
import static rs117.hd.utils.HDUtils.hintGC;
import static rs117.hd.utils.HDUtils.isRunningCloseToMemoryCeiling;
import static rs117.hd.utils.MathUtils.*;

@Slf4j
Expand Down Expand Up @@ -98,6 +100,11 @@ public class SceneManager {
private Zone[][] nextZones;
private final List<SortedZone> sortedZones = new ArrayList<>();
private boolean reloadRequested;
@Getter
private boolean isExpandedMapLoadingChunksOverridden;
private boolean canRestoreExpandedMapLoadingChunks;
private long memoryState;
private long nextMemoryStateCheck;

public boolean isZoneStreamingEnabled() {
return plugin.configZoneStreaming;
Expand Down Expand Up @@ -136,6 +143,7 @@ public WorldViewContext getContext(int worldViewId) {
public void initialize(RenderState renderState, UBOWorldViews uboWorldViews) {
this.renderState = renderState;
this.uboWorldViews = uboWorldViews;
root.isFirstLoad = true;
root.initialize(renderState, injector);
}

Expand All @@ -159,12 +167,44 @@ public void destroy() {
uboWorldViews = null;
}

public boolean isConsistentlyRunningCloseToMemoryCeiling() {
int count = 0;
for(int i = 0; i < 64; i++) {
long mask = 1L << i;
if((memoryState & mask) == mask)
count++;
}
return count > 32;
}

public void update() {
assert client.isClientThread();
frameTimer.begin(Timer.UPDATE_AREA_HIDING);
updateAreaHiding();
frameTimer.end(Timer.UPDATE_AREA_HIDING);

if(System.currentTimeMillis() > nextMemoryStateCheck) {
if (isRunningCloseToMemoryCeiling(false)) {
memoryState |= 1L << (plugin.frame % 64L);
} else {
memoryState &= ~(1L << (plugin.frame % 64L));
}
nextMemoryStateCheck = System.currentTimeMillis() + 78;
}

if(isExpandedMapLoadingChunksOverridden) {
if(canRestoreExpandedMapLoadingChunks) {
isExpandedMapLoadingChunksOverridden = false;
canRestoreExpandedMapLoadingChunks = false;
client.setExpandedMapLoading(plugin.getExpandedMapLoadingChunks());
log.debug("Restored Expanded Map Loading Chunks to {}...", plugin.getExpandedMapLoadingChunks());
}
} else if (isConsistentlyRunningCloseToMemoryCeiling() && plugin.getExpandedMapLoadingChunks() > 0) {
log.debug("Disabling Expanded Map Loading Chunks to reduce memory pressure, this will be restored once memory has stabilized");
client.setExpandedMapLoading(0);
isExpandedMapLoadingChunksOverridden = true;
}

if (reloadRequested && loadingLock.getHoldCount() == 0) {
reloadRequested = false;
try {
Expand Down Expand Up @@ -421,6 +461,15 @@ public synchronized void loadScene(WorldView worldView, Scene scene) {
nextSceneContext.destroy();
nextSceneContext = null;

// Determine if we're consistently running close to the memory ceiling and force sync loading to reduce chances of OOMing
final boolean canAsyncExecute = !isConsistentlyRunningCloseToMemoryCeiling();
if(!canAsyncExecute) {
log.debug("Forcing sync load to reduce memory allocation pressure, load will be drastically slower...");
hintGC(1000);
} else if(isExpandedMapLoadingChunksOverridden) {
canRestoreExpandedMapLoadingChunks = true;
}

nextZones = new Zone[NUM_ZONES][NUM_ZONES];
nextSceneContext = new ZoneSceneContext(
client,
Expand Down Expand Up @@ -448,8 +497,16 @@ public synchronized void loadScene(WorldView worldView, Scene scene) {
loadSceneLightsTask.cancel();
calculateRoofChangesTask.cancel();

generateSceneDataTask.queue();
loadSceneLightsTask.queue();
if(root.sceneContext != null)
proceduralGenerator.moveSceneData(nextSceneContext, root.sceneContext);

generateSceneDataTask
.setExecuteAsync(canAsyncExecute)
.queue();

loadSceneLightsTask
.setExecuteAsync(canAsyncExecute)
.queue();

if (nextSceneContext.enableAreaHiding) {
assert nextSceneContext.sceneBase != null;
Expand Down Expand Up @@ -500,7 +557,9 @@ public synchronized void loadScene(WorldView worldView, Scene scene) {
}

// Queue after ensuring previous scene has been cancelled
calculateRoofChangesTask.queue();
calculateRoofChangesTask
.setExecuteAsync(canAsyncExecute)
.queue();

final int dx = scene.getBaseX() - prev.getBaseX() >> 3;
final int dy = scene.getBaseY() - prev.getBaseY() >> 3;
Expand Down Expand Up @@ -538,7 +597,7 @@ public synchronized void loadScene(WorldView worldView, Scene scene) {
}
}

boolean staggerLoad =
boolean staggerLoad = canAsyncExecute &&
isZoneStreamingEnabled() &&
!nextSceneContext.isInHouse &&
root.sceneContext != null &&
Expand All @@ -554,6 +613,7 @@ public synchronized void loadScene(WorldView worldView, Scene scene) {
if (!staggerLoad || dist < ZONE_DEFER_DIST_START) {
ZoneUploadJob
.build(ctx, nextSceneContext, zone, true, x, z)
.setExecuteAsync(canAsyncExecute)
.queue(ctx.sceneLoadGroup, generateSceneDataTask);
nextSceneContext.totalMapZones++;
} else {
Expand All @@ -579,6 +639,7 @@ public synchronized void loadScene(WorldView worldView, Scene scene) {
nextZones[sorted.x][sorted.z] = newZone;
ZoneUploadJob
.build(ctx, nextSceneContext, newZone, true, sorted.x, sorted.z)
.setExecuteAsync(canAsyncExecute)
.queue(ctx.sceneLoadGroup, generateSceneDataTask);
}
sorted.free();
Expand Down Expand Up @@ -612,13 +673,15 @@ public void swapScene(Scene scene) {
fishingSpotReplacer.despawnRuneLiteObjects();
npcDisplacementCache.clear();

boolean isFirst = root.sceneContext == null;
if (!isFirst)
if (root.sceneContext != null)
root.sceneContext.destroy(); // Destroy the old context before replacing it

// Wait for roof change calculation to complete
calculateRoofChangesTask.waitForCompletion();

if(isConsistentlyRunningCloseToMemoryCeiling())
hintGC();

WorldViewContext ctx = root;
if (!nextRoofChanges.isEmpty()) {
for (int x = 0; x < ctx.sizeX; ++x) {
Expand Down Expand Up @@ -693,7 +756,8 @@ public void swapScene(Scene scene) {
nextZones = null;
nextSceneContext = null;

if (isFirst) {
if (root.isFirstLoad) {
root.isFirstLoad = false;
root.initBuffers();

// Load all pre-existing sub scenes on the first scene load
Expand Down
1 change: 1 addition & 0 deletions src/main/java/rs117/hd/renderer/zone/WorldViewContext.java
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ public class WorldViewContext {
Zone[][] zones;
GLBuffer vboM;
boolean isLoading = true;
boolean isFirstLoad = true;

int minLevel, level, maxLevel;
Set<Integer> hideRoofIds;
Expand Down
6 changes: 0 additions & 6 deletions src/main/java/rs117/hd/renderer/zone/ZoneUploadJob.java
Original file line number Diff line number Diff line change
Expand Up @@ -150,10 +150,4 @@ public String toString() {
x, z
);
}

@Override
@SuppressWarnings("deprecation")
protected void finalize() {
log.debug("ZoneUploadJob finalized, it should have been pooled? - {}", this);
}
}
68 changes: 57 additions & 11 deletions src/main/java/rs117/hd/scene/ProceduralGenerator.java
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,41 @@ public void generateSceneData(SceneContext sceneContext)
log.debug("-- generateUnderwaterTerrain: {}ms", timerGenerateUnderwaterTerrain);
}

public void moveSceneData(SceneContext sceneContext, SceneContext prevSceneContext) {
sceneContext.tileIsWater = prevSceneContext.tileIsWater;
sceneContext.skipTile = prevSceneContext.skipTile;

for(int i = 0; i < sceneContext.tileIsWater.length; i++) {
for(int k = 0; k < sceneContext.tileIsWater.length; k++) {
Arrays.fill(sceneContext.tileIsWater[i][k], false);
Arrays.fill(sceneContext.skipTile[i][k], false);
}
}

sceneContext.vertexIsWater = prevSceneContext.vertexIsWater;
sceneContext.vertexIsWater.clear();

sceneContext.vertexIsLand = prevSceneContext.vertexIsLand;
sceneContext.vertexIsLand.clear();

sceneContext.vertexIsOverlay = prevSceneContext.vertexIsOverlay;
sceneContext.vertexIsOverlay.clear();

sceneContext.vertexIsUnderlay = prevSceneContext.vertexIsUnderlay;
sceneContext.vertexIsUnderlay.clear();

sceneContext.vertexUnderwaterDepth = prevSceneContext.vertexUnderwaterDepth;
sceneContext.vertexUnderwaterDepth.clear();

sceneContext.vertexTerrainNormals = prevSceneContext.vertexTerrainNormals;
sceneContext.vertexTerrainNormals.clear();

sceneContext.highPriorityColor = prevSceneContext.highPriorityColor;
sceneContext.highPriorityColor.clear();

clearSceneData(prevSceneContext);
}

public void clearSceneData(SceneContext sceneContext) {
sceneContext.tileIsWater = null;
sceneContext.vertexIsWater = null;
Expand All @@ -116,17 +151,22 @@ public void clearSceneData(SceneContext sceneContext) {
*/
private void generateTerrainData(SceneContext sceneContext)
{
sceneContext.vertexTerrainColor = new HashMap<>();
if(sceneContext.vertexTerrainColor == null)
sceneContext.vertexTerrainColor = new HashMap<>();
// used for overriding potentially undesirable vertex colors
// for example, colors that aren't supposed to be visible
sceneContext.highPriorityColor = new HashMap<>();
sceneContext.vertexTerrainTexture = new HashMap<>();
if(sceneContext.highPriorityColor == null)
sceneContext.highPriorityColor = new HashMap<>();
if(sceneContext.vertexTerrainTexture == null)
sceneContext.vertexTerrainTexture = new HashMap<>();
// for faces without an overlay is set to true
sceneContext.vertexIsUnderlay = new HashMap<>();
if(sceneContext.vertexIsUnderlay == null)
sceneContext.vertexIsUnderlay = new HashMap<>();
// for faces with an overlay is set to true
// the result of these maps can be used to determine the vertices
// between underlays and overlays for custom blending
sceneContext.vertexIsOverlay = new HashMap<>();
if(sceneContext.vertexIsOverlay == null)
sceneContext.vertexIsOverlay = new HashMap<>();

Tile[][][] tiles = sceneContext.scene.getExtendedTiles();
int sizeX = sceneContext.sizeX;
Expand Down Expand Up @@ -334,18 +374,23 @@ private void generateUnderwaterTerrain(SceneContext sceneContext)
int sizeX = sceneContext.sizeX;
int sizeY = sceneContext.sizeZ;
// true if a tile contains at least 1 face which qualifies as water
sceneContext.tileIsWater = new boolean[MAX_Z][sizeX][sizeY];
if(sceneContext.tileIsWater == null || sceneContext.tileIsWater[0].length != sizeX || sceneContext.tileIsWater[0].length != sizeY)
sceneContext.tileIsWater = new boolean[MAX_Z][sizeX][sizeY];
// true if a vertex is part of a face which qualifies as water; non-existent if not
sceneContext.vertexIsWater = new HashMap<>();
if(sceneContext.vertexIsWater == null)
sceneContext.vertexIsWater = new HashMap<>();
// true if a vertex is part of a face which qualifies as land; non-existent if not
// tiles along the shoreline will be true for both vertexIsWater and vertexIsLand
sceneContext.vertexIsLand = new HashMap<>();
if(sceneContext.vertexIsLand == null)
sceneContext.vertexIsLand = new HashMap<>();
// if true, the tile will be skipped when the scene is drawn
// this is due to certain edge cases with water on the same X/Y on different planes
sceneContext.skipTile = new boolean[MAX_Z][sizeX][sizeY];
if(sceneContext.skipTile == null || sceneContext.skipTile[0].length != sizeX || sceneContext.skipTile[0].length != sizeY)
sceneContext.skipTile = new boolean[MAX_Z][sizeX][sizeY];
// the height adjustment for each vertex, to be applied to the vertex'
// real height to create the underwater terrain
sceneContext.vertexUnderwaterDepth = new HashMap<>();
if(sceneContext.vertexUnderwaterDepth == null)
sceneContext.vertexUnderwaterDepth = new HashMap<>();
// the basic 'levels' of underwater terrain, used to sink terrain based on its distance
// from the shore, then used to produce the world-space height offset
// 0 = land
Expand Down Expand Up @@ -685,7 +730,8 @@ else if (tile.getSceneTileModel() != null)
*/
private void calculateTerrainNormals(SceneContext sceneContext)
{
sceneContext.vertexTerrainNormals = new HashMap<>();
if(sceneContext.vertexTerrainNormals == null)
sceneContext.vertexTerrainNormals = new HashMap<>();

for (Tile[][] plane : sceneContext.scene.getExtendedTiles()) {
for (Tile[] column : plane) {
Expand Down
6 changes: 0 additions & 6 deletions src/main/java/rs117/hd/scene/SceneContext.java
Original file line number Diff line number Diff line change
Expand Up @@ -79,12 +79,6 @@ public class SceneContext {
public final HashSet<Projectile> knownProjectiles = new HashSet<>();
public final ArrayList<TileObject> lightSpawnsToHandleOnClientThread = new ArrayList<>();

// Model pusher arrays, to avoid simultaneous usage from different threads
public final int[] modelFaceVertices = new int[12];
public final float[] modelFaceUvs = new float[12];
public final float[] modelFaceNormals = new float[12];
public final int[] modelPusherResults = new int[2];

public SceneContext(Client client, Scene scene, int expandedMapLoadingChunks) {
this.client = client;
this.scene = scene;
Expand Down
Loading