diff --git a/build.gradle b/build.gradle index 271373346..ba512ff5a 100644 --- a/build.gradle +++ b/build.gradle @@ -21,7 +21,7 @@ apply plugin: 'eclipse' apply plugin: 'maven-publish' apply plugin: 'com.github.johnrengelman.shadow' -version = 'a1.5.0' +version = 'a1.5.1' group = 'com.seibel.lod' archivesBaseName = 'Distant-Horizons_1.16.5' diff --git a/src/main/java/com/seibel/lod/LodMain.java b/src/main/java/com/seibel/lod/LodMain.java index 895640e13..19e88b0bc 100644 --- a/src/main/java/com/seibel/lod/LodMain.java +++ b/src/main/java/com/seibel/lod/LodMain.java @@ -41,7 +41,7 @@ import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext; * @author James Seibel * @version 7-3-2021 */ -@Mod(ModInfo.MODID) +@Mod(ModInfo.ID) public class LodMain { public static LodMain instance; diff --git a/src/main/java/com/seibel/lod/ModInfo.java b/src/main/java/com/seibel/lod/ModInfo.java index 5738d05b9..76ccad425 100644 --- a/src/main/java/com/seibel/lod/ModInfo.java +++ b/src/main/java/com/seibel/lod/ModInfo.java @@ -22,12 +22,14 @@ package com.seibel.lod; /** * This file is similar to mcmod.info * @author James Seibel - * @version 10-16-2021 + * @version 10-23-2021 */ public final class ModInfo { - public static final String MODID = "lod"; - public static final String MODNAME = "DistantHorizons"; - public static final String MODAPI = "LodAPI"; - public static final String VERSION = "a1.5.0"; + public static final String ID = "lod"; + public static final String NAME = "DistantHorizons"; + /** Human readable version of MOD_NAME */ + public static final String READABLE_NAME = "Distant Horizons"; + public static final String API = "LodAPI"; + public static final String VERSION = "a1.5.1"; } \ No newline at end of file diff --git a/src/main/java/com/seibel/lod/builders/bufferBuilding/LodBufferBuilder.java b/src/main/java/com/seibel/lod/builders/bufferBuilding/LodBufferBuilder.java index 827783aae..ab927db81 100644 --- a/src/main/java/com/seibel/lod/builders/bufferBuilding/LodBufferBuilder.java +++ b/src/main/java/com/seibel/lod/builders/bufferBuilding/LodBufferBuilder.java @@ -32,7 +32,7 @@ import java.util.concurrent.locks.ReentrantLock; import org.lwjgl.opengl.GL11; import org.lwjgl.opengl.GL15; -import org.lwjgl.opengl.GL15C; +import org.lwjgl.opengl.GL30; import org.lwjgl.opengl.GL45; import com.google.common.util.concurrent.ThreadFactoryBuilder; @@ -40,6 +40,7 @@ import com.mojang.blaze3d.systems.RenderSystem; import com.seibel.lod.builders.bufferBuilding.lodTemplates.Box; import com.seibel.lod.config.LodConfig; import com.seibel.lod.enums.GlProxyContext; +import com.seibel.lod.enums.GpuUploadMethod; import com.seibel.lod.enums.VanillaOverdraw; import com.seibel.lod.objects.LodDimension; import com.seibel.lod.objects.LodRegion; @@ -67,7 +68,7 @@ import net.minecraft.world.LightType; /** * This object is used to create NearFarBuffer objects. * @author James Seibel - * @version 10-10-2021 + * @version 10-23-2021 */ public class LodBufferBuilder { @@ -109,14 +110,6 @@ public class LodBufferBuilder /** The OpenGL IDs of the storage buffers used by the drawableVbos */ public int[][][] drawableStorageBufferIds; - /** used to debug how the buildableStorageBuffers are growing */ - public int[][][] bufferPreviousCapacity; - /** - * This is toggled when the buffers are swapped, so we only - * display the expansion log for one set of buffers - */ - public boolean printExpansionLog = true; - /** Used when building new VBOs */ public volatile VertexBuffer[][][] buildableVbos; /** VBOs that are sent over to the LodNodeRenderer */ @@ -542,6 +535,8 @@ public class LodBufferBuilder */ public void setupBuffers(LodDimension lodDimension) { + GlProxy glProxy = GlProxy.getInstance(); + bufferLock.lock(); int numbRegionsWide = lodDimension.getWidth(); long regionMemoryRequired; @@ -554,10 +549,11 @@ public class LodBufferBuilder buildableVbos = new VertexBuffer[numbRegionsWide][numbRegionsWide][]; drawableVbos = new VertexBuffer[numbRegionsWide][numbRegionsWide][]; - buildableStorageBufferIds = new int[numbRegionsWide][numbRegionsWide][]; - drawableStorageBufferIds = new int[numbRegionsWide][numbRegionsWide][]; - - bufferPreviousCapacity = new int[numbRegionsWide][numbRegionsWide][]; + if (glProxy.bufferStorageSupported) + { + buildableStorageBufferIds = new int[numbRegionsWide][numbRegionsWide][]; + drawableStorageBufferIds = new int[numbRegionsWide][numbRegionsWide][]; + } for (int x = 0; x < numbRegionsWide; x++) { @@ -579,9 +575,11 @@ public class LodBufferBuilder buildableVbos[x][z] = new VertexBuffer[numberOfBuffers]; drawableVbos[x][z] = new VertexBuffer[numberOfBuffers]; - buildableStorageBufferIds[x][z] = new int[numberOfBuffers]; - drawableStorageBufferIds[x][z] = new int[numberOfBuffers]; - bufferPreviousCapacity[x][z] = new int[numberOfBuffers]; + if (glProxy.bufferStorageSupported) + { + buildableStorageBufferIds[x][z] = new int[numberOfBuffers]; + drawableStorageBufferIds[x][z] = new int[numberOfBuffers]; + } } else { @@ -591,16 +589,16 @@ public class LodBufferBuilder buildableVbos[x][z] = new VertexBuffer[1]; drawableVbos[x][z] = new VertexBuffer[1]; - buildableStorageBufferIds[x][z] = new int[1]; - drawableStorageBufferIds[x][z] = new int[1]; - bufferPreviousCapacity[x][z] = new int[1]; + if (glProxy.bufferStorageSupported) + { + buildableStorageBufferIds[x][z] = new int[1]; + drawableStorageBufferIds[x][z] = new int[1]; + } } for (int i = 0; i < numberOfBuffersPerRegion[x][z]; i++) { - bufferPreviousCapacity[x][z][i] = (int) regionMemoryRequired; - buildableBuffers[x][z][i] = new BufferBuilder((int) regionMemoryRequired); buildableVbos[x][z][i] = new VertexBuffer(LodUtil.LOD_VERTEX_FORMAT); @@ -609,24 +607,27 @@ public class LodBufferBuilder // create the initial mapped buffers (system memory) GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, buildableVbos[x][z][i].id); - GL15.glBufferData(GL15.GL_ARRAY_BUFFER, regionMemoryRequired, GL45.GL_DYNAMIC_DRAW); + GL15.glBufferData(GL15.GL_ARRAY_BUFFER, regionMemoryRequired, GL15.GL_DYNAMIC_DRAW); GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0); GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, drawableVbos[x][z][i].id); - GL15.glBufferData(GL15.GL_ARRAY_BUFFER, regionMemoryRequired, GL45.GL_DYNAMIC_DRAW); + GL15.glBufferData(GL15.GL_ARRAY_BUFFER, regionMemoryRequired, GL15.GL_DYNAMIC_DRAW); GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0); - // create the buffer storage (GPU memory) - buildableStorageBufferIds[x][z][i] = GL45.glGenBuffers(); - GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, buildableStorageBufferIds[x][z][i]); - GL45.glBufferStorage(GL15.GL_ARRAY_BUFFER, regionMemoryRequired, 0); // the 0 flag means to create the storage in the GPUs memory - GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0); - - drawableStorageBufferIds[x][z][i] = GL45.glGenBuffers(); - GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, drawableStorageBufferIds[x][z][i]); - GL45.glBufferStorage(GL15.GL_ARRAY_BUFFER, regionMemoryRequired, 0); - GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0); + if (glProxy.bufferStorageSupported) + { + // create the buffer storage (GPU memory) + buildableStorageBufferIds[x][z][i] = GL15.glGenBuffers(); + GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, buildableStorageBufferIds[x][z][i]); + GL45.glBufferStorage(GL15.GL_ARRAY_BUFFER, regionMemoryRequired, 0); // the 0 flag means to create the storage in the GPUs memory + GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0); + + drawableStorageBufferIds[x][z][i] = GL15.glGenBuffers(); + GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, drawableStorageBufferIds[x][z][i]); + GL45.glBufferStorage(GL15.GL_ARRAY_BUFFER, regionMemoryRequired, 0); + GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0); + } } } } @@ -665,8 +666,8 @@ public class LodBufferBuilder // was called from (if any). RenderSystem.recordRenderCall(() -> { - GL45.glDeleteBuffers(buildableId); - GL45.glDeleteBuffers(drawableId); + GL15.glDeleteBuffers(buildableId); + GL15.glDeleteBuffers(drawableId); }); } } @@ -708,9 +709,9 @@ public class LodBufferBuilder RenderSystem.recordRenderCall(() -> { if (buildableId != 0) - GL45.glDeleteBuffers(buildableId); + GL15.glDeleteBuffers(buildableId); if (drawableId != 0) - GL45.glDeleteBuffers(drawableId); + GL15.glDeleteBuffers(drawableId); }); } } @@ -773,6 +774,16 @@ public class LodBufferBuilder // this helps prevent interference (IE stuttering) with the Minecraft context. glProxy.setGlContext(GlProxyContext.LOD_BUILDER); + // determine the upload method + GpuUploadMethod uploadMethod = LodConfig.CLIENT.graphics.advancedGraphicsOption.gpuUploadMethod.get(); + if (!glProxy.bufferStorageSupported && uploadMethod == GpuUploadMethod.BUFFER_STORAGE) + { + // if buffer storage isn't supported + // default to SUB_DATA + LodConfig.CLIENT.graphics.advancedGraphicsOption.gpuUploadMethod.set(GpuUploadMethod.SUB_DATA); + uploadMethod = GpuUploadMethod.SUB_DATA; + } + // actually upload the buffers for (int x = 0; x < buildableVbos.length; x++) { @@ -783,7 +794,7 @@ public class LodBufferBuilder for (int i = 0; i < buildableBuffers[x][z].length; i++) { ByteBuffer uploadBuffer = buildableBuffers[x][z][i].popNextBuffer().getSecond(); - vboUpload(buildableVbos[x][z][i], buildableStorageBufferIds[x][z][i], uploadBuffer, x, z, i, true); + vboUpload(buildableVbos[x][z][i], buildableStorageBufferIds[x][z][i], uploadBuffer, true, uploadMethod); lodDim.setRegenRegionBufferByArrayIndex(x, z, false); } } @@ -807,11 +818,10 @@ public class LodBufferBuilder } } - /** Uploads the uploadBuffer into the VBO and then into GPU memory. */ + /** Uploads the uploadBuffer so the GPU can use it. + * @param uploadMethod */ private void vboUpload(VertexBuffer vbo, int storageBufferId, ByteBuffer uploadBuffer, - int xVboIndex, int zVboIndex, int iVboIndex, boolean allowBufferExpansion) - // x/zVboIndex are just used for the debugging console logging - // and should be removed when the logger is removed. + boolean allowBufferExpansion, GpuUploadMethod uploadMethod) { // this shouldn't happen, but just to be safe if (vbo.id != -1 && GlProxy.getInstance().getGlContext() == GlProxyContext.LOD_BUILDER) @@ -820,63 +830,98 @@ public class LodBufferBuilder vbo.vertexCount = (uploadBuffer.capacity() / vbo.format.getVertexSize()); - GL45.glBindBuffer(GL45.GL_ARRAY_BUFFER, vbo.id); + GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, vbo.id); try { - // get a pointer to the buffer in system memory - ByteBuffer vboBuffer = GL45.glMapBufferRange(GL45.GL_ARRAY_BUFFER, 0, uploadBuffer.capacity(), GL45.GL_MAP_WRITE_BIT | GL45.GL_MAP_UNSYNCHRONIZED_BIT); - if (vboBuffer == null) + // if possible use the faster buffer storage route + if (uploadMethod == GpuUploadMethod.BUFFER_STORAGE) { - int previousCapacity = uploadBuffer.capacity(); - - // only expand the buffers if the uploadBuffer actually - // has something in it and expansion is allowed - if (previousCapacity != 0 && allowBufferExpansion) + // get a pointer to the buffer in system memory + ByteBuffer vboBuffer = GL30.glMapBufferRange(GL15.GL_ARRAY_BUFFER, 0, uploadBuffer.capacity(), GL30.GL_MAP_WRITE_BIT | GL30.GL_MAP_UNSYNCHRONIZED_BIT); + if (vboBuffer == null) { - // the buffer(s) aren't big enough, expand them. - // This does cause lag/stuttering, so it should be avoided! + int previousCapacity = uploadBuffer.capacity(); - // expand the buffer in system memory - GL45.glBufferData(GL45.GL_ARRAY_BUFFER, (int) (uploadBuffer.capacity() * BUFFER_EXPANSION_MULTIPLIER), GL45.GL_DYNAMIC_DRAW); - GL45.glBufferSubData(GL45.GL_ARRAY_BUFFER, 0, uploadBuffer); - - // un-bind the system memory buffer - GL15.glUnmapBuffer(GL15.GL_ARRAY_BUFFER); - GL15C.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0); - - // expand the buffer storage - GL45.glDeleteBuffers(storageBufferId); - GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, storageBufferId); - GL45.glBufferStorage(GL15.GL_ARRAY_BUFFER, (int) (uploadBuffer.capacity() * BUFFER_EXPANSION_MULTIPLIER), 0); - GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0); - - - // recursively try to upload into the newly created buffer storage - // but don't recurse again if that fails - // (we don't want an infinitely expanding buffer!) - vboUpload(vbo, storageBufferId, uploadBuffer, xVboIndex, zVboIndex, iVboIndex, false); - - - - /*if (printExpansionLog) + // only expand the buffers if the uploadBuffer actually + // has something in it and expansion is allowed + if (previousCapacity != 0 && allowBufferExpansion) { - // NOTE: this will display twice because we are double buffering - // (using 1 buffer to generate into and one to draw) - ClientProxy.LOGGER.info("vbo (" + xVboIndex + "," + zVboIndex + ") expanded: " + bufferPreviousCapacity[xVboIndex][zVboIndex][iVboIndex] + " -> " + (int)(uploadBuffer.capacity() * BUFFER_EXPANSION_MULTIPLIER)); - bufferPreviousCapacity[xVboIndex][zVboIndex][iVboIndex] = (int) (uploadBuffer.capacity() * BUFFER_EXPANSION_MULTIPLIER); - }*/ + // the buffer(s) aren't big enough, expand them. + // This does cause lag/stuttering, so it should be avoided! + + // expand the buffer in system memory + GL15.glBufferData(GL15.GL_ARRAY_BUFFER, (int) (uploadBuffer.capacity() * BUFFER_EXPANSION_MULTIPLIER), GL15.GL_DYNAMIC_DRAW); + GL15.glBufferSubData(GL15.GL_ARRAY_BUFFER, 0, uploadBuffer); + + // un-bind the system memory buffer + GL15.glUnmapBuffer(GL15.GL_ARRAY_BUFFER); + GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0); + + // expand the buffer storage + GL15.glDeleteBuffers(storageBufferId); + GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, storageBufferId); + GL45.glBufferStorage(GL15.GL_ARRAY_BUFFER, (int) (uploadBuffer.capacity() * BUFFER_EXPANSION_MULTIPLIER), 0); + GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0); + + + // recursively try to upload into the newly created buffer storage + // but don't recurse again if that fails + // (we don't want an infinitely expanding buffer!) + vboUpload(vbo, storageBufferId, uploadBuffer, false, uploadMethod); + } + } + else + { + // upload the buffer into system memory... + vboBuffer.put(uploadBuffer); + GL15.glUnmapBuffer(GL15.GL_ARRAY_BUFFER); + + // ...then upload into GPU memory + // (uploading into GPU memory directly can only be done + // through the glCopyBufferSubData/glCopyNamed... methods) + GL45.glCopyNamedBufferSubData(vbo.id, storageBufferId, 0, 0, uploadBuffer.capacity()); + } + } + else if (uploadMethod == GpuUploadMethod.BUFFER_MAPPING) + { + // no stuttering but high GPU usage + // stores everything in system memory instead of GPU memory + // making rendering much slower. + // Unless the user is running integrated graphics, + // in that case this will actually work better than SUB_DATA. + + + ByteBuffer vboBuffer = null; + + // map buffer range is better since it can be explicitly unsynchronized + if (GlProxy.getInstance().mapBufferRangeSupported) + vboBuffer = GL30.glMapBufferRange(GL30.GL_ARRAY_BUFFER, 0, uploadBuffer.capacity(), GL30.GL_MAP_WRITE_BIT | GL30.GL_MAP_UNSYNCHRONIZED_BIT); + else + vboBuffer = GL15.glMapBuffer(GL30.GL_ARRAY_BUFFER, uploadBuffer.capacity()); + + + if (vboBuffer == null) + { + GL15.glBufferData(GL45.GL_ARRAY_BUFFER, (int) (uploadBuffer.capacity() * BUFFER_EXPANSION_MULTIPLIER), GL15.GL_DYNAMIC_DRAW); + GL15.glBufferSubData(GL15.GL_ARRAY_BUFFER, 0, uploadBuffer); + } + else + { + vboBuffer.put(uploadBuffer); } } else { - // upload the buffer into system memory... - vboBuffer.put(uploadBuffer); - GL15.glUnmapBuffer(GL15.GL_ARRAY_BUFFER); + // hybrid subData/bufferData // + // less stutter, low GPU usage - // ...then upload into GPU memory - // (uploading into GPU memory directly can only be done - // through the glCopyBufferSubData/glCopyNamed... methods) - GL45.glCopyNamedBufferSubData(vbo.id, storageBufferId, 0, 0, uploadBuffer.capacity()); + //long size = GL31.glGetBufferParameteri64(GL15.GL_ARRAY_BUFFER, GL15.GL_BUFFER_SIZE); // hopefully just a int should be long enough + long size = GL15.glGetBufferParameteri(GL15.GL_ARRAY_BUFFER, GL15.GL_BUFFER_SIZE); + if (size < uploadBuffer.capacity() * BUFFER_EXPANSION_MULTIPLIER) + { + GL15.glBufferData(GL15.GL_ARRAY_BUFFER, (int) (uploadBuffer.capacity() * BUFFER_EXPANSION_MULTIPLIER), GL15.GL_DYNAMIC_DRAW); + } + GL15.glBufferSubData(GL15.GL_ARRAY_BUFFER, 0, uploadBuffer); } } catch (Exception e) @@ -886,11 +931,14 @@ public class LodBufferBuilder } finally { - GL15C.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0); + if (uploadMethod == GpuUploadMethod.BUFFER_MAPPING) + GL15.glUnmapBuffer(GL15.GL_ARRAY_BUFFER); + + GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0); } - } - } + }//if vbo exists and in correct GL context + }//vboUpload /** Get the newly created VBOs */ public VertexBuffersAndOffset getVertexBuffers() @@ -907,10 +955,6 @@ public class LodBufferBuilder drawableStorageBufferIds = buildableStorageBufferIds; buildableStorageBufferIds = tmpStorage; - // we only want to print the expansion log for - // one set of buffers, not both - printExpansionLog = !printExpansionLog; - drawableCenterChunkPos = buildableCenterChunkPos; // the vbos have been swapped diff --git a/src/main/java/com/seibel/lod/config/LodConfig.java b/src/main/java/com/seibel/lod/config/LodConfig.java index 0c630bb04..060454db3 100644 --- a/src/main/java/com/seibel/lod/config/LodConfig.java +++ b/src/main/java/com/seibel/lod/config/LodConfig.java @@ -35,6 +35,7 @@ import com.seibel.lod.enums.DistanceGenerationMode; import com.seibel.lod.enums.FogDistance; import com.seibel.lod.enums.FogDrawOverride; import com.seibel.lod.enums.GenerationPriority; +import com.seibel.lod.enums.GpuUploadMethod; import com.seibel.lod.enums.HorizontalQuality; import com.seibel.lod.enums.HorizontalResolution; import com.seibel.lod.enums.HorizontalScale; @@ -51,7 +52,7 @@ import net.minecraftforge.fml.config.ModConfig; /** * This handles any configuration the user has access to. * @author James Seibel - * @version 10-19-2021 + * @version 10-23-2021 */ @Mod.EventBusSubscriber public class LodConfig @@ -222,6 +223,8 @@ public class LodConfig public final ForgeConfigSpec.EnumValue vanillaOverdraw; + public final ForgeConfigSpec.EnumValue gpuUploadMethod; + AdvancedGraphicsOption(ForgeConfigSpec.Builder builder) { @@ -273,6 +276,17 @@ public class LodConfig + " " + VanillaOverdraw.ALWAYS + ": LODs will render on all vanilla chunks preventing holes in the world. \n" + " " + VanillaOverdraw.BORDER + ": LODs will render only on the border of vanilla chunks preventing only some holes in the world. \n") .defineEnum("Vanilla Overdraw", VanillaOverdraw.DYNAMIC); + + gpuUploadMethod = builder + .comment("\n\n" + + " What method should be used to upload geometry to the GPU? \n\\n" + + "" + + " " + GpuUploadMethod.BUFFER_STORAGE + ": Default if OpenGL 4.5 is supported. Fast rendering, no stuttering. \n" + + " " + GpuUploadMethod.SUB_DATA + ": Default if OpenGL 4.5 is NOT supported. Fast rendering but may stutter when uploading. \n" + + " " + GpuUploadMethod.BUFFER_MAPPING + ": Slow rendering but won't stutter when uploading. Possibly better than " + GpuUploadMethod.SUB_DATA + " if using a integrated GPU. \n") + .defineEnum("GPU Upload Method", GpuUploadMethod.BUFFER_STORAGE); + + builder.pop(); } } @@ -519,7 +533,7 @@ public class LodConfig /** {@link Path} to the configuration file of this mod */ - private static final Path CONFIG_PATH = Paths.get("config", ModInfo.MODNAME + ".toml"); + private static final Path CONFIG_PATH = Paths.get("config", ModInfo.NAME + ".toml"); public static final ForgeConfigSpec CLIENT_SPEC; public static final Client CLIENT; @@ -540,13 +554,13 @@ public class LodConfig @SubscribeEvent public static void onLoad(final ModConfig.Loading configEvent) { - LogManager.getLogger().debug(ModInfo.MODNAME, "Loaded forge config file {}", configEvent.getConfig().getFileName()); + LogManager.getLogger().debug(ModInfo.NAME, "Loaded forge config file {}", configEvent.getConfig().getFileName()); } @SubscribeEvent public static void onFileChange(final ModConfig.Reloading configEvent) { - LogManager.getLogger().debug(ModInfo.MODNAME, "Forge config just got changed on the file system!"); + LogManager.getLogger().debug(ModInfo.NAME, "Forge config just got changed on the file system!"); } } diff --git a/src/main/java/com/seibel/lod/enums/GpuUploadMethod.java b/src/main/java/com/seibel/lod/enums/GpuUploadMethod.java new file mode 100644 index 000000000..b88be18be --- /dev/null +++ b/src/main/java/com/seibel/lod/enums/GpuUploadMethod.java @@ -0,0 +1,38 @@ +/* + * This file is part of the Distant Horizon mod (formerly the LOD Mod), + * licensed under the GNU GPL 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 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.seibel.lod.enums; + +/** + * Buffer_Storage, Sub_Data, Buffer_Mapping + * + * @author James Seibel + * @version 10-23-2021 + */ +public enum GpuUploadMethod +{ + /** Default if OpenGL 4.5 is supported. Fast rendering, no stuttering. */ + BUFFER_STORAGE, + + /** Default if OpenGL 4.5 is NOT supported. Fast rendering but may stutter when uploading. */ + SUB_DATA, + + /** May end up storing buffers in System memory. Slower rendering but won't stutter when uploading. */ + BUFFER_MAPPING, +} \ No newline at end of file diff --git a/src/main/java/com/seibel/lod/proxy/ClientProxy.java b/src/main/java/com/seibel/lod/proxy/ClientProxy.java index 28f8d2b38..ba10a96fb 100644 --- a/src/main/java/com/seibel/lod/proxy/ClientProxy.java +++ b/src/main/java/com/seibel/lod/proxy/ClientProxy.java @@ -53,7 +53,7 @@ import net.minecraftforge.eventbus.api.SubscribeEvent; * This handles all events sent to the client, * and is the starting point for most of the mod. * @author James_Seibel - * @version 10-15-2021 + * @version 10-23-2021 */ public class ClientProxy { @@ -176,6 +176,8 @@ public class ClientProxy // LodConfig.CLIENT.graphics.fogDrawOverride.set(FogDrawOverride.ALWAYS_DRAW_FOG_FANCY); // LodConfig.CLIENT.graphics.shadingMode.set(ShadingMode.DARKEN_SIDES); // LodConfig.CLIENT.graphics.vanillaOverdraw.set(VanillaOverdraw.HALF); + +// LodConfig.CLIENT.graphics.advancedGraphicsOption.gpuUploadMethod.set(GpuUploadMethod.BUFFER_STORAGE); // LodConfig.CLIENT.worldGenerator.distanceGenerationMode.set(DistanceGenerationMode.SURFACE); // LodConfig.CLIENT.graphics.qualityOption.lodChunkRenderDistance.set(128); diff --git a/src/main/java/com/seibel/lod/proxy/GlProxy.java b/src/main/java/com/seibel/lod/proxy/GlProxy.java index af9992470..9418c7fda 100644 --- a/src/main/java/com/seibel/lod/proxy/GlProxy.java +++ b/src/main/java/com/seibel/lod/proxy/GlProxy.java @@ -24,7 +24,9 @@ import org.lwjgl.opengl.GL; import org.lwjgl.opengl.GLCapabilities; import com.mojang.blaze3d.systems.RenderSystem; +import com.seibel.lod.ModInfo; import com.seibel.lod.enums.GlProxyContext; +import com.seibel.lod.wrappers.MinecraftWrapper; /** * A singleton that holds references to different openGL contexts @@ -38,12 +40,14 @@ import com.seibel.lod.enums.GlProxyContext; * https://gamedev.stackexchange.com/questions/91995/edit-vbo-data-or-create-a-new-one

* * @author James Seibel - * @version 10-2-2021 + * @version 10-23-2021 */ public class GlProxy { private static GlProxy instance = null; + private static MinecraftWrapper mc = MinecraftWrapper.INSTANCE; + /** Minecraft's GLFW window */ public final long minecraftGlContext; /** Minecraft's GL context */ @@ -63,16 +67,22 @@ public class GlProxy /** Does this computer's GPU support fancy fog? */ public final boolean fancyFogAvailable; + /** Requires OpenGL 4.5, and offers the best buffer uploading */ + public final boolean bufferStorageSupported; + + /** Requires OpenGL 3.0 */ + public final boolean mapBufferRangeSupported; private GlProxy() { + ClientProxy.LOGGER.error("Creating " + GlProxy.class.getSimpleName() + "... If this is the last message you see in the log there must have been a OpenGL error."); + // getting Minecraft's context has to be done on the render thread, // where the GL context is if (!RenderSystem.isOnRenderThread()) throw new IllegalStateException(GlProxy.class.getSimpleName() + " was created outside the render thread!"); - - + //============================// @@ -101,6 +111,30 @@ public class GlProxy + //==============================// + // determine the OpenGL version // + //==============================// + + bufferStorageSupported = minecraftGlCapabilities.OpenGL45; + mapBufferRangeSupported = minecraftGlCapabilities.OpenGL30; + + if (!minecraftGlCapabilities.OpenGL15) + { + String errorMessage = ModInfo.READABLE_NAME + " was initializing " + GlProxy.class.getSimpleName() + " and discoverd this GPU doesn't support OpenGL 1.5 or greater."; + mc.crashMinecraft(errorMessage + " Sorry I couldn't tell you sooner :(", new UnsupportedOperationException("This GPU doesn't support OpenGL 1.5 or greater.")); + } + + if (!bufferStorageSupported) + { + String fallBackVersion = mapBufferRangeSupported ? "3.0" : "1.5"; + + ClientProxy.LOGGER.error("This GPU doesn't support OpenGL 4.5, falling back to OpenGL " + fallBackVersion + ". This may cause stuttering and reduced performance."); + } + + + + + //==================================// // get any GPU related capabilities // //==================================// @@ -110,6 +144,12 @@ public class GlProxy if (!fancyFogAvailable) ClientProxy.LOGGER.info("This GPU does not support GL_NV_fog_distance. This means that the fancy fog option will not be available."); + + + + + // GlProxy creation success + ClientProxy.LOGGER.error(GlProxy.class.getSimpleName() + " creation successful. OpenGL smiles upon you this day."); } @@ -188,15 +228,8 @@ public class GlProxy public static GlProxy getInstance() { - try - { - if (instance == null) - instance = new GlProxy(); - } - catch (Exception e) - { - e.printStackTrace(); - } + if (instance == null) + instance = new GlProxy(); return instance; } diff --git a/src/main/java/com/seibel/lod/render/LodRenderer.java b/src/main/java/com/seibel/lod/render/LodRenderer.java index c1ec9bc6f..d238a67b0 100644 --- a/src/main/java/com/seibel/lod/render/LodRenderer.java +++ b/src/main/java/com/seibel/lod/render/LodRenderer.java @@ -36,6 +36,7 @@ import com.seibel.lod.enums.DebugMode; import com.seibel.lod.enums.FogDistance; import com.seibel.lod.enums.FogDrawOverride; import com.seibel.lod.enums.FogQuality; +import com.seibel.lod.enums.GpuUploadMethod; import com.seibel.lod.handlers.ReflectionHandler; import com.seibel.lod.objects.LodDimension; import com.seibel.lod.objects.NearFarFogSettings; @@ -64,7 +65,7 @@ import net.minecraft.util.math.vector.Vector3d; * This is where LODs are draw to the world. * * @author James Seibel - * @version 10-19-2021 + * @version 10-23-2021 */ public class LodRenderer { @@ -157,6 +158,7 @@ public class LodRenderer * @param mcMatrixStack This matrix stack should come straight from MC's renderChunkLayer (or future equivalent) method * @param partialTicks how far into the current tick this method was called. */ + @SuppressWarnings("deprecation") public void drawLODs(LodDimension lodDim, MatrixStack mcMatrixStack, float partialTicks, IProfiler newProfiler) { if (lodDim == null) @@ -276,6 +278,7 @@ public class LodRenderer Vector3d cameraDir = new Vector3d(renderInfo.getLookVector()); boolean cullingDisabled = LodConfig.CLIENT.graphics.advancedGraphicsOption.disableDirectionalCulling.get(); + boolean renderBufferStorage = LodConfig.CLIENT.graphics.advancedGraphicsOption.gpuUploadMethod.get() == GpuUploadMethod.BUFFER_STORAGE && GlProxy.getInstance().bufferStorageSupported; // used to determine what type of fog to render int halfWidth = vbos.length / 2; @@ -292,6 +295,7 @@ public class LodRenderer RegionPos vboPos = new RegionPos( x + vboCenterRegionPos.x - (lodDim.getWidth() / 2), z + vboCenterRegionPos.z - (lodDim.getWidth() / 2)); + if (cullingDisabled || RenderUtil.isRegionInViewFrustum(renderInfo.getBlockPosition(), cameraDir, vboPos.blockPos())) { if ((x > halfWidth - quarterWidth && x < halfWidth + quarterWidth) @@ -301,10 +305,12 @@ public class LodRenderer setupFog(fogSettings.far.distance, fogSettings.far.quality); - for (int i = 0; i < storageBufferIds[x][z].length; i++) - { - drawBuffer(vbos[x][z][i], storageBufferIds[x][z][i], modelViewMatrix); - } + if (storageBufferIds != null && renderBufferStorage) + for (int i = 0; i < storageBufferIds[x][z].length; i++) + drawStorageBuffer(vbos[x][z][i], storageBufferIds[x][z][i], modelViewMatrix); + else + for (int i = 0; i < vbos[x][z].length; i++) + drawVertexBuffer(vbos[x][z][i], modelViewMatrix); } } } @@ -345,12 +351,29 @@ public class LodRenderer /** This is where the actual drawing happens. */ - private void drawBuffer(VertexBuffer vbo, int bufferStorageId, Matrix4f modelViewMatrix) + private void drawStorageBuffer(VertexBuffer vbo, int bufferStorageId, Matrix4f modelViewMatrix) { if (vbo == null) return; - GL15C.glBindBuffer(GL15.GL_ARRAY_BUFFER, bufferStorageId); + GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, bufferStorageId); + // 0L is the starting pointer + LodUtil.LOD_VERTEX_FORMAT.setupBufferState(0L); + + vbo.draw(modelViewMatrix, GL11.GL_QUADS); + + GL15C.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0); + LodUtil.LOD_VERTEX_FORMAT.clearBufferState(); + } + + + /** This is where the actual drawing happens. */ + private void drawVertexBuffer(VertexBuffer vbo, Matrix4f modelViewMatrix) + { + if (vbo == null) + return; + + GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, vbo.id); // 0L is the starting pointer LodUtil.LOD_VERTEX_FORMAT.setupBufferState(0L); @@ -446,6 +469,7 @@ public class LodRenderer /** * Revert any changes that were made to the fog. */ + @SuppressWarnings("deprecation") private void cleanupFog(NearFarFogSettings fogSettings, float defaultFogStartDist, float defaultFogEndDist, int defaultFogMode, int defaultFogDistance) diff --git a/src/main/java/com/seibel/lod/wrappers/MinecraftWrapper.java b/src/main/java/com/seibel/lod/wrappers/MinecraftWrapper.java index c30960b5a..67f2297fe 100644 --- a/src/main/java/com/seibel/lod/wrappers/MinecraftWrapper.java +++ b/src/main/java/com/seibel/lod/wrappers/MinecraftWrapper.java @@ -22,6 +22,8 @@ package com.seibel.lod.wrappers; import java.awt.Color; import java.io.File; +import com.seibel.lod.ModInfo; +import com.seibel.lod.proxy.ClientProxy; import com.seibel.lod.util.LodUtil; import net.minecraft.client.GameSettings; @@ -36,6 +38,7 @@ import net.minecraft.client.renderer.WorldRenderer; import net.minecraft.client.renderer.model.ModelManager; import net.minecraft.client.renderer.texture.NativeImage; import net.minecraft.client.world.ClientWorld; +import net.minecraft.crash.CrashReport; import net.minecraft.entity.Entity; import net.minecraft.profiler.IProfiler; import net.minecraft.server.integrated.IntegratedServer; @@ -235,4 +238,20 @@ public class MinecraftWrapper { return mc.levelRenderer; } + + + /** + * Crashes Minecraft, displaying the given errorMessage

+ * In the following format:
+ * + * The game crashed whilst errorMessage
+ * Error: java.lang.ExceptionClass: exceptionErrorMessage
+ * Exit Code: -1
+ */ + public void crashMinecraft(String errorMessage, Throwable exception) + { + ClientProxy.LOGGER.error(ModInfo.READABLE_NAME + " had the following error: [" + errorMessage + "]. Crashing Minecraft..."); + CrashReport report = new CrashReport(errorMessage, exception); + Minecraft.crash(report); + } } diff --git a/src/main/resources/META-INF/mods.toml b/src/main/resources/META-INF/mods.toml index 1e9e40d2d..a3f8731c0 100644 --- a/src/main/resources/META-INF/mods.toml +++ b/src/main/resources/META-INF/mods.toml @@ -24,7 +24,7 @@ modId="lod" #mandatory #// The version number of the mod - there's a few well known ${} variables useable here or just hardcode it #//${file.jarVersion} will substitute the value of the Implementation-Version as read from the mod's JAR file metadata #// see the associated build.gradle script for how to populate this completely automatically during a build -version="a1.5.0" #mandatory +version="a1.5.1" #mandatory #// A display name for the mod displayName="Distant Horizons" #mandatory