Compare commits
22 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 269f2c30fd | |||
| b674f49600 | |||
| b592012ba8 | |||
| 5d1e8a44fd | |||
| 40b27335ea | |||
| d0b07a5d2f | |||
| cb0fee9780 | |||
| 895e9276cd | |||
| 9ee0af8b01 | |||
| 69941fb7f8 | |||
| 36862a968f | |||
| 27204336b2 | |||
| 4846cf5019 | |||
| f7f3c1146f | |||
| aaa5e958f0 | |||
| 726da953bd | |||
| c4f4935fdd | |||
| 3ef8bd7e20 | |||
| ec72762067 | |||
| 4d0ed2a6dc | |||
| 7b252b173b | |||
| 7b0c66e3ae |
+1
-1
@@ -111,7 +111,7 @@ public abstract class DhApiBlockColorOverrideEvent implements IDhApiEvent<DhApiB
|
|||||||
public int getRed() { return ColorUtil.getRed(this.colorAsInt); }
|
public int getRed() { return ColorUtil.getRed(this.colorAsInt); }
|
||||||
public int getGreen() { return ColorUtil.getGreen(this.colorAsInt); }
|
public int getGreen() { return ColorUtil.getGreen(this.colorAsInt); }
|
||||||
public int getBlue() { return ColorUtil.getBlue(this.colorAsInt); }
|
public int getBlue() { return ColorUtil.getBlue(this.colorAsInt); }
|
||||||
public void setColor(int red, int green, int blue) throws IllegalArgumentException { setColor(255, red, green, blue); }
|
public void setColor(int red, int green, int blue) throws IllegalArgumentException { this.setColor(this.getAlpha(), red, green, blue); }
|
||||||
/**
|
/**
|
||||||
* Note: when if you set a partially transparent alpha channel the underlying {@link IDhApiBlockStateWrapper#getOpacity()}
|
* Note: when if you set a partially transparent alpha channel the underlying {@link IDhApiBlockStateWrapper#getOpacity()}
|
||||||
* method should also return a non-opaque value.
|
* method should also return a non-opaque value.
|
||||||
|
|||||||
+1
-1
@@ -35,7 +35,7 @@ import java.util.Map;
|
|||||||
*/
|
*/
|
||||||
public class DependencyInjector<BindableType extends IBindable> implements IDependencyInjector<BindableType> // Note to self: Don't try adding a generic type to IDhApiEvent, the constructor won't accept it
|
public class DependencyInjector<BindableType extends IBindable> implements IDependencyInjector<BindableType> // Note to self: Don't try adding a generic type to IDhApiEvent, the constructor won't accept it
|
||||||
{
|
{
|
||||||
protected final Map<Class<? extends BindableType>, ArrayList<BindableType>> dependencies = new HashMap<>();
|
protected final HashMap<Class<? extends BindableType>, ArrayList<BindableType>> dependencies = new HashMap<>();
|
||||||
|
|
||||||
/** Internal class reference to BindableType since we can't get it any other way. */
|
/** Internal class reference to BindableType since we can't get it any other way. */
|
||||||
protected final Class<? extends BindableType> bindableInterface;
|
protected final Class<? extends BindableType> bindableInterface;
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ public final class ModInfo
|
|||||||
public static final String NAME = "DistantHorizons";
|
public static final String NAME = "DistantHorizons";
|
||||||
/** Human-readable version of NAME */
|
/** Human-readable version of NAME */
|
||||||
public static final String READABLE_NAME = "Distant Horizons";
|
public static final String READABLE_NAME = "Distant Horizons";
|
||||||
public static final String VERSION = "3.0.2-b";
|
public static final String VERSION = "3.0.4-b-dev";
|
||||||
/** Returns true if the current build is an unstable developer build, false otherwise. */
|
/** Returns true if the current build is an unstable developer build, false otherwise. */
|
||||||
public static final boolean IS_DEV_BUILD = VERSION.toLowerCase().contains("dev");
|
public static final boolean IS_DEV_BUILD = VERSION.toLowerCase().contains("dev");
|
||||||
|
|
||||||
@@ -52,7 +52,7 @@ public final class ModInfo
|
|||||||
/** This version should be updated whenever new methods are added to the DH API */
|
/** This version should be updated whenever new methods are added to the DH API */
|
||||||
public static final int API_MINOR_VERSION = 1;
|
public static final int API_MINOR_VERSION = 1;
|
||||||
/** This version should be updated whenever non-breaking fixes are added to the DH API */
|
/** This version should be updated whenever non-breaking fixes are added to the DH API */
|
||||||
public static final int API_PATCH_VERSION = 0;
|
public static final int API_PATCH_VERSION = 1;
|
||||||
|
|
||||||
/** If the config file has an older version it'll be re-created from scratch. */
|
/** If the config file has an older version it'll be re-created from scratch. */
|
||||||
public static final int CONFIG_FILE_VERSION = 4;
|
public static final int CONFIG_FILE_VERSION = 4;
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ import com.seibel.distanthorizons.api.enums.config.EDhApiMcRenderingFadeMode;
|
|||||||
import com.seibel.distanthorizons.api.enums.rendering.EDhApiRenderPass;
|
import com.seibel.distanthorizons.api.enums.rendering.EDhApiRenderPass;
|
||||||
import com.seibel.distanthorizons.api.methods.events.abstractEvents.*;
|
import com.seibel.distanthorizons.api.methods.events.abstractEvents.*;
|
||||||
import com.seibel.distanthorizons.core.api.internal.rendering.DhRenderState;
|
import com.seibel.distanthorizons.core.api.internal.rendering.DhRenderState;
|
||||||
|
import com.seibel.distanthorizons.core.dependencyInjection.ModAccessorInjector;
|
||||||
import com.seibel.distanthorizons.core.enums.MinecraftTextFormat;
|
import com.seibel.distanthorizons.core.enums.MinecraftTextFormat;
|
||||||
import com.seibel.distanthorizons.core.file.structure.ClientOnlySaveStructure;
|
import com.seibel.distanthorizons.core.file.structure.ClientOnlySaveStructure;
|
||||||
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
|
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
|
||||||
@@ -40,6 +41,7 @@ import com.seibel.distanthorizons.core.util.objects.Pair;
|
|||||||
import com.seibel.distanthorizons.core.util.objects.RollingAverage;
|
import com.seibel.distanthorizons.core.util.objects.RollingAverage;
|
||||||
import com.seibel.distanthorizons.core.util.threading.ThreadPoolUtil;
|
import com.seibel.distanthorizons.core.util.threading.ThreadPoolUtil;
|
||||||
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftRenderWrapper;
|
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftRenderWrapper;
|
||||||
|
import com.seibel.distanthorizons.core.wrapperInterfaces.modAccessor.IIrisAccessor;
|
||||||
import com.seibel.distanthorizons.core.wrapperInterfaces.render.renderPass.IDhMetaRenderer;
|
import com.seibel.distanthorizons.core.wrapperInterfaces.render.renderPass.IDhMetaRenderer;
|
||||||
import com.seibel.distanthorizons.core.wrapperInterfaces.render.renderPass.IDhVanillaFadeRenderer;
|
import com.seibel.distanthorizons.core.wrapperInterfaces.render.renderPass.IDhVanillaFadeRenderer;
|
||||||
import com.seibel.distanthorizons.core.wrapperInterfaces.render.renderPass.IDhTestTriangleRenderer;
|
import com.seibel.distanthorizons.core.wrapperInterfaces.render.renderPass.IDhTestTriangleRenderer;
|
||||||
@@ -63,6 +65,7 @@ import org.jetbrains.annotations.NotNull;
|
|||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
import org.lwjgl.glfw.GLFW;
|
import org.lwjgl.glfw.GLFW;
|
||||||
|
|
||||||
|
import java.awt.*;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.concurrent.LinkedBlockingQueue;
|
import java.util.concurrent.LinkedBlockingQueue;
|
||||||
@@ -147,6 +150,15 @@ public class ClientApi
|
|||||||
|
|
||||||
public static long firstRenderTimeMs = 0;
|
public static long firstRenderTimeMs = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* keeping track of this is necessary to fix
|
||||||
|
* out-of-date LODs from rendering when the shading
|
||||||
|
* is changed by Iris, causing LODs to often
|
||||||
|
* lack the side shading, which looks pretty bad
|
||||||
|
* when shaders are disabled.
|
||||||
|
*/
|
||||||
|
private boolean irisShadersEnabledLastFrame = false;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//==============//
|
//==============//
|
||||||
@@ -160,7 +172,7 @@ public class ClientApi
|
|||||||
//==============//
|
//==============//
|
||||||
// world events //
|
// world events //
|
||||||
//==============//
|
//==============//
|
||||||
//region
|
//region world events
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* May be fired slightly before or after the associated
|
* May be fired slightly before or after the associated
|
||||||
@@ -245,7 +257,7 @@ public class ClientApi
|
|||||||
//==============//
|
//==============//
|
||||||
// level events //
|
// level events //
|
||||||
//==============//
|
//==============//
|
||||||
//region
|
//region level events
|
||||||
|
|
||||||
public void clientLevelUnloadEvent(IClientLevelWrapper level)
|
public void clientLevelUnloadEvent(IClientLevelWrapper level)
|
||||||
{
|
{
|
||||||
@@ -364,7 +376,7 @@ public class ClientApi
|
|||||||
//============//
|
//============//
|
||||||
// networking //
|
// networking //
|
||||||
//============//
|
//============//
|
||||||
//region
|
//region networking
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Forwards a decoded message into the registered handlers.
|
* Forwards a decoded message into the registered handlers.
|
||||||
@@ -404,7 +416,7 @@ public class ClientApi
|
|||||||
//===============//
|
//===============//
|
||||||
// LOD rendering //
|
// LOD rendering //
|
||||||
//===============//
|
//===============//
|
||||||
//region
|
//region lod rendering
|
||||||
|
|
||||||
/** Should be called before {@link ClientApi#renderDeferredLodsForShaders} */
|
/** Should be called before {@link ClientApi#renderDeferredLodsForShaders} */
|
||||||
public void renderLods() { this.renderLodLayer(false); }
|
public void renderLods() { this.renderLodLayer(false); }
|
||||||
@@ -428,12 +440,16 @@ public class ClientApi
|
|||||||
//===========//
|
//===========//
|
||||||
//region
|
//region
|
||||||
|
|
||||||
|
// only run these tasks once per frame
|
||||||
|
if (!renderingDeferredLayer)
|
||||||
|
{
|
||||||
//DhApiTerrainDataRepo.asyncDebugMethod(
|
//DhApiTerrainDataRepo.asyncDebugMethod(
|
||||||
// RENDER_STATE.clientLevelWrapper,
|
// RENDER_STATE.clientLevelWrapper,
|
||||||
// MC_CLIENT.getPlayerBlockPos().getX(),
|
// MC_CLIENT.getPlayerBlockPos().getX(),
|
||||||
// MC_CLIENT.getPlayerBlockPos().getY(),
|
// MC_CLIENT.getPlayerBlockPos().getY(),
|
||||||
// MC_CLIENT.getPlayerBlockPos().getZ()
|
// MC_CLIENT.getPlayerBlockPos().getZ()
|
||||||
//);
|
//);
|
||||||
|
}
|
||||||
|
|
||||||
//endregion
|
//endregion
|
||||||
|
|
||||||
@@ -499,6 +515,27 @@ public class ClientApi
|
|||||||
}
|
}
|
||||||
|
|
||||||
//endregion
|
//endregion
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//====================//
|
||||||
|
// Iris data re-build //
|
||||||
|
//====================//
|
||||||
|
//region
|
||||||
|
|
||||||
|
// delayed getter since ClientApi is created before this accessor is bound
|
||||||
|
IIrisAccessor irisAccessor = ModAccessorInjector.INSTANCE.get(IIrisAccessor.class);
|
||||||
|
if (irisAccessor != null)
|
||||||
|
{
|
||||||
|
boolean shadersActive = irisAccessor.isShaderPackInUse();
|
||||||
|
if (this.irisShadersEnabledLastFrame != shadersActive)
|
||||||
|
{
|
||||||
|
this.irisShadersEnabledLastFrame = shadersActive;
|
||||||
|
DhApi.Delayed.renderProxy.clearRenderDataCache();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//endregion
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -667,7 +704,7 @@ public class ClientApi
|
|||||||
//================//
|
//================//
|
||||||
// fade rendering //
|
// fade rendering //
|
||||||
//================//
|
//================//
|
||||||
//region
|
//region fade rendering
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The first fade pass.
|
* The first fade pass.
|
||||||
@@ -737,7 +774,7 @@ public class ClientApi
|
|||||||
//==========//
|
//==========//
|
||||||
// keyboard //
|
// keyboard //
|
||||||
//==========//
|
//==========//
|
||||||
//region
|
//region keyboard
|
||||||
|
|
||||||
/** Trigger once on key press, with CLIENT PLAYER. */
|
/** Trigger once on key press, with CLIENT PLAYER. */
|
||||||
public void keyPressedEvent(int glfwKey)
|
public void keyPressedEvent(int glfwKey)
|
||||||
@@ -773,7 +810,7 @@ public class ClientApi
|
|||||||
//======//
|
//======//
|
||||||
// chat //
|
// chat //
|
||||||
//======//
|
//======//
|
||||||
//region
|
//region chat
|
||||||
|
|
||||||
private void sendQueuedChatMessages()
|
private void sendQueuedChatMessages()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -964,6 +964,7 @@ public class Config
|
|||||||
public static ConfigCategory debugWireframe = new ConfigCategory.Builder().set(DebugWireframe.class).build();
|
public static ConfigCategory debugWireframe = new ConfigCategory.Builder().set(DebugWireframe.class).build();
|
||||||
public static ConfigCategory openGl = new ConfigCategory.Builder().set(OpenGl.class).build();
|
public static ConfigCategory openGl = new ConfigCategory.Builder().set(OpenGl.class).build();
|
||||||
public static ConfigCategory columnBuilderDebugging = new ConfigCategory.Builder().set(ColumnBuilderDebugging.class).build();
|
public static ConfigCategory columnBuilderDebugging = new ConfigCategory.Builder().set(ColumnBuilderDebugging.class).build();
|
||||||
|
public static ConfigCategory positionFinderDebugging = new ConfigCategory.Builder().set(PositionFinder.class).build();
|
||||||
public static ConfigCategory f3Screen = new ConfigCategory.Builder().set(F3Screen.class).build();
|
public static ConfigCategory f3Screen = new ConfigCategory.Builder().set(F3Screen.class).build();
|
||||||
public static ConfigCategory exampleConfigScreen = new ConfigCategory.Builder().set(ExampleConfigScreen.class).build();
|
public static ConfigCategory exampleConfigScreen = new ConfigCategory.Builder().set(ExampleConfigScreen.class).build();
|
||||||
|
|
||||||
@@ -1095,6 +1096,36 @@ public class Config
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static class PositionFinder
|
||||||
|
{
|
||||||
|
//public static ConfigUIComment positionFinderHeader = new ConfigUIComment.Builder().setParentConfigClass(ColumnBuilderDebugging.class).build();
|
||||||
|
|
||||||
|
public static ConfigEntry<Boolean> positionFinderEnable = new ConfigEntry.Builder<Boolean>()
|
||||||
|
.set(false)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
public static ConfigEntry<Integer> positionFinderDetailLevel = new ConfigEntry.Builder<Integer>()
|
||||||
|
.set((int) DhSectionPos.SECTION_MINIMUM_DETAIL_LEVEL)
|
||||||
|
.build();
|
||||||
|
public static ConfigEntry<Integer> positionFinderXPos = new ConfigEntry.Builder<Integer>()
|
||||||
|
.set(0)
|
||||||
|
.build();
|
||||||
|
public static ConfigEntry<Integer> positionFinderZPos = new ConfigEntry.Builder<Integer>()
|
||||||
|
.set(0)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
public static ConfigEntry<Integer> positionFinderMinBlockY = new ConfigEntry.Builder<Integer>()
|
||||||
|
.set(-64)
|
||||||
|
.build();
|
||||||
|
public static ConfigEntry<Integer> positionFinderMaxBlockY = new ConfigEntry.Builder<Integer>()
|
||||||
|
.set(125)
|
||||||
|
.build();
|
||||||
|
public static ConfigEntry<Float> positionFinderMarginPercent = new ConfigEntry.Builder<Float>()
|
||||||
|
.set(0.0f)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
public static class F3Screen
|
public static class F3Screen
|
||||||
{
|
{
|
||||||
public static ConfigUIComment f3ScreenHeader = new ConfigUIComment.Builder().setParentConfigClass(F3Screen.class).build();
|
public static ConfigUIComment f3ScreenHeader = new ConfigUIComment.Builder().setParentConfigClass(F3Screen.class).build();
|
||||||
|
|||||||
+51
-51
@@ -59,9 +59,19 @@ public final class BufferQuad
|
|||||||
|
|
||||||
public boolean hasError = false;
|
public boolean hasError = false;
|
||||||
|
|
||||||
|
// Pre-computed sort keys to avoid recomputing on every comparison
|
||||||
|
// Slight increase in memory for reduction in cpu usage
|
||||||
|
public final long sortKeyEastWest;
|
||||||
|
public final long sortKeyNorthSouth;
|
||||||
|
|
||||||
|
|
||||||
BufferQuad(
|
|
||||||
|
//=============//
|
||||||
|
// constructor //
|
||||||
|
//=============//
|
||||||
|
//region
|
||||||
|
|
||||||
|
public BufferQuad(
|
||||||
short x, short y, short z, short widthEastWest, short widthNorthSouthOrHeight,
|
short x, short y, short z, short widthEastWest, short widthNorthSouthOrHeight,
|
||||||
int color, byte irisBlockMaterialId, byte skylight, byte blockLight,
|
int color, byte irisBlockMaterialId, byte skylight, byte blockLight,
|
||||||
EDhDirection direction)
|
EDhDirection direction)
|
||||||
@@ -85,64 +95,46 @@ public final class BufferQuad
|
|||||||
this.skyLight = skylight;
|
this.skyLight = skylight;
|
||||||
this.blockLight = blockLight;
|
this.blockLight = blockLight;
|
||||||
this.direction = direction;
|
this.direction = direction;
|
||||||
|
this.sortKeyEastWest = computeSortKey(direction, true);
|
||||||
|
this.sortKeyNorthSouth = computeSortKey(direction, false);
|
||||||
}
|
}
|
||||||
|
private long computeSortKey(EDhDirection dir, boolean eastWest)
|
||||||
|
|
||||||
|
|
||||||
/** a rough but fast calculation */
|
|
||||||
double calculateDistance(double relativeX, double relativeY, double relativeZ)
|
|
||||||
{
|
{
|
||||||
return Math.pow(relativeX - this.x, 2) + Math.pow(relativeY - this.y, 2) + Math.pow(relativeZ - this.z, 2);
|
if (eastWest)
|
||||||
|
{
|
||||||
|
switch (dir.axis)
|
||||||
|
{
|
||||||
|
case X: return (long) x << 48 | (long) y << 32 | (long) z << 16;
|
||||||
|
case Y: return (long) y << 48 | (long) z << 32 | (long) x << 16;
|
||||||
|
case Z: return (long) z << 48 | (long) y << 32 | (long) x << 16;
|
||||||
|
default: throw new IllegalArgumentException("Invalid Axis enum: [" + dir.axis + "].");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
switch (dir.axis)
|
||||||
|
{
|
||||||
|
case X: return (long) x << 48 | (long) z << 32 | (long) y << 16;
|
||||||
|
case Y: return (long) y << 48 | (long) x << 32 | (long) z << 16;
|
||||||
|
case Z: return (long) z << 48 | (long) x << 32 | (long) y << 16;
|
||||||
|
default: throw new IllegalArgumentException("Invalid Axis enum: [" + dir.axis + "].");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** compares this quad's position to the given quad */
|
//endregion
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/** compares this quad's position to the given quad using pre-computed sort keys */
|
||||||
public int compare(BufferQuad quad, BufferMergeDirectionEnum compareDirection)
|
public int compare(BufferQuad quad, BufferMergeDirectionEnum compareDirection)
|
||||||
{
|
{
|
||||||
if (this.direction != quad.direction)
|
if (this.direction != quad.direction)
|
||||||
throw new IllegalArgumentException("The other quad is not in the same direction: " + quad.direction + " vs " + this.direction);
|
throw new IllegalArgumentException("The other quad is not in the same direction: " + quad.direction + " vs " + this.direction);
|
||||||
|
|
||||||
if (compareDirection == BufferMergeDirectionEnum.EastWest)
|
return compareDirection == BufferMergeDirectionEnum.EastWest
|
||||||
{
|
? Long.compare(this.sortKeyEastWest, quad.sortKeyEastWest)
|
||||||
switch (this.direction.axis)
|
: Long.compare(this.sortKeyNorthSouth, quad.sortKeyNorthSouth);
|
||||||
{
|
|
||||||
case X:
|
|
||||||
return threeDimensionalCompare(this.x, this.y, this.z, quad.x, quad.y, quad.z);
|
|
||||||
case Y:
|
|
||||||
return threeDimensionalCompare(this.y, this.z, this.x, quad.y, quad.z, quad.x);
|
|
||||||
case Z:
|
|
||||||
return threeDimensionalCompare(this.z, this.y, this.x, quad.z, quad.y, quad.x);
|
|
||||||
|
|
||||||
default:
|
|
||||||
throw new IllegalArgumentException("Invalid Axis enum: [" + this.direction.axis + "].");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
switch (this.direction.axis)
|
|
||||||
{
|
|
||||||
case X:
|
|
||||||
return threeDimensionalCompare(this.x, this.z, this.y, quad.x, quad.z, quad.y);
|
|
||||||
case Y:
|
|
||||||
return threeDimensionalCompare(this.y, this.x, this.z, quad.y, quad.x, quad.z);
|
|
||||||
case Z:
|
|
||||||
return threeDimensionalCompare(this.z, this.x, this.y, quad.z, quad.x, quad.y);
|
|
||||||
|
|
||||||
default:
|
|
||||||
throw new IllegalArgumentException("Invalid Axis enum: [" + this.direction.axis + "].");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* Compares two 3D points A and B. <br>
|
|
||||||
* The X, Y, and Z coordinates can be passed into parameters 0, 1, and 2 in any order
|
|
||||||
* provided they are in the same order for both A and B. <br>
|
|
||||||
* With the 0th parameter being the most significant when comparing.
|
|
||||||
*/
|
|
||||||
private static int threeDimensionalCompare(short a0, short a1, short a2, short b0, short b1, short b2)
|
|
||||||
{
|
|
||||||
long a = (long) a0 << 48 | (long) a1 << 32 | (long) a2 << 16;
|
|
||||||
long b = (long) b0 << 48 | (long) b1 << 32 | (long) b2 << 16;
|
|
||||||
return Long.compare(a, b);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -154,11 +146,15 @@ public final class BufferQuad
|
|||||||
public boolean tryMerge(BufferQuad quad, BufferMergeDirectionEnum mergeDirection)
|
public boolean tryMerge(BufferQuad quad, BufferMergeDirectionEnum mergeDirection)
|
||||||
{
|
{
|
||||||
if (quad.hasError || this.hasError)
|
if (quad.hasError || this.hasError)
|
||||||
|
{
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// only merge quads that are in the same direction
|
// only merge quads that are in the same direction
|
||||||
if (this.direction != quad.direction)
|
if (this.direction != quad.direction)
|
||||||
|
{
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// make sure these quads share the same perpendicular axis
|
// make sure these quads share the same perpendicular axis
|
||||||
if ((mergeDirection == BufferMergeDirectionEnum.EastWest && this.y != quad.y)
|
if ((mergeDirection == BufferMergeDirectionEnum.EastWest && this.y != quad.y)
|
||||||
@@ -175,7 +171,6 @@ public final class BufferQuad
|
|||||||
short otherParallelCompareStartPos;
|
short otherParallelCompareStartPos;
|
||||||
switch (this.direction.axis)
|
switch (this.direction.axis)
|
||||||
{
|
{
|
||||||
default: // shouldn't normally happen, just here to make the compiler happy
|
|
||||||
case X:
|
case X:
|
||||||
if (mergeDirection == BufferMergeDirectionEnum.EastWest)
|
if (mergeDirection == BufferMergeDirectionEnum.EastWest)
|
||||||
{
|
{
|
||||||
@@ -232,6 +227,9 @@ public final class BufferQuad
|
|||||||
otherParallelCompareStartPos = quad.z;
|
otherParallelCompareStartPos = quad.z;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
default: // shouldn't normally happen, just here to make the compiler happy
|
||||||
|
throw new IllegalArgumentException("Unsupported axis: ["+this.direction.axis+"]");
|
||||||
}
|
}
|
||||||
|
|
||||||
// get the width of this quad in the relevant axis
|
// get the width of this quad in the relevant axis
|
||||||
@@ -333,4 +331,6 @@ public final class BufferQuad
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+15
-6
@@ -341,13 +341,24 @@ public class ColumnBox
|
|||||||
|
|
||||||
|
|
||||||
// Apply light to the range [adjMinY, adjMaxY)
|
// Apply light to the range [adjMinY, adjMaxY)
|
||||||
applyLightToRange(segments, newSegments, adjMinY, adjMaxY, lightToApply);
|
applyLightToRangeAndPopulateNewSgements(segments, newSegments, adjMinY, adjMaxY, lightToApply);
|
||||||
|
{
|
||||||
|
// swap references so we can use the newly populated segments
|
||||||
|
LongArrayList temp = segments;
|
||||||
|
segments = newSegments;
|
||||||
|
newSegments = temp;
|
||||||
|
}
|
||||||
|
|
||||||
// Fill overhang area [adjMaxY, adjAboveMinY) with adjSkyLight
|
// Fill overhang area [adjMaxY, adjAboveMinY) with adjSkyLight
|
||||||
short adjAboveMinY = RenderDataPointUtil.getYMin(adjAbovePoint);
|
short adjAboveMinY = RenderDataPointUtil.getYMin(adjAbovePoint);
|
||||||
if (adjMaxY < adjAboveMinY)
|
if (adjMaxY < adjAboveMinY)
|
||||||
{
|
{
|
||||||
applyLightToRange(segments, newSegments, adjMaxY, adjAboveMinY, adjSkyLight);
|
applyLightToRangeAndPopulateNewSgements(segments, newSegments, adjMaxY, adjAboveMinY, adjSkyLight);
|
||||||
|
{
|
||||||
|
LongArrayList temp = segments;
|
||||||
|
segments = newSegments;
|
||||||
|
newSegments = temp;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -373,10 +384,11 @@ public class ColumnBox
|
|||||||
/**
|
/**
|
||||||
* Apply the new light value over the given y range,
|
* Apply the new light value over the given y range,
|
||||||
* splitting segments as needed
|
* splitting segments as needed
|
||||||
|
* and putting the new segments into "newSegments"
|
||||||
* <p>
|
* <p>
|
||||||
* source: claude.ai
|
* source: claude.ai
|
||||||
*/
|
*/
|
||||||
private static void applyLightToRange(
|
private static void applyLightToRangeAndPopulateNewSgements(
|
||||||
LongArrayList segments, LongArrayList newSegments,
|
LongArrayList segments, LongArrayList newSegments,
|
||||||
short rangeStart, short rangeEnd,
|
short rangeStart, short rangeEnd,
|
||||||
byte newLight)
|
byte newLight)
|
||||||
@@ -419,9 +431,6 @@ public class ColumnBox
|
|||||||
newSegments.add(YSegmentUtil.encode(rangeEnd, endY, skyLight));
|
newSegments.add(YSegmentUtil.encode(rangeEnd, endY, skyLight));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
segments.clear();
|
|
||||||
segments.addAll(newSegments);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void tryAddVerticalFaceWithSkyLightToBuilder(
|
private static void tryAddVerticalFaceWithSkyLightToBuilder(
|
||||||
|
|||||||
-20
@@ -55,26 +55,6 @@ public class ColumnRenderBufferBuilder
|
|||||||
// vbo building //
|
// vbo building //
|
||||||
//==============//
|
//==============//
|
||||||
|
|
||||||
/** @link adjData should be null for adjacent sections that cross detail level boundaries */
|
|
||||||
public static CompletableFuture<LodBufferContainer> uploadBuffersAsync(
|
|
||||||
IDhClientLevel clientLevel,
|
|
||||||
long pos,
|
|
||||||
LodQuadBuilder quadBuilder
|
|
||||||
)
|
|
||||||
{
|
|
||||||
DhBlockPos minBlockPos = new DhBlockPos(DhSectionPos.getMinCornerBlockX(pos), clientLevel.getLevelWrapper().getMinHeight(), DhSectionPos.getMinCornerBlockZ(pos));
|
|
||||||
LodBufferContainer bufferContainer = new LodBufferContainer(pos, minBlockPos);
|
|
||||||
CompletableFuture<LodBufferContainer> uploadFuture = bufferContainer.tryMakeAndUploadBuffersAsync(quadBuilder);
|
|
||||||
uploadFuture.whenComplete((uploadedBuffer, exception) ->
|
|
||||||
{
|
|
||||||
// clean up if not uploaded
|
|
||||||
if (uploadedBuffer != null && !uploadedBuffer.buffersUploaded)
|
|
||||||
{
|
|
||||||
uploadedBuffer.close();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return uploadFuture;
|
|
||||||
}
|
|
||||||
public static void makeLodRenderData(
|
public static void makeLodRenderData(
|
||||||
LodQuadBuilder quadBuilder, ColumnRenderSource renderSource, IDhClientLevel clientLevel,
|
LodQuadBuilder quadBuilder, ColumnRenderSource renderSource, IDhClientLevel clientLevel,
|
||||||
ColumnRenderSource[] adjRegions, boolean[] isSameDetailLevel)
|
ColumnRenderSource[] adjRegions, boolean[] isSameDetailLevel)
|
||||||
|
|||||||
+111
-159
@@ -20,13 +20,13 @@
|
|||||||
package com.seibel.distanthorizons.core.dataObjects.render.bufferBuilding;
|
package com.seibel.distanthorizons.core.dataObjects.render.bufferBuilding;
|
||||||
|
|
||||||
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
|
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
|
||||||
|
import com.seibel.distanthorizons.core.level.IDhClientLevel;
|
||||||
import com.seibel.distanthorizons.core.logging.DhLogger;
|
import com.seibel.distanthorizons.core.logging.DhLogger;
|
||||||
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
|
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
|
||||||
import com.seibel.distanthorizons.core.pos.DhSectionPos;
|
import com.seibel.distanthorizons.core.pos.DhSectionPos;
|
||||||
import com.seibel.distanthorizons.core.pos.blockPos.DhBlockPos;
|
import com.seibel.distanthorizons.core.pos.blockPos.DhBlockPos;
|
||||||
import com.seibel.distanthorizons.core.render.RenderThreadTaskHandler;
|
import com.seibel.distanthorizons.core.render.RenderThreadTaskHandler;
|
||||||
import com.seibel.distanthorizons.core.util.ExceptionUtil;
|
import com.seibel.distanthorizons.core.util.ExceptionUtil;
|
||||||
import com.seibel.distanthorizons.core.util.LodUtil;
|
|
||||||
import com.seibel.distanthorizons.core.wrapperInterfaces.IWrapperFactory;
|
import com.seibel.distanthorizons.core.wrapperInterfaces.IWrapperFactory;
|
||||||
import com.seibel.distanthorizons.core.wrapperInterfaces.render.AbstractDhRenderApiDefinition;
|
import com.seibel.distanthorizons.core.wrapperInterfaces.render.AbstractDhRenderApiDefinition;
|
||||||
import com.seibel.distanthorizons.core.wrapperInterfaces.render.objects.ILodContainerUniformBufferWrapper;
|
import com.seibel.distanthorizons.core.wrapperInterfaces.render.objects.ILodContainerUniformBufferWrapper;
|
||||||
@@ -37,7 +37,6 @@ import org.lwjgl.system.MemoryUtil;
|
|||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.concurrent.*;
|
import java.util.concurrent.*;
|
||||||
import java.util.concurrent.atomic.AtomicReference;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Java representation of one or more OpenGL buffers for rendering.
|
* Java representation of one or more OpenGL buffers for rendering.
|
||||||
@@ -63,8 +62,6 @@ public class LodBufferContainer implements AutoCloseable
|
|||||||
|
|
||||||
public ILodContainerUniformBufferWrapper uniformContainer = WRAPPER_FACTORY.createLodContainerUniformWrapper();
|
public ILodContainerUniformBufferWrapper uniformContainer = WRAPPER_FACTORY.createLodContainerUniformWrapper();
|
||||||
|
|
||||||
private final AtomicReference<CompletableFuture<LodBufferContainer>> uploadFutureRef = new AtomicReference<>(null);
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//==============//
|
//==============//
|
||||||
@@ -72,7 +69,7 @@ public class LodBufferContainer implements AutoCloseable
|
|||||||
//==============//
|
//==============//
|
||||||
//region
|
//region
|
||||||
|
|
||||||
public LodBufferContainer(long pos, DhBlockPos minCornerBlockPos)
|
private LodBufferContainer(long pos, DhBlockPos minCornerBlockPos)
|
||||||
{
|
{
|
||||||
this.pos = pos;
|
this.pos = pos;
|
||||||
this.minCornerBlockPos = minCornerBlockPos;
|
this.minCornerBlockPos = minCornerBlockPos;
|
||||||
@@ -92,41 +89,12 @@ public class LodBufferContainer implements AutoCloseable
|
|||||||
//region
|
//region
|
||||||
|
|
||||||
/** Should be run on a DH thread. */
|
/** Should be run on a DH thread. */
|
||||||
public synchronized CompletableFuture<LodBufferContainer> tryMakeAndUploadBuffersAsync(LodQuadBuilder builder)
|
public static CompletableFuture<LodBufferContainer> tryMakeAndUploadBuffersAsync(
|
||||||
|
long pos, IDhClientLevel clientLevel,
|
||||||
|
LodQuadBuilder builder)
|
||||||
{
|
{
|
||||||
//================//
|
|
||||||
// handle futures //
|
|
||||||
//================//
|
|
||||||
//region
|
|
||||||
|
|
||||||
// separate variable to prevent race condition when checking null
|
|
||||||
CompletableFuture<LodBufferContainer> oldFuture = this.uploadFutureRef.get();
|
|
||||||
if (oldFuture != null)
|
|
||||||
{
|
|
||||||
// upload already in process
|
|
||||||
return oldFuture;
|
|
||||||
}
|
|
||||||
|
|
||||||
// new upload needed
|
// new upload needed
|
||||||
CompletableFuture<LodBufferContainer> future = new CompletableFuture<>();
|
CompletableFuture<LodBufferContainer> future = new CompletableFuture<>();
|
||||||
future.handle((lodBufferContainer, throwable) ->
|
|
||||||
{
|
|
||||||
if (!this.uploadFutureRef.compareAndSet(future, null))
|
|
||||||
{
|
|
||||||
LOGGER.warn("upload future ref changed for pos ["+DhSectionPos.toString(this.pos)+"].");
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!this.uploadFutureRef.compareAndSet(null, future))
|
|
||||||
{
|
|
||||||
oldFuture = this.uploadFutureRef.get();
|
|
||||||
LodUtil.assertTrue(oldFuture != null, "Concurrency error");
|
|
||||||
return oldFuture;
|
|
||||||
}
|
|
||||||
|
|
||||||
//endregion
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -135,91 +103,119 @@ public class LodBufferContainer implements AutoCloseable
|
|||||||
//================//
|
//================//
|
||||||
//region
|
//region
|
||||||
|
|
||||||
|
DhBlockPos minCornerBlockPos = new DhBlockPos(
|
||||||
|
DhSectionPos.getMinCornerBlockX(pos),
|
||||||
|
clientLevel.getLevelWrapper().getMinHeight(),
|
||||||
|
DhSectionPos.getMinCornerBlockZ(pos));
|
||||||
|
LodBufferContainer bufferContainer = new LodBufferContainer(pos, minCornerBlockPos);
|
||||||
|
|
||||||
|
// create CPU vertex buffers
|
||||||
ArrayList<ByteBuffer> opaqueBuffers = builder.makeOpaqueVertexBuffers();
|
ArrayList<ByteBuffer> opaqueBuffers = builder.makeOpaqueVertexBuffers();
|
||||||
ArrayList<ByteBuffer> transparentBuffers = builder.makeTransparentVertexBuffers();
|
ArrayList<ByteBuffer> transparentBuffers = builder.makeTransparentVertexBuffers();
|
||||||
|
|
||||||
this.vboOpaqueWrappers = resizeWrapperArray(this.vboOpaqueWrappers, opaqueBuffers.size());
|
// update arrays to contain buffers
|
||||||
this.vboTransparentWrappers = resizeWrapperArray(this.vboTransparentWrappers, transparentBuffers.size());
|
bufferContainer.vboOpaqueWrappers = resizeWrapperArray(bufferContainer.vboOpaqueWrappers, opaqueBuffers.size());
|
||||||
|
bufferContainer.vboTransparentWrappers = resizeWrapperArray(bufferContainer.vboTransparentWrappers, transparentBuffers.size());
|
||||||
|
|
||||||
// mac requires separate IBO objects for each VBO when using OpenGL,
|
// create CPU index buffers if needed.
|
||||||
|
// Mac requires separate IBO objects for each VBO when using OpenGL,
|
||||||
// all other OS's can share a single IBO for quicker loading times
|
// all other OS's can share a single IBO for quicker loading times
|
||||||
boolean useSingleIbo = RENDER_DEF.useSingleIbo();
|
boolean useSingleIbo = RENDER_DEF.useSingleIbo();
|
||||||
@Nullable ArrayList<ByteBuffer> opaqueIndexBuffers = useSingleIbo ? null : this.createIndexBuffers(opaqueBuffers);
|
@Nullable ArrayList<ByteBuffer> opaqueIndexBuffers = useSingleIbo ? null : bufferContainer.createIndexBuffers(opaqueBuffers);
|
||||||
@Nullable ArrayList<ByteBuffer> transparentIndexBuffers = useSingleIbo ? null : this.createIndexBuffers(transparentBuffers);
|
@Nullable ArrayList<ByteBuffer> transparentIndexBuffers = useSingleIbo ? null : bufferContainer.createIndexBuffers(transparentBuffers);
|
||||||
|
|
||||||
//endregion
|
//endregion
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//================//
|
|
||||||
// upload buffers //
|
|
||||||
//================//
|
|
||||||
//region
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
//=============//
|
//=============//
|
||||||
// create VBOs //
|
// create VBOs //
|
||||||
//=============//
|
//=============//
|
||||||
|
//region
|
||||||
|
|
||||||
CompletableFuture<Void> createOpaqueFuture = createBufferWrappersAsync(future, this.vboOpaqueWrappers, opaqueBuffers);
|
CompletableFuture<Void> createFuture = new CompletableFuture<Void>();
|
||||||
CompletableFuture<Void> createTransparentFuture = createBufferWrappersAsync(future, this.vboTransparentWrappers, transparentBuffers);
|
RenderThreadTaskHandler.INSTANCE.queueRunningOnRenderThread("LodBufferContainer Setup", () ->
|
||||||
|
|
||||||
CompletableFuture<Void> createFuture = CompletableFuture.allOf(createOpaqueFuture, createTransparentFuture);
|
|
||||||
createFuture.exceptionally((Throwable e) ->
|
|
||||||
{
|
{
|
||||||
// create VBOs failed //
|
try
|
||||||
|
|
||||||
if (!ExceptionUtil.isShutdownException(e))
|
|
||||||
{
|
{
|
||||||
LOGGER.error("Unexpected issue creating buffer [" + this.minCornerBlockPos + "], error: [" + e.getMessage() + "].", e);
|
// skip this event if requested
|
||||||
|
if (Thread.interrupted()
|
||||||
|
|| future.isCancelled())
|
||||||
|
{
|
||||||
|
throw new InterruptedException();
|
||||||
}
|
}
|
||||||
future.completeExceptionally(e);
|
|
||||||
return null;
|
|
||||||
});
|
|
||||||
createFuture.thenRun(() ->
|
|
||||||
{
|
|
||||||
//=============//
|
|
||||||
// upload VBOs //
|
|
||||||
//=============//
|
|
||||||
|
|
||||||
CompletableFuture<Void> opaqueFuture = uploadBuffersAsync(future, this.vboOpaqueWrappers, opaqueBuffers, opaqueIndexBuffers);
|
createBufferWrappers(bufferContainer.vboOpaqueWrappers, opaqueBuffers);
|
||||||
CompletableFuture<Void> transparentFuture = uploadBuffersAsync(future, this.vboTransparentWrappers, transparentBuffers, transparentIndexBuffers);
|
createBufferWrappers(bufferContainer.vboTransparentWrappers, transparentBuffers);
|
||||||
|
|
||||||
CompletableFuture<Void> uploadFuture = CompletableFuture.allOf(opaqueFuture, transparentFuture);
|
createFuture.complete(null);
|
||||||
uploadFuture.exceptionally((Throwable e) ->
|
|
||||||
{
|
|
||||||
// upload failed //
|
|
||||||
|
|
||||||
if (!ExceptionUtil.isShutdownException(e))
|
|
||||||
{
|
|
||||||
LOGGER.error("Unexpected issue uploading buffer [" + this.minCornerBlockPos + "], error: [" + e.getMessage() + "].", e);
|
|
||||||
}
|
|
||||||
future.completeExceptionally(e);
|
|
||||||
return null;
|
|
||||||
});
|
|
||||||
uploadFuture.thenRun(() ->
|
|
||||||
{
|
|
||||||
// upload success /
|
|
||||||
|
|
||||||
this.buffersUploaded = true;
|
|
||||||
future.complete(this);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
if (!ExceptionUtil.isShutdownException(e))
|
if (!ExceptionUtil.isShutdownException(e))
|
||||||
{
|
{
|
||||||
LOGGER.error("Unexpected issue prepping buffer uploading [" + this.minCornerBlockPos + "], error: [" + e.getMessage() + "].", e);
|
LOGGER.error("Unexpected issue creating buffers for pos: ["+DhSectionPos.toString(bufferContainer.pos)+"], error: ["+e.getMessage()+"].", e);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bufferContainer.close();
|
||||||
|
createFuture.completeExceptionally(e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
//endregion
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//====================//
|
||||||
|
// upload VBOs to GPU //
|
||||||
|
//====================//
|
||||||
|
//region
|
||||||
|
|
||||||
|
createFuture.exceptionally((Throwable e) ->
|
||||||
|
{
|
||||||
|
// create VBOs failed //
|
||||||
|
if (!ExceptionUtil.isShutdownException(e))
|
||||||
|
{
|
||||||
|
LOGGER.error("Unexpected issue creating buffer [" + bufferContainer.minCornerBlockPos + "], error: [" + e.getMessage() + "].", e);
|
||||||
|
}
|
||||||
|
|
||||||
|
bufferContainer.close();
|
||||||
future.completeExceptionally(e);
|
future.completeExceptionally(e);
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
createFuture.thenRun(() ->
|
||||||
|
{
|
||||||
|
CompletableFuture<Void> opaqueFuture = uploadBuffersAsync(future, bufferContainer.vboOpaqueWrappers, opaqueBuffers, opaqueIndexBuffers);
|
||||||
|
CompletableFuture<Void> transparentFuture = uploadBuffersAsync(future, bufferContainer.vboTransparentWrappers, transparentBuffers, transparentIndexBuffers);
|
||||||
|
CompletableFuture<Void> uploadFuture = CompletableFuture.allOf(opaqueFuture, transparentFuture);
|
||||||
|
uploadFuture.exceptionally((Throwable e) ->
|
||||||
|
{
|
||||||
|
// upload failed //
|
||||||
|
if (!ExceptionUtil.isShutdownException(e))
|
||||||
|
{
|
||||||
|
LOGGER.error("Unexpected issue uploading buffer [" + bufferContainer.minCornerBlockPos + "], error: [" + e.getMessage() + "].", e);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bufferContainer.close();
|
||||||
|
future.completeExceptionally(e);
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
uploadFuture.thenRun(() ->
|
||||||
|
{
|
||||||
|
// upload success //
|
||||||
|
bufferContainer.buffersUploaded = true;
|
||||||
|
future.complete(bufferContainer);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
//================//
|
//endregion
|
||||||
// buffer cleanup //
|
|
||||||
//================//
|
|
||||||
|
|
||||||
|
//====================//
|
||||||
|
// CPU Buffer cleanup //
|
||||||
|
//====================//
|
||||||
|
//region
|
||||||
|
|
||||||
future.whenComplete((LodBufferContainer lodBufferContainer, Throwable throwable) ->
|
future.whenComplete((LodBufferContainer lodBufferContainer, Throwable throwable) ->
|
||||||
{
|
{
|
||||||
@@ -290,11 +286,8 @@ public class LodBufferContainer implements AutoCloseable
|
|||||||
return newVbos;
|
return newVbos;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static CompletableFuture<Void> createBufferWrappersAsync(
|
private static void createBufferWrappers(IVertexBufferWrapper[] vboWrappers, ArrayList<ByteBuffer> vertexBuffers)
|
||||||
CompletableFuture<LodBufferContainer> parentFuture,
|
|
||||||
IVertexBufferWrapper[] vboWrappers, ArrayList<ByteBuffer> vertexBuffers)
|
|
||||||
{
|
{
|
||||||
ArrayList<CompletableFuture<Void>> createVboFutureList = new ArrayList<>();
|
|
||||||
for (int i = 0; i < vertexBuffers.size(); i++)
|
for (int i = 0; i < vertexBuffers.size(); i++)
|
||||||
{
|
{
|
||||||
if (i >= vboWrappers.length)
|
if (i >= vboWrappers.length)
|
||||||
@@ -304,45 +297,9 @@ public class LodBufferContainer implements AutoCloseable
|
|||||||
|
|
||||||
if (vboWrappers[i] == null)
|
if (vboWrappers[i] == null)
|
||||||
{
|
{
|
||||||
final int finalVboIndex = i;
|
vboWrappers[i] = WRAPPER_FACTORY.createVboWrapper("distantHorizons:McLodRenderer");
|
||||||
|
|
||||||
CompletableFuture<Void> future = new CompletableFuture<>();
|
|
||||||
createVboFutureList.add(future);
|
|
||||||
|
|
||||||
RenderThreadTaskHandler.INSTANCE.queueRunningOnRenderThread("LodBufferContainer Setup", () ->
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
// skip this event if requested
|
|
||||||
if (Thread.interrupted()
|
|
||||||
|| parentFuture.isCancelled())
|
|
||||||
{
|
|
||||||
throw new InterruptedException();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
vboWrappers[finalVboIndex] = WRAPPER_FACTORY.createVboWrapper("distantHorizons:McLodRenderer");
|
|
||||||
future.complete(null);
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
future.completeExceptionally(e);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (createVboFutureList.size() == 0)
|
|
||||||
{
|
|
||||||
return CompletableFuture.completedFuture(null);
|
|
||||||
}
|
|
||||||
|
|
||||||
CompletableFuture<?>[] futureArray = new CompletableFuture[createVboFutureList.size()];
|
|
||||||
for (int i = 0; i < createVboFutureList.size(); i++)
|
|
||||||
{
|
|
||||||
futureArray[i] = createVboFutureList.get(i);
|
|
||||||
}
|
|
||||||
return CompletableFuture.allOf(futureArray);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Index buffers should be null if {@link AbstractDhRenderApiDefinition#useSingleIbo()} returns true. */
|
/** Index buffers should be null if {@link AbstractDhRenderApiDefinition#useSingleIbo()} returns true. */
|
||||||
@@ -365,8 +322,6 @@ public class LodBufferContainer implements AutoCloseable
|
|||||||
|
|
||||||
// final variables for use in lambdas //
|
// final variables for use in lambdas //
|
||||||
|
|
||||||
final int finalVboIndex = vboIndex;
|
|
||||||
|
|
||||||
final IVertexBufferWrapper finalVboWrapper = vboWrappers[vboIndex];
|
final IVertexBufferWrapper finalVboWrapper = vboWrappers[vboIndex];
|
||||||
|
|
||||||
final ByteBuffer finalVertexBuffer = vertexBuffers.get(vboIndex);
|
final ByteBuffer finalVertexBuffer = vertexBuffers.get(vboIndex);
|
||||||
@@ -385,6 +340,8 @@ public class LodBufferContainer implements AutoCloseable
|
|||||||
CompletableFuture<Void> vertexUploadFuture = new CompletableFuture<>();
|
CompletableFuture<Void> vertexUploadFuture = new CompletableFuture<>();
|
||||||
uploadFutureList.add(vertexUploadFuture);
|
uploadFutureList.add(vertexUploadFuture);
|
||||||
|
|
||||||
|
|
||||||
|
final StackTraceElement[] stackTraceElements = Thread.currentThread().getStackTrace();
|
||||||
RenderThreadTaskHandler.INSTANCE.queueRunningOnRenderThread("LodBufferContainer VBO Upload", () ->
|
RenderThreadTaskHandler.INSTANCE.queueRunningOnRenderThread("LodBufferContainer VBO Upload", () ->
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
@@ -396,21 +353,12 @@ public class LodBufferContainer implements AutoCloseable
|
|||||||
throw new InterruptedException();
|
throw new InterruptedException();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
finalVboWrapper.uploadVertexBuffer(finalVertexBuffer, finalVertexCount);
|
finalVboWrapper.uploadVertexBuffer(finalVertexBuffer, finalVertexCount);
|
||||||
vertexUploadFuture.complete(null);
|
vertexUploadFuture.complete(null);
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
vboWrappers[finalVboIndex] = null;
|
|
||||||
finalVboWrapper.close();
|
|
||||||
LOGGER.error("Failed to upload buffer. Error: [" + e.getMessage() + "].", e);
|
LOGGER.error("Failed to upload buffer. Error: [" + e.getMessage() + "].", e);
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
vertexUploadFuture.completeExceptionally(e);
|
vertexUploadFuture.completeExceptionally(e);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -445,6 +393,7 @@ public class LodBufferContainer implements AutoCloseable
|
|||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
|
finalVboWrapper.close();
|
||||||
indexUploadFuture.completeExceptionally(e);
|
indexUploadFuture.completeExceptionally(e);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -532,26 +481,29 @@ public class LodBufferContainer implements AutoCloseable
|
|||||||
|
|
||||||
RenderThreadTaskHandler.INSTANCE.queueRunningOnRenderThread("LodBufferContainer Close", () ->
|
RenderThreadTaskHandler.INSTANCE.queueRunningOnRenderThread("LodBufferContainer Close", () ->
|
||||||
{
|
{
|
||||||
for (IVertexBufferWrapper buffer : this.vboOpaqueWrappers)
|
tryCloseBufferWrapperArray(this.vboOpaqueWrappers);
|
||||||
{
|
tryCloseBufferWrapperArray(this.vboTransparentWrappers);
|
||||||
if (buffer != null)
|
|
||||||
{
|
|
||||||
buffer.close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (IVertexBufferWrapper buffer : this.vboTransparentWrappers)
|
|
||||||
{
|
|
||||||
if (buffer != null)
|
|
||||||
{
|
|
||||||
buffer.close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
this.uniformContainer.close();
|
this.uniformContainer.close();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void tryCloseBufferWrapperArray(@Nullable IVertexBufferWrapper[] bufferWrappers)
|
||||||
|
{
|
||||||
|
if (bufferWrappers != null)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < bufferWrappers.length; i++)
|
||||||
|
{
|
||||||
|
IVertexBufferWrapper buffer = bufferWrappers[i];
|
||||||
|
bufferWrappers[i] = null;
|
||||||
|
if (buffer != null)
|
||||||
|
{
|
||||||
|
buffer.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//endregion
|
//endregion
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
+2
-2
@@ -61,7 +61,7 @@ public class LodQuadBuilder
|
|||||||
public static final int BYTES_PER_QUAD = BYTES_PER_VERTEX * 4;
|
public static final int BYTES_PER_QUAD = BYTES_PER_VERTEX * 4;
|
||||||
|
|
||||||
public static final int[][][] DIRECTION_VERTEX_IBO_QUAD = new int[][][]
|
public static final int[][][] DIRECTION_VERTEX_IBO_QUAD = new int[][][]
|
||||||
///region
|
//region
|
||||||
{
|
{
|
||||||
// X,Z //
|
// X,Z //
|
||||||
{ // UP
|
{ // UP
|
||||||
@@ -109,7 +109,7 @@ public class LodQuadBuilder
|
|||||||
{0, 0}, // 3
|
{0, 0}, // 3
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
///endregion
|
//endregion
|
||||||
|
|
||||||
private int premergeCount = 0;
|
private int premergeCount = 0;
|
||||||
|
|
||||||
|
|||||||
+17
-6
@@ -129,6 +129,7 @@ public class FullDataToRenderDataTransformer
|
|||||||
ColumnRenderView tempExpandingColumnView = ColumnRenderView.getPooled();
|
ColumnRenderView tempExpandingColumnView = ColumnRenderView.getPooled();
|
||||||
RenderDataPointReducingList reducingList = new RenderDataPointReducingList())
|
RenderDataPointReducingList reducingList = new RenderDataPointReducingList())
|
||||||
{
|
{
|
||||||
|
DhBlockPosMutable mutableBlockPos = new DhBlockPosMutable();
|
||||||
for (int x = 0; x < FullDataSourceV2.WIDTH; x++)
|
for (int x = 0; x < FullDataSourceV2.WIDTH; x++)
|
||||||
{
|
{
|
||||||
for (int z = 0; z < FullDataSourceV2.WIDTH; z++)
|
for (int z = 0; z < FullDataSourceV2.WIDTH; z++)
|
||||||
@@ -142,7 +143,7 @@ public class FullDataToRenderDataTransformer
|
|||||||
baseX + BitShiftUtil.pow(x, dataDetail), baseZ + BitShiftUtil.pow(z, dataDetail),
|
baseX + BitShiftUtil.pow(x, dataDetail), baseZ + BitShiftUtil.pow(z, dataDetail),
|
||||||
columnArrayView, dataColumn,
|
columnArrayView, dataColumn,
|
||||||
// pooled references so we don't need to re-allocate/get them 4000 times per render source
|
// pooled references so we don't need to re-allocate/get them 4000 times per render source
|
||||||
phantomCheckout, tempExpandingColumnView, reducingList);
|
phantomCheckout, tempExpandingColumnView, reducingList, mutableBlockPos);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -157,7 +158,7 @@ public class FullDataToRenderDataTransformer
|
|||||||
ColumnRenderView columnArrayView,
|
ColumnRenderView columnArrayView,
|
||||||
LongArrayList fullDataColumn,
|
LongArrayList fullDataColumn,
|
||||||
// pooled references
|
// pooled references
|
||||||
PhantomArrayListCheckout phantomCheckout, ColumnRenderView tempExpandingColumnView, RenderDataPointReducingList reducingList)
|
PhantomArrayListCheckout phantomCheckout, ColumnRenderView tempExpandingColumnView, RenderDataPointReducingList reducingList, DhBlockPosMutable mutableBlockPos)
|
||||||
{
|
{
|
||||||
// we can't do anything if the full data is missing or empty
|
// we can't do anything if the full data is missing or empty
|
||||||
if (fullDataColumn == null
|
if (fullDataColumn == null
|
||||||
@@ -170,7 +171,7 @@ public class FullDataToRenderDataTransformer
|
|||||||
if (fullDataLength <= columnArrayView.maxVerticalSliceCount)
|
if (fullDataLength <= columnArrayView.maxVerticalSliceCount)
|
||||||
{
|
{
|
||||||
// Directly use the arrayView since it fits.
|
// Directly use the arrayView since it fits.
|
||||||
setRenderColumnView(levelWrapper, fullDataSource, blockX, blockZ, columnArrayView, fullDataColumn);
|
setRenderColumnView(levelWrapper, fullDataSource, blockX, blockZ, columnArrayView, fullDataColumn, mutableBlockPos);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -178,7 +179,7 @@ public class FullDataToRenderDataTransformer
|
|||||||
|
|
||||||
// expand the ColumnArrayView to fit the new larger max vertical size
|
// expand the ColumnArrayView to fit the new larger max vertical size
|
||||||
tempExpandingColumnView.populate(dataArrayList, fullDataLength, 0, fullDataLength);
|
tempExpandingColumnView.populate(dataArrayList, fullDataLength, 0, fullDataLength);
|
||||||
setRenderColumnView(levelWrapper, fullDataSource, blockX, blockZ, tempExpandingColumnView, fullDataColumn);
|
setRenderColumnView(levelWrapper, fullDataSource, blockX, blockZ, tempExpandingColumnView, fullDataColumn, mutableBlockPos);
|
||||||
|
|
||||||
columnArrayView.changeVerticalSizeFrom(tempExpandingColumnView, reducingList);
|
columnArrayView.changeVerticalSizeFrom(tempExpandingColumnView, reducingList);
|
||||||
}
|
}
|
||||||
@@ -186,7 +187,7 @@ public class FullDataToRenderDataTransformer
|
|||||||
private static void setRenderColumnView(
|
private static void setRenderColumnView(
|
||||||
IClientLevelWrapper levelWrapper, FullDataSourceV2 fullDataSource,
|
IClientLevelWrapper levelWrapper, FullDataSourceV2 fullDataSource,
|
||||||
int blockX, int blockZ,
|
int blockX, int blockZ,
|
||||||
ColumnRenderView renderColumnData, LongArrayList fullColumnData)
|
ColumnRenderView renderColumnData, LongArrayList fullColumnData, DhBlockPosMutable mutableBlockPos)
|
||||||
{
|
{
|
||||||
//===============//
|
//===============//
|
||||||
// config values //
|
// config values //
|
||||||
@@ -242,7 +243,8 @@ public class FullDataToRenderDataTransformer
|
|||||||
|
|
||||||
FullDataPointIdMap fullDataMapping = fullDataSource.mapping;
|
FullDataPointIdMap fullDataMapping = fullDataSource.mapping;
|
||||||
|
|
||||||
DhBlockPosMutable mutableBlockPos = new DhBlockPosMutable(blockX, 0, blockZ);
|
mutableBlockPos.setX(blockX);
|
||||||
|
mutableBlockPos.setZ(blockZ);
|
||||||
|
|
||||||
// goes from the top down
|
// goes from the top down
|
||||||
for (int fullDataIndex = 0; fullDataIndex < fullColumnData.size(); fullDataIndex++)
|
for (int fullDataIndex = 0; fullDataIndex < fullColumnData.size(); fullDataIndex++)
|
||||||
@@ -450,9 +452,18 @@ public class FullDataToRenderDataTransformer
|
|||||||
// use the previous block's color
|
// use the previous block's color
|
||||||
color = colorToApplyToNextBlock;
|
color = colorToApplyToNextBlock;
|
||||||
colorToApplyToNextBlock = -1;
|
colorToApplyToNextBlock = -1;
|
||||||
|
|
||||||
|
// use the skylight override if present
|
||||||
|
if (skylightToApplyToNextBlock != -1)
|
||||||
|
{
|
||||||
skyLight = skylightToApplyToNextBlock;
|
skyLight = skylightToApplyToNextBlock;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (blocklightToApplyToNextBlock != -1)
|
||||||
|
{
|
||||||
blockLight = blocklightToApplyToNextBlock;
|
blockLight = blocklightToApplyToNextBlock;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -29,8 +29,10 @@ import com.seibel.distanthorizons.core.pos.blockPos.DhBlockPos2D;
|
|||||||
import com.seibel.distanthorizons.core.render.QuadTree.LodQuadTree;
|
import com.seibel.distanthorizons.core.render.QuadTree.LodQuadTree;
|
||||||
import com.seibel.distanthorizons.core.render.RenderBufferHandler;
|
import com.seibel.distanthorizons.core.render.RenderBufferHandler;
|
||||||
import com.seibel.distanthorizons.core.util.LodUtil;
|
import com.seibel.distanthorizons.core.util.LodUtil;
|
||||||
|
import com.seibel.distanthorizons.core.util.math.Vec3d;
|
||||||
import com.seibel.distanthorizons.core.wrapperInterfaces.IWrapperFactory;
|
import com.seibel.distanthorizons.core.wrapperInterfaces.IWrapperFactory;
|
||||||
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper;
|
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper;
|
||||||
|
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftRenderWrapper;
|
||||||
import com.seibel.distanthorizons.core.wrapperInterfaces.render.renderPass.IDhGenericRenderer;
|
import com.seibel.distanthorizons.core.wrapperInterfaces.render.renderPass.IDhGenericRenderer;
|
||||||
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper;
|
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper;
|
||||||
import com.seibel.distanthorizons.core.logging.DhLogger;
|
import com.seibel.distanthorizons.core.logging.DhLogger;
|
||||||
@@ -44,6 +46,7 @@ public class ClientLevelModule implements Closeable, IDataSourceUpdateListenerFu
|
|||||||
{
|
{
|
||||||
private static final DhLogger LOGGER = new DhLoggerBuilder().build();
|
private static final DhLogger LOGGER = new DhLoggerBuilder().build();
|
||||||
private static final IMinecraftClientWrapper MC_CLIENT = SingletonInjector.INSTANCE.get(IMinecraftClientWrapper.class);
|
private static final IMinecraftClientWrapper MC_CLIENT = SingletonInjector.INSTANCE.get(IMinecraftClientWrapper.class);
|
||||||
|
private static final IMinecraftRenderWrapper MC_RENDER = SingletonInjector.INSTANCE.get(IMinecraftRenderWrapper.class);
|
||||||
private static final IWrapperFactory WRAPPER_FACTORY = SingletonInjector.INSTANCE.get(IWrapperFactory.class);
|
private static final IWrapperFactory WRAPPER_FACTORY = SingletonInjector.INSTANCE.get(IWrapperFactory.class);
|
||||||
|
|
||||||
private final IDhClientLevel clientLevel;
|
private final IDhClientLevel clientLevel;
|
||||||
@@ -106,7 +109,10 @@ public class ClientLevelModule implements Closeable, IDataSourceUpdateListenerFu
|
|||||||
this.ClientRenderStateRef.set(clientRenderState);
|
this.ClientRenderStateRef.set(clientRenderState);
|
||||||
}
|
}
|
||||||
|
|
||||||
clientRenderState.quadtree.tryTick(new DhBlockPos2D(MC_CLIENT.getPlayerBlockPos()));
|
// use camera position instead of player pos so free cam mods work better
|
||||||
|
Vec3d cameraDoublePos = MC_RENDER.getCameraExactPosition();
|
||||||
|
DhBlockPos2D cameraBlockPos = new DhBlockPos2D((int)cameraDoublePos.x, (int)cameraDoublePos.z);
|
||||||
|
clientRenderState.quadtree.tryTick(cameraBlockPos);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -170,6 +176,7 @@ public class ClientLevelModule implements Closeable, IDataSourceUpdateListenerFu
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.fullDataSourceProvider.removeDataSourceUpdateListener(this);
|
this.fullDataSourceProvider.removeDataSourceUpdateListener(this);
|
||||||
|
this.genericRenderer.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -259,7 +266,7 @@ public class ClientLevelModule implements Closeable, IDataSourceUpdateListenerFu
|
|||||||
@Override
|
@Override
|
||||||
public void close()
|
public void close()
|
||||||
{
|
{
|
||||||
LOGGER.info("Shutting down " + ClientRenderState.class.getSimpleName());
|
//LOGGER.info("Shutting down " + ClientRenderState.class.getSimpleName());
|
||||||
this.quadtree.close();
|
this.quadtree.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+2
-13
@@ -1202,17 +1202,6 @@ public class LodQuadTree extends QuadTree<LodRenderSection> implements IDebugRen
|
|||||||
{
|
{
|
||||||
this.populateListWithEnabledRenderSections(this.debugNodeList);
|
this.populateListWithEnabledRenderSections(this.debugNodeList);
|
||||||
|
|
||||||
//// can be uncommented for debugging/finding a specific position
|
|
||||||
//debugRenderer.makeParticle(
|
|
||||||
// new AbstractDebugWireframeRenderer.BoxParticle(
|
|
||||||
// new AbstractDebugWireframeRenderer.Box(
|
|
||||||
// DhSectionPos.encode((byte)7, 3,-1)
|
|
||||||
// , -64, 400,
|
|
||||||
// 0.1f,
|
|
||||||
// Color.YELLOW),
|
|
||||||
// 0.5, 0f
|
|
||||||
// ));
|
|
||||||
|
|
||||||
for (int i = 0; i < this.debugNodeList.size(); i++)
|
for (int i = 0; i < this.debugNodeList.size(); i++)
|
||||||
{
|
{
|
||||||
LodRenderSection renderSection = this.debugNodeList.get(i);
|
LodRenderSection renderSection = this.debugNodeList.get(i);
|
||||||
@@ -1264,7 +1253,7 @@ public class LodQuadTree extends QuadTree<LodRenderSection> implements IDebugRen
|
|||||||
@Override
|
@Override
|
||||||
public void close()
|
public void close()
|
||||||
{
|
{
|
||||||
LOGGER.info("Shutting down LodQuadTree...");
|
//LOGGER.info("Shutting down LodQuadTree...");
|
||||||
|
|
||||||
DEBUG_RENDERER.unregister(this, Config.Client.Advanced.Debugging.DebugWireframe.showQuadTreeRenderStatus);
|
DEBUG_RENDERER.unregister(this, Config.Client.Advanced.Debugging.DebugWireframe.showQuadTreeRenderStatus);
|
||||||
Config.Common.WorldGenerator.enableDistantGeneration.removeListener(this);
|
Config.Common.WorldGenerator.enableDistantGeneration.removeListener(this);
|
||||||
@@ -1299,7 +1288,7 @@ public class LodQuadTree extends QuadTree<LodRenderSection> implements IDebugRen
|
|||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
LOGGER.info("Finished shutting down LodQuadTree");
|
//LOGGER.info("Finished shutting down LodQuadTree");
|
||||||
}
|
}
|
||||||
|
|
||||||
//endregion base methods
|
//endregion base methods
|
||||||
|
|||||||
+73
-58
@@ -36,6 +36,7 @@ import com.seibel.distanthorizons.core.pos.DhSectionPos;
|
|||||||
import com.seibel.distanthorizons.core.render.renderer.AbstractDebugWireframeRenderer;
|
import com.seibel.distanthorizons.core.render.renderer.AbstractDebugWireframeRenderer;
|
||||||
import com.seibel.distanthorizons.core.render.renderer.IDebugRenderable;
|
import com.seibel.distanthorizons.core.render.renderer.IDebugRenderable;
|
||||||
import com.seibel.distanthorizons.core.dataObjects.render.bufferBuilding.LodBufferContainer;
|
import com.seibel.distanthorizons.core.dataObjects.render.bufferBuilding.LodBufferContainer;
|
||||||
|
import com.seibel.distanthorizons.core.util.ExceptionUtil;
|
||||||
import com.seibel.distanthorizons.core.util.LodUtil;
|
import com.seibel.distanthorizons.core.util.LodUtil;
|
||||||
import com.seibel.distanthorizons.core.util.threading.PriorityTaskPicker;
|
import com.seibel.distanthorizons.core.util.threading.PriorityTaskPicker;
|
||||||
import com.seibel.distanthorizons.core.util.threading.ThreadPoolUtil;
|
import com.seibel.distanthorizons.core.util.threading.ThreadPoolUtil;
|
||||||
@@ -62,7 +63,7 @@ public class LodRenderSection implements IDebugRenderable, AutoCloseable
|
|||||||
|
|
||||||
public final long pos;
|
public final long pos;
|
||||||
|
|
||||||
private final IDhClientLevel level;
|
private final IDhClientLevel clientLevel;
|
||||||
private final IClientLevelWrapper levelWrapper;
|
private final IClientLevelWrapper levelWrapper;
|
||||||
@WillNotClose
|
@WillNotClose
|
||||||
private final FullDataSourceProviderV2 fullDataSourceProvider;
|
private final FullDataSourceProviderV2 fullDataSourceProvider;
|
||||||
@@ -97,13 +98,6 @@ public class LodRenderSection implements IDebugRenderable, AutoCloseable
|
|||||||
*/
|
*/
|
||||||
private Runnable getAndBuildRenderDataRunnable = null;
|
private Runnable getAndBuildRenderDataRunnable = null;
|
||||||
|
|
||||||
/**
|
|
||||||
* Represents just uploading the {@link LodQuadBuilder} to the GPU. <br>
|
|
||||||
* Separate from {@link LodRenderSection#getAndBuildRenderDataFutureRef} because they run on
|
|
||||||
* different threads (buffer uploading is on the MC render thread) and need to be canceled separately.
|
|
||||||
*/
|
|
||||||
private final AtomicReference<CompletableFuture<LodBufferContainer>> bufferUploadFutureRef = new AtomicReference<>(null);
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//=============//
|
//=============//
|
||||||
@@ -114,12 +108,12 @@ public class LodRenderSection implements IDebugRenderable, AutoCloseable
|
|||||||
public LodRenderSection(
|
public LodRenderSection(
|
||||||
long pos,
|
long pos,
|
||||||
LodQuadTree quadTree,
|
LodQuadTree quadTree,
|
||||||
IDhClientLevel level, FullDataSourceProviderV2 fullDataSourceProvider)
|
IDhClientLevel clientLevel, FullDataSourceProviderV2 fullDataSourceProvider)
|
||||||
{
|
{
|
||||||
this.pos = pos;
|
this.pos = pos;
|
||||||
this.quadTree = quadTree;
|
this.quadTree = quadTree;
|
||||||
this.level = level;
|
this.clientLevel = clientLevel;
|
||||||
this.levelWrapper = level.getClientLevelWrapper();
|
this.levelWrapper = clientLevel.getClientLevelWrapper();
|
||||||
this.fullDataSourceProvider = fullDataSourceProvider;
|
this.fullDataSourceProvider = fullDataSourceProvider;
|
||||||
|
|
||||||
DEBUG_RENDERER.register(this, Config.Client.Advanced.Debugging.DebugWireframe.showRenderSectionStatus);
|
DEBUG_RENDERER.register(this, Config.Client.Advanced.Debugging.DebugWireframe.showRenderSectionStatus);
|
||||||
@@ -161,6 +155,8 @@ public class LodRenderSection implements IDebugRenderable, AutoCloseable
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
// shouldn't happen since this method is synchronized, but just in case
|
||||||
|
// make sure we only ever start one upload task
|
||||||
if (!this.getAndBuildRenderDataFutureRef.compareAndSet(null, future))
|
if (!this.getAndBuildRenderDataFutureRef.compareAndSet(null, future))
|
||||||
{
|
{
|
||||||
CompletableFuture<Void> oldFuture = this.getAndBuildRenderDataFutureRef.get();
|
CompletableFuture<Void> oldFuture = this.getAndBuildRenderDataFutureRef.get();
|
||||||
@@ -173,6 +169,7 @@ public class LodRenderSection implements IDebugRenderable, AutoCloseable
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
// build LOD data on a DH thread
|
||||||
LodQuadBuilder lodQuadBuilder = this.getAndBuildRenderData();
|
LodQuadBuilder lodQuadBuilder = this.getAndBuildRenderData();
|
||||||
if (lodQuadBuilder == null)
|
if (lodQuadBuilder == null)
|
||||||
{
|
{
|
||||||
@@ -180,7 +177,8 @@ public class LodRenderSection implements IDebugRenderable, AutoCloseable
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.uploadToGpuAsync(lodQuadBuilder)
|
// uploading will primarily happen on the render thread
|
||||||
|
this.uploadToGpuAsync(future, lodQuadBuilder)
|
||||||
.thenRun(() ->
|
.thenRun(() ->
|
||||||
{
|
{
|
||||||
// the future is passed in separately (IE not using the local var) to prevent any possible race condition null pointers
|
// the future is passed in separately (IE not using the local var) to prevent any possible race condition null pointers
|
||||||
@@ -190,7 +188,7 @@ public class LodRenderSection implements IDebugRenderable, AutoCloseable
|
|||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
LOGGER.error("Unexpected issue creating render data for pos: ["+DhSectionPos.toString(this.pos)+"], error: ["+e.getMessage()+"].", e);
|
LOGGER.error("Unexpected issue creating render data for pos: ["+DhSectionPos.toString(this.pos)+"], error: ["+e.getMessage()+"].", e);
|
||||||
future.complete(null);
|
future.completeExceptionally(e);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
executor.execute(this.getAndBuildRenderDataRunnable);
|
executor.execute(this.getAndBuildRenderDataRunnable);
|
||||||
@@ -205,6 +203,14 @@ public class LodRenderSection implements IDebugRenderable, AutoCloseable
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//=======================//
|
||||||
|
// Get LOD ID data //
|
||||||
|
// and build render data //
|
||||||
|
//=======================//
|
||||||
|
//region
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
private synchronized LodQuadBuilder getAndBuildRenderData()
|
private synchronized LodQuadBuilder getAndBuildRenderData()
|
||||||
{
|
{
|
||||||
@@ -218,7 +224,7 @@ public class LodRenderSection implements IDebugRenderable, AutoCloseable
|
|||||||
|
|
||||||
|
|
||||||
boolean enableTransparency = Config.Client.Advanced.Graphics.Quality.transparency.get().transparencyEnabled;
|
boolean enableTransparency = Config.Client.Advanced.Graphics.Quality.transparency.get().transparencyEnabled;
|
||||||
LodQuadBuilder lodQuadBuilder = new LodQuadBuilder(enableTransparency, this.level.getClientLevelWrapper());
|
LodQuadBuilder lodQuadBuilder = new LodQuadBuilder(enableTransparency, this.clientLevel.getClientLevelWrapper());
|
||||||
|
|
||||||
|
|
||||||
// get the adjacent positions
|
// get the adjacent positions
|
||||||
@@ -241,7 +247,7 @@ public class LodRenderSection implements IDebugRenderable, AutoCloseable
|
|||||||
|
|
||||||
// the render sources are only needed by this synchronous method,
|
// the render sources are only needed by this synchronous method,
|
||||||
// then they can be closed
|
// then they can be closed
|
||||||
ColumnRenderBufferBuilder.makeLodRenderData(lodQuadBuilder, thisRenderSource, this.level, adjacentRenderSections, adjIsSameDetailLevel);
|
ColumnRenderBufferBuilder.makeLodRenderData(lodQuadBuilder, thisRenderSource, this.clientLevel, adjacentRenderSections, adjIsSameDetailLevel);
|
||||||
return lodQuadBuilder;
|
return lodQuadBuilder;
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
@@ -291,53 +297,63 @@ public class LodRenderSection implements IDebugRenderable, AutoCloseable
|
|||||||
detailLevel += DhSectionPos.SECTION_MINIMUM_DETAIL_LEVEL;
|
detailLevel += DhSectionPos.SECTION_MINIMUM_DETAIL_LEVEL;
|
||||||
return detailLevel == DhSectionPos.getDetailLevel(this.pos);
|
return detailLevel == DhSectionPos.getDetailLevel(this.pos);
|
||||||
}
|
}
|
||||||
private synchronized CompletableFuture<LodBufferContainer> uploadToGpuAsync(LodQuadBuilder lodQuadBuilder)
|
|
||||||
|
//endregion
|
||||||
|
|
||||||
|
|
||||||
|
private synchronized CompletableFuture<LodBufferContainer> uploadToGpuAsync(
|
||||||
|
CompletableFuture<Void> parentFuture,
|
||||||
|
LodQuadBuilder lodQuadBuilder)
|
||||||
{
|
{
|
||||||
CompletableFuture<LodBufferContainer> oldFuture = this.bufferUploadFutureRef.getAndSet(null);
|
CompletableFuture<LodBufferContainer> uploadFuture = LodBufferContainer.tryMakeAndUploadBuffersAsync(this.pos, this.clientLevel, lodQuadBuilder);
|
||||||
if (oldFuture != null)
|
uploadFuture.whenComplete((bufferContainer, e) ->
|
||||||
{
|
{
|
||||||
// canceling the previous future
|
try
|
||||||
// prevents the CPU from working on something that won't be used
|
{
|
||||||
oldFuture.cancel(true);
|
// handle errors and early shutdown
|
||||||
|
if (e != null)
|
||||||
|
{
|
||||||
|
if (!ExceptionUtil.isShutdownException(e))
|
||||||
|
{
|
||||||
|
LOGGER.error("Unexpected issue uploading buffers for pos: [" + DhSectionPos.toString(this.pos) + "], error: [" + e.getMessage() + "].", e);
|
||||||
}
|
}
|
||||||
|
|
||||||
CompletableFuture<LodBufferContainer> future = ColumnRenderBufferBuilder.uploadBuffersAsync(this.level, this.pos, lodQuadBuilder);
|
if (bufferContainer != null)
|
||||||
future.handle((lodBufferContainer, throwable) ->
|
|
||||||
{
|
{
|
||||||
if (!this.bufferUploadFutureRef.compareAndSet(future, null)
|
// shouldn't happen, but just in case
|
||||||
// if the old future is canceled then the future ref will be different and that's expected
|
bufferContainer.close();
|
||||||
&& !future.isCancelled()
|
}
|
||||||
// if the old future is already done, then we don't care about the ref being swapped
|
return;
|
||||||
&& !future.isDone())
|
|
||||||
{
|
|
||||||
LOGGER.warn("Buffer upload future ref changed for pos: ["+DhSectionPos.toString(this.pos)+"].");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
// close the old container
|
||||||
});
|
LodBufferContainer oldContainer = this.renderBufferContainer;
|
||||||
|
this.renderBufferContainer = bufferContainer.buffersUploaded ? bufferContainer : null;
|
||||||
future.thenAccept((LodBufferContainer buffer) ->
|
if (oldContainer != null)
|
||||||
{
|
{
|
||||||
// needed to clean up the old data
|
oldContainer.close();
|
||||||
LodBufferContainer previousContainer = this.renderBufferContainer;
|
}
|
||||||
|
|
||||||
// upload complete
|
// upload complete
|
||||||
this.renderBufferContainer = buffer.buffersUploaded ? buffer : null;
|
|
||||||
this.renderDataDirty = false;
|
this.renderDataDirty = false;
|
||||||
|
|
||||||
if (previousContainer != null)
|
|
||||||
|
if (parentFuture.isCancelled())
|
||||||
{
|
{
|
||||||
previousContainer.close();
|
// if the parent future was canceled that likely means
|
||||||
|
// this LodRenderSection was closed before this point,
|
||||||
|
// meaning this buffer will become homeless,
|
||||||
|
// so we need to clean it up here
|
||||||
|
bufferContainer.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception finishEx)
|
||||||
|
{
|
||||||
|
LOGGER.error("Unexpected buffer finish exception: ["+finishEx.getMessage()+"]", finishEx);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
return uploadFuture;
|
||||||
if (!this.bufferUploadFutureRef.compareAndSet(null, future))
|
|
||||||
{
|
|
||||||
LodUtil.assertNotReach("Buffer upload future ref couldn't be set due to concurrency error, pos: ["+DhSectionPos.toString(this.pos)+"].");
|
|
||||||
}
|
|
||||||
|
|
||||||
return future;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//endregion render data uploading
|
//endregion render data uploading
|
||||||
@@ -391,8 +407,8 @@ public class LodRenderSection implements IDebugRenderable, AutoCloseable
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
int levelMinY = this.level.getLevelWrapper().getMinHeight();
|
int levelMinY = this.clientLevel.getLevelWrapper().getMinHeight();
|
||||||
int levelMaxY = this.level.getLevelWrapper().getMaxHeight();
|
int levelMaxY = this.clientLevel.getLevelWrapper().getMaxHeight();
|
||||||
|
|
||||||
// show the wireframe a bit lower than world max height,
|
// show the wireframe a bit lower than world max height,
|
||||||
// since most worlds don't render all the way up to the max height
|
// since most worlds don't render all the way up to the max height
|
||||||
@@ -429,13 +445,7 @@ public class LodRenderSection implements IDebugRenderable, AutoCloseable
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
this.setRenderingEnabled(false);
|
// render loading is no longer needed
|
||||||
if (this.renderBufferContainer != null)
|
|
||||||
{
|
|
||||||
this.renderBufferContainer.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
// removes any in-progress futures since they aren't needed any more
|
|
||||||
CompletableFuture<Void> buildFuture = this.getAndBuildRenderDataFutureRef.get();
|
CompletableFuture<Void> buildFuture = this.getAndBuildRenderDataFutureRef.get();
|
||||||
if (buildFuture != null)
|
if (buildFuture != null)
|
||||||
{
|
{
|
||||||
@@ -451,12 +461,17 @@ public class LodRenderSection implements IDebugRenderable, AutoCloseable
|
|||||||
renderLoaderExecutor.remove(runnable);
|
renderLoaderExecutor.remove(runnable);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// cancel the future after removing the runnable
|
||||||
|
// to make sure the runnable is properly removed
|
||||||
|
buildFuture.cancel(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
CompletableFuture<LodBufferContainer> uploadFuture = this.bufferUploadFutureRef.get();
|
|
||||||
if (uploadFuture != null)
|
this.setRenderingEnabled(false);
|
||||||
|
if (this.renderBufferContainer != null)
|
||||||
{
|
{
|
||||||
uploadFuture.cancel(true);
|
this.renderBufferContainer.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -176,45 +176,6 @@ public class RenderParams extends DhApiRenderParam
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
//// potential fix for a segfault when
|
|
||||||
//// Sodium and DH are running together
|
|
||||||
//if (EPlatform.get() == EPlatform.MACOS
|
|
||||||
// && !initialLoadingComplete)
|
|
||||||
//{
|
|
||||||
// // Once MC starts rendering, wait a few seconds so
|
|
||||||
// // MC/Sodium can finish their shader compiling before DH does its own.
|
|
||||||
// // This will allow DH to compile its own shaders after Sodium finishes
|
|
||||||
// // compiling its own.
|
|
||||||
// long nowMs = System.currentTimeMillis();
|
|
||||||
// long firstAllowedRenderTimeMs = firstRenderTimeMs + TIME_FOR_MAC_TO_FINISH_COMPILING_IN_MS;
|
|
||||||
// if (nowMs < firstAllowedRenderTimeMs)
|
|
||||||
// {
|
|
||||||
// return "Waiting for initial MC compile...";
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
//
|
|
||||||
// // null shouldn't happen, but just in case
|
|
||||||
// PriorityTaskPicker.Executor renderLoadExecutor = ThreadPoolUtil.getRenderLoadingExecutor();
|
|
||||||
// if (renderLoadExecutor == null)
|
|
||||||
// {
|
|
||||||
// return "Waiting for DH Threadpool...";
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// // wait for DH to finish loading, by the time that's done
|
|
||||||
// // java should have finished all of DH's JIT compiling,
|
|
||||||
// // which will hopefully mean less concurrency and thus a lower
|
|
||||||
// // chance of breaking
|
|
||||||
// // (plus this gives Sodium/vanill a bit longer to finish their setup)
|
|
||||||
// int taskCount = renderLoadExecutor.getQueueSize();
|
|
||||||
// if (taskCount > 0)
|
|
||||||
// {
|
|
||||||
// return "Waiting for DH JIT compiling...";
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// initialLoadingComplete = true;
|
|
||||||
//}
|
|
||||||
|
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+78
-6
@@ -10,6 +10,7 @@ import com.seibel.distanthorizons.core.util.TimerUtil;
|
|||||||
import com.seibel.distanthorizons.core.util.objects.RollingAverage;
|
import com.seibel.distanthorizons.core.util.objects.RollingAverage;
|
||||||
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper;
|
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper;
|
||||||
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftRenderWrapper;
|
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftRenderWrapper;
|
||||||
|
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftSharedWrapper;
|
||||||
import com.seibel.distanthorizons.coreapi.ModInfo;
|
import com.seibel.distanthorizons.coreapi.ModInfo;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
@@ -49,6 +50,14 @@ public class RenderThreadTaskHandler
|
|||||||
|
|
||||||
|
|
||||||
private long nanoSinceTasksRun = System.nanoTime();
|
private long nanoSinceTasksRun = System.nanoTime();
|
||||||
|
private final boolean running;
|
||||||
|
|
||||||
|
private Thread renderThread;
|
||||||
|
/**
|
||||||
|
* the currently running {@link QueuedRunnable}
|
||||||
|
* will be null if nothing is running.
|
||||||
|
*/
|
||||||
|
private volatile @Nullable QueuedRunnable currentQueuedRunnable;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -57,7 +66,22 @@ public class RenderThreadTaskHandler
|
|||||||
//=============//
|
//=============//
|
||||||
//region
|
//region
|
||||||
|
|
||||||
private RenderThreadTaskHandler() { TIMER.scheduleAtFixedRate(TimerUtil.createTimerTask(this::manualCleanupTick), MS_BETWEEN_CLEANUP_TICKS, MS_BETWEEN_CLEANUP_TICKS); }
|
private RenderThreadTaskHandler()
|
||||||
|
{
|
||||||
|
// we only want to run this when the client is available
|
||||||
|
IMinecraftSharedWrapper mcShared = SingletonInjector.INSTANCE.get(IMinecraftSharedWrapper.class);
|
||||||
|
if (!mcShared.isDedicatedServer())
|
||||||
|
{
|
||||||
|
LOGGER.debug("Starting ["+RenderThreadTaskHandler.class.getSimpleName()+"]...");
|
||||||
|
this.running = true;
|
||||||
|
TIMER.scheduleAtFixedRate(TimerUtil.createTimerTask(this::manualCleanupTick), MS_BETWEEN_CLEANUP_TICKS, MS_BETWEEN_CLEANUP_TICKS);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
this.running = false;
|
||||||
|
LOGGER.debug("Skipping ["+RenderThreadTaskHandler.class.getSimpleName()+"] startup due to running on a dedicated server.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//endregion
|
//endregion
|
||||||
|
|
||||||
@@ -70,6 +94,13 @@ public class RenderThreadTaskHandler
|
|||||||
|
|
||||||
public void queueRunningOnRenderThread(String name, Runnable renderCall)
|
public void queueRunningOnRenderThread(String name, Runnable renderCall)
|
||||||
{
|
{
|
||||||
|
// don't queuing tasks if they'll never be run
|
||||||
|
if (!this.running)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// don't get the stacktrace on release to reduce GC pressure
|
// don't get the stacktrace on release to reduce GC pressure
|
||||||
StackTraceElement[] stackTrace = null;
|
StackTraceElement[] stackTrace = null;
|
||||||
if (ModInfo.IS_DEV_BUILD)
|
if (ModInfo.IS_DEV_BUILD)
|
||||||
@@ -116,12 +147,21 @@ public class RenderThreadTaskHandler
|
|||||||
long loopStartTimeNano = System.nanoTime();
|
long loopStartTimeNano = System.nanoTime();
|
||||||
this.nanoSinceTasksRun = loopStartTimeNano;
|
this.nanoSinceTasksRun = loopStartTimeNano;
|
||||||
|
|
||||||
|
|
||||||
|
if (this.renderThread == null)
|
||||||
|
{
|
||||||
|
this.renderThread = Thread.currentThread();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
QueuedRunnable runnable = RENDER_THREAD_RUNNABLE_QUEUE.poll();
|
QueuedRunnable runnable = RENDER_THREAD_RUNNABLE_QUEUE.poll();
|
||||||
while(runnable != null)
|
while(runnable != null)
|
||||||
{
|
{
|
||||||
long taskStartNano = System.nanoTime();
|
long taskStartNano = System.nanoTime();
|
||||||
|
|
||||||
|
this.currentQueuedRunnable = runnable;
|
||||||
runnable.run();
|
runnable.run();
|
||||||
|
this.currentQueuedRunnable = null;
|
||||||
|
|
||||||
// only try running for a limited amount of time to prevent lag spikes
|
// only try running for a limited amount of time to prevent lag spikes
|
||||||
long taskNano = System.nanoTime() - taskStartNano;
|
long taskNano = System.nanoTime() - taskStartNano;
|
||||||
@@ -179,8 +219,19 @@ public class RenderThreadTaskHandler
|
|||||||
// this means we could have GL jobs building up.
|
// this means we could have GL jobs building up.
|
||||||
// Run the queued tasks on MC's executor (hopefully this should always run,
|
// Run the queued tasks on MC's executor (hopefully this should always run,
|
||||||
// even if DH's render code isn't being hit).
|
// even if DH's render code isn't being hit).
|
||||||
IMinecraftClientWrapper MC = SingletonInjector.INSTANCE.get(IMinecraftClientWrapper.class);
|
IMinecraftClientWrapper mcClient = SingletonInjector.INSTANCE.get(IMinecraftClientWrapper.class);
|
||||||
MC.executeOnRenderThread(() -> this.runRenderThreadTasks(500 * 1_000_000L));
|
if (mcClient != null)
|
||||||
|
{
|
||||||
|
mcClient.executeOnRenderThread(() -> this.runRenderThreadTasks(500 * 1_000_000L));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// shouldn't happen, but just in case
|
||||||
|
|
||||||
|
// somehow the timer started when there wasn't a client wrapper
|
||||||
|
// this probably means the timer was started on a dedicated server
|
||||||
|
RATE_LIMITED_LOGGER.warn("["+RenderThreadTaskHandler.class.getSimpleName()+"] timer started when ["+IMinecraftClientWrapper.class.getSimpleName()+"] is null. This shouldn't happen but can likely be ignored.");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//endregion
|
//endregion
|
||||||
@@ -190,7 +241,7 @@ public class RenderThreadTaskHandler
|
|||||||
//===========//
|
//===========//
|
||||||
// debugging //
|
// debugging //
|
||||||
//===========//
|
//===========//
|
||||||
///region
|
//region
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* if tasks are currently queued the debug
|
* if tasks are currently queued the debug
|
||||||
@@ -246,7 +297,28 @@ public class RenderThreadTaskHandler
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
///endregion
|
|
||||||
|
/** Returns true if the currently running thread is being run by this handler */
|
||||||
|
public boolean isCurrentThread()
|
||||||
|
{
|
||||||
|
if (this.renderThread != null)
|
||||||
|
{
|
||||||
|
return Thread.currentThread() == this.renderThread;
|
||||||
|
}
|
||||||
|
|
||||||
|
// shouldn't normally be needed, but can be used if this
|
||||||
|
// handler hasn't been run yet
|
||||||
|
return Thread.currentThread().getName().equals("Render thread");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Only recommended to be used by the task that's currently being run.
|
||||||
|
* Use {@link RenderThreadTaskHandler#isCurrentThread()} to check. <br>
|
||||||
|
* Can be used to get stack traces for render thread tasks while they're being run.
|
||||||
|
*/
|
||||||
|
public @Nullable QueuedRunnable getCurrentlyRunningTask() { return this.currentQueuedRunnable; }
|
||||||
|
|
||||||
|
//endregion
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -255,7 +327,7 @@ public class RenderThreadTaskHandler
|
|||||||
//================//
|
//================//
|
||||||
//region
|
//region
|
||||||
|
|
||||||
private static class QueuedRunnable implements Runnable
|
public static class QueuedRunnable implements Runnable
|
||||||
{
|
{
|
||||||
/** used to easily track what's being done on the render thread */
|
/** used to easily track what's being done on the render thread */
|
||||||
public final String name;
|
public final String name;
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ import com.seibel.distanthorizons.core.dependencyInjection.ModAccessorInjector;
|
|||||||
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
|
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
|
||||||
import com.seibel.distanthorizons.core.logging.DhLogger;
|
import com.seibel.distanthorizons.core.logging.DhLogger;
|
||||||
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
|
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
|
||||||
|
import com.seibel.distanthorizons.core.pos.DhSectionPos;
|
||||||
import com.seibel.distanthorizons.core.render.DhApiRenderProxy;
|
import com.seibel.distanthorizons.core.render.DhApiRenderProxy;
|
||||||
import com.seibel.distanthorizons.core.render.RenderBufferHandler;
|
import com.seibel.distanthorizons.core.render.RenderBufferHandler;
|
||||||
import com.seibel.distanthorizons.core.render.RenderParams;
|
import com.seibel.distanthorizons.core.render.RenderParams;
|
||||||
@@ -36,6 +37,8 @@ import com.seibel.distanthorizons.core.wrapperInterfaces.modAccessor.IIrisAccess
|
|||||||
import com.seibel.distanthorizons.core.wrapperInterfaces.render.renderPass.*;
|
import com.seibel.distanthorizons.core.wrapperInterfaces.render.renderPass.*;
|
||||||
import com.seibel.distanthorizons.coreapi.DependencyInjection.ApiEventInjector;
|
import com.seibel.distanthorizons.coreapi.DependencyInjection.ApiEventInjector;
|
||||||
|
|
||||||
|
import java.awt.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This is where all the magic happens. <br>
|
* This is where all the magic happens. <br>
|
||||||
* This is where LODs are draw to the world.
|
* This is where LODs are draw to the world.
|
||||||
@@ -288,6 +291,22 @@ public class LodRenderer
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (Config.Client.Advanced.Debugging.PositionFinder.positionFinderEnable.get())
|
||||||
|
{
|
||||||
|
// can be used to find specific positions when debugging
|
||||||
|
this.debugWireframeRenderer.renderBox(new AbstractDebugWireframeRenderer.Box(
|
||||||
|
DhSectionPos.encode(
|
||||||
|
Config.Client.Advanced.Debugging.PositionFinder.positionFinderDetailLevel.get().byteValue(),
|
||||||
|
Config.Client.Advanced.Debugging.PositionFinder.positionFinderXPos.get(),
|
||||||
|
Config.Client.Advanced.Debugging.PositionFinder.positionFinderZPos.get()),
|
||||||
|
Config.Client.Advanced.Debugging.PositionFinder.positionFinderMinBlockY.get(),
|
||||||
|
Config.Client.Advanced.Debugging.PositionFinder.positionFinderMaxBlockY.get(),
|
||||||
|
Config.Client.Advanced.Debugging.PositionFinder.positionFinderMarginPercent.get(),
|
||||||
|
Color.GREEN
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//=============================//
|
//=============================//
|
||||||
// Apply to the MC Framebuffer //
|
// Apply to the MC Framebuffer //
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ public class ExceptionUtil
|
|||||||
return throwable instanceof InterruptedException
|
return throwable instanceof InterruptedException
|
||||||
|| throwable instanceof UncheckedInterruptedException
|
|| throwable instanceof UncheckedInterruptedException
|
||||||
|| throwable instanceof RejectedExecutionException
|
|| throwable instanceof RejectedExecutionException
|
||||||
|
|| throwable instanceof CancellationException
|
||||||
|| throwable instanceof ClosedByInterruptException;
|
|| throwable instanceof ClosedByInterruptException;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -37,8 +38,8 @@ public class ExceptionUtil
|
|||||||
unwrapped instanceof CancellationException;
|
unwrapped instanceof CancellationException;
|
||||||
}
|
}
|
||||||
public static Throwable ensureUnwrap(Throwable t)
|
public static Throwable ensureUnwrap(Throwable t)
|
||||||
{
|
{ return t instanceof CompletionException ? ensureUnwrap(t.getCause()) : t; }
|
||||||
return t instanceof CompletionException ? ensureUnwrap(t.getCause()) : t;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+15
@@ -451,6 +451,21 @@ public class RenderDataPointReducingList extends AbstractPhantomArrayList
|
|||||||
this.setBigger(smaller, bigger);
|
this.setBigger(smaller, bigger);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (writeIndex == 0)
|
||||||
|
{
|
||||||
|
// if every data point in the list is NULL (0) the write index will be 0,
|
||||||
|
// and in order to prevent accessing index -1 below,
|
||||||
|
// setting the write index to 1 is needed.
|
||||||
|
|
||||||
|
// This shouldn't happen normally, however if the lod data is slightly malformed
|
||||||
|
// (which is specifically the case for the commonly shared wyncraft LODs)
|
||||||
|
// this check is needed.
|
||||||
|
// It would probably be best to fix the 6 or so NULL datapoints that are next
|
||||||
|
// to each other in the full data source, but for now this fix works.
|
||||||
|
|
||||||
|
writeIndex = 1;
|
||||||
|
}
|
||||||
|
|
||||||
this.smallest = this.sortingArray.getShort(0);
|
this.smallest = this.sortingArray.getShort(0);
|
||||||
this.biggest = this.sortingArray.getShort(writeIndex - 1);
|
this.biggest = this.sortingArray.getShort(writeIndex - 1);
|
||||||
this.setSmaller(this.getSmallest(), NULL);
|
this.setSmaller(this.getSmallest(), NULL);
|
||||||
|
|||||||
+2
-43
@@ -331,7 +331,7 @@ public class PhantomArrayListPool
|
|||||||
if (pool.logGarbageCollectedStacks
|
if (pool.logGarbageCollectedStacks
|
||||||
&& checkout.allocationStackTrace != null) // stack trace shouldn't be null, but just in case
|
&& checkout.allocationStackTrace != null) // stack trace shouldn't be null, but just in case
|
||||||
{
|
{
|
||||||
putAndIncrementTrackingString(checkout.allocationStackTrace, allocationStackTraceCountPairList);
|
PhantomLoggingHelper.putAndIncrementTrackingString(checkout.allocationStackTrace, allocationStackTraceCountPairList);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -363,18 +363,7 @@ public class PhantomArrayListPool
|
|||||||
// log stack traces if present
|
// log stack traces if present
|
||||||
if (pool.logGarbageCollectedStacks)
|
if (pool.logGarbageCollectedStacks)
|
||||||
{
|
{
|
||||||
// high numbers first
|
PhantomLoggingHelper.LogAllocationStackTracePairCounts(LOGGER, allocationStackTraceCountPairList);
|
||||||
allocationStackTraceCountPairList.sort((a, b) -> Integer.compare(b.second.get(), a.second.get()));
|
|
||||||
|
|
||||||
StringBuilder stringBuilder = new StringBuilder();
|
|
||||||
for (int j = 0; j < allocationStackTraceCountPairList.size(); j++)
|
|
||||||
{
|
|
||||||
int count = allocationStackTraceCountPairList.get(j).second.get();
|
|
||||||
String stack = allocationStackTraceCountPairList.get(j).first;
|
|
||||||
|
|
||||||
stringBuilder.append(count).append(". ").append(stack).append("\n");
|
|
||||||
}
|
|
||||||
LOGGER.warn("Stacks: ["+ allocationStackTraceCountPairList.size()+"]\n" + stringBuilder.toString());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -389,36 +378,6 @@ public class PhantomArrayListPool
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/**
|
|
||||||
* This was separated out so it could be used for other string pair lists.
|
|
||||||
* James originally had an idea to add a shorter static string
|
|
||||||
* ID to each allocated {@link PhantomArrayListCheckout} as a simpler version of the stack trace,
|
|
||||||
* however it became a bit more difficult and messy than he wanted to deal with, so for now we just
|
|
||||||
* have the stack trace.
|
|
||||||
*/
|
|
||||||
private static void putAndIncrementTrackingString(
|
|
||||||
String key,
|
|
||||||
ArrayList<Pair<String, AtomicInteger>> allocationStackTraceCountPairList)
|
|
||||||
{
|
|
||||||
// sequential search, for the number of elements we're dealing with (less than 20)
|
|
||||||
// this should be sufficiently fast
|
|
||||||
boolean pairFound = false;
|
|
||||||
for (int i = 0; i < allocationStackTraceCountPairList.size(); i++)
|
|
||||||
{
|
|
||||||
Pair<String, AtomicInteger> possiblePair = allocationStackTraceCountPairList.get(i);
|
|
||||||
if (possiblePair.first.equals(key))
|
|
||||||
{
|
|
||||||
possiblePair.second.getAndIncrement();
|
|
||||||
pairFound = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!pairFound)
|
|
||||||
{
|
|
||||||
allocationStackTraceCountPairList.add(new Pair<>(key, new AtomicInteger(1)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
///endregion
|
///endregion
|
||||||
|
|
||||||
|
|||||||
+232
@@ -0,0 +1,232 @@
|
|||||||
|
package com.seibel.distanthorizons.core.util.objects.pooling;
|
||||||
|
|
||||||
|
import com.seibel.distanthorizons.core.logging.DhLogger;
|
||||||
|
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
|
||||||
|
import com.seibel.distanthorizons.core.logging.f3.F3Screen;
|
||||||
|
import com.seibel.distanthorizons.core.util.ThreadUtil;
|
||||||
|
import com.seibel.distanthorizons.core.util.objects.Pair;
|
||||||
|
|
||||||
|
import java.lang.ref.PhantomReference;
|
||||||
|
import java.lang.ref.Reference;
|
||||||
|
import java.lang.ref.ReferenceQueue;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
import java.util.concurrent.ThreadPoolExecutor;
|
||||||
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
|
||||||
|
public class PhantomLoggingHelper
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* This was separated out so it could be used for other string pair lists.
|
||||||
|
* James originally had an idea to add a shorter static string
|
||||||
|
* ID to each allocated {@link PhantomArrayListCheckout} as a simpler version of the stack trace,
|
||||||
|
* however it became a bit more difficult and messy than he wanted to deal with, so for now we just
|
||||||
|
* have the stack trace.
|
||||||
|
*/
|
||||||
|
public static void putAndIncrementTrackingString(
|
||||||
|
String key,
|
||||||
|
ArrayList<Pair<String, AtomicInteger>> allocationStackTraceCountPairList)
|
||||||
|
{
|
||||||
|
// sequential search, for the number of elements we're dealing with (less than 20)
|
||||||
|
// this should be sufficiently fast
|
||||||
|
boolean pairFound = false;
|
||||||
|
for (int i = 0; i < allocationStackTraceCountPairList.size(); i++)
|
||||||
|
{
|
||||||
|
Pair<String, AtomicInteger> possiblePair = allocationStackTraceCountPairList.get(i);
|
||||||
|
if (possiblePair.first.equals(key))
|
||||||
|
{
|
||||||
|
possiblePair.second.getAndIncrement();
|
||||||
|
pairFound = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!pairFound)
|
||||||
|
{
|
||||||
|
allocationStackTraceCountPairList.add(new Pair<>(key, new AtomicInteger(1)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void LogAllocationStackTracePairCounts(DhLogger logger, ArrayList<Pair<String, AtomicInteger>> allocationStackTraceCountPairList)
|
||||||
|
{
|
||||||
|
// high numbers first
|
||||||
|
allocationStackTraceCountPairList.sort((a, b) -> Integer.compare(b.second.get(), a.second.get()));
|
||||||
|
|
||||||
|
StringBuilder stringBuilder = new StringBuilder();
|
||||||
|
for (int j = 0; j < allocationStackTraceCountPairList.size(); j++)
|
||||||
|
{
|
||||||
|
int count = allocationStackTraceCountPairList.get(j).second.get();
|
||||||
|
String stack = allocationStackTraceCountPairList.get(j).first;
|
||||||
|
|
||||||
|
stringBuilder.append(count).append(". ").append(stack).append("\n");
|
||||||
|
}
|
||||||
|
logger.warn("Stacks: ["+ allocationStackTraceCountPairList.size()+"]\n" + stringBuilder.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//================//
|
||||||
|
// helper classes //
|
||||||
|
//================//
|
||||||
|
//region
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Can be quickly added to a {@link AutoCloseable} implementing
|
||||||
|
* class to confirm it's being properly closed.
|
||||||
|
*/
|
||||||
|
public static class BasicPhantomReference implements AutoCloseable
|
||||||
|
{
|
||||||
|
private static final DhLogger LOGGER = new DhLoggerBuilder().build();
|
||||||
|
|
||||||
|
/** if enabled the number of GC'ed buffers will be logged */
|
||||||
|
private static final boolean LOG_PHANTOM_RECOVERY = true;
|
||||||
|
/**
|
||||||
|
* If enabled the GC'ed buffers allocation/upload stacks will be logged.
|
||||||
|
* Note: due to how the buffers are often run on the render thread,
|
||||||
|
* these stacks will likely only be of limited use.
|
||||||
|
* For more robust debugging it would likely be best to somehow track
|
||||||
|
* the stacks of where these calls are happening before they're queued
|
||||||
|
* for the render thread.
|
||||||
|
*/
|
||||||
|
private static final boolean LOG_PHANTOM_ALLOCATION_STACKS = true;
|
||||||
|
|
||||||
|
private static final int PHANTOM_REF_CHECK_TIME_IN_MS = 5 * 1000;
|
||||||
|
private static final ReferenceQueue<BasicPhantomReference> PHANTOM_REFERENCE_QUEUE = new ReferenceQueue<>();
|
||||||
|
private static final ConcurrentHashMap<PhantomReference<? extends BasicPhantomReference>, Class<?>> PHANTOM_TO_PARENT_CLASS = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
|
private static final ThreadPoolExecutor CLEANUP_THREAD = ThreadUtil.makeSingleDaemonThreadPool("BasicPhantom Cleanup");
|
||||||
|
|
||||||
|
|
||||||
|
private final Class<?> parentClass;
|
||||||
|
private final PhantomReference<? extends BasicPhantomReference> phantomReference;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//==============//
|
||||||
|
// constructors //
|
||||||
|
//==============//
|
||||||
|
//region
|
||||||
|
|
||||||
|
static { CLEANUP_THREAD.execute(() -> runPhantomReferenceCleanupLoop()); }
|
||||||
|
|
||||||
|
public BasicPhantomReference(Class<?> parentClass)
|
||||||
|
{
|
||||||
|
this.parentClass = parentClass;
|
||||||
|
this.phantomReference = new PhantomReference<>(this, PHANTOM_REFERENCE_QUEUE);
|
||||||
|
PHANTOM_TO_PARENT_CLASS.put(this.phantomReference, this.parentClass);
|
||||||
|
}
|
||||||
|
|
||||||
|
//endregion
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//================//
|
||||||
|
// base overrides //
|
||||||
|
//================//
|
||||||
|
//region
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close()
|
||||||
|
{
|
||||||
|
this.phantomReference.clear();
|
||||||
|
PHANTOM_TO_PARENT_CLASS.remove(this.phantomReference);
|
||||||
|
}
|
||||||
|
|
||||||
|
//endregion
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//================//
|
||||||
|
// static cleanup //
|
||||||
|
//================//
|
||||||
|
//region
|
||||||
|
|
||||||
|
private static void runPhantomReferenceCleanupLoop()
|
||||||
|
{
|
||||||
|
// these arrays are stored here so they don't have to be re-allocated each loop
|
||||||
|
ArrayList<Pair<String, AtomicInteger>> allocationStackTraceCountPairList = new ArrayList<>();
|
||||||
|
ArrayList<Pair<String, AtomicInteger>> parentClassNameCountPairList = new ArrayList<>();
|
||||||
|
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
allocationStackTraceCountPairList.clear();
|
||||||
|
parentClassNameCountPairList.clear();
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Thread.sleep(PHANTOM_REF_CHECK_TIME_IN_MS);
|
||||||
|
}
|
||||||
|
catch (InterruptedException ignore) { }
|
||||||
|
|
||||||
|
int collectedCount = 0;
|
||||||
|
|
||||||
|
Reference<? extends BasicPhantomReference> phantomRef = PHANTOM_REFERENCE_QUEUE.poll();
|
||||||
|
while (phantomRef != null)
|
||||||
|
{
|
||||||
|
// destroy the buffer if it hasn't been cleared yet
|
||||||
|
Class<?> parentClass = PHANTOM_TO_PARENT_CLASS.remove((PhantomReference<? extends BasicPhantomReference>)phantomRef); // cast to make IntelliJ happy
|
||||||
|
{
|
||||||
|
String parentClassName = "NULL";
|
||||||
|
if (parentClass != null)
|
||||||
|
{
|
||||||
|
parentClassName = parentClass.getSimpleName();
|
||||||
|
}
|
||||||
|
|
||||||
|
PhantomLoggingHelper.putAndIncrementTrackingString(parentClassName, parentClassNameCountPairList);
|
||||||
|
//LOGGER.info("Phantom collected for class: [" + parentClassName + "]");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//if (LOG_PHANTOM_ALLOCATION_STACKS) // stack trace shouldn't be null, but just in case
|
||||||
|
//{
|
||||||
|
// String stack = BUFFER_ID_TO_ALLOCATION_STRING.get(idRef);
|
||||||
|
// if (stack != null)
|
||||||
|
// {
|
||||||
|
// PhantomLoggingHelper.putAndIncrementTrackingString(stack, allocationStackTraceCountPairList);
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
|
||||||
|
|
||||||
|
collectedCount++;
|
||||||
|
phantomRef = PHANTOM_REFERENCE_QUEUE.poll();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if (LOG_PHANTOM_RECOVERY)
|
||||||
|
{
|
||||||
|
// we only want to log when something has been returned
|
||||||
|
if (collectedCount != 0)
|
||||||
|
{
|
||||||
|
LOGGER.warn("Phantoms collected: ["+ F3Screen.NUMBER_FORMAT.format(collectedCount)+"].");
|
||||||
|
|
||||||
|
PhantomLoggingHelper.LogAllocationStackTracePairCounts(LOGGER, parentClassNameCountPairList);
|
||||||
|
|
||||||
|
//// log stack traces if present
|
||||||
|
//if (LOG_PHANTOM_ALLOCATION_STACKS)
|
||||||
|
//{
|
||||||
|
// PhantomLoggingHelper.LogAllocationStackTracePairCounts(LOGGER, allocationStackTraceCountPairList);
|
||||||
|
//}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
LOGGER.error("Unexpected error in buffer cleanup thread: [" + e.getMessage() + "].", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//endregion
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//endregion
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
+8
-1
@@ -73,10 +73,17 @@ public class RateLimitedThreadPoolExecutor extends ThreadPoolExecutor
|
|||||||
{
|
{
|
||||||
super.afterExecute(runnable, throwable);
|
super.afterExecute(runnable, throwable);
|
||||||
|
|
||||||
|
double ratio = this.runTimeRatioConfig.get();
|
||||||
|
if (ratio >= 1.0)
|
||||||
|
{
|
||||||
|
// Avoid sleeping for 0 time
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
long runTime = System.nanoTime() - this.runStartTime.get();
|
long runTime = System.nanoTime() - this.runStartTime.get();
|
||||||
Thread.sleep(TimeUnit.NANOSECONDS.toMillis((long) (runTime / this.runTimeRatioConfig.get() - runTime)));
|
Thread.sleep(TimeUnit.NANOSECONDS.toMillis((long) (runTime / ratio - runTime)));
|
||||||
}
|
}
|
||||||
catch (InterruptedException ignore)
|
catch (InterruptedException ignore)
|
||||||
{
|
{
|
||||||
|
|||||||
+5
-1
@@ -23,10 +23,14 @@ import com.seibel.distanthorizons.api.interfaces.render.IDhApiCustomRenderRegist
|
|||||||
import com.seibel.distanthorizons.core.render.RenderParams;
|
import com.seibel.distanthorizons.core.render.RenderParams;
|
||||||
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IProfilerWrapper;
|
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IProfilerWrapper;
|
||||||
|
|
||||||
public interface IDhGenericRenderer extends IDhApiCustomRenderRegister
|
public interface IDhGenericRenderer extends IDhApiCustomRenderRegister, AutoCloseable
|
||||||
{
|
{
|
||||||
void render(RenderParams renderEventParam, IProfilerWrapper profiler, boolean renderingWithSsao);
|
void render(RenderParams renderEventParam, IProfilerWrapper profiler, boolean renderingWithSsao);
|
||||||
|
|
||||||
String getVboRenderDebugMenuString();
|
String getVboRenderDebugMenuString();
|
||||||
|
|
||||||
|
@Override void close(); // override to remove "throws exception"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -533,6 +533,26 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
"distanthorizons.config.client.advanced.debugging.positionFinderDebugging":
|
||||||
|
"Position Finder",
|
||||||
|
|
||||||
|
"distanthorizons.config.client.advanced.debugging.positionFinderDebugging.positionFinderEnable":
|
||||||
|
"Enable Position Finder",
|
||||||
|
"distanthorizons.config.client.advanced.debugging.positionFinderDebugging.positionFinderDetailLevel":
|
||||||
|
"Absolute Detail Level",
|
||||||
|
"distanthorizons.config.client.advanced.debugging.positionFinderDebugging.positionFinderXPos":
|
||||||
|
"Pos X",
|
||||||
|
"distanthorizons.config.client.advanced.debugging.positionFinderDebugging.positionFinderZPos":
|
||||||
|
"Pos Z",
|
||||||
|
"distanthorizons.config.client.advanced.debugging.positionFinderDebugging.positionFinderMinBlockY":
|
||||||
|
"Min Block Y",
|
||||||
|
"distanthorizons.config.client.advanced.debugging.positionFinderDebugging.positionFinderMaxBlockY":
|
||||||
|
"Max Block Y",
|
||||||
|
"distanthorizons.config.client.advanced.debugging.positionFinderDebugging.positionFinderMarginPercent":
|
||||||
|
"Margin %",
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
"distanthorizons.config.client.advanced.debugging.f3Screen":
|
"distanthorizons.config.client.advanced.debugging.f3Screen":
|
||||||
"F3 Screen",
|
"F3 Screen",
|
||||||
|
|
||||||
|
|||||||
@@ -28,13 +28,36 @@ layout (std140) uniform fragUniformBlock
|
|||||||
vec3 calcViewPosition(float fragmentDepth, mat4 invMvmProj)
|
vec3 calcViewPosition(float fragmentDepth, mat4 invMvmProj)
|
||||||
{
|
{
|
||||||
// normalized device coordinates
|
// normalized device coordinates
|
||||||
vec4 ndc = vec4(TexCoord.xy, fragmentDepth, 1.0);
|
vec4 ndc = vec4(
|
||||||
|
TexCoord.x, // UV [0,1]
|
||||||
|
TexCoord.y,
|
||||||
|
fragmentDepth, // depth [0,1]
|
||||||
|
1.0
|
||||||
|
);
|
||||||
|
// AKA: remap the [0,1] UV coordinates and depth value
|
||||||
|
// into the [-1,1] positions used by
|
||||||
|
// the NDC cube rendering stage
|
||||||
ndc.xyz = ndc.xyz * 2.0 - 1.0;
|
ndc.xyz = ndc.xyz * 2.0 - 1.0;
|
||||||
|
|
||||||
vec4 eyeCoord = invMvmProj * ndc;
|
vec4 eyeCoord = invMvmProj * ndc;
|
||||||
return eyeCoord.xyz / eyeCoord.w;
|
return eyeCoord.xyz / eyeCoord.w;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO vulkan
|
||||||
|
vec3 calcReversedZViewPosition(float fragmentDepth, mat4 invMvmProj)
|
||||||
|
{
|
||||||
|
// Vulkan NDC: xy in [-1,+1], z already in [0,1] — don't remap z
|
||||||
|
vec4 ndc = vec4(
|
||||||
|
TexCoord.x * 2.0 - 1.0, // UV [0,1] -> NDC [-1,+1]
|
||||||
|
TexCoord.y * 2.0 - 1.0,
|
||||||
|
fragmentDepth, // no remapping needed, this depth is already in the [0,1] range
|
||||||
|
1.0 // w=1 placeholder for matrix multiplication
|
||||||
|
);
|
||||||
|
|
||||||
|
vec4 eyeCoord = invMvmProj * ndc;
|
||||||
|
return eyeCoord.xyz / eyeCoord.w;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Used to fade out vanilla chunks so the transition
|
* Used to fade out vanilla chunks so the transition
|
||||||
* between DH and vanilla is smoother.
|
* between DH and vanilla is smoother.
|
||||||
@@ -71,12 +94,13 @@ void main()
|
|||||||
{
|
{
|
||||||
fragColor = vec4(combinedMcDhColor.rgb, 0.0);
|
fragColor = vec4(combinedMcDhColor.rgb, 0.0);
|
||||||
}
|
}
|
||||||
// a fragment depth of "1" means the fragment wasn't drawn to,
|
// // a fragment depth of "1" means the fragment wasn't drawn to,
|
||||||
// we only want to fade vanilla rendered objects, not to the sky or LODs
|
// // we only want to fade vanilla rendered objects, not to the sky or LODs
|
||||||
else if (mcFragmentDepth < 1.0)
|
// else if (mcFragmentDepth < 1.0)
|
||||||
|
else if (mcFragmentDepth > 0)
|
||||||
{
|
{
|
||||||
// fade based on distance from the camera
|
// fade based on distance from the camera
|
||||||
vec3 mcVertexWorldPos = calcViewPosition(mcFragmentDepth, uMcInvMvmProj);
|
vec3 mcVertexWorldPos = calcReversedZViewPosition(mcFragmentDepth, uMcInvMvmProj);
|
||||||
float mcFragmentDistance = length(mcVertexWorldPos.xzy);
|
float mcFragmentDistance = length(mcVertexWorldPos.xzy);
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
#version 150 core
|
#version 150 core
|
||||||
|
|
||||||
in vec2 vPosition;
|
in vec2 vPosition;
|
||||||
in vec4 vColor;
|
|
||||||
|
|
||||||
out vec4 fColor;
|
out vec4 fColor;
|
||||||
out vec2 TexCoord;
|
out vec2 TexCoord;
|
||||||
|
|||||||
@@ -113,12 +113,13 @@ void main()
|
|||||||
float fadeDistance = uFadeDistanceInBlocks;
|
float fadeDistance = uFadeDistanceInBlocks;
|
||||||
if (distanceFromCamera < fadeDistance)
|
if (distanceFromCamera < fadeDistance)
|
||||||
{
|
{
|
||||||
#ifdef GL_ARB_derivative_control
|
// TODO vulkan
|
||||||
// Get higher precision derivatives when available
|
// #ifdef GL_ARB_derivative_control
|
||||||
vec3 viewNormal = cross(dFdxFine(viewPos.xyz), dFdyFine(viewPos.xyz));
|
// // Get higher precision derivatives when available
|
||||||
#else
|
// vec3 viewNormal = cross(dFdxFine(viewPos.xyz), dFdyFine(viewPos.xyz));
|
||||||
|
// #else
|
||||||
vec3 viewNormal = cross(dFdx(viewPos.xyz), dFdy(viewPos.xyz));
|
vec3 viewNormal = cross(dFdx(viewPos.xyz), dFdy(viewPos.xyz));
|
||||||
#endif
|
// #endif
|
||||||
|
|
||||||
viewNormal = normalize(viewNormal);
|
viewNormal = normalize(viewNormal);
|
||||||
occlusion = GetSpiralOcclusion(TexCoord, viewPos, viewNormal);
|
occlusion = GetSpiralOcclusion(TexCoord, viewPos, viewNormal);
|
||||||
|
|||||||
Reference in New Issue
Block a user