rewrote the render side of texture bakery, is very significantly better

This commit is contained in:
mcrcortex
2025-05-06 01:06:37 +10:00
parent 553935626e
commit 45aaf9b981
13 changed files with 583 additions and 65 deletions

View File

@@ -0,0 +1,72 @@
package me.cortex.voxy.client.core.gl;
import me.cortex.voxy.common.util.TrackedObject;
import java.util.Arrays;
import static org.lwjgl.opengl.GL45C.*;
public class GlVertexArray extends TrackedObject {
public final int id;
private int[] indices = new int[0];
private int stride;
public GlVertexArray() {
this.id = glCreateVertexArrays();
}
@Override
public void free() {
this.free0();
glDeleteVertexArrays(this.id);
}
public void bind() {
glBindVertexArray(this.id);
}
public GlVertexArray bindBuffer(int buffer) {
//TODO: optimization, use glVertexArrayVertexBuffers
for (int index : this.indices) {
glVertexArrayVertexBuffer(this.id, index, buffer, 0, this.stride);
}
return this;
}
public GlVertexArray bindElementBuffer(int buffer) {
glVertexArrayElementBuffer(this.id, buffer);
return this;
}
public GlVertexArray setStride(int stride) {
this.stride = stride;
return this;
}
public GlVertexArray setI(int index, int type, int count, int offset) {
this.addIndex(index);
glEnableVertexArrayAttrib(this.id, index);
glVertexArrayAttribIFormat(this.id, index, count, type, offset);
return this;
}
public GlVertexArray setF(int index, int type, int count, int offset) {
return this.setF(index, type, count, false, offset);
}
public GlVertexArray setF(int index, int type, int count, boolean normalize, int offset) {
this.addIndex(index);
glEnableVertexArrayAttrib(this.id, index);
glVertexArrayAttribFormat(this.id, index, count, type, normalize, offset);
return this;
}
private void addIndex(int index) {
for (int i : this.indices) {
if (i == index) {
return;
}
}
this.indices = Arrays.copyOf(this.indices, this.indices.length+1);
this.indices[this.indices.length-1] = index;
}
}

View File

@@ -123,7 +123,7 @@ public class BakedBlockEntityModel {
layer.putInto(bb);
var mesh = bb.endNullable();
if (mesh!=null)
BudgetBufferRenderer.draw(mesh, texture, matrix);
BudgetBufferRenderer.drawFast(mesh, texture, matrix);
}
}

View File

@@ -8,54 +8,103 @@ import com.mojang.blaze3d.systems.RenderPass;
import com.mojang.blaze3d.systems.RenderSystem;
import com.mojang.blaze3d.textures.GpuTexture;
import com.mojang.blaze3d.vertex.VertexFormat;
import me.cortex.voxy.client.core.gl.GlBuffer;
import me.cortex.voxy.client.core.gl.GlVertexArray;
import me.cortex.voxy.client.core.gl.shader.Shader;
import me.cortex.voxy.client.core.gl.shader.ShaderType;
import me.cortex.voxy.client.core.rendering.util.SharedIndexBuffer;
import me.cortex.voxy.client.core.rendering.util.UploadStream;
import me.cortex.voxy.common.util.UnsafeUtil;
import net.minecraft.client.gl.*;
import net.minecraft.client.render.BuiltBuffer;
import net.minecraft.client.render.VertexFormats;
import net.minecraft.util.Identifier;
import org.joml.Matrix4f;
import org.lwjgl.system.MemoryUtil;
import static org.lwjgl.opengl.ARBVertexArrayObject.glBindVertexArray;
import static org.lwjgl.opengl.GL15.GL_ELEMENT_ARRAY_BUFFER;
import static org.lwjgl.opengl.GL15.glBindBuffer;
import static org.lwjgl.opengl.GL20.glUniformMatrix4fv;
import static org.lwjgl.opengl.GL33.glBindSampler;
import static org.lwjgl.opengl.GL43.glBindVertexBuffer;
import static org.lwjgl.opengl.GL45.*;
public class BudgetBufferRenderer {
public static final RenderPipeline RENDERER_THING = RenderPipeline.builder()
.withLocation(Identifier.of("voxy","bakery/position_tex"))
.withVertexShader(Identifier.of("voxy","bakery/position_tex"))
.withFragmentShader(Identifier.of("voxy","bakery/position_tex"))
.withUniform("transform", UniformType.MATRIX4X4)
.withSampler("tex")
.withDepthTestFunction(DepthTestFunction.LEQUAL_DEPTH_TEST)
.withVertexFormat(VertexFormats.POSITION_TEXTURE_COLOR, VertexFormat.DrawMode.QUADS)
.build();
private static final Shader bakeryShader = Shader.make()
.add(ShaderType.VERTEX, "voxy:bakery/position_tex.vsh")
.add(ShaderType.FRAGMENT, "voxy:bakery/position_tex.fsh")
.compile();
private static final GlBuffer indexBuffer;
static {
var i = RenderSystem.getSequentialBuffer(VertexFormat.DrawMode.QUADS);
int id = ((GlGpuBuffer) i.getIndexBuffer(4096*3*2)).id;
if (i.getIndexType() != VertexFormat.IndexType.SHORT) {
throw new IllegalStateException();
}
indexBuffer = new GlBuffer(3*2*2*4096);
glCopyNamedBufferSubData(id, indexBuffer.id, 0, 0, 3*2*2*4096);
}
private static final int STRIDE = 24;
private static final GlVertexArray VA = new GlVertexArray()
.setStride(STRIDE)
.setF(0, GL_FLOAT, 3, 0)//pos
.setI(1, GL_INT, 1, 4 * 3)//metadata
.setF(2, GL_FLOAT, 2, 4 * 4)//UV
.bindElementBuffer(indexBuffer.id);
private static GlBuffer immediateBuffer;
private static int quadCount;
public static void drawFast(BuiltBuffer buffer, GpuTexture tex, Matrix4f matrix) {
if (buffer.getDrawParameters().mode() != VertexFormat.DrawMode.QUADS) {
throw new IllegalStateException("Fast only supports quads");
}
var buff = buffer.getBuffer();
int size = buff.remaining();
if (size%STRIDE != 0) throw new IllegalStateException();
size /= STRIDE;
if (size%4 != 0) throw new IllegalStateException();
size /= 4;
setup(MemoryUtil.memAddress(buff), size, ((net.minecraft.client.texture.GlTexture)tex).getGlId());
buffer.close();
render(matrix);
}
public static void setup(long dataPtr, int quads, int texId) {
if (quads == 0) {
throw new IllegalStateException();
}
public static void draw(BuiltBuffer buffer, GpuTexture tex, Matrix4f matrix) {
//Fuz the gpu sampler state
GlStateManager._activeTexture(GlConst.GL_TEXTURE0);
GlStateManager._bindTexture(0);
GlStateManager._activeTexture(GlConst.GL_TEXTURE1);
GlStateManager._bindTexture(0);
GlStateManager._activeTexture(GlConst.GL_TEXTURE2);
GlStateManager._bindTexture(0);
GlStateManager._activeTexture(GlConst.GL_TEXTURE2+1);
GlStateManager._bindTexture(0);
RenderSystem.ShapeIndexBuffer shapeIndexBuffer = RenderSystem.getSequentialBuffer(buffer.getDrawParameters().mode());
GpuBuffer gpuBuffer = buffer.getDrawParameters().format().uploadImmediateVertexBuffer(buffer.getBuffer());
quadCount = quads;
var res = (GlResourceManager)RenderSystem.getDevice()
.createCommandEncoder();
res.currentProgram = null;
res.currentPipeline = null;
try (RenderPass renderPass = new RenderPassImpl(res, false)) {
renderPass.setPipeline(RENDERER_THING);
renderPass.setVertexBuffer(0, gpuBuffer);
renderPass.bindSampler("tex", tex);
renderPass.setUniform("transform", matrix);
renderPass.setIndexBuffer(shapeIndexBuffer.getIndexBuffer(buffer.getDrawParameters().indexCount()), shapeIndexBuffer.getIndexType());
renderPass.drawIndexed(0, buffer.getDrawParameters().indexCount());
long size = quads * 4L * STRIDE;
if (immediateBuffer == null || immediateBuffer.size()<size) {
if (immediateBuffer != null) {
immediateBuffer.free();
}
immediateBuffer = new GlBuffer(size*2L);//This also accounts for when immediateBuffer == null
VA.bindBuffer(immediateBuffer.id);
}
//gpuBuffer.close();
buffer.close();
res.currentProgram = null;
res.currentPipeline = null;
long ptr = UploadStream.INSTANCE.upload(immediateBuffer, 0, size);
MemoryUtil.memCopy(dataPtr, ptr, size);
UploadStream.INSTANCE.commit();
bakeryShader.bind();
VA.bind();
glBindSampler(0, 0);
glBindTextureUnit(0, texId);
}
public static void render(Matrix4f matrix) {
glUniformMatrix4fv(1, false, matrix.get(new float[16]));
glDrawElements(GL_TRIANGLES, quadCount * 2 * 3, GL_UNSIGNED_SHORT, 0);
}
}

View File

@@ -21,8 +21,12 @@ import java.util.concurrent.locks.StampedLock;
import static org.lwjgl.opengl.ARBFramebufferObject.GL_COLOR_ATTACHMENT0;
import static org.lwjgl.opengl.GL11.GL_COLOR_BUFFER_BIT;
import static org.lwjgl.opengl.GL11.glGetInteger;
import static org.lwjgl.opengl.GL11C.GL_NEAREST;
import static org.lwjgl.opengl.GL30.GL_FRAMEBUFFER;
import static org.lwjgl.opengl.GL30.GL_FRAMEBUFFER_BINDING;
import static org.lwjgl.opengl.GL30C.GL_DRAW_FRAMEBUFFER_BINDING;
import static org.lwjgl.opengl.GL30C.glBindFramebuffer;
import static org.lwjgl.opengl.GL45.glBlitNamedFramebuffer;
public class ModelBakerySubsystem {
@@ -79,6 +83,8 @@ public class ModelBakerySubsystem {
Integer i = this.blockIdQueue.poll();
int j = 0;
if (i != null) {
int fbBinding = glGetInteger(GL_FRAMEBUFFER_BINDING);
do {
this.factory.addEntry(i);
j++;
@@ -86,6 +92,8 @@ public class ModelBakerySubsystem {
break;
i = this.blockIdQueue.poll();
} while (i != null);
glBindFramebuffer(GL_FRAMEBUFFER, fbBinding);//This is done here as stops needing to set then unset the fb in the thing 1000x
}
this.blockIdCount.addAndGet(-j);
}

View File

@@ -6,6 +6,7 @@ import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet;
import it.unimi.dsi.fastutil.objects.ObjectSet;
import me.cortex.voxy.client.core.gl.Capabilities;
import me.cortex.voxy.client.core.model.bakery.ModelTextureBakery;
import me.cortex.voxy.client.core.model.bakery.ModelTextureBakery2;
import me.cortex.voxy.client.core.rendering.util.RawDownloadStream;
import me.cortex.voxy.client.core.rendering.util.UploadStream;
import me.cortex.voxy.common.Logger;
@@ -67,7 +68,7 @@ public class ModelFactory {
private final Biome DEFAULT_BIOME = MinecraftClient.getInstance().world.getRegistryManager().getOrThrow(RegistryKeys.BIOME).get(BiomeKeys.PLAINS);
public final ModelTextureBakery bakery;
public final ModelTextureBakery2 bakery;
//Model data might also contain a constant colour if the colour resolver produces a constant colour, this saves space in the
@@ -125,7 +126,7 @@ public class ModelFactory {
public ModelFactory(Mapper mapper, ModelStore storage) {
this.mapper = mapper;
this.storage = storage;
this.bakery = new ModelTextureBakery(MODEL_TEXTURE_SIZE, MODEL_TEXTURE_SIZE);
this.bakery = new ModelTextureBakery2(MODEL_TEXTURE_SIZE, MODEL_TEXTURE_SIZE);
this.metadataCache = new long[1<<16];
this.fluidStateLUT = new int[1<<16];
@@ -194,7 +195,7 @@ public class ModelFactory {
}
this.resultJobs.add(()->processTextureBakeResult(blockId, blockState, textureData));
});
this.bakery.renderFacesToStream(blockState, 123456, isFluid, this.downstream.getBufferId(), allocation);
this.bakery.renderToStream(blockState, this.downstream.getBufferId(), allocation);
return true;
}

View File

@@ -6,11 +6,13 @@ import me.cortex.voxy.client.core.gl.GlTexture;
import me.cortex.voxy.client.core.gl.shader.Shader;
import me.cortex.voxy.client.core.gl.shader.ShaderType;
import static org.lwjgl.opengl.ARBDirectStateAccess.glClearNamedFramebufferfv;
import static org.lwjgl.opengl.ARBDirectStateAccess.glTextureParameteri;
import static org.lwjgl.opengl.GL11.*;
import static org.lwjgl.opengl.GL11.GL_STENCIL_INDEX;
import static org.lwjgl.opengl.GL30.*;
import static org.lwjgl.opengl.GL43.*;
import static org.lwjgl.opengl.GL45.glClearNamedFramebufferfi;
public class GlViewCapture {
private final int width;
@@ -57,6 +59,11 @@ public class GlViewCapture {
glDispatchCompute(3, 2, 1);
}
public void clear() {
glClearNamedFramebufferfv(this.framebuffer.id, GL_COLOR, 0, new float[]{0,0,0,0});
glClearNamedFramebufferfi(this.framebuffer.id, GL_DEPTH_STENCIL, 0, 1.0f, 0);
}
public void free() {
this.framebuffer.free();
this.colourTex.free();

View File

@@ -219,6 +219,8 @@ public class ModelTextureBakery {
if (!renderFluid) {
//TODO: need to do 2 variants for quads, one which have coloured, ones that dont, might be able to pull a spare bit
// at the end whether or not a pixel should be mixed with texture
//TODO: CACHE THE BUILT MODEL AND REUSE AND JUST RENDER FROM DIFFERENT VIEWS
renderQuads(bb, model, new MatrixStack(), randomValue);
} else {
MinecraftClient.getInstance().getBlockRenderManager().renderFluid(BlockPos.ORIGIN, new BlockRenderView() {
@@ -291,7 +293,7 @@ public class ModelTextureBakery {
var mesh = bb.endNullable();
if (mesh != null)
BudgetBufferRenderer.draw(mesh, texture, transform);
BudgetBufferRenderer.drawFast(mesh, texture, transform);
}
private static void renderQuads(BufferBuilder builder, BlockStateModel model, MatrixStack stack, long randomValue) {

View File

@@ -0,0 +1,375 @@
package me.cortex.voxy.client.core.model.bakery;
import me.cortex.voxy.client.core.model.BudgetBufferRenderer;
import me.cortex.voxy.common.util.MemoryBuffer;
import net.minecraft.block.BlockState;
import net.minecraft.block.Blocks;
import net.minecraft.block.FluidBlock;
import net.minecraft.block.entity.BlockEntity;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.render.RenderLayer;
import net.minecraft.client.render.RenderLayers;
import net.minecraft.client.render.VertexConsumer;
import net.minecraft.client.render.model.BakedQuad;
import net.minecraft.client.render.model.BlockStateModel;
import net.minecraft.client.util.math.MatrixStack;
import net.minecraft.fluid.FluidState;
import net.minecraft.util.Identifier;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Direction;
import net.minecraft.util.math.RotationAxis;
import net.minecraft.util.math.random.LocalRandom;
import net.minecraft.world.BlockRenderView;
import net.minecraft.world.LightType;
import net.minecraft.world.biome.ColorResolver;
import net.minecraft.world.chunk.light.LightingProvider;
import org.jetbrains.annotations.Nullable;
import org.joml.Matrix4f;
import org.lwjgl.system.MemoryUtil;
import static org.lwjgl.opengl.ARBShaderImageLoadStore.GL_FRAMEBUFFER_BARRIER_BIT;
import static org.lwjgl.opengl.ARBShaderImageLoadStore.glMemoryBarrier;
import static org.lwjgl.opengl.GL11.*;
import static org.lwjgl.opengl.GL11C.GL_DEPTH;
import static org.lwjgl.opengl.GL14C.glBlendFuncSeparate;
import static org.lwjgl.opengl.GL30.*;
import static org.lwjgl.opengl.GL45.glClearNamedFramebufferfv;
public class ModelTextureBakery2 {
//Note: the first bit of metadata is if alpha discard is enabled
private static final Matrix4f[] VIEWS = new Matrix4f[6];
private final GlViewCapture capture;
private final ReuseVertexConsumer vc = new ReuseVertexConsumer();
private static final int FORMAT_STRIDE = 24;
private static final class ReuseVertexConsumer implements VertexConsumer {
private MemoryBuffer buffer = new MemoryBuffer(8192);
private long ptr;
private int count;
public ReuseVertexConsumer() {
this.reset();
}
@Override
public ReuseVertexConsumer vertex(float x, float y, float z) {
this.ensureCanPut();
this.ptr += FORMAT_STRIDE; this.count++; //Goto next vertex
MemoryUtil.memPutFloat(this.ptr, x);
MemoryUtil.memPutFloat(this.ptr + 4, y);
MemoryUtil.memPutFloat(this.ptr + 8, z);
return this;
}
public ReuseVertexConsumer meta(int metadata) {
MemoryUtil.memPutInt(this.ptr + 12, metadata);
return this;
}
@Override
public ReuseVertexConsumer color(int red, int green, int blue, int alpha) {
return this;
}
@Override
public ReuseVertexConsumer texture(float u, float v) {
MemoryUtil.memPutFloat(this.ptr + 16, u);
MemoryUtil.memPutFloat(this.ptr + 20, v);
return this;
}
@Override
public ReuseVertexConsumer overlay(int u, int v) {
return this;
}
@Override
public ReuseVertexConsumer light(int u, int v) {
return this;
}
@Override
public ReuseVertexConsumer normal(float x, float y, float z) {
return this;
}
public ReuseVertexConsumer quad(BakedQuad quad, int metadata) {
int[] data = quad.vertexData();
for (int i = 0; i < 4; i++) {
float x = Float.intBitsToFloat(data[i * 8]);
float y = Float.intBitsToFloat(data[i * 8 + 1]);
float z = Float.intBitsToFloat(data[i * 8 + 2]);
this.vertex(x,y,z);
float u = Float.intBitsToFloat(data[i * 8 + 4]);
float v = Float.intBitsToFloat(data[i * 8 + 5]);
this.texture(u,v);
this.meta(metadata);
}
return this;
}
private void ensureCanPut() {
if ((long) (this.count + 1) * FORMAT_STRIDE < this.buffer.size) {
return;
}
long offset = this.buffer.address-this.ptr;
//1.5x the size
var newBuffer = new MemoryBuffer((((int)(this.buffer.size*1.5)+FORMAT_STRIDE-1)/FORMAT_STRIDE)*FORMAT_STRIDE);
this.buffer.cpyTo(newBuffer.address);
this.buffer.free();
this.buffer = newBuffer;
this.ptr = offset + newBuffer.address;
}
public ReuseVertexConsumer reset() {
this.count = 0;
this.ptr = this.buffer.address - FORMAT_STRIDE;//the thing is first time this gets incremented by FORMAT_STRIDE
return this;
}
public void free() {
this.buffer.free();
this.buffer = null;
}
}
private final int width;
private final int height;
public ModelTextureBakery2(int width, int height) {
this.capture = new GlViewCapture(width, height);
this.width = width;
this.height = height;
}
private void bakeBlockModel(BlockState state, RenderLayer layer) {
var model = MinecraftClient.getInstance()
.getBakedModelManager()
.getBlockModels()
.getModel(state);
boolean hasDiscard = layer == RenderLayer.getCutout() ||
layer == RenderLayer.getCutoutMipped() ||
layer == RenderLayer.getTripwire();
for (Direction direction : new Direction[]{Direction.DOWN, Direction.UP, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST, null}) {
for (var part : model.getParts(new LocalRandom(42L))) {
var quads = part.getQuads(direction);
for (var quad : quads) {
//TODO: add meta specifiying quad has a tint
int meta = hasDiscard?1:0;
this.vc.quad(quad, meta);
}
}
}
}
private void bakeFluidState(BlockState state, int face) {
MinecraftClient.getInstance().getBlockRenderManager().renderFluid(BlockPos.ORIGIN, new BlockRenderView() {
@Override
public float getBrightness(Direction direction, boolean shaded) {
return 0;
}
@Override
public LightingProvider getLightingProvider() {
return null;
}
@Override
public int getLightLevel(LightType type, BlockPos pos) {
return 0;
}
@Override
public int getColor(BlockPos pos, ColorResolver colorResolver) {
return 0;
}
@Nullable
@Override
public BlockEntity getBlockEntity(BlockPos pos) {
return null;
}
@Override
public BlockState getBlockState(BlockPos pos) {
if (shouldReturnAirForFluid(pos, face)) {
return Blocks.AIR.getDefaultState();
}
//Fixme:
// This makes it so that the top face of water is always air, if this is commented out
// the up block will be a liquid state which makes the sides full
// if this is uncommented, that issue is fixed but e.g. stacking water layers ontop of eachother
// doesnt fill the side of the block
//if (pos.getY() == 1) {
// return Blocks.AIR.getDefaultState();
//}
return state;
}
@Override
public FluidState getFluidState(BlockPos pos) {
if (shouldReturnAirForFluid(pos, face)) {
return Blocks.AIR.getDefaultState().getFluidState();
}
return state.getFluidState();
}
@Override
public int getHeight() {
return 0;
}
@Override
public int getBottomY() {
return 0;
}
}, this.vc, state, state.getFluidState());
}
private static boolean shouldReturnAirForFluid(BlockPos pos, int face) {
var fv = Direction.byIndex(face).getVector();
int dot = fv.getX()*pos.getX() + fv.getY()*pos.getY() + fv.getZ()*pos.getZ();
return dot >= 1;
}
public void free() {
this.capture.free();
this.vc.free();
}
public void renderToStream(BlockState state, int streamBuffer, int streamOffset) {
this.capture.clear();
boolean isBlock = true;
RenderLayer layer;
if (state.getBlock() instanceof FluidBlock) {
layer = RenderLayers.getFluidLayer(state.getFluidState());
isBlock = false;
} else {
layer = RenderLayers.getBlockLayer(state);
}
//TODO: support block model entities
//state.hasBlockEntity()
//Setup GL state
int[] viewdat = new int[4];
int blockTextureId;
{
glEnable(GL_STENCIL_TEST);
glEnable(GL_DEPTH_TEST);
glEnable(GL_CULL_FACE);
if (layer == RenderLayer.getTranslucent()) {
glEnable(GL_BLEND);
glBlendFuncSeparate(GL_ONE_MINUS_DST_ALPHA, GL_DST_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
} else {
glDisable(GL_BLEND);
}
glStencilOp(GL_KEEP, GL_KEEP, GL_INCR);
glStencilFunc(GL_ALWAYS, 1, 0xFF);
glStencilMask(0xFF);
glGetIntegerv(GL_VIEWPORT, viewdat);//TODO: faster way todo this, or just use main framebuffer resolution
//Bind the capture framebuffer
glBindFramebuffer(GL_FRAMEBUFFER, this.capture.framebuffer.id);
var tex = MinecraftClient.getInstance().getTextureManager().getTexture(Identifier.of("minecraft", "textures/atlas/blocks.png")).getGlTexture();
blockTextureId = ((net.minecraft.client.texture.GlTexture)tex).getGlId();
}
//TODO: fastpath for blocks
if (isBlock) {
this.vc.reset();
this.bakeBlockModel(state, layer);
if (this.vc.count != 0) {//only render if there... is shit to render
if (this.vc.count % 4 != 0) throw new IllegalStateException();
//Setup for continual emission
BudgetBufferRenderer.setup(this.vc.buffer.address, this.vc.count / 4, blockTextureId);//note: this.vc.buffer.address NOT this.vc.ptr
var mat = new Matrix4f();
for (int i = 0; i < VIEWS.length; i++) {
glViewport((i % 3) * this.width, (i / 3) * this.height, this.width, this.height);
//The projection matrix
mat.set(2, 0, 0, 0,
0, 2, 0, 0,
0, 0, -1f, 0,
-1, -1, 0, 1);
BudgetBufferRenderer.render(mat.mul(VIEWS[i]));
}
}
glBindVertexArray(0);
} else {//Is fluid, slow path :(
if (!(state.getBlock() instanceof FluidBlock)) throw new IllegalStateException();
var mat = new Matrix4f();
for (int i = 0; i < VIEWS.length; i++) {
this.vc.reset();
this.bakeFluidState(state, i);
if (this.vc.count == 0) continue;
BudgetBufferRenderer.setup(this.vc.buffer.address, this.vc.count / 4, blockTextureId);
glViewport((i % 3) * this.width, (i / 3) * this.height, this.width, this.height);
//The projection matrix
mat.set(2, 0, 0, 0,
0, 2, 0, 0,
0, 0, -1f, 0,
-1, -1, 0, 1);
BudgetBufferRenderer.render(mat.mul(VIEWS[i]));
}
glBindVertexArray(0);
}
//"Restore" gl state
glViewport(viewdat[0], viewdat[1], viewdat[2], viewdat[3]);
glDisable(GL_STENCIL_TEST);
glDisable(GL_BLEND);
//Finish and download
glMemoryBarrier(GL_FRAMEBUFFER_BARRIER_BIT);
this.capture.emitToStream(streamBuffer, streamOffset);
glBindFramebuffer(GL_FRAMEBUFFER, this.capture.framebuffer.id);
glClearDepth(1);
glClear(GL_DEPTH_BUFFER_BIT);
}
static {
//TODO: FIXME: need to bake in the correct orientation, HOWEVER some orientations require a flipped winding order!!!!
addView(0, -90,0, 0, false);//Direction.DOWN
addView(1, 90,0, 0, false);//Direction.UP
addView(2, 0,180, 0, true);//Direction.NORTH
addView(3, 0,0, 0, false);//Direction.SOUTH
//TODO: check these arnt the wrong way round
addView(4, 0,90, 270, false);//Direction.EAST
addView(5, 0,270, 270, false);//Direction.WEST
}
private static void addView(int i, float pitch, float yaw, float rotation, boolean flipX) {
var stack = new MatrixStack();
stack.translate(0.5f,0.5f,0.5f);
stack.multiply(RotationAxis.POSITIVE_Z.rotationDegrees(rotation));
stack.multiply(RotationAxis.POSITIVE_X.rotationDegrees(pitch));
stack.multiply(RotationAxis.POSITIVE_Y.rotationDegrees(yaw));
stack.translate(-0.5f,-0.5f,-0.5f);
VIEWS[i] = new Matrix4f(stack.peek().getPositionMatrix());
}
}

View File

@@ -80,7 +80,7 @@ public class RenderService<T extends AbstractSectionRenderer<J, ?>, J extends Vi
this.viewportSelector = new ViewportSelector<>(this.sectionRenderer::createViewport);
this.renderGen = new RenderGenerationService(world, this.modelService, serviceThreadPool,
this.geometryUpdateQueue::push, this.sectionRenderer.getGeometryManager() instanceof IUsesMeshlets,
()->this.geometryUpdateQueue.count()<1000);
()->this.geometryUpdateQueue.count()<7000);
router.setCallbacks(this.renderGen::enqueueTask, section -> {
section.acquire();
@@ -142,7 +142,7 @@ public class RenderService<T extends AbstractSectionRenderer<J, ?>, J extends Vi
//if (this.modelService.getProcessingCount() < 750)
{//Very bad hack to try control things
this.geometryUpdateQueue.consumeNano(1_000_000 - (System.nanoTime() - frameStart));
this.geometryUpdateQueue.consumeNano(1_500_000 - (System.nanoTime() - frameStart));
}
this.nodeCleaner.tick(this.traversal.getNodeBuffer());//Probably do this here??

View File

@@ -206,7 +206,7 @@ public class ServiceThreadPool {
if (service == null) {
Logger.warn("No available jobs, sleeping releasing returning");
try {
Thread.sleep(500);
Thread.sleep((long) (200*Math.random()+5));
} catch (InterruptedException e) {
throw new RuntimeException(e);
}

View File

@@ -173,21 +173,26 @@ public class ActiveSectionTracker {
sec = section;
}
}
WorldSection aa = null;
if (sec != null) {
long stamp2 = this.lruLock.writeLock();
WorldSection a = this.lruSecondaryCache.put(section.key, section);
if (a != null) {
throw new IllegalStateException("duplicate sections in cache is impossible");
}
//If cache is bigger than its ment to be, remove the least recently used and free it
if (this.lruSize < this.lruSecondaryCache.size()) {
aa = this.lruSecondaryCache.removeFirst();
}
this.lruLock.unlockWrite(stamp2);
}
lock.unlockWrite(stamp);
if (sec != null) {
stamp = this.lruLock.writeLock();
WorldSection a = this.lruSecondaryCache.put(section.key, section);
//If cache is bigger than its ment to be, remove the least recently used and free it
if (a == null && this.lruSize < this.lruSecondaryCache.size()) {
a = this.lruSecondaryCache.removeFirst();
}
this.lruLock.unlockWrite(stamp);
if (a != null) {
a._releaseArray();
}
if (aa != null) {
aa._releaseArray();
}
}

View File

@@ -1,13 +1,13 @@
#version 430
layout(location=0) uniform sampler2D tex;
in vec2 texCoord;
in flat uint metadata;
in vec2 texCoord;
out vec4 colour;
void main() {
colour = texture(tex, texCoord)*(metadata&1);
if (colour.a <0.0001f) {
colour = texture(tex, texCoord);
if (colour.a < 0.0001f && ((metadata&1u)!=0)) {
discard;
}
}

View File

@@ -1,16 +1,15 @@
#version 430
layout(location=0) in vec3 pos;
layout(location=1) in vec2 uv;
layout(location=2) in vec4 _metadata;
layout(location=1) in uint _metadata;
layout(location=2) in vec2 uv;
uniform mat4 transform;
layout(location=1) uniform mat4 transform;
out vec2 texCoord;
out flat uint metadata;
void main() {
uvec4 meta = uvec4(_metadata*255);
metadata = (meta.r<<16)|(meta.g<<8)|(meta.b);
metadata = _metadata;
gl_Position = transform * vec4(pos, 1.0);
texCoord = uv;