This commit is contained in:
s809
2024-01-21 17:20:49 +05:00
16 changed files with 181 additions and 147 deletions
@@ -19,10 +19,6 @@
package com.seibel.distanthorizons.core.config.eventHandlers;
import com.seibel.distanthorizons.api.DhApi;
import com.seibel.distanthorizons.api.enums.config.ELodShading;
import com.seibel.distanthorizons.api.enums.config.EMaxHorizontalResolution;
import com.seibel.distanthorizons.api.enums.config.EVerticalQuality;
import com.seibel.distanthorizons.core.config.Config;
import com.seibel.distanthorizons.core.config.listeners.IConfigListener;
@@ -467,52 +467,4 @@ public class CompleteFullDataSource extends FullDataArrayAccessor implements IFu
@Override
public int getWidthInDataPoints() { return this.width; }
//========//
// unused //
//========//
public void updateFromLowerCompleteSource(CompleteFullDataSource subData)
{
LodUtil.assertTrue(this.sectionPos.overlapsExactly(subData.sectionPos));
LodUtil.assertTrue(subData.sectionPos.getDetailLevel() < this.sectionPos.getDetailLevel());
if (!firstDataPosCanAffectSecond(this.sectionPos, subData.sectionPos))
{
return;
}
DhSectionPos lowerSectPos = subData.sectionPos;
byte detailDiff = (byte) (this.sectionPos.getDetailLevel() - subData.sectionPos.getDetailLevel());
byte targetDataDetail = this.getDataDetailLevel();
DhLodPos minDataPos = this.sectionPos.getMinCornerLodPos(targetDataDetail);
if (detailDiff <= SECTION_SIZE_OFFSET)
{
int count = 1 << detailDiff;
int dataPerCount = WIDTH / count;
DhLodPos subDataPos = lowerSectPos.getSectionBBoxPos().getCornerLodPos(targetDataDetail);
int dataOffsetX = subDataPos.x - minDataPos.x;
int dataOffsetZ = subDataPos.z - minDataPos.z;
LodUtil.assertTrue(dataOffsetX >= 0 && dataOffsetX < WIDTH && dataOffsetZ >= 0 && dataOffsetZ < WIDTH);
for (int xOffset = 0; xOffset < count; xOffset++)
{
for (int zOffset = 0; zOffset < count; zOffset++)
{
SingleColumnFullDataAccessor column = this.get(xOffset + dataOffsetX, zOffset + dataOffsetZ);
column.downsampleFrom(subData.subView(dataPerCount, xOffset * dataPerCount, zOffset * dataPerCount));
}
}
}
else
{
// Count == 1
DhLodPos subDataPos = lowerSectPos.getSectionBBoxPos().convertToDetailLevel(targetDataDetail);
int dataOffsetX = subDataPos.x - minDataPos.x;
int dataOffsetZ = subDataPos.z - minDataPos.z;
LodUtil.assertTrue(dataOffsetX >= 0 && dataOffsetX < WIDTH && dataOffsetZ >= 0 && dataOffsetZ < WIDTH);
subData.get(0, 0).deepCopyTo(get(dataOffsetX, dataOffsetZ));
}
}
}
@@ -382,7 +382,7 @@ public class ColumnRenderBuffer extends AbstractRenderBuffer
{
this.buffersUploaded = false;
GLProxy.getInstance().recordOpenGlCall(() ->
GLProxy.getInstance().queueRunningOnRenderThread(() ->
{
for (GLVertexBuffer buffer : this.vbos)
{
@@ -62,8 +62,8 @@ public abstract class AbstractDataSourceHandler<TDataSource extends IDataSource<
// constructor //
//=============//
public AbstractDataSourceHandler(TDhLevel level, AbstractSaveStructure saveStructure, AbstractDataSourceRepo repo) { this(level, saveStructure, repo, null); }
public AbstractDataSourceHandler(TDhLevel level, AbstractSaveStructure saveStructure, AbstractDataSourceRepo repo, @Nullable File saveDirOverride)
public AbstractDataSourceHandler(TDhLevel level, AbstractSaveStructure saveStructure) { this(level, saveStructure, null); }
public AbstractDataSourceHandler(TDhLevel level, AbstractSaveStructure saveStructure, @Nullable File saveDirOverride)
{
this.level = level;
this.saveDir = (saveDirOverride == null) ? saveStructure.getFullDataFolder(level.getLevelWrapper()) : saveDirOverride;
@@ -83,7 +83,7 @@ public abstract class AbstractDataSourceHandler<TDataSource extends IDataSource<
this.queueSaveLockArray[i] = new ReentrantLock();
}
this.repo = repo;
this.repo = this.createRepo();
// determine the top detail level currently in the database
int maxSectionDetailLevel = this.repo.getMaxSectionDetailLevel();
@@ -97,6 +97,9 @@ public abstract class AbstractDataSourceHandler<TDataSource extends IDataSource<
// abstract methods //
//==================//
/** When this is called the parent folders should be created */
protected abstract AbstractDataSourceRepo createRepo();
protected abstract TDataSource createDataSourceFromDto(DataSourceDto dto) throws InterruptedException, IOException;
/**
* Creates a new data source using any DTOs already present in the database.
@@ -182,20 +185,31 @@ public abstract class AbstractDataSourceHandler<TDataSource extends IDataSource<
//===============//
@Override
public void updateDataSourcesWithChunkData(ChunkSizedFullDataAccessor chunkDataView)
{
DhSectionPos chunkSectionPos = chunkDataView.getSectionPos().convertNewToDetailLevel(DhSectionPos.SECTION_MINIMUM_DETAIL_LEVEL);
this.recursivelyUpdateDataSourcesAsync(chunkSectionPos, chunkDataView);
}
/** Updates every data source from this position up to {@link AbstractDataSourceHandler#topSectionDetailLevelRef} */
protected void recursivelyUpdateDataSourcesAsync(DhSectionPos pos, ChunkSizedFullDataAccessor chunkDataView)
public CompletableFuture<Void> updateDataSourcesWithChunkDataAsync(ChunkSizedFullDataAccessor chunkDataView)
{
DhSectionPos pos = chunkDataView.getSectionPos().convertNewToDetailLevel(DhSectionPos.SECTION_MINIMUM_DETAIL_LEVEL);
ThreadPoolExecutor executor = ThreadPools.getFileHandlerExecutor();
if (executor == null || executor.isTerminated())
{
return;
return CompletableFuture.completedFuture(null);
}
try
{
// run file handling on a separate thread
return CompletableFuture.runAsync(() -> this.updateDataSourcesRecursively(pos, chunkDataView), executor);
}
catch (RejectedExecutionException ignore)
{
// can happen if the executor was shutdown while this task was queued
return CompletableFuture.completedFuture(null);
}
}
/** Updates every data source from this position up to {@link AbstractDataSourceHandler#topSectionDetailLevelRef} */
private void updateDataSourcesRecursively(DhSectionPos pos, ChunkSizedFullDataAccessor chunkDataView)
{
// update up until we reach the highest available data source
if (pos.getDetailLevel() > this.topSectionDetailLevelRef.get())
{
@@ -203,23 +217,15 @@ public abstract class AbstractDataSourceHandler<TDataSource extends IDataSource<
}
try
{
executor.execute(() ->
{
DhSectionPos chunkSectionPos = chunkDataView.getSectionPos();
LodUtil.assertTrue(chunkSectionPos.overlapsExactly(pos), "Update failed, chunk [" + chunkSectionPos + "] does not overlap section [" + pos + "].");
// update this pos
this.updateDataSourceAtPos(pos, chunkDataView);
// recursively update the parent pos
DhSectionPos parentPos = pos.getParentPos();
this.recursivelyUpdateDataSourcesAsync(parentPos, chunkDataView);
this.recursivelyUpdateDataSourcesAsync(parentPos, chunkDataView);
});
}
catch (RejectedExecutionException ignore) { /* can happen if the executor was shutdown while this task was queued */ }
DhSectionPos chunkSectionPos = chunkDataView.getSectionPos();
LodUtil.assertTrue(chunkSectionPos.overlapsExactly(pos), "Update failed, chunk [" + chunkSectionPos + "] does not overlap section [" + pos + "].");
// update this pos
this.updateDataSourceAtPos(pos, chunkDataView);
// recursively update the parent pos
DhSectionPos parentPos = pos.getParentPos();
this.updateDataSourcesRecursively(parentPos, chunkDataView);
}
protected void updateDataSourceAtPos(DhSectionPos pos, ChunkSizedFullDataAccessor chunkData)
{
@@ -286,6 +292,10 @@ public abstract class AbstractDataSourceHandler<TDataSource extends IDataSource<
@Override
public void run()
{
// remove this task from the queue
AbstractDataSourceHandler.this.saveTimerTasksBySectionPos.remove(pos);
try
{
final TDataSource finalDataSource = AbstractDataSourceHandler.this.unsavedDataSourceBySectionPos.remove(pos);
@@ -10,6 +10,6 @@ public interface ISourceProvider<TDataSource extends IDataSource<TDhLevel>, TDhL
{
CompletableFuture<TDataSource> getAsync(DhSectionPos pos);
void updateDataSourcesWithChunkData(ChunkSizedFullDataAccessor chunkData);
CompletableFuture<Void> updateDataSourcesWithChunkDataAsync(ChunkSizedFullDataAccessor chunkData);
}
@@ -31,6 +31,8 @@ import com.seibel.distanthorizons.core.level.IDhLevel;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import com.seibel.distanthorizons.core.pos.DhSectionPos;
import com.seibel.distanthorizons.core.render.renderer.DebugRenderer;
import com.seibel.distanthorizons.core.sql.AbstractDataSourceRepo;
import com.seibel.distanthorizons.core.sql.FullDataRepo;
import com.seibel.distanthorizons.core.sql.DataSourceDto;
import com.seibel.distanthorizons.core.sql.FullDataRepo;
import org.apache.logging.log4j.Logger;
@@ -69,17 +71,20 @@ public class FullDataFileHandler extends AbstractDataSourceHandler<IFullDataSour
//=============//
public FullDataFileHandler(IDhLevel level, AbstractSaveStructure saveStructure) { this(level, saveStructure, null); }
public FullDataFileHandler(IDhLevel level, AbstractSaveStructure saveStructure, @Nullable File saveDirOverride)
public FullDataFileHandler(IDhLevel level, AbstractSaveStructure saveStructure, @Nullable File saveDirOverride) { super(level, saveStructure, saveDirOverride); }
//====================//
// Abstract overrides //
//====================//
@Override
protected AbstractDataSourceRepo createRepo()
{
super(level, saveStructure, createRepo(level, saveStructure), saveDirOverride);
}
private static FullDataRepo createRepo(IDhLevel level, AbstractSaveStructure saveStructure)
{
File saveDir = saveStructure.getFullDataFolder(level.getLevelWrapper());
try
{
return new FullDataRepo("jdbc:sqlite", saveDir.getPath() + "/" + AbstractSaveStructure.DATABASE_NAME);
return new FullDataRepo("jdbc:sqlite", this.saveDir.getPath() + "/" + AbstractSaveStructure.DATABASE_NAME);
}
catch (SQLException e)
{
@@ -89,12 +94,6 @@ public class FullDataFileHandler extends AbstractDataSourceHandler<IFullDataSour
}
}
//====================//
// Abstract overrides //
//====================//
@Override
protected IFullDataSource createDataSourceFromDto(DataSourceDto dto) throws InterruptedException, IOException
{
@@ -107,7 +106,6 @@ public class FullDataFileHandler extends AbstractDataSourceHandler<IFullDataSour
protected IFullDataSource createNewDataSourceFromExistingDtos(DhSectionPos pos)
{
IIncompleteFullDataSource newFullDataSource = this.makeEmptyDataSource(pos);
boolean showFullDataFileSampling = Config.Client.Advanced.Debugging.DebugWireframe.showFullDataFileStatus.get();
@@ -124,12 +122,12 @@ public class FullDataFileHandler extends AbstractDataSourceHandler<IFullDataSour
ArrayList<DhSectionPos> possibleChildList = new ArrayList<>();
pos.forEachChild((childPos) ->
{
if (childPos.getDetailLevel() > this.minDetailLevel)
if (childPos.getDetailLevel() >= this.minDetailLevel)
{
possibleChildList.add(childPos);
}
});
while (!possibleChildList.isEmpty())
while (possibleChildList.size() != 0)
{
DhSectionPos possiblePos = possibleChildList.remove(possibleChildList.size()-1);
if (this.repo.existsWithPrimaryKey(possiblePos.serialize()))
@@ -140,7 +138,7 @@ public class FullDataFileHandler extends AbstractDataSourceHandler<IFullDataSour
{
possiblePos.forEachChild((childPos) ->
{
if (childPos.getDetailLevel() > this.minDetailLevel)
if (childPos.getDetailLevel() >= this.minDetailLevel)
{
possibleChildList.add(childPos);
}
@@ -190,8 +188,6 @@ public class FullDataFileHandler extends AbstractDataSourceHandler<IFullDataSour
LowDetailIncompleteFullDataSource.createEmpty(pos);
}
//===================//
@@ -265,8 +265,7 @@ public class GeneratedFullDataFileHandler extends FullDataFileHandler
{
if (chunkSizedFullDataSource.getSectionPos().overlapsExactly(this.loadedTargetFullDataSource.getSectionPos()))
{
((DhLevel) level).updateDataSourcesWithChunkData(chunkSizedFullDataSource);
//GeneratedFullDataFileHandler.this.write(this.loadedTargetFullDataSource.getSectionPos(), chunkSizedFullDataSource);
((DhLevel) GeneratedFullDataFileHandler.this.level).updateDataSourcesWithChunkData(chunkSizedFullDataSource);
}
};
}
@@ -275,10 +274,7 @@ public class GeneratedFullDataFileHandler extends FullDataFileHandler
}
/**
* used by external event listeners <br>
* TODO may or may not be best to have this in a separate file
*/
/** used by external event listeners */
@FunctionalInterface
public interface IOnWorldGenCompleteListener
{
@@ -39,7 +39,7 @@ public interface IFullDataSourceProvider extends ISourceProvider<IFullDataSource
CompletableFuture<IFullDataSource> getAsync(DhSectionPos pos);
IFullDataSource get(DhSectionPos pos);
void updateDataSourcesWithChunkData(ChunkSizedFullDataAccessor chunkData);
CompletableFuture<Void> updateDataSourcesWithChunkDataAsync(ChunkSizedFullDataAccessor chunkData);
int getUnsavedDataSourceCount();
@@ -38,7 +38,7 @@ public interface IRenderSourceProvider extends ISourceProvider<ColumnRenderSourc
{
CompletableFuture<ColumnRenderSource> getAsync(DhSectionPos pos);
void updateDataSourcesWithChunkData(ChunkSizedFullDataAccessor chunkData);
CompletableFuture<Void> updateDataSourcesWithChunkDataAsync(ChunkSizedFullDataAccessor chunkData);
/** Deletes any data stored in the render cache so it can be re-created */
void deleteRenderCache();
@@ -25,13 +25,16 @@ import com.seibel.distanthorizons.core.dataObjects.render.ColumnRenderSourceLoad
import com.seibel.distanthorizons.core.dataObjects.transformers.FullDataToRenderDataTransformer;
import com.seibel.distanthorizons.core.file.AbstractDataSourceHandler;
import com.seibel.distanthorizons.core.file.structure.AbstractSaveStructure;
import com.seibel.distanthorizons.core.level.IDhLevel;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import com.seibel.distanthorizons.core.logging.f3.F3Screen;
import com.seibel.distanthorizons.core.pos.DhSectionPos;
import com.seibel.distanthorizons.core.dataObjects.render.ColumnRenderSource;
import com.seibel.distanthorizons.core.file.fullDatafile.IFullDataSourceProvider;
import com.seibel.distanthorizons.core.level.IDhClientLevel;
import com.seibel.distanthorizons.core.sql.AbstractDataSourceRepo;
import com.seibel.distanthorizons.core.sql.DataSourceDto;
import com.seibel.distanthorizons.core.sql.FullDataRepo;
import com.seibel.distanthorizons.core.sql.RenderDataRepo;
import com.seibel.distanthorizons.core.util.threading.ThreadPools;
import org.apache.logging.log4j.Logger;
@@ -58,18 +61,39 @@ public class RenderSourceFileHandler extends AbstractDataSourceHandler<ColumnRen
public RenderSourceFileHandler(IFullDataSourceProvider sourceProvider, IDhClientLevel clientLevel, AbstractSaveStructure saveStructure)
{
super(clientLevel, saveStructure, createRepo(clientLevel, saveStructure));
super(clientLevel, saveStructure);
this.fullDataSourceProvider = sourceProvider;
this.threadPoolMsg = new F3Screen.NestedMessage(this::f3Log);
}
private static RenderDataRepo createRepo(IDhClientLevel clientLevel, AbstractSaveStructure saveStructure)
//===========//
// overrides //
//===========//
@Override
public ColumnRenderSource get(DhSectionPos pos)
{
File saveDir = saveStructure.getRenderCacheFolder(clientLevel.getLevelWrapper());
// call the full data provider to make sure the full data is up to date
// and any necessary world generation has been queued/completed
this.fullDataSourceProvider.get(pos);
return super.get(pos);
}
//====================//
// Abstract overrides //
//====================//
@Override
protected AbstractDataSourceRepo createRepo()
{
try
{
return new RenderDataRepo("jdbc:sqlite", saveDir.getPath() + "/" + AbstractSaveStructure.DATABASE_NAME);
return new RenderDataRepo("jdbc:sqlite", this.saveDir.getPath() + "/" + AbstractSaveStructure.DATABASE_NAME);
}
catch (SQLException e)
{
@@ -79,12 +103,6 @@ public class RenderSourceFileHandler extends AbstractDataSourceHandler<ColumnRen
}
}
//====================//
// Abstract overrides //
//====================//
@Override
protected ColumnRenderSource createDataSourceFromDto(DataSourceDto dto) throws InterruptedException, IOException
{ return ColumnRenderSourceLoader.INSTANCE.loadRenderSource(dto, dto.getInputStream(), this.level); }
@@ -132,10 +150,13 @@ public class RenderSourceFileHandler extends AbstractDataSourceHandler<ColumnRen
return lines.toArray(new String[0]);
}
public void updateDataSourcesWithChunkData(ChunkSizedFullDataAccessor chunkDataView)
@Override
public CompletableFuture<Void> updateDataSourcesWithChunkDataAsync(ChunkSizedFullDataAccessor chunkDataView)
{
super.updateDataSourcesWithChunkData(chunkDataView);
this.fullDataSourceProvider.updateDataSourcesWithChunkData(chunkDataView);
return CompletableFuture.allOf(
super.updateDataSourcesWithChunkDataAsync(chunkDataView),
this.fullDataSourceProvider.updateDataSourcesWithChunkDataAsync(chunkDataView)
);
}
@@ -41,6 +41,7 @@ import com.seibel.distanthorizons.coreapi.util.math.Mat4f;
import org.apache.logging.log4j.Logger;
import java.io.Closeable;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.atomic.AtomicReference;
public class ClientLevelModule implements Closeable
@@ -181,12 +182,14 @@ public class ClientLevelModule implements Closeable
ClientRenderState ClientRenderState = this.ClientRenderStateRef.get();
if (ClientRenderState != null)
{
ClientRenderState.renderSourceFileHandler.updateDataSourcesWithChunkData(data);
ClientRenderState.quadtree.reloadPos(data.sectionPos);
ClientRenderState.renderSourceFileHandler
.updateDataSourcesWithChunkDataAsync(data)
// wait for the update to finish before triggering a reload to prevent holes in the world
.thenRun(() -> ClientRenderState.quadtree.reloadPos(data.sectionPos));
}
else
{
this.parentClientLevel.getFileHandler().updateDataSourcesWithChunkData(data);
this.parentClientLevel.getFileHandler().updateDataSourcesWithChunkDataAsync(data);
}
}
@@ -236,7 +236,7 @@ public class DhServerLevel extends DhLevel implements IDhServerLevel
{
DhSectionPos pos = data.getSectionPos();
pos = pos.convertNewToDetailLevel(CompleteFullDataSource.SECTION_SIZE_OFFSET);
this.getFileHandler().updateDataSourcesWithChunkData(data);
this.getFileHandler().updateDataSourcesWithChunkDataAsync(data);
}
@Override
@@ -19,10 +19,13 @@
package com.seibel.distanthorizons.core.render;
import com.seibel.distanthorizons.core.config.Config;
import com.seibel.distanthorizons.core.enums.EDhDirection;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import com.seibel.distanthorizons.core.logging.f3.F3Screen;
import com.seibel.distanthorizons.core.pos.DhSectionPos;
import com.seibel.distanthorizons.core.pos.Pos2D;
import com.seibel.distanthorizons.core.util.LodUtil;
import com.seibel.distanthorizons.core.util.objects.SortedArraySet;
import com.seibel.distanthorizons.core.util.objects.quadTree.QuadNode;
import com.seibel.distanthorizons.core.render.renderer.LodRenderer;
@@ -38,7 +41,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
* This object tells the {@link LodRenderer} what buffers to render
* TODO rename this class, maybe RenderBufferOrganizer or something more specific?
*/
public class RenderBufferHandler
public class RenderBufferHandler implements AutoCloseable
{
private static final Logger LOGGER = DhLoggerBuilder.getLogger();
@@ -50,8 +53,20 @@ public class RenderBufferHandler
private final AtomicBoolean rebuildAllBuffers = new AtomicBoolean(false);
public F3Screen.DynamicMessage f3Message;
public RenderBufferHandler(LodQuadTree lodQuadTree) { this.lodQuadTree = lodQuadTree; }
//=============//
// constructor //
//=============//
public RenderBufferHandler(LodQuadTree lodQuadTree)
{
this.lodQuadTree = lodQuadTree;
this.f3Message = new F3Screen.DynamicMessage(() -> LodUtil.formatLog("Rendered Buffer Count: " + this.loadedNearToFarBuffers.size()));
}
@@ -227,6 +242,12 @@ public class RenderBufferHandler
}
//=========//
// cleanup //
//=========//
@Override
public void close()
{
Iterator<QuadNode<LodRenderSection>> nodeIterator = this.lodQuadTree.nodeIterator();
@@ -47,10 +47,10 @@ import java.io.PrintStream;
import java.lang.invoke.MethodHandles;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.stream.Stream;
/**
* A singleton that holds references to different openGL contexts
@@ -65,15 +65,11 @@ import java.util.stream.Stream;
*
* https://gamedev.stackexchange.com/questions/91995/edit-vbo-data-or-create-a-new-one <br>
* https://stackoverflow.com/questions/63509735/massive-performance-loss-with-glmapbuffer <br><br>
*
* @author James Seibel
*/
public class GLProxy
{
private static final IMinecraftClientWrapper MC = SingletonInjector.INSTANCE.get(IMinecraftClientWrapper.class);
private ExecutorService workerThread = Executors.newSingleThreadExecutor(new ThreadFactoryBuilder().setNameFormat(GLProxy.class.getSimpleName() + "-Worker-Thread").build());
private static final Logger LOGGER = DhLoggerBuilder.getLogger(MethodHandles.lookup().lookupClass().getSimpleName());
public static final ConfigBasedLogger GL_LOGGER = new ConfigBasedLogger(LogManager.getLogger(GLProxy.class),
() -> Config.Client.Advanced.Logging.logRendererGLEvent.get());
@@ -88,6 +84,10 @@ public class GLProxy
private static GLProxy instance = null;
private ExecutorService workerThread = Executors.newSingleThreadExecutor(new ThreadFactoryBuilder().setNameFormat(GLProxy.class.getSimpleName() + "-Worker-Thread").build());
private ConcurrentLinkedQueue<Runnable> renderThreadRunnableQueue = new ConcurrentLinkedQueue<>();
/** Minecraft's GLFW window */
public final long minecraftGlContext;
/** Minecraft's GL capabilities */
@@ -459,9 +459,9 @@ public class GLProxy
//============================//
// MC render thread runnables //
//============================//
//=========================//
// Worker Thread Runnables //
//=========================//
/**
* Asynchronously calls the given runnable on proxy's OpenGL context.
@@ -472,14 +472,18 @@ public class GLProxy
public void recordOpenGlCall(Runnable renderCall)
{
StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
this.workerThread.execute(() -> this.runnableContainer(renderCall, stackTrace));
this.workerThread.execute(() -> this.runOpenGlCall(renderCall, stackTrace, true));
}
private void runnableContainer(Runnable renderCall, StackTraceElement[] stackTrace)
private void runOpenGlCall(Runnable renderCall, StackTraceElement[] stackTrace, boolean useProxyWorkerContext)
{
try
{
// set up the context...
this.setGlContext(EGLProxyContext.PROXY_WORKER);
// set up the context if requested...
if (useProxyWorkerContext)
{
this.setGlContext(EGLProxyContext.PROXY_WORKER);
}
// ...run the actual code...
renderCall.run();
}
@@ -492,7 +496,10 @@ public class GLProxy
finally
{
// ...and make sure the context is released when the thread finishes
this.setGlContext(EGLProxyContext.NONE);
if (useProxyWorkerContext)
{
this.setGlContext(EGLProxyContext.NONE);
}
}
}
@@ -528,6 +535,32 @@ public class GLProxy
//=========================//
// Render Thread Runnables //
//=========================//
public void queueRunningOnRenderThread(Runnable renderCall)
{
StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
this.renderThreadRunnableQueue.add(() -> this.runOpenGlCall(renderCall, stackTrace, false));
}
/**
* Doesn't do any thread/GL Context validation.
* Running this outside of the render thread may cause crashes or other issues.
*/
public void runRenderThreadTasks()
{
Runnable runnable = this.renderThreadRunnableQueue.poll();
while(runnable != null)
{
runnable.run();
runnable = this.renderThreadRunnableQueue.poll();
}
}
//=========//
// logging //
//=========//
@@ -122,9 +122,12 @@ public class GLBuffer implements AutoCloseable
}
private static void destroyBufferId(boolean async, int id)
{
if (async && GLProxy.getInstance().getGlContext() != EGLProxyContext.PROXY_WORKER)
EGLProxyContext glContext = GLProxy.getInstance().getGlContext();
if (async
&& glContext != EGLProxyContext.PROXY_WORKER
&& glContext != EGLProxyContext.MINECRAFT)
{
GLProxy.getInstance().recordOpenGlCall(() -> destroyBufferId(false, id));
GLProxy.getInstance().queueRunningOnRenderThread(() -> destroyBufferId(false, id));
}
else
{
@@ -200,7 +203,7 @@ public class GLBuffer implements AutoCloseable
LodUtil.assertTrue(this.bufferStorage, "Buffer is not bufferStorage but its trying to use bufferStorage upload method!");
int bbSize = bb.limit() - bb.position();
this.destroy(false);
this.destroy(true);
this.create(true);
this.bind();
GL44.glBufferStorage(this.getBufferBindingTarget(), bb, bufferStorageHint);
@@ -313,7 +316,7 @@ public class GLBuffer implements AutoCloseable
{
// recreate if the buffer storage type changed
this.bind();
this.destroy(false);
this.destroy(true);
this.create(uploadMethod.useBufferStorage);
this.bind();
}
@@ -486,6 +486,9 @@ public class LodRenderer
profiler.popPush("LOD cleanup");
LagSpikeCatcher drawCleanup = new LagSpikeCatcher();
GLProxy.getInstance().runRenderThreadTasks();
lightmap.unbind();
if (ENABLE_IBO)
{
@@ -666,7 +669,7 @@ public class LodRenderer
this.setupLock.lock();
EVENT_LOGGER.info("Queuing Renderer Cleanup for main render thread");
GLProxy.getInstance().recordOpenGlCall(() ->
GLProxy.getInstance().queueRunningOnRenderThread(() ->
{
EVENT_LOGGER.info("Renderer Cleanup Started");