Add the first attempt at multithreading the buffer building

This commit is contained in:
James Seibel
2021-02-10 15:29:58 -06:00
parent fdfc55f200
commit 81b1980670
@@ -1,6 +1,8 @@
package com.backsun.lod.renderer;
import java.awt.Color;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import org.lwjgl.opengl.GL11;
import org.lwjgl.util.glu.Project;
@@ -15,9 +17,12 @@ import com.backsun.lod.util.fog.FogQuality;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.BufferBuilder;
import net.minecraft.client.renderer.GLAllocation;
import net.minecraft.client.renderer.GlStateManager;
import net.minecraft.client.renderer.Tessellator;
import net.minecraft.client.renderer.vertex.DefaultVertexFormats;
import net.minecraft.client.renderer.vertex.VertexFormat;
import net.minecraft.client.renderer.vertex.VertexFormatElement;
import net.minecraft.entity.Entity;
import net.minecraft.util.math.AxisAlignedBB;
import net.minecraft.util.math.MathHelper;
@@ -52,7 +57,8 @@ public class LodRenderer
{
mc = Minecraft.getMinecraft();
tessellator = Tessellator.getInstance();
// for some reason "Tessellator.getInstance()" won't work here, we have to create a new one
tessellator = new Tessellator(2097152);
bufferBuilder = tessellator.getBuffer();
reflectionHandler = new ReflectionHandler();
@@ -251,7 +257,7 @@ public class LodRenderer
mc.mcProfiler.endStartSection("LOD build buffer");
// send the LODs over to the GPU
sendToGPUAndDraw(lodArray, colorArray, cameraX, cameraY ,cameraZ);
sendToGPUAndDraw(lodArray, colorArray);
@@ -293,113 +299,334 @@ public class LodRenderer
//private ExecutorService threadPool = Executors.newFixedThreadPool(8);
private int numbThreads = 1;
private BuildBufferThread[] threads = new BuildBufferThread[numbThreads];
private ByteBuffer[] buffers = new ByteBuffer[numbThreads];
/**
* draw an array of cubes (or squares) with the given colors.
* @param lods bounding boxes to draw
* @param colors color of each box to draw
*/
private void sendToGPUAndDraw(AxisAlignedBB[][] lods, Color[][] colors, double cameraX, double cameraY, double cameraZ)
private void sendToGPUAndDraw(AxisAlignedBB[][] lods, Color[][] colors)
{
int numbChunksWide = lods.length;
AxisAlignedBB bb;
int red;
int green;
int blue;
int alpha;
bufferBuilder.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION_COLOR);
// x axis
for (int i = 0; i < numbChunksWide; i++)
for(int i = 0; i < numbThreads; i++)
{
// z axis
for (int j = 0; j < numbChunksWide; j++)
if (buffers[i] == null)
{
// skip the middle
// (As the player moves some chunks will overlap or be missing,
// this is just how chunk loading/unloading works. This can hopefully
// be hidden with careful use of fog)
int middle = (numbChunksWide / 2) - 1;
if (isCoordinateInLoadedArea(i, j, middle))
{
continue;
}
if (lods[i][j] == null || colors[i][j] == null)
continue;
bb = lods[i][j];
// get the color of this LOD object
red = colors[i][j].getRed();
green = colors[i][j].getGreen();
blue = colors[i][j].getBlue();
alpha = colors[i][j].getAlpha();
// only draw all 6 sides if there is some thickness to the box
if (bb.minY != bb.maxY)
{
// top (facing up)
bufferBuilder.pos(bb.minX, bb.maxY, bb.minZ).color(red, green, blue, alpha).endVertex();
bufferBuilder.pos(bb.minX, bb.maxY, bb.maxZ).color(red, green, blue, alpha).endVertex();
bufferBuilder.pos(bb.maxX, bb.maxY, bb.maxZ).color(red, green, blue, alpha).endVertex();
bufferBuilder.pos(bb.maxX, bb.maxY, bb.minZ).color(red, green, blue, alpha).endVertex();
// bottom (facing down)
bufferBuilder.pos(bb.maxX, bb.minY, bb.minZ).color(red, green, blue, alpha).endVertex();
bufferBuilder.pos(bb.maxX, bb.minY, bb.maxZ).color(red, green, blue, alpha).endVertex();
bufferBuilder.pos(bb.minX, bb.minY, bb.maxZ).color(red, green, blue, alpha).endVertex();
bufferBuilder.pos(bb.minX, bb.minY, bb.minZ).color(red, green, blue, alpha).endVertex();
// south (facing -Z)
bufferBuilder.pos(bb.maxX, bb.minY, bb.maxZ).color(red, green, blue, alpha).endVertex();
bufferBuilder.pos(bb.maxX, bb.maxY, bb.maxZ).color(red, green, blue, alpha).endVertex();
bufferBuilder.pos(bb.minX, bb.maxY, bb.maxZ).color(red, green, blue, alpha).endVertex();
bufferBuilder.pos(bb.minX, bb.minY, bb.maxZ).color(red, green, blue, alpha).endVertex();
// north (facing +Z)
bufferBuilder.pos(bb.minX, bb.minY, bb.minZ).color(red, green, blue, alpha).endVertex();
bufferBuilder.pos(bb.minX, bb.maxY, bb.minZ).color(red, green, blue, alpha).endVertex();
bufferBuilder.pos(bb.maxX, bb.maxY, bb.minZ).color(red, green, blue, alpha).endVertex();
bufferBuilder.pos(bb.maxX, bb.minY, bb.minZ).color(red, green, blue, alpha).endVertex();
// west (facing -X)
bufferBuilder.pos(bb.minX, bb.minY, bb.minZ).color(red, green, blue, alpha).endVertex();
bufferBuilder.pos(bb.minX, bb.minY, bb.maxZ).color(red, green, blue, alpha).endVertex();
bufferBuilder.pos(bb.minX, bb.maxY, bb.maxZ).color(red, green, blue, alpha).endVertex();
bufferBuilder.pos(bb.minX, bb.maxY, bb.minZ).color(red, green, blue, alpha).endVertex();
// east (facing +X)
bufferBuilder.pos(bb.maxX, bb.maxY, bb.minZ).color(red, green, blue, alpha).endVertex();
bufferBuilder.pos(bb.maxX, bb.maxY, bb.maxZ).color(red, green, blue, alpha).endVertex();
bufferBuilder.pos(bb.maxX, bb.minY, bb.maxZ).color(red, green, blue, alpha).endVertex();
bufferBuilder.pos(bb.maxX, bb.minY, bb.minZ).color(red, green, blue, alpha).endVertex();
}
else
{
// bottom (facing up)
bufferBuilder.pos(bb.minX, bb.minY, bb.minZ).color(red, green, blue, alpha).endVertex();
bufferBuilder.pos(bb.minX, bb.minY, bb.maxZ).color(red, green, blue, alpha).endVertex();
bufferBuilder.pos(bb.maxX, bb.minY, bb.maxZ).color(red, green, blue, alpha).endVertex();
bufferBuilder.pos(bb.maxX, bb.minY, bb.minZ).color(red, green, blue, alpha).endVertex();
// top (facing up)
// bufferBuilder.pos(bb.minX, bb.maxY, bb.minZ).color(red, green, blue, alpha).endVertex();
// bufferBuilder.pos(bb.minX, bb.maxY, bb.maxZ).color(red, green, blue, alpha).endVertex();
// bufferBuilder.pos(bb.maxX, bb.maxY, bb.maxZ).color(red, green, blue, alpha).endVertex();
// bufferBuilder.pos(bb.maxX, bb.maxY, bb.minZ).color(red, green, blue, alpha).endVertex();
}
} // z axis
} // x axis
buffers[i] = ByteBuffer.allocateDirect(bufferBuilder.getByteBuffer().capacity()); // 8388608
buffers[i].order(ByteOrder.LITTLE_ENDIAN);
}
int pos = bufferBuilder.getByteBuffer().position();
buffers[i].position(pos);
// System.out.println(bufferBuilder.getByteBuffer() + "\t\t" + buffers[i].toString());
BuildBufferThread thread = new BuildBufferThread(buffers[i], lods, colors, i, numbThreads);
thread.run();
threads[i] = thread;
try
{ threads[i].join(); }
catch(Exception e)
{ e.printStackTrace(); }
}
// for(int i = 0; i < 8; i++)
// {
// try
// { threads[i].join(); }
// catch(Exception e)
// { e.printStackTrace(); }
// }
// ByteBuffer tessBuffer = bufferBuilder.getByteBuffer();
// ByteBuffer demoBuffer = buffers[0];
//
// boolean same = true;
// for(int i = 0; i < tessBuffer.capacity(); i++)
// {
// int testIndex = i;
// byte demoVal = demoBuffer.get(testIndex);
// byte tessVal = tessBuffer.get(testIndex);
// if(demoVal != tessVal)
// {
// System.out.println("ye-no " + i);
// same = false;
// break;
// }
// }
// if (same)
// System.out.println("they are the same jim");
mc.mcProfiler.endStartSection("LOD draw");
// draw the LODs
tessellator.draw();
for(int i = 0; i < numbThreads; i++)
{
bufferBuilder.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION_COLOR);
bufferBuilder.putBulkData(buffers[i]);
tessellator.draw();
// this is required otherwise nothing is drawn
bufferBuilder.getByteBuffer().clear();
}
}
private class BuildBufferThread extends Thread
{
ByteBuffer buffer;
AxisAlignedBB[][] lods;
Color[][] colors;
int start = 0;
int end = -1;
BuildBufferThread(ByteBuffer newByteBuffer, AxisAlignedBB[][] newLods, Color[][] newColors, int threadCount, int totalThreads)
{
buffer = newByteBuffer;
lods = newLods;
colors = newColors;
int numbChunksWide = lods.length;
int rowsToRender = numbChunksWide / totalThreads;
start = threadCount * rowsToRender;
end = (threadCount + 1) * rowsToRender;
vertexCount = 0;
vertexFormat = DefaultVertexFormats.POSITION_COLOR;
vertexFormatIndex = 0;
vertexFormatElement = vertexFormat.getElement(this.vertexFormatIndex);
}
@Override
public void run()
{
int numbChunksWide = lods.length;
AxisAlignedBB bb;
int red;
int green;
int blue;
int alpha;
// x axis
for (int i = start; i < end; i++)
{
// z axis
for (int j = 0; j < numbChunksWide; j++)
{
// skip the middle
// (As the player moves some chunks will overlap or be missing,
// this is just how chunk loading/unloading works. This can hopefully
// be hidden with careful use of fog)
int middle = (numbChunksWide / 2) - 1;
if (isCoordinateInLoadedArea(i, j, middle))
{
continue;
}
if (lods[i][j] == null || colors[i][j] == null)
continue;
bb = lods[i][j];
// get the color of this LOD object
red = colors[i][j].getRed();
green = colors[i][j].getGreen();
blue = colors[i][j].getBlue();
alpha = colors[i][j].getAlpha();
// only draw all 6 sides if there is some thickness to the box
if (bb.minY != bb.maxY)
{
// top (facing up)
addPosAndColor(buffer, bb.minX, bb.maxY, bb.minZ, red, green, blue, alpha);
addPosAndColor(buffer, bb.minX, bb.maxY, bb.maxZ, red, green, blue, alpha);
addPosAndColor(buffer, bb.maxX, bb.maxY, bb.maxZ, red, green, blue, alpha);
addPosAndColor(buffer, bb.maxX, bb.maxY, bb.minZ, red, green, blue, alpha);
// bottom (facing down)
addPosAndColor(buffer, bb.maxX, bb.minY, bb.minZ, red, green, blue, alpha);
addPosAndColor(buffer, bb.maxX, bb.minY, bb.maxZ, red, green, blue, alpha);
addPosAndColor(buffer, bb.minX, bb.minY, bb.maxZ, red, green, blue, alpha);
addPosAndColor(buffer, bb.minX, bb.minY, bb.minZ, red, green, blue, alpha);
// south (facing -Z)
addPosAndColor(buffer, bb.maxX, bb.minY, bb.maxZ, red, green, blue, alpha);
addPosAndColor(buffer, bb.maxX, bb.maxY, bb.maxZ, red, green, blue, alpha);
addPosAndColor(buffer, bb.minX, bb.maxY, bb.maxZ, red, green, blue, alpha);
addPosAndColor(buffer, bb.minX, bb.minY, bb.maxZ, red, green, blue, alpha);
// north (facing +Z)
addPosAndColor(buffer, bb.minX, bb.minY, bb.minZ, red, green, blue, alpha);
addPosAndColor(buffer, bb.minX, bb.maxY, bb.minZ, red, green, blue, alpha);
addPosAndColor(buffer, bb.maxX, bb.maxY, bb.minZ, red, green, blue, alpha);
addPosAndColor(buffer, bb.maxX, bb.minY, bb.minZ, red, green, blue, alpha);
// west (facing -X)
addPosAndColor(buffer, bb.minX, bb.minY, bb.minZ, red, green, blue, alpha);
addPosAndColor(buffer, bb.minX, bb.minY, bb.maxZ, red, green, blue, alpha);
addPosAndColor(buffer, bb.minX, bb.maxY, bb.maxZ, red, green, blue, alpha);
addPosAndColor(buffer, bb.minX, bb.maxY, bb.minZ, red, green, blue, alpha);
// east (facing +X)
addPosAndColor(buffer, bb.maxX, bb.maxY, bb.minZ, red, green, blue, alpha);
addPosAndColor(buffer, bb.maxX, bb.maxY, bb.maxZ, red, green, blue, alpha);
addPosAndColor(buffer, bb.maxX, bb.minY, bb.maxZ, red, green, blue, alpha);
addPosAndColor(buffer, bb.maxX, bb.minY, bb.minZ, red, green, blue, alpha);
}
else
{
// bottom (facing up)
// buffer.pos(bb.minX, bb.minY, bb.minZ).color(red, green, blue, alpha).endVertex();
// buffer.pos(bb.minX, bb.minY, bb.maxZ).color(red, green, blue, alpha).endVertex();
// buffer.pos(bb.maxX, bb.minY, bb.maxZ).color(red, green, blue, alpha).endVertex();
// buffer.pos(bb.maxX, bb.minY, bb.minZ).color(red, green, blue, alpha).endVertex();
// top (facing up)
// bufferBuilder.pos(bb.minX, bb.maxY, bb.minZ).color(red, green, blue, alpha).endVertex();
// bufferBuilder.pos(bb.minX, bb.maxY, bb.maxZ).color(red, green, blue, alpha).endVertex();
// bufferBuilder.pos(bb.maxX, bb.maxY, bb.maxZ).color(red, green, blue, alpha).endVertex();
// bufferBuilder.pos(bb.maxX, bb.maxY, bb.minZ).color(red, green, blue, alpha).endVertex();
}
} // z axis
} // x axis
}
private void addPosAndColor(ByteBuffer buffer, double x, double y, double z, int red, int green, int blue, int alpha)
{
addPos(buffer, x, y, z);
addColor(buffer, red, green, blue, alpha);
endVertex();
}
private int vertexCount = 0;
private VertexFormat vertexFormat = null;
private int vertexFormatIndex = 0;
private VertexFormatElement vertexFormatElement = null;
private void addPos(ByteBuffer byteBuffer, double x, double y, double z)
{
int i = this.vertexCount * this.vertexFormat.getNextOffset() + this.vertexFormat.getOffset(this.vertexFormatIndex);
switch (this.vertexFormatElement.getType())
{
case FLOAT: // This is the one currently used
byteBuffer.putFloat(i, (float)(x));
byteBuffer.putFloat(i + 4, (float)(y));
byteBuffer.putFloat(i + 8, (float)(z));
break;
case UINT:
case INT:
byteBuffer.putInt(i, Float.floatToRawIntBits((float)(x)));
byteBuffer.putInt(i + 4, Float.floatToRawIntBits((float)(y)));
byteBuffer.putInt(i + 8, Float.floatToRawIntBits((float)(z)));
break;
case USHORT:
case SHORT:
byteBuffer.putShort(i, (short)((int)(x)));
byteBuffer.putShort(i + 2, (short)((int)(y)));
byteBuffer.putShort(i + 4, (short)((int)(z)));
break;
case UBYTE:
case BYTE:
byteBuffer.put(i, (byte)((int)(x)));
byteBuffer.put(i + 1, (byte)((int)(y)));
byteBuffer.put(i + 2, (byte)((int)(z)));
}
nextVertexFormatIndex();
}
private void addColor(ByteBuffer byteBuffer, int red, int green, int blue, int alpha)
{
int i = this.vertexCount * this.vertexFormat.getNextOffset() + this.vertexFormat.getOffset(this.vertexFormatIndex);
switch (this.vertexFormatElement.getType())
{
case FLOAT:
byteBuffer.putFloat(i, red / 255.0F);
byteBuffer.putFloat(i + 4, green / 255.0F);
byteBuffer.putFloat(i + 8, blue / 255.0F);
byteBuffer.putFloat(i + 12, alpha / 255.0F);
break;
case UINT:
case INT:
byteBuffer.putFloat(i, red);
byteBuffer.putFloat(i + 4, green);
byteBuffer.putFloat(i + 8, blue);
byteBuffer.putFloat(i + 12, alpha);
break;
case USHORT:
case SHORT:
byteBuffer.putShort(i, (short)red);
byteBuffer.putShort(i + 2, (short)green);
byteBuffer.putShort(i + 4, (short)blue);
byteBuffer.putShort(i + 6, (short)alpha);
break;
case UBYTE:
case BYTE:
if (ByteOrder.nativeOrder() == ByteOrder.LITTLE_ENDIAN)
{
// this is the one used currently
byteBuffer.put(i, (byte)red);
byteBuffer.put(i + 1, (byte)green);
byteBuffer.put(i + 2, (byte)blue);
byteBuffer.put(i + 3, (byte)alpha);
}
else
{
byteBuffer.put(i, (byte)alpha);
byteBuffer.put(i + 1, (byte)blue);
byteBuffer.put(i + 2, (byte)green);
byteBuffer.put(i + 3, (byte)red);
}
}
nextVertexFormatIndex();
}
private void nextVertexFormatIndex()
{
++this.vertexFormatIndex;
this.vertexFormatIndex %= this.vertexFormat.getElementCount();
this.vertexFormatElement = this.vertexFormat.getElement(this.vertexFormatIndex);
if (this.vertexFormatElement.getUsage() == VertexFormatElement.EnumUsage.PADDING)
{
this.nextVertexFormatIndex();
}
}
private void endVertex()
{
++this.vertexCount;
growBuffer(this.vertexFormat.getNextOffset());
}
private void growBuffer(int p_181670_1_)
{
//if (MathHelper.roundUp(p_181670_1_, 4) / 4 > this.rawIntBuffer.remaining() || this.vertexCount * this.vertexFormat.getNextOffset() + p_181670_1_ > this.byteBuffer.capacity())
if (this.vertexCount * this.vertexFormat.getNextOffset() + p_181670_1_ > buffer.capacity())
{
int i = buffer.capacity();
int j = i + MathHelper.roundUp(p_181670_1_, 2097152);
// int k = this.rawIntBuffer.position();
ByteBuffer directBytebuffer = GLAllocation.createDirectByteBuffer(j);
buffer.position(0);
directBytebuffer.put(buffer);
directBytebuffer.rewind();
buffer = directBytebuffer;
// this.rawFloatBuffer = buffer.asFloatBuffer().asReadOnlyBuffer();
// this.rawIntBuffer = buffer.asIntBuffer();
// this.rawIntBuffer.position(k);
// this.rawShortBuffer = buffer.asShortBuffer();
// this.rawShortBuffer.position(k << 1);
}
}
}