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();
|
||||
|
||||
// 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);
|
||||
// starting and stopping the DataRenderTransformer is necessary to prevent attempting to
|
||||
// access the MC level at inappropriate times, which can cause exceptions
|
||||
if (currentWorld != null)
|
||||
{
|
||||
ThreadPoolUtil.setupThreadPools();
|
||||
|
||||
ApiEventInjector.INSTANCE.fireAllEvents(DhApiWorldLoadEvent.class, new DhApiWorldLoadEvent.EventParam());
|
||||
}
|
||||
else
|
||||
{
|
||||
ThreadPoolUtil.shutdownThreadPools();
|
||||
|
||||
// delayed get because SharedApi will be created before the singleton has been bound
|
||||
AbstractDebugWireframeRenderer debugWireframeRenderer = SingletonInjector.INSTANCE.get(AbstractDebugWireframeRenderer.class);
|
||||
debugWireframeRenderer.clearRenderables();
|
||||
|
||||
if (MC_RENDER != null)
|
||||
{
|
||||
MC_RENDER.clearTargetFrameBuffer();
|
||||
}
|
||||
|
||||
// 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();
|
||||
|
||||
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();
|
||||
|
||||
static
|
||||
{
|
||||
ignoredRenderBlockCsv.addListener(new ConfigChangeListener<String>(ignoredRenderBlockCsv,
|
||||
(blockCsv) ->
|
||||
{
|
||||
IWrapperFactory wrapperFactory = SingletonInjector.INSTANCE.get(IWrapperFactory.class);
|
||||
if (wrapperFactory != null)
|
||||
{
|
||||
wrapperFactory.resetRendererIgnoredBlocksSet();
|
||||
DhApi.Delayed.renderProxy.clearRenderDataCache();
|
||||
}
|
||||
}));
|
||||
|
||||
ignoredRenderCaveBlockCsv.addListener(new ConfigChangeListener<String>(ignoredRenderCaveBlockCsv,
|
||||
(blockCsv) ->
|
||||
{
|
||||
IWrapperFactory wrapperFactory = SingletonInjector.INSTANCE.get(IWrapperFactory.class);
|
||||
if (wrapperFactory != null)
|
||||
{
|
||||
wrapperFactory.resetRendererIgnoredCaveBlocks();
|
||||
DhApi.Delayed.renderProxy.clearRenderDataCache();
|
||||
}
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
public static class Experimental
|
||||
@@ -834,8 +814,7 @@ public class Config
|
||||
.build();
|
||||
|
||||
public static ConfigEntry<EDhApiRenderApi> renderingApi = new ConfigEntry.Builder<EDhApiRenderApi>()
|
||||
.set(EDhApiRenderApi.AUTO)
|
||||
.setAppearance(EConfigEntryAppearance.ONLY_IN_FILE) // very experimental option and only supported
|
||||
.set(EDhApiRenderApi.AUTO)
|
||||
.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();
|
||||
})
|
||||
@@ -181,7 +182,14 @@ public class PregenManager
|
||||
double chunksToGenerate = Math.ceil(Math.sqrt(this.sectionsToGenerate) / 2 * 4 * 10) / 10; // ceil to nearest 0.1
|
||||
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())
|
||||
{
|
||||
|
||||
+168
-31
@@ -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,9 +1008,11 @@ 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));
|
||||
return this.calcDetailLevelFromDistance(blockDistance);
|
||||
double blockDistance = playerPos.dist(targetBlockPosX, targetBlockPosZ);
|
||||
return this.calcDetailLevelFromDistance(blockDistance);
|
||||
}
|
||||
|
||||
private void updateDetailLevelVariables()
|
||||
|
||||
-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
|
||||
|
||||
|
||||
|
||||
|
||||
+128
-126
@@ -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,126 +91,12 @@ 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)
|
||||
private void beforeRender(DhApiRenderParam renderEventParam)
|
||||
{
|
||||
if (Config.Client.Advanced.Graphics.Culling.disableBeaconDistanceCulling.get())
|
||||
{
|
||||
@@ -237,7 +118,7 @@ public class BeaconRenderHandler
|
||||
private void tryUpdateBeaconCullingAsync()
|
||||
{
|
||||
ThreadPoolExecutor executor = ThreadPoolUtil.getBeaconCullingExecutor();
|
||||
if (executor != null
|
||||
if (executor != null
|
||||
&& !this.cullingThreadRunning)
|
||||
{
|
||||
this.cullingThreadRunning = true;
|
||||
@@ -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 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(BeaconBeamDTO beacon1, BeaconBeamDTO beacon2)
|
||||
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
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
+20
-10
@@ -185,24 +185,34 @@ 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" +
|
||||
"2. your DH CPU preset is too high \n" +
|
||||
"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();
|
||||
|
||||
@@ -172,49 +172,18 @@
|
||||
"This is the same as vanilla Biome Blending settings for LOD area. \n\nNote that anything other than '0' will greatly effect Lod building time\nand increase triangle count. The cost on chunk generation speed is also \nquite large if set to too high.\n\n'0' equals to Vanilla Biome Blending of '1x1', \n'1' equals to Vanilla Biome Blending of '3x3', \n'2' equals to Vanilla Biome Blending of '5x5'... \n",
|
||||
|
||||
|
||||
|
||||
"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,12 +360,20 @@
|
||||
"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",
|
||||
"distanthorizons.config.client.advanced.graphics.overrideVanillaGraphicsSettings.@tooltip":
|
||||
@@ -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