Compare commits
41 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 71e6b9b58e | |||
| 1ec342928f | |||
| d45a1379bd | |||
| ecb3dce963 | |||
| 2b8cddd424 | |||
| 13895fec51 | |||
| 88c7245be6 | |||
| 6aad156a32 | |||
| 94535a213e | |||
| 63d6d42356 | |||
| acecbede8e | |||
| e2a8953e4c | |||
| 32eae23963 | |||
| 480c3b3ec5 | |||
| 6c0736a2a0 | |||
| c89abd414b | |||
| da2454b249 | |||
| 5933ef8245 | |||
| b9984c7723 | |||
| 2de50475f1 | |||
| bb838328a7 | |||
| 1980e64b2f | |||
| c3df26f5cb | |||
| 17df533fa6 | |||
| 2393bdd8e8 | |||
| 082b364f52 | |||
| 8a39610b8c | |||
| aa53835772 | |||
| cd5a3ce52b | |||
| 38e104a9fc | |||
| e27cee1f71 | |||
| 15d1d78954 | |||
| e2421c97ed | |||
| 8b72920652 | |||
| 212031a05f | |||
| 804293e291 | |||
| cfdd464f30 | |||
| 4c9d703e15 | |||
| 0eba376e70 | |||
| 89e6355d73 | |||
| 40149bc1d1 |
+20
-4
@@ -2,10 +2,26 @@ import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar
|
|||||||
|
|
||||||
plugins {
|
plugins {
|
||||||
id "java"
|
id "java"
|
||||||
|
id "com.gradleup.shadow"
|
||||||
id "com.github.johnrengelman.shadow" version '8.1.1' apply false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
repositories {
|
||||||
|
mavenCentral()
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks.withType(JavaCompile).configureEach {
|
||||||
|
options.release = 8
|
||||||
|
options.encoding = "UTF-8"
|
||||||
|
}
|
||||||
|
|
||||||
|
configurations {
|
||||||
|
testImplementation.extendsFrom compileOnly
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
compileOnly "org.apache.logging.log4j:log4j-api:${rootProject.log4j_version}"
|
||||||
|
testImplementation "junit:junit:4.13"
|
||||||
|
}
|
||||||
|
|
||||||
shadowJar {
|
shadowJar {
|
||||||
// required for basic shadowJar setup
|
// required for basic shadowJar setup
|
||||||
@@ -21,7 +37,7 @@ task addSourcesToCompiledJar(type: ShadowJar) {
|
|||||||
doFirst {
|
doFirst {
|
||||||
System.out.println("Adding source files from: \n" +
|
System.out.println("Adding source files from: \n" +
|
||||||
"[" + sourceJarPath + "] to compiled API jar: \n" +
|
"[" + sourceJarPath + "] to compiled API jar: \n" +
|
||||||
"[" + shadowJar.archivePath + "]")
|
"[" + shadowJar.archiveFile.get().asFile + "]")
|
||||||
|
|
||||||
// Validate the input JAR file
|
// Validate the input JAR file
|
||||||
if (!secondJarFile.exists()) {
|
if (!secondJarFile.exists()) {
|
||||||
@@ -42,7 +58,7 @@ task addSourcesToCompiledJar(type: ShadowJar) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// set the jars to merge
|
// set the jars to merge
|
||||||
from shadowJar.archivePath
|
from shadowJar.archiveFile.get().asFile
|
||||||
from secondJarFile
|
from secondJarFile
|
||||||
|
|
||||||
// alternative method to Include the source files in the combined JAR
|
// alternative method to Include the source files in the combined JAR
|
||||||
|
|||||||
+9
-9
@@ -20,17 +20,17 @@
|
|||||||
package com.seibel.distanthorizons.api.enums.rendering;
|
package com.seibel.distanthorizons.api.enums.rendering;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Default <br>
|
* DEFAULT <br>
|
||||||
* Debug <br>
|
* DEBUG_TRIANGLE <br>
|
||||||
* Disabled <br>
|
* DISABLED <br>
|
||||||
*
|
*
|
||||||
* @since API 2.0.0
|
* @since API 2.0.0
|
||||||
* @version 2024-4-6
|
* @version 2026-03-23
|
||||||
*/
|
*/
|
||||||
public enum EDhApiRendererMode
|
public enum EDhApiRendererMode
|
||||||
{
|
{
|
||||||
DEFAULT,
|
DEFAULT,
|
||||||
DEBUG,
|
DEBUG_TRIANGLE,
|
||||||
DISABLED;
|
DISABLED;
|
||||||
|
|
||||||
|
|
||||||
@@ -40,8 +40,8 @@ public enum EDhApiRendererMode
|
|||||||
switch (type)
|
switch (type)
|
||||||
{
|
{
|
||||||
case DEFAULT:
|
case DEFAULT:
|
||||||
return DEBUG;
|
return DEBUG_TRIANGLE;
|
||||||
case DEBUG:
|
case DEBUG_TRIANGLE:
|
||||||
return DISABLED;
|
return DISABLED;
|
||||||
default:
|
default:
|
||||||
return DEFAULT;
|
return DEFAULT;
|
||||||
@@ -55,10 +55,10 @@ public enum EDhApiRendererMode
|
|||||||
{
|
{
|
||||||
case DEFAULT:
|
case DEFAULT:
|
||||||
return DISABLED;
|
return DISABLED;
|
||||||
case DEBUG:
|
case DEBUG_TRIANGLE:
|
||||||
return DEFAULT;
|
return DEFAULT;
|
||||||
default:
|
default:
|
||||||
return DEBUG;
|
return DEBUG_TRIANGLE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -38,7 +38,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 = "2.4.6-b-dev";
|
public static final String VERSION = "3.0.0-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");
|
||||||
|
|
||||||
|
|||||||
+45
-32
@@ -1,18 +1,23 @@
|
|||||||
plugins {
|
plugins {
|
||||||
id "java"
|
id "java"
|
||||||
id "com.github.johnrengelman.shadow" version '8.1.1' apply false // Set this to true if you're using the standalone Core project
|
id "com.gradleup.shadow"
|
||||||
}
|
}
|
||||||
|
|
||||||
apply plugin: "application"
|
repositories {
|
||||||
|
mavenCentral()
|
||||||
|
maven { url "https://repo.enonic.com/public/" }
|
||||||
|
}
|
||||||
|
|
||||||
application {
|
tasks.withType(JavaCompile).configureEach {
|
||||||
mainClass.set("com.seibel.distanthorizons.core.jar.JarMain")
|
options.release = 8
|
||||||
|
options.encoding = "UTF-8"
|
||||||
}
|
}
|
||||||
|
|
||||||
configurations {
|
configurations {
|
||||||
shadowedArtifact // Used by DH to specify that we want to implement the shadowed core JAR file instead of the regular JAR file
|
shadowedArtifact // Used by DH to specify that we want to implement the shadowed core JAR file instead of the regular JAR file
|
||||||
shade
|
shade
|
||||||
implementation.extendsFrom shade
|
implementation.extendsFrom shade
|
||||||
|
testImplementation.extendsFrom compileOnly
|
||||||
}
|
}
|
||||||
|
|
||||||
OperatingSystem os = org.gradle.nativeplatform.platform.internal.DefaultNativePlatform.currentOperatingSystem;
|
OperatingSystem os = org.gradle.nativeplatform.platform.internal.DefaultNativePlatform.currentOperatingSystem;
|
||||||
@@ -20,38 +25,46 @@ OperatingSystem os = org.gradle.nativeplatform.platform.internal.DefaultNativePl
|
|||||||
// Set the OS lwjgl is using to the current os
|
// Set the OS lwjgl is using to the current os
|
||||||
project.ext.lwjglNatives = "natives-" + os.toFamilyName()
|
project.ext.lwjglNatives = "natives-" + os.toFamilyName()
|
||||||
|
|
||||||
dependencies { // All of these dependencies are in Vanilla Minecraft, but we need to depend on it as we arent importing Minecraft in the core
|
dependencies {
|
||||||
// Imports most of lwjgl's libraries (well, only the ones that we need)
|
// API project dependency
|
||||||
implementation platform("org.lwjgl:lwjgl-bom:${rootProject.lwjgl_version}")
|
implementation project(":api")
|
||||||
|
|
||||||
// REMEMBER: Dont shadow stuff here, these are just the libs that are included in Minecraft so that the core can use
|
// MC-provided libraries (available at runtime via Minecraft)
|
||||||
implementation "org.lwjgl:lwjgl"
|
compileOnly platform("org.lwjgl:lwjgl-bom:${rootProject.lwjgl_version}")
|
||||||
implementation "org.lwjgl:lwjgl-assimp"
|
compileOnly "org.lwjgl:lwjgl"
|
||||||
implementation "org.lwjgl:lwjgl-glfw"
|
compileOnly "org.lwjgl:lwjgl-assimp"
|
||||||
// OpenGL is removed since DH now handles rendering in the "Common" project
|
compileOnly "org.lwjgl:lwjgl-glfw"
|
||||||
// so we can use OpenGL for old MC versions and Blaze3D (IE Vulkan) for newer ones
|
compileOnly "org.lwjgl:lwjgl-stb"
|
||||||
// implementation "org.lwjgl:lwjgl-openal"
|
compileOnly "org.lwjgl:lwjgl-tinyfd"
|
||||||
// implementation "org.lwjgl:lwjgl-opengl"
|
testRuntimeOnly platform("org.lwjgl:lwjgl-bom:${rootProject.lwjgl_version}")
|
||||||
implementation "org.lwjgl:lwjgl-stb"
|
testRuntimeOnly "org.lwjgl:lwjgl::$lwjglNatives"
|
||||||
implementation "org.lwjgl:lwjgl-tinyfd"
|
testRuntimeOnly "org.lwjgl:lwjgl-assimp::$lwjglNatives"
|
||||||
runtimeOnly "org.lwjgl:lwjgl::$lwjglNatives"
|
testRuntimeOnly "org.lwjgl:lwjgl-glfw::$lwjglNatives"
|
||||||
runtimeOnly "org.lwjgl:lwjgl-assimp::$lwjglNatives"
|
testRuntimeOnly "org.lwjgl:lwjgl-stb::$lwjglNatives"
|
||||||
runtimeOnly "org.lwjgl:lwjgl-glfw::$lwjglNatives"
|
testRuntimeOnly "org.lwjgl:lwjgl-tinyfd::$lwjglNatives"
|
||||||
// runtimeOnly "org.lwjgl:lwjgl-openal::$lwjglNatives"
|
|
||||||
// runtimeOnly "org.lwjgl:lwjgl-opengl::$lwjglNatives"
|
|
||||||
runtimeOnly "org.lwjgl:lwjgl-stb::$lwjglNatives"
|
|
||||||
runtimeOnly "org.lwjgl:lwjgl-tinyfd::$lwjglNatives"
|
|
||||||
|
|
||||||
// FIXME for some reason this line doesn't actually shade in the library
|
compileOnly("org.apache.logging.log4j:log4j-api:${rootProject.log4j_version}")
|
||||||
// shade "it.unimi.dsi:fastutil:${rootProject.fastutil_version}" // Add our own fastutil version
|
compileOnly("org.apache.logging.log4j:log4j-core:${rootProject.log4j_version}")
|
||||||
|
compileOnly("it.unimi.dsi:fastutil:${rootProject.fastutil_version}")
|
||||||
|
compileOnly("org.joml:joml:${rootProject.joml_version}")
|
||||||
|
compileOnly("io.netty:netty-buffer:${rootProject.netty_version}")
|
||||||
|
compileOnly("org.jetbrains:annotations:16.0.2")
|
||||||
|
compileOnly("com.google.code.findbugs:jsr305:3.0.2")
|
||||||
|
compileOnly("com.google.common:google-collect:0.5")
|
||||||
|
compileOnly("com.google.guava:guava:31.1-jre")
|
||||||
|
|
||||||
|
// DH's bundled libraries (shadowed + relocated in loader jars)
|
||||||
|
implementation("com.github.luben:zstd-jni:${rootProject.zstd_version}")
|
||||||
|
implementation("org.lz4:lz4-java:${rootProject.lz4_version}")
|
||||||
|
implementation("org.tukaani:xz:${rootProject.xz_version}")
|
||||||
|
implementation("org.xerial:sqlite-jdbc:${rootProject.sqlite_jdbc_version}")
|
||||||
|
implementation("com.electronwill.night-config:toml:${rootProject.nightconfig_version}")
|
||||||
|
implementation("com.electronwill.night-config:json:${rootProject.nightconfig_version}")
|
||||||
|
|
||||||
// Some other dependencies
|
// JUnit (core tests only)
|
||||||
implementation("org.jetbrains:annotations:16.0.2")
|
compileOnly("junit:junit:4.13")
|
||||||
implementation("com.google.code.findbugs:jsr305:3.0.2")
|
compileOnly("org.junit.jupiter:junit-jupiter:5.8.2")
|
||||||
implementation("com.google.common:google-collect:0.5")
|
compileOnly("org.junit.jupiter:junit-jupiter-engine:5.8.2")
|
||||||
implementation("com.google.guava:guava:31.1-jre")
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
artifacts {
|
artifacts {
|
||||||
|
|||||||
+1
-1
@@ -35,6 +35,6 @@ public class DhApiAmbientOcclusionConfig implements IDhApiAmbientOcclusionConfig
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public IDhApiConfigValue<Boolean> enabled()
|
public IDhApiConfigValue<Boolean> enabled()
|
||||||
{ return new DhApiConfigValue<Boolean, Boolean>(Config.Client.Advanced.Graphics.Ssao.enableSsao); }
|
{ return new DhApiConfigValue<Boolean, Boolean>(Config.Client.Advanced.Graphics.enableSsao); }
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -34,7 +34,7 @@ public class DhApiDebuggingConfig implements IDhApiDebuggingConfig
|
|||||||
|
|
||||||
|
|
||||||
public IDhApiConfigValue<EDhApiDebugRendering> debugRendering()
|
public IDhApiConfigValue<EDhApiDebugRendering> debugRendering()
|
||||||
{ return new DhApiConfigValue<EDhApiDebugRendering, EDhApiDebugRendering>(Config.Client.Advanced.Debugging.debugRendering); }
|
{ return new DhApiConfigValue<EDhApiDebugRendering, EDhApiDebugRendering>(Config.Client.Advanced.Debugging.debugRenderingColors); }
|
||||||
|
|
||||||
public IDhApiConfigValue<Boolean> debugKeybindings()
|
public IDhApiConfigValue<Boolean> debugKeybindings()
|
||||||
{ return new DhApiConfigValue<Boolean, Boolean>(Config.Client.Advanced.Debugging.enableDebugKeybindings); }
|
{ return new DhApiConfigValue<Boolean, Boolean>(Config.Client.Advanced.Debugging.enableDebugKeybindings); }
|
||||||
|
|||||||
@@ -41,7 +41,6 @@ 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.render.renderPass.IDhMetaRenderer;
|
import com.seibel.distanthorizons.core.wrapperInterfaces.render.renderPass.IDhMetaRenderer;
|
||||||
import com.seibel.distanthorizons.core.wrapperInterfaces.render.renderPass.IDhTerrainRenderer;
|
|
||||||
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;
|
||||||
import com.seibel.distanthorizons.coreapi.DependencyInjection.ApiEventInjector;
|
import com.seibel.distanthorizons.coreapi.DependencyInjection.ApiEventInjector;
|
||||||
@@ -763,8 +762,8 @@ public class ClientApi
|
|||||||
}
|
}
|
||||||
else if (glfwKey == GLFW.GLFW_KEY_F8)
|
else if (glfwKey == GLFW.GLFW_KEY_F8)
|
||||||
{
|
{
|
||||||
Config.Client.Advanced.Debugging.debugRendering.set(EDhApiDebugRendering.next(Config.Client.Advanced.Debugging.debugRendering.get()));
|
Config.Client.Advanced.Debugging.debugRenderingColors.set(EDhApiDebugRendering.next(Config.Client.Advanced.Debugging.debugRenderingColors.get()));
|
||||||
MC_CLIENT.sendChatMessage("F8: Set debug mode to " + Config.Client.Advanced.Debugging.debugRendering.get());
|
MC_CLIENT.sendChatMessage("F8: Set debug mode to " + Config.Client.Advanced.Debugging.debugRenderingColors.get());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -90,7 +90,7 @@ public class ClientPluginChannelApi
|
|||||||
|
|
||||||
LOGGER.info("Server level key received: [" + msg.levelKey + "].");
|
LOGGER.info("Server level key received: [" + msg.levelKey + "].");
|
||||||
|
|
||||||
RenderThreadTaskHandler.INSTANCE.queueRunningOnRenderThread(() ->
|
RenderThreadTaskHandler.INSTANCE.queueRunningOnRenderThread("ClientPluginChannelApi onLevelInitMessage", () ->
|
||||||
{
|
{
|
||||||
IClientLevelWrapper clientLevel = MC.getWrappedClientLevel(true);
|
IClientLevelWrapper clientLevel = MC.getWrappedClientLevel(true);
|
||||||
IServerKeyedClientLevel existingKeyedClientLevel = KEYED_CLIENT_LEVEL_MANAGER.getServerKeyedLevel();
|
IServerKeyedClientLevel existingKeyedClientLevel = KEYED_CLIENT_LEVEL_MANAGER.getServerKeyedLevel();
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ import com.seibel.distanthorizons.core.level.IDhLevel;
|
|||||||
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
|
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
|
||||||
import com.seibel.distanthorizons.core.pos.blockPos.DhBlockPos2D;
|
import com.seibel.distanthorizons.core.pos.blockPos.DhBlockPos2D;
|
||||||
import com.seibel.distanthorizons.core.pos.DhChunkPos;
|
import com.seibel.distanthorizons.core.pos.DhChunkPos;
|
||||||
|
import com.seibel.distanthorizons.core.render.RenderThreadTaskHandler;
|
||||||
import com.seibel.distanthorizons.core.render.renderer.AbstractDebugWireframeRenderer;
|
import com.seibel.distanthorizons.core.render.renderer.AbstractDebugWireframeRenderer;
|
||||||
import com.seibel.distanthorizons.core.sql.repo.AbstractDhRepo;
|
import com.seibel.distanthorizons.core.sql.repo.AbstractDhRepo;
|
||||||
import com.seibel.distanthorizons.core.util.objects.Pair;
|
import com.seibel.distanthorizons.core.util.objects.Pair;
|
||||||
@@ -63,6 +64,7 @@ public class SharedApi
|
|||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
private static AbstractDhWorld currentWorld;
|
private static AbstractDhWorld currentWorld;
|
||||||
|
private static final Object worldLockObject = new Object();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -87,46 +89,51 @@ public class SharedApi
|
|||||||
|
|
||||||
public static void setDhWorld(AbstractDhWorld newWorld)
|
public static void setDhWorld(AbstractDhWorld newWorld)
|
||||||
{
|
{
|
||||||
AbstractDhWorld oldWorld = currentWorld;
|
synchronized (worldLockObject)
|
||||||
if (oldWorld != null)
|
|
||||||
{
|
{
|
||||||
oldWorld.close();
|
AbstractDhWorld oldWorld = currentWorld;
|
||||||
}
|
if (oldWorld != null)
|
||||||
currentWorld = newWorld;
|
|
||||||
|
|
||||||
// starting and stopping the DataRenderTransformer is necessary to prevent attempting to
|
|
||||||
// access the MC level at inappropriate times, which can cause exceptions
|
|
||||||
if (currentWorld != null)
|
|
||||||
{
|
|
||||||
ThreadPoolUtil.setupThreadPools();
|
|
||||||
|
|
||||||
ApiEventInjector.INSTANCE.fireAllEvents(DhApiWorldLoadEvent.class, new DhApiWorldLoadEvent.EventParam());
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
ThreadPoolUtil.shutdownThreadPools();
|
|
||||||
|
|
||||||
// delayed get because SharedApi will be created before the singleton has been bound
|
|
||||||
AbstractDebugWireframeRenderer debugWireframeRenderer = SingletonInjector.INSTANCE.get(AbstractDebugWireframeRenderer.class);
|
|
||||||
debugWireframeRenderer.clearRenderables();
|
|
||||||
|
|
||||||
if (MC_RENDER != null)
|
|
||||||
{
|
{
|
||||||
MC_RENDER.clearTargetFrameBuffer();
|
oldWorld.close();
|
||||||
}
|
}
|
||||||
|
currentWorld = newWorld;
|
||||||
|
|
||||||
// shouldn't be necessary, but if we missed closing one of the connections this should make sure they're all closed
|
// starting and stopping the DataRenderTransformer is necessary to prevent attempting to
|
||||||
AbstractDhRepo.closeAllConnections();
|
// access the MC level at inappropriate times, which can cause exceptions
|
||||||
// needs to be closed on world shutdown to clear out un-processed chunks
|
if (currentWorld != null)
|
||||||
WORLD_CHUNK_UPDATE_MANAGER.clear();
|
{
|
||||||
|
ThreadPoolUtil.setupThreadPools();
|
||||||
|
|
||||||
// recommend that the garbage collector cleans up any objects from the old world and thread pools
|
ApiEventInjector.INSTANCE.fireAllEvents(DhApiWorldLoadEvent.class, new DhApiWorldLoadEvent.EventParam());
|
||||||
System.gc();
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ThreadPoolUtil.shutdownThreadPools();
|
||||||
|
|
||||||
ApiEventInjector.INSTANCE.fireAllEvents(DhApiWorldUnloadEvent.class, new DhApiWorldUnloadEvent.EventParam());
|
// delayed get because SharedApi will be created before the singleton has been bound
|
||||||
|
AbstractDebugWireframeRenderer debugWireframeRenderer = SingletonInjector.INSTANCE.get(AbstractDebugWireframeRenderer.class);
|
||||||
|
debugWireframeRenderer.clearRenderables();
|
||||||
|
|
||||||
// fired after the unload event so API users can't change the read-only for any new worlds
|
if (MC_RENDER != null)
|
||||||
DhApiWorldProxy.INSTANCE.setReadOnly(false, false);
|
{
|
||||||
|
MC_RENDER.clearTargetFrameBuffer();
|
||||||
|
}
|
||||||
|
|
||||||
|
// shouldn't be necessary, but if we missed closing one of the connections this should make sure they're all closed
|
||||||
|
AbstractDhRepo.closeAllConnections();
|
||||||
|
// needs to be closed on world shutdown to clear out un-processed chunks
|
||||||
|
WORLD_CHUNK_UPDATE_MANAGER.clear();
|
||||||
|
|
||||||
|
RenderThreadTaskHandler.INSTANCE.clearDebugStats();
|
||||||
|
|
||||||
|
// recommend that the garbage collector cleans up any objects from the old world and thread pools
|
||||||
|
System.gc();
|
||||||
|
|
||||||
|
ApiEventInjector.INSTANCE.fireAllEvents(DhApiWorldUnloadEvent.class, new DhApiWorldUnloadEvent.EventParam());
|
||||||
|
|
||||||
|
// fired after the unload event so API users can't change the read-only for any new worlds
|
||||||
|
DhApiWorldProxy.INSTANCE.setReadOnly(false, false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+10
@@ -11,6 +11,8 @@ import com.seibel.distanthorizons.core.wrapperInterfaces.world.IServerLevelWrapp
|
|||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Set;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
|
||||||
@@ -28,6 +30,9 @@ public class WorldChunkUpdateManager
|
|||||||
/** singleton since we only expect to have one world loaded at a time */
|
/** singleton since we only expect to have one world loaded at a time */
|
||||||
public static final WorldChunkUpdateManager INSTANCE = new WorldChunkUpdateManager();
|
public static final WorldChunkUpdateManager INSTANCE = new WorldChunkUpdateManager();
|
||||||
|
|
||||||
|
public static final Set<String> LOGGED_GET_ERROR_MESSAGES = Collections.newSetFromMap(new ConcurrentHashMap<String, Boolean>());
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Queues are only removed during world shutdown.
|
* Queues are only removed during world shutdown.
|
||||||
* The assumption is that there will be a limited number of {@link ILevelWrapper}'s
|
* The assumption is that there will be a limited number of {@link ILevelWrapper}'s
|
||||||
@@ -37,6 +42,7 @@ public class WorldChunkUpdateManager
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//=============//
|
//=============//
|
||||||
// constructor //
|
// constructor //
|
||||||
//=============//
|
//=============//
|
||||||
@@ -62,6 +68,7 @@ public class WorldChunkUpdateManager
|
|||||||
AbstractDhWorld world = SharedApi.getAbstractDhWorld();
|
AbstractDhWorld world = SharedApi.getAbstractDhWorld();
|
||||||
if (world == null)
|
if (world == null)
|
||||||
{
|
{
|
||||||
|
// world isn't loaded, no warnings need to be logged
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -73,6 +80,8 @@ public class WorldChunkUpdateManager
|
|||||||
// but this check confirms it
|
// but this check confirms it
|
||||||
&& !(levelWrapper instanceof IClientLevelWrapper))
|
&& !(levelWrapper instanceof IClientLevelWrapper))
|
||||||
{
|
{
|
||||||
|
// how did we get a server level wrapper on the client?
|
||||||
|
// this shouldn't happen, but just in case
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
else if (
|
else if (
|
||||||
@@ -81,6 +90,7 @@ public class WorldChunkUpdateManager
|
|||||||
// when hosting a server we only care about the server wrappers
|
// when hosting a server we only care about the server wrappers
|
||||||
&& !(levelWrapper instanceof IServerLevelWrapper))
|
&& !(levelWrapper instanceof IServerLevelWrapper))
|
||||||
{
|
{
|
||||||
|
// ignore client updates on the server
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -19,7 +19,6 @@
|
|||||||
|
|
||||||
package com.seibel.distanthorizons.core.config;
|
package com.seibel.distanthorizons.core.config;
|
||||||
|
|
||||||
import com.seibel.distanthorizons.api.DhApi;
|
|
||||||
import com.seibel.distanthorizons.api.enums.config.*;
|
import com.seibel.distanthorizons.api.enums.config.*;
|
||||||
import com.seibel.distanthorizons.api.enums.config.quickOptions.*;
|
import com.seibel.distanthorizons.api.enums.config.quickOptions.*;
|
||||||
import com.seibel.distanthorizons.api.enums.rendering.*;
|
import com.seibel.distanthorizons.api.enums.rendering.*;
|
||||||
@@ -27,14 +26,12 @@ import com.seibel.distanthorizons.api.enums.worldGeneration.EDhApiDistantGenerat
|
|||||||
import com.seibel.distanthorizons.api.enums.worldGeneration.EDhApiDistantGeneratorProgressDisplayLocation;
|
import com.seibel.distanthorizons.api.enums.worldGeneration.EDhApiDistantGeneratorProgressDisplayLocation;
|
||||||
import com.seibel.distanthorizons.core.config.eventHandlers.*;
|
import com.seibel.distanthorizons.core.config.eventHandlers.*;
|
||||||
import com.seibel.distanthorizons.core.config.eventHandlers.presets.*;
|
import com.seibel.distanthorizons.core.config.eventHandlers.presets.*;
|
||||||
import com.seibel.distanthorizons.core.config.listeners.ConfigChangeListener;
|
|
||||||
import com.seibel.distanthorizons.core.config.types.*;
|
import com.seibel.distanthorizons.core.config.types.*;
|
||||||
import com.seibel.distanthorizons.core.config.types.enums.*;
|
import com.seibel.distanthorizons.core.config.types.enums.*;
|
||||||
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
|
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
|
||||||
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.util.NativeDialogUtil;
|
import com.seibel.distanthorizons.core.util.NativeDialogUtil;
|
||||||
import com.seibel.distanthorizons.core.wrapperInterfaces.IWrapperFactory;
|
|
||||||
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftSharedWrapper;
|
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftSharedWrapper;
|
||||||
import com.seibel.distanthorizons.coreapi.ModInfo;
|
import com.seibel.distanthorizons.coreapi.ModInfo;
|
||||||
import com.seibel.distanthorizons.core.logging.DhLogger;
|
import com.seibel.distanthorizons.core.logging.DhLogger;
|
||||||
@@ -146,8 +143,11 @@ public class Config
|
|||||||
public static ConfigCategory quality = new ConfigCategory.Builder().set(Quality.class).build();
|
public static ConfigCategory quality = new ConfigCategory.Builder().set(Quality.class).build();
|
||||||
public static ConfigUISpacer qualitySpacer = new ConfigUISpacer.Builder().build();
|
public static ConfigUISpacer qualitySpacer = new ConfigUISpacer.Builder().build();
|
||||||
|
|
||||||
public static ConfigUiLinkedEntry quickEnableSsao = new ConfigUiLinkedEntry(Ssao.enableSsao);
|
|
||||||
public static ConfigCategory ssao = new ConfigCategory.Builder().set(Ssao.class).build();
|
public static ConfigEntry<Boolean> enableSsao = new ConfigEntry.Builder<Boolean>()
|
||||||
|
.set(true)
|
||||||
|
.comment("Enable Screen Space Ambient Occlusion")
|
||||||
|
.build();
|
||||||
public static ConfigUISpacer ssaoSpacer = new ConfigUISpacer.Builder().build();
|
public static ConfigUISpacer ssaoSpacer = new ConfigUISpacer.Builder().build();
|
||||||
|
|
||||||
|
|
||||||
@@ -358,17 +358,6 @@ public class Config
|
|||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class Ssao
|
|
||||||
{
|
|
||||||
public static ConfigUIComment ssaoHeader = new ConfigUIComment.Builder().setParentConfigClass(Ssao.class).build();
|
|
||||||
|
|
||||||
public static ConfigEntry<Boolean> enableSsao = new ConfigEntry.Builder<Boolean>()
|
|
||||||
.set(true)
|
|
||||||
.comment("Enable Screen Space Ambient Occlusion")
|
|
||||||
.build();
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class GenericRendering
|
public static class GenericRendering
|
||||||
{
|
{
|
||||||
public static ConfigUIComment genericRendererHeader = new ConfigUIComment.Builder().setParentConfigClass(GenericRendering.class).build();
|
public static ConfigUIComment genericRendererHeader = new ConfigUIComment.Builder().setParentConfigClass(GenericRendering.class).build();
|
||||||
@@ -424,14 +413,6 @@ public class Config
|
|||||||
+ "Changes will only be seen when the world is re-loaded.\n"
|
+ "Changes will only be seen when the world is re-loaded.\n"
|
||||||
+ "")
|
+ "")
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
public static ConfigEntry<Boolean> enableInstancedRendering = new ConfigEntry.Builder<Boolean>()
|
|
||||||
.set(true)
|
|
||||||
.comment(""
|
|
||||||
+ "Can be disabled to use much slower but more compatible direct rendering. \n"
|
|
||||||
+ "Disabling this can be used to fix some crashes on Mac. \n"
|
|
||||||
+ "")
|
|
||||||
.build();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class Fog
|
public static class Fog
|
||||||
@@ -565,7 +546,7 @@ public class Config
|
|||||||
.build();
|
.build();
|
||||||
|
|
||||||
public static ConfigEntry<Float> heightFogBaseHeight = new ConfigEntry.Builder<Float>()
|
public static ConfigEntry<Float> heightFogBaseHeight = new ConfigEntry.Builder<Float>()
|
||||||
.setMinDefaultMax(-4096.0f, 80.0f, 4096.0f)
|
.setMinDefaultMax(-3_000_000.0f, 80.0f, 3_000_000.0f)
|
||||||
.comment("If the height fog is calculated around a set height, what is that height position?")
|
.comment("If the height fog is calculated around a set height, what is that height position?")
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
@@ -745,57 +726,56 @@ public class Config
|
|||||||
+ "Disable this if shadows render incorrectly.")
|
+ "Disable this if shadows render incorrectly.")
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
|
public static ConfigUISpacer ignoreCsvStartSpacer = new ConfigUISpacer.Builder().build();
|
||||||
|
|
||||||
public static ConfigEntry<String> ignoredRenderBlockCsv = new ConfigEntry.Builder<String>()
|
public static ConfigEntry<String> ignoredRenderBlockCsv = new ConfigEntry.Builder<String>()
|
||||||
.set("minecraft:barrier,minecraft:structure_void,minecraft:light,minecraft:tripwire,minecraft:brown_mushroom")
|
.set("minecraft:barrier,minecraft:structure_void,minecraft:light,minecraft:tripwire,minecraft:brown_mushroom")
|
||||||
.setAppearance(EConfigEntryAppearance.ALL)
|
.setAppearance(EConfigEntryAppearance.ALL)
|
||||||
|
.addListener(RenderBlockCacheCsvHandler.INSTANCE)
|
||||||
.comment(""
|
.comment(""
|
||||||
+ "A comma separated list of block resource locations that won't be rendered by DH. \n"
|
+ "A comma separated list of block resource locations that won't be rendered by DH. \n"
|
||||||
+ "Air is always included in this list. \n"
|
+ "Air is always included in this list. \n"
|
||||||
+ "Requires a restart to change. \n"
|
|
||||||
+ "\n"
|
+ "\n"
|
||||||
+ "Note:\n"
|
+ "Note:\n"
|
||||||
+ "If you see gaps, or holes you may have to change\n"
|
+ "If you see gaps, or holes you may have to change\n"
|
||||||
+ "worldCompression to ["+EDhApiWorldCompressionMode.MERGE_SAME_BLOCKS+"] and re-generate the LODs.\n"
|
+ "worldCompression to ["+EDhApiWorldCompressionMode.MERGE_SAME_BLOCKS+"] and re-generate the LODs.\n"
|
||||||
+ "Black spots may happen occur to block lighting being zero for covered blocks.\n"
|
|
||||||
+ "")
|
+ "")
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
public static ConfigEntry<String> ignoredRenderCaveBlockCsv = new ConfigEntry.Builder<String>()
|
public static ConfigEntry<String> ignoredRenderCaveBlockCsv = new ConfigEntry.Builder<String>()
|
||||||
.set("") // config is empty since most cave blocks will be automatically ignored due to being: transparent, non-solid, or liquids, but new blocks can be added here if needed
|
.set("")
|
||||||
.setAppearance(EConfigEntryAppearance.ALL)
|
.setAppearance(EConfigEntryAppearance.ALL)
|
||||||
|
.addListener(RenderBlockCacheCsvHandler.INSTANCE)
|
||||||
.comment(""
|
.comment(""
|
||||||
+ "A comma separated list of block resource locations that shouldn't be rendered \n"
|
+ "A comma separated list of block resource locations that shouldn't be rendered \n"
|
||||||
+ "if they are in a 0 sky light underground area. \n"
|
+ "if they are in a 0 sky light underground area. \n"
|
||||||
+ "Air is always included in this list. \n"
|
+ "Air is always included in this list. \n"
|
||||||
+ "Requires a restart to change. \n"
|
+ "\n"
|
||||||
|
+ "Defaults to an empty list since most cave blocks will be automatically ignored due to being: \n"
|
||||||
|
+ "transparent, non-solid, or liquids, but new blocks can be added here if needed.\n"
|
||||||
+ "")
|
+ "")
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
|
public static ConfigEntry<String> waterSubSurfaceBlockReplacementCsv = new ConfigEntry.Builder<String>()
|
||||||
|
.set("minecraft:kelp,minecraft:tall_seagrass,minecraft:seagrass")
|
||||||
|
.setAppearance(EConfigEntryAppearance.ALL)
|
||||||
|
.addListener(RenderBlockCacheCsvHandler.INSTANCE)
|
||||||
|
.comment(""
|
||||||
|
+ "A comma separated list of block resource locations that will be replaced by water \n"
|
||||||
|
+ "if they're visible on the water's surface. \n"
|
||||||
|
+ "")
|
||||||
|
.build();
|
||||||
|
|
||||||
static
|
public static ConfigEntry<String> waterSurfaceBlockReplacementCsv = new ConfigEntry.Builder<String>()
|
||||||
{
|
.set("minecraft:lily_pad")
|
||||||
ignoredRenderBlockCsv.addListener(new ConfigChangeListener<String>(ignoredRenderBlockCsv,
|
.setAppearance(EConfigEntryAppearance.ALL)
|
||||||
(blockCsv) ->
|
.addListener(RenderBlockCacheCsvHandler.INSTANCE)
|
||||||
{
|
.comment(""
|
||||||
IWrapperFactory wrapperFactory = SingletonInjector.INSTANCE.get(IWrapperFactory.class);
|
+ "A comma separated list of block resource locations that will be removed \n"
|
||||||
if (wrapperFactory != null)
|
+ "when on top of water. \n"
|
||||||
{
|
+ "")
|
||||||
wrapperFactory.resetRendererIgnoredBlocksSet();
|
.build();
|
||||||
DhApi.Delayed.renderProxy.clearRenderDataCache();
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
|
|
||||||
ignoredRenderCaveBlockCsv.addListener(new ConfigChangeListener<String>(ignoredRenderCaveBlockCsv,
|
|
||||||
(blockCsv) ->
|
|
||||||
{
|
|
||||||
IWrapperFactory wrapperFactory = SingletonInjector.INSTANCE.get(IWrapperFactory.class);
|
|
||||||
if (wrapperFactory != null)
|
|
||||||
{
|
|
||||||
wrapperFactory.resetRendererIgnoredCaveBlocks();
|
|
||||||
DhApi.Delayed.renderProxy.clearRenderDataCache();
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class Experimental
|
public static class Experimental
|
||||||
@@ -835,7 +815,6 @@ public class Config
|
|||||||
|
|
||||||
public static ConfigEntry<EDhApiRenderApi> renderingApi = new ConfigEntry.Builder<EDhApiRenderApi>()
|
public static ConfigEntry<EDhApiRenderApi> renderingApi = new ConfigEntry.Builder<EDhApiRenderApi>()
|
||||||
.set(EDhApiRenderApi.AUTO)
|
.set(EDhApiRenderApi.AUTO)
|
||||||
.setAppearance(EConfigEntryAppearance.ONLY_IN_FILE) // very experimental option and only supported
|
|
||||||
.comment(""
|
.comment(""
|
||||||
+ "Requires a restart to change. \n"
|
+ "Requires a restart to change. \n"
|
||||||
+ " \n"
|
+ " \n"
|
||||||
@@ -908,11 +887,11 @@ public class Config
|
|||||||
+ "What renderer is active? \n"
|
+ "What renderer is active? \n"
|
||||||
+ "\n"
|
+ "\n"
|
||||||
+ EDhApiRendererMode.DEFAULT + ": Default lod renderer \n"
|
+ EDhApiRendererMode.DEFAULT + ": Default lod renderer \n"
|
||||||
+ EDhApiRendererMode.DEBUG + ": Debug testing renderer \n"
|
+ EDhApiRendererMode.DEBUG_TRIANGLE + ": Debug testing renderer \n"
|
||||||
+ EDhApiRendererMode.DISABLED + ": Disable rendering")
|
+ EDhApiRendererMode.DISABLED + ": Disable rendering")
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
public static ConfigEntry<EDhApiDebugRendering> debugRendering = new ConfigEntry.Builder<EDhApiDebugRendering>()
|
public static ConfigEntry<EDhApiDebugRendering> debugRenderingColors = new ConfigEntry.Builder<EDhApiDebugRendering>()
|
||||||
.set(EDhApiDebugRendering.OFF)
|
.set(EDhApiDebugRendering.OFF)
|
||||||
.comment(""
|
.comment(""
|
||||||
+ "Should specialized colors/rendering modes be used? \n"
|
+ "Should specialized colors/rendering modes be used? \n"
|
||||||
@@ -925,6 +904,13 @@ public class Config
|
|||||||
.addListener(ReloadLodsConfigEventHandler.DELAYED_INSTANCE)
|
.addListener(ReloadLodsConfigEventHandler.DELAYED_INSTANCE)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
|
public static ConfigEntry<Boolean> enableWhiteWorld = new ConfigEntry.Builder<Boolean>()
|
||||||
|
.set(false)
|
||||||
|
.comment(""
|
||||||
|
+ "Stops vertex colors from being passed. \n"
|
||||||
|
+ "Useful for debugging shaders")
|
||||||
|
.build();
|
||||||
|
|
||||||
public static ConfigEntry<Boolean> lodOnlyMode = new ConfigEntry.Builder<Boolean>()
|
public static ConfigEntry<Boolean> lodOnlyMode = new ConfigEntry.Builder<Boolean>()
|
||||||
.set(false)
|
.set(false)
|
||||||
.comment(""
|
.comment(""
|
||||||
@@ -952,13 +938,6 @@ public class Config
|
|||||||
+ "")
|
+ "")
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
public static ConfigEntry<Boolean> enableWhiteWorld = new ConfigEntry.Builder<Boolean>()
|
|
||||||
.set(false)
|
|
||||||
.comment(""
|
|
||||||
+ "Stops vertex colors from being passed. \n"
|
|
||||||
+ "Useful for debugging shaders")
|
|
||||||
.build();
|
|
||||||
|
|
||||||
public static ConfigEntry<Boolean> showOverlappingQuadErrors = new ConfigEntry.Builder<Boolean>()
|
public static ConfigEntry<Boolean> showOverlappingQuadErrors = new ConfigEntry.Builder<Boolean>()
|
||||||
.set(false)
|
.set(false)
|
||||||
.comment(""
|
.comment(""
|
||||||
@@ -1019,15 +998,6 @@ public class Config
|
|||||||
.set(false)
|
.set(false)
|
||||||
.comment("Render LOD section status?")
|
.comment("Render LOD section status?")
|
||||||
.build();
|
.build();
|
||||||
public static ConfigEntry<Boolean> showRenderSectionToggling = new ConfigEntry.Builder<Boolean>()
|
|
||||||
.set(false)
|
|
||||||
.comment("" +
|
|
||||||
"A white box will be drawn when an LOD starts rendering \n" +
|
|
||||||
"and a purple box when an LOD stops rendering. \n" +
|
|
||||||
"\n" +
|
|
||||||
"This can be used to debug Quad Tree holes.\n" +
|
|
||||||
"")
|
|
||||||
.build();
|
|
||||||
|
|
||||||
public static ConfigEntry<Boolean> showQuadTreeRenderStatus = new ConfigEntry.Builder<Boolean>()
|
public static ConfigEntry<Boolean> showQuadTreeRenderStatus = new ConfigEntry.Builder<Boolean>()
|
||||||
.set(false)
|
.set(false)
|
||||||
@@ -1080,13 +1050,6 @@ public class Config
|
|||||||
+ "")
|
+ "")
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
public static ConfigEntry<EDhApiGpuUploadMethod> glUploadMode = new ConfigEntry.Builder<EDhApiGpuUploadMethod>()
|
|
||||||
.set(EDhApiGpuUploadMethod.AUTO)
|
|
||||||
.comment(""
|
|
||||||
+ "\n"
|
|
||||||
+ "")
|
|
||||||
.build();
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class ColumnBuilderDebugging
|
public static class ColumnBuilderDebugging
|
||||||
@@ -1153,6 +1116,11 @@ public class Config
|
|||||||
.comment("Shows info about each thread pool.")
|
.comment("Shows info about each thread pool.")
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
|
public static ConfigEntry<Boolean> showRenderThreadTasks = new ConfigEntry.Builder<Boolean>()
|
||||||
|
.set(false)
|
||||||
|
.comment("Shows info about the render thread tasks.")
|
||||||
|
.build();
|
||||||
|
|
||||||
public static ConfigEntry<Boolean> showCombinedObjectPools = new ConfigEntry.Builder<Boolean>()
|
public static ConfigEntry<Boolean> showCombinedObjectPools = new ConfigEntry.Builder<Boolean>()
|
||||||
.set(false)
|
.set(false)
|
||||||
.comment("Shows the combined memory use and array counts for all DH pooled objects.")
|
.comment("Shows the combined memory use and array counts for all DH pooled objects.")
|
||||||
@@ -1164,7 +1132,7 @@ public class Config
|
|||||||
|
|
||||||
public static ConfigEntry<Boolean> showQueuedChunkUpdateCount = new ConfigEntry.Builder<Boolean>()
|
public static ConfigEntry<Boolean> showQueuedChunkUpdateCount = new ConfigEntry.Builder<Boolean>()
|
||||||
.set(true)
|
.set(true)
|
||||||
.comment("Shows how many chunks are queud for processing and the max count that can be queued.")
|
.comment("Shows how many chunks are queued for processing and the max count that can be queued.")
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
public static ConfigEntry<Boolean> showLevelStatus = new ConfigEntry.Builder<Boolean>()
|
public static ConfigEntry<Boolean> showLevelStatus = new ConfigEntry.Builder<Boolean>()
|
||||||
|
|||||||
+69
@@ -0,0 +1,69 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of the Distant Horizons mod
|
||||||
|
* licensed under the GNU LGPL v3 License.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2020 James Seibel
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
* the Free Software Foundation, version 3.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.seibel.distanthorizons.core.config.eventHandlers;
|
||||||
|
|
||||||
|
import com.seibel.distanthorizons.api.DhApi;
|
||||||
|
import com.seibel.distanthorizons.api.enums.config.EDhApiMcRenderingFadeMode;
|
||||||
|
import com.seibel.distanthorizons.api.methods.events.abstractEvents.DhApiBeforeRenderEvent;
|
||||||
|
import com.seibel.distanthorizons.api.methods.events.sharedParameterObjects.DhApiCancelableEventParam;
|
||||||
|
import com.seibel.distanthorizons.api.methods.events.sharedParameterObjects.DhApiRenderParam;
|
||||||
|
import com.seibel.distanthorizons.core.config.Config;
|
||||||
|
import com.seibel.distanthorizons.core.config.listeners.IConfigListener;
|
||||||
|
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
|
||||||
|
import com.seibel.distanthorizons.core.logging.DhLogger;
|
||||||
|
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
|
||||||
|
import com.seibel.distanthorizons.core.wrapperInterfaces.IWrapperFactory;
|
||||||
|
import com.seibel.distanthorizons.coreapi.util.StringUtil;
|
||||||
|
|
||||||
|
public class RenderBlockCacheCsvHandler implements IConfigListener
|
||||||
|
{
|
||||||
|
private static final DhLogger LOGGER = new DhLoggerBuilder().build();
|
||||||
|
|
||||||
|
public static RenderBlockCacheCsvHandler INSTANCE = new RenderBlockCacheCsvHandler();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//=============//
|
||||||
|
// constructor //
|
||||||
|
//=============//
|
||||||
|
|
||||||
|
/** private since we only ever need one handler at a time */
|
||||||
|
private RenderBlockCacheCsvHandler() { }
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//=================//
|
||||||
|
// config handling //
|
||||||
|
//=================//
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onConfigValueSet()
|
||||||
|
{
|
||||||
|
IWrapperFactory wrapperFactory = SingletonInjector.INSTANCE.get(IWrapperFactory.class);
|
||||||
|
if (wrapperFactory != null)
|
||||||
|
{
|
||||||
|
wrapperFactory.resetCachedIgnoredBlocksSets();
|
||||||
|
DhApi.Delayed.renderProxy.clearRenderDataCache();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
+1
-1
@@ -78,7 +78,7 @@ public class RenderQualityPresetConfigEventHandler extends AbstractPresetConfigE
|
|||||||
this.put(EDhApiQualityPreset.HIGH, EDhApiTransparency.COMPLETE);
|
this.put(EDhApiQualityPreset.HIGH, EDhApiTransparency.COMPLETE);
|
||||||
this.put(EDhApiQualityPreset.EXTREME, EDhApiTransparency.COMPLETE);
|
this.put(EDhApiQualityPreset.EXTREME, EDhApiTransparency.COMPLETE);
|
||||||
}});
|
}});
|
||||||
private final ConfigPresetOptions<EDhApiQualityPreset, Boolean> ssaoEnabled = new ConfigPresetOptions<>(Config.Client.Advanced.Graphics.Ssao.enableSsao,
|
private final ConfigPresetOptions<EDhApiQualityPreset, Boolean> ssaoEnabled = new ConfigPresetOptions<>(Config.Client.Advanced.Graphics.enableSsao,
|
||||||
new HashMap<EDhApiQualityPreset, Boolean>()
|
new HashMap<EDhApiQualityPreset, Boolean>()
|
||||||
{{
|
{{
|
||||||
this.put(EDhApiQualityPreset.MINIMUM, false);
|
this.put(EDhApiQualityPreset.MINIMUM, false);
|
||||||
|
|||||||
+2
-2
@@ -64,7 +64,7 @@ public class ColumnRenderBufferBuilder
|
|||||||
{
|
{
|
||||||
DhBlockPos minBlockPos = new DhBlockPos(DhSectionPos.getMinCornerBlockX(pos), clientLevel.getLevelWrapper().getMinHeight(), DhSectionPos.getMinCornerBlockZ(pos));
|
DhBlockPos minBlockPos = new DhBlockPos(DhSectionPos.getMinCornerBlockX(pos), clientLevel.getLevelWrapper().getMinHeight(), DhSectionPos.getMinCornerBlockZ(pos));
|
||||||
LodBufferContainer bufferContainer = new LodBufferContainer(pos, minBlockPos);
|
LodBufferContainer bufferContainer = new LodBufferContainer(pos, minBlockPos);
|
||||||
CompletableFuture<LodBufferContainer> uploadFuture = bufferContainer.makeAndUploadBuffersAsync(quadBuilder);
|
CompletableFuture<LodBufferContainer> uploadFuture = bufferContainer.tryMakeAndUploadBuffersAsync(quadBuilder);
|
||||||
uploadFuture.whenComplete((uploadedBuffer, exception) ->
|
uploadFuture.whenComplete((uploadedBuffer, exception) ->
|
||||||
{
|
{
|
||||||
// clean up if not uploaded
|
// clean up if not uploaded
|
||||||
@@ -328,7 +328,7 @@ public class ColumnRenderBufferBuilder
|
|||||||
|
|
||||||
int color;
|
int color;
|
||||||
boolean fullBright = false;
|
boolean fullBright = false;
|
||||||
EDhApiDebugRendering debugging = Config.Client.Advanced.Debugging.debugRendering.get();
|
EDhApiDebugRendering debugging = Config.Client.Advanced.Debugging.debugRenderingColors.get();
|
||||||
switch (debugging)
|
switch (debugging)
|
||||||
{
|
{
|
||||||
case OFF:
|
case OFF:
|
||||||
|
|||||||
+92
@@ -0,0 +1,92 @@
|
|||||||
|
package com.seibel.distanthorizons.core.dataObjects.render.bufferBuilding;
|
||||||
|
|
||||||
|
import org.lwjgl.system.MemoryUtil;
|
||||||
|
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
import java.nio.ByteOrder;
|
||||||
|
|
||||||
|
public class IndexBufferBuilder
|
||||||
|
{
|
||||||
|
|
||||||
|
|
||||||
|
//==========//
|
||||||
|
// building //
|
||||||
|
//==========//
|
||||||
|
//region
|
||||||
|
|
||||||
|
/** Buffer should be freed by {@link MemoryUtil#memFree} */
|
||||||
|
public static ByteBuffer createBuffer(int quadCount)
|
||||||
|
{
|
||||||
|
int indexCount = quadCount * 6; // 2 triangles per quad
|
||||||
|
ByteBuffer buffer = MemoryUtil.memAlloc(indexCount * Integer.BYTES);
|
||||||
|
buffer.order(ByteOrder.nativeOrder());
|
||||||
|
buildBufferInt(quadCount, buffer);
|
||||||
|
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
private static void buildBufferByte(int quadCount, ByteBuffer buffer)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < quadCount; i++)
|
||||||
|
{
|
||||||
|
int vIndex = i * 4;
|
||||||
|
// First triangle
|
||||||
|
buffer.put((byte) (vIndex));
|
||||||
|
buffer.put((byte) (vIndex + 1));
|
||||||
|
buffer.put((byte) (vIndex + 2));
|
||||||
|
// Second triangle
|
||||||
|
buffer.put((byte) (vIndex + 2));
|
||||||
|
buffer.put((byte) (vIndex + 3));
|
||||||
|
buffer.put((byte) (vIndex));
|
||||||
|
}
|
||||||
|
if (buffer.hasRemaining())
|
||||||
|
{
|
||||||
|
throw new IllegalStateException("QuadElementBuffer is not full somehow after building");
|
||||||
|
}
|
||||||
|
buffer.rewind();
|
||||||
|
}
|
||||||
|
private static void buildBufferShort(int quadCount, ByteBuffer buffer)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < quadCount; i++)
|
||||||
|
{
|
||||||
|
int vIndex = i * 4;
|
||||||
|
// First triangle
|
||||||
|
buffer.putShort((short) (vIndex));
|
||||||
|
buffer.putShort((short) (vIndex + 1));
|
||||||
|
buffer.putShort((short) (vIndex + 2));
|
||||||
|
// Second triangle
|
||||||
|
buffer.putShort((short) (vIndex + 2));
|
||||||
|
buffer.putShort((short) (vIndex + 3));
|
||||||
|
buffer.putShort((short) (vIndex));
|
||||||
|
}
|
||||||
|
if (buffer.hasRemaining())
|
||||||
|
{
|
||||||
|
throw new IllegalStateException("QuadElementBuffer is not full somehow after building");
|
||||||
|
}
|
||||||
|
buffer.rewind();
|
||||||
|
}
|
||||||
|
private static void buildBufferInt(int quadCount, ByteBuffer buffer)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < quadCount; i++)
|
||||||
|
{
|
||||||
|
int vIndex = i * 4;
|
||||||
|
// First triangle
|
||||||
|
buffer.putInt(vIndex);
|
||||||
|
buffer.putInt(vIndex + 1);
|
||||||
|
buffer.putInt(vIndex + 2);
|
||||||
|
// Second triangle
|
||||||
|
buffer.putInt(vIndex + 2);
|
||||||
|
buffer.putInt(vIndex + 3);
|
||||||
|
buffer.putInt(vIndex);
|
||||||
|
}
|
||||||
|
if (buffer.hasRemaining())
|
||||||
|
{
|
||||||
|
throw new IllegalStateException("QuadElementBuffer is not full somehow after building");
|
||||||
|
}
|
||||||
|
buffer.rewind();
|
||||||
|
}
|
||||||
|
|
||||||
|
//endregion
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
+314
-75
@@ -25,11 +25,13 @@ 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.LodUtil;
|
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.objects.ILodContainerUniformBufferWrapper;
|
import com.seibel.distanthorizons.core.wrapperInterfaces.render.objects.ILodContainerUniformBufferWrapper;
|
||||||
import com.seibel.distanthorizons.core.wrapperInterfaces.render.renderPass.IDhTerrainRenderer;
|
|
||||||
import com.seibel.distanthorizons.core.wrapperInterfaces.render.objects.IVertexBufferWrapper;
|
import com.seibel.distanthorizons.core.wrapperInterfaces.render.objects.IVertexBufferWrapper;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
import org.lwjgl.system.MemoryUtil;
|
import org.lwjgl.system.MemoryUtil;
|
||||||
|
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
@@ -47,6 +49,7 @@ public class LodBufferContainer implements AutoCloseable
|
|||||||
private static final DhLogger LOGGER = new DhLoggerBuilder().build();
|
private static final DhLogger LOGGER = new DhLoggerBuilder().build();
|
||||||
|
|
||||||
private static final IWrapperFactory WRAPPER_FACTORY = SingletonInjector.INSTANCE.get(IWrapperFactory.class);
|
private static final IWrapperFactory WRAPPER_FACTORY = SingletonInjector.INSTANCE.get(IWrapperFactory.class);
|
||||||
|
private static final AbstractDhRenderApiDefinition RENDER_DEF = SingletonInjector.INSTANCE.get(AbstractDhRenderApiDefinition.class);
|
||||||
|
|
||||||
|
|
||||||
/** the position closest to minimum X/Z infinity and the level's lowest Y */
|
/** the position closest to minimum X/Z infinity and the level's lowest Y */
|
||||||
@@ -55,8 +58,8 @@ public class LodBufferContainer implements AutoCloseable
|
|||||||
|
|
||||||
public boolean buffersUploaded = false;
|
public boolean buffersUploaded = false;
|
||||||
|
|
||||||
public IVertexBufferWrapper[] vbos;
|
public IVertexBufferWrapper[] vboOpaqueWrappers;
|
||||||
public IVertexBufferWrapper[] vbosTransparent;
|
public IVertexBufferWrapper[] vboTransparentWrappers;
|
||||||
|
|
||||||
public ILodContainerUniformBufferWrapper uniformContainer = WRAPPER_FACTORY.createLodContainerUniformWrapper();
|
public ILodContainerUniformBufferWrapper uniformContainer = WRAPPER_FACTORY.createLodContainerUniformWrapper();
|
||||||
|
|
||||||
@@ -73,8 +76,8 @@ public class LodBufferContainer implements AutoCloseable
|
|||||||
{
|
{
|
||||||
this.pos = pos;
|
this.pos = pos;
|
||||||
this.minCornerBlockPos = minCornerBlockPos;
|
this.minCornerBlockPos = minCornerBlockPos;
|
||||||
this.vbos = new IVertexBufferWrapper[0];
|
this.vboOpaqueWrappers = new IVertexBufferWrapper[0];
|
||||||
this.vbosTransparent = new IVertexBufferWrapper[0];
|
this.vboTransparentWrappers = new IVertexBufferWrapper[0];
|
||||||
|
|
||||||
this.uniformContainer.createUniformData(this);
|
this.uniformContainer.createUniformData(this);
|
||||||
}
|
}
|
||||||
@@ -89,8 +92,13 @@ 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> makeAndUploadBuffersAsync(LodQuadBuilder builder)
|
public synchronized CompletableFuture<LodBufferContainer> tryMakeAndUploadBuffersAsync(LodQuadBuilder builder)
|
||||||
{
|
{
|
||||||
|
//================//
|
||||||
|
// handle futures //
|
||||||
|
//================//
|
||||||
|
//region
|
||||||
|
|
||||||
// separate variable to prevent race condition when checking null
|
// separate variable to prevent race condition when checking null
|
||||||
CompletableFuture<LodBufferContainer> oldFuture = this.uploadFutureRef.get();
|
CompletableFuture<LodBufferContainer> oldFuture = this.uploadFutureRef.get();
|
||||||
if (oldFuture != null)
|
if (oldFuture != null)
|
||||||
@@ -118,65 +126,149 @@ public class LodBufferContainer implements AutoCloseable
|
|||||||
return oldFuture;
|
return oldFuture;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//endregion
|
||||||
|
|
||||||
|
|
||||||
// make the buffers
|
|
||||||
|
//================//
|
||||||
|
// create buffers //
|
||||||
|
//================//
|
||||||
|
//region
|
||||||
|
|
||||||
ArrayList<ByteBuffer> opaqueBuffers = builder.makeOpaqueVertexBuffers();
|
ArrayList<ByteBuffer> opaqueBuffers = builder.makeOpaqueVertexBuffers();
|
||||||
ArrayList<ByteBuffer> transparentBuffers = builder.makeTransparentVertexBuffers();
|
ArrayList<ByteBuffer> transparentBuffers = builder.makeTransparentVertexBuffers();
|
||||||
|
|
||||||
this.vbos = resizeBufferArray(this.vbos, opaqueBuffers.size());
|
this.vboOpaqueWrappers = resizeWrapperArray(this.vboOpaqueWrappers, opaqueBuffers.size());
|
||||||
this.vbosTransparent = resizeBufferArray(this.vbosTransparent, transparentBuffers.size());
|
this.vboTransparentWrappers = resizeWrapperArray(this.vboTransparentWrappers, transparentBuffers.size());
|
||||||
|
|
||||||
|
// mac requires separate IBO objects for each VBO when using OpenGL,
|
||||||
|
// all other OS's can share a single IBO for quicker loading times
|
||||||
|
boolean useSingleIbo = RENDER_DEF.useSingleIbo();
|
||||||
|
@Nullable ArrayList<ByteBuffer> opaqueIndexBuffers = useSingleIbo ? null : this.createIndexBuffers(opaqueBuffers);
|
||||||
|
@Nullable ArrayList<ByteBuffer> transparentIndexBuffers = useSingleIbo ? null : this.createIndexBuffers(transparentBuffers);
|
||||||
|
|
||||||
|
//endregion
|
||||||
|
|
||||||
|
|
||||||
// upload on MC's render thread
|
|
||||||
RenderThreadTaskHandler.INSTANCE.queueRunningOnRenderThread(() ->
|
//================//
|
||||||
|
// upload buffers //
|
||||||
|
//================//
|
||||||
|
//region
|
||||||
|
|
||||||
|
try
|
||||||
{
|
{
|
||||||
try
|
//=============//
|
||||||
|
// create VBOs //
|
||||||
|
//=============//
|
||||||
|
|
||||||
|
CompletableFuture<Void> createOpaqueFuture = createBufferWrappersAsync(future, this.vboOpaqueWrappers, opaqueBuffers);
|
||||||
|
CompletableFuture<Void> createTransparentFuture = createBufferWrappersAsync(future, this.vboTransparentWrappers, transparentBuffers);
|
||||||
|
|
||||||
|
CompletableFuture<Void> createFuture = CompletableFuture.allOf(createOpaqueFuture, createTransparentFuture);
|
||||||
|
createFuture.exceptionally((Throwable e) ->
|
||||||
{
|
{
|
||||||
// skip this event if requested
|
// create VBOs failed //
|
||||||
if (Thread.interrupted()
|
|
||||||
|| future.isCancelled())
|
if (!ExceptionUtil.isShutdownException(e))
|
||||||
{
|
{
|
||||||
throw new InterruptedException();
|
LOGGER.error("Unexpected issue creating buffer [" + this.minCornerBlockPos + "], error: [" + e.getMessage() + "].", e);
|
||||||
}
|
}
|
||||||
|
|
||||||
// upload on the render thread
|
|
||||||
uploadBuffers(this.vbos, opaqueBuffers);
|
|
||||||
uploadBuffers(this.vbosTransparent, transparentBuffers);
|
|
||||||
this.buffersUploaded = true;
|
|
||||||
|
|
||||||
// success
|
|
||||||
future.complete(this);
|
|
||||||
}
|
|
||||||
catch (InterruptedException ignore)
|
|
||||||
{
|
|
||||||
future.complete(this);
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
LOGGER.error("Unexpected issue uploading buffer ["+this.minCornerBlockPos +"], error: ["+e.getMessage()+"].", e);
|
|
||||||
|
|
||||||
future.completeExceptionally(e);
|
future.completeExceptionally(e);
|
||||||
}
|
return null;
|
||||||
finally
|
});
|
||||||
|
createFuture.thenRun(() ->
|
||||||
{
|
{
|
||||||
// all the buffers must be manually freed to prevent memory leaks
|
//=============//
|
||||||
|
// upload VBOs //
|
||||||
|
//=============//
|
||||||
|
|
||||||
for (ByteBuffer buffer : opaqueBuffers)
|
CompletableFuture<Void> opaqueFuture = uploadBuffersAsync(future, this.vboOpaqueWrappers, opaqueBuffers, opaqueIndexBuffers);
|
||||||
{
|
CompletableFuture<Void> transparentFuture = uploadBuffersAsync(future, this.vboTransparentWrappers, transparentBuffers, transparentIndexBuffers);
|
||||||
MemoryUtil.memFree(buffer);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (ByteBuffer buffer : transparentBuffers)
|
CompletableFuture<Void> uploadFuture = CompletableFuture.allOf(opaqueFuture, transparentFuture);
|
||||||
|
uploadFuture.exceptionally((Throwable e) ->
|
||||||
{
|
{
|
||||||
MemoryUtil.memFree(buffer);
|
// 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)
|
||||||
|
{
|
||||||
|
if (!ExceptionUtil.isShutdownException(e))
|
||||||
|
{
|
||||||
|
LOGGER.error("Unexpected issue prepping buffer uploading [" + this.minCornerBlockPos + "], error: [" + e.getMessage() + "].", e);
|
||||||
}
|
}
|
||||||
|
future.completeExceptionally(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//================//
|
||||||
|
// buffer cleanup //
|
||||||
|
//================//
|
||||||
|
|
||||||
|
future.whenComplete((LodBufferContainer lodBufferContainer, Throwable throwable) ->
|
||||||
|
{
|
||||||
|
// all the buffers must be manually freed to prevent memory leaks
|
||||||
|
|
||||||
|
tryFreeByteBufferList(opaqueBuffers);
|
||||||
|
tryFreeByteBufferList(transparentBuffers);
|
||||||
|
|
||||||
|
tryFreeByteBufferList(opaqueIndexBuffers);
|
||||||
|
tryFreeByteBufferList(transparentIndexBuffers);
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
//endregion
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
return future;
|
return future;
|
||||||
}
|
}
|
||||||
private static IVertexBufferWrapper[] resizeBufferArray(IVertexBufferWrapper[] vbos, int newSize)
|
private static void tryFreeByteBufferList(@Nullable ArrayList<ByteBuffer> list)
|
||||||
|
{
|
||||||
|
if (list != null)
|
||||||
|
{
|
||||||
|
for (ByteBuffer buffer : list)
|
||||||
|
{
|
||||||
|
MemoryUtil.memFree(buffer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private ArrayList<ByteBuffer> createIndexBuffers(ArrayList<ByteBuffer> vertexBuffers)
|
||||||
|
{
|
||||||
|
ArrayList<ByteBuffer> indexBuffers = new ArrayList<>();
|
||||||
|
|
||||||
|
for (int i = 0; i < vertexBuffers.size(); i++)
|
||||||
|
{
|
||||||
|
ByteBuffer buffer = vertexBuffers.get(i);
|
||||||
|
int size = buffer.limit() - buffer.position();
|
||||||
|
int vertexCount = size / LodQuadBuilder.BYTES_PER_VERTEX;
|
||||||
|
|
||||||
|
ByteBuffer indexBuffer = IndexBufferBuilder.createBuffer(vertexCount);
|
||||||
|
indexBuffers.add(indexBuffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
return indexBuffers;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static IVertexBufferWrapper[] resizeWrapperArray(IVertexBufferWrapper[] vbos, int newSize)
|
||||||
{
|
{
|
||||||
if (vbos.length == newSize)
|
if (vbos.length == newSize)
|
||||||
{
|
{
|
||||||
@@ -197,46 +289,188 @@ public class LodBufferContainer implements AutoCloseable
|
|||||||
}
|
}
|
||||||
return newVbos;
|
return newVbos;
|
||||||
}
|
}
|
||||||
private static void uploadBuffers(IVertexBufferWrapper[] vbos, ArrayList<ByteBuffer> byteBuffers) throws InterruptedException
|
|
||||||
|
private static CompletableFuture<Void> createBufferWrappersAsync(
|
||||||
|
CompletableFuture<LodBufferContainer> parentFuture,
|
||||||
|
IVertexBufferWrapper[] vboWrappers, ArrayList<ByteBuffer> vertexBuffers)
|
||||||
{
|
{
|
||||||
int vboIndex = 0;
|
ArrayList<CompletableFuture<Void>> createVboFutureList = new ArrayList<>();
|
||||||
for (int i = 0; i < byteBuffers.size(); i++)
|
for (int i = 0; i < vertexBuffers.size(); i++)
|
||||||
{
|
{
|
||||||
if (vboIndex >= vbos.length)
|
if (i >= vboWrappers.length)
|
||||||
|
{
|
||||||
|
throw new RuntimeException("Too many vertex buffers!!");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (vboWrappers[i] == null)
|
||||||
|
{
|
||||||
|
final int finalVboIndex = i;
|
||||||
|
|
||||||
|
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. */
|
||||||
|
private static CompletableFuture<Void> uploadBuffersAsync(
|
||||||
|
CompletableFuture<LodBufferContainer> parentFuture,
|
||||||
|
IVertexBufferWrapper[] vboWrappers,
|
||||||
|
ArrayList<ByteBuffer> vertexBuffers, @Nullable ArrayList<ByteBuffer> indexBuffers
|
||||||
|
)
|
||||||
|
{
|
||||||
|
ArrayList<CompletableFuture<Void>> uploadFutureList = new ArrayList<>();
|
||||||
|
int vboIndex = 0;
|
||||||
|
for (int i = 0; i < vertexBuffers.size(); i++)
|
||||||
|
{
|
||||||
|
if (vboIndex >= vboWrappers.length)
|
||||||
{
|
{
|
||||||
throw new RuntimeException("Too many vertex buffers!!");
|
throw new RuntimeException("Too many vertex buffers!!");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// get or create the VBO
|
|
||||||
if (vbos[vboIndex] == null)
|
|
||||||
{
|
|
||||||
vbos[vboIndex] = SingletonInjector.INSTANCE.get(IWrapperFactory.class).createVboWrapper("distantHorizons:McLodRenderer");
|
|
||||||
}
|
|
||||||
IVertexBufferWrapper vbo = vbos[vboIndex];
|
|
||||||
|
|
||||||
ByteBuffer buffer = byteBuffers.get(i);
|
// final variables for use in lambdas //
|
||||||
int size = buffer.limit() - buffer.position();
|
|
||||||
int vertexCount = size / LodQuadBuilder.BYTES_PER_VERTEX;
|
|
||||||
|
|
||||||
try
|
final int finalVboIndex = vboIndex;
|
||||||
|
|
||||||
|
final IVertexBufferWrapper finalVboWrapper = vboWrappers[vboIndex];
|
||||||
|
|
||||||
|
final ByteBuffer finalVertexBuffer = vertexBuffers.get(vboIndex);
|
||||||
|
// index buffers are optional
|
||||||
|
@Nullable final ByteBuffer finalIndexBuffer = (indexBuffers != null) ? indexBuffers.get(vboIndex) : null;
|
||||||
|
|
||||||
|
final int finalVertexCount = vertexByteBufferToVertexCount(finalVertexBuffer);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//===============//
|
||||||
|
// vertex upload //
|
||||||
|
//===============//
|
||||||
|
//region
|
||||||
|
|
||||||
|
CompletableFuture<Void> vertexUploadFuture = new CompletableFuture<>();
|
||||||
|
uploadFutureList.add(vertexUploadFuture);
|
||||||
|
|
||||||
|
RenderThreadTaskHandler.INSTANCE.queueRunningOnRenderThread("LodBufferContainer VBO Upload", () ->
|
||||||
{
|
{
|
||||||
vbo.upload(buffer, vertexCount);
|
try
|
||||||
}
|
{
|
||||||
catch (Exception e)
|
// skip this event if requested
|
||||||
|
if (Thread.interrupted()
|
||||||
|
|| parentFuture.isCancelled())
|
||||||
|
{
|
||||||
|
throw new InterruptedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
finalVboWrapper.uploadVertexBuffer(finalVertexBuffer, finalVertexCount);
|
||||||
|
vertexUploadFuture.complete(null);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
vboWrappers[finalVboIndex] = null;
|
||||||
|
finalVboWrapper.close();
|
||||||
|
LOGGER.error("Failed to upload buffer. Error: [" + e.getMessage() + "].", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
vertexUploadFuture.completeExceptionally(e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
//endregion
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//==============//
|
||||||
|
// index upload //
|
||||||
|
//==============//
|
||||||
|
//region
|
||||||
|
|
||||||
|
if (finalIndexBuffer != null)
|
||||||
{
|
{
|
||||||
vbos[vboIndex] = null;
|
CompletableFuture<Void> indexUploadFuture = new CompletableFuture<>();
|
||||||
vbo.close();
|
uploadFutureList.add(indexUploadFuture);
|
||||||
LOGGER.error("Failed to upload buffer. Error: ["+e.getMessage()+"].", e);
|
|
||||||
|
RenderThreadTaskHandler.INSTANCE.queueRunningOnRenderThread("LodBufferContainer IBO Upload", () ->
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// skip this event if requested
|
||||||
|
if (Thread.interrupted()
|
||||||
|
|| parentFuture.isCancelled())
|
||||||
|
{
|
||||||
|
throw new InterruptedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
finalVboWrapper.uploadIndexBuffer(finalIndexBuffer, finalVertexCount);
|
||||||
|
indexUploadFuture.complete(null);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
indexUploadFuture.completeExceptionally(e);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
//endregion
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
vboIndex++;
|
vboIndex++;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (vboIndex < vbos.length)
|
if (vboIndex < vboWrappers.length)
|
||||||
{
|
{
|
||||||
throw new RuntimeException("Too few vertex buffers!!");
|
throw new RuntimeException("Too few vertex buffers!!");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// merge futures //
|
||||||
|
|
||||||
|
CompletableFuture<?>[] futureArray = new CompletableFuture[uploadFutureList.size()];
|
||||||
|
for (int i = 0; i < uploadFutureList.size(); i++)
|
||||||
|
{
|
||||||
|
futureArray[i] = uploadFutureList.get(i);
|
||||||
|
}
|
||||||
|
return CompletableFuture.allOf(futureArray);
|
||||||
}
|
}
|
||||||
|
|
||||||
//endregion
|
//endregion
|
||||||
@@ -248,29 +482,34 @@ public class LodBufferContainer implements AutoCloseable
|
|||||||
//================//
|
//================//
|
||||||
//region
|
//region
|
||||||
|
|
||||||
|
private static int vertexByteBufferToVertexCount(ByteBuffer buffer)
|
||||||
|
{
|
||||||
|
int size = buffer.limit() - buffer.position();
|
||||||
|
int vertexCount = size / LodQuadBuilder.BYTES_PER_VERTEX;
|
||||||
|
return vertexCount;
|
||||||
|
}
|
||||||
|
|
||||||
/** can be used when debugging */
|
/** can be used when debugging */
|
||||||
public boolean hasNonNullVbos() { return this.vbos != null || this.vbosTransparent != null; }
|
public boolean hasNonNullVbos() { return this.vboOpaqueWrappers != null || this.vboTransparentWrappers != null; }
|
||||||
|
|
||||||
/** can be used when debugging */
|
/** can be used when debugging */
|
||||||
public int vboBufferCount()
|
public int vboBufferCount()
|
||||||
{
|
{
|
||||||
int count = 0;
|
int count = 0;
|
||||||
|
|
||||||
if (this.vbos != null)
|
if (this.vboOpaqueWrappers != null)
|
||||||
{
|
{
|
||||||
count += this.vbos.length;
|
count += this.vboOpaqueWrappers.length;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.vbosTransparent != null)
|
if (this.vboTransparentWrappers != null)
|
||||||
{
|
{
|
||||||
count += this.vbosTransparent.length;
|
count += this.vboTransparentWrappers.length;
|
||||||
}
|
}
|
||||||
|
|
||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean uploadInProgress() { return this.uploadFutureRef.get() != null; }
|
|
||||||
|
|
||||||
//endregion
|
//endregion
|
||||||
|
|
||||||
|
|
||||||
@@ -291,9 +530,9 @@ public class LodBufferContainer implements AutoCloseable
|
|||||||
{
|
{
|
||||||
this.buffersUploaded = false;
|
this.buffersUploaded = false;
|
||||||
|
|
||||||
RenderThreadTaskHandler.INSTANCE.queueRunningOnRenderThread(() ->
|
RenderThreadTaskHandler.INSTANCE.queueRunningOnRenderThread("LodBufferContainer Close", () ->
|
||||||
{
|
{
|
||||||
for (IVertexBufferWrapper buffer : this.vbos)
|
for (IVertexBufferWrapper buffer : this.vboOpaqueWrappers)
|
||||||
{
|
{
|
||||||
if (buffer != null)
|
if (buffer != null)
|
||||||
{
|
{
|
||||||
@@ -301,7 +540,7 @@ public class LodBufferContainer implements AutoCloseable
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (IVertexBufferWrapper buffer : this.vbosTransparent)
|
for (IVertexBufferWrapper buffer : this.vboTransparentWrappers)
|
||||||
{
|
{
|
||||||
if (buffer != null)
|
if (buffer != null)
|
||||||
{
|
{
|
||||||
|
|||||||
+16
-13
@@ -32,7 +32,6 @@ 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.util.ColorUtil;
|
import com.seibel.distanthorizons.core.util.ColorUtil;
|
||||||
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftRenderWrapper;
|
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftRenderWrapper;
|
||||||
import com.seibel.distanthorizons.core.wrapperInterfaces.render.renderPass.IDhTerrainRenderer;
|
|
||||||
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper;
|
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper;
|
||||||
import org.lwjgl.system.MemoryUtil;
|
import org.lwjgl.system.MemoryUtil;
|
||||||
|
|
||||||
@@ -57,8 +56,9 @@ public class LodQuadBuilder
|
|||||||
private final EDhApiDebugRendering debugRenderingMode;
|
private final EDhApiDebugRendering debugRenderingMode;
|
||||||
private final EDhApiGrassSideRendering grassSideRenderingMode;
|
private final EDhApiGrassSideRendering grassSideRenderingMode;
|
||||||
|
|
||||||
/** the number of bytes for */
|
/** the number of bytes for a single vertex */
|
||||||
public static final int BYTES_PER_VERTEX = 14;
|
public static final int BYTES_PER_VERTEX = 16;
|
||||||
|
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
|
||||||
@@ -131,7 +131,7 @@ public class LodQuadBuilder
|
|||||||
|
|
||||||
this.clientLevelWrapper = clientLevelWrapper;
|
this.clientLevelWrapper = clientLevelWrapper;
|
||||||
|
|
||||||
this.debugRenderingMode = Config.Client.Advanced.Debugging.debugRendering.get();
|
this.debugRenderingMode = Config.Client.Advanced.Debugging.debugRenderingColors.get();
|
||||||
this.grassSideRenderingMode = Config.Client.Advanced.Graphics.Quality.grassSideRendering.get();
|
this.grassSideRenderingMode = Config.Client.Advanced.Graphics.Quality.grassSideRendering.get();
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -308,7 +308,8 @@ public class LodQuadBuilder
|
|||||||
{
|
{
|
||||||
// if this is the first iteration or the buffer is full,
|
// if this is the first iteration or the buffer is full,
|
||||||
// create a new buffer
|
// create a new buffer
|
||||||
if (buffer == null || !buffer.hasRemaining())
|
if (buffer == null
|
||||||
|
|| buffer.remaining() < BYTES_PER_QUAD)
|
||||||
{
|
{
|
||||||
buffer = MemoryUtil.memAlloc(getMaxBufferByteSize());
|
buffer = MemoryUtil.memAlloc(getMaxBufferByteSize());
|
||||||
byteBufferList.add(buffer);
|
byteBufferList.add(buffer);
|
||||||
@@ -498,16 +499,18 @@ public class LodQuadBuilder
|
|||||||
return maxBufferByteSize;
|
return maxBufferByteSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
// number of bytes a single quad takes
|
// 2 MB
|
||||||
int QUADS_BYTE_SIZE = BYTES_PER_VERTEX * 4;
|
// note: this is relatively small (10 MB was the previous max) to reduce stuttering
|
||||||
// how big a single VBO can be in bytes
|
// during the upload process by having smaller upload steps
|
||||||
int MAX_VBO_BYTE_SIZE = 10 * 1024 * 1024; // 10 MB
|
int maxVboByteSize = 2 * 1024 * 1024;
|
||||||
int MAX_QUADS_PER_BUFFER = MAX_VBO_BYTE_SIZE / QUADS_BYTE_SIZE;
|
|
||||||
int FULL_SIZED_BUFFER = MAX_QUADS_PER_BUFFER * QUADS_BYTE_SIZE;
|
|
||||||
|
|
||||||
maxBufferByteSize = FULL_SIZED_BUFFER;
|
int maxQuadsPerBuffer = maxVboByteSize / BYTES_PER_QUAD;
|
||||||
|
// integer truncation to remove decimal component
|
||||||
|
int fullSizedBuffer = maxQuadsPerBuffer * BYTES_PER_QUAD;
|
||||||
|
|
||||||
return FULL_SIZED_BUFFER;
|
maxBufferByteSize = fullSizedBuffer;
|
||||||
|
|
||||||
|
return fullSizedBuffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
///endregion
|
///endregion
|
||||||
|
|||||||
+41
-10
@@ -194,8 +194,12 @@ public class FullDataToRenderDataTransformer
|
|||||||
boolean ignoreNonCollidingBlocks = (Config.Client.Advanced.Graphics.Quality.blocksToIgnore.get() == EDhApiBlocksToAvoid.NON_COLLIDING);
|
boolean ignoreNonCollidingBlocks = (Config.Client.Advanced.Graphics.Quality.blocksToIgnore.get() == EDhApiBlocksToAvoid.NON_COLLIDING);
|
||||||
boolean colorBelowWithAvoidedBlocks = Config.Client.Advanced.Graphics.Quality.tintWithAvoidedBlocks.get();
|
boolean colorBelowWithAvoidedBlocks = Config.Client.Advanced.Graphics.Quality.tintWithAvoidedBlocks.get();
|
||||||
|
|
||||||
ObjectOpenHashSet<IBlockStateWrapper> blockStatesToIgnore = WRAPPER_FACTORY.getRendererIgnoredBlocks(levelWrapper);
|
final ObjectOpenHashSet<IBlockStateWrapper> blockStatesToIgnore = WRAPPER_FACTORY.getRendererIgnoredBlocks(levelWrapper);
|
||||||
ObjectOpenHashSet<IBlockStateWrapper> caveBlockStatesToIgnore = WRAPPER_FACTORY.getRendererIgnoredCaveBlocks(levelWrapper);
|
final ObjectOpenHashSet<IBlockStateWrapper> caveBlockStatesToIgnore = WRAPPER_FACTORY.getRendererIgnoredCaveBlocks(levelWrapper);
|
||||||
|
final ObjectOpenHashSet<IBlockStateWrapper> waterSubsurfaceReplacementBlocks = WRAPPER_FACTORY.getWaterSubsurfaceReplacementBlocks(levelWrapper);
|
||||||
|
final ObjectOpenHashSet<IBlockStateWrapper> waterSurfaceReplacementBlocks = WRAPPER_FACTORY.getWaterSurfaceReplacementBlocks(levelWrapper);
|
||||||
|
final IBlockStateWrapper water = WRAPPER_FACTORY.getWaterBlockStateWrapper(levelWrapper);
|
||||||
|
|
||||||
|
|
||||||
// build snow block cache if needed
|
// build snow block cache if needed
|
||||||
if (snowLayerBlockStates == null)
|
if (snowLayerBlockStates == null)
|
||||||
@@ -223,6 +227,7 @@ public class FullDataToRenderDataTransformer
|
|||||||
int colorToApplyToNextBlock = -1;
|
int colorToApplyToNextBlock = -1;
|
||||||
int lastColor = 0;
|
int lastColor = 0;
|
||||||
int lastBottom = -10_000;
|
int lastBottom = -10_000;
|
||||||
|
IBlockStateWrapper lastBlock = null;
|
||||||
|
|
||||||
int skylightToApplyToNextBlock = -1;
|
int skylightToApplyToNextBlock = -1;
|
||||||
int blocklightToApplyToNextBlock = -1;
|
int blocklightToApplyToNextBlock = -1;
|
||||||
@@ -283,6 +288,12 @@ public class FullDataToRenderDataTransformer
|
|||||||
// cave culling check //
|
// cave culling check //
|
||||||
//====================//
|
//====================//
|
||||||
|
|
||||||
|
if (waterSubsurfaceReplacementBlocks.contains(block)
|
||||||
|
&& (lastBlock == null || lastBlock.isAir()))
|
||||||
|
{
|
||||||
|
block = water;
|
||||||
|
}
|
||||||
|
|
||||||
boolean ignoreBlock = blockStatesToIgnore.contains(block);
|
boolean ignoreBlock = blockStatesToIgnore.contains(block);
|
||||||
boolean caveBlock = caveBlockStatesToIgnore.contains(block);
|
boolean caveBlock = caveBlockStatesToIgnore.contains(block);
|
||||||
if (caveBlock
|
if (caveBlock
|
||||||
@@ -328,6 +339,9 @@ public class FullDataToRenderDataTransformer
|
|||||||
else if (ignoreBlock)
|
else if (ignoreBlock)
|
||||||
{
|
{
|
||||||
// this is an ignored block, but shouldn't be merged like a cave block
|
// this is an ignored block, but shouldn't be merged like a cave block
|
||||||
|
|
||||||
|
// applying this sky light to the next block should prevent black spots for opaque covering blocks
|
||||||
|
skylightToApplyToNextBlock = skyLight;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -343,21 +357,37 @@ public class FullDataToRenderDataTransformer
|
|||||||
&& !block.isLiquid()
|
&& !block.isLiquid()
|
||||||
&& block.getOpacity() != LodUtil.BLOCK_FULLY_OPAQUE;
|
&& block.getOpacity() != LodUtil.BLOCK_FULLY_OPAQUE;
|
||||||
|
|
||||||
// merge snow into the block below it
|
// handle height reduction
|
||||||
if (snowLayerBlockStates.contains(block))
|
boolean isSnowLayer = snowLayerBlockStates.contains(block);
|
||||||
|
boolean isWaterSurfaceReplacement = waterSurfaceReplacementBlocks.contains(block);
|
||||||
|
if (isSnowLayer || isWaterSurfaceReplacement)
|
||||||
{
|
{
|
||||||
// sometimes a snow datapoint will be multiple blocks tall,
|
if (isWaterSurfaceReplacement)
|
||||||
|
{
|
||||||
|
// replace the block with water
|
||||||
|
block = WRAPPER_FACTORY.getWaterBlockStateWrapper(levelWrapper);
|
||||||
|
}
|
||||||
|
|
||||||
|
// sometimes a datapoint will be multiple blocks tall,
|
||||||
// in that case we just want to drop the top by 1
|
// in that case we just want to drop the top by 1
|
||||||
blockHeight -= 1;
|
blockHeight -= 1;
|
||||||
if (blockHeight == 0)
|
if (blockHeight == 0)
|
||||||
{
|
{
|
||||||
// this snow block was entirely removed, just color the block below it
|
// this block was entirely removed, just color the block below it
|
||||||
ignoreNonSolidBlock = true;
|
ignoreNonSolidBlock = true;
|
||||||
|
|
||||||
// snow is a special case where it should always tint the block
|
|
||||||
// below it, if not done grass will appear as gray
|
if (isSnowLayer)
|
||||||
int snowColor = levelWrapper.getBlockColor(mutableBlockPos, biome, fullDataSource, block);
|
{
|
||||||
colorToApplyToNextBlock = ColorUtil.setAlpha(snowColor, 255);
|
// snow is a special case where it should always tint the block
|
||||||
|
// below it, if not done grass will appear as gray
|
||||||
|
int snowColor = levelWrapper.getBlockColor(mutableBlockPos, biome, fullDataSource, block);
|
||||||
|
colorToApplyToNextBlock = ColorUtil.setAlpha(snowColor, 255);
|
||||||
|
}
|
||||||
|
else //if (isWaterSurfaceReplacement)
|
||||||
|
{
|
||||||
|
colorToApplyToNextBlock = levelWrapper.getBlockColor(mutableBlockPos, biome, fullDataSource, block);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -449,6 +479,7 @@ public class FullDataToRenderDataTransformer
|
|||||||
}
|
}
|
||||||
lastBottom = bottomY;
|
lastBottom = bottomY;
|
||||||
lastColor = color;
|
lastColor = color;
|
||||||
|
lastBlock = block;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -9,7 +9,6 @@ import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
|
|||||||
import com.seibel.distanthorizons.core.file.fullDatafile.GeneratedFullDataSourceProvider;
|
import com.seibel.distanthorizons.core.file.fullDatafile.GeneratedFullDataSourceProvider;
|
||||||
import com.seibel.distanthorizons.core.generation.tasks.DataSourceRetrievalResult;
|
import com.seibel.distanthorizons.core.generation.tasks.DataSourceRetrievalResult;
|
||||||
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
|
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
|
||||||
import com.seibel.distanthorizons.core.generation.tasks.ERetrievalResultState;
|
|
||||||
import com.seibel.distanthorizons.core.pos.DhSectionPos;
|
import com.seibel.distanthorizons.core.pos.DhSectionPos;
|
||||||
import com.seibel.distanthorizons.core.pos.blockPos.DhBlockPos2D;
|
import com.seibel.distanthorizons.core.pos.blockPos.DhBlockPos2D;
|
||||||
import com.seibel.distanthorizons.core.util.FormatUtil;
|
import com.seibel.distanthorizons.core.util.FormatUtil;
|
||||||
@@ -93,7 +92,8 @@ public class PregenManager
|
|||||||
private final AtomicInteger nextSectionSpiralIndex = new AtomicInteger(0);
|
private final AtomicInteger nextSectionSpiralIndex = new AtomicInteger(0);
|
||||||
|
|
||||||
private final AtomicLong lastTaskFinishTime = new AtomicLong(System.currentTimeMillis());
|
private final AtomicLong lastTaskFinishTime = new AtomicLong(System.currentTimeMillis());
|
||||||
private final RollingAverage averageTaskCompletionIntervalMs = new RollingAverage(1000);
|
private RollingAverage averageTaskCompletionIntervalMs = new RollingAverage(1000);
|
||||||
|
private final RollingAverage averageTaskCompletionIntervalMsShort = new RollingAverage(50);
|
||||||
|
|
||||||
private final AtomicLong lastLogTime = new AtomicLong();
|
private final AtomicLong lastLogTime = new AtomicLong();
|
||||||
|
|
||||||
@@ -112,6 +112,7 @@ public class PregenManager
|
|||||||
|
|
||||||
long timeSincePreviousTaskFinish = System.currentTimeMillis() - this.lastTaskFinishTime.getAndSet(System.currentTimeMillis());
|
long timeSincePreviousTaskFinish = System.currentTimeMillis() - this.lastTaskFinishTime.getAndSet(System.currentTimeMillis());
|
||||||
this.averageTaskCompletionIntervalMs.add(timeSincePreviousTaskFinish);
|
this.averageTaskCompletionIntervalMs.add(timeSincePreviousTaskFinish);
|
||||||
|
this.averageTaskCompletionIntervalMsShort.add(timeSincePreviousTaskFinish);
|
||||||
|
|
||||||
PregenState.this.fillPendingQueue();
|
PregenState.this.fillPendingQueue();
|
||||||
})
|
})
|
||||||
@@ -182,6 +183,13 @@ public class PregenManager
|
|||||||
int chunkRatePerSecond = (int) (1000 / this.averageTaskCompletionIntervalMs.getAverage() * 4 * 4);
|
int chunkRatePerSecond = (int) (1000 / this.averageTaskCompletionIntervalMs.getAverage() * 4 * 4);
|
||||||
double etaMs = this.averageTaskCompletionIntervalMs.getAverage() * (this.sectionsToGenerate - this.nextSectionSpiralIndex.get());
|
double etaMs = this.averageTaskCompletionIntervalMs.getAverage() * (this.sectionsToGenerate - this.nextSectionSpiralIndex.get());
|
||||||
|
|
||||||
|
// Reset long rolling average if short average diverged too much (<0.5 / >2.0)
|
||||||
|
double averageRatio = this.averageTaskCompletionIntervalMsShort.getAverage() / this.averageTaskCompletionIntervalMs.getAverage();
|
||||||
|
if (averageRatio < 0.5 || averageRatio > 2.0)
|
||||||
|
{
|
||||||
|
this.averageTaskCompletionIntervalMs = new RollingAverage(1000);
|
||||||
|
}
|
||||||
|
|
||||||
return MessageFormat.format("Generated radius: {0,number,#.###} / {1,number,#.#} chunks ({2} cps, {3,number,#.###%}), ETA: {4}",
|
return MessageFormat.format("Generated radius: {0,number,#.###} / {1,number,#.#} chunks ({2} cps, {3,number,#.###%}), ETA: {4}",
|
||||||
this.generatedRadius.getValue(),
|
this.generatedRadius.getValue(),
|
||||||
chunksToGenerate,
|
chunksToGenerate,
|
||||||
|
|||||||
@@ -80,6 +80,7 @@ public abstract class AbstractDhLevel implements IDhLevel
|
|||||||
//=============//
|
//=============//
|
||||||
// constructor //
|
// constructor //
|
||||||
//=============//
|
//=============//
|
||||||
|
//region
|
||||||
|
|
||||||
protected AbstractDhLevel() { }
|
protected AbstractDhLevel() { }
|
||||||
|
|
||||||
@@ -135,11 +136,14 @@ public abstract class AbstractDhLevel implements IDhLevel
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//endregion
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//=================//
|
//=================//
|
||||||
// default methods //
|
// default methods //
|
||||||
//=================//
|
//=================//
|
||||||
|
//region
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void updateChunkAsync(IChunkWrapper chunkWrapper, int chunkHash)
|
public void updateChunkAsync(IChunkWrapper chunkWrapper, int chunkHash)
|
||||||
@@ -208,11 +212,14 @@ public abstract class AbstractDhLevel implements IDhLevel
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//endregion
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//=======//
|
//=======//
|
||||||
// repos //
|
// repos //
|
||||||
//=======//
|
//=======//
|
||||||
|
//region
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getChunkHash(DhChunkPos pos)
|
public int getChunkHash(DhChunkPos pos)
|
||||||
@@ -226,11 +233,14 @@ public abstract class AbstractDhLevel implements IDhLevel
|
|||||||
return (dto != null) ? dto.chunkHash : 0;
|
return (dto != null) ? dto.chunkHash : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//endregion
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//=================//
|
//=================//
|
||||||
// beacon handling //
|
// beacon handling //
|
||||||
//=================//
|
//=================//
|
||||||
|
//region
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void updateBeaconBeamsForSectionPos(long sectionPos, List<BeaconBeamDTO> activeBeamList)
|
public void updateBeaconBeamsForSectionPos(long sectionPos, List<BeaconBeamDTO> activeBeamList)
|
||||||
@@ -362,6 +372,8 @@ public abstract class AbstractDhLevel implements IDhLevel
|
|||||||
@Nullable
|
@Nullable
|
||||||
public BeaconBeamRepo getBeaconBeamRepo() { return this.beaconBeamRepo; }
|
public BeaconBeamRepo getBeaconBeamRepo() { return this.beaconBeamRepo; }
|
||||||
|
|
||||||
|
//endregion
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//================//
|
//================//
|
||||||
|
|||||||
@@ -98,7 +98,7 @@ public abstract class AbstractDhServerLevel extends AbstractDhLevel implements I
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean shouldDoWorldGen()
|
public boolean shouldDoWorldGen()
|
||||||
{ return Config.Common.WorldGenerator.enableDistantGeneration.get() && !this.worldGenPlayerCenteringQueue.isEmpty(); }
|
{ return Config.Common.WorldGenerator.enableDistantGeneration.get(); }
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public DhBlockPos2D getTargetPosForGeneration()
|
public DhBlockPos2D getTargetPosForGeneration()
|
||||||
|
|||||||
@@ -182,7 +182,6 @@ public class DhClientLevel extends AbstractDhLevel implements IDhClientLevel
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
// TODO this has a lock which can cause stuttering/lag issues
|
|
||||||
this.updateBeaconBeamsForSectionPos(dataSourceDto.pos, message.payload.beaconBeams);
|
this.updateBeaconBeamsForSectionPos(dataSourceDto.pos, message.payload.beaconBeams);
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
|
|||||||
@@ -168,7 +168,8 @@ public class LodRequestModule implements Closeable
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
dataFileHandler.clearRetrievalQueue();
|
dataFileHandler.clearRetrievalQueue();
|
||||||
worldGenState.closeAsync(true).join(); //TODO: Make it async.
|
// synchronized shutdown necessary to make sure the tasks are all handled correctly
|
||||||
|
worldGenState.closeAsync(true).join();
|
||||||
dataFileHandler.removeWorldGenCompleteListener(this.onWorldGenCompleteListener);
|
dataFileHandler.removeWorldGenCompleteListener(this.onWorldGenCompleteListener);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -198,7 +199,8 @@ public class LodRequestModule implements Closeable
|
|||||||
|
|
||||||
if (worldGenState != null)
|
if (worldGenState != null)
|
||||||
{
|
{
|
||||||
worldGenState.closeAsync(true).join(); //TODO: Make it async.
|
// synchronized shutdown necessary to make sure the tasks are all handled correctly
|
||||||
|
worldGenState.closeAsync(true).join();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -388,17 +390,17 @@ public class LodRequestModule implements Closeable
|
|||||||
this.progressUpdateThreadRunning = false;
|
this.progressUpdateThreadRunning = false;
|
||||||
|
|
||||||
return this.retrievalQueue.startClosingAsync(true, doInterrupt)
|
return this.retrievalQueue.startClosingAsync(true, doInterrupt)
|
||||||
.exceptionally(e ->
|
.exceptionally(e ->
|
||||||
{
|
{
|
||||||
LOGGER.error("Error during first stage of generation queue shutdown, Error: ["+e.getMessage()+"].", e);
|
LOGGER.error("Error during first stage of generation queue shutdown, Error: ["+e.getMessage()+"].", e);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
).thenRun(this.retrievalQueue::close)
|
).thenRun(this.retrievalQueue::close)
|
||||||
.exceptionally(e ->
|
.exceptionally(e ->
|
||||||
{
|
{
|
||||||
LOGGER.error("Error during second stage of generation queue shutdown, Error: ["+e.getMessage()+"].", e);
|
LOGGER.error("Error during second stage of generation queue shutdown, Error: ["+e.getMessage()+"].", e);
|
||||||
return null;
|
return null;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ import com.seibel.distanthorizons.core.jar.ModJarInfo;
|
|||||||
import com.seibel.distanthorizons.core.level.IDhClientLevel;
|
import com.seibel.distanthorizons.core.level.IDhClientLevel;
|
||||||
import com.seibel.distanthorizons.core.level.IDhLevel;
|
import com.seibel.distanthorizons.core.level.IDhLevel;
|
||||||
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
|
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
|
||||||
|
import com.seibel.distanthorizons.core.render.RenderThreadTaskHandler;
|
||||||
import com.seibel.distanthorizons.core.util.objects.pooling.PhantomArrayListPool;
|
import com.seibel.distanthorizons.core.util.objects.pooling.PhantomArrayListPool;
|
||||||
import com.seibel.distanthorizons.core.pos.DhSectionPos;
|
import com.seibel.distanthorizons.core.pos.DhSectionPos;
|
||||||
import com.seibel.distanthorizons.core.render.RenderBufferHandler;
|
import com.seibel.distanthorizons.core.render.RenderBufferHandler;
|
||||||
@@ -149,6 +150,13 @@ public class F3Screen
|
|||||||
messageList.add("");
|
messageList.add("");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// render thread tasks
|
||||||
|
if (Config.Client.Advanced.Debugging.F3Screen.showRenderThreadTasks.get())
|
||||||
|
{
|
||||||
|
RenderThreadTaskHandler.INSTANCE.addDebugMenuStringsToList(messageList);
|
||||||
|
messageList.add("");
|
||||||
|
}
|
||||||
|
|
||||||
// combined object pools
|
// combined object pools
|
||||||
if (Config.Client.Advanced.Debugging.F3Screen.showCombinedObjectPools.get())
|
if (Config.Client.Advanced.Debugging.F3Screen.showCombinedObjectPools.get())
|
||||||
{
|
{
|
||||||
|
|||||||
+167
-30
@@ -34,9 +34,12 @@ import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
|
|||||||
import com.seibel.distanthorizons.core.pos.blockPos.DhBlockPos2D;
|
import com.seibel.distanthorizons.core.pos.blockPos.DhBlockPos2D;
|
||||||
import com.seibel.distanthorizons.core.pos.DhSectionPos;
|
import com.seibel.distanthorizons.core.pos.DhSectionPos;
|
||||||
import com.seibel.distanthorizons.core.render.RenderBufferHandler;
|
import com.seibel.distanthorizons.core.render.RenderBufferHandler;
|
||||||
|
import com.seibel.distanthorizons.core.render.RenderThreadTaskHandler;
|
||||||
import com.seibel.distanthorizons.core.render.renderer.AbstractDebugWireframeRenderer;
|
import com.seibel.distanthorizons.core.render.renderer.AbstractDebugWireframeRenderer;
|
||||||
import com.seibel.distanthorizons.core.render.renderer.BeaconRenderHandler;
|
import com.seibel.distanthorizons.core.render.renderer.BeaconRenderHandler;
|
||||||
import com.seibel.distanthorizons.core.render.renderer.IDebugRenderable;
|
import com.seibel.distanthorizons.core.render.renderer.IDebugRenderable;
|
||||||
|
import com.seibel.distanthorizons.core.sql.dto.BeaconBeamDTO;
|
||||||
|
import com.seibel.distanthorizons.core.sql.repo.BeaconBeamRepo;
|
||||||
import com.seibel.distanthorizons.core.util.LodUtil;
|
import com.seibel.distanthorizons.core.util.LodUtil;
|
||||||
import com.seibel.distanthorizons.core.util.ThreadUtil;
|
import com.seibel.distanthorizons.core.util.ThreadUtil;
|
||||||
import com.seibel.distanthorizons.core.util.WorldGenUtil;
|
import com.seibel.distanthorizons.core.util.WorldGenUtil;
|
||||||
@@ -54,10 +57,7 @@ import org.jetbrains.annotations.Nullable;
|
|||||||
import javax.annotation.WillNotClose;
|
import javax.annotation.WillNotClose;
|
||||||
import java.awt.*;
|
import java.awt.*;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.concurrent.CompletableFuture;
|
import java.util.concurrent.*;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
|
||||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
|
||||||
import java.util.concurrent.ThreadPoolExecutor;
|
|
||||||
import java.util.concurrent.atomic.AtomicBoolean;
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
import java.util.concurrent.locks.ReentrantLock;
|
import java.util.concurrent.locks.ReentrantLock;
|
||||||
|
|
||||||
@@ -96,8 +96,19 @@ public class LodQuadTree extends QuadTree<LodRenderSection> implements IDebugRen
|
|||||||
private final AtomicBoolean queueThreadRunningRef = new AtomicBoolean(false);
|
private final AtomicBoolean queueThreadRunningRef = new AtomicBoolean(false);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* contains the list of beacons currently being rendered in this section
|
||||||
|
* if this list is modified the {@link LodQuadTree#beaconRenderHandler} should be updated to match.
|
||||||
|
*/
|
||||||
|
private final ArrayList<BeaconRenderHandler.BeaconBeamWithWidth> beaconList = new ArrayList<>();
|
||||||
@Nullable
|
@Nullable
|
||||||
public final BeaconRenderHandler beaconRenderHandler;
|
private final BeaconRenderHandler beaconRenderHandler;
|
||||||
|
@Nullable
|
||||||
|
private final BeaconBeamRepo beaconBeamRepo;
|
||||||
|
/** used to prevent updating the beacons concurrently */
|
||||||
|
@NotNull
|
||||||
|
private CompletableFuture<Void> beaconUpdateFuture = CompletableFuture.completedFuture(null);
|
||||||
|
|
||||||
|
|
||||||
/** the smallest numerical detail level number that can be rendered */
|
/** the smallest numerical detail level number that can be rendered */
|
||||||
private byte maxLeafRenderDetailLevel;
|
private byte maxLeafRenderDetailLevel;
|
||||||
@@ -148,6 +159,8 @@ public class LodQuadTree extends QuadTree<LodRenderSection> implements IDebugRen
|
|||||||
IDhGenericRenderer genericObjectRenderer = this.level.getGenericRenderer();
|
IDhGenericRenderer genericObjectRenderer = this.level.getGenericRenderer();
|
||||||
this.beaconRenderHandler = (genericObjectRenderer != null) ? new BeaconRenderHandler(genericObjectRenderer) : null;
|
this.beaconRenderHandler = (genericObjectRenderer != null) ? new BeaconRenderHandler(genericObjectRenderer) : null;
|
||||||
|
|
||||||
|
this.beaconBeamRepo = this.level.getBeaconBeamRepo();
|
||||||
|
|
||||||
Config.Common.WorldGenerator.enableDistantGeneration.addListener(this);
|
Config.Common.WorldGenerator.enableDistantGeneration.addListener(this);
|
||||||
Config.Server.enableServerGeneration.addListener(this);
|
Config.Server.enableServerGeneration.addListener(this);
|
||||||
|
|
||||||
@@ -366,21 +379,30 @@ public class LodQuadTree extends QuadTree<LodRenderSection> implements IDebugRen
|
|||||||
if (node == null || node.value == null) { continue; }
|
if (node == null || node.value == null) { continue; }
|
||||||
|
|
||||||
node.value.setRenderingEnabled(false);
|
node.value.setRenderingEnabled(false);
|
||||||
node.value.tryDisableBeacons();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for (QuadNode<LodRenderSection> node : this.tickNodeHolder.getEnableDeleteChildrenNodes())
|
for (QuadNode<LodRenderSection> node : this.tickNodeHolder.getEnableDeleteChildrenNodes())
|
||||||
{
|
{
|
||||||
if (node == null || node.value == null) { continue; }
|
if (node == null
|
||||||
|
|| node.value == null
|
||||||
node.deleteAllChildren((childRenderSection) ->
|
// only clear the children if there are children to clear
|
||||||
|
|| node.getDirectChildCount() == 0)
|
||||||
{
|
{
|
||||||
if (childRenderSection != null)
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// run this on the render thread to hopefully prevent
|
||||||
|
// closing render data while rendering is happening
|
||||||
|
RenderThreadTaskHandler.INSTANCE.queueRunningOnRenderThread("LodQuadTree delayed child cleanup", () ->
|
||||||
|
{
|
||||||
|
node.deleteAllChildren((childRenderSection) ->
|
||||||
{
|
{
|
||||||
childRenderSection.setRenderingEnabled(false);
|
if (childRenderSection != null)
|
||||||
childRenderSection.tryDisableBeacons();
|
{
|
||||||
childRenderSection.close();
|
childRenderSection.setRenderingEnabled(false);
|
||||||
}
|
childRenderSection.close();
|
||||||
|
}
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -393,21 +415,7 @@ public class LodQuadTree extends QuadTree<LodRenderSection> implements IDebugRen
|
|||||||
//=================//
|
//=================//
|
||||||
//region
|
//region
|
||||||
|
|
||||||
// must be handled after beacon disabling
|
this.tryRefreshRenderingBeaconsAsync(playerPos);
|
||||||
// otherwise the beacons will be missing
|
|
||||||
|
|
||||||
for (QuadNode<LodRenderSection> node : this.tickNodeHolder.getEnabledNodes())
|
|
||||||
{
|
|
||||||
if (node == null || node.value == null) { continue; }
|
|
||||||
|
|
||||||
node.value.tryEnableBeacons();
|
|
||||||
}
|
|
||||||
for (QuadNode<LodRenderSection> node : this.tickNodeHolder.getEnableDeleteChildrenNodes())
|
|
||||||
{
|
|
||||||
if (node == null || node.value == null) { continue; }
|
|
||||||
|
|
||||||
node.value.tryEnableBeacons();
|
|
||||||
}
|
|
||||||
|
|
||||||
//endregion
|
//endregion
|
||||||
|
|
||||||
@@ -860,6 +868,133 @@ public class LodQuadTree extends QuadTree<LodRenderSection> implements IDebugRen
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//=================//
|
||||||
|
// beacon handling //
|
||||||
|
//=================//
|
||||||
|
//region beacon handling
|
||||||
|
|
||||||
|
/** gets the active beacon list and stops/starts beacon rendering as necessary */
|
||||||
|
private void tryRefreshRenderingBeaconsAsync(DhBlockPos2D playerPos)
|
||||||
|
{
|
||||||
|
// do nothing if beacon rendering or repos are unavailable
|
||||||
|
if (this.beaconBeamRepo == null
|
||||||
|
|| this.beaconRenderHandler == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
AbstractExecutorService executor = ThreadPoolUtil.getFileHandlerExecutor();
|
||||||
|
if (executor == null)
|
||||||
|
{
|
||||||
|
// shouldn't normally happen, but just in case
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this.beaconUpdateFuture.isDone())
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
CompletableFuture<Void> future = new CompletableFuture<>();
|
||||||
|
this.beaconUpdateFuture = future;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
executor.execute(() ->
|
||||||
|
{
|
||||||
|
this.refreshRenderingBeacons(playerPos);
|
||||||
|
|
||||||
|
try { Thread.sleep(2_000); } catch (InterruptedException ignore) { }
|
||||||
|
future.complete(null);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
catch (RejectedExecutionException e)
|
||||||
|
{
|
||||||
|
// the thread pool was probably shut down because it's size is being changed, just wait a sec and it should be back
|
||||||
|
future.completeExceptionally(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private void refreshRenderingBeacons(DhBlockPos2D playerPos)
|
||||||
|
{
|
||||||
|
if (this.beaconBeamRepo == null
|
||||||
|
|| this.beaconRenderHandler == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// Synchronized to prevent two threads for accessing the array at once
|
||||||
|
synchronized (this.beaconList)
|
||||||
|
{
|
||||||
|
// get beacons //
|
||||||
|
|
||||||
|
int blockDistanceRadius = (this.blockRenderDistanceDiameter / 2);
|
||||||
|
int minBlockPosX = playerPos.x - blockDistanceRadius;
|
||||||
|
int minBlockPosZ = playerPos.z - blockDistanceRadius;
|
||||||
|
int maxBlockPosX = playerPos.x + blockDistanceRadius;
|
||||||
|
int maxBlockPosZ = playerPos.z + blockDistanceRadius;
|
||||||
|
|
||||||
|
ArrayList<BeaconBeamDTO> dbBeacons = this.beaconBeamRepo.getAllBeamsInBlockPosRange(
|
||||||
|
minBlockPosX, maxBlockPosX,
|
||||||
|
minBlockPosZ, maxBlockPosZ
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
// convert DB beacons //
|
||||||
|
|
||||||
|
ArrayList<BeaconRenderHandler.BeaconBeamWithWidth> newBeaconList = new ArrayList<>(this.beaconList.size());
|
||||||
|
for (BeaconBeamDTO beaconBeam : dbBeacons)
|
||||||
|
{
|
||||||
|
byte beaconDetailLevel = this.calcExpectedDetailLevel(playerPos, beaconBeam.blockPos.getX(), beaconBeam.blockPos.getZ());
|
||||||
|
newBeaconList.add(new BeaconRenderHandler.BeaconBeamWithWidth(beaconBeam, beaconDetailLevel));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
boolean replaceBeacons = false;
|
||||||
|
if (this.beaconList.size() != newBeaconList.size())
|
||||||
|
{
|
||||||
|
// lists are different sizes
|
||||||
|
replaceBeacons = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// sort the beacons so they can be compared
|
||||||
|
this.beaconList.sort(BeaconRenderHandler.NegativeInfiniteBlockPosComparator.INSTANCE);
|
||||||
|
newBeaconList.sort(BeaconRenderHandler.NegativeInfiniteBlockPosComparator.INSTANCE);
|
||||||
|
|
||||||
|
for (int i = 0; i < this.beaconList.size(); i++)
|
||||||
|
{
|
||||||
|
BeaconRenderHandler.BeaconBeamWithWidth oldBeam = this.beaconList.get(i);
|
||||||
|
BeaconRenderHandler.BeaconBeamWithWidth newBeam = newBeaconList.get(i);
|
||||||
|
if (!oldBeam.equals(newBeam))
|
||||||
|
{
|
||||||
|
replaceBeacons = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// only replace the beacons if something changed
|
||||||
|
// this is done to prevent constantly re-uploading the render data
|
||||||
|
if (replaceBeacons)
|
||||||
|
{
|
||||||
|
this.beaconList.clear();
|
||||||
|
this.beaconList.addAll(newBeaconList);
|
||||||
|
this.beaconRenderHandler.replaceRenderingBeacons(this.beaconList);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
LOGGER.error("Unexpected issue updating beacons, error: ["+e.getMessage()+"].", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//endregion beacon handling
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//====================//
|
//====================//
|
||||||
// detail level logic //
|
// detail level logic //
|
||||||
//====================//
|
//====================//
|
||||||
@@ -873,8 +1008,10 @@ public class LodQuadTree extends QuadTree<LodRenderSection> implements IDebugRen
|
|||||||
* @return detail level of this section pos
|
* @return detail level of this section pos
|
||||||
*/
|
*/
|
||||||
public byte calcExpectedDetailLevel(DhBlockPos2D playerPos, long sectionPos)
|
public byte calcExpectedDetailLevel(DhBlockPos2D playerPos, long sectionPos)
|
||||||
|
{ return this.calcExpectedDetailLevel(playerPos, DhSectionPos.getCenterBlockPosX(sectionPos), DhSectionPos.getCenterBlockPosZ(sectionPos)); }
|
||||||
|
public byte calcExpectedDetailLevel(DhBlockPos2D playerPos, int targetBlockPosX, int targetBlockPosZ)
|
||||||
{
|
{
|
||||||
double blockDistance = playerPos.dist(DhSectionPos.getCenterBlockPosX(sectionPos), DhSectionPos.getCenterBlockPosZ(sectionPos));
|
double blockDistance = playerPos.dist(targetBlockPosX, targetBlockPosZ);
|
||||||
return this.calcDetailLevelFromDistance(blockDistance);
|
return this.calcDetailLevelFromDistance(blockDistance);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
-146
@@ -73,21 +73,6 @@ public class LodRenderSection implements IDebugRenderable, AutoCloseable
|
|||||||
private final FullDataSourceProviderV2 fullDataSourceProvider;
|
private final FullDataSourceProviderV2 fullDataSourceProvider;
|
||||||
private final LodQuadTree quadTree;
|
private final LodQuadTree quadTree;
|
||||||
|
|
||||||
/**
|
|
||||||
* contains the list of beacons currently being rendered in this section
|
|
||||||
* if this list is modified the {@link LodRenderSection#beaconRenderHandler} should be updated to match.
|
|
||||||
*/
|
|
||||||
private final ArrayList<BeaconBeamDTO> activeBeaconList = new ArrayList<>();
|
|
||||||
@Nullable
|
|
||||||
public final BeaconRenderHandler beaconRenderHandler;
|
|
||||||
@Nullable
|
|
||||||
public final BeaconBeamRepo beaconBeamRepo;
|
|
||||||
/**
|
|
||||||
* locking is necessary to prevent some weird threading issues
|
|
||||||
* causing beacons to appear/disappear at the wrong times.
|
|
||||||
*/
|
|
||||||
private final ReentrantLock beaconRenderHandlingLock = new ReentrantLock();
|
|
||||||
|
|
||||||
|
|
||||||
private boolean renderingEnabled = false;
|
private boolean renderingEnabled = false;
|
||||||
private boolean beaconsRendering = false;
|
private boolean beaconsRendering = false;
|
||||||
@@ -134,9 +119,6 @@ public class LodRenderSection implements IDebugRenderable, AutoCloseable
|
|||||||
this.levelWrapper = level.getClientLevelWrapper();
|
this.levelWrapper = level.getClientLevelWrapper();
|
||||||
this.fullDataSourceProvider = fullDataSourceProvider;
|
this.fullDataSourceProvider = fullDataSourceProvider;
|
||||||
|
|
||||||
this.beaconRenderHandler = this.quadTree.beaconRenderHandler;
|
|
||||||
this.beaconBeamRepo = this.level.getBeaconBeamRepo();
|
|
||||||
|
|
||||||
DEBUG_RENDERER.register(this, Config.Client.Advanced.Debugging.DebugWireframe.showRenderSectionStatus);
|
DEBUG_RENDERER.register(this, Config.Client.Advanced.Debugging.DebugWireframe.showRenderSectionStatus);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -188,8 +170,6 @@ public class LodRenderSection implements IDebugRenderable, AutoCloseable
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
this.refreshActiveBeaconList();
|
|
||||||
|
|
||||||
LodQuadBuilder lodQuadBuilder = this.getAndBuildRenderData();
|
LodQuadBuilder lodQuadBuilder = this.getAndBuildRenderData();
|
||||||
if (lodQuadBuilder == null)
|
if (lodQuadBuilder == null)
|
||||||
{
|
{
|
||||||
@@ -376,130 +356,6 @@ public class LodRenderSection implements IDebugRenderable, AutoCloseable
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
//=================//
|
|
||||||
// beacon handling //
|
|
||||||
//=================//
|
|
||||||
//region beacon handling
|
|
||||||
|
|
||||||
/** gets the active beacon list and stops/starts beacon rendering as necessary */
|
|
||||||
private void refreshActiveBeaconList()
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
this.beaconRenderHandlingLock.lock();
|
|
||||||
|
|
||||||
// do nothing if beacon rendering or repos are unavailable
|
|
||||||
if (this.beaconBeamRepo == null
|
|
||||||
|| this.beaconRenderHandler == null)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Synchronized to prevent two threads for accessing the array at once
|
|
||||||
synchronized (this.activeBeaconList)
|
|
||||||
{
|
|
||||||
ArrayList<BeaconBeamDTO> activeBeacons = this.beaconBeamRepo.getAllBeamsForPos(this.pos);
|
|
||||||
|
|
||||||
// swap old and new active beacon list
|
|
||||||
this.activeBeaconList.clear();
|
|
||||||
this.activeBeaconList.addAll(activeBeacons);
|
|
||||||
|
|
||||||
// if the beacons are currently rendering,
|
|
||||||
// re-create them so we can see any potential changes
|
|
||||||
if (this.beaconsRendering)
|
|
||||||
{
|
|
||||||
this.tryDisableBeacons();
|
|
||||||
this.tryEnableBeacons();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
this.beaconRenderHandlingLock.unlock();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void tryDisableBeacons()
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
this.beaconRenderHandlingLock.lock();
|
|
||||||
|
|
||||||
|
|
||||||
// do nothing if beacon rendering is unavailable
|
|
||||||
if (this.beaconRenderHandler == null)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!this.beaconsRendering)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.beaconsRendering = false;
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if (Config.Client.Advanced.Debugging.DebugWireframe.showRenderSectionStatus.get())
|
|
||||||
{
|
|
||||||
// show that this position has just been disabled
|
|
||||||
DEBUG_RENDERER.makeParticle(
|
|
||||||
new AbstractDebugWireframeRenderer.BoxParticle(
|
|
||||||
new AbstractDebugWireframeRenderer.Box(this.pos, 128f, 156f, 0.09f, Color.CYAN.darker()),
|
|
||||||
0.2, 32f
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
synchronized (this.activeBeaconList)
|
|
||||||
{
|
|
||||||
this.beaconRenderHandler.stopRenderingBeaconsInRange(this.pos);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
this.beaconRenderHandlingLock.unlock();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void tryEnableBeacons()
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
this.beaconRenderHandlingLock.lock();
|
|
||||||
|
|
||||||
|
|
||||||
// do nothing if beacon rendering is unavailable
|
|
||||||
if (this.beaconRenderHandler == null)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.beaconsRendering)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.beaconsRendering = true;
|
|
||||||
|
|
||||||
|
|
||||||
synchronized (this.activeBeaconList)
|
|
||||||
{
|
|
||||||
byte absoluteDetailLevel = (byte)(DhSectionPos.getDetailLevel(this.pos) - DhSectionPos.SECTION_MINIMUM_DETAIL_LEVEL);
|
|
||||||
this.beaconRenderHandler.startRenderingBeacons(this.activeBeaconList, absoluteDetailLevel);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
this.beaconRenderHandlingLock.unlock();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//endregion beacon handling
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//==============//
|
//==============//
|
||||||
// base methods //
|
// base methods //
|
||||||
//==============//
|
//==============//
|
||||||
@@ -562,8 +418,6 @@ public class LodRenderSection implements IDebugRenderable, AutoCloseable
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
this.tryDisableBeacons();
|
|
||||||
|
|
||||||
if (this.renderBufferContainer != null)
|
if (this.renderBufferContainer != null)
|
||||||
{
|
{
|
||||||
this.renderBufferContainer.close();
|
this.renderBufferContainer.close();
|
||||||
|
|||||||
@@ -172,43 +172,43 @@ public class RenderParams extends DhApiRenderParam
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// potential fix for a segfault when
|
//// potential fix for a segfault when
|
||||||
// Sodium and DH are running together
|
//// Sodium and DH are running together
|
||||||
if (EPlatform.get() == EPlatform.MACOS
|
//if (EPlatform.get() == EPlatform.MACOS
|
||||||
&& !initialLoadingComplete)
|
// && !initialLoadingComplete)
|
||||||
{
|
//{
|
||||||
// Once MC starts rendering, wait a few seconds so
|
// // Once MC starts rendering, wait a few seconds so
|
||||||
// MC/Sodium can finish their shader compiling before DH does its own.
|
// // MC/Sodium can finish their shader compiling before DH does its own.
|
||||||
// This will allow DH to compile its own shaders after Sodium finishes
|
// // This will allow DH to compile its own shaders after Sodium finishes
|
||||||
// compiling its own.
|
// // compiling its own.
|
||||||
long nowMs = System.currentTimeMillis();
|
// long nowMs = System.currentTimeMillis();
|
||||||
long firstAllowedRenderTimeMs = firstRenderTimeMs + TIME_FOR_MAC_TO_FINISH_COMPILING_IN_MS;
|
// long firstAllowedRenderTimeMs = firstRenderTimeMs + TIME_FOR_MAC_TO_FINISH_COMPILING_IN_MS;
|
||||||
if (nowMs < firstAllowedRenderTimeMs)
|
// if (nowMs < firstAllowedRenderTimeMs)
|
||||||
{
|
// {
|
||||||
return "Waiting for initial MC compile...";
|
// return "Waiting for initial MC compile...";
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
|
//
|
||||||
// null shouldn't happen, but just in case
|
// // null shouldn't happen, but just in case
|
||||||
PriorityTaskPicker.Executor renderLoadExecutor = ThreadPoolUtil.getRenderLoadingExecutor();
|
// PriorityTaskPicker.Executor renderLoadExecutor = ThreadPoolUtil.getRenderLoadingExecutor();
|
||||||
if (renderLoadExecutor == null)
|
// if (renderLoadExecutor == null)
|
||||||
{
|
// {
|
||||||
return "Waiting for DH Threadpool...";
|
// return "Waiting for DH Threadpool...";
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
// wait for DH to finish loading, by the time that's done
|
// // wait for DH to finish loading, by the time that's done
|
||||||
// java should have finished all of DH's JIT compiling,
|
// // java should have finished all of DH's JIT compiling,
|
||||||
// which will hopefully mean less concurrency and thus a lower
|
// // which will hopefully mean less concurrency and thus a lower
|
||||||
// chance of breaking
|
// // chance of breaking
|
||||||
// (plus this gives Sodium/vanill a bit longer to finish their setup)
|
// // (plus this gives Sodium/vanill a bit longer to finish their setup)
|
||||||
int taskCount = renderLoadExecutor.getQueueSize();
|
// int taskCount = renderLoadExecutor.getQueueSize();
|
||||||
if (taskCount > 0)
|
// if (taskCount > 0)
|
||||||
{
|
// {
|
||||||
return "Waiting for DH JIT compiling...";
|
// return "Waiting for DH JIT compiling...";
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
initialLoadingComplete = true;
|
// initialLoadingComplete = true;
|
||||||
}
|
//}
|
||||||
|
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
|
|||||||
+226
-36
@@ -2,32 +2,53 @@ package com.seibel.distanthorizons.core.render;
|
|||||||
|
|
||||||
import com.seibel.distanthorizons.core.config.Config;
|
import com.seibel.distanthorizons.core.config.Config;
|
||||||
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
|
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
|
||||||
|
import com.seibel.distanthorizons.core.enums.MinecraftTextFormat;
|
||||||
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.util.ExceptionUtil;
|
||||||
import com.seibel.distanthorizons.core.util.TimerUtil;
|
import com.seibel.distanthorizons.core.util.TimerUtil;
|
||||||
|
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.coreapi.ModInfo;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
import java.text.NumberFormat;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Timer;
|
import java.util.Timer;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
import java.util.concurrent.atomic.LongAdder;
|
||||||
|
|
||||||
public class RenderThreadTaskHandler
|
public class RenderThreadTaskHandler
|
||||||
{
|
{
|
||||||
public static final DhLogger LOGGER = new DhLoggerBuilder()
|
private static final DhLogger LOGGER = new DhLoggerBuilder()
|
||||||
.fileLevelConfig(Config.Common.Logging.logRendererEventToFile)
|
.fileLevelConfig(Config.Common.Logging.logRendererEventToFile)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
private static final ConcurrentLinkedQueue<Runnable> RENDER_THREAD_RUNNABLE_QUEUE = new ConcurrentLinkedQueue<>();
|
private static final DhLogger RATE_LIMITED_LOGGER = new DhLoggerBuilder()
|
||||||
|
.fileLevelConfig(Config.Common.Logging.logRendererEventToFile)
|
||||||
|
.maxCountPerSecond(4)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
private static final ConcurrentLinkedQueue<QueuedRunnable> RENDER_THREAD_RUNNABLE_QUEUE = new ConcurrentLinkedQueue<>();
|
||||||
|
|
||||||
|
private static final ConcurrentHashMap<String, RollingAverage> AVERAGE_NANO_RUN_TIME_BY_TASK_NAME = new ConcurrentHashMap<>();
|
||||||
|
private static final LongAdder COMPLETED_TASK_COUNTER = new LongAdder();
|
||||||
|
private static final NumberFormat DECIMAL_NUMBER_FORMAT = NumberFormat.getNumberInstance();
|
||||||
|
private static final NumberFormat INT_NUMBER_FORMAT = NumberFormat.getIntegerInstance();
|
||||||
|
private static final boolean LOG_SLOW_TASKS = false;
|
||||||
|
|
||||||
private static final Timer TIMER = TimerUtil.CreateTimer("Cleanup timer");
|
private static final Timer TIMER = TimerUtil.CreateTimer("Cleanup timer");
|
||||||
private static final long MS_BETWEEN_CLEANUP_TICKS = 1_000L;
|
private static final long MS_BETWEEN_CLEANUP_TICKS = 1_000L;
|
||||||
private static final long MS_BEFORE_RUN_CLEANUP_TIMER = 1_000L;
|
private static final long NANOS_BEFORE_RUN_CLEANUP_TIMER = TimeUnit.NANOSECONDS.convert(1_000L, TimeUnit.MILLISECONDS);
|
||||||
|
|
||||||
|
|
||||||
public static final RenderThreadTaskHandler INSTANCE = new RenderThreadTaskHandler();
|
public static final RenderThreadTaskHandler INSTANCE = new RenderThreadTaskHandler();
|
||||||
|
|
||||||
|
|
||||||
private long msSinceGlTasksRun = System.currentTimeMillis();
|
private long nanoSinceTasksRun = System.nanoTime();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -47,23 +68,17 @@ public class RenderThreadTaskHandler
|
|||||||
//==============//
|
//==============//
|
||||||
//region
|
//region
|
||||||
|
|
||||||
public void queueRunningOnRenderThread(Runnable renderCall)
|
public void queueRunningOnRenderThread(String name, Runnable renderCall)
|
||||||
{
|
{
|
||||||
StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
|
// don't get the stacktrace on release to reduce GC pressure
|
||||||
RENDER_THREAD_RUNNABLE_QUEUE.add(() -> this.createRenderThreadRunnable(renderCall, stackTrace));
|
StackTraceElement[] stackTrace = null;
|
||||||
}
|
if (ModInfo.IS_DEV_BUILD)
|
||||||
private void createRenderThreadRunnable(Runnable renderCall, StackTraceElement[] stackTrace)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
{
|
||||||
renderCall.run();
|
stackTrace = Thread.currentThread().getStackTrace();
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
RuntimeException error = new RuntimeException("Uncaught Exception during GL call execution:", e);
|
|
||||||
error.setStackTrace(stackTrace);
|
|
||||||
LOGGER.error("[" + Thread.currentThread().getName() + "] ran into an unexpected error running a GL call, Error: ["+ e.getMessage() +"].", error);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QueuedRunnable runnable = new QueuedRunnable(name, renderCall, stackTrace);
|
||||||
|
RENDER_THREAD_RUNNABLE_QUEUE.add(runnable);
|
||||||
}
|
}
|
||||||
|
|
||||||
//endregion
|
//endregion
|
||||||
@@ -83,31 +98,63 @@ public class RenderThreadTaskHandler
|
|||||||
{
|
{
|
||||||
IMinecraftRenderWrapper MC_RENDER = SingletonInjector.INSTANCE.get(IMinecraftRenderWrapper.class);
|
IMinecraftRenderWrapper MC_RENDER = SingletonInjector.INSTANCE.get(IMinecraftRenderWrapper.class);
|
||||||
|
|
||||||
|
// https://fpstoms.com/
|
||||||
int frameLimit = MC_RENDER.getFrameLimit();
|
int frameLimit = MC_RENDER.getFrameLimit();
|
||||||
if (frameLimit <= 1)
|
if (frameLimit <= 1)
|
||||||
{
|
{
|
||||||
frameLimit = 4; // 240 FPS
|
frameLimit = 240;
|
||||||
}
|
}
|
||||||
|
|
||||||
// https://fpstoms.com/
|
|
||||||
int msPerFrame = 1000 / frameLimit;
|
|
||||||
this.runRenderThreadTasks(msPerFrame);
|
|
||||||
}
|
|
||||||
private void runRenderThreadTasks(long msMaxRunTime)
|
|
||||||
{
|
|
||||||
long startTimeMs = System.currentTimeMillis();
|
|
||||||
this.msSinceGlTasksRun = startTimeMs;
|
|
||||||
|
|
||||||
Runnable runnable = RENDER_THREAD_RUNNABLE_QUEUE.poll();
|
int msPerFrame = 1000 / frameLimit;
|
||||||
|
long nanoPerFrame = msPerFrame * 1_000_000L;
|
||||||
|
nanoPerFrame /= 2; // divide the time in half so we can only impact half of the framerate at worst
|
||||||
|
this.runRenderThreadTasks(nanoPerFrame);
|
||||||
|
}
|
||||||
|
private void runRenderThreadTasks(long nanoMaxRunTime)
|
||||||
|
{
|
||||||
|
long loopStartTimeNano = System.nanoTime();
|
||||||
|
this.nanoSinceTasksRun = loopStartTimeNano;
|
||||||
|
|
||||||
|
QueuedRunnable runnable = RENDER_THREAD_RUNNABLE_QUEUE.poll();
|
||||||
while(runnable != null)
|
while(runnable != null)
|
||||||
{
|
{
|
||||||
|
long taskStartNano = System.nanoTime();
|
||||||
|
|
||||||
runnable.run();
|
runnable.run();
|
||||||
|
|
||||||
// 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 currentTimeMs = System.currentTimeMillis();
|
long taskNano = System.nanoTime() - taskStartNano;
|
||||||
long runDuration = currentTimeMs - startTimeMs;
|
long totalLoopNano = System.nanoTime() - loopStartTimeNano;
|
||||||
if (runDuration > msMaxRunTime)
|
|
||||||
|
// stat tracking
|
||||||
|
if (ModInfo.IS_DEV_BUILD)
|
||||||
{
|
{
|
||||||
|
if (!AVERAGE_NANO_RUN_TIME_BY_TASK_NAME.containsKey(runnable.name))
|
||||||
|
{
|
||||||
|
AVERAGE_NANO_RUN_TIME_BY_TASK_NAME.put(runnable.name, new RollingAverage(1_000));
|
||||||
|
}
|
||||||
|
AVERAGE_NANO_RUN_TIME_BY_TASK_NAME.get(runnable.name).add(totalLoopNano);
|
||||||
|
|
||||||
|
COMPLETED_TASK_COUNTER.increment();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// estimate when our ending nano-time would be once the next task is run
|
||||||
|
long expectedNextTaskNano = totalLoopNano
|
||||||
|
// doubling this task's time gives a rough over-estimate of how long the next task should take
|
||||||
|
+ (taskNano * 2);
|
||||||
|
// If the next task would push us over the max run time, stop now.
|
||||||
|
// This prevents stuttering at the cost of lower throughput.
|
||||||
|
if (expectedNextTaskNano >= nanoMaxRunTime)
|
||||||
|
{
|
||||||
|
if (LOG_SLOW_TASKS
|
||||||
|
&& totalLoopNano > nanoMaxRunTime)
|
||||||
|
{
|
||||||
|
// this task took longer than what we wanted
|
||||||
|
RATE_LIMITED_LOGGER.warn("["+runnable.name+"] slow, actual ["+totalLoopNano+"], allowed ["+nanoMaxRunTime+"].");
|
||||||
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -121,9 +168,9 @@ public class RenderThreadTaskHandler
|
|||||||
*/
|
*/
|
||||||
private void manualCleanupTick()
|
private void manualCleanupTick()
|
||||||
{
|
{
|
||||||
long nowMs = System.currentTimeMillis();
|
long nowNano = System.nanoTime();
|
||||||
long msSinceLast = nowMs - this.msSinceGlTasksRun;
|
long nanoSinceLast = nowNano - this.nanoSinceTasksRun;
|
||||||
if (msSinceLast > MS_BEFORE_RUN_CLEANUP_TIMER)
|
if (nanoSinceLast < NANOS_BEFORE_RUN_CLEANUP_TIMER)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -133,10 +180,153 @@ public class RenderThreadTaskHandler
|
|||||||
// 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 MC = SingletonInjector.INSTANCE.get(IMinecraftClientWrapper.class);
|
||||||
MC.executeOnRenderThread(() -> this.runRenderThreadTasks(1_000));
|
MC.executeOnRenderThread(() -> this.runRenderThreadTasks(500 * 1_000_000L));
|
||||||
}
|
}
|
||||||
|
|
||||||
//end region
|
//endregion
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//===========//
|
||||||
|
// debugging //
|
||||||
|
//===========//
|
||||||
|
///region
|
||||||
|
|
||||||
|
/**
|
||||||
|
* if tasks are currently queued the debug
|
||||||
|
* stats may not be zero after this method has been called.
|
||||||
|
*/
|
||||||
|
public void clearDebugStats()
|
||||||
|
{
|
||||||
|
AVERAGE_NANO_RUN_TIME_BY_TASK_NAME.clear();
|
||||||
|
COMPLETED_TASK_COUNTER.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addDebugMenuStringsToList(List<String> messageList)
|
||||||
|
{
|
||||||
|
if (!ModInfo.IS_DEV_BUILD)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
String o = MinecraftTextFormat.ORANGE;
|
||||||
|
String g = MinecraftTextFormat.GREEN;
|
||||||
|
String b = MinecraftTextFormat.DARK_BLUE;
|
||||||
|
String y = MinecraftTextFormat.YELLOW;
|
||||||
|
String cf = MinecraftTextFormat.CLEAR_FORMATTING;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
String queueSize = DECIMAL_NUMBER_FORMAT.format(RENDER_THREAD_RUNNABLE_QUEUE.size());
|
||||||
|
String completedCount = DECIMAL_NUMBER_FORMAT.format(COMPLETED_TASK_COUNTER.sum());
|
||||||
|
|
||||||
|
String messageHeader = "Render Tasks, Queue: "+o+queueSize+cf+", Done: "+g+completedCount+cf;
|
||||||
|
messageList.add(messageHeader);
|
||||||
|
|
||||||
|
AVERAGE_NANO_RUN_TIME_BY_TASK_NAME.forEach((name, rollingAverage) ->
|
||||||
|
{
|
||||||
|
// thread runtime
|
||||||
|
String runTimeAvgStr;
|
||||||
|
double runTimeAvgInNano = rollingAverage.getAverage();
|
||||||
|
if (!Double.isNaN(runTimeAvgInNano))
|
||||||
|
{
|
||||||
|
double runTimeAvgInMs = runTimeAvgInNano / 1_000_000.0;
|
||||||
|
runTimeAvgStr = DECIMAL_NUMBER_FORMAT.format(runTimeAvgInMs);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
runTimeAvgStr = "<0";
|
||||||
|
}
|
||||||
|
|
||||||
|
String lifetimeCount = INT_NUMBER_FORMAT.format(rollingAverage.getLifetimeCount());
|
||||||
|
|
||||||
|
String message = name+" Avg: "+b+runTimeAvgStr+"ms"+cf+" #: "+y+lifetimeCount+cf;
|
||||||
|
messageList.add(message);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
///endregion
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//================//
|
||||||
|
// helper classes //
|
||||||
|
//================//
|
||||||
|
//region
|
||||||
|
|
||||||
|
private static class QueuedRunnable implements Runnable
|
||||||
|
{
|
||||||
|
/** used to easily track what's being done on the render thread */
|
||||||
|
public final String name;
|
||||||
|
public final Runnable renderCall;
|
||||||
|
/** will be null on release build to reduce GC pressure */
|
||||||
|
@Nullable
|
||||||
|
public final StackTraceElement[] stackTrace;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//=============//
|
||||||
|
// constructor //
|
||||||
|
//=============//
|
||||||
|
//region
|
||||||
|
|
||||||
|
public QueuedRunnable(String name, Runnable renderCall, @Nullable StackTraceElement[] stackTrace)
|
||||||
|
{
|
||||||
|
this.name = name;
|
||||||
|
this.renderCall = renderCall;
|
||||||
|
this.stackTrace = stackTrace;
|
||||||
|
}
|
||||||
|
|
||||||
|
//endregion
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//=========//
|
||||||
|
// running //
|
||||||
|
//=========//
|
||||||
|
//region
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
this.renderCall.run();
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
if (ExceptionUtil.isShutdownException(e))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
RuntimeException error = new RuntimeException("Uncaught Exception during GL call execution. StackTrace: ["+(this.stackTrace != null ? "Present" : "Missing")+"] Error: ["+e.getMessage()+"]", e);
|
||||||
|
if (this.stackTrace != null)
|
||||||
|
{
|
||||||
|
error.setStackTrace(this.stackTrace);
|
||||||
|
}
|
||||||
|
LOGGER.error("[" + Thread.currentThread().getName() + "] ran into an unexpected error running a GL call, Error: ["+ e.getMessage() +"].", error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//endregion
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//================//
|
||||||
|
// base overrides //
|
||||||
|
//================//
|
||||||
|
//region
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() { return this.name; }
|
||||||
|
|
||||||
|
//endregion
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//endregion
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
+126
-124
@@ -46,7 +46,6 @@ import java.util.*;
|
|||||||
import java.util.concurrent.RejectedExecutionException;
|
import java.util.concurrent.RejectedExecutionException;
|
||||||
import java.util.concurrent.ThreadPoolExecutor;
|
import java.util.concurrent.ThreadPoolExecutor;
|
||||||
import java.util.concurrent.locks.ReentrantLock;
|
import java.util.concurrent.locks.ReentrantLock;
|
||||||
import java.util.function.Predicate;
|
|
||||||
|
|
||||||
public class BeaconRenderHandler
|
public class BeaconRenderHandler
|
||||||
{
|
{
|
||||||
@@ -57,8 +56,6 @@ public class BeaconRenderHandler
|
|||||||
/** how often should we check if a beacon should be culled? */
|
/** how often should we check if a beacon should be culled? */
|
||||||
private static final int MAX_CULLING_FREQUENCY_IN_MS = 1_000;
|
private static final int MAX_CULLING_FREQUENCY_IN_MS = 1_000;
|
||||||
|
|
||||||
private static final Comparator<BeaconBeamDTO> NEGATIVE_BLOCKPOS_COMPARATOR = new NegativeInfiniteBlockPosComparator();
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
private final ReentrantLock updateLock = new ReentrantLock();
|
private final ReentrantLock updateLock = new ReentrantLock();
|
||||||
@@ -67,8 +64,6 @@ public class BeaconRenderHandler
|
|||||||
private final IDhApiRenderableBoxGroup activeBeaconBoxRenderGroup;
|
private final IDhApiRenderableBoxGroup activeBeaconBoxRenderGroup;
|
||||||
/** contains all beacons that could be rendered (including those that are being culled) */
|
/** contains all beacons that could be rendered (including those that are being culled) */
|
||||||
private final ArrayList<DhApiRenderableBox> fullBeaconBoxList = new ArrayList<>();
|
private final ArrayList<DhApiRenderableBox> fullBeaconBoxList = new ArrayList<>();
|
||||||
/** contains all beacons that could be rendered */
|
|
||||||
private final HashSet<DhBlockPos> fullBeaconBlockPosSet = new HashSet<>();
|
|
||||||
|
|
||||||
private boolean cullingThreadRunning = false;
|
private boolean cullingThreadRunning = false;
|
||||||
private boolean updateRenderDataNextFrame = false;
|
private boolean updateRenderDataNextFrame = false;
|
||||||
@@ -96,125 +91,11 @@ public class BeaconRenderHandler
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
//=================//
|
//===============//
|
||||||
// render handling //
|
// before render //
|
||||||
//=================//
|
//===============//
|
||||||
//region
|
//region
|
||||||
|
|
||||||
public void startRenderingBeacons(ArrayList<BeaconBeamDTO> beaconList, byte detailLevel)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
this.updateLock.lock();
|
|
||||||
|
|
||||||
|
|
||||||
// how wide should each beacon be?
|
|
||||||
int beaconBlockWidth = 1;
|
|
||||||
if (Config.Client.Advanced.Graphics.GenericRendering.expandDistantBeacons.get())
|
|
||||||
{
|
|
||||||
beaconBlockWidth = DhSectionPos.getBlockWidth(detailLevel);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
ArrayList<BeaconBeamDTO> sortedBeaconList = new ArrayList<>(beaconList);
|
|
||||||
|
|
||||||
// merge distant beams if requested
|
|
||||||
if (Config.Client.Advanced.Graphics.GenericRendering.expandDistantBeacons.get())
|
|
||||||
{
|
|
||||||
// sort beacons from neg inf -> pos inf
|
|
||||||
// so we can consistently merge adjacent beacons
|
|
||||||
sortedBeaconList.sort(NEGATIVE_BLOCKPOS_COMPARATOR);
|
|
||||||
|
|
||||||
// go through each beacon...
|
|
||||||
for (int outerIndex = 0; outerIndex < sortedBeaconList.size(); outerIndex++)
|
|
||||||
{
|
|
||||||
BeaconBeamDTO outerBeacon = sortedBeaconList.get(outerIndex);
|
|
||||||
DhBlockPos outerBlockPos = outerBeacon.blockPos;
|
|
||||||
|
|
||||||
// ...and remove any beacons that are within the block width to prevent overlaps
|
|
||||||
for (int mergeIndex = outerIndex + 1; mergeIndex < sortedBeaconList.size(); mergeIndex++)
|
|
||||||
{
|
|
||||||
BeaconBeamDTO beaconToMerge = sortedBeaconList.get(mergeIndex);
|
|
||||||
DhBlockPos mergeBlockPos = beaconToMerge.blockPos;
|
|
||||||
|
|
||||||
int xDiff = mergeBlockPos.getX() - outerBlockPos.getX();
|
|
||||||
int zDiff = mergeBlockPos.getZ() - outerBlockPos.getZ();
|
|
||||||
|
|
||||||
// merge (remove) this beacon if
|
|
||||||
// it's close to the outer beacon
|
|
||||||
if (xDiff < beaconBlockWidth
|
|
||||||
&& zDiff < beaconBlockWidth)
|
|
||||||
{
|
|
||||||
sortedBeaconList.remove(mergeIndex);
|
|
||||||
mergeIndex--; // minus 1 so we don't go past the end of the array when incrementing in the for loop up top
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
//LOGGER.info("startRenderingBeacons ["+sortedBeaconList+"]");
|
|
||||||
|
|
||||||
// add each beacon to the renderer
|
|
||||||
for (int i = 0; i < sortedBeaconList.size(); i++)
|
|
||||||
{
|
|
||||||
BeaconBeamDTO beacon = sortedBeaconList.get(i);
|
|
||||||
if (!this.fullBeaconBlockPosSet.add(beacon.blockPos))
|
|
||||||
{
|
|
||||||
// skip already present beacons
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
int maxBeaconBeamHeight = Config.Client.Advanced.Graphics.GenericRendering.beaconRenderHeight.get();
|
|
||||||
DhApiRenderableBox beaconBox = new DhApiRenderableBox(
|
|
||||||
new DhApiVec3d(beacon.blockPos.getX(), beacon.blockPos.getY() + 1, beacon.blockPos.getZ()),
|
|
||||||
new DhApiVec3d(beacon.blockPos.getX() + beaconBlockWidth, maxBeaconBeamHeight, beacon.blockPos.getZ() + beaconBlockWidth),
|
|
||||||
beacon.color,
|
|
||||||
EDhApiBlockMaterial.ILLUMINATED
|
|
||||||
);
|
|
||||||
|
|
||||||
this.activeBeaconBoxRenderGroup.add(beaconBox);
|
|
||||||
this.fullBeaconBoxList.add(beaconBox);
|
|
||||||
this.activeBeaconBoxRenderGroup.triggerBoxChange();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
this.updateLock.unlock();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void stopRenderingBeaconsInRange(long pos)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
this.updateLock.lock();
|
|
||||||
|
|
||||||
Predicate<DhApiRenderableBox> removeBoxPredicate = (DhApiRenderableBox box) ->
|
|
||||||
{
|
|
||||||
DhBlockPos blockPos = new DhBlockPos((int)box.minPos.x, (int)box.minPos.y, (int)box.minPos.z);
|
|
||||||
boolean contains = DhSectionPos.contains(pos, blockPos);
|
|
||||||
//if (contains)
|
|
||||||
//{
|
|
||||||
// LOGGER.info("stopRenderingBeaconsInRange ["+DhSectionPos.toString(pos)+"] ["+blockPos+"]");
|
|
||||||
//}
|
|
||||||
return contains;
|
|
||||||
};
|
|
||||||
this.activeBeaconBoxRenderGroup.removeIf(removeBoxPredicate);
|
|
||||||
this.fullBeaconBoxList.removeIf(removeBoxPredicate);
|
|
||||||
|
|
||||||
this.fullBeaconBlockPosSet.removeIf((DhBlockPos blockPos) -> DhSectionPos.contains(pos, blockPos));
|
|
||||||
|
|
||||||
this.activeBeaconBoxRenderGroup.triggerBoxChange();
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
this.updateLock.unlock();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private void beforeRender(DhApiRenderParam renderEventParam)
|
private void beforeRender(DhApiRenderParam renderEventParam)
|
||||||
{
|
{
|
||||||
if (Config.Client.Advanced.Graphics.Culling.disableBeaconDistanceCulling.get())
|
if (Config.Client.Advanced.Graphics.Culling.disableBeaconDistanceCulling.get())
|
||||||
@@ -304,15 +185,136 @@ public class BeaconRenderHandler
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//==============//
|
||||||
|
// registration //
|
||||||
|
//==============//
|
||||||
|
//region
|
||||||
|
|
||||||
|
public void replaceRenderingBeacons(ArrayList<BeaconBeamWithWidth> beaconList)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
this.updateLock.lock();
|
||||||
|
|
||||||
|
ArrayList<BeaconBeamWithWidth> sortedBeaconList = new ArrayList<>(beaconList);
|
||||||
|
|
||||||
|
// merge distant beams if requested
|
||||||
|
if (Config.Client.Advanced.Graphics.GenericRendering.expandDistantBeacons.get())
|
||||||
|
{
|
||||||
|
// sort beacons from neg inf -> pos inf
|
||||||
|
// so we can consistently merge adjacent beacons
|
||||||
|
sortedBeaconList.sort(NegativeInfiniteBlockPosComparator.INSTANCE);
|
||||||
|
|
||||||
|
// go through each beacon...
|
||||||
|
for (int outerIndex = 0; outerIndex < sortedBeaconList.size(); outerIndex++)
|
||||||
|
{
|
||||||
|
BeaconBeamWithWidth outerBeacon = sortedBeaconList.get(outerIndex);
|
||||||
|
DhBlockPos outerBlockPos = outerBeacon.blockPos;
|
||||||
|
|
||||||
|
// ...and remove any beacons that are within the block width to prevent overlaps
|
||||||
|
for (int mergeIndex = outerIndex + 1; mergeIndex < sortedBeaconList.size(); mergeIndex++)
|
||||||
|
{
|
||||||
|
BeaconBeamWithWidth beaconToMerge = sortedBeaconList.get(mergeIndex);
|
||||||
|
DhBlockPos mergeBlockPos = beaconToMerge.blockPos;
|
||||||
|
|
||||||
|
int xDiff = mergeBlockPos.getX() - outerBlockPos.getX();
|
||||||
|
int zDiff = mergeBlockPos.getZ() - outerBlockPos.getZ();
|
||||||
|
|
||||||
|
// merge (remove) this beacon if
|
||||||
|
// it's close to the outer beacon
|
||||||
|
if (xDiff < beaconToMerge.beaconBlockWidth
|
||||||
|
&& zDiff < beaconToMerge.beaconBlockWidth)
|
||||||
|
{
|
||||||
|
sortedBeaconList.remove(mergeIndex);
|
||||||
|
mergeIndex--; // minus 1 so we don't go past the end of the array when incrementing in the for loop up top
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
this.activeBeaconBoxRenderGroup.clear();
|
||||||
|
this.fullBeaconBoxList.clear();
|
||||||
|
|
||||||
|
// add each beacon to the renderer
|
||||||
|
for (int i = 0; i < sortedBeaconList.size(); i++)
|
||||||
|
{
|
||||||
|
BeaconBeamWithWidth beacon = sortedBeaconList.get(i);
|
||||||
|
int maxBeaconBeamHeight = Config.Client.Advanced.Graphics.GenericRendering.beaconRenderHeight.get();
|
||||||
|
DhApiRenderableBox beaconBox = new DhApiRenderableBox(
|
||||||
|
new DhApiVec3d(beacon.blockPos.getX(), beacon.blockPos.getY() + 1, beacon.blockPos.getZ()),
|
||||||
|
new DhApiVec3d(beacon.blockPos.getX() + beacon.beaconBlockWidth, maxBeaconBeamHeight, beacon.blockPos.getZ() + beacon.beaconBlockWidth),
|
||||||
|
beacon.color,
|
||||||
|
EDhApiBlockMaterial.ILLUMINATED
|
||||||
|
);
|
||||||
|
|
||||||
|
this.activeBeaconBoxRenderGroup.add(beaconBox);
|
||||||
|
this.fullBeaconBoxList.add(beaconBox);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.activeBeaconBoxRenderGroup.triggerBoxChange();
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
this.updateLock.unlock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//endregion
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//================//
|
//================//
|
||||||
// helper classes //
|
// helper classes //
|
||||||
//================//
|
//================//
|
||||||
//region
|
//region
|
||||||
|
|
||||||
private static class NegativeInfiniteBlockPosComparator implements Comparator<BeaconBeamDTO>
|
public static class BeaconBeamWithWidth extends BeaconBeamDTO
|
||||||
{
|
{
|
||||||
|
public final int beaconBlockWidth;
|
||||||
|
|
||||||
|
public BeaconBeamWithWidth(BeaconBeamDTO beaconBeamDTO, byte lodDetailLevel)
|
||||||
|
{
|
||||||
|
super(beaconBeamDTO.blockPos, beaconBeamDTO.color);
|
||||||
|
|
||||||
|
|
||||||
|
// how wide should each beacon be?
|
||||||
|
if (Config.Client.Advanced.Graphics.GenericRendering.expandDistantBeacons.get())
|
||||||
|
{
|
||||||
|
this.beaconBlockWidth = DhSectionPos.getBlockWidth(lodDetailLevel);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
this.beaconBlockWidth = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int compare(BeaconBeamDTO beacon1, BeaconBeamDTO beacon2)
|
public boolean equals(Object obj)
|
||||||
|
{
|
||||||
|
if (obj == null
|
||||||
|
|| obj.getClass() != this.getClass())
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
BeaconBeamWithWidth that = (BeaconBeamWithWidth) obj;
|
||||||
|
if (that.beaconBlockWidth != this.beaconBlockWidth)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return super.equals(that);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class NegativeInfiniteBlockPosComparator implements Comparator<BeaconBeamWithWidth>
|
||||||
|
{
|
||||||
|
public static final NegativeInfiniteBlockPosComparator INSTANCE = new NegativeInfiniteBlockPosComparator();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int compare(BeaconBeamWithWidth beacon1, BeaconBeamWithWidth beacon2)
|
||||||
{
|
{
|
||||||
DhBlockPos blockPos1 = beacon1.blockPos;
|
DhBlockPos blockPos1 = beacon1.blockPos;
|
||||||
DhBlockPos blockPos2 = beacon2.blockPos;
|
DhBlockPos blockPos2 = beacon2.blockPos;
|
||||||
|
|||||||
+1
-15
@@ -96,9 +96,6 @@ public class CloudRenderHandler
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
private boolean disabledWarningLogged = false;
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//=============//
|
//=============//
|
||||||
// constructor //
|
// constructor //
|
||||||
@@ -287,7 +284,7 @@ public class CloudRenderHandler
|
|||||||
CloudParams cloudParams = new CloudParams(textureWidth, x, z);
|
CloudParams cloudParams = new CloudParams(textureWidth, x, z);
|
||||||
boxGroup.setPreRenderFunc((renderParam) -> this.preRender(renderParam, cloudParams));
|
boxGroup.setPreRenderFunc((renderParam) -> this.preRender(renderParam, cloudParams));
|
||||||
|
|
||||||
renderer.add(boxGroup);
|
this.renderer.add(boxGroup);
|
||||||
this.boxGroupByOffset[x+CLOUD_INSTANCE_RADIUS_COUNT][z+CLOUD_INSTANCE_RADIUS_COUNT] = boxGroup;
|
this.boxGroupByOffset[x+CLOUD_INSTANCE_RADIUS_COUNT][z+CLOUD_INSTANCE_RADIUS_COUNT] = boxGroup;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -319,17 +316,6 @@ public class CloudRenderHandler
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
//if (!this.renderer.getInstancedRenderingAvailable())
|
|
||||||
//{
|
|
||||||
// if (!this.disabledWarningLogged)
|
|
||||||
// {
|
|
||||||
// this.disabledWarningLogged = true;
|
|
||||||
// LOGGER.warn("Instanced rendering unavailable, cloud rendering disabled.");
|
|
||||||
// }
|
|
||||||
// boxGroup.setActive(false);
|
|
||||||
// return;
|
|
||||||
//}
|
|
||||||
|
|
||||||
IClientLevelWrapper clientLevelWrapper = this.level.getClientLevelWrapper();
|
IClientLevelWrapper clientLevelWrapper = this.level.getClientLevelWrapper();
|
||||||
if (clientLevelWrapper == null)
|
if (clientLevelWrapper == null)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -101,7 +101,7 @@ public class LodRenderer
|
|||||||
* otherwise it will only render opaque LODs.
|
* otherwise it will only render opaque LODs.
|
||||||
*/
|
*/
|
||||||
public void render(RenderParams renderParams, IProfilerWrapper profiler)
|
public void render(RenderParams renderParams, IProfilerWrapper profiler)
|
||||||
{ this.renderLodPass(renderParams, profiler, false); }
|
{ this.renderTerrain(renderParams, profiler, false); }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This method is designed for Iris to be able
|
* This method is designed for Iris to be able
|
||||||
@@ -110,9 +110,9 @@ public class LodRenderer
|
|||||||
* but shouldn't be activated as per deferWaterRendering.
|
* but shouldn't be activated as per deferWaterRendering.
|
||||||
*/
|
*/
|
||||||
public void renderDeferred(RenderParams renderParams, IProfilerWrapper profiler)
|
public void renderDeferred(RenderParams renderParams, IProfilerWrapper profiler)
|
||||||
{ this.renderLodPass(renderParams, profiler, true); }
|
{ this.renderTerrain(renderParams, profiler, true); }
|
||||||
|
|
||||||
private void renderLodPass(RenderParams renderParams, IProfilerWrapper profiler, boolean runningDeferredPass)
|
private void renderTerrain(RenderParams renderParams, IProfilerWrapper profiler, boolean runningDeferredPass)
|
||||||
{
|
{
|
||||||
//====================//
|
//====================//
|
||||||
// validate rendering //
|
// validate rendering //
|
||||||
@@ -201,7 +201,7 @@ public class LodRenderer
|
|||||||
// opaque LODs
|
// opaque LODs
|
||||||
profiler.popPush("LOD Opaque");
|
profiler.popPush("LOD Opaque");
|
||||||
|
|
||||||
this.renderLodPass(this.terrainRenderer, renderBufferHandler, renderParams, /*opaquePass*/ true, profiler);
|
this.renderTerrain(this.terrainRenderer, renderBufferHandler, renderParams, /*opaquePass*/ true, profiler);
|
||||||
|
|
||||||
// custom objects with SSAO
|
// custom objects with SSAO
|
||||||
if (Config.Client.Advanced.Graphics.GenericRendering.enableGenericRendering.get())
|
if (Config.Client.Advanced.Graphics.GenericRendering.enableGenericRendering.get())
|
||||||
@@ -211,7 +211,7 @@ public class LodRenderer
|
|||||||
}
|
}
|
||||||
|
|
||||||
// SSAO
|
// SSAO
|
||||||
if (Config.Client.Advanced.Graphics.Ssao.enableSsao.get())
|
if (Config.Client.Advanced.Graphics.enableSsao.get())
|
||||||
{
|
{
|
||||||
profiler.popPush("LOD SSAO");
|
profiler.popPush("LOD SSAO");
|
||||||
this.ssaoRenderer.render(renderParams);
|
this.ssaoRenderer.render(renderParams);
|
||||||
@@ -229,7 +229,7 @@ public class LodRenderer
|
|||||||
&& Config.Client.Advanced.Graphics.Quality.transparency.get().transparencyEnabled)
|
&& Config.Client.Advanced.Graphics.Quality.transparency.get().transparencyEnabled)
|
||||||
{
|
{
|
||||||
profiler.popPush("LOD Transparent");
|
profiler.popPush("LOD Transparent");
|
||||||
this.renderLodPass(this.terrainRenderer, renderBufferHandler, renderParams, /*opaquePass*/ false, profiler);
|
this.renderTerrain(this.terrainRenderer, renderBufferHandler, renderParams, /*opaquePass*/ false, profiler);
|
||||||
}
|
}
|
||||||
|
|
||||||
// far plane clip fading
|
// far plane clip fading
|
||||||
@@ -287,7 +287,7 @@ public class LodRenderer
|
|||||||
if (Config.Client.Advanced.Graphics.Quality.transparency.get().transparencyEnabled)
|
if (Config.Client.Advanced.Graphics.Quality.transparency.get().transparencyEnabled)
|
||||||
{
|
{
|
||||||
profiler.popPush("LOD Transparent");
|
profiler.popPush("LOD Transparent");
|
||||||
this.renderLodPass(this.terrainRenderer, renderBufferHandler, renderParams, /*opaquePass*/ false, profiler);
|
this.renderTerrain(this.terrainRenderer, renderBufferHandler, renderParams, /*opaquePass*/ false, profiler);
|
||||||
|
|
||||||
|
|
||||||
if (Config.Client.Advanced.Graphics.Fog.enableDhFog.get()
|
if (Config.Client.Advanced.Graphics.Fog.enableDhFog.get()
|
||||||
@@ -327,7 +327,7 @@ public class LodRenderer
|
|||||||
//===============//
|
//===============//
|
||||||
//region
|
//region
|
||||||
|
|
||||||
private void renderLodPass(IDhTerrainRenderer lodRenderer, RenderBufferHandler lodBufferHandler, RenderParams renderEventParam, boolean opaquePass, IProfilerWrapper profilerWrapper)
|
private void renderTerrain(IDhTerrainRenderer terrainRenderer, RenderBufferHandler lodBufferHandler, RenderParams renderEventParam, boolean opaquePass, IProfilerWrapper profilerWrapper)
|
||||||
{
|
{
|
||||||
//===========//
|
//===========//
|
||||||
// rendering //
|
// rendering //
|
||||||
@@ -338,7 +338,7 @@ public class LodRenderer
|
|||||||
SortedArraySet<LodBufferContainer> lodBufferContainer = lodBufferHandler.getColumnRenderBuffers();
|
SortedArraySet<LodBufferContainer> lodBufferContainer = lodBufferHandler.getColumnRenderBuffers();
|
||||||
if (lodBufferContainer != null)
|
if (lodBufferContainer != null)
|
||||||
{
|
{
|
||||||
lodRenderer.render(renderEventParam, opaquePass, lodBufferContainer, profilerWrapper);
|
terrainRenderer.render(renderEventParam, opaquePass, lodBufferContainer, profilerWrapper);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+4
-2
@@ -56,7 +56,7 @@ public class RenderableBoxGroup
|
|||||||
|
|
||||||
public boolean active = true;
|
public boolean active = true;
|
||||||
public boolean ssaoEnabled = true;
|
public boolean ssaoEnabled = true;
|
||||||
private boolean vertexDataDirty = true;
|
private boolean vertexDataDirty = false;
|
||||||
|
|
||||||
public byte skyLight = LodUtil.MAX_MC_LIGHT;
|
public byte skyLight = LodUtil.MAX_MC_LIGHT;
|
||||||
public byte blockLight = LodUtil.MIN_MC_LIGHT;
|
public byte blockLight = LodUtil.MIN_MC_LIGHT;
|
||||||
@@ -198,6 +198,7 @@ public class RenderableBoxGroup
|
|||||||
if (this.altVertexBufferContainer.getState() == IDhGenericObjectVertexBufferContainer.EState.READY_TO_UPLOAD)
|
if (this.altVertexBufferContainer.getState() == IDhGenericObjectVertexBufferContainer.EState.READY_TO_UPLOAD)
|
||||||
{
|
{
|
||||||
this.altVertexBufferContainer.uploadDataToGpu();
|
this.altVertexBufferContainer.uploadDataToGpu();
|
||||||
|
this.altVertexBufferContainer.setState(IDhGenericObjectVertexBufferContainer.EState.RENDER);
|
||||||
|
|
||||||
// swap VBO references for rendering
|
// swap VBO references for rendering
|
||||||
IDhGenericObjectVertexBufferContainer temp = this.vertexBufferContainer;
|
IDhGenericObjectVertexBufferContainer temp = this.vertexBufferContainer;
|
||||||
@@ -248,6 +249,7 @@ public class RenderableBoxGroup
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
this.altVertexBufferContainer.updateVertexData(this.uploadBoxList);
|
this.altVertexBufferContainer.updateVertexData(this.uploadBoxList);
|
||||||
|
this.altVertexBufferContainer.setState(IDhGenericObjectVertexBufferContainer.EState.READY_TO_UPLOAD);
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
@@ -346,7 +348,7 @@ public class RenderableBoxGroup
|
|||||||
@Override
|
@Override
|
||||||
public void close()
|
public void close()
|
||||||
{
|
{
|
||||||
RenderThreadTaskHandler.INSTANCE.queueRunningOnRenderThread(() ->
|
RenderThreadTaskHandler.INSTANCE.queueRunningOnRenderThread("RenderBoxGroup Close", () ->
|
||||||
{
|
{
|
||||||
this.vertexBufferContainer.close();
|
this.vertexBufferContainer.close();
|
||||||
this.altVertexBufferContainer.close();
|
this.altVertexBufferContainer.close();
|
||||||
|
|||||||
+35
@@ -0,0 +1,35 @@
|
|||||||
|
package com.seibel.distanthorizons.core.render.renderer;
|
||||||
|
|
||||||
|
import com.seibel.distanthorizons.core.config.types.ConfigEntry;
|
||||||
|
import com.seibel.distanthorizons.core.render.RenderParams;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used to prevent attempts to render debug stuff on the server side.
|
||||||
|
*/
|
||||||
|
public class StubDebugWireframeRenderer extends AbstractDebugWireframeRenderer
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
public void render(RenderParams renderParams) { }
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void renderBox(Box box) { }
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void makeParticle(BoxParticle particle) { }
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void register(IDebugRenderable renderable, ConfigEntry<Boolean> config) { }
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addRenderer(IDebugRenderable renderable, ConfigEntry<Boolean> config) { }
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void unregister(IDebugRenderable renderable, ConfigEntry<Boolean> config) { }
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void removeRenderer(IDebugRenderable renderable, ConfigEntry<Boolean> config) { }
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void clearRenderables() { }
|
||||||
|
|
||||||
|
}
|
||||||
@@ -72,6 +72,20 @@ public class BeaconBeamDTO implements IBaseDTO<DhBlockPos>, INetworkObject
|
|||||||
@Override
|
@Override
|
||||||
public DhBlockPos getKey() { return this.blockPos; }
|
public DhBlockPos getKey() { return this.blockPos; }
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object obj)
|
||||||
|
{
|
||||||
|
if (obj == null
|
||||||
|
|| obj.getClass() != this.getClass())
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
BeaconBeamDTO that = (BeaconBeamDTO)obj;
|
||||||
|
return this.blockPos.equals(that.blockPos)
|
||||||
|
&& this.color.equals(that.color);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void close()
|
public void close()
|
||||||
{ /* no closing needed */ }
|
{ /* no closing needed */ }
|
||||||
|
|||||||
@@ -25,9 +25,11 @@ import com.seibel.distanthorizons.core.sql.DatabaseUpdater;
|
|||||||
import com.seibel.distanthorizons.core.sql.DbConnectionClosedException;
|
import com.seibel.distanthorizons.core.sql.DbConnectionClosedException;
|
||||||
import com.seibel.distanthorizons.core.sql.dto.IBaseDTO;
|
import com.seibel.distanthorizons.core.sql.dto.IBaseDTO;
|
||||||
import com.seibel.distanthorizons.core.sql.repo.phantoms.AutoClosableTrackingWrapper;
|
import com.seibel.distanthorizons.core.sql.repo.phantoms.AutoClosableTrackingWrapper;
|
||||||
|
import com.seibel.distanthorizons.core.util.ExceptionUtil;
|
||||||
import com.seibel.distanthorizons.core.util.KeyedLockContainer;
|
import com.seibel.distanthorizons.core.util.KeyedLockContainer;
|
||||||
import com.seibel.distanthorizons.coreapi.ModInfo;
|
import com.seibel.distanthorizons.coreapi.ModInfo;
|
||||||
import com.seibel.distanthorizons.core.logging.DhLogger;
|
import com.seibel.distanthorizons.core.logging.DhLogger;
|
||||||
|
import com.seibel.distanthorizons.coreapi.util.StringUtil;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
@@ -496,20 +498,12 @@ public abstract class AbstractDhRepo<TKey, TDTO extends IBaseDTO<TKey>> implemen
|
|||||||
Connection connection = CONNECTIONS_BY_CONNECTION_STRING.remove(connectionString);
|
Connection connection = CONNECTIONS_BY_CONNECTION_STRING.remove(connectionString);
|
||||||
if (connection != null)
|
if (connection != null)
|
||||||
{
|
{
|
||||||
|
// don't try closing an already closed connection
|
||||||
if (!connection.isClosed())
|
if (!connection.isClosed())
|
||||||
{
|
{
|
||||||
LOGGER.info("Closing database connection: [" + connectionString + "]");
|
LOGGER.info("Closing database connection: [" + connectionString + "]");
|
||||||
connection.close();
|
connection.close();
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
// these warnings can be ignored in release builds, as long as the connection is closed it doesn't really matter
|
|
||||||
// TODO fix duplicate closes
|
|
||||||
if (ModInfo.IS_DEV_BUILD)
|
|
||||||
{
|
|
||||||
LOGGER.warn("Attempting to close already closed database connection: [" + connectionString + "]");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch(SQLException e)
|
catch(SQLException e)
|
||||||
@@ -560,22 +554,13 @@ public abstract class AbstractDhRepo<TKey, TDTO extends IBaseDTO<TKey>> implemen
|
|||||||
LOGGER.warn(stringBuilder.toString());
|
LOGGER.warn(stringBuilder.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// don't try closing an already closed connection
|
||||||
if (!this.connection.isClosed())
|
if (!this.connection.isClosed())
|
||||||
{
|
{
|
||||||
LOGGER.info("Closing database connection: [" + this.connectionString + "]...");
|
LOGGER.info("Closing database connection: [" + this.connectionString + "]...");
|
||||||
this.connection.close();
|
this.connection.close();
|
||||||
LOGGER.info("Finished closing database connection: [" + this.connectionString + "]");
|
LOGGER.info("Finished closing database connection: [" + this.connectionString + "]");
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
// these warnings can be ignored in release builds, as long as the connection is closed it doesn't really matter
|
|
||||||
// TODO fix duplicate closes
|
|
||||||
if (ModInfo.IS_DEV_BUILD)
|
|
||||||
{
|
|
||||||
LOGGER.warn("Attempting to close already closed database connection: [" + this.connectionString + "]");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
ACTIVE_CONNECTION_STRINGS_BY_REPO.remove(this);
|
ACTIVE_CONNECTION_STRINGS_BY_REPO.remove(this);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,12 +19,17 @@
|
|||||||
|
|
||||||
package com.seibel.distanthorizons.core.util;
|
package com.seibel.distanthorizons.core.util;
|
||||||
|
|
||||||
|
import com.seibel.distanthorizons.api.DhApi;
|
||||||
import com.seibel.distanthorizons.api.objects.math.DhApiMat4f;
|
import com.seibel.distanthorizons.api.objects.math.DhApiMat4f;
|
||||||
import com.seibel.distanthorizons.core.api.internal.ClientApi;
|
import com.seibel.distanthorizons.core.api.internal.ClientApi;
|
||||||
import com.seibel.distanthorizons.core.config.Config;
|
import com.seibel.distanthorizons.core.config.Config;
|
||||||
|
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.DhLoggerBuilder;
|
||||||
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.modAccessor.IIrisAccessor;
|
||||||
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper;
|
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper;
|
||||||
import com.seibel.distanthorizons.coreapi.util.MathUtil;
|
import com.seibel.distanthorizons.coreapi.util.MathUtil;
|
||||||
import com.seibel.distanthorizons.core.util.math.Mat4f;
|
import com.seibel.distanthorizons.core.util.math.Mat4f;
|
||||||
@@ -35,8 +40,11 @@ import com.seibel.distanthorizons.core.util.math.Mat4f;
|
|||||||
*/
|
*/
|
||||||
public class RenderUtil
|
public class RenderUtil
|
||||||
{
|
{
|
||||||
|
private static final DhLogger LOGGER = new DhLoggerBuilder().maxCountPerSecond(1).build();
|
||||||
|
|
||||||
private static final IMinecraftClientWrapper MC = SingletonInjector.INSTANCE.get(IMinecraftClientWrapper.class);
|
private static final IMinecraftClientWrapper MC = SingletonInjector.INSTANCE.get(IMinecraftClientWrapper.class);
|
||||||
private static final IMinecraftRenderWrapper MC_RENDER = SingletonInjector.INSTANCE.get(IMinecraftRenderWrapper.class);
|
private static final IMinecraftRenderWrapper MC_RENDER = SingletonInjector.INSTANCE.get(IMinecraftRenderWrapper.class);
|
||||||
|
private static final IIrisAccessor IRIS_ACCESSOR = ModAccessorInjector.INSTANCE.get(IIrisAccessor.class);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* all speeds are measured in blocks per second
|
* all speeds are measured in blocks per second
|
||||||
@@ -79,7 +87,7 @@ public class RenderUtil
|
|||||||
nearClipDist = Math.min(nearClipDist, 7.5f);
|
nearClipDist = Math.min(nearClipDist, 7.5f);
|
||||||
}
|
}
|
||||||
|
|
||||||
float farClipDist = (float) RenderUtil.getFarClipPlaneDistanceInBlocks();
|
float farClipDist = RenderUtil.getFarClipPlaneDistanceInBlocks();
|
||||||
|
|
||||||
// Create a copy of the current matrix, so it won't be modified.
|
// Create a copy of the current matrix, so it won't be modified.
|
||||||
Mat4f lodProj = new Mat4f(mcProjMat);
|
Mat4f lodProj = new Mat4f(mcProjMat);
|
||||||
@@ -245,12 +253,31 @@ public class RenderUtil
|
|||||||
|
|
||||||
//region
|
//region
|
||||||
|
|
||||||
public static int getFarClipPlaneDistanceInBlocks()
|
public static float getFarClipPlaneDistanceInBlocks()
|
||||||
{
|
{
|
||||||
int lodChunkDist = Config.Client.Advanced.Graphics.Quality.lodChunkRenderDistanceRadius.get();
|
if (IRIS_ACCESSOR != null)
|
||||||
int lodBlockDist = lodChunkDist * LodUtil.CHUNK_WIDTH;
|
{
|
||||||
// * 2 to prevent clipping when high above the world
|
// Iris doesn't use the far clip plane DH generates, instead
|
||||||
return (lodBlockDist + LodUtil.REGION_WIDTH) * 2;
|
// they use a manually generated one, which causes problems.
|
||||||
|
// This is a hack so DH's far clip plane matches up with what Iris thinks it is,
|
||||||
|
// fixing projection/depth mapping.
|
||||||
|
// https://github.com/IrisShaders/Iris/issues/2534
|
||||||
|
|
||||||
|
int lodChunkDist = DhApi.Delayed.configs.graphics().chunkRenderDistance().getValue();
|
||||||
|
int lodBlockDist = lodChunkDist * 16; /* 16 = chunk width in blocks */
|
||||||
|
// sqrt 2 to prevent the corners from being cut off
|
||||||
|
return (float) ((lodBlockDist + 512 /* 512 = region width in blocks */) * Math.sqrt(2));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Current DH logic
|
||||||
|
// uses a farther depth to help when far above the world
|
||||||
|
|
||||||
|
int lodChunkDist = Config.Client.Advanced.Graphics.Quality.lodChunkRenderDistanceRadius.get();
|
||||||
|
int lodBlockDist = lodChunkDist * LodUtil.CHUNK_WIDTH;
|
||||||
|
// * 2 to prevent clipping when high above the world
|
||||||
|
return (lodBlockDist + LodUtil.REGION_WIDTH) * 2;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//endregion
|
//endregion
|
||||||
|
|||||||
@@ -17,12 +17,15 @@ public class RollingAverage
|
|||||||
private int index = 0;
|
private int index = 0;
|
||||||
private double sum = 0.0;
|
private double sum = 0.0;
|
||||||
private final Lock arrayLock = new ReentrantLock();
|
private final Lock arrayLock = new ReentrantLock();
|
||||||
|
/** how many items have been added to this average over its lifetime */
|
||||||
|
private long lifetimeCount = 0;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//=============//
|
//=============//
|
||||||
// constructor //
|
// constructor //
|
||||||
//=============//
|
//=============//
|
||||||
|
//region
|
||||||
|
|
||||||
public RollingAverage(int size)
|
public RollingAverage(int size)
|
||||||
{
|
{
|
||||||
@@ -35,11 +38,14 @@ public class RollingAverage
|
|||||||
this.values = new double[size];
|
this.values = new double[size];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//endregion
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//=======//
|
//=======//
|
||||||
// input //
|
// input //
|
||||||
//=======//
|
//=======//
|
||||||
|
//region
|
||||||
|
|
||||||
public void add(double value)
|
public void add(double value)
|
||||||
{
|
{
|
||||||
@@ -56,6 +62,8 @@ public class RollingAverage
|
|||||||
this.index = (this.index + 1) % this.maxSize;
|
this.index = (this.index + 1) % this.maxSize;
|
||||||
|
|
||||||
this.currentSize = Math.max(this.index+1, this.currentSize);
|
this.currentSize = Math.max(this.index+1, this.currentSize);
|
||||||
|
|
||||||
|
this.lifetimeCount++;
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
@@ -71,6 +79,7 @@ public class RollingAverage
|
|||||||
this.sum = 0;
|
this.sum = 0;
|
||||||
this.index = 0;
|
this.index = 0;
|
||||||
this.currentSize = 0;
|
this.currentSize = 0;
|
||||||
|
this.lifetimeCount = 0;
|
||||||
Arrays.fill(this.values, 0);
|
Arrays.fill(this.values, 0);
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
@@ -79,11 +88,14 @@ public class RollingAverage
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//endregion
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//========//
|
//========//
|
||||||
// output //
|
// output //
|
||||||
//========//
|
//========//
|
||||||
|
//region
|
||||||
|
|
||||||
/** Gets the current rolling average. */
|
/** Gets the current rolling average. */
|
||||||
public double getAverage()
|
public double getAverage()
|
||||||
@@ -101,14 +113,23 @@ public class RollingAverage
|
|||||||
/** rounded to two decimals*/
|
/** rounded to two decimals*/
|
||||||
public String getAverageRoundedString() { return String.format("%.2f", this.getAverage()); }
|
public String getAverageRoundedString() { return String.format("%.2f", this.getAverage()); }
|
||||||
|
|
||||||
|
/** how many items have been added to the rolling average since it's last {@link RollingAverage#clear()} */
|
||||||
|
public long getLifetimeCount() { return this.lifetimeCount; }
|
||||||
|
|
||||||
|
//endregion
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//================//
|
//================//
|
||||||
// base overrides //
|
// base overrides //
|
||||||
//================//
|
//================//
|
||||||
|
//region
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() { return "avg: ["+this.getAverageRoundedString()+"], count: ["+this.currentSize+"], max count: ["+this.maxSize+"]."; }
|
public String toString() { return "avg: ["+this.getAverageRoundedString()+"], count: ["+this.currentSize+"], max count: ["+this.maxSize+"]."; }
|
||||||
|
|
||||||
|
//endregion
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+19
-9
@@ -185,13 +185,22 @@ public class PhantomArrayListPool
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
// this reference is pointing to null,
|
// this reference is pointing to null,
|
||||||
// the checkout must have been garbage collected,
|
// the checkout was garbage collected
|
||||||
// that means we don't have enough memory
|
|
||||||
if (!lowMemoryWarningLogged)
|
if (!lowMemoryWarningLogged)
|
||||||
{
|
{
|
||||||
lowMemoryWarningLogged = true;
|
// Complain if there isn't much free ram.
|
||||||
|
// Some garbage collectors may remove our soft references unnecessarily
|
||||||
|
// even when there is free space, and this should prevent the warning from
|
||||||
|
// popping up unnecessarily.
|
||||||
|
|
||||||
String message = MinecraftTextFormat.ORANGE + "Distant Horizons: Insufficient memory detected." + MinecraftTextFormat.CLEAR_FORMATTING + "\n" +
|
long freeMemoryBytes = Runtime.getRuntime().freeMemory();
|
||||||
|
|
||||||
|
long expectedFreeMemoryBytes = 1_073_741_824 /* 1 Gibibyte */ / 8; // 128 MibiBytes
|
||||||
|
if (freeMemoryBytes < expectedFreeMemoryBytes)
|
||||||
|
{
|
||||||
|
lowMemoryWarningLogged = true;
|
||||||
|
|
||||||
|
String message = MinecraftTextFormat.ORANGE + "Distant Horizons: Insufficient memory detected." + MinecraftTextFormat.CLEAR_FORMATTING + "\n" +
|
||||||
"This may cause stuttering or crashing. \n" +
|
"This may cause stuttering or crashing. \n" +
|
||||||
"Potential causes: \n" +
|
"Potential causes: \n" +
|
||||||
"1. your allocated memory isn't high enough \n" +
|
"1. your allocated memory isn't high enough \n" +
|
||||||
@@ -199,10 +208,11 @@ public class PhantomArrayListPool
|
|||||||
"3. your DH quality preset is too high \n" +
|
"3. your DH quality preset is too high \n" +
|
||||||
"4. you have other memory hungry mod(s)";
|
"4. you have other memory hungry mod(s)";
|
||||||
|
|
||||||
LOGGER.warn(message);
|
LOGGER.warn(message);
|
||||||
if (Config.Common.Logging.Warning.showPoolInsufficientMemoryWarning.get())
|
if (Config.Common.Logging.Warning.showPoolInsufficientMemoryWarning.get())
|
||||||
{
|
{
|
||||||
ClientApi.INSTANCE.showChatMessageNextFrame(message);
|
ClientApi.INSTANCE.showChatMessageNextFrame(message);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -319,7 +329,7 @@ public class PhantomArrayListPool
|
|||||||
pool.returnCheckout(checkout);
|
pool.returnCheckout(checkout);
|
||||||
|
|
||||||
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);
|
putAndIncrementTrackingString(checkout.allocationStackTrace, allocationStackTraceCountPairList);
|
||||||
}
|
}
|
||||||
|
|||||||
+11
@@ -86,6 +86,17 @@ public class QuadNode<T>
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/** @return the number of non-null direct child nodes */
|
||||||
|
public int getDirectChildCount()
|
||||||
|
{
|
||||||
|
int count = 0;
|
||||||
|
if (this.nwChild != null) { count++; }
|
||||||
|
if (this.neChild != null) { count++; }
|
||||||
|
if (this.swChild != null) { count++; }
|
||||||
|
if (this.seChild != null) { count++; }
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Use {@link QuadNode#getNonNullChildCount()} if you want the number of non-null child values.
|
* Use {@link QuadNode#getNonNullChildCount()} if you want the number of non-null child values.
|
||||||
*
|
*
|
||||||
|
|||||||
+7
-7
@@ -179,13 +179,13 @@ public class ThreadPoolUtil
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
PriorityTaskPicker.Executor executor = getRenderLoadingExecutor();
|
//PriorityTaskPicker.Executor executor = getRenderLoadingExecutor();
|
||||||
if (executor != null
|
//if (executor != null
|
||||||
&& executor.getQueueSize() > 0)
|
// && executor.getQueueSize() > 0)
|
||||||
{
|
//{
|
||||||
// pause if LODs are being loaded for rendering
|
// // pause if LODs are being loaded for rendering
|
||||||
return false;
|
// return false;
|
||||||
}
|
//}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,13 +21,20 @@ package com.seibel.distanthorizons.core.world;
|
|||||||
|
|
||||||
import com.seibel.distanthorizons.core.api.internal.ClientApi;
|
import com.seibel.distanthorizons.core.api.internal.ClientApi;
|
||||||
import com.seibel.distanthorizons.core.enums.MinecraftTextFormat;
|
import com.seibel.distanthorizons.core.enums.MinecraftTextFormat;
|
||||||
|
import com.seibel.distanthorizons.core.generation.PregenManager;
|
||||||
import com.seibel.distanthorizons.core.level.DhServerLevel;
|
import com.seibel.distanthorizons.core.level.DhServerLevel;
|
||||||
import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper;
|
import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper;
|
||||||
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IServerLevelWrapper;
|
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IServerLevelWrapper;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
|
||||||
public class DhServerWorld extends AbstractDhServerWorld<DhServerLevel>
|
public class DhServerWorld extends AbstractDhServerWorld<DhServerLevel>
|
||||||
{
|
{
|
||||||
|
private final PregenManager pregenManager = new PregenManager();
|
||||||
|
public PregenManager getPregenManager() { return this.pregenManager; }
|
||||||
|
|
||||||
|
|
||||||
//==============//
|
//==============//
|
||||||
// constructors //
|
// constructors //
|
||||||
//==============//
|
//==============//
|
||||||
@@ -88,4 +95,17 @@ public class DhServerWorld extends AbstractDhServerWorld<DhServerLevel>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close()
|
||||||
|
{
|
||||||
|
CompletableFuture<Void> runningPregen = this.pregenManager.getRunningPregen();
|
||||||
|
if (runningPregen != null)
|
||||||
|
{
|
||||||
|
LOGGER.info("Stopping the running pregen task.");
|
||||||
|
runningPregen.cancel(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
super.close();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+4
-5
@@ -65,6 +65,7 @@ public interface IWrapperFactory extends IDhApiWrapperFactory, IBindable
|
|||||||
|
|
||||||
IBlockStateWrapper deserializeBlockStateWrapper(String str, ILevelWrapper levelWrapper) throws IOException;
|
IBlockStateWrapper deserializeBlockStateWrapper(String str, ILevelWrapper levelWrapper) throws IOException;
|
||||||
IBlockStateWrapper getAirBlockStateWrapper();
|
IBlockStateWrapper getAirBlockStateWrapper();
|
||||||
|
IBlockStateWrapper getWaterBlockStateWrapper(ILevelWrapper levelWrapper);
|
||||||
default IBlockStateWrapper deserializeBlockStateWrapperOrGetDefault(String str, ILevelWrapper levelWrapper)
|
default IBlockStateWrapper deserializeBlockStateWrapperOrGetDefault(String str, ILevelWrapper levelWrapper)
|
||||||
{
|
{
|
||||||
IBlockStateWrapper blockState;
|
IBlockStateWrapper blockState;
|
||||||
@@ -92,10 +93,10 @@ public interface IWrapperFactory extends IDhApiWrapperFactory, IBindable
|
|||||||
*/
|
*/
|
||||||
ObjectOpenHashSet<IBlockStateWrapper> getRendererIgnoredCaveBlocks(ILevelWrapper levelWrapper);
|
ObjectOpenHashSet<IBlockStateWrapper> getRendererIgnoredCaveBlocks(ILevelWrapper levelWrapper);
|
||||||
|
|
||||||
|
ObjectOpenHashSet<IBlockStateWrapper> getWaterSubsurfaceReplacementBlocks(ILevelWrapper levelWrapper);
|
||||||
|
ObjectOpenHashSet<IBlockStateWrapper> getWaterSurfaceReplacementBlocks(ILevelWrapper levelWrapper);
|
||||||
/** clears the cached values */
|
/** clears the cached values */
|
||||||
void resetRendererIgnoredCaveBlocks();
|
void resetCachedIgnoredBlocksSets();
|
||||||
/** clears the cached values */
|
|
||||||
void resetRendererIgnoredBlocksSet();
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -109,9 +110,7 @@ public interface IWrapperFactory extends IDhApiWrapperFactory, IBindable
|
|||||||
|
|
||||||
IVertexBufferWrapper createVboWrapper(String name);
|
IVertexBufferWrapper createVboWrapper(String name);
|
||||||
ILodContainerUniformBufferWrapper createLodContainerUniformWrapper();
|
ILodContainerUniformBufferWrapper createLodContainerUniformWrapper();
|
||||||
|
|
||||||
IDhGenericObjectVertexBufferContainer createGenericObjectVboContainer();
|
IDhGenericObjectVertexBufferContainer createGenericObjectVboContainer();
|
||||||
|
|
||||||
IDhGenericRenderer createGenericRenderer();
|
IDhGenericRenderer createGenericRenderer();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
-2
@@ -68,8 +68,6 @@ public interface IMinecraftRenderWrapper extends IBindable
|
|||||||
|
|
||||||
Color getSkyColor();
|
Color getSkyColor();
|
||||||
|
|
||||||
double getFov(float partialTicks);
|
|
||||||
|
|
||||||
/** Measured in chunks */
|
/** Measured in chunks */
|
||||||
int getRenderDistance();
|
int getRenderDistance();
|
||||||
|
|
||||||
|
|||||||
+16
@@ -1,6 +1,7 @@
|
|||||||
package com.seibel.distanthorizons.core.wrapperInterfaces.render;
|
package com.seibel.distanthorizons.core.wrapperInterfaces.render;
|
||||||
|
|
||||||
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
|
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
|
||||||
|
import com.seibel.distanthorizons.core.jar.EPlatform;
|
||||||
import com.seibel.distanthorizons.core.render.renderer.AbstractDebugWireframeRenderer;
|
import com.seibel.distanthorizons.core.render.renderer.AbstractDebugWireframeRenderer;
|
||||||
import com.seibel.distanthorizons.core.wrapperInterfaces.render.objects.IDhGenericObjectVertexBufferContainer;
|
import com.seibel.distanthorizons.core.wrapperInterfaces.render.objects.IDhGenericObjectVertexBufferContainer;
|
||||||
import com.seibel.distanthorizons.core.wrapperInterfaces.render.objects.ILodContainerUniformBufferWrapper;
|
import com.seibel.distanthorizons.core.wrapperInterfaces.render.objects.ILodContainerUniformBufferWrapper;
|
||||||
@@ -18,6 +19,16 @@ public abstract class AbstractDhRenderApiDefinition implements IBindable
|
|||||||
/** Used for debugging */
|
/** Used for debugging */
|
||||||
public abstract String getApiName();
|
public abstract String getApiName();
|
||||||
|
|
||||||
|
private final boolean useSingleIbo = (EPlatform.get() != EPlatform.MACOS);
|
||||||
|
/**
|
||||||
|
* Mac has a problem where binding an IBO that's longer than the VBO
|
||||||
|
* can cause OpenGL to render past the end of the VBO, throwing random junk
|
||||||
|
* on the screen. <br>
|
||||||
|
* To fix this we have to use individual IBOs for each VBO, which
|
||||||
|
* is slower due to having to construct new IBOs.
|
||||||
|
*/
|
||||||
|
public boolean useSingleIbo() { return this.useSingleIbo; }
|
||||||
|
|
||||||
//endregion
|
//endregion
|
||||||
|
|
||||||
|
|
||||||
@@ -36,6 +47,11 @@ public abstract class AbstractDhRenderApiDefinition implements IBindable
|
|||||||
public abstract IDhVanillaFadeRenderer getVanillaFadeRenderer();
|
public abstract IDhVanillaFadeRenderer getVanillaFadeRenderer();
|
||||||
public abstract IDhTestTriangleRenderer getTestTriangleRenderer();
|
public abstract IDhTestTriangleRenderer getTestTriangleRenderer();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* this will NOT run on the render thread.
|
||||||
|
* Render thread setup tasks should be handled
|
||||||
|
* during the first rendered frame.
|
||||||
|
*/
|
||||||
public void bindRenderers()
|
public void bindRenderers()
|
||||||
{
|
{
|
||||||
SingletonInjector.INSTANCE.bind(AbstractDhRenderApiDefinition.class, this);
|
SingletonInjector.INSTANCE.bind(AbstractDhRenderApiDefinition.class, this);
|
||||||
|
|||||||
+4
-1
@@ -19,13 +19,16 @@
|
|||||||
|
|
||||||
package com.seibel.distanthorizons.core.wrapperInterfaces.render.objects;
|
package com.seibel.distanthorizons.core.wrapperInterfaces.render.objects;
|
||||||
|
|
||||||
|
import com.seibel.distanthorizons.core.wrapperInterfaces.render.AbstractDhRenderApiDefinition;
|
||||||
import com.seibel.distanthorizons.coreapi.interfaces.dependencyInjection.IBindable;
|
import com.seibel.distanthorizons.coreapi.interfaces.dependencyInjection.IBindable;
|
||||||
|
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
|
|
||||||
public interface IVertexBufferWrapper extends IBindable, AutoCloseable
|
public interface IVertexBufferWrapper extends IBindable, AutoCloseable
|
||||||
{
|
{
|
||||||
void upload(ByteBuffer buffer, int vertexCount);
|
void uploadVertexBuffer(ByteBuffer buffer, int vertexCount);
|
||||||
|
/** Does nothing if {@link AbstractDhRenderApiDefinition#useSingleIbo()} returns true */
|
||||||
|
void uploadIndexBuffer(ByteBuffer buffer, int vertexCount);
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
void close();
|
void close();
|
||||||
|
|||||||
@@ -173,48 +173,17 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
"distanthorizons.config.client.advanced.graphics.ssao":
|
"distanthorizons.config.client.advanced.graphics.enableSsao":
|
||||||
"Ambient Occlusion",
|
|
||||||
|
|
||||||
"distanthorizons.config.client.advanced.graphics.ssao.enableSsao":
|
|
||||||
"Enable Ambient Occlusion",
|
"Enable Ambient Occlusion",
|
||||||
"distanthorizons.config.client.advanced.graphics.ssao.enableSsao.@tooltip":
|
"distanthorizons.config.client.advanced.graphics.enableSsao.@tooltip":
|
||||||
"Ambient Occlusion adds depth to the lighting of blocks.",
|
"Ambient Occlusion adds depth to the lighting of blocks.",
|
||||||
"distanthorizons.config.client.advanced.graphics.ssao.sampleCount":
|
|
||||||
"Sample Count",
|
|
||||||
"distanthorizons.config.client.advanced.graphics.ssao.sampleCount.@tooltip":
|
|
||||||
"Determines how many points in space are sampled for the occlusion test. \nHigher numbers will improve quality and reduce banding, but will increase GPU load.",
|
|
||||||
"distanthorizons.config.client.advanced.graphics.ssao.radius":
|
|
||||||
"Radius",
|
|
||||||
"distanthorizons.config.client.advanced.graphics.ssao.radius.@tooltip":
|
|
||||||
"Determines the radius Screen Space Ambient Occlusion is applied, measured in blocks.",
|
|
||||||
"distanthorizons.config.client.advanced.graphics.ssao.strength":
|
|
||||||
"Strength",
|
|
||||||
"distanthorizons.config.client.advanced.graphics.ssao.strength.@tooltip":
|
|
||||||
"Determines how dark the Screen Space Ambient Occlusion effect will be.",
|
|
||||||
"distanthorizons.config.client.advanced.graphics.ssao.bias":
|
|
||||||
"Bias",
|
|
||||||
"distanthorizons.config.client.advanced.graphics.ssao.bias.@tooltip":
|
|
||||||
"Increasing the value can reduce banding at the cost of reducing the strength of the effect.",
|
|
||||||
"distanthorizons.config.client.advanced.graphics.ssao.minLight":
|
|
||||||
"Min Light",
|
|
||||||
"distanthorizons.config.client.advanced.graphics.ssao.minLight.@tooltip":
|
|
||||||
"Determines how dark the occlusion shadows can be. \n0 = totally black at the corners \n1 = no shadow",
|
|
||||||
"distanthorizons.config.client.advanced.graphics.ssao.blurRadius":
|
|
||||||
"Blur Radius",
|
|
||||||
"distanthorizons.config.client.advanced.graphics.ssao.blurRadius.@tooltip":
|
|
||||||
"The radius, measured in pixels, that blurring is calculated for the SSAO. \nHigher numbers will reduce banding at the cost of GPU performance.",
|
|
||||||
"distanthorizons.config.client.advanced.graphics.ssao.fadeDistanceInBlocks":
|
|
||||||
"Fade Distance",
|
|
||||||
"distanthorizons.config.client.advanced.graphics.ssao.fadeDistanceInBlocks.@tooltip":
|
|
||||||
"The distance in blocks from the camera where the SSAO will fade out to. \nThis is done to prevent banding and noise at extreme distances.",
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
"distanthorizons.config.client.advanced.graphics.genericRendering":
|
"distanthorizons.config.client.advanced.graphics.genericRendering":
|
||||||
"Generic Object Rendering",
|
"Generic Object Rendering",
|
||||||
"distanthorizons.config.client.advanced.graphics.genericRendering.enableGenericRendering":
|
"distanthorizons.config.client.advanced.graphics.genericRendering.enableGenericRendering":
|
||||||
"Enable Rendering",
|
"Enable Generic Rendering",
|
||||||
"distanthorizons.config.client.advanced.graphics.genericRendering.enableGenericRendering.@tooltip":
|
"distanthorizons.config.client.advanced.graphics.genericRendering.enableGenericRendering.@tooltip":
|
||||||
"If true non terrain objects will be rendered in DH's terrain. \nThis includes beacon beams and clouds.",
|
"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":
|
"distanthorizons.config.client.advanced.graphics.genericRendering.enableBeaconRendering":
|
||||||
@@ -369,7 +338,7 @@
|
|||||||
"distanthorizons.config.client.advanced.graphics.culling.reduceOverdrawWithFastMovement.@tooltip":
|
"distanthorizons.config.client.advanced.graphics.culling.reduceOverdrawWithFastMovement.@tooltip":
|
||||||
"If set to true the overdraw prevention radius will get closer \nto the camera when flying/moving quickly. \n\nThis helps reduce issues where Minecraft can't load or \ngenerate chunks fast enough to keep up with DH.",
|
"If set to true the overdraw prevention radius will get closer \nto the camera when flying/moving quickly. \n\nThis helps reduce issues where Minecraft can't load or \ngenerate chunks fast enough to keep up with DH.",
|
||||||
"distanthorizons.config.client.advanced.graphics.culling.enableCaveCulling":
|
"distanthorizons.config.client.advanced.graphics.culling.enableCaveCulling":
|
||||||
"Cave Culling",
|
"Enable Cave Culling",
|
||||||
"distanthorizons.config.client.advanced.graphics.culling.enableCaveCulling.@tooltip":
|
"distanthorizons.config.client.advanced.graphics.culling.enableCaveCulling.@tooltip":
|
||||||
"If enabled caves will be culled \n\nAdditional Info: Currently this cull all faces \n with skylight value of 0 under the cave culling height in dimensions that \n do not have a ceiling. \n",
|
"If enabled caves will be culled \n\nAdditional Info: Currently this cull all faces \n with skylight value of 0 under the cave culling height in dimensions that \n do not have a ceiling. \n",
|
||||||
"distanthorizons.config.client.advanced.graphics.culling.caveCullingHeight":
|
"distanthorizons.config.client.advanced.graphics.culling.caveCullingHeight":
|
||||||
@@ -391,11 +360,19 @@
|
|||||||
"distanthorizons.config.client.advanced.graphics.culling.ignoredRenderBlockCsv":
|
"distanthorizons.config.client.advanced.graphics.culling.ignoredRenderBlockCsv":
|
||||||
"Ignored Block CSV",
|
"Ignored Block CSV",
|
||||||
"distanthorizons.config.client.advanced.graphics.culling.ignoredRenderBlockCsv.@tooltip":
|
"distanthorizons.config.client.advanced.graphics.culling.ignoredRenderBlockCsv.@tooltip":
|
||||||
"A comma separated list of block resource locations that won't be rendered by DH. \nNote: air is always included in this list.",
|
"A comma separated list of block resource locations that won't be rendered by DH. \nAir is always included in this list. \n\nNote: \nIf you see gaps, or holes you may have to change \nworldCompression to [MERGE_SAME_BLOCKS] and re-generate the LODs.",
|
||||||
"distanthorizons.config.client.advanced.graphics.culling.ignoredRenderCaveBlockCsv":
|
"distanthorizons.config.client.advanced.graphics.culling.ignoredRenderCaveBlockCsv":
|
||||||
"Ignored Cave Block CSV",
|
"Ignored Cave Block CSV",
|
||||||
"distanthorizons.config.client.advanced.graphics.culling.ignoredRenderCaveBlockCsv.@tooltip":
|
"distanthorizons.config.client.advanced.graphics.culling.ignoredRenderCaveBlockCsv.@tooltip":
|
||||||
"A comma separated list of block resource locations that shouldn't be rendered \nif they are in a 0 sky light underground area. \nNote: air is always included in this list.",
|
"A comma separated list of block resource locations that shouldn't be rendered \nif they are in a 0 sky light underground area. \nAir is always included in this list. \n\nDefaults to an empty list since most cave blocks will be automatically ignored due to being: \ntransparent, non-solid, or liquids, but new blocks can be added here if needed.",
|
||||||
|
"distanthorizons.config.client.advanced.graphics.culling.waterSubSurfaceBlockReplacementCsv":
|
||||||
|
"Water Sub-Surface Replacement",
|
||||||
|
"distanthorizons.config.client.advanced.graphics.culling.waterSubSurfaceBlockReplacementCsv.@tooltip":
|
||||||
|
"A comma separated list of block resource locations that will be replaced by water \nif they're visible on the water's surface.",
|
||||||
|
"distanthorizons.config.client.advanced.graphics.culling.waterSurfaceBlockReplacementCsv":
|
||||||
|
"Water Surface Replacement",
|
||||||
|
"distanthorizons.config.client.advanced.graphics.culling.waterSurfaceBlockReplacementCsv.@tooltip":
|
||||||
|
"A comma separated list of block resource locations that will be removed \nwhen on top of water.",
|
||||||
|
|
||||||
"distanthorizons.config.client.advanced.graphics.overrideVanillaGraphicsSettings":
|
"distanthorizons.config.client.advanced.graphics.overrideVanillaGraphicsSettings":
|
||||||
"Override Vanilla Settings",
|
"Override Vanilla Settings",
|
||||||
@@ -454,8 +431,8 @@
|
|||||||
|
|
||||||
"distanthorizons.config.client.advanced.debugging.rendererMode":
|
"distanthorizons.config.client.advanced.debugging.rendererMode":
|
||||||
"Renderer Mode",
|
"Renderer Mode",
|
||||||
"distanthorizons.config.client.advanced.debugging.debugRendering":
|
"distanthorizons.config.client.advanced.debugging.debugRenderingColors":
|
||||||
"Debug Rendering",
|
"Debug Rendering Colors",
|
||||||
"distanthorizons.config.client.advanced.debugging.lodOnlyMode":
|
"distanthorizons.config.client.advanced.debugging.lodOnlyMode":
|
||||||
"Only Render LODs",
|
"Only Render LODs",
|
||||||
"distanthorizons.config.client.advanced.debugging.lodOnlyMode.@tooltip":
|
"distanthorizons.config.client.advanced.debugging.lodOnlyMode.@tooltip":
|
||||||
@@ -565,6 +542,8 @@
|
|||||||
"Player Section Pos Detail Level",
|
"Player Section Pos Detail Level",
|
||||||
"distanthorizons.config.client.advanced.debugging.f3Screen.showThreadPools":
|
"distanthorizons.config.client.advanced.debugging.f3Screen.showThreadPools":
|
||||||
"Show Thread Pools",
|
"Show Thread Pools",
|
||||||
|
"distanthorizons.config.client.advanced.debugging.f3Screen.showRenderThreadTasks":
|
||||||
|
"Show Render Thread Tasks",
|
||||||
"distanthorizons.config.client.advanced.debugging.f3Screen.showCombinedObjectPools":
|
"distanthorizons.config.client.advanced.debugging.f3Screen.showCombinedObjectPools":
|
||||||
"Show Combined Object Pools",
|
"Show Combined Object Pools",
|
||||||
"distanthorizons.config.client.advanced.debugging.f3Screen.showSeparatedObjectPools":
|
"distanthorizons.config.client.advanced.debugging.f3Screen.showSeparatedObjectPools":
|
||||||
@@ -1043,7 +1022,7 @@
|
|||||||
|
|
||||||
"distanthorizons.config.enum.EDhApiRendererMode.DEFAULT":
|
"distanthorizons.config.enum.EDhApiRendererMode.DEFAULT":
|
||||||
"Default",
|
"Default",
|
||||||
"distanthorizons.config.enum.EDhApiRendererMode.DEBUG":
|
"distanthorizons.config.enum.EDhApiRendererMode.DEBUG_TRIANGLE":
|
||||||
"Debug Triangle",
|
"Debug Triangle",
|
||||||
"distanthorizons.config.enum.EDhApiRendererMode.DISABLED":
|
"distanthorizons.config.enum.EDhApiRendererMode.DISABLED":
|
||||||
"Disabled",
|
"Disabled",
|
||||||
|
|||||||
@@ -39,14 +39,14 @@ public class DhApiConfigTest
|
|||||||
public void ConfigTest()
|
public void ConfigTest()
|
||||||
{
|
{
|
||||||
ConfigEntry<EDhApiRendererMode> coreConfig = new ConfigEntry.Builder<EDhApiRendererMode>()
|
ConfigEntry<EDhApiRendererMode> coreConfig = new ConfigEntry.Builder<EDhApiRendererMode>()
|
||||||
.set(EDhApiRendererMode.DEBUG)
|
.set(EDhApiRendererMode.DEBUG_TRIANGLE)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
DhApiConfigValue<EDhApiRendererMode, Boolean> apiConfig = new DhApiConfigValue<>(coreConfig, new RenderModeEnabledConverter());
|
DhApiConfigValue<EDhApiRendererMode, Boolean> apiConfig = new DhApiConfigValue<>(coreConfig, new RenderModeEnabledConverter());
|
||||||
|
|
||||||
// start with no API value
|
// start with no API value
|
||||||
Assert.assertNull("API Value shouldn't be set yet", apiConfig.getApiValue());
|
Assert.assertNull("API Value shouldn't be set yet", apiConfig.getApiValue());
|
||||||
Assert.assertEquals("underlying config should be 'DEBUG'", EDhApiRendererMode.DEBUG, coreConfig.get());
|
Assert.assertEquals("underlying config should be 'DEBUG'", EDhApiRendererMode.DEBUG_TRIANGLE, coreConfig.get());
|
||||||
|
|
||||||
|
|
||||||
// set API value
|
// set API value
|
||||||
@@ -62,7 +62,7 @@ public class DhApiConfigTest
|
|||||||
// clear API value
|
// clear API value
|
||||||
apiConfig.clearValue();
|
apiConfig.clearValue();
|
||||||
Assert.assertNull("API Value should be null", apiConfig.getApiValue());
|
Assert.assertNull("API Value should be null", apiConfig.getApiValue());
|
||||||
Assert.assertEquals("underlying config should be 'DEBUG'", EDhApiRendererMode.DEBUG, coreConfig.get());
|
Assert.assertEquals("underlying config should be 'DEBUG'", EDhApiRendererMode.DEBUG_TRIANGLE, coreConfig.get());
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user