From 81b198067031a5ddbbd16e12dae6da93563675fd Mon Sep 17 00:00:00 2001 From: James Seibel Date: Wed, 10 Feb 2021 15:29:58 -0600 Subject: [PATCH] Add the first attempt at multithreading the buffer building --- .../com/backsun/lod/renderer/LodRenderer.java | 419 ++++++++++++++---- 1 file changed, 323 insertions(+), 96 deletions(-) diff --git a/src/main/java/com/backsun/lod/renderer/LodRenderer.java b/src/main/java/com/backsun/lod/renderer/LodRenderer.java index 0bbd760d1..6a6b8c560 100644 --- a/src/main/java/com/backsun/lod/renderer/LodRenderer.java +++ b/src/main/java/com/backsun/lod/renderer/LodRenderer.java @@ -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); + } + } + + } + +