diff --git a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/render/bufferBuilding/BufferQuad.java b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/render/bufferBuilding/BufferQuad.java index e090e1fb8..4faf71b36 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/render/bufferBuilding/BufferQuad.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/render/bufferBuilding/BufferQuad.java @@ -59,9 +59,19 @@ public final class BufferQuad 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, int color, byte irisBlockMaterialId, byte skylight, byte blockLight, EDhDirection direction) @@ -85,64 +95,46 @@ public final class BufferQuad this.skyLight = skylight; this.blockLight = blockLight; this.direction = direction; + this.sortKeyEastWest = computeSortKey(direction, true); + this.sortKeyNorthSouth = computeSortKey(direction, false); } - - - - /** a rough but fast calculation */ - double calculateDistance(double relativeX, double relativeY, double relativeZ) + private long computeSortKey(EDhDirection dir, boolean eastWest) { - 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) { if (this.direction != quad.direction) throw new IllegalArgumentException("The other quad is not in the same direction: " + quad.direction + " vs " + this.direction); - if (compareDirection == BufferMergeDirectionEnum.EastWest) - { - switch (this.direction.axis) - { - 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.
- * 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.
- * 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); + return compareDirection == BufferMergeDirectionEnum.EastWest + ? Long.compare(this.sortKeyEastWest, quad.sortKeyEastWest) + : Long.compare(this.sortKeyNorthSouth, quad.sortKeyNorthSouth); } @@ -154,11 +146,15 @@ public final class BufferQuad public boolean tryMerge(BufferQuad quad, BufferMergeDirectionEnum mergeDirection) { if (quad.hasError || this.hasError) + { return false; + } // only merge quads that are in the same direction if (this.direction != quad.direction) + { return false; + } // make sure these quads share the same perpendicular axis if ((mergeDirection == BufferMergeDirectionEnum.EastWest && this.y != quad.y) @@ -175,7 +171,6 @@ public final class BufferQuad short otherParallelCompareStartPos; switch (this.direction.axis) { - default: // shouldn't normally happen, just here to make the compiler happy case X: if (mergeDirection == BufferMergeDirectionEnum.EastWest) { @@ -232,6 +227,9 @@ public final class BufferQuad otherParallelCompareStartPos = quad.z; } 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 @@ -333,4 +331,6 @@ public final class BufferQuad return true; } + + } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/render/bufferBuilding/ColumnBox.java b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/render/bufferBuilding/ColumnBox.java index cfa0082e2..bff8ece2a 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/render/bufferBuilding/ColumnBox.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/render/bufferBuilding/ColumnBox.java @@ -341,13 +341,24 @@ public class ColumnBox // 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 short adjAboveMinY = RenderDataPointUtil.getYMin(adjAbovePoint); 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, * splitting segments as needed + * and putting the new segments into "newSegments" *

* source: claude.ai */ - private static void applyLightToRange( + private static void applyLightToRangeAndPopulateNewSgements( LongArrayList segments, LongArrayList newSegments, short rangeStart, short rangeEnd, byte newLight) @@ -419,9 +431,6 @@ public class ColumnBox newSegments.add(YSegmentUtil.encode(rangeEnd, endY, skyLight)); } } - - segments.clear(); - segments.addAll(newSegments); } private static void tryAddVerticalFaceWithSkyLightToBuilder( diff --git a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/transformers/FullDataToRenderDataTransformer.java b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/transformers/FullDataToRenderDataTransformer.java index 3af137531..95f249aa2 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/transformers/FullDataToRenderDataTransformer.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/transformers/FullDataToRenderDataTransformer.java @@ -129,6 +129,7 @@ public class FullDataToRenderDataTransformer ColumnRenderView tempExpandingColumnView = ColumnRenderView.getPooled(); RenderDataPointReducingList reducingList = new RenderDataPointReducingList()) { + DhBlockPosMutable mutableBlockPos = new DhBlockPosMutable(); for (int x = 0; x < FullDataSourceV2.WIDTH; x++) { 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), columnArrayView, dataColumn, // 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, LongArrayList fullDataColumn, // 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 if (fullDataColumn == null @@ -170,7 +171,7 @@ public class FullDataToRenderDataTransformer if (fullDataLength <= columnArrayView.maxVerticalSliceCount) { // Directly use the arrayView since it fits. - setRenderColumnView(levelWrapper, fullDataSource, blockX, blockZ, columnArrayView, fullDataColumn); + setRenderColumnView(levelWrapper, fullDataSource, blockX, blockZ, columnArrayView, fullDataColumn, mutableBlockPos); } else { @@ -178,7 +179,7 @@ public class FullDataToRenderDataTransformer // expand the ColumnArrayView to fit the new larger max vertical size 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); } @@ -186,7 +187,7 @@ public class FullDataToRenderDataTransformer private static void setRenderColumnView( IClientLevelWrapper levelWrapper, FullDataSourceV2 fullDataSource, int blockX, int blockZ, - ColumnRenderView renderColumnData, LongArrayList fullColumnData) + ColumnRenderView renderColumnData, LongArrayList fullColumnData, DhBlockPosMutable mutableBlockPos) { //===============// // config values // @@ -242,7 +243,8 @@ public class FullDataToRenderDataTransformer FullDataPointIdMap fullDataMapping = fullDataSource.mapping; - DhBlockPosMutable mutableBlockPos = new DhBlockPosMutable(blockX, 0, blockZ); + mutableBlockPos.setX(blockX); + mutableBlockPos.setZ(blockZ); // goes from the top down for (int fullDataIndex = 0; fullDataIndex < fullColumnData.size(); fullDataIndex++) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/util/threading/RateLimitedThreadPoolExecutor.java b/core/src/main/java/com/seibel/distanthorizons/core/util/threading/RateLimitedThreadPoolExecutor.java index ad142d2e9..aa8203c3d 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/util/threading/RateLimitedThreadPoolExecutor.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/util/threading/RateLimitedThreadPoolExecutor.java @@ -73,10 +73,17 @@ public class RateLimitedThreadPoolExecutor extends ThreadPoolExecutor { super.afterExecute(runnable, throwable); + double ratio = this.runTimeRatioConfig.get(); + if (ratio >= 1.0) + { + // Avoid sleeping for 0 time + return; + } + try { 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) { diff --git a/core/src/main/resources/assets/distanthorizons/lang/en_us.json b/core/src/main/resources/assets/distanthorizons/lang/en_us.json index 69dbd660e..4887d3807 100644 --- a/core/src/main/resources/assets/distanthorizons/lang/en_us.json +++ b/core/src/main/resources/assets/distanthorizons/lang/en_us.json @@ -188,11 +188,11 @@ "If true non terrain objects will be rendered in DH's terrain. \nThis includes beacon beams and clouds.", "distanthorizons.config.client.advanced.graphics.genericRendering.enableBeaconRendering": "Enable Beacon Rendering", - "distanthorizons.config.client.advanced.graphics.genericRendering.beaconRenderHeight": + "distanthorizons.config.client.advanced.graphics.genericRendering.beaconRenderHeight": "Beacon render height", "distanthorizons.config.client.advanced.graphics.genericRendering.beaconRenderHeight.@tooltip": "Sets the maximum height at which beacons will render. \nThis will only affect new beacons coming into LOD render distance. \nBeacons currently visible in LOD chunks will not be affected.", - "distanthorizons.config.client.advanced.graphics.genericRendering.expandDistantBeacons": + "distanthorizons.config.client.advanced.graphics.genericRendering.expandDistantBeacons": "Expand Distant Beacons", "distanthorizons.config.client.advanced.graphics.genericRendering.expandDistantBeacons.@tooltip": "If true LOD beacon beams will be rendered wider at extreme distances, \nmaking them easier to see. \nIf false all LOD beacon beams will only ever be 1 block wide.",