mirror of
https://github.com/Jozufozu/Flywheel.git
synced 2025-02-07 02:34:58 +01:00
Overwritten overwrites
- Add mixinextras and remove overwrite - Merge AsyncFontTexture and FontTexture Signed-off-by: Jozufozu <jozsefaug@gmail.com>
This commit is contained in:
parent
71cf582e97
commit
c1bf31856f
11 changed files with 185 additions and 370 deletions
|
@ -72,6 +72,8 @@ jarSets {
|
|||
dependencies {
|
||||
modCompileOnly("net.fabricmc:fabric-loader:${property("fabric_loader_version")}")
|
||||
|
||||
compileOnly(annotationProcessor("io.github.llamalad7:mixinextras-common:0.4.1")!!)
|
||||
|
||||
testImplementation("org.junit.jupiter:junit-jupiter:5.8.1")
|
||||
}
|
||||
|
||||
|
|
|
@ -1,159 +0,0 @@
|
|||
package dev.engine_room.flywheel.backend.font;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.commons.compress.utils.Lists;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import com.mojang.blaze3d.font.SheetGlyphInfo;
|
||||
import com.mojang.blaze3d.platform.NativeImage;
|
||||
import com.mojang.blaze3d.platform.TextureUtil;
|
||||
import com.mojang.blaze3d.systems.RenderSystem;
|
||||
|
||||
import dev.engine_room.flywheel.lib.internal.GlyphExtension;
|
||||
import net.fabricmc.api.EnvType;
|
||||
import net.fabricmc.api.Environment;
|
||||
import net.minecraft.client.gui.font.GlyphRenderTypes;
|
||||
import net.minecraft.client.gui.font.glyphs.BakedGlyph;
|
||||
import net.minecraft.client.renderer.texture.AbstractTexture;
|
||||
import net.minecraft.client.renderer.texture.Dumpable;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.server.packs.resources.ResourceManager;
|
||||
|
||||
public class AsyncFontTexture extends AbstractTexture implements Dumpable {
|
||||
private static final int SIZE = 256;
|
||||
private final GlyphRenderTypes renderTypes;
|
||||
private final ResourceLocation name;
|
||||
private final boolean colored;
|
||||
private final Node root;
|
||||
|
||||
private final List<Upload> uploads = Lists.newArrayList();
|
||||
|
||||
private boolean flushScheduled = false;
|
||||
|
||||
public AsyncFontTexture(ResourceLocation name, GlyphRenderTypes renderTypes, boolean colored) {
|
||||
this.name = name;
|
||||
this.colored = colored;
|
||||
this.root = new Node(0, 0, 256, 256);
|
||||
this.renderTypes = renderTypes;
|
||||
|
||||
if (RenderSystem.isOnRenderThreadOrInit()) {
|
||||
this.init();
|
||||
} else {
|
||||
RenderSystem.recordRenderCall(this::init);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void load(ResourceManager resourceManager) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
this.releaseId();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public BakedGlyph add(SheetGlyphInfo glyphInfo) {
|
||||
if (glyphInfo.isColored() != this.colored) {
|
||||
return null;
|
||||
}
|
||||
Node node = this.root.insert(glyphInfo);
|
||||
if (node != null) {
|
||||
if (RenderSystem.isOnRenderThreadOrInit()) {
|
||||
this.bind();
|
||||
glyphInfo.upload(node.x, node.y);
|
||||
} else {
|
||||
uploads.add(new Upload(glyphInfo, node.x, node.y));
|
||||
|
||||
if (!flushScheduled) {
|
||||
RenderSystem.recordRenderCall(this::flush);
|
||||
flushScheduled = true;
|
||||
}
|
||||
}
|
||||
var out = new BakedGlyph(this.renderTypes, ((float) node.x + 0.01f) / 256.0f, ((float) node.x - 0.01f + (float) glyphInfo.getPixelWidth()) / 256.0f, ((float) node.y + 0.01f) / 256.0f, ((float) node.y - 0.01f + (float) glyphInfo.getPixelHeight()) / 256.0f, glyphInfo.getLeft(), glyphInfo.getRight(), glyphInfo.getUp(), glyphInfo.getDown());
|
||||
|
||||
((GlyphExtension) out).flywheel$texture(name);
|
||||
|
||||
return out;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dumpContents(ResourceLocation resourceLocation, Path path) {
|
||||
String string = resourceLocation.toDebugFileName();
|
||||
TextureUtil.writeAsPNG(path, string, this.getId(), 0, 256, 256, i -> (i & 0xFF000000) == 0 ? -16777216 : i);
|
||||
}
|
||||
|
||||
public void init() {
|
||||
TextureUtil.prepareImage(colored ? NativeImage.InternalGlFormat.RGBA : NativeImage.InternalGlFormat.RED, this.getId(), 256, 256);
|
||||
}
|
||||
|
||||
public void flush() {
|
||||
this.bind();
|
||||
for (Upload upload : this.uploads) {
|
||||
upload.info.upload(upload.x, upload.y);
|
||||
}
|
||||
|
||||
uploads.clear();
|
||||
|
||||
flushScheduled = false;
|
||||
}
|
||||
|
||||
public record Upload(SheetGlyphInfo info, int x, int y) {
|
||||
}
|
||||
|
||||
@Environment(value = EnvType.CLIENT)
|
||||
static class Node {
|
||||
final int x;
|
||||
final int y;
|
||||
private final int width;
|
||||
private final int height;
|
||||
@Nullable
|
||||
private Node left;
|
||||
@Nullable
|
||||
private Node right;
|
||||
private boolean occupied;
|
||||
|
||||
Node(int x, int y, int width, int height) {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
}
|
||||
|
||||
@Nullable Node insert(SheetGlyphInfo glyphInfo) {
|
||||
if (this.left != null && this.right != null) {
|
||||
Node node = this.left.insert(glyphInfo);
|
||||
if (node == null) {
|
||||
node = this.right.insert(glyphInfo);
|
||||
}
|
||||
return node;
|
||||
}
|
||||
if (this.occupied) {
|
||||
return null;
|
||||
}
|
||||
int i = glyphInfo.getPixelWidth();
|
||||
int j = glyphInfo.getPixelHeight();
|
||||
if (i > this.width || j > this.height) {
|
||||
return null;
|
||||
}
|
||||
if (i == this.width && j == this.height) {
|
||||
this.occupied = true;
|
||||
return this;
|
||||
}
|
||||
int k = this.width - i;
|
||||
int l = this.height - j;
|
||||
if (k > l) {
|
||||
this.left = new Node(this.x, this.y, i, this.height);
|
||||
this.right = new Node(this.x + i + 1, this.y, this.width - i - 1, this.height);
|
||||
} else {
|
||||
this.left = new Node(this.x, this.y, this.width, j);
|
||||
this.right = new Node(this.x, this.y + j + 1, this.width, this.height - j - 1);
|
||||
}
|
||||
return this.left.insert(glyphInfo);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,146 +1,59 @@
|
|||
package dev.engine_room.flywheel.backend.mixin;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.function.IntFunction;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.spongepowered.asm.mixin.Final;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Overwrite;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
import org.spongepowered.asm.mixin.Unique;
|
||||
|
||||
import com.llamalad7.mixinextras.injector.wrapmethod.WrapMethod;
|
||||
import com.llamalad7.mixinextras.injector.wrapoperation.Operation;
|
||||
|
||||
import net.minecraft.client.gui.font.CodepointMap;
|
||||
|
||||
@Mixin(CodepointMap.class)
|
||||
public class CodePointMapMixin<T> {
|
||||
@Shadow
|
||||
@Final
|
||||
private T[][] blockMap;
|
||||
|
||||
@Shadow
|
||||
@Final
|
||||
private T[] empty;
|
||||
|
||||
@Shadow
|
||||
@Final
|
||||
private IntFunction<T[]> blockConstructor;
|
||||
|
||||
@Unique
|
||||
private final Object flywheel$lock = new Object();
|
||||
|
||||
/**
|
||||
* @author
|
||||
* @reason
|
||||
*/
|
||||
@Overwrite
|
||||
public void clear() {
|
||||
@WrapMethod(method = "clear")
|
||||
private void flywheel$wrapClearAsSynchronized(Operation<Void> original) {
|
||||
synchronized (flywheel$lock) {
|
||||
Arrays.fill(this.blockMap, this.empty);
|
||||
original.call();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @author
|
||||
* @reason
|
||||
*/
|
||||
@Nullable
|
||||
@Overwrite
|
||||
public T get(int index) {
|
||||
int i = index >> 8;
|
||||
int j = index & 0xFF;
|
||||
@WrapMethod(method = "get")
|
||||
private T flywheel$wrapGetAsSynchronized(int index, Operation<T> original) {
|
||||
synchronized (flywheel$lock) {
|
||||
return this.blockMap[i][j];
|
||||
return original.call(index);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @author
|
||||
* @reason
|
||||
*/
|
||||
@Nullable
|
||||
@Overwrite
|
||||
public T put(int index, T value) {
|
||||
int i = index >> 8;
|
||||
int j = index & 0xFF;
|
||||
T object;
|
||||
@WrapMethod(method = "put")
|
||||
private T flywheel$wrapPutAsSynchronized(int index, T value, Operation<T> original) {
|
||||
synchronized (flywheel$lock) {
|
||||
T[] objects = this.blockMap[i];
|
||||
if (objects == this.empty) {
|
||||
objects = this.blockConstructor.apply(256);
|
||||
this.blockMap[i] = objects;
|
||||
objects[j] = value;
|
||||
return null;
|
||||
}
|
||||
object = objects[j];
|
||||
objects[j] = value;
|
||||
return original.call(index, value);
|
||||
}
|
||||
return object;
|
||||
}
|
||||
|
||||
/**
|
||||
* @author
|
||||
* @reason
|
||||
*/
|
||||
@Overwrite
|
||||
public T computeIfAbsent(int index, IntFunction<T> valueIfAbsentGetter) {
|
||||
int i = index >> 8;
|
||||
int j = index & 0xFF;
|
||||
T out;
|
||||
@WrapMethod(method = "computeIfAbsent")
|
||||
private T flywheel$wrapComputeIfAbsentAsSynchronized(int index, IntFunction<T> valueIfAbsentGetter, Operation<T> original) {
|
||||
synchronized (flywheel$lock) {
|
||||
T[] objects = this.blockMap[i];
|
||||
T object = objects[j];
|
||||
if (object != null) {
|
||||
return object;
|
||||
}
|
||||
if (objects == this.empty) {
|
||||
objects = this.blockConstructor.apply(256);
|
||||
this.blockMap[i] = objects;
|
||||
}
|
||||
out = valueIfAbsentGetter.apply(index);
|
||||
objects[j] = out;
|
||||
return original.call(index, valueIfAbsentGetter);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
/**
|
||||
* @author
|
||||
* @reason
|
||||
*/
|
||||
@Nullable
|
||||
@Overwrite
|
||||
public T remove(int index) {
|
||||
int i = index >> 8;
|
||||
int j = index & 0xFF;
|
||||
T object;
|
||||
@WrapMethod(method = "remove")
|
||||
private T flywheel$wrapRemoveAsSynchronized(int index, Operation<T> original) {
|
||||
synchronized (flywheel$lock) {
|
||||
T[] objects = this.blockMap[i];
|
||||
if (objects == this.empty) {
|
||||
return null;
|
||||
}
|
||||
object = objects[j];
|
||||
objects[j] = null;
|
||||
return original.call(index);
|
||||
}
|
||||
return object;
|
||||
}
|
||||
|
||||
/**
|
||||
* @author
|
||||
* @reason
|
||||
*/
|
||||
@Overwrite
|
||||
public void forEach(CodepointMap.Output<T> output) {
|
||||
@WrapMethod(method = "forEach")
|
||||
private void flywheel$wrapForEachAsSynchronized(CodepointMap.Output<T> output, Operation<Void> original) {
|
||||
synchronized (flywheel$lock) {
|
||||
for (int i = 0; i < this.blockMap.length; ++i) {
|
||||
T[] objects = this.blockMap[i];
|
||||
if (objects == this.empty) continue;
|
||||
for (int j = 0; j < objects.length; ++j) {
|
||||
T object = objects[j];
|
||||
if (object == null) continue;
|
||||
int k = i << 8 | j;
|
||||
output.accept(k, object);
|
||||
}
|
||||
}
|
||||
original.call(output);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,121 +1,31 @@
|
|||
package dev.engine_room.flywheel.backend.mixin;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.spongepowered.asm.mixin.Final;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Overwrite;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
import org.spongepowered.asm.mixin.Unique;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import com.mojang.blaze3d.font.GlyphInfo;
|
||||
import com.mojang.blaze3d.font.GlyphProvider;
|
||||
import com.mojang.blaze3d.font.SheetGlyphInfo;
|
||||
import com.llamalad7.mixinextras.injector.ModifyExpressionValue;
|
||||
import com.llamalad7.mixinextras.sugar.Local;
|
||||
|
||||
import dev.engine_room.flywheel.backend.font.AsyncFontTexture;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
||||
import it.unimi.dsi.fastutil.ints.IntList;
|
||||
import dev.engine_room.flywheel.lib.internal.FontTextureExtension;
|
||||
import net.minecraft.client.gui.font.FontSet;
|
||||
import net.minecraft.client.gui.font.GlyphRenderTypes;
|
||||
import net.minecraft.client.gui.font.glyphs.BakedGlyph;
|
||||
import net.minecraft.client.renderer.texture.TextureManager;
|
||||
import net.minecraft.client.gui.font.FontTexture;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.util.Mth;
|
||||
import net.minecraft.util.RandomSource;
|
||||
|
||||
@Mixin(FontSet.class)
|
||||
public abstract class FontSetMixin {
|
||||
// Replace serial random with thread-local random
|
||||
@Shadow
|
||||
@Final
|
||||
private TextureManager textureManager;
|
||||
private static RandomSource RANDOM = RandomSource.createNewThreadLocalInstance();
|
||||
|
||||
@Shadow
|
||||
private BakedGlyph missingGlyph;
|
||||
|
||||
@Shadow
|
||||
@Final
|
||||
private ResourceLocation name;
|
||||
|
||||
@Shadow
|
||||
@Final
|
||||
private Int2ObjectMap<IntList> glyphsByWidth;
|
||||
|
||||
@Shadow
|
||||
public abstract BakedGlyph getGlyph(int character);
|
||||
|
||||
@Unique
|
||||
private static final RandomSource RANDOM = RandomSource.createNewThreadLocalInstance();
|
||||
|
||||
@Unique
|
||||
private List<AsyncFontTexture> flywheel$textures;
|
||||
|
||||
|
||||
@Inject(method = "<init>", at = @At("TAIL"))
|
||||
public void init(TextureManager textureManager, ResourceLocation name, CallbackInfo ci) {
|
||||
flywheel$textures = Lists.newArrayList();
|
||||
@ModifyExpressionValue(method = "stitch", at = @At(value = "NEW", target = "net/minecraft/client/gui/font/FontTexture"))
|
||||
private FontTexture flywheel$setNameAfterCreate(FontTexture original, @Local ResourceLocation name) {
|
||||
// Forward the name to the FontTexture so we can forward the name to the BakedGlyphs it creates.
|
||||
// We need to know that to determine which Material to use when actually setting up instances.
|
||||
((FontTextureExtension) original).flywheel$setName(name);
|
||||
return original;
|
||||
}
|
||||
|
||||
@Inject(method = "reload", at = @At("TAIL"))
|
||||
public void reload(List<GlyphProvider> glyphProviders, CallbackInfo ci) {
|
||||
flywheel$closeTextures();
|
||||
}
|
||||
|
||||
/**
|
||||
* @author Jozufozu
|
||||
* @reason Use thread safe random
|
||||
*/
|
||||
@Overwrite
|
||||
public BakedGlyph getRandomGlyph(GlyphInfo glyph) {
|
||||
IntList intList = this.glyphsByWidth.get(Mth.ceil(glyph.getAdvance(false)));
|
||||
if (intList != null && !intList.isEmpty()) {
|
||||
// Override to use thread safe random
|
||||
// FIXME: can we just replace the static field instead?
|
||||
return this.getGlyph(intList.getInt(RANDOM.nextInt(intList.size())));
|
||||
}
|
||||
return this.missingGlyph;
|
||||
}
|
||||
|
||||
/**
|
||||
* @author Jozufozu
|
||||
* @reason Use our stitching
|
||||
*/
|
||||
@Overwrite
|
||||
private BakedGlyph stitch(SheetGlyphInfo glyphInfo) {
|
||||
for (AsyncFontTexture fontTexture : flywheel$textures) {
|
||||
BakedGlyph bakedGlyph = fontTexture.add(glyphInfo);
|
||||
if (bakedGlyph == null) continue;
|
||||
|
||||
return bakedGlyph;
|
||||
}
|
||||
ResourceLocation resourceLocation = this.name.withSuffix("/" + flywheel$textures.size());
|
||||
boolean bl = glyphInfo.isColored();
|
||||
GlyphRenderTypes glyphRenderTypes = bl ? GlyphRenderTypes.createForColorTexture(resourceLocation) : GlyphRenderTypes.createForIntensityTexture(resourceLocation);
|
||||
|
||||
AsyncFontTexture fontTexture2 = new AsyncFontTexture(resourceLocation, glyphRenderTypes, bl);
|
||||
flywheel$textures.add(fontTexture2);
|
||||
BakedGlyph bakedGlyph2 = fontTexture2.add(glyphInfo);
|
||||
|
||||
this.textureManager.register(resourceLocation, fontTexture2);
|
||||
|
||||
return bakedGlyph2 == null ? this.missingGlyph : bakedGlyph2;
|
||||
}
|
||||
|
||||
@Inject(method = "close", at = @At("TAIL"))
|
||||
private void flywheel$close(CallbackInfo ci) {
|
||||
flywheel$closeTextures();
|
||||
}
|
||||
|
||||
@Unique
|
||||
private void flywheel$closeTextures() {
|
||||
for (AsyncFontTexture texture : flywheel$textures) {
|
||||
texture.close();
|
||||
}
|
||||
|
||||
flywheel$textures.clear();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,13 @@
|
|||
package dev.engine_room.flywheel.backend.mixin;
|
||||
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.gen.Accessor;
|
||||
|
||||
@Mixin(targets = "net.minecraft.client.gui.font.FontTexture$Node")
|
||||
public interface FontTexture$NodeAccessor {
|
||||
@Accessor("x")
|
||||
int flywheel$getX();
|
||||
|
||||
@Accessor("y")
|
||||
int flywheel$getY();
|
||||
}
|
|
@ -0,0 +1,114 @@
|
|||
package dev.engine_room.flywheel.backend.mixin;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Unique;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Coerce;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||
|
||||
import com.llamalad7.mixinextras.injector.ModifyExpressionValue;
|
||||
import com.llamalad7.mixinextras.injector.v2.WrapWithCondition;
|
||||
import com.llamalad7.mixinextras.injector.wrapoperation.Operation;
|
||||
import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation;
|
||||
import com.llamalad7.mixinextras.sugar.Share;
|
||||
import com.llamalad7.mixinextras.sugar.ref.LocalRef;
|
||||
import com.mojang.blaze3d.font.SheetGlyphInfo;
|
||||
import com.mojang.blaze3d.platform.NativeImage;
|
||||
import com.mojang.blaze3d.systems.RenderSystem;
|
||||
|
||||
import dev.engine_room.flywheel.backend.util.FontTextureUpload;
|
||||
import dev.engine_room.flywheel.lib.internal.FontTextureExtension;
|
||||
import dev.engine_room.flywheel.lib.internal.GlyphExtension;
|
||||
import net.minecraft.client.gui.font.FontTexture;
|
||||
import net.minecraft.client.gui.font.glyphs.BakedGlyph;
|
||||
import net.minecraft.client.renderer.texture.AbstractTexture;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
|
||||
@Mixin(FontTexture.class)
|
||||
public abstract class FontTextureMixin extends AbstractTexture implements FontTextureExtension {
|
||||
@Unique
|
||||
private final List<FontTextureUpload> flywheel$uploads = new ArrayList<>();
|
||||
@Unique
|
||||
private boolean flywheel$flushScheduled = false;
|
||||
|
||||
@Unique
|
||||
private ResourceLocation flywheel$name;
|
||||
|
||||
@WrapOperation(method = "<init>", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/font/FontTexture;getId()I"))
|
||||
private int flywheel$skipGetId(FontTexture instance, Operation<Integer> original) {
|
||||
// getId lazily creates the texture id, which is good,
|
||||
// but it doesn't check for the render thread, which explodes.
|
||||
if (RenderSystem.isOnRenderThreadOrInit()) {
|
||||
return original.call(instance);
|
||||
}
|
||||
// We'll call getId manually in the recorded render call below.
|
||||
return 0;
|
||||
}
|
||||
|
||||
@WrapOperation(method = "<init>", at = @At(value = "INVOKE", target = "Lcom/mojang/blaze3d/platform/TextureUtil;prepareImage(Lcom/mojang/blaze3d/platform/NativeImage$InternalGlFormat;III)V"))
|
||||
private void flywheel$skipPrepareImage(NativeImage.InternalGlFormat arg, int i, int j, int k, Operation<Void> original) {
|
||||
if (RenderSystem.isOnRenderThreadOrInit()) {
|
||||
original.call(arg, i, j, k);
|
||||
} else {
|
||||
RenderSystem.recordRenderCall(() -> original.call(arg, getId(), j, k));
|
||||
}
|
||||
}
|
||||
|
||||
@WrapWithCondition(method = "add", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/font/FontTexture;bind()V"))
|
||||
private boolean flywheel$onlyOnRenderThreadOrInitBindAndUpload(FontTexture instance) {
|
||||
return RenderSystem.isOnRenderThreadOrInit();
|
||||
}
|
||||
|
||||
@WrapWithCondition(method = "add", at = @At(value = "INVOKE", target = "Lcom/mojang/blaze3d/font/SheetGlyphInfo;upload(II)V"))
|
||||
private boolean flywheel$onlyOnRenderThreadOrInitBindAndUpload2(SheetGlyphInfo instance, int x, int y) {
|
||||
return RenderSystem.isOnRenderThreadOrInit();
|
||||
}
|
||||
|
||||
@WrapOperation(method = "add", at = @At(value = "FIELD", target = "Lnet/minecraft/client/gui/font/FontTexture$Node;x:I", ordinal = 0))
|
||||
private int flywheel$shareNode(@Coerce Object instance, Operation<Integer> original, @Share("node") LocalRef<Object> node) {
|
||||
node.set(instance);
|
||||
return original.call(instance);
|
||||
}
|
||||
|
||||
@Inject(method = "add", at = @At(value = "INVOKE", target = "Lcom/mojang/blaze3d/font/SheetGlyphInfo;upload(II)V", shift = At.Shift.AFTER))
|
||||
private void flywheel$uploadOrFlush(SheetGlyphInfo glyphInfo, CallbackInfoReturnable<BakedGlyph> cir, @Share("node") LocalRef<Object> node) {
|
||||
FontTexture$NodeAccessor accessor = ((FontTexture$NodeAccessor) node.get());
|
||||
|
||||
// Shove all the uploads into a list to be processed as a batch.
|
||||
// Saves a lot of lambda allocations that would be spent binding the same texture over and over.
|
||||
flywheel$uploads.add(new FontTextureUpload(glyphInfo, accessor.flywheel$getX(), accessor.flywheel$getY()));
|
||||
|
||||
if (!flywheel$flushScheduled) {
|
||||
RenderSystem.recordRenderCall(this::flywheel$flush);
|
||||
flywheel$flushScheduled = true;
|
||||
}
|
||||
}
|
||||
|
||||
@ModifyExpressionValue(method = "add", at = @At(value = "NEW", target = "net/minecraft/client/gui/font/glyphs/BakedGlyph"))
|
||||
private BakedGlyph flywheel$setGlyphExtensionName(BakedGlyph original) {
|
||||
((GlyphExtension) original).flywheel$texture(flywheel$name);
|
||||
return original;
|
||||
}
|
||||
|
||||
@Unique
|
||||
public void flywheel$flush() {
|
||||
this.bind();
|
||||
for (FontTextureUpload upload : flywheel$uploads) {
|
||||
upload.info()
|
||||
.upload(upload.x(), upload.y());
|
||||
}
|
||||
|
||||
flywheel$uploads.clear();
|
||||
|
||||
flywheel$flushScheduled = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void flywheel$setName(ResourceLocation value) {
|
||||
flywheel$name = value;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
package dev.engine_room.flywheel.backend.util;
|
||||
|
||||
import com.mojang.blaze3d.font.SheetGlyphInfo;
|
||||
|
||||
/**
|
||||
* For use in {@link dev.engine_room.flywheel.backend.mixin.FontTextureMixin}
|
||||
* to batch glyph uploads when they're created in a flywheel worker thread.
|
||||
*/
|
||||
public record FontTextureUpload(SheetGlyphInfo info, int x, int y) {
|
||||
}
|
|
@ -8,6 +8,8 @@
|
|||
"AbstractClientPlayerAccessor",
|
||||
"CodePointMapMixin",
|
||||
"FontSetMixin",
|
||||
"FontTexture$NodeAccessor",
|
||||
"FontTextureMixin",
|
||||
"GlStateManagerMixin",
|
||||
"LevelRendererAccessor",
|
||||
"OptionsMixin",
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
package dev.engine_room.flywheel.lib.internal;
|
||||
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
|
||||
public interface FontTextureExtension {
|
||||
void flywheel$setName(ResourceLocation value);
|
||||
}
|
|
@ -87,6 +87,9 @@ dependencies {
|
|||
modCompileOnly("maven.modrinth:embeddium:${property("embeddium_version")}")
|
||||
modCompileOnly("maven.modrinth:oculus:${property("oculus_version")}")
|
||||
|
||||
compileOnly(annotationProcessor("io.github.llamalad7:mixinextras-common:0.4.1")!!)
|
||||
implementation(include("io.github.llamalad7:mixinextras-forge:0.4.1")!!)
|
||||
|
||||
"forApi"(project(path = ":common", configuration = "commonApiOnly"))
|
||||
"forLib"(project(path = ":common", configuration = "commonLib"))
|
||||
"forBackend"(project(path = ":common", configuration = "commonBackend"))
|
||||
|
|
|
@ -26,7 +26,7 @@ parchment_version = 2023.09.03
|
|||
# Minecraft build dependency versions
|
||||
minecraft_version = 1.20.1
|
||||
forge_version = 47.2.19
|
||||
fabric_loader_version = 0.15.9
|
||||
fabric_loader_version=0.16.5
|
||||
fabric_api_version = 0.92.1+1.20.1
|
||||
|
||||
# Build dependency mod versions
|
||||
|
|
Loading…
Reference in a new issue