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 {
|
||||
id "java"
|
||||
|
||||
id "com.github.johnrengelman.shadow" version '8.1.1' apply false
|
||||
id "com.gradleup.shadow"
|
||||
}
|
||||
|
||||
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 {
|
||||
// required for basic shadowJar setup
|
||||
@@ -21,7 +37,7 @@ task addSourcesToCompiledJar(type: ShadowJar) {
|
||||
doFirst {
|
||||
System.out.println("Adding source files from: \n" +
|
||||
"[" + sourceJarPath + "] to compiled API jar: \n" +
|
||||
"[" + shadowJar.archivePath + "]")
|
||||
"[" + shadowJar.archiveFile.get().asFile + "]")
|
||||
|
||||
// Validate the input JAR file
|
||||
if (!secondJarFile.exists()) {
|
||||
@@ -42,7 +58,7 @@ task addSourcesToCompiledJar(type: ShadowJar) {
|
||||
}
|
||||
|
||||
// set the jars to merge
|
||||
from shadowJar.archivePath
|
||||
from shadowJar.archiveFile.get().asFile
|
||||
from secondJarFile
|
||||
|
||||
// alternative method to Include the source files in the combined JAR
|
||||
|
||||
+9
-9
@@ -20,17 +20,17 @@
|
||||
package com.seibel.distanthorizons.api.enums.rendering;
|
||||
|
||||
/**
|
||||
* Default <br>
|
||||
* Debug <br>
|
||||
* Disabled <br>
|
||||
* DEFAULT <br>
|
||||
* DEBUG_TRIANGLE <br>
|
||||
* DISABLED <br>
|
||||
*
|
||||
* @since API 2.0.0
|
||||
* @version 2024-4-6
|
||||
* @version 2026-03-23
|
||||
*/
|
||||
public enum EDhApiRendererMode
|
||||
{
|
||||
DEFAULT,
|
||||
DEBUG,
|
||||
DEBUG_TRIANGLE,
|
||||
DISABLED;
|
||||
|
||||
|
||||
@@ -40,8 +40,8 @@ public enum EDhApiRendererMode
|
||||
switch (type)
|
||||
{
|
||||
case DEFAULT:
|
||||
return DEBUG;
|
||||
case DEBUG:
|
||||
return DEBUG_TRIANGLE;
|
||||
case DEBUG_TRIANGLE:
|
||||
return DISABLED;
|
||||
default:
|
||||
return DEFAULT;
|
||||
@@ -55,10 +55,10 @@ public enum EDhApiRendererMode
|
||||
{
|
||||
case DEFAULT:
|
||||
return DISABLED;
|
||||
case DEBUG:
|
||||
case DEBUG_TRIANGLE:
|
||||
return DEFAULT;
|
||||
default:
|
||||
return DEBUG;
|
||||
return DEBUG_TRIANGLE;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -38,7 +38,7 @@ public final class ModInfo
|
||||
public static final String NAME = "DistantHorizons";
|
||||
/** Human-readable version of NAME */
|
||||
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. */
|
||||
public static final boolean IS_DEV_BUILD = VERSION.toLowerCase().contains("dev");
|
||||
|
||||
|
||||
+45
-32
@@ -1,18 +1,23 @@
|
||||
plugins {
|
||||
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 {
|
||||
mainClass.set("com.seibel.distanthorizons.core.jar.JarMain")
|
||||
tasks.withType(JavaCompile).configureEach {
|
||||
options.release = 8
|
||||
options.encoding = "UTF-8"
|
||||
}
|
||||
|
||||
configurations {
|
||||
shadowedArtifact // Used by DH to specify that we want to implement the shadowed core JAR file instead of the regular JAR file
|
||||
shade
|
||||
implementation.extendsFrom shade
|
||||
testImplementation.extendsFrom compileOnly
|
||||
}
|
||||
|
||||
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
|
||||
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
|
||||
// Imports most of lwjgl's libraries (well, only the ones that we need)
|
||||
implementation platform("org.lwjgl:lwjgl-bom:${rootProject.lwjgl_version}")
|
||||
dependencies {
|
||||
// API project dependency
|
||||
implementation project(":api")
|
||||
|
||||
// REMEMBER: Dont shadow stuff here, these are just the libs that are included in Minecraft so that the core can use
|
||||
implementation "org.lwjgl:lwjgl"
|
||||
implementation "org.lwjgl:lwjgl-assimp"
|
||||
implementation "org.lwjgl:lwjgl-glfw"
|
||||
// OpenGL is removed since DH now handles rendering in the "Common" project
|
||||
// so we can use OpenGL for old MC versions and Blaze3D (IE Vulkan) for newer ones
|
||||
// implementation "org.lwjgl:lwjgl-openal"
|
||||
// implementation "org.lwjgl:lwjgl-opengl"
|
||||
implementation "org.lwjgl:lwjgl-stb"
|
||||
implementation "org.lwjgl:lwjgl-tinyfd"
|
||||
runtimeOnly "org.lwjgl:lwjgl::$lwjglNatives"
|
||||
runtimeOnly "org.lwjgl:lwjgl-assimp::$lwjglNatives"
|
||||
runtimeOnly "org.lwjgl:lwjgl-glfw::$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"
|
||||
// MC-provided libraries (available at runtime via Minecraft)
|
||||
compileOnly platform("org.lwjgl:lwjgl-bom:${rootProject.lwjgl_version}")
|
||||
compileOnly "org.lwjgl:lwjgl"
|
||||
compileOnly "org.lwjgl:lwjgl-assimp"
|
||||
compileOnly "org.lwjgl:lwjgl-glfw"
|
||||
compileOnly "org.lwjgl:lwjgl-stb"
|
||||
compileOnly "org.lwjgl:lwjgl-tinyfd"
|
||||
testRuntimeOnly platform("org.lwjgl:lwjgl-bom:${rootProject.lwjgl_version}")
|
||||
testRuntimeOnly "org.lwjgl:lwjgl::$lwjglNatives"
|
||||
testRuntimeOnly "org.lwjgl:lwjgl-assimp::$lwjglNatives"
|
||||
testRuntimeOnly "org.lwjgl:lwjgl-glfw::$lwjglNatives"
|
||||
testRuntimeOnly "org.lwjgl:lwjgl-stb::$lwjglNatives"
|
||||
testRuntimeOnly "org.lwjgl:lwjgl-tinyfd::$lwjglNatives"
|
||||
|
||||
// FIXME for some reason this line doesn't actually shade in the library
|
||||
// shade "it.unimi.dsi:fastutil:${rootProject.fastutil_version}" // Add our own fastutil version
|
||||
compileOnly("org.apache.logging.log4j:log4j-api:${rootProject.log4j_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
|
||||
implementation("org.jetbrains:annotations:16.0.2")
|
||||
implementation("com.google.code.findbugs:jsr305:3.0.2")
|
||||
implementation("com.google.common:google-collect:0.5")
|
||||
implementation("com.google.guava:guava:31.1-jre")
|
||||
|
||||
// JUnit (core tests only)
|
||||
compileOnly("junit:junit:4.13")
|
||||
compileOnly("org.junit.jupiter:junit-jupiter:5.8.2")
|
||||
compileOnly("org.junit.jupiter:junit-jupiter-engine:5.8.2")
|
||||
}
|
||||
|
||||
artifacts {
|
||||
|
||||
+1
-1
@@ -35,6 +35,6 @@ public class DhApiAmbientOcclusionConfig implements IDhApiAmbientOcclusionConfig
|
||||
|
||||
@Override
|
||||
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()
|
||||
{ return new DhApiConfigValue<EDhApiDebugRendering, EDhApiDebugRendering>(Config.Client.Advanced.Debugging.debugRendering); }
|
||||
{ return new DhApiConfigValue<EDhApiDebugRendering, EDhApiDebugRendering>(Config.Client.Advanced.Debugging.debugRenderingColors); }
|
||||
|
||||
public IDhApiConfigValue<Boolean> debugKeybindings()
|
||||
{ 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.wrapperInterfaces.minecraft.IMinecraftRenderWrapper;
|
||||
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.IDhTestTriangleRenderer;
|
||||
import com.seibel.distanthorizons.coreapi.DependencyInjection.ApiEventInjector;
|
||||
@@ -763,8 +762,8 @@ public class ClientApi
|
||||
}
|
||||
else if (glfwKey == GLFW.GLFW_KEY_F8)
|
||||
{
|
||||
Config.Client.Advanced.Debugging.debugRendering.set(EDhApiDebugRendering.next(Config.Client.Advanced.Debugging.debugRendering.get()));
|
||||
MC_CLIENT.sendChatMessage("F8: Set debug mode to " + 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.debugRenderingColors.get());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -90,7 +90,7 @@ public class ClientPluginChannelApi
|
||||
|
||||
LOGGER.info("Server level key received: [" + msg.levelKey + "].");
|
||||
|
||||
RenderThreadTaskHandler.INSTANCE.queueRunningOnRenderThread(() ->
|
||||
RenderThreadTaskHandler.INSTANCE.queueRunningOnRenderThread("ClientPluginChannelApi onLevelInitMessage", () ->
|
||||
{
|
||||
IClientLevelWrapper clientLevel = MC.getWrappedClientLevel(true);
|
||||
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.pos.blockPos.DhBlockPos2D;
|
||||
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.sql.repo.AbstractDhRepo;
|
||||
import com.seibel.distanthorizons.core.util.objects.Pair;
|
||||
@@ -63,6 +64,7 @@ public class SharedApi
|
||||
|
||||
@Nullable
|
||||
private static AbstractDhWorld currentWorld;
|
||||
private static final Object worldLockObject = new Object();
|
||||
|
||||
|
||||
|
||||
@@ -87,46 +89,51 @@ public class SharedApi
|
||||
|
||||
public static void setDhWorld(AbstractDhWorld newWorld)
|
||||
{
|
||||
AbstractDhWorld oldWorld = currentWorld;
|
||||
if (oldWorld != null)
|
||||
synchronized (worldLockObject)
|
||||
{
|
||||
oldWorld.close();
|
||||
}
|
||||
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)
|
||||
AbstractDhWorld oldWorld = currentWorld;
|
||||
if (oldWorld != 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
|
||||
AbstractDhRepo.closeAllConnections();
|
||||
// needs to be closed on world shutdown to clear out un-processed chunks
|
||||
WORLD_CHUNK_UPDATE_MANAGER.clear();
|
||||
// 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();
|
||||
|
||||
// recommend that the garbage collector cleans up any objects from the old world and thread pools
|
||||
System.gc();
|
||||
ApiEventInjector.INSTANCE.fireAllEvents(DhApiWorldLoadEvent.class, new DhApiWorldLoadEvent.EventParam());
|
||||
}
|
||||
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
|
||||
DhApiWorldProxy.INSTANCE.setReadOnly(false, false);
|
||||
if (MC_RENDER != null)
|
||||
{
|
||||
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 java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
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 */
|
||||
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.
|
||||
* The assumption is that there will be a limited number of {@link ILevelWrapper}'s
|
||||
@@ -37,6 +42,7 @@ public class WorldChunkUpdateManager
|
||||
|
||||
|
||||
|
||||
|
||||
//=============//
|
||||
// constructor //
|
||||
//=============//
|
||||
@@ -62,6 +68,7 @@ public class WorldChunkUpdateManager
|
||||
AbstractDhWorld world = SharedApi.getAbstractDhWorld();
|
||||
if (world == null)
|
||||
{
|
||||
// world isn't loaded, no warnings need to be logged
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -73,6 +80,8 @@ public class WorldChunkUpdateManager
|
||||
// but this check confirms it
|
||||
&& !(levelWrapper instanceof IClientLevelWrapper))
|
||||
{
|
||||
// how did we get a server level wrapper on the client?
|
||||
// this shouldn't happen, but just in case
|
||||
return null;
|
||||
}
|
||||
else if (
|
||||
@@ -81,6 +90,7 @@ public class WorldChunkUpdateManager
|
||||
// when hosting a server we only care about the server wrappers
|
||||
&& !(levelWrapper instanceof IServerLevelWrapper))
|
||||
{
|
||||
// ignore client updates on the server
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
@@ -19,7 +19,6 @@
|
||||
|
||||
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.quickOptions.*;
|
||||
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.core.config.eventHandlers.*;
|
||||
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.enums.*;
|
||||
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
|
||||
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
|
||||
import com.seibel.distanthorizons.core.pos.DhSectionPos;
|
||||
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.coreapi.ModInfo;
|
||||
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 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();
|
||||
|
||||
|
||||
@@ -358,17 +358,6 @@ public class Config
|
||||
.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 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"
|
||||
+ "")
|
||||
.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
|
||||
@@ -565,7 +546,7 @@ public class Config
|
||||
.build();
|
||||
|
||||
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?")
|
||||
.build();
|
||||
|
||||
@@ -745,57 +726,56 @@ public class Config
|
||||
+ "Disable this if shadows render incorrectly.")
|
||||
.build();
|
||||
|
||||
public static ConfigUISpacer ignoreCsvStartSpacer = new ConfigUISpacer.Builder().build();
|
||||
|
||||
public static ConfigEntry<String> ignoredRenderBlockCsv = new ConfigEntry.Builder<String>()
|
||||
.set("minecraft:barrier,minecraft:structure_void,minecraft:light,minecraft:tripwire,minecraft:brown_mushroom")
|
||||
.setAppearance(EConfigEntryAppearance.ALL)
|
||||
.addListener(RenderBlockCacheCsvHandler.INSTANCE)
|
||||
.comment(""
|
||||
+ "A comma separated list of block resource locations that won't be rendered by DH. \n"
|
||||
+ "Air is always included in this list. \n"
|
||||
+ "Requires a restart to change. \n"
|
||||
+ "\n"
|
||||
+ "Note:\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"
|
||||
+ "Black spots may happen occur to block lighting being zero for covered blocks.\n"
|
||||
+ "")
|
||||
.build();
|
||||
|
||||
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)
|
||||
.addListener(RenderBlockCacheCsvHandler.INSTANCE)
|
||||
.comment(""
|
||||
+ "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"
|
||||
+ "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();
|
||||
|
||||
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
|
||||
{
|
||||
ignoredRenderBlockCsv.addListener(new ConfigChangeListener<String>(ignoredRenderBlockCsv,
|
||||
(blockCsv) ->
|
||||
{
|
||||
IWrapperFactory wrapperFactory = SingletonInjector.INSTANCE.get(IWrapperFactory.class);
|
||||
if (wrapperFactory != null)
|
||||
{
|
||||
wrapperFactory.resetRendererIgnoredBlocksSet();
|
||||
DhApi.Delayed.renderProxy.clearRenderDataCache();
|
||||
}
|
||||
}));
|
||||
public static ConfigEntry<String> waterSurfaceBlockReplacementCsv = new ConfigEntry.Builder<String>()
|
||||
.set("minecraft:lily_pad")
|
||||
.setAppearance(EConfigEntryAppearance.ALL)
|
||||
.addListener(RenderBlockCacheCsvHandler.INSTANCE)
|
||||
.comment(""
|
||||
+ "A comma separated list of block resource locations that will be removed \n"
|
||||
+ "when on top of water. \n"
|
||||
+ "")
|
||||
.build();
|
||||
|
||||
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
|
||||
@@ -835,7 +815,6 @@ public class Config
|
||||
|
||||
public static ConfigEntry<EDhApiRenderApi> renderingApi = new ConfigEntry.Builder<EDhApiRenderApi>()
|
||||
.set(EDhApiRenderApi.AUTO)
|
||||
.setAppearance(EConfigEntryAppearance.ONLY_IN_FILE) // very experimental option and only supported
|
||||
.comment(""
|
||||
+ "Requires a restart to change. \n"
|
||||
+ " \n"
|
||||
@@ -908,11 +887,11 @@ public class Config
|
||||
+ "What renderer is active? \n"
|
||||
+ "\n"
|
||||
+ EDhApiRendererMode.DEFAULT + ": Default lod renderer \n"
|
||||
+ EDhApiRendererMode.DEBUG + ": Debug testing renderer \n"
|
||||
+ EDhApiRendererMode.DEBUG_TRIANGLE + ": Debug testing renderer \n"
|
||||
+ EDhApiRendererMode.DISABLED + ": Disable rendering")
|
||||
.build();
|
||||
|
||||
public static ConfigEntry<EDhApiDebugRendering> debugRendering = new ConfigEntry.Builder<EDhApiDebugRendering>()
|
||||
public static ConfigEntry<EDhApiDebugRendering> debugRenderingColors = new ConfigEntry.Builder<EDhApiDebugRendering>()
|
||||
.set(EDhApiDebugRendering.OFF)
|
||||
.comment(""
|
||||
+ "Should specialized colors/rendering modes be used? \n"
|
||||
@@ -925,6 +904,13 @@ public class Config
|
||||
.addListener(ReloadLodsConfigEventHandler.DELAYED_INSTANCE)
|
||||
.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>()
|
||||
.set(false)
|
||||
.comment(""
|
||||
@@ -952,13 +938,6 @@ public class Config
|
||||
+ "")
|
||||
.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>()
|
||||
.set(false)
|
||||
.comment(""
|
||||
@@ -1019,15 +998,6 @@ public class Config
|
||||
.set(false)
|
||||
.comment("Render LOD section status?")
|
||||
.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>()
|
||||
.set(false)
|
||||
@@ -1080,13 +1050,6 @@ public class Config
|
||||
+ "")
|
||||
.build();
|
||||
|
||||
public static ConfigEntry<EDhApiGpuUploadMethod> glUploadMode = new ConfigEntry.Builder<EDhApiGpuUploadMethod>()
|
||||
.set(EDhApiGpuUploadMethod.AUTO)
|
||||
.comment(""
|
||||
+ "\n"
|
||||
+ "")
|
||||
.build();
|
||||
|
||||
}
|
||||
|
||||
public static class ColumnBuilderDebugging
|
||||
@@ -1153,6 +1116,11 @@ public class Config
|
||||
.comment("Shows info about each thread pool.")
|
||||
.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>()
|
||||
.set(false)
|
||||
.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>()
|
||||
.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();
|
||||
|
||||
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.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>()
|
||||
{{
|
||||
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));
|
||||
LodBufferContainer bufferContainer = new LodBufferContainer(pos, minBlockPos);
|
||||
CompletableFuture<LodBufferContainer> uploadFuture = bufferContainer.makeAndUploadBuffersAsync(quadBuilder);
|
||||
CompletableFuture<LodBufferContainer> uploadFuture = bufferContainer.tryMakeAndUploadBuffersAsync(quadBuilder);
|
||||
uploadFuture.whenComplete((uploadedBuffer, exception) ->
|
||||
{
|
||||
// clean up if not uploaded
|
||||
@@ -328,7 +328,7 @@ public class ColumnRenderBufferBuilder
|
||||
|
||||
int color;
|
||||
boolean fullBright = false;
|
||||
EDhApiDebugRendering debugging = Config.Client.Advanced.Debugging.debugRendering.get();
|
||||
EDhApiDebugRendering debugging = Config.Client.Advanced.Debugging.debugRenderingColors.get();
|
||||
switch (debugging)
|
||||
{
|
||||
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.blockPos.DhBlockPos;
|
||||
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.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.renderPass.IDhTerrainRenderer;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.render.objects.IVertexBufferWrapper;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.lwjgl.system.MemoryUtil;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
@@ -47,6 +49,7 @@ public class LodBufferContainer implements AutoCloseable
|
||||
private static final DhLogger LOGGER = new DhLoggerBuilder().build();
|
||||
|
||||
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 */
|
||||
@@ -55,8 +58,8 @@ public class LodBufferContainer implements AutoCloseable
|
||||
|
||||
public boolean buffersUploaded = false;
|
||||
|
||||
public IVertexBufferWrapper[] vbos;
|
||||
public IVertexBufferWrapper[] vbosTransparent;
|
||||
public IVertexBufferWrapper[] vboOpaqueWrappers;
|
||||
public IVertexBufferWrapper[] vboTransparentWrappers;
|
||||
|
||||
public ILodContainerUniformBufferWrapper uniformContainer = WRAPPER_FACTORY.createLodContainerUniformWrapper();
|
||||
|
||||
@@ -73,8 +76,8 @@ public class LodBufferContainer implements AutoCloseable
|
||||
{
|
||||
this.pos = pos;
|
||||
this.minCornerBlockPos = minCornerBlockPos;
|
||||
this.vbos = new IVertexBufferWrapper[0];
|
||||
this.vbosTransparent = new IVertexBufferWrapper[0];
|
||||
this.vboOpaqueWrappers = new IVertexBufferWrapper[0];
|
||||
this.vboTransparentWrappers = new IVertexBufferWrapper[0];
|
||||
|
||||
this.uniformContainer.createUniformData(this);
|
||||
}
|
||||
@@ -89,8 +92,13 @@ public class LodBufferContainer implements AutoCloseable
|
||||
//region
|
||||
|
||||
/** 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
|
||||
CompletableFuture<LodBufferContainer> oldFuture = this.uploadFutureRef.get();
|
||||
if (oldFuture != null)
|
||||
@@ -118,65 +126,149 @@ public class LodBufferContainer implements AutoCloseable
|
||||
return oldFuture;
|
||||
}
|
||||
|
||||
//endregion
|
||||
|
||||
|
||||
// make the buffers
|
||||
|
||||
//================//
|
||||
// create buffers //
|
||||
//================//
|
||||
//region
|
||||
|
||||
ArrayList<ByteBuffer> opaqueBuffers = builder.makeOpaqueVertexBuffers();
|
||||
ArrayList<ByteBuffer> transparentBuffers = builder.makeTransparentVertexBuffers();
|
||||
|
||||
this.vbos = resizeBufferArray(this.vbos, opaqueBuffers.size());
|
||||
this.vbosTransparent = resizeBufferArray(this.vbosTransparent, transparentBuffers.size());
|
||||
this.vboOpaqueWrappers = resizeWrapperArray(this.vboOpaqueWrappers, opaqueBuffers.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
|
||||
if (Thread.interrupted()
|
||||
|| future.isCancelled())
|
||||
// create VBOs failed //
|
||||
|
||||
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);
|
||||
}
|
||||
finally
|
||||
return null;
|
||||
});
|
||||
createFuture.thenRun(() ->
|
||||
{
|
||||
// all the buffers must be manually freed to prevent memory leaks
|
||||
//=============//
|
||||
// upload VBOs //
|
||||
//=============//
|
||||
|
||||
for (ByteBuffer buffer : opaqueBuffers)
|
||||
{
|
||||
MemoryUtil.memFree(buffer);
|
||||
}
|
||||
CompletableFuture<Void> opaqueFuture = uploadBuffersAsync(future, this.vboOpaqueWrappers, opaqueBuffers, opaqueIndexBuffers);
|
||||
CompletableFuture<Void> transparentFuture = uploadBuffersAsync(future, this.vboTransparentWrappers, transparentBuffers, transparentIndexBuffers);
|
||||
|
||||
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;
|
||||
}
|
||||
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)
|
||||
{
|
||||
@@ -197,46 +289,188 @@ public class LodBufferContainer implements AutoCloseable
|
||||
}
|
||||
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;
|
||||
for (int i = 0; i < byteBuffers.size(); i++)
|
||||
ArrayList<CompletableFuture<Void>> createVboFutureList = new ArrayList<>();
|
||||
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!!");
|
||||
}
|
||||
|
||||
|
||||
// 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);
|
||||
int size = buffer.limit() - buffer.position();
|
||||
int vertexCount = size / LodQuadBuilder.BYTES_PER_VERTEX;
|
||||
// final variables for use in lambdas //
|
||||
|
||||
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);
|
||||
}
|
||||
catch (Exception e)
|
||||
try
|
||||
{
|
||||
// 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;
|
||||
vbo.close();
|
||||
LOGGER.error("Failed to upload buffer. Error: ["+e.getMessage()+"].", e);
|
||||
CompletableFuture<Void> indexUploadFuture = new CompletableFuture<>();
|
||||
uploadFutureList.add(indexUploadFuture);
|
||||
|
||||
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++;
|
||||
}
|
||||
|
||||
if (vboIndex < vbos.length)
|
||||
if (vboIndex < vboWrappers.length)
|
||||
{
|
||||
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
|
||||
@@ -248,29 +482,34 @@ public class LodBufferContainer implements AutoCloseable
|
||||
//================//
|
||||
//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 */
|
||||
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 */
|
||||
public int vboBufferCount()
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
public boolean uploadInProgress() { return this.uploadFutureRef.get() != null; }
|
||||
|
||||
//endregion
|
||||
|
||||
|
||||
@@ -291,9 +530,9 @@ public class LodBufferContainer implements AutoCloseable
|
||||
{
|
||||
this.buffersUploaded = false;
|
||||
|
||||
RenderThreadTaskHandler.INSTANCE.queueRunningOnRenderThread(() ->
|
||||
RenderThreadTaskHandler.INSTANCE.queueRunningOnRenderThread("LodBufferContainer Close", () ->
|
||||
{
|
||||
for (IVertexBufferWrapper buffer : this.vbos)
|
||||
for (IVertexBufferWrapper buffer : this.vboOpaqueWrappers)
|
||||
{
|
||||
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)
|
||||
{
|
||||
|
||||
+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.util.ColorUtil;
|
||||
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 org.lwjgl.system.MemoryUtil;
|
||||
|
||||
@@ -57,8 +56,9 @@ public class LodQuadBuilder
|
||||
private final EDhApiDebugRendering debugRenderingMode;
|
||||
private final EDhApiGrassSideRendering grassSideRenderingMode;
|
||||
|
||||
/** the number of bytes for */
|
||||
public static final int BYTES_PER_VERTEX = 14;
|
||||
/** the number of bytes for a single vertex */
|
||||
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[][][]
|
||||
///region
|
||||
@@ -131,7 +131,7 @@ public class LodQuadBuilder
|
||||
|
||||
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();
|
||||
|
||||
}
|
||||
@@ -308,7 +308,8 @@ public class LodQuadBuilder
|
||||
{
|
||||
// if this is the first iteration or the buffer is full,
|
||||
// create a new buffer
|
||||
if (buffer == null || !buffer.hasRemaining())
|
||||
if (buffer == null
|
||||
|| buffer.remaining() < BYTES_PER_QUAD)
|
||||
{
|
||||
buffer = MemoryUtil.memAlloc(getMaxBufferByteSize());
|
||||
byteBufferList.add(buffer);
|
||||
@@ -498,16 +499,18 @@ public class LodQuadBuilder
|
||||
return maxBufferByteSize;
|
||||
}
|
||||
|
||||
// number of bytes a single quad takes
|
||||
int QUADS_BYTE_SIZE = BYTES_PER_VERTEX * 4;
|
||||
// how big a single VBO can be in bytes
|
||||
int MAX_VBO_BYTE_SIZE = 10 * 1024 * 1024; // 10 MB
|
||||
int MAX_QUADS_PER_BUFFER = MAX_VBO_BYTE_SIZE / QUADS_BYTE_SIZE;
|
||||
int FULL_SIZED_BUFFER = MAX_QUADS_PER_BUFFER * QUADS_BYTE_SIZE;
|
||||
// 2 MB
|
||||
// note: this is relatively small (10 MB was the previous max) to reduce stuttering
|
||||
// during the upload process by having smaller upload steps
|
||||
int maxVboByteSize = 2 * 1024 * 1024;
|
||||
|
||||
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
|
||||
|
||||
+41
-10
@@ -194,8 +194,12 @@ public class FullDataToRenderDataTransformer
|
||||
boolean ignoreNonCollidingBlocks = (Config.Client.Advanced.Graphics.Quality.blocksToIgnore.get() == EDhApiBlocksToAvoid.NON_COLLIDING);
|
||||
boolean colorBelowWithAvoidedBlocks = Config.Client.Advanced.Graphics.Quality.tintWithAvoidedBlocks.get();
|
||||
|
||||
ObjectOpenHashSet<IBlockStateWrapper> blockStatesToIgnore = WRAPPER_FACTORY.getRendererIgnoredBlocks(levelWrapper);
|
||||
ObjectOpenHashSet<IBlockStateWrapper> caveBlockStatesToIgnore = WRAPPER_FACTORY.getRendererIgnoredCaveBlocks(levelWrapper);
|
||||
final ObjectOpenHashSet<IBlockStateWrapper> blockStatesToIgnore = WRAPPER_FACTORY.getRendererIgnoredBlocks(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
|
||||
if (snowLayerBlockStates == null)
|
||||
@@ -223,6 +227,7 @@ public class FullDataToRenderDataTransformer
|
||||
int colorToApplyToNextBlock = -1;
|
||||
int lastColor = 0;
|
||||
int lastBottom = -10_000;
|
||||
IBlockStateWrapper lastBlock = null;
|
||||
|
||||
int skylightToApplyToNextBlock = -1;
|
||||
int blocklightToApplyToNextBlock = -1;
|
||||
@@ -283,6 +288,12 @@ public class FullDataToRenderDataTransformer
|
||||
// cave culling check //
|
||||
//====================//
|
||||
|
||||
if (waterSubsurfaceReplacementBlocks.contains(block)
|
||||
&& (lastBlock == null || lastBlock.isAir()))
|
||||
{
|
||||
block = water;
|
||||
}
|
||||
|
||||
boolean ignoreBlock = blockStatesToIgnore.contains(block);
|
||||
boolean caveBlock = caveBlockStatesToIgnore.contains(block);
|
||||
if (caveBlock
|
||||
@@ -328,6 +339,9 @@ public class FullDataToRenderDataTransformer
|
||||
else if (ignoreBlock)
|
||||
{
|
||||
// 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;
|
||||
}
|
||||
|
||||
@@ -343,21 +357,37 @@ public class FullDataToRenderDataTransformer
|
||||
&& !block.isLiquid()
|
||||
&& block.getOpacity() != LodUtil.BLOCK_FULLY_OPAQUE;
|
||||
|
||||
// merge snow into the block below it
|
||||
if (snowLayerBlockStates.contains(block))
|
||||
// handle height reduction
|
||||
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
|
||||
blockHeight -= 1;
|
||||
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;
|
||||
|
||||
// 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);
|
||||
|
||||
if (isSnowLayer)
|
||||
{
|
||||
// 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;
|
||||
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.generation.tasks.DataSourceRetrievalResult;
|
||||
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.blockPos.DhBlockPos2D;
|
||||
import com.seibel.distanthorizons.core.util.FormatUtil;
|
||||
@@ -93,7 +92,8 @@ public class PregenManager
|
||||
private final AtomicInteger nextSectionSpiralIndex = new AtomicInteger(0);
|
||||
|
||||
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();
|
||||
|
||||
@@ -112,6 +112,7 @@ public class PregenManager
|
||||
|
||||
long timeSincePreviousTaskFinish = System.currentTimeMillis() - this.lastTaskFinishTime.getAndSet(System.currentTimeMillis());
|
||||
this.averageTaskCompletionIntervalMs.add(timeSincePreviousTaskFinish);
|
||||
this.averageTaskCompletionIntervalMsShort.add(timeSincePreviousTaskFinish);
|
||||
|
||||
PregenState.this.fillPendingQueue();
|
||||
})
|
||||
@@ -182,6 +183,13 @@ public class PregenManager
|
||||
int chunkRatePerSecond = (int) (1000 / this.averageTaskCompletionIntervalMs.getAverage() * 4 * 4);
|
||||
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}",
|
||||
this.generatedRadius.getValue(),
|
||||
chunksToGenerate,
|
||||
|
||||
@@ -80,6 +80,7 @@ public abstract class AbstractDhLevel implements IDhLevel
|
||||
//=============//
|
||||
// constructor //
|
||||
//=============//
|
||||
//region
|
||||
|
||||
protected AbstractDhLevel() { }
|
||||
|
||||
@@ -135,11 +136,14 @@ public abstract class AbstractDhLevel implements IDhLevel
|
||||
}
|
||||
}
|
||||
|
||||
//endregion
|
||||
|
||||
|
||||
|
||||
//=================//
|
||||
// default methods //
|
||||
//=================//
|
||||
//region
|
||||
|
||||
@Override
|
||||
public void updateChunkAsync(IChunkWrapper chunkWrapper, int chunkHash)
|
||||
@@ -208,11 +212,14 @@ public abstract class AbstractDhLevel implements IDhLevel
|
||||
});
|
||||
}
|
||||
|
||||
//endregion
|
||||
|
||||
|
||||
|
||||
//=======//
|
||||
// repos //
|
||||
//=======//
|
||||
//region
|
||||
|
||||
@Override
|
||||
public int getChunkHash(DhChunkPos pos)
|
||||
@@ -226,11 +233,14 @@ public abstract class AbstractDhLevel implements IDhLevel
|
||||
return (dto != null) ? dto.chunkHash : 0;
|
||||
}
|
||||
|
||||
//endregion
|
||||
|
||||
|
||||
|
||||
//=================//
|
||||
// beacon handling //
|
||||
//=================//
|
||||
//region
|
||||
|
||||
@Override
|
||||
public void updateBeaconBeamsForSectionPos(long sectionPos, List<BeaconBeamDTO> activeBeamList)
|
||||
@@ -362,6 +372,8 @@ public abstract class AbstractDhLevel implements IDhLevel
|
||||
@Nullable
|
||||
public BeaconBeamRepo getBeaconBeamRepo() { return this.beaconBeamRepo; }
|
||||
|
||||
//endregion
|
||||
|
||||
|
||||
|
||||
//================//
|
||||
|
||||
@@ -98,7 +98,7 @@ public abstract class AbstractDhServerLevel extends AbstractDhLevel implements I
|
||||
|
||||
@Override
|
||||
public boolean shouldDoWorldGen()
|
||||
{ return Config.Common.WorldGenerator.enableDistantGeneration.get() && !this.worldGenPlayerCenteringQueue.isEmpty(); }
|
||||
{ return Config.Common.WorldGenerator.enableDistantGeneration.get(); }
|
||||
|
||||
@Override
|
||||
public DhBlockPos2D getTargetPosForGeneration()
|
||||
|
||||
@@ -182,7 +182,6 @@ public class DhClientLevel extends AbstractDhLevel implements IDhClientLevel
|
||||
{
|
||||
try
|
||||
{
|
||||
// TODO this has a lock which can cause stuttering/lag issues
|
||||
this.updateBeaconBeamsForSectionPos(dataSourceDto.pos, message.payload.beaconBeams);
|
||||
}
|
||||
catch (Exception e)
|
||||
|
||||
@@ -168,7 +168,8 @@ public class LodRequestModule implements Closeable
|
||||
}
|
||||
}
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -198,7 +199,8 @@ public class LodRequestModule implements Closeable
|
||||
|
||||
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;
|
||||
|
||||
return this.retrievalQueue.startClosingAsync(true, doInterrupt)
|
||||
.exceptionally(e ->
|
||||
{
|
||||
LOGGER.error("Error during first stage of generation queue shutdown, Error: ["+e.getMessage()+"].", e);
|
||||
return null;
|
||||
}
|
||||
).thenRun(this.retrievalQueue::close)
|
||||
.exceptionally(e ->
|
||||
{
|
||||
LOGGER.error("Error during second stage of generation queue shutdown, Error: ["+e.getMessage()+"].", e);
|
||||
return null;
|
||||
});
|
||||
.exceptionally(e ->
|
||||
{
|
||||
LOGGER.error("Error during first stage of generation queue shutdown, Error: ["+e.getMessage()+"].", e);
|
||||
return null;
|
||||
}
|
||||
).thenRun(this.retrievalQueue::close)
|
||||
.exceptionally(e ->
|
||||
{
|
||||
LOGGER.error("Error during second stage of generation queue shutdown, Error: ["+e.getMessage()+"].", e);
|
||||
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.IDhLevel;
|
||||
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.pos.DhSectionPos;
|
||||
import com.seibel.distanthorizons.core.render.RenderBufferHandler;
|
||||
@@ -149,6 +150,13 @@ public class F3Screen
|
||||
messageList.add("");
|
||||
}
|
||||
|
||||
// render thread tasks
|
||||
if (Config.Client.Advanced.Debugging.F3Screen.showRenderThreadTasks.get())
|
||||
{
|
||||
RenderThreadTaskHandler.INSTANCE.addDebugMenuStringsToList(messageList);
|
||||
messageList.add("");
|
||||
}
|
||||
|
||||
// combined object pools
|
||||
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.DhSectionPos;
|
||||
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.BeaconRenderHandler;
|
||||
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.ThreadUtil;
|
||||
import com.seibel.distanthorizons.core.util.WorldGenUtil;
|
||||
@@ -54,10 +57,7 @@ import org.jetbrains.annotations.Nullable;
|
||||
import javax.annotation.WillNotClose;
|
||||
import java.awt.*;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
import java.util.concurrent.ThreadPoolExecutor;
|
||||
import java.util.concurrent.*;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
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);
|
||||
|
||||
|
||||
/**
|
||||
* 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
|
||||
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 */
|
||||
private byte maxLeafRenderDetailLevel;
|
||||
@@ -148,6 +159,8 @@ public class LodQuadTree extends QuadTree<LodRenderSection> implements IDebugRen
|
||||
IDhGenericRenderer genericObjectRenderer = this.level.getGenericRenderer();
|
||||
this.beaconRenderHandler = (genericObjectRenderer != null) ? new BeaconRenderHandler(genericObjectRenderer) : null;
|
||||
|
||||
this.beaconBeamRepo = this.level.getBeaconBeamRepo();
|
||||
|
||||
Config.Common.WorldGenerator.enableDistantGeneration.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; }
|
||||
|
||||
node.value.setRenderingEnabled(false);
|
||||
node.value.tryDisableBeacons();
|
||||
}
|
||||
|
||||
for (QuadNode<LodRenderSection> node : this.tickNodeHolder.getEnableDeleteChildrenNodes())
|
||||
{
|
||||
if (node == null || node.value == null) { continue; }
|
||||
|
||||
node.deleteAllChildren((childRenderSection) ->
|
||||
if (node == null
|
||||
|| node.value == null
|
||||
// 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);
|
||||
childRenderSection.tryDisableBeacons();
|
||||
childRenderSection.close();
|
||||
}
|
||||
if (childRenderSection != null)
|
||||
{
|
||||
childRenderSection.setRenderingEnabled(false);
|
||||
childRenderSection.close();
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@@ -393,21 +415,7 @@ public class LodQuadTree extends QuadTree<LodRenderSection> implements IDebugRen
|
||||
//=================//
|
||||
//region
|
||||
|
||||
// must be handled after beacon disabling
|
||||
// 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();
|
||||
}
|
||||
this.tryRefreshRenderingBeaconsAsync(playerPos);
|
||||
|
||||
//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 //
|
||||
//====================//
|
||||
@@ -873,8 +1008,10 @@ public class LodQuadTree extends QuadTree<LodRenderSection> implements IDebugRen
|
||||
* @return detail level of this section pos
|
||||
*/
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
-146
@@ -73,21 +73,6 @@ public class LodRenderSection implements IDebugRenderable, AutoCloseable
|
||||
private final FullDataSourceProviderV2 fullDataSourceProvider;
|
||||
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 beaconsRendering = false;
|
||||
@@ -134,9 +119,6 @@ public class LodRenderSection implements IDebugRenderable, AutoCloseable
|
||||
this.levelWrapper = level.getClientLevelWrapper();
|
||||
this.fullDataSourceProvider = fullDataSourceProvider;
|
||||
|
||||
this.beaconRenderHandler = this.quadTree.beaconRenderHandler;
|
||||
this.beaconBeamRepo = this.level.getBeaconBeamRepo();
|
||||
|
||||
DEBUG_RENDERER.register(this, Config.Client.Advanced.Debugging.DebugWireframe.showRenderSectionStatus);
|
||||
}
|
||||
|
||||
@@ -188,8 +170,6 @@ public class LodRenderSection implements IDebugRenderable, AutoCloseable
|
||||
{
|
||||
try
|
||||
{
|
||||
this.refreshActiveBeaconList();
|
||||
|
||||
LodQuadBuilder lodQuadBuilder = this.getAndBuildRenderData();
|
||||
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 //
|
||||
//==============//
|
||||
@@ -562,8 +418,6 @@ public class LodRenderSection implements IDebugRenderable, AutoCloseable
|
||||
}
|
||||
|
||||
|
||||
this.tryDisableBeacons();
|
||||
|
||||
if (this.renderBufferContainer != null)
|
||||
{
|
||||
this.renderBufferContainer.close();
|
||||
|
||||
@@ -172,43 +172,43 @@ public class RenderParams extends DhApiRenderParam
|
||||
}
|
||||
|
||||
|
||||
// potential fix for a segfault when
|
||||
// Sodium and DH are running together
|
||||
if (EPlatform.get() == EPlatform.MACOS
|
||||
&& !initialLoadingComplete)
|
||||
{
|
||||
// Once MC starts rendering, wait a few seconds so
|
||||
// MC/Sodium can finish their shader compiling before DH does its own.
|
||||
// This will allow DH to compile its own shaders after Sodium finishes
|
||||
// compiling its own.
|
||||
long nowMs = System.currentTimeMillis();
|
||||
long firstAllowedRenderTimeMs = firstRenderTimeMs + TIME_FOR_MAC_TO_FINISH_COMPILING_IN_MS;
|
||||
if (nowMs < firstAllowedRenderTimeMs)
|
||||
{
|
||||
return "Waiting for initial MC compile...";
|
||||
}
|
||||
|
||||
|
||||
// null shouldn't happen, but just in case
|
||||
PriorityTaskPicker.Executor renderLoadExecutor = ThreadPoolUtil.getRenderLoadingExecutor();
|
||||
if (renderLoadExecutor == null)
|
||||
{
|
||||
return "Waiting for DH Threadpool...";
|
||||
}
|
||||
|
||||
// wait for DH to finish loading, by the time that's done
|
||||
// java should have finished all of DH's JIT compiling,
|
||||
// which will hopefully mean less concurrency and thus a lower
|
||||
// chance of breaking
|
||||
// (plus this gives Sodium/vanill a bit longer to finish their setup)
|
||||
int taskCount = renderLoadExecutor.getQueueSize();
|
||||
if (taskCount > 0)
|
||||
{
|
||||
return "Waiting for DH JIT compiling...";
|
||||
}
|
||||
|
||||
initialLoadingComplete = true;
|
||||
}
|
||||
//// potential fix for a segfault when
|
||||
//// Sodium and DH are running together
|
||||
//if (EPlatform.get() == EPlatform.MACOS
|
||||
// && !initialLoadingComplete)
|
||||
//{
|
||||
// // Once MC starts rendering, wait a few seconds so
|
||||
// // MC/Sodium can finish their shader compiling before DH does its own.
|
||||
// // This will allow DH to compile its own shaders after Sodium finishes
|
||||
// // compiling its own.
|
||||
// long nowMs = System.currentTimeMillis();
|
||||
// long firstAllowedRenderTimeMs = firstRenderTimeMs + TIME_FOR_MAC_TO_FINISH_COMPILING_IN_MS;
|
||||
// if (nowMs < firstAllowedRenderTimeMs)
|
||||
// {
|
||||
// return "Waiting for initial MC compile...";
|
||||
// }
|
||||
//
|
||||
//
|
||||
// // null shouldn't happen, but just in case
|
||||
// PriorityTaskPicker.Executor renderLoadExecutor = ThreadPoolUtil.getRenderLoadingExecutor();
|
||||
// if (renderLoadExecutor == null)
|
||||
// {
|
||||
// return "Waiting for DH Threadpool...";
|
||||
// }
|
||||
//
|
||||
// // wait for DH to finish loading, by the time that's done
|
||||
// // java should have finished all of DH's JIT compiling,
|
||||
// // which will hopefully mean less concurrency and thus a lower
|
||||
// // chance of breaking
|
||||
// // (plus this gives Sodium/vanill a bit longer to finish their setup)
|
||||
// int taskCount = renderLoadExecutor.getQueueSize();
|
||||
// if (taskCount > 0)
|
||||
// {
|
||||
// return "Waiting for DH JIT compiling...";
|
||||
// }
|
||||
//
|
||||
// initialLoadingComplete = true;
|
||||
//}
|
||||
|
||||
|
||||
return null;
|
||||
|
||||
+226
-36
@@ -2,32 +2,53 @@ package com.seibel.distanthorizons.core.render;
|
||||
|
||||
import com.seibel.distanthorizons.core.config.Config;
|
||||
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.DhLoggerBuilder;
|
||||
import com.seibel.distanthorizons.core.util.ExceptionUtil;
|
||||
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.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.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.LongAdder;
|
||||
|
||||
public class RenderThreadTaskHandler
|
||||
{
|
||||
public static final DhLogger LOGGER = new DhLoggerBuilder()
|
||||
private static final DhLogger LOGGER = new DhLoggerBuilder()
|
||||
.fileLevelConfig(Config.Common.Logging.logRendererEventToFile)
|
||||
.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 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();
|
||||
|
||||
|
||||
private long msSinceGlTasksRun = System.currentTimeMillis();
|
||||
private long nanoSinceTasksRun = System.nanoTime();
|
||||
|
||||
|
||||
|
||||
@@ -47,23 +68,17 @@ public class RenderThreadTaskHandler
|
||||
//==============//
|
||||
//region
|
||||
|
||||
public void queueRunningOnRenderThread(Runnable renderCall)
|
||||
public void queueRunningOnRenderThread(String name, Runnable renderCall)
|
||||
{
|
||||
StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
|
||||
RENDER_THREAD_RUNNABLE_QUEUE.add(() -> this.createRenderThreadRunnable(renderCall, stackTrace));
|
||||
}
|
||||
private void createRenderThreadRunnable(Runnable renderCall, StackTraceElement[] stackTrace)
|
||||
{
|
||||
try
|
||||
// don't get the stacktrace on release to reduce GC pressure
|
||||
StackTraceElement[] stackTrace = null;
|
||||
if (ModInfo.IS_DEV_BUILD)
|
||||
{
|
||||
renderCall.run();
|
||||
}
|
||||
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);
|
||||
stackTrace = Thread.currentThread().getStackTrace();
|
||||
}
|
||||
|
||||
QueuedRunnable runnable = new QueuedRunnable(name, renderCall, stackTrace);
|
||||
RENDER_THREAD_RUNNABLE_QUEUE.add(runnable);
|
||||
}
|
||||
|
||||
//endregion
|
||||
@@ -83,31 +98,63 @@ public class RenderThreadTaskHandler
|
||||
{
|
||||
IMinecraftRenderWrapper MC_RENDER = SingletonInjector.INSTANCE.get(IMinecraftRenderWrapper.class);
|
||||
|
||||
// https://fpstoms.com/
|
||||
int frameLimit = MC_RENDER.getFrameLimit();
|
||||
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)
|
||||
{
|
||||
long taskStartNano = System.nanoTime();
|
||||
|
||||
runnable.run();
|
||||
|
||||
// only try running for a limited amount of time to prevent lag spikes
|
||||
long currentTimeMs = System.currentTimeMillis();
|
||||
long runDuration = currentTimeMs - startTimeMs;
|
||||
if (runDuration > msMaxRunTime)
|
||||
long taskNano = System.nanoTime() - taskStartNano;
|
||||
long totalLoopNano = System.nanoTime() - loopStartTimeNano;
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
@@ -121,9 +168,9 @@ public class RenderThreadTaskHandler
|
||||
*/
|
||||
private void manualCleanupTick()
|
||||
{
|
||||
long nowMs = System.currentTimeMillis();
|
||||
long msSinceLast = nowMs - this.msSinceGlTasksRun;
|
||||
if (msSinceLast > MS_BEFORE_RUN_CLEANUP_TIMER)
|
||||
long nowNano = System.nanoTime();
|
||||
long nanoSinceLast = nowNano - this.nanoSinceTasksRun;
|
||||
if (nanoSinceLast < NANOS_BEFORE_RUN_CLEANUP_TIMER)
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -133,10 +180,153 @@ public class RenderThreadTaskHandler
|
||||
// Run the queued tasks on MC's executor (hopefully this should always run,
|
||||
// even if DH's render code isn't being hit).
|
||||
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.ThreadPoolExecutor;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
public class BeaconRenderHandler
|
||||
{
|
||||
@@ -57,8 +56,6 @@ public class BeaconRenderHandler
|
||||
/** 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 Comparator<BeaconBeamDTO> NEGATIVE_BLOCKPOS_COMPARATOR = new NegativeInfiniteBlockPosComparator();
|
||||
|
||||
|
||||
|
||||
private final ReentrantLock updateLock = new ReentrantLock();
|
||||
@@ -67,8 +64,6 @@ public class BeaconRenderHandler
|
||||
private final IDhApiRenderableBoxGroup activeBeaconBoxRenderGroup;
|
||||
/** contains all beacons that could be rendered (including those that are being culled) */
|
||||
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 updateRenderDataNextFrame = false;
|
||||
@@ -96,125 +91,11 @@ public class BeaconRenderHandler
|
||||
|
||||
|
||||
|
||||
//=================//
|
||||
// render handling //
|
||||
//=================//
|
||||
//===============//
|
||||
// before render //
|
||||
//===============//
|
||||
//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)
|
||||
{
|
||||
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 //
|
||||
//================//
|
||||
//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
|
||||
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 blockPos2 = beacon2.blockPos;
|
||||
|
||||
+1
-15
@@ -96,9 +96,6 @@ public class CloudRenderHandler
|
||||
};
|
||||
|
||||
|
||||
private boolean disabledWarningLogged = false;
|
||||
|
||||
|
||||
|
||||
//=============//
|
||||
// constructor //
|
||||
@@ -287,7 +284,7 @@ public class CloudRenderHandler
|
||||
CloudParams cloudParams = new CloudParams(textureWidth, x, z);
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -319,17 +316,6 @@ public class CloudRenderHandler
|
||||
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();
|
||||
if (clientLevelWrapper == null)
|
||||
{
|
||||
|
||||
@@ -101,7 +101,7 @@ public class LodRenderer
|
||||
* otherwise it will only render opaque LODs.
|
||||
*/
|
||||
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
|
||||
@@ -110,9 +110,9 @@ public class LodRenderer
|
||||
* but shouldn't be activated as per deferWaterRendering.
|
||||
*/
|
||||
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 //
|
||||
@@ -201,7 +201,7 @@ public class LodRenderer
|
||||
// opaque LODs
|
||||
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
|
||||
if (Config.Client.Advanced.Graphics.GenericRendering.enableGenericRendering.get())
|
||||
@@ -211,7 +211,7 @@ public class LodRenderer
|
||||
}
|
||||
|
||||
// SSAO
|
||||
if (Config.Client.Advanced.Graphics.Ssao.enableSsao.get())
|
||||
if (Config.Client.Advanced.Graphics.enableSsao.get())
|
||||
{
|
||||
profiler.popPush("LOD SSAO");
|
||||
this.ssaoRenderer.render(renderParams);
|
||||
@@ -229,7 +229,7 @@ public class LodRenderer
|
||||
&& Config.Client.Advanced.Graphics.Quality.transparency.get().transparencyEnabled)
|
||||
{
|
||||
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
|
||||
@@ -287,7 +287,7 @@ public class LodRenderer
|
||||
if (Config.Client.Advanced.Graphics.Quality.transparency.get().transparencyEnabled)
|
||||
{
|
||||
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()
|
||||
@@ -327,7 +327,7 @@ public class LodRenderer
|
||||
//===============//
|
||||
//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 //
|
||||
@@ -338,7 +338,7 @@ public class LodRenderer
|
||||
SortedArraySet<LodBufferContainer> lodBufferContainer = lodBufferHandler.getColumnRenderBuffers();
|
||||
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 ssaoEnabled = true;
|
||||
private boolean vertexDataDirty = true;
|
||||
private boolean vertexDataDirty = false;
|
||||
|
||||
public byte skyLight = LodUtil.MAX_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)
|
||||
{
|
||||
this.altVertexBufferContainer.uploadDataToGpu();
|
||||
this.altVertexBufferContainer.setState(IDhGenericObjectVertexBufferContainer.EState.RENDER);
|
||||
|
||||
// swap VBO references for rendering
|
||||
IDhGenericObjectVertexBufferContainer temp = this.vertexBufferContainer;
|
||||
@@ -248,6 +249,7 @@ public class RenderableBoxGroup
|
||||
try
|
||||
{
|
||||
this.altVertexBufferContainer.updateVertexData(this.uploadBoxList);
|
||||
this.altVertexBufferContainer.setState(IDhGenericObjectVertexBufferContainer.EState.READY_TO_UPLOAD);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
@@ -346,7 +348,7 @@ public class RenderableBoxGroup
|
||||
@Override
|
||||
public void close()
|
||||
{
|
||||
RenderThreadTaskHandler.INSTANCE.queueRunningOnRenderThread(() ->
|
||||
RenderThreadTaskHandler.INSTANCE.queueRunningOnRenderThread("RenderBoxGroup Close", () ->
|
||||
{
|
||||
this.vertexBufferContainer.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
|
||||
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
|
||||
public void close()
|
||||
{ /* 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.dto.IBaseDTO;
|
||||
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.coreapi.ModInfo;
|
||||
import com.seibel.distanthorizons.core.logging.DhLogger;
|
||||
import com.seibel.distanthorizons.coreapi.util.StringUtil;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
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);
|
||||
if (connection != null)
|
||||
{
|
||||
// don't try closing an already closed connection
|
||||
if (!connection.isClosed())
|
||||
{
|
||||
LOGGER.info("Closing database connection: [" + connectionString + "]");
|
||||
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)
|
||||
@@ -560,22 +554,13 @@ public abstract class AbstractDhRepo<TKey, TDTO extends IBaseDTO<TKey>> implemen
|
||||
LOGGER.warn(stringBuilder.toString());
|
||||
}
|
||||
|
||||
|
||||
// don't try closing an already closed connection
|
||||
if (!this.connection.isClosed())
|
||||
{
|
||||
LOGGER.info("Closing database connection: [" + this.connectionString + "]...");
|
||||
this.connection.close();
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -19,12 +19,17 @@
|
||||
|
||||
package com.seibel.distanthorizons.core.util;
|
||||
|
||||
import com.seibel.distanthorizons.api.DhApi;
|
||||
import com.seibel.distanthorizons.api.objects.math.DhApiMat4f;
|
||||
import com.seibel.distanthorizons.core.api.internal.ClientApi;
|
||||
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.logging.DhLogger;
|
||||
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper;
|
||||
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.coreapi.util.MathUtil;
|
||||
import com.seibel.distanthorizons.core.util.math.Mat4f;
|
||||
@@ -35,8 +40,11 @@ import com.seibel.distanthorizons.core.util.math.Mat4f;
|
||||
*/
|
||||
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 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
|
||||
@@ -79,7 +87,7 @@ public class RenderUtil
|
||||
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.
|
||||
Mat4f lodProj = new Mat4f(mcProjMat);
|
||||
@@ -245,12 +253,31 @@ public class RenderUtil
|
||||
|
||||
//region
|
||||
|
||||
public static int getFarClipPlaneDistanceInBlocks()
|
||||
public static float getFarClipPlaneDistanceInBlocks()
|
||||
{
|
||||
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;
|
||||
if (IRIS_ACCESSOR != null)
|
||||
{
|
||||
// Iris doesn't use the far clip plane DH generates, instead
|
||||
// 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
|
||||
|
||||
@@ -17,12 +17,15 @@ public class RollingAverage
|
||||
private int index = 0;
|
||||
private double sum = 0.0;
|
||||
private final Lock arrayLock = new ReentrantLock();
|
||||
/** how many items have been added to this average over its lifetime */
|
||||
private long lifetimeCount = 0;
|
||||
|
||||
|
||||
|
||||
//=============//
|
||||
// constructor //
|
||||
//=============//
|
||||
//region
|
||||
|
||||
public RollingAverage(int size)
|
||||
{
|
||||
@@ -35,11 +38,14 @@ public class RollingAverage
|
||||
this.values = new double[size];
|
||||
}
|
||||
|
||||
//endregion
|
||||
|
||||
|
||||
|
||||
//=======//
|
||||
// input //
|
||||
//=======//
|
||||
//region
|
||||
|
||||
public void add(double value)
|
||||
{
|
||||
@@ -56,6 +62,8 @@ public class RollingAverage
|
||||
this.index = (this.index + 1) % this.maxSize;
|
||||
|
||||
this.currentSize = Math.max(this.index+1, this.currentSize);
|
||||
|
||||
this.lifetimeCount++;
|
||||
}
|
||||
finally
|
||||
{
|
||||
@@ -71,6 +79,7 @@ public class RollingAverage
|
||||
this.sum = 0;
|
||||
this.index = 0;
|
||||
this.currentSize = 0;
|
||||
this.lifetimeCount = 0;
|
||||
Arrays.fill(this.values, 0);
|
||||
}
|
||||
finally
|
||||
@@ -79,11 +88,14 @@ public class RollingAverage
|
||||
}
|
||||
}
|
||||
|
||||
//endregion
|
||||
|
||||
|
||||
|
||||
//========//
|
||||
// output //
|
||||
//========//
|
||||
//region
|
||||
|
||||
/** Gets the current rolling average. */
|
||||
public double getAverage()
|
||||
@@ -101,14 +113,23 @@ public class RollingAverage
|
||||
/** rounded to two decimals*/
|
||||
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 //
|
||||
//================//
|
||||
//region
|
||||
|
||||
@Override
|
||||
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
|
||||
{
|
||||
// this reference is pointing to null,
|
||||
// the checkout must have been garbage collected,
|
||||
// that means we don't have enough memory
|
||||
// the checkout was garbage collected
|
||||
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" +
|
||||
"Potential causes: \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" +
|
||||
"4. you have other memory hungry mod(s)";
|
||||
|
||||
LOGGER.warn(message);
|
||||
if (Config.Common.Logging.Warning.showPoolInsufficientMemoryWarning.get())
|
||||
{
|
||||
ClientApi.INSTANCE.showChatMessageNextFrame(message);
|
||||
LOGGER.warn(message);
|
||||
if (Config.Common.Logging.Warning.showPoolInsufficientMemoryWarning.get())
|
||||
{
|
||||
ClientApi.INSTANCE.showChatMessageNextFrame(message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -319,7 +329,7 @@ public class PhantomArrayListPool
|
||||
pool.returnCheckout(checkout);
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
+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.
|
||||
*
|
||||
|
||||
+7
-7
@@ -179,13 +179,13 @@ public class ThreadPoolUtil
|
||||
return false;
|
||||
}
|
||||
|
||||
PriorityTaskPicker.Executor executor = getRenderLoadingExecutor();
|
||||
if (executor != null
|
||||
&& executor.getQueueSize() > 0)
|
||||
{
|
||||
// pause if LODs are being loaded for rendering
|
||||
return false;
|
||||
}
|
||||
//PriorityTaskPicker.Executor executor = getRenderLoadingExecutor();
|
||||
//if (executor != null
|
||||
// && executor.getQueueSize() > 0)
|
||||
//{
|
||||
// // pause if LODs are being loaded for rendering
|
||||
// return false;
|
||||
//}
|
||||
|
||||
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.enums.MinecraftTextFormat;
|
||||
import com.seibel.distanthorizons.core.generation.PregenManager;
|
||||
import com.seibel.distanthorizons.core.level.DhServerLevel;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IServerLevelWrapper;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
public class DhServerWorld extends AbstractDhServerWorld<DhServerLevel>
|
||||
{
|
||||
private final PregenManager pregenManager = new PregenManager();
|
||||
public PregenManager getPregenManager() { return this.pregenManager; }
|
||||
|
||||
|
||||
//==============//
|
||||
// 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 getAirBlockStateWrapper();
|
||||
IBlockStateWrapper getWaterBlockStateWrapper(ILevelWrapper levelWrapper);
|
||||
default IBlockStateWrapper deserializeBlockStateWrapperOrGetDefault(String str, ILevelWrapper levelWrapper)
|
||||
{
|
||||
IBlockStateWrapper blockState;
|
||||
@@ -92,10 +93,10 @@ public interface IWrapperFactory extends IDhApiWrapperFactory, IBindable
|
||||
*/
|
||||
ObjectOpenHashSet<IBlockStateWrapper> getRendererIgnoredCaveBlocks(ILevelWrapper levelWrapper);
|
||||
|
||||
ObjectOpenHashSet<IBlockStateWrapper> getWaterSubsurfaceReplacementBlocks(ILevelWrapper levelWrapper);
|
||||
ObjectOpenHashSet<IBlockStateWrapper> getWaterSurfaceReplacementBlocks(ILevelWrapper levelWrapper);
|
||||
/** clears the cached values */
|
||||
void resetRendererIgnoredCaveBlocks();
|
||||
/** clears the cached values */
|
||||
void resetRendererIgnoredBlocksSet();
|
||||
void resetCachedIgnoredBlocksSets();
|
||||
|
||||
|
||||
/**
|
||||
@@ -109,9 +110,7 @@ public interface IWrapperFactory extends IDhApiWrapperFactory, IBindable
|
||||
|
||||
IVertexBufferWrapper createVboWrapper(String name);
|
||||
ILodContainerUniformBufferWrapper createLodContainerUniformWrapper();
|
||||
|
||||
IDhGenericObjectVertexBufferContainer createGenericObjectVboContainer();
|
||||
|
||||
IDhGenericRenderer createGenericRenderer();
|
||||
|
||||
}
|
||||
|
||||
-2
@@ -68,8 +68,6 @@ public interface IMinecraftRenderWrapper extends IBindable
|
||||
|
||||
Color getSkyColor();
|
||||
|
||||
double getFov(float partialTicks);
|
||||
|
||||
/** Measured in chunks */
|
||||
int getRenderDistance();
|
||||
|
||||
|
||||
+16
@@ -1,6 +1,7 @@
|
||||
package com.seibel.distanthorizons.core.wrapperInterfaces.render;
|
||||
|
||||
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.wrapperInterfaces.render.objects.IDhGenericObjectVertexBufferContainer;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.render.objects.ILodContainerUniformBufferWrapper;
|
||||
@@ -18,6 +19,16 @@ public abstract class AbstractDhRenderApiDefinition implements IBindable
|
||||
/** Used for debugging */
|
||||
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
|
||||
|
||||
|
||||
@@ -36,6 +47,11 @@ public abstract class AbstractDhRenderApiDefinition implements IBindable
|
||||
public abstract IDhVanillaFadeRenderer getVanillaFadeRenderer();
|
||||
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()
|
||||
{
|
||||
SingletonInjector.INSTANCE.bind(AbstractDhRenderApiDefinition.class, this);
|
||||
|
||||
+4
-1
@@ -19,13 +19,16 @@
|
||||
|
||||
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 java.nio.ByteBuffer;
|
||||
|
||||
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
|
||||
void close();
|
||||
|
||||
@@ -173,48 +173,17 @@
|
||||
|
||||
|
||||
|
||||
"distanthorizons.config.client.advanced.graphics.ssao":
|
||||
"Ambient Occlusion",
|
||||
|
||||
"distanthorizons.config.client.advanced.graphics.ssao.enableSsao":
|
||||
"distanthorizons.config.client.advanced.graphics.enableSsao":
|
||||
"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.",
|
||||
"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":
|
||||
"Generic Object Rendering",
|
||||
"distanthorizons.config.client.advanced.graphics.genericRendering.enableGenericRendering":
|
||||
"Enable Rendering",
|
||||
"Enable Generic Rendering",
|
||||
"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.",
|
||||
"distanthorizons.config.client.advanced.graphics.genericRendering.enableBeaconRendering":
|
||||
@@ -369,7 +338,7 @@
|
||||
"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.",
|
||||
"distanthorizons.config.client.advanced.graphics.culling.enableCaveCulling":
|
||||
"Cave Culling",
|
||||
"Enable Cave Culling",
|
||||
"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",
|
||||
"distanthorizons.config.client.advanced.graphics.culling.caveCullingHeight":
|
||||
@@ -391,11 +360,19 @@
|
||||
"distanthorizons.config.client.advanced.graphics.culling.ignoredRenderBlockCsv":
|
||||
"Ignored Block CSV",
|
||||
"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":
|
||||
"Ignored Cave Block CSV",
|
||||
"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":
|
||||
"Override Vanilla Settings",
|
||||
@@ -454,8 +431,8 @@
|
||||
|
||||
"distanthorizons.config.client.advanced.debugging.rendererMode":
|
||||
"Renderer Mode",
|
||||
"distanthorizons.config.client.advanced.debugging.debugRendering":
|
||||
"Debug Rendering",
|
||||
"distanthorizons.config.client.advanced.debugging.debugRenderingColors":
|
||||
"Debug Rendering Colors",
|
||||
"distanthorizons.config.client.advanced.debugging.lodOnlyMode":
|
||||
"Only Render LODs",
|
||||
"distanthorizons.config.client.advanced.debugging.lodOnlyMode.@tooltip":
|
||||
@@ -565,6 +542,8 @@
|
||||
"Player Section Pos Detail Level",
|
||||
"distanthorizons.config.client.advanced.debugging.f3Screen.showThreadPools":
|
||||
"Show Thread Pools",
|
||||
"distanthorizons.config.client.advanced.debugging.f3Screen.showRenderThreadTasks":
|
||||
"Show Render Thread Tasks",
|
||||
"distanthorizons.config.client.advanced.debugging.f3Screen.showCombinedObjectPools":
|
||||
"Show Combined Object Pools",
|
||||
"distanthorizons.config.client.advanced.debugging.f3Screen.showSeparatedObjectPools":
|
||||
@@ -1043,7 +1022,7 @@
|
||||
|
||||
"distanthorizons.config.enum.EDhApiRendererMode.DEFAULT":
|
||||
"Default",
|
||||
"distanthorizons.config.enum.EDhApiRendererMode.DEBUG":
|
||||
"distanthorizons.config.enum.EDhApiRendererMode.DEBUG_TRIANGLE":
|
||||
"Debug Triangle",
|
||||
"distanthorizons.config.enum.EDhApiRendererMode.DISABLED":
|
||||
"Disabled",
|
||||
|
||||
@@ -39,14 +39,14 @@ public class DhApiConfigTest
|
||||
public void ConfigTest()
|
||||
{
|
||||
ConfigEntry<EDhApiRendererMode> coreConfig = new ConfigEntry.Builder<EDhApiRendererMode>()
|
||||
.set(EDhApiRendererMode.DEBUG)
|
||||
.set(EDhApiRendererMode.DEBUG_TRIANGLE)
|
||||
.build();
|
||||
|
||||
DhApiConfigValue<EDhApiRendererMode, Boolean> apiConfig = new DhApiConfigValue<>(coreConfig, new RenderModeEnabledConverter());
|
||||
|
||||
// start with no API value
|
||||
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
|
||||
@@ -62,7 +62,7 @@ public class DhApiConfigTest
|
||||
// clear API value
|
||||
apiConfig.clearValue();
|
||||
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