Merge branch '1.18/dev' into 1.18/shader-sanity

# Conflicts:
#	src/main/java/com/jozufozu/flywheel/backend/instancing/instancing/InstancingEngine.java
This commit is contained in:
Jozufozu 2022-01-09 22:35:42 -08:00
commit 0b84bec4e1
14 changed files with 248 additions and 135 deletions

View file

@ -1,3 +1,13 @@
0.5.1:
Fixes
- Fix crash on resource reload with backend off
- Fix artifacts while using Optifine shaders
- Fix crash when trying to access biomes in a VirtualRenderWorld
Technical/API
- Refactor instance renderer registration to separate client and server logic (thanks Pepper!)
- VirtualRenderWorlds now have a "biomeOffset" field which gets added when looking up biomes
- Added GlStateTracker which hooks into GlStateManager to track changes in buffer, vertex array, and program bindings
0.5.0a: 0.5.0a:
Fixes Fixes
- Address crash experienced by some users while rendering any tile. - Address crash experienced by some users while rendering any tile.

View file

@ -0,0 +1,62 @@
package com.jozufozu.flywheel.backend.gl;
import com.jozufozu.flywheel.backend.gl.buffer.GlBufferType;
import com.mojang.blaze3d.platform.GlStateManager;
/**
* Tracks bound buffers/vbos because GlStateManager doesn't do that for us.
*/
public class GlStateTracker {
private static final int[] buffers = new int[GlBufferType.values().length];
private static int vao;
private static int program;
public static int getBuffer(GlBufferType type) {
return buffers[type.ordinal()];
}
public static int getVertexArray() {
return vao;
}
public static int getProgram() {
return program;
}
public static void _setBuffer(GlBufferType type, int buffer) {
buffers[type.ordinal()] = buffer;
}
public static void _setProgram(int id) {
program = id;
}
public static void _setVertexArray(int id) {
vao = id;
}
public static State getRestoreState() {
return new State(buffers.clone(), vao, program);
}
public static record State(int[] buffers, int vao, int program) {
public void restore() {
GlBufferType[] values = GlBufferType.values();
for (int i = 0; i < values.length; i++) {
if (buffers[i] != GlStateTracker.buffers[i]) {
GlStateManager._glBindBuffer(values[i].glEnum, buffers[i]);
}
}
if (vao != GlStateTracker.vao) {
GlStateManager._glBindVertexArray(vao);
}
if (program != GlStateTracker.program) {
GlStateManager._glUseProgram(program);
}
}
}
}

View file

@ -4,7 +4,6 @@ import org.lwjgl.opengl.GL20;
import com.jozufozu.flywheel.core.layout.BufferLayout; import com.jozufozu.flywheel.core.layout.BufferLayout;
import com.jozufozu.flywheel.core.layout.LayoutItem; import com.jozufozu.flywheel.core.layout.LayoutItem;
import com.jozufozu.flywheel.mixin.BufferUploaderAccessor;
import com.mojang.blaze3d.platform.GlStateManager; import com.mojang.blaze3d.platform.GlStateManager;
public class GlVertexArray extends GlObject { public class GlVertexArray extends GlObject {
@ -14,20 +13,14 @@ public class GlVertexArray extends GlObject {
public static void bind(int vao) { public static void bind(int vao) {
GlStateManager._glBindVertexArray(vao); GlStateManager._glBindVertexArray(vao);
BufferUploaderAccessor.flywheel$setLastVAO(vao);
} }
public void bind() { public void bind() {
bind(handle()); bind(handle());
} }
public static int getBoundVertexArray() {
return BufferUploaderAccessor.flywheel$getLastVAO();
}
public static void unbind() { public static void unbind() {
GlStateManager._glBindVertexArray(0); GlStateManager._glBindVertexArray(0);
BufferUploaderAccessor.flywheel$setLastVAO(0);
} }
public void enableArrays(int count) { public void enableArrays(int count) {

View file

@ -8,7 +8,7 @@ import org.lwjgl.opengl.GL40;
import org.lwjgl.opengl.GL42; import org.lwjgl.opengl.GL42;
import org.lwjgl.opengl.GL43; import org.lwjgl.opengl.GL43;
import com.jozufozu.flywheel.mixin.BufferUploaderAccessor; import com.jozufozu.flywheel.backend.gl.GlStateTracker;
import com.mojang.blaze3d.platform.GlStateManager; import com.mojang.blaze3d.platform.GlStateManager;
public enum GlBufferType { public enum GlBufferType {
@ -33,29 +33,34 @@ public enum GlBufferType {
this.glEnum = glEnum; this.glEnum = glEnum;
} }
public void bind(int buffer) { public static GlBufferType fromTarget(int pTarget) {
GlStateManager._glBindBuffer(glEnum, buffer); return switch (pTarget) {
case GL15C.GL_ARRAY_BUFFER -> ARRAY_BUFFER;
case GL15C.GL_ELEMENT_ARRAY_BUFFER -> ELEMENT_ARRAY_BUFFER;
case GL21.GL_PIXEL_PACK_BUFFER -> PIXEL_PACK_BUFFER;
case GL21.GL_PIXEL_UNPACK_BUFFER -> PIXEL_UNPACK_BUFFER;
case GL30.GL_TRANSFORM_FEEDBACK_BUFFER -> TRANSFORM_FEEDBACK_BUFFER;
case GL31.GL_UNIFORM_BUFFER -> UNIFORM_BUFFER;
case GL31.GL_TEXTURE_BUFFER -> TEXTURE_BUFFER;
case GL31.GL_COPY_READ_BUFFER -> COPY_READ_BUFFER;
case GL31.GL_COPY_WRITE_BUFFER -> COPY_WRITE_BUFFER;
case GL40.GL_DRAW_INDIRECT_BUFFER -> DRAW_INDIRECT_BUFFER;
case GL42.GL_ATOMIC_COUNTER_BUFFER -> ATOMIC_COUNTER_BUFFER;
case GL43.GL_DISPATCH_INDIRECT_BUFFER -> DISPATCH_INDIRECT_BUFFER;
case GL43.GL_SHADER_STORAGE_BUFFER -> SHADER_STORAGE_BUFFER;
default -> throw new IllegalArgumentException("Unknown target: " + pTarget);
};
}
switch (this.glEnum) { public void bind(int buffer) {
case GL15C.GL_ELEMENT_ARRAY_BUFFER -> BufferUploaderAccessor.flywheel$setLastEBO(buffer); GlStateManager._glBindBuffer(glEnum, buffer);
case GL15C.GL_ARRAY_BUFFER -> BufferUploaderAccessor.flywheel$setLastVBO(buffer);
}
} }
public void unbind() { public void unbind() {
GlStateManager._glBindBuffer(glEnum, 0); GlStateManager._glBindBuffer(glEnum, 0);
switch (this.glEnum) {
case GL15C.GL_ELEMENT_ARRAY_BUFFER -> BufferUploaderAccessor.flywheel$setLastEBO(0);
case GL15C.GL_ARRAY_BUFFER -> BufferUploaderAccessor.flywheel$setLastVBO(0);
}
} }
public int getBoundBuffer() { public int getBoundBuffer() {
return switch (this.glEnum) { return GlStateTracker.getBuffer(this);
case GL15C.GL_ELEMENT_ARRAY_BUFFER -> BufferUploaderAccessor.flywheel$getLastEBO();
case GL15C.GL_ARRAY_BUFFER -> BufferUploaderAccessor.flywheel$getLastVBO();
default -> -1;
};
} }
} }

View file

@ -13,7 +13,6 @@ import org.lwjgl.system.MemoryStack;
import com.jozufozu.flywheel.backend.Backend; import com.jozufozu.flywheel.backend.Backend;
import com.jozufozu.flywheel.backend.gl.GlObject; import com.jozufozu.flywheel.backend.gl.GlObject;
import com.jozufozu.flywheel.mixin.ShaderInstanceAccessor;
import com.mojang.blaze3d.shaders.ProgramManager; import com.mojang.blaze3d.shaders.ProgramManager;
import com.mojang.math.Matrix4f; import com.mojang.math.Matrix4f;
@ -31,14 +30,11 @@ public abstract class GlProgram extends GlObject {
} }
public void bind() { public void bind() {
int handle = handle(); ProgramManager.glUseProgram(handle());
ProgramManager.glUseProgram(handle);
ShaderInstanceAccessor.flywheel$setLastProgramId(handle);
} }
public void unbind() { public static void unbind() {
ProgramManager.glUseProgram(0); ProgramManager.glUseProgram(0);
ShaderInstanceAccessor.flywheel$setLastProgramId(0);
} }
/** /**

View file

@ -3,6 +3,7 @@ package com.jozufozu.flywheel.backend.instancing.instancing;
import java.util.EnumMap; import java.util.EnumMap;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.util.function.Supplier;
import java.util.stream.Stream; import java.util.stream.Stream;
import javax.annotation.Nullable; import javax.annotation.Nullable;
@ -23,6 +24,7 @@ import net.minecraft.client.Camera;
import net.minecraft.client.renderer.RenderType; import net.minecraft.client.renderer.RenderType;
import net.minecraft.core.BlockPos; import net.minecraft.core.BlockPos;
import net.minecraft.core.Vec3i; import net.minecraft.core.Vec3i;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.util.Mth; import net.minecraft.util.Mth;
public class InstancingEngine<P extends WorldProgram> implements Engine { public class InstancingEngine<P extends WorldProgram> implements Engine {
@ -73,10 +75,6 @@ public class InstancingEngine<P extends WorldProgram> implements Engine {
*/ */
@Override @Override
public void render(TaskEngine taskEngine, RenderLayerEvent event) { public void render(TaskEngine taskEngine, RenderLayerEvent event) {
int ebo = GlBufferType.ELEMENT_ARRAY_BUFFER.getBoundBuffer();
int vbo = GlBufferType.ARRAY_BUFFER.getBoundBuffer();
int vao = GlVertexArray.getBoundVertexArray();
double camX; double camX;
double camY; double camY;
double camZ; double camZ;
@ -96,10 +94,6 @@ public class InstancingEngine<P extends WorldProgram> implements Engine {
} }
getGroupsToRender(event.getLayer()).forEach(group -> group.render(viewProjection, camX, camY, camZ, event.getLayer())); getGroupsToRender(event.getLayer()).forEach(group -> group.render(viewProjection, camX, camY, camZ, event.getLayer()));
GlBufferType.ELEMENT_ARRAY_BUFFER.bind(ebo);
GlBufferType.ARRAY_BUFFER.bind(vbo);
GlVertexArray.bind(vao);
} }
private Stream<InstancedMaterialGroup<P>> getGroupsToRender(@Nullable RenderLayer layer) { private Stream<InstancedMaterialGroup<P>> getGroupsToRender(@Nullable RenderLayer layer) {

View file

@ -29,7 +29,7 @@ public class WorldProgram extends GlProgram {
super.bind(); super.bind();
registerSamplers(); registerSamplers();
super.unbind(); unbind();
} }
protected void registerSamplers() { protected void registerSamplers() {

View file

@ -17,6 +17,7 @@ import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction; import net.minecraft.core.Direction;
import net.minecraft.core.RegistryAccess; import net.minecraft.core.RegistryAccess;
import net.minecraft.core.SectionPos; import net.minecraft.core.SectionPos;
import net.minecraft.core.Vec3i;
import net.minecraft.sounds.SoundEvent; import net.minecraft.sounds.SoundEvent;
import net.minecraft.sounds.SoundSource; import net.minecraft.sounds.SoundSource;
import net.minecraft.tags.TagContainer; import net.minecraft.tags.TagContainer;
@ -25,6 +26,7 @@ import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.crafting.RecipeManager; import net.minecraft.world.item.crafting.RecipeManager;
import net.minecraft.world.level.Level; import net.minecraft.world.level.Level;
import net.minecraft.world.level.biome.Biome; import net.minecraft.world.level.biome.Biome;
import net.minecraft.world.level.biome.BiomeManager;
import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.Blocks; import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.world.level.block.entity.BlockEntity;
@ -54,21 +56,39 @@ public class VirtualRenderWorld extends Level implements FlywheelWorld {
protected final int height; protected final int height;
protected final int minBuildHeight; protected final int minBuildHeight;
protected final Vec3i biomeOffset;
public VirtualRenderWorld(Level level) { public VirtualRenderWorld(Level level) {
this(level, level.getHeight(), level.getMinBuildHeight()); this(level, Vec3i.ZERO, level.getHeight(), level.getMinBuildHeight());
} }
public VirtualRenderWorld(Level level, int height, int minBuildHeight) { public VirtualRenderWorld(Level level, Vec3i biomeOffset) {
this(level, biomeOffset, level.getHeight(), level.getMinBuildHeight());
}
public VirtualRenderWorld(Level level, Vec3i biomeOffset, int height, int minBuildHeight) {
super((WritableLevelData) level.getLevelData(), level.dimension(), level.dimensionType(), level::getProfiler, super((WritableLevelData) level.getLevelData(), level.dimension(), level.dimensionType(), level::getProfiler,
true, false, 0); true, false, 0);
this.biomeOffset = biomeOffset;
this.level = level; this.level = level;
this.height = height; this.height = nextMultipleOf16(height);
this.minBuildHeight = minBuildHeight; this.minBuildHeight = nextMultipleOf16(minBuildHeight);
this.chunkSource = new VirtualChunkSource(this); this.chunkSource = new VirtualChunkSource(this);
this.lighter = new LevelLightEngine(chunkSource, true, false); this.lighter = new LevelLightEngine(chunkSource, true, false);
} }
/**
* We need to ensure that height and minBuildHeight are multiples of 16.
* Adapted from: https://math.stackexchange.com/questions/291468
*/
public static int nextMultipleOf16(int a) {
if (a < 0) {
return -(((Math.abs(a) - 1) | 15) + 1);
} else {
return ((a - 1) | 15) + 1;
}
}
/** /**
* Run this after you're done using setBlock(). * Run this after you're done using setBlock().
*/ */
@ -165,6 +185,27 @@ public class VirtualRenderWorld extends Level implements FlywheelWorld {
return getBlockState(scratch.set(x, y, z)); return getBlockState(scratch.set(x, y, z));
} }
// BIOME OFFSET
@Override
public Biome getBiome(BlockPos pPos) {
return super.getBiome(pPos.offset(biomeOffset));
}
@Override
public Biome getUncachedNoiseBiome(int pX, int pY, int pZ) {
// Control flow should never reach this method,
// so we add biomeOffset in case some other mod calls this directly.
return level.getUncachedNoiseBiome(pX + biomeOffset.getX(), pY + biomeOffset.getY(), pZ + biomeOffset.getZ());
}
@Override
public Biome getNoiseBiome(int pX, int pY, int pZ) {
// Control flow should never reach this method,
// so we add biomeOffset in case some other mod calls this directly.
return level.getNoiseBiome(pX + biomeOffset.getX(), pY + biomeOffset.getY(), pZ + biomeOffset.getZ());
}
// RENDERING CONSTANTS // RENDERING CONSTANTS
@Override @Override
@ -179,6 +220,11 @@ public class VirtualRenderWorld extends Level implements FlywheelWorld {
// THIN WRAPPERS AHEAD // THIN WRAPPERS AHEAD
@Override
public BiomeManager getBiomeManager() {
return level.getBiomeManager();
}
@Override @Override
public RegistryAccess registryAccess() { public RegistryAccess registryAccess() {
return level.registryAccess(); return level.registryAccess();
@ -214,11 +260,6 @@ public class VirtualRenderWorld extends Level implements FlywheelWorld {
return level.getScoreboard(); return level.getScoreboard();
} }
@Override
public Biome getUncachedNoiseBiome(int p_225604_1_, int p_225604_2_, int p_225604_3_) {
return level.getUncachedNoiseBiome(p_225604_1_, p_225604_2_, p_225604_3_);
}
// UNIMPORTANT CONSTANTS // UNIMPORTANT CONSTANTS
@Override @Override

View file

@ -1,39 +0,0 @@
package com.jozufozu.flywheel.mixin;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.gen.Accessor;
import com.mojang.blaze3d.vertex.BufferUploader;
@Mixin(BufferUploader.class)
public interface BufferUploaderAccessor {
@Accessor("lastVertexArrayObject")
static void flywheel$setLastVAO(int id) {
throw new AssertionError();
}
@Accessor("lastVertexBufferObject")
static void flywheel$setLastVBO(int id) {
throw new AssertionError();
}
@Accessor("lastIndexBufferObject")
static void flywheel$setLastEBO(int id) {
throw new AssertionError();
}
@Accessor("lastIndexBufferObject")
static int flywheel$getLastEBO() {
throw new AssertionError();
}
@Accessor("lastVertexBufferObject")
static int flywheel$getLastVBO() {
throw new AssertionError();
}
@Accessor("lastVertexArrayObject")
static int flywheel$getLastVAO() {
throw new AssertionError();
}
}

View file

@ -0,0 +1,29 @@
package com.jozufozu.flywheel.mixin;
import javax.annotation.Nullable;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import com.jozufozu.flywheel.backend.gl.GlStateTracker;
import com.mojang.blaze3d.vertex.BufferUploader;
import com.mojang.blaze3d.vertex.VertexFormat;
@Mixin(BufferUploader.class)
public class BufferUploaderMixin {
@Shadow
@Nullable
private static VertexFormat lastFormat;
@Inject(method = "reset", at = @At("HEAD"))
private static void stopBufferUploaderFromClearingBufferStateIfNothingIsBound(CallbackInfo ci) {
// Trust our tracker over BufferUploader's.
if (GlStateTracker.getVertexArray() == 0) {
lastFormat = null;
}
}
}

View file

@ -0,0 +1,29 @@
package com.jozufozu.flywheel.mixin;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import com.jozufozu.flywheel.backend.gl.GlStateTracker;
import com.jozufozu.flywheel.backend.gl.buffer.GlBufferType;
import com.mojang.blaze3d.platform.GlStateManager;
@Mixin(GlStateManager.class)
public class GlStateManagerMixin {
@Inject(method = "_glBindBuffer", at = @At("TAIL"))
private static void onBindBuffer(int pTarget, int pBuffer, CallbackInfo ci) {
GlStateTracker._setBuffer(GlBufferType.fromTarget(pTarget), pBuffer);
}
@Inject(method = "_glBindVertexArray", at = @At("TAIL"))
private static void onBindVertexArray(int pArray, CallbackInfo ci) {
GlStateTracker._setVertexArray(pArray);
}
@Inject(method = "_glUseProgram", at = @At("TAIL"))
private static void onUseProgram(int pProgram, CallbackInfo ci) {
GlStateTracker._setProgram(pProgram);
}
}

View file

@ -8,6 +8,7 @@ import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import com.jozufozu.flywheel.backend.Backend; import com.jozufozu.flywheel.backend.Backend;
import com.jozufozu.flywheel.backend.gl.GlStateTracker;
import com.jozufozu.flywheel.backend.instancing.InstancedRenderDispatcher; import com.jozufozu.flywheel.backend.instancing.InstancedRenderDispatcher;
import com.jozufozu.flywheel.core.crumbling.CrumblingRenderer; import com.jozufozu.flywheel.core.crumbling.CrumblingRenderer;
import com.jozufozu.flywheel.event.BeginFrameEvent; import com.jozufozu.flywheel.event.BeginFrameEvent;
@ -57,7 +58,11 @@ public class LevelRendererMixin {
RenderBuffers renderBuffers = this.renderBuffers; RenderBuffers renderBuffers = this.renderBuffers;
GlStateTracker.State restoreState = GlStateTracker.getRestoreState();
MinecraftForge.EVENT_BUS.post(new RenderLayerEvent(level, type, stack, renderBuffers, camX, camY, camZ)); MinecraftForge.EVENT_BUS.post(new RenderLayerEvent(level, type, stack, renderBuffers, camX, camY, camZ));
restoreState.restore();
} }
@Inject(at = @At("TAIL"), method = "allChanged") @Inject(at = @At("TAIL"), method = "allChanged")
@ -76,7 +81,9 @@ public class LevelRendererMixin {
Vec3 cameraPos = info.getPosition(); Vec3 cameraPos = info.getPosition();
GlStateTracker.State restoreState = GlStateTracker.getRestoreState();
CrumblingRenderer.renderBreaking(new RenderLayerEvent(level, null, stack, null, cameraPos.x, cameraPos.y, cameraPos.z)); CrumblingRenderer.renderBreaking(new RenderLayerEvent(level, null, stack, null, cameraPos.x, cameraPos.y, cameraPos.z));
restoreState.restore();
} }
// Instancing // Instancing

View file

@ -1,14 +0,0 @@
package com.jozufozu.flywheel.mixin;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.gen.Accessor;
import net.minecraft.client.renderer.ShaderInstance;
@Mixin(ShaderInstance.class)
public interface ShaderInstanceAccessor {
@Accessor("lastProgramId")
static void flywheel$setLastProgramId(int id) {
throw new AssertionError();
}
}

View file

@ -1,37 +1,37 @@
{ {
"required": true, "required": true,
"minVersion": "0.8", "minVersion": "0.8",
"package": "com.jozufozu.flywheel.mixin", "package": "com.jozufozu.flywheel.mixin",
"compatibilityLevel": "JAVA_17", "compatibilityLevel": "JAVA_17",
"refmap": "flywheel.refmap.json", "refmap": "flywheel.refmap.json",
"client": [ "client": [
"BlockEntityTypeMixin", "BlockEntityTypeMixin",
"BufferBuilderMixin", "BufferBuilderMixin",
"BufferUploaderAccessor", "BufferUploaderMixin",
"CameraMixin", "CameraMixin",
"CancelEntityRenderMixin", "CancelEntityRenderMixin",
"ChunkRebuildHooksMixin", "ChunkRebuildHooksMixin",
"EntityTypeMixin", "EntityTypeMixin",
"FixFabulousDepthMixin", "FixFabulousDepthMixin",
"FrustumMixin", "FrustumMixin",
"InstanceAddMixin", "GlStateManagerMixin",
"InstanceRemoveMixin", "InstanceAddMixin",
"LevelRendererAccessor", "InstanceRemoveMixin",
"LevelRendererMixin", "LevelRendererAccessor",
"PausedPartialTickAccessor", "LevelRendererMixin",
"RenderTexturesMixin", "PausedPartialTickAccessor",
"RenderTypeMixin", "RenderTexturesMixin",
"ShaderCloseMixin", "RenderTypeMixin",
"ShaderInstanceAccessor", "ShaderCloseMixin",
"atlas.AtlasDataMixin", "atlas.AtlasDataMixin",
"atlas.SheetDataAccessor", "atlas.SheetDataAccessor",
"light.LightUpdateMixin", "light.LightUpdateMixin",
"light.NetworkLightUpdateMixin", "light.NetworkLightUpdateMixin",
"matrix.Matrix3fMixin", "matrix.Matrix3fMixin",
"matrix.Matrix4fMixin", "matrix.Matrix4fMixin",
"matrix.PoseStackMixin" "matrix.PoseStackMixin"
], ],
"injectors": { "injectors": {
"defaultRequire": 1 "defaultRequire": 1
} }
} }