Re-reload

- Rename ReloadRenderersEvent to ReloadLevelRendererEvent
- Rename Engine#renderCrumblingInstances to #renderCrumbling
- Make all mixin classes package-private and abstract
- Make all event classes final and document which event bus they are
posted on
- Add EndClientResourceReloadEvent
  - Replace some usages of ReloadLevelRendererEvent with
EndClientResourceReloadEvent, including ModelHolder and ModelCache
- Always add all existing entities from world to
VisualizationManagerImpl on construction if level is instanceof Level
- Delete all VisualizationManagerImpls on resource reload
- Improve MemoryBlock utility
  - Add MemoryBlock#copyTo(MemoryBlock)
  - Remove MemoryBlock#reallocTracked and make #realloc create a tracked
block if and only if the existing block is tracked
  - Fix reallocating a debug memory block creating a regular memory
block
- Change BakedModelBufferer to only invoke the result consumer if the
data is not empty
- Improve BackendArgument
  - Fix classloading BackendArgument early causing it to return
incomplete suggestions
  - Always allow specifying namespace, allow matching only by path, and
always display suggested IDs with namespace
This commit is contained in:
PepperCode1 2023-11-29 20:03:26 -08:00
parent 7bae0f4d8c
commit f72abf8e1d
60 changed files with 501 additions and 372 deletions

View File

@ -5,10 +5,10 @@ import java.util.ArrayList;
import org.apache.maven.artifact.versioning.ArtifactVersion;
import org.slf4j.Logger;
import com.jozufozu.flywheel.api.event.ReloadRenderersEvent;
import com.jozufozu.flywheel.api.event.EndClientResourceReloadEvent;
import com.jozufozu.flywheel.api.visualization.VisualizationManager;
import com.jozufozu.flywheel.backend.Backends;
import com.jozufozu.flywheel.backend.Loader;
import com.jozufozu.flywheel.backend.compile.FlwPrograms;
import com.jozufozu.flywheel.backend.compile.Pipelines;
import com.jozufozu.flywheel.backend.engine.UniformBuffer;
import com.jozufozu.flywheel.backend.engine.batching.DrawBuffer;
@ -70,7 +70,7 @@ public class Flywheel {
IEventBus forgeEventBus = MinecraftForge.EVENT_BUS;
IEventBus modEventBus = FMLJavaModLoadingContext.get()
.getModEventBus();
modEventBus.addListener(Flywheel::setup);
modEventBus.addListener(Flywheel::onCommonSetup);
FlwConfig.get().registerSpecs(modLoadingContext);
@ -85,7 +85,7 @@ public class Flywheel {
private static void clientInit(IEventBus forgeEventBus, IEventBus modEventBus) {
forgeEventBus.addListener(Flywheel::addDebugInfo);
forgeEventBus.addListener(BackendManagerImpl::onReloadRenderers);
forgeEventBus.addListener(BackendManagerImpl::onReloadLevelRenderer);
forgeEventBus.addListener(VisualizationEventHandler::onClientTick);
forgeEventBus.addListener(VisualizationEventHandler::onBeginFrame);
@ -96,14 +96,17 @@ public class Flywheel {
forgeEventBus.addListener(FlwCommands::registerClientCommands);
forgeEventBus.addListener(DrawBuffer::onReloadRenderers);
forgeEventBus.addListener(UniformBuffer::onReloadRenderers);
forgeEventBus.addListener(UniformBuffer::onReloadLevelRenderer);
forgeEventBus.addListener(LightUpdater::onClientTick);
forgeEventBus.addListener((ReloadRenderersEvent e) -> ModelCache.onReloadRenderers(e));
forgeEventBus.addListener(ModelHolder::onReloadRenderers);
forgeEventBus.addListener((LevelEvent.Unload e) -> LevelAttached.onUnloadLevel(e));
modEventBus.addListener(Flywheel::lateInit);
modEventBus.addListener(Flywheel::onClientSetup);
modEventBus.addListener(BackendManagerImpl::onEndClientResourceReload);
modEventBus.addListener((EndClientResourceReloadEvent e) -> ModelCache.onEndClientResourceReload(e));
modEventBus.addListener(ModelHolder::onEndClientResourceReload);
modEventBus.addListener(PartialModel::onModelRegistry);
modEventBus.addListener(PartialModel::onModelBake);
@ -115,12 +118,12 @@ public class Flywheel {
Pipelines.init();
Backends.init();
Loader.init();
FlwPrograms.ResourceReloadListener.register();
ShadersModHandler.init();
}
private static void lateInit(final FMLClientSetupEvent event) {
private static void onClientSetup(FMLClientSetupEvent event) {
VertexTypes.init();
InstanceTypes.init();
Materials.init();
@ -134,7 +137,8 @@ public class Flywheel {
IdRegistryImpl.freezeAll();
}
private static void setup(final FMLCommonSetupEvent event) {
private static void onCommonSetup(FMLCommonSetupEvent event) {
// FIXME: argument types also need to be registered to BuiltInRegistries.COMMAND_ARGUMENT_TYPE
ArgumentTypeInfos.registerByClass(BackendArgument.class, SingletonArgumentInfo.contextFree(() -> BackendArgument.INSTANCE));
}

View File

@ -31,13 +31,13 @@ public interface Engine extends InstancerProvider {
/**
* Render the given instances as a crumbling overlay.
* <br>
* This is guaranteed to be called between the first and last calls to {@link #renderStage}.
* This is guaranteed to be called between the first and last calls to {@link #renderStage} for the current frame.
*
* @param executor The task executor running the frame plan.
* @param context The render context for this frame.
* @param crumblingBlocks The instances to render.
* @param crumblingBlocks The instances to render. This list is never empty.
*/
void renderCrumblingInstances(TaskExecutor executor, RenderContext context, List<CrumblingBlock> crumblingBlocks);
void renderCrumbling(TaskExecutor executor, RenderContext context, List<CrumblingBlock> crumblingBlocks);
/**
* Maintain the render origin to be within a certain distance from the camera in all directions,

View File

@ -2,7 +2,10 @@ package com.jozufozu.flywheel.api.event;
import net.minecraftforge.eventbus.api.Event;
public class BeginFrameEvent extends Event {
/**
* This event is posted to the Forge event bus.
*/
public final class BeginFrameEvent extends Event {
private final RenderContext context;
public BeginFrameEvent(RenderContext context) {

View File

@ -0,0 +1,42 @@
package com.jozufozu.flywheel.api.event;
import java.util.Optional;
import net.minecraft.client.Minecraft;
import net.minecraft.server.packs.resources.ResourceManager;
import net.minecraftforge.eventbus.api.Event;
import net.minecraftforge.fml.event.IModBusEvent;
/**
* This event is posted to mod event buses.
*/
// TODO: This should not be a mod bus event. However, currently, it cannot be a Forge bus event since that bus is not started by the time this event needs to be posted.
public final class EndClientResourceReloadEvent extends Event implements IModBusEvent {
private final Minecraft minecraft;
private final ResourceManager resourceManager;
private final boolean initialReload;
private final Optional<Throwable> error;
public EndClientResourceReloadEvent(Minecraft minecraft, ResourceManager resourceManager, boolean initialReload, Optional<Throwable> error) {
this.minecraft = minecraft;
this.resourceManager = resourceManager;
this.initialReload = initialReload;
this.error = error;
}
public Minecraft getMinecraft() {
return minecraft;
}
public ResourceManager getResourceManager() {
return resourceManager;
}
public boolean isInitialReload() {
return initialReload;
}
public Optional<Throwable> getError() {
return error;
}
}

View File

@ -5,10 +5,14 @@ import org.jetbrains.annotations.Nullable;
import net.minecraft.client.multiplayer.ClientLevel;
import net.minecraftforge.eventbus.api.Event;
public class ReloadRenderersEvent extends Event {
/**
* This event is posted to the Forge event bus.
*/
public final class ReloadLevelRendererEvent extends Event {
@Nullable
private final ClientLevel level;
public ReloadRenderersEvent(ClientLevel level) {
public ReloadLevelRendererEvent(@Nullable ClientLevel level) {
this.level = level;
}

View File

@ -9,7 +9,10 @@ import net.minecraft.client.multiplayer.ClientLevel;
import net.minecraft.client.renderer.RenderBuffers;
import net.minecraftforge.eventbus.api.Event;
public class RenderStageEvent extends Event {
/**
* This event is posted to the Forge event bus.
*/
public final class RenderStageEvent extends Event {
private final RenderContext context;
private final RenderStage stage;

View File

@ -1,46 +0,0 @@
package com.jozufozu.flywheel.backend;
import com.jozufozu.flywheel.backend.compile.FlwPrograms;
import com.jozufozu.flywheel.impl.BackendManagerImpl;
import net.minecraft.client.Minecraft;
import net.minecraft.server.packs.resources.ReloadableResourceManager;
import net.minecraft.server.packs.resources.ResourceManager;
import net.minecraft.server.packs.resources.ResourceManagerReloadListener;
/**
* The main entity for loading shaders.
*
* <p>
* This class is responsible for invoking the loading, parsing, and compilation stages.
* </p>
*/
public class Loader implements ResourceManagerReloadListener {
public static final Loader INSTANCE = new Loader();
private Loader() {
}
@Override
public void onResourceManagerReload(ResourceManager manager) {
FlwPrograms.reload(manager);
// TODO: Move this to the impl package
// TODO: To ensure this runs after all backends are ready, inject into Minecraft after the reload and before levelRenderer.allChanged()
// Alternatively, consider adding API
// TODO: This should reset all VisualizationManagerImpls, not just the one for the static client level
BackendManagerImpl.refresh(Minecraft.getInstance().level);
}
public static void init() {
// Can be null when running data generators due to the unfortunate time we call this
Minecraft minecraft = Minecraft.getInstance();
if (minecraft == null) {
return;
}
if (minecraft.getResourceManager() instanceof ReloadableResourceManager reloadable) {
reloadable.registerReloadListener(INSTANCE);
}
}
}

View File

@ -14,7 +14,10 @@ import com.jozufozu.flywheel.glsl.generate.FnSignature;
import com.jozufozu.flywheel.glsl.generate.GlslExpr;
import com.jozufozu.flywheel.lib.material.MaterialIndices;
import net.minecraft.client.Minecraft;
import net.minecraft.server.packs.resources.ReloadableResourceManager;
import net.minecraft.server.packs.resources.ResourceManager;
import net.minecraft.server.packs.resources.ResourceManagerReloadListener;
public class FlwPrograms {
private FlwPrograms() {
@ -86,4 +89,28 @@ public class FlwPrograms {
}
return builder.build();
}
public static class ResourceReloadListener implements ResourceManagerReloadListener {
public static final ResourceReloadListener INSTANCE = new ResourceReloadListener();
private ResourceReloadListener() {
}
@Override
public void onResourceManagerReload(ResourceManager manager) {
FlwPrograms.reload(manager);
}
public static void register() {
// Can be null when running data generators due to the unfortunate time we call this
Minecraft minecraft = Minecraft.getInstance();
if (minecraft == null) {
return;
}
if (minecraft.getResourceManager() instanceof ReloadableResourceManager reloadable) {
reloadable.registerReloadListener(INSTANCE);
}
}
}
}

View File

@ -104,12 +104,11 @@ public abstract class AbstractInstancer<I extends Instance> implements Instancer
deleted.clear();
}
public void delete() {
}
@Override
public String toString() {
return "AbstractInstancer[" + getInstanceCount() + ']';
}
public void delete() {
}
}

View File

@ -33,7 +33,6 @@ public abstract class InstancerStorage<N extends AbstractInstancer<?>> {
*/
private final Object creationLock = new Object();
/**
* A list of initialized instancers.
* <br>

View File

@ -6,7 +6,7 @@ import java.util.Set;
import org.lwjgl.opengl.GL32;
import com.google.common.collect.ImmutableList;
import com.jozufozu.flywheel.api.event.ReloadRenderersEvent;
import com.jozufozu.flywheel.api.event.ReloadLevelRendererEvent;
import com.jozufozu.flywheel.api.uniform.ShaderUniforms;
import com.jozufozu.flywheel.gl.buffer.GlBuffer;
import com.jozufozu.flywheel.gl.shader.GlProgram;
@ -66,7 +66,7 @@ public class UniformBuffer {
buffer.delete();
}
public static void onReloadRenderers(ReloadRenderersEvent event) {
public static void onReloadLevelRenderer(ReloadLevelRendererEvent event) {
if (instance != null) {
instance.delete();
instance = null;

View File

@ -44,7 +44,7 @@ public class BatchingEngine extends AbstractEngine {
}
@Override
public void renderCrumblingInstances(TaskExecutor executor, RenderContext context, List<CrumblingBlock> crumblingBlocks) {
public void renderCrumbling(TaskExecutor executor, RenderContext context, List<CrumblingBlock> crumblingBlocks) {
executor.syncUntil(flushFlag::isRaised);
var batchContext = BatchContext.create(context, this.renderOrigin);

View File

@ -4,7 +4,7 @@ import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.List;
import com.jozufozu.flywheel.api.event.ReloadRenderersEvent;
import com.jozufozu.flywheel.api.event.ReloadLevelRendererEvent;
import com.jozufozu.flywheel.api.event.RenderStage;
import com.jozufozu.flywheel.api.vertex.ReusableVertexList;
import com.jozufozu.flywheel.api.vertex.VertexListProvider;
@ -172,7 +172,7 @@ public class DrawBuffer {
buffer = null;
}
public static void onReloadRenderers(ReloadRenderersEvent event) {
public static void onReloadRenderers(ReloadLevelRendererEvent event) {
ALL.forEach(DrawBuffer::free);
}

View File

@ -15,31 +15,6 @@ import com.jozufozu.flywheel.lib.util.Pair;
public class IndirectDrawManager extends InstancerStorage<IndirectInstancer<?>> {
public final Map<Pair<InstanceType<?>, VertexType>, IndirectCullingGroup<?>> renderLists = new HashMap<>();
public void flush() {
super.flush();
for (IndirectCullingGroup<?> value : renderLists.values()) {
value.beginFrame();
}
}
public void invalidate() {
super.invalidate();
renderLists.values()
.forEach(IndirectCullingGroup::delete);
renderLists.clear();
}
public boolean hasStage(RenderStage stage) {
for (var list : renderLists.values()) {
if (list.hasStage(stage)) {
return true;
}
}
return false;
}
@Override
protected <I extends Instance> IndirectInstancer<?> create(InstanceType<I> type) {
return new IndirectInstancer<>(type);
@ -59,4 +34,31 @@ public class IndirectDrawManager extends InstancerStorage<IndirectInstancer<?>>
break; // TODO: support multiple meshes per model
}
}
public boolean hasStage(RenderStage stage) {
for (var list : renderLists.values()) {
if (list.hasStage(stage)) {
return true;
}
}
return false;
}
@Override
public void flush() {
super.flush();
for (IndirectCullingGroup<?> value : renderLists.values()) {
value.beginFrame();
}
}
@Override
public void invalidate() {
super.invalidate();
renderLists.values()
.forEach(IndirectCullingGroup::delete);
renderLists.clear();
}
}

View File

@ -61,7 +61,7 @@ public class IndirectEngine extends AbstractEngine {
}
@Override
public void renderCrumblingInstances(TaskExecutor executor, RenderContext context, List<CrumblingBlock> crumblingBlocks) {
public void renderCrumbling(TaskExecutor executor, RenderContext context, List<CrumblingBlock> crumblingBlocks) {
// TODO: implement
}

View File

@ -21,7 +21,6 @@ import com.jozufozu.flywheel.backend.engine.InstancerKey;
import com.jozufozu.flywheel.backend.engine.InstancerStorage;
public class InstancedDrawManager extends InstancerStorage<InstancedInstancer<?>> {
/**
* The set of draw calls to make in each {@link RenderStage}.
*/

View File

@ -79,7 +79,7 @@ public class InstancedInstancer<I extends Instance> extends AbstractInstancer<I>
changed.clear();
} catch (Exception e) {
Flywheel.LOGGER.error("Error updating GPUInstancer:", e);
Flywheel.LOGGER.error("Error updating InstancedInstancer:", e);
}
}
@ -111,6 +111,7 @@ public class InstancedInstancer<I extends Instance> extends AbstractInstancer<I>
vao.bindAttributes(1, startAttrib, instanceFormat.attributes());
}
@Override
public void delete() {
vbo.delete();
vbo = null;

View File

@ -68,11 +68,7 @@ public class InstancingEngine extends AbstractEngine {
}
@Override
public void renderCrumblingInstances(TaskExecutor executor, RenderContext context, List<CrumblingBlock> crumblingBlocks) {
if (crumblingBlocks.isEmpty()) {
return;
}
public void renderCrumbling(TaskExecutor executor, RenderContext context, List<CrumblingBlock> crumblingBlocks) {
// Need to wait for flush before we can inspect instancer state.
executor.syncUntil(flushFlag::isRaised);

View File

@ -2,11 +2,9 @@ package com.jozufozu.flywheel.config;
import java.util.Collection;
import java.util.List;
import java.util.Locale;
import java.util.concurrent.CompletableFuture;
import org.jetbrains.annotations.NotNull;
import com.jozufozu.flywheel.Flywheel;
import com.jozufozu.flywheel.api.backend.Backend;
import com.jozufozu.flywheel.lib.util.ResourceUtil;
import com.mojang.brigadier.StringReader;
@ -14,31 +12,17 @@ import com.mojang.brigadier.arguments.ArgumentType;
import com.mojang.brigadier.context.CommandContext;
import com.mojang.brigadier.exceptions.CommandSyntaxException;
import com.mojang.brigadier.exceptions.DynamicCommandExceptionType;
import com.mojang.brigadier.exceptions.SimpleCommandExceptionType;
import com.mojang.brigadier.suggestion.Suggestions;
import com.mojang.brigadier.suggestion.SuggestionsBuilder;
import net.minecraft.ResourceLocationException;
import net.minecraft.commands.SharedSuggestionProvider;
import net.minecraft.network.chat.Component;
import net.minecraft.resources.ResourceLocation;
public class BackendArgument implements ArgumentType<Backend> {
private static final List<String> STRING_IDS = Backend.REGISTRY.getAllIds()
.stream()
.map(rl -> {
if (Flywheel.ID
.equals(rl.getNamespace())) {
return rl.getPath();
} else {
return rl.toString();
}
})
.toList();
private static final List<String> EXAMPLES = List.of("off", "flywheel:off", "instancing");
private static final SimpleCommandExceptionType ERROR_INVALID = new SimpleCommandExceptionType(Component.translatable("argument.id.invalid"));
public static final DynamicCommandExceptionType ERROR_UNKNOWN_BACKEND = new DynamicCommandExceptionType(arg -> {
private static final DynamicCommandExceptionType ERROR_UNKNOWN_BACKEND = new DynamicCommandExceptionType(arg -> {
return Component.literal("Unknown backend '" + arg + "'");
});
@ -46,7 +30,7 @@ public class BackendArgument implements ArgumentType<Backend> {
@Override
public Backend parse(StringReader reader) throws CommandSyntaxException {
ResourceLocation id = getRead(reader);
ResourceLocation id = ResourceUtil.readFlywheelDefault(reader);
Backend backend = Backend.REGISTRY.get(id);
if (backend == null) {
@ -56,34 +40,20 @@ public class BackendArgument implements ArgumentType<Backend> {
return backend;
}
/**
* Copied from {@link ResourceLocation#read}, but defaults to flywheel namespace.
*/
@NotNull
private static ResourceLocation getRead(StringReader reader) throws CommandSyntaxException {
int i = reader.getCursor();
while(reader.canRead() && ResourceLocation.isAllowedInResourceLocation(reader.peek())) {
reader.skip();
}
String s = reader.getString().substring(i, reader.getCursor());
try {
return ResourceUtil.defaultToFlywheelNamespace(s);
} catch (ResourceLocationException resourcelocationexception) {
reader.setCursor(i);
throw ERROR_INVALID.createWithContext(reader);
}
}
@Override
public <S> CompletableFuture<Suggestions> listSuggestions(CommandContext<S> context, SuggestionsBuilder builder) {
return SharedSuggestionProvider.suggest(STRING_IDS, builder);
String input = builder.getRemaining().toLowerCase(Locale.ROOT);
for (ResourceLocation id : Backend.REGISTRY.getAllIds()) {
String idStr = id.toString();
if (SharedSuggestionProvider.matchesSubStr(input, idStr) || SharedSuggestionProvider.matchesSubStr(input, id.getPath())) {
builder.suggest(idStr);
}
}
return builder.buildFuture();
}
@Override
public Collection<String> getExamples() {
return STRING_IDS;
return EXAMPLES;
}
}

View File

@ -23,7 +23,7 @@ public class FlwConfig {
public final ClientConfig client;
private final ForgeConfigSpec clientSpec;
public FlwConfig() {
private FlwConfig() {
Pair<ClientConfig, ForgeConfigSpec> clientPair = new ForgeConfigSpec.Builder().configure(ClientConfig::new);
this.client = clientPair.getLeft();
clientSpec = clientPair.getRight();
@ -74,7 +74,7 @@ public class FlwConfig {
public final ConfigValue<String> backend;
public final BooleanValue limitUpdates;
public ClientConfig(ForgeConfigSpec.Builder builder) {
private ClientConfig(ForgeConfigSpec.Builder builder) {
backend = builder.comment("Select the backend to use.")
.define("backend", Backend.REGISTRY.getIdOrThrow(BackendManager.getDefaultBackend()).toString());

View File

@ -2,21 +2,21 @@ package com.jozufozu.flywheel.extension;
import net.minecraft.client.multiplayer.ClientLevel;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.level.Level;
public interface ClientLevelExtension {
public interface LevelExtension {
/**
* Get an iterator over all entities in this level.
*
* <p>
* Normally, this would be accomplished by {@link ClientLevel#entitiesForRendering()}, but the output of that
* method is filtered of entities that are rendered by flywheel. This interface provides a workaround.
* Normally, this would be accomplished by {@link ClientLevel#entitiesForRendering}, but the output of that
* method does not include entities that are rendered by Flywheel. This interface provides a workaround.
* </p>
* @return An iterator over all entities in the level, including entities that are rendered by flywheel.
* @return An iterator over all entities in the level, including entities that are rendered by Flywheel.
*/
Iterable<Entity> flywheel$getAllLoadedEntities();
static Iterable<Entity> getAllLoadedEntities(ClientLevel level) {
return ((ClientLevelExtension) level).flywheel$getAllLoadedEntities();
static Iterable<Entity> getAllLoadedEntities(Level level) {
return ((LevelExtension) level).flywheel$getAllLoadedEntities();
}
}

View File

@ -85,7 +85,7 @@ public class SourceFile implements SourceComponent {
ResourceLocation location;
try {
location = ResourceUtil.defaultToFlywheelNamespace(string);
location = ResourceUtil.parseFlywheelDefault(string);
} catch (ResourceLocationException e) {
failures.add(Pair.of(fileSpan, new LoadError.MalformedInclude(e)));
continue;

View File

@ -1,11 +1,11 @@
package com.jozufozu.flywheel.impl;
import org.jetbrains.annotations.Nullable;
import org.slf4j.Logger;
import com.jozufozu.flywheel.Flywheel;
import com.jozufozu.flywheel.api.backend.Backend;
import com.jozufozu.flywheel.api.event.ReloadRenderersEvent;
import com.jozufozu.flywheel.api.event.EndClientResourceReloadEvent;
import com.jozufozu.flywheel.api.event.ReloadLevelRendererEvent;
import com.jozufozu.flywheel.backend.Backends;
import com.jozufozu.flywheel.config.FlwConfig;
import com.jozufozu.flywheel.impl.visualization.VisualizationManagerImpl;
@ -59,19 +59,7 @@ public final class BackendManagerImpl {
return Backends.INDIRECT;
}
public static void onReloadRenderers(ReloadRenderersEvent event) {
refresh(event.getLevel());
}
public static void refresh(@Nullable ClientLevel level) {
backend = chooseBackend();
if (level != null) {
VisualizationManagerImpl.reset(level);
}
}
private static Backend chooseBackend() {
private static void chooseBackend() {
var preferred = FlwConfig.get().getBackend();
var actual = preferred.findFallback();
@ -79,7 +67,7 @@ public final class BackendManagerImpl {
LOGGER.warn("Flywheel backend fell back from '{}' to '{}'", Backend.REGISTRY.getIdOrThrow(preferred), Backend.REGISTRY.getIdOrThrow(actual));
}
return actual;
backend = actual;
}
public static String getBackendString() {
@ -93,4 +81,22 @@ public final class BackendManagerImpl {
public static void init() {
CrashReportCallables.registerCrashCallable("Flywheel Backend", BackendManagerImpl::getBackendString);
}
public static void onEndClientResourceReload(EndClientResourceReloadEvent event) {
if (event.getError().isPresent()) {
return;
}
chooseBackend();
VisualizationManagerImpl.resetAll();
}
public static void onReloadLevelRenderer(ReloadLevelRendererEvent event) {
chooseBackend();
ClientLevel level = event.getLevel();
if (level != null) {
VisualizationManagerImpl.reset(level);
}
}
}

View File

@ -18,7 +18,7 @@ import com.jozufozu.flywheel.api.visual.TickableVisual;
import com.jozufozu.flywheel.api.visualization.VisualManager;
import com.jozufozu.flywheel.api.visualization.VisualizationLevel;
import com.jozufozu.flywheel.api.visualization.VisualizationManager;
import com.jozufozu.flywheel.extension.ClientLevelExtension;
import com.jozufozu.flywheel.extension.LevelExtension;
import com.jozufozu.flywheel.impl.task.FlwTaskExecutor;
import com.jozufozu.flywheel.impl.visualization.manager.BlockEntityVisualManager;
import com.jozufozu.flywheel.impl.visualization.manager.EffectVisualManager;
@ -32,10 +32,10 @@ import com.jozufozu.flywheel.lib.util.LevelAttached;
import it.unimi.dsi.fastutil.longs.Long2ObjectMap;
import net.minecraft.client.Minecraft;
import net.minecraft.client.multiplayer.ClientLevel;
import net.minecraft.core.Vec3i;
import net.minecraft.server.level.BlockDestructionProgress;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.LevelAccessor;
import net.minecraft.world.level.block.entity.BlockEntity;
@ -88,6 +88,11 @@ public class VisualizationManagerImpl implements VisualizationManager {
.then(engine.createFramePlan())
.then(RaisePlan.raise(frameFlag))
.simplify();
if (level instanceof Level l) {
LevelExtension.getAllLoadedEntities(l)
.forEach(entities::queueAdd);
}
}
public static boolean supportsVisualization(@Nullable LevelAccessor level) {
@ -127,19 +132,14 @@ public class VisualizationManagerImpl implements VisualizationManager {
return MANAGERS.get(level);
}
// TODO: Consider making this reset action reuse the existing added game objects instead of readding them, potentially by keeping the same VisualizationManagerImpl and not fully deleting it
// TODO: Consider changing parameter type to Level since it is also possible to get all entities from it
public static void reset(ClientLevel level) {
// TODO: Consider making these reset actions reuse the existing game objects instead of re-adding them
// potentially by keeping the same VisualizationManagerImpl and deleting the engine and visuals but not the game objects
public static void reset(LevelAccessor level) {
MANAGERS.remove(level);
VisualizationManagerImpl manager = get(level);
if (manager == null) {
return;
}
}
// Block entities are loaded while chunks are baked.
// Entities are loaded with the level, so when chunks are reloaded they need to be re-added.
ClientLevelExtension.getAllLoadedEntities(level)
.forEach(manager.getEntities()::queueAdd);
public static void resetAll() {
MANAGERS.reset();
}
@Override
@ -239,7 +239,9 @@ public class VisualizationManagerImpl implements VisualizationManager {
crumblingBlocks.add(new Engine.CrumblingBlock(maxDestruction.getProgress(), maxDestruction.getPos(), instances));
}
engine.renderCrumblingInstances(taskExecutor, context, crumblingBlocks);
if (!crumblingBlocks.isEmpty()) {
engine.renderCrumbling(taskExecutor, context, crumblingBlocks);
}
}
/**

View File

@ -0,0 +1,76 @@
package com.jozufozu.flywheel.lib.memory;
import java.lang.ref.Cleaner;
import java.nio.ByteBuffer;
import org.lwjgl.system.MemoryUtil;
non-sealed abstract class AbstractMemoryBlockImpl implements MemoryBlock {
static final Cleaner CLEANER = Cleaner.create();
final long ptr;
final long size;
boolean freed;
AbstractMemoryBlockImpl(long ptr, long size) {
this.ptr = ptr;
this.size = size;
}
@Override
public long ptr() {
return ptr;
}
@Override
public long size() {
return size;
}
@Override
public boolean isFreed() {
return freed;
}
@Override
public void copyTo(MemoryBlock block) {
long bytes = Math.min(size, block.size());
copyTo(block.ptr(), bytes);
}
@Override
public void copyTo(long ptr, long bytes) {
MemoryUtil.memCopy(this.ptr, ptr, bytes);
}
@Override
public void copyTo(long ptr) {
copyTo(ptr, size);
}
@Override
public void clear() {
MemoryUtil.memSet(ptr, 0, size);
}
@Override
public ByteBuffer asBuffer() {
int intSize = (int) size;
if (intSize != size) {
throw new UnsupportedOperationException("Cannot create buffer with long capacity!");
}
return MemoryUtil.memByteBuffer(ptr, intSize);
}
void freeInner() {
FlwMemoryTracker._freeCPUMemory(size);
freed = true;
}
@Override
public void free() {
FlwMemoryTracker.free(ptr);
freeInner();
}
}

View File

@ -5,16 +5,23 @@ import java.lang.ref.Cleaner;
import com.jozufozu.flywheel.Flywheel;
import com.jozufozu.flywheel.lib.util.StringUtil;
class DebugMemoryBlockImpl extends MemoryBlockImpl {
class DebugMemoryBlockImpl extends AbstractMemoryBlockImpl {
final Cleaner cleaner;
final CleaningAction cleaningAction;
final Cleaner.Cleanable cleanable;
DebugMemoryBlockImpl(long ptr, long size, Cleaner cleaner) {
DebugMemoryBlockImpl(long ptr, long size, Cleaner cleaner, int skipFrames) {
super(ptr, size);
cleaningAction = new CleaningAction(ptr, size, getStackTrace());
this.cleaner = cleaner;
cleaningAction = new CleaningAction(ptr, size, getStackTrace(skipFrames + 1));
cleanable = cleaner.register(this, cleaningAction);
}
@Override
public boolean isTracked() {
return false;
}
@Override
void freeInner() {
super.freeInner();
@ -22,24 +29,37 @@ class DebugMemoryBlockImpl extends MemoryBlockImpl {
cleanable.clean();
}
static StackWalker.StackFrame[] getStackTrace() {
// skip 4 frames to get to the caller:
@Override
public MemoryBlock realloc(long size) {
MemoryBlock block = new DebugMemoryBlockImpl(FlwMemoryTracker.realloc(ptr, size), size, cleaner, 1);
FlwMemoryTracker._allocCPUMemory(block.size());
freeInner();
return block;
}
static StackWalker.StackFrame[] getStackTrace(int skipFrames) {
// for DebugMemoryBlockImpl::realloc, skip 3 frames to get the allocation site:
// - this method
// - DebugMemoryBlockImpl::new
// - DebugMemoryBlockImpl::realloc
// - {caller is here}
// for DebugMemoryBlockImpl::malloc/calloc, skip 4 frames to get the allocation site:
// - this method
// - DebugMemoryBlockImpl::new
// - DebugMemoryBlockImpl::malloc/calloc
// - MemoryBlock::malloc/calloc
// - {caller is here}
return StackWalker.getInstance().walk(s -> s.skip(4).toArray(StackWalker.StackFrame[]::new));
return StackWalker.getInstance().walk(s -> s.skip(skipFrames + 1).toArray(StackWalker.StackFrame[]::new));
}
static MemoryBlock malloc(long size) {
MemoryBlock block = new DebugMemoryBlockImpl(FlwMemoryTracker.malloc(size), size, FlwMemoryTracker.CLEANER);
MemoryBlock block = new DebugMemoryBlockImpl(FlwMemoryTracker.malloc(size), size, CLEANER, 2);
FlwMemoryTracker._allocCPUMemory(block.size());
return block;
}
static MemoryBlock calloc(long num, long size) {
MemoryBlock block = new DebugMemoryBlockImpl(FlwMemoryTracker.calloc(num, size), num * size, FlwMemoryTracker.CLEANER);
MemoryBlock block = new DebugMemoryBlockImpl(FlwMemoryTracker.calloc(num, size), num * size, CLEANER, 2);
FlwMemoryTracker._allocCPUMemory(block.size());
return block;
}

View File

@ -1,6 +1,5 @@
package com.jozufozu.flywheel.lib.memory;
import java.lang.ref.Cleaner;
import java.util.concurrent.atomic.AtomicLong;
import org.lwjgl.system.MemoryUtil;
@ -10,8 +9,6 @@ import com.jozufozu.flywheel.lib.util.StringUtil;
public final class FlwMemoryTracker {
public static final boolean DEBUG_MEMORY_SAFETY = System.getProperty("flw.debugMemorySafety") != null;
static final Cleaner CLEANER = Cleaner.create();
private static final AtomicLong CPU_MEMORY = new AtomicLong(0);
private static final AtomicLong GPU_MEMORY = new AtomicLong(0);

View File

@ -2,7 +2,7 @@ package com.jozufozu.flywheel.lib.memory;
import java.nio.ByteBuffer;
public sealed interface MemoryBlock permits MemoryBlockImpl {
public sealed interface MemoryBlock permits AbstractMemoryBlockImpl {
long ptr();
long size();
@ -11,6 +11,8 @@ public sealed interface MemoryBlock permits MemoryBlockImpl {
boolean isTracked();
void copyTo(MemoryBlock block);
void copyTo(long ptr, long bytes);
void copyTo(long ptr);
@ -21,8 +23,6 @@ public sealed interface MemoryBlock permits MemoryBlockImpl {
MemoryBlock realloc(long size);
MemoryBlock reallocTracked(long size);
void free();
static MemoryBlock malloc(long size) {

View File

@ -1,33 +1,8 @@
package com.jozufozu.flywheel.lib.memory;
import java.nio.ByteBuffer;
import org.lwjgl.system.MemoryUtil;
non-sealed class MemoryBlockImpl implements MemoryBlock {
final long ptr;
final long size;
boolean freed;
class MemoryBlockImpl extends AbstractMemoryBlockImpl {
MemoryBlockImpl(long ptr, long size) {
this.ptr = ptr;
this.size = size;
}
@Override
public long ptr() {
return ptr;
}
@Override
public long size() {
return size;
}
@Override
public boolean isFreed() {
return freed;
super(ptr, size);
}
@Override
@ -35,35 +10,6 @@ non-sealed class MemoryBlockImpl implements MemoryBlock {
return false;
}
@Override
public void copyTo(long ptr, long bytes) {
MemoryUtil.memCopy(this.ptr, ptr, bytes);
}
@Override
public void copyTo(long ptr) {
copyTo(ptr, size);
}
@Override
public void clear() {
MemoryUtil.memSet(ptr, 0, size);
}
@Override
public ByteBuffer asBuffer() {
int intSize = (int) size;
if (intSize != size) {
throw new UnsupportedOperationException("Cannot create buffer with long capacity!");
}
return MemoryUtil.memByteBuffer(ptr, intSize);
}
void freeInner() {
FlwMemoryTracker._freeCPUMemory(size);
freed = true;
}
@Override
public MemoryBlock realloc(long size) {
MemoryBlock block = new MemoryBlockImpl(FlwMemoryTracker.realloc(ptr, size), size);
@ -72,20 +18,6 @@ non-sealed class MemoryBlockImpl implements MemoryBlock {
return block;
}
@Override
public MemoryBlock reallocTracked(long size) {
MemoryBlock block = new TrackedMemoryBlockImpl(FlwMemoryTracker.realloc(ptr, size), size, FlwMemoryTracker.CLEANER);
FlwMemoryTracker._allocCPUMemory(block.size());
freeInner();
return block;
}
@Override
public void free() {
FlwMemoryTracker.free(ptr);
freeInner();
}
static MemoryBlock malloc(long size) {
MemoryBlock block = new MemoryBlockImpl(FlwMemoryTracker.malloc(size), size);
FlwMemoryTracker._allocCPUMemory(block.size());

View File

@ -2,12 +2,14 @@ package com.jozufozu.flywheel.lib.memory;
import java.lang.ref.Cleaner;
class TrackedMemoryBlockImpl extends MemoryBlockImpl {
class TrackedMemoryBlockImpl extends AbstractMemoryBlockImpl {
final Cleaner cleaner;
final CleaningAction cleaningAction;
final Cleaner.Cleanable cleanable;
TrackedMemoryBlockImpl(long ptr, long size, Cleaner cleaner) {
super(ptr, size);
this.cleaner = cleaner;
cleaningAction = new CleaningAction(ptr, size);
cleanable = cleaner.register(this, cleaningAction);
}
@ -24,14 +26,22 @@ class TrackedMemoryBlockImpl extends MemoryBlockImpl {
cleanable.clean();
}
@Override
public MemoryBlock realloc(long size) {
MemoryBlock block = new TrackedMemoryBlockImpl(FlwMemoryTracker.realloc(ptr, size), size, cleaner);
FlwMemoryTracker._allocCPUMemory(block.size());
freeInner();
return block;
}
static MemoryBlock malloc(long size) {
MemoryBlock block = new TrackedMemoryBlockImpl(FlwMemoryTracker.malloc(size), size, FlwMemoryTracker.CLEANER);
MemoryBlock block = new TrackedMemoryBlockImpl(FlwMemoryTracker.malloc(size), size, CLEANER);
FlwMemoryTracker._allocCPUMemory(block.size());
return block;
}
static MemoryBlock calloc(long num, long size) {
MemoryBlock block = new TrackedMemoryBlockImpl(FlwMemoryTracker.calloc(num, size), num * size, FlwMemoryTracker.CLEANER);
MemoryBlock block = new TrackedMemoryBlockImpl(FlwMemoryTracker.calloc(num, size), num * size, CLEANER);
FlwMemoryTracker._allocCPUMemory(block.size());
return block;
}

View File

@ -8,7 +8,7 @@ import java.util.function.Function;
import org.jetbrains.annotations.ApiStatus;
import com.jozufozu.flywheel.api.event.ReloadRenderersEvent;
import com.jozufozu.flywheel.api.event.EndClientResourceReloadEvent;
import com.jozufozu.flywheel.api.model.Model;
public class ModelCache<T> {
@ -31,7 +31,7 @@ public class ModelCache<T> {
}
@ApiStatus.Internal
public static void onReloadRenderers(ReloadRenderersEvent event) {
public static void onEndClientResourceReload(EndClientResourceReloadEvent event) {
for (ModelCache<?> cache : ALL) {
cache.clear();
}

View File

@ -7,7 +7,7 @@ import java.util.function.Supplier;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.Nullable;
import com.jozufozu.flywheel.api.event.ReloadRenderersEvent;
import com.jozufozu.flywheel.api.event.EndClientResourceReloadEvent;
import com.jozufozu.flywheel.api.model.Model;
public class ModelHolder {
@ -51,7 +51,7 @@ public class ModelHolder {
}
@ApiStatus.Internal
public static void onReloadRenderers(ReloadRenderersEvent event) {
public static void onEndClientResourceReload(EndClientResourceReloadEvent event) {
for (ModelHolder holder : ALL) {
holder.clear();
}

View File

@ -56,9 +56,11 @@ public final class BakedModelBufferer {
blockRenderer.tesselateBlock(renderWorld, model, state, BlockPos.ZERO, poseStack, buffer, false, random, 42L, OverlayTexture.NO_OVERLAY, modelData, renderType);
poseStack.popPose();
RenderedBuffer data = buffer.end();
resultConsumer.accept(renderType, data);
data.release();
RenderedBuffer data = buffer.endOrDiscardIfEmpty();
if (data != null) {
resultConsumer.accept(renderType, data);
data.release();
}
}
}
@ -89,12 +91,16 @@ public final class BakedModelBufferer {
blockRenderer.tesselateBlock(renderWorld, model, state, BlockPos.ZERO, poseStack, shadeSeparatingWrapper, false, random, 42L, OverlayTexture.NO_OVERLAY, modelData, renderType);
poseStack.popPose();
RenderedBuffer shadedData = shadedBuffer.end();
RenderedBuffer unshadedData = unshadedBuffer.end();
resultConsumer.accept(renderType, true, shadedData);
shadedData.release();
resultConsumer.accept(renderType, false, unshadedData);
unshadedData.release();
RenderedBuffer shadedData = shadedBuffer.endOrDiscardIfEmpty();
if (shadedData != null) {
resultConsumer.accept(renderType, true, shadedData);
shadedData.release();
}
RenderedBuffer unshadedData = unshadedBuffer.endOrDiscardIfEmpty();
if (unshadedData != null) {
resultConsumer.accept(renderType, false, unshadedData);
unshadedData.release();
}
}
shadeSeparatingWrapper.clear();
@ -163,9 +169,11 @@ public final class BakedModelBufferer {
for (int layerIndex = 0; layerIndex < CHUNK_LAYER_AMOUNT; layerIndex++) {
RenderType renderType = CHUNK_LAYERS[layerIndex];
BufferBuilder buffer = buffers[layerIndex];
RenderedBuffer data = buffer.end();
resultConsumer.accept(renderType, data);
data.release();
RenderedBuffer data = buffer.endOrDiscardIfEmpty();
if (data != null) {
resultConsumer.accept(renderType, data);
data.release();
}
}
}
@ -224,12 +232,16 @@ public final class BakedModelBufferer {
RenderType renderType = CHUNK_LAYERS[layerIndex];
BufferBuilder shadedBuffer = shadedBuffers[layerIndex];
BufferBuilder unshadedBuffer = unshadedBuffers[layerIndex];
RenderedBuffer shadedData = shadedBuffer.end();
RenderedBuffer unshadedData = unshadedBuffer.end();
resultConsumer.accept(renderType, true, shadedData);
shadedData.release();
resultConsumer.accept(renderType, false, unshadedData);
unshadedData.release();
RenderedBuffer shadedData = shadedBuffer.endOrDiscardIfEmpty();
if (shadedData != null) {
resultConsumer.accept(renderType, true, shadedData);
shadedData.release();
}
RenderedBuffer unshadedData = unshadedBuffer.endOrDiscardIfEmpty();
if (unshadedBuffer != null) {
resultConsumer.accept(renderType, false, unshadedData);
unshadedData.release();
}
}
}

View File

@ -81,23 +81,19 @@ public class BakedModelBuilder {
if (shadeSeparated) {
ShadeSeparatedResultConsumer resultConsumer = (renderType, shaded, data) -> {
if (!data.isEmpty()) {
Material material = materialFunc.apply(renderType, shaded);
if (material != null) {
MemoryBlock meshData = ModelUtil.convertVanillaBuffer(data, VertexTypes.BLOCK);
meshMapBuilder.put(material, new SimpleMesh(VertexTypes.BLOCK, meshData, "source=BakedModelBuilder," + "bakedModel=" + bakedModel + ",renderType=" + renderType + ",shaded=" + shaded));
}
Material material = materialFunc.apply(renderType, shaded);
if (material != null) {
MemoryBlock meshData = ModelUtil.convertVanillaBuffer(data, VertexTypes.BLOCK);
meshMapBuilder.put(material, new SimpleMesh(VertexTypes.BLOCK, meshData, "source=BakedModelBuilder," + "bakedModel=" + bakedModel + ",renderType=" + renderType + ",shaded=" + shaded));
}
};
BakedModelBufferer.bufferSingleShadeSeparated(ModelUtil.VANILLA_RENDERER.getModelRenderer(), renderWorld, bakedModel, blockState, poseStack, modelData, resultConsumer);
} else {
ResultConsumer resultConsumer = (renderType, data) -> {
if (!data.isEmpty()) {
Material material = materialFunc.apply(renderType, true);
if (material != null) {
MemoryBlock meshData = ModelUtil.convertVanillaBuffer(data, VertexTypes.BLOCK);
meshMapBuilder.put(material, new SimpleMesh(VertexTypes.BLOCK, meshData, "source=BakedModelBuilder," + "bakedModel=" + bakedModel + ",renderType=" + renderType));
}
Material material = materialFunc.apply(renderType, true);
if (material != null) {
MemoryBlock meshData = ModelUtil.convertVanillaBuffer(data, VertexTypes.BLOCK);
meshMapBuilder.put(material, new SimpleMesh(VertexTypes.BLOCK, meshData, "source=BakedModelBuilder," + "bakedModel=" + bakedModel + ",renderType=" + renderType));
}
};
BakedModelBufferer.bufferSingle(ModelUtil.VANILLA_RENDERER.getModelRenderer(), renderWorld, bakedModel, blockState, poseStack, modelData, resultConsumer);

View File

@ -70,23 +70,19 @@ public class BlockModelBuilder {
if (shadeSeparated) {
ShadeSeparatedResultConsumer resultConsumer = (renderType, shaded, data) -> {
if (!data.isEmpty()) {
Material material = materialFunc.apply(renderType, shaded);
if (material != null) {
MemoryBlock meshData = ModelUtil.convertVanillaBuffer(data, VertexTypes.BLOCK);
meshMapBuilder.put(material, new SimpleMesh(VertexTypes.BLOCK, meshData, "source=BlockModelBuilder," + "blockState=" + state + ",renderType=" + renderType + ",shaded=" + shaded));
}
Material material = materialFunc.apply(renderType, shaded);
if (material != null) {
MemoryBlock meshData = ModelUtil.convertVanillaBuffer(data, VertexTypes.BLOCK);
meshMapBuilder.put(material, new SimpleMesh(VertexTypes.BLOCK, meshData, "source=BlockModelBuilder," + "blockState=" + state + ",renderType=" + renderType + ",shaded=" + shaded));
}
};
BakedModelBufferer.bufferBlockShadeSeparated(ModelUtil.VANILLA_RENDERER, renderWorld, state, poseStack, modelData, resultConsumer);
} else {
ResultConsumer resultConsumer = (renderType, data) -> {
if (!data.isEmpty()) {
Material material = materialFunc.apply(renderType, true);
if (material != null) {
MemoryBlock meshData = ModelUtil.convertVanillaBuffer(data, VertexTypes.BLOCK);
meshMapBuilder.put(material, new SimpleMesh(VertexTypes.BLOCK, meshData, "source=BlockModelBuilder," + "blockState=" + state + ",renderType=" + renderType));
}
Material material = materialFunc.apply(renderType, true);
if (material != null) {
MemoryBlock meshData = ModelUtil.convertVanillaBuffer(data, VertexTypes.BLOCK);
meshMapBuilder.put(material, new SimpleMesh(VertexTypes.BLOCK, meshData, "source=BlockModelBuilder," + "blockState=" + state + ",renderType=" + renderType));
}
};
BakedModelBufferer.bufferBlock(ModelUtil.VANILLA_RENDERER, renderWorld, state, poseStack, modelData, resultConsumer);

View File

@ -74,23 +74,19 @@ public class MultiBlockModelBuilder {
if (shadeSeparated) {
ShadeSeparatedResultConsumer resultConsumer = (renderType, shaded, data) -> {
if (!data.isEmpty()) {
Material material = materialFunc.apply(renderType, shaded);
if (material != null) {
MemoryBlock meshData = ModelUtil.convertVanillaBuffer(data, VertexTypes.BLOCK);
meshMapBuilder.put(material, new SimpleMesh(VertexTypes.BLOCK, meshData, "source=MultiBlockModelBuilder," + "renderType=" + renderType + ",shaded=" + shaded));
}
Material material = materialFunc.apply(renderType, shaded);
if (material != null) {
MemoryBlock meshData = ModelUtil.convertVanillaBuffer(data, VertexTypes.BLOCK);
meshMapBuilder.put(material, new SimpleMesh(VertexTypes.BLOCK, meshData, "source=MultiBlockModelBuilder," + "renderType=" + renderType + ",shaded=" + shaded));
}
};
BakedModelBufferer.bufferMultiBlockShadeSeparated(blocks, ModelUtil.VANILLA_RENDERER, renderWorld, poseStack, modelDataMap, resultConsumer);
} else {
ResultConsumer resultConsumer = (renderType, data) -> {
if (!data.isEmpty()) {
Material material = materialFunc.apply(renderType, true);
if (material != null) {
MemoryBlock meshData = ModelUtil.convertVanillaBuffer(data, VertexTypes.BLOCK);
meshMapBuilder.put(material, new SimpleMesh(VertexTypes.BLOCK, meshData, "source=MultiBlockModelBuilder," + "renderType=" + renderType));
}
Material material = materialFunc.apply(renderType, true);
if (material != null) {
MemoryBlock meshData = ModelUtil.convertVanillaBuffer(data, VertexTypes.BLOCK);
meshMapBuilder.put(material, new SimpleMesh(VertexTypes.BLOCK, meshData, "source=MultiBlockModelBuilder," + "renderType=" + renderType));
}
};
BakedModelBufferer.bufferMultiBlock(blocks, ModelUtil.VANILLA_RENDERER, renderWorld, poseStack, modelDataMap, resultConsumer);

View File

@ -126,7 +126,7 @@ class VertexWriter implements VertexConsumer {
public MemoryBlock copyDataAndReset() {
MemoryBlock dataCopy = MemoryBlock.malloc(vertexCount * STRIDE);
data.copyTo(dataCopy.ptr(), dataCopy.size());
data.copyTo(dataCopy);
vertexCount = 0;
filledPosition = false;

View File

@ -1,11 +1,24 @@
package com.jozufozu.flywheel.lib.util;
import com.jozufozu.flywheel.Flywheel;
import com.mojang.brigadier.StringReader;
import com.mojang.brigadier.exceptions.CommandSyntaxException;
import com.mojang.brigadier.exceptions.SimpleCommandExceptionType;
import net.minecraft.ResourceLocationException;
import net.minecraft.network.chat.Component;
import net.minecraft.resources.ResourceLocation;
public class ResourceUtil {
public static ResourceLocation defaultToFlywheelNamespace(String location) {
public final class ResourceUtil {
private static final SimpleCommandExceptionType ERROR_INVALID = new SimpleCommandExceptionType(Component.translatable("argument.id.invalid"));
private ResourceUtil() {
}
/**
* Same as {@link ResourceLocation#ResourceLocation(String)}, but defaults to Flywheel namespace.
*/
public static ResourceLocation parseFlywheelDefault(String location) {
String namespace = Flywheel.ID;
String path = location;
@ -19,4 +32,24 @@ public class ResourceUtil {
return new ResourceLocation(namespace, path);
}
/**
* Same as {@link ResourceLocation#read(StringReader)}, but defaults to Flywheel namespace.
*/
public static ResourceLocation readFlywheelDefault(StringReader reader) throws CommandSyntaxException {
int i = reader.getCursor();
while (reader.canRead() && ResourceLocation.isAllowedInResourceLocation(reader.peek())) {
reader.skip();
}
String s = reader.getString().substring(i, reader.getCursor());
try {
return parseFlywheelDefault(s);
} catch (ResourceLocationException resourcelocationexception) {
reader.setCursor(i);
throw ERROR_INVALID.createWithContext(reader);
}
}
}

View File

@ -11,7 +11,7 @@ import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.entity.BlockEntityType;
@Mixin(BlockEntityType.class)
public class BlockEntityTypeMixin<T extends BlockEntity> implements BlockEntityTypeExtension<T> {
abstract class BlockEntityTypeMixin<T extends BlockEntity> implements BlockEntityTypeExtension<T> {
@Unique
private BlockEntityVisualizer<? super T> flywheel$visualizer;

View File

@ -14,7 +14,7 @@ import com.mojang.blaze3d.vertex.VertexFormat;
import com.mojang.blaze3d.vertex.VertexFormatElement;
@Mixin(BufferBuilder.class)
public abstract class BufferBuilderMixin implements BufferBuilderExtension {
abstract class BufferBuilderMixin implements BufferBuilderExtension {
@Shadow
private ByteBuffer buffer;

View File

@ -3,30 +3,19 @@ package com.jozufozu.flywheel.mixin;
import java.util.ArrayList;
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.CallbackInfoReturnable;
import com.google.common.collect.Lists;
import com.jozufozu.flywheel.api.visualization.VisualizationManager;
import com.jozufozu.flywheel.extension.ClientLevelExtension;
import com.jozufozu.flywheel.impl.visualization.VisualizationHelper;
import net.minecraft.client.multiplayer.ClientLevel;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.level.entity.LevelEntityGetter;
@Mixin(ClientLevel.class)
public abstract class ClientLevelMixin implements ClientLevelExtension {
@Shadow
protected abstract LevelEntityGetter<Entity> getEntities();
@Override
public Iterable<Entity> flywheel$getAllLoadedEntities() {
return getEntities().getAll();
}
abstract class ClientLevelMixin {
@Inject(method = "entitiesForRendering()Ljava/lang/Iterable;", at = @At("RETURN"), cancellable = true)
private void flywheel$filterEntities(CallbackInfoReturnable<Iterable<Entity>> cir) {
if (!VisualizationManager.supportsVisualization((ClientLevel) (Object) this)) {

View File

@ -11,7 +11,7 @@ import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.EntityType;
@Mixin(EntityType.class)
public class EntityTypeMixin<T extends Entity> implements EntityTypeExtension<T> {
abstract class EntityTypeMixin<T extends Entity> implements EntityTypeExtension<T> {
@Unique
private EntityVisualizer<? super T> flywheel$visualizer;

View File

@ -11,7 +11,7 @@ import com.jozufozu.flywheel.lib.uniform.FlwShaderUniforms;
import net.minecraft.client.renderer.FogRenderer;
@Mixin(FogRenderer.class)
public class FogUpdateMixin {
abstract class FogUpdateMixin {
@Unique
private static void flywheel$updateFog() {
FlwShaderUniforms.FOG_UPDATE = true;

View File

@ -10,7 +10,7 @@ import com.jozufozu.flywheel.gl.buffer.GlBufferType;
import com.mojang.blaze3d.platform.GlStateManager;
@Mixin(GlStateManager.class)
public class GlStateManagerMixin {
abstract class GlStateManagerMixin {
@Inject(method = "_glBindBuffer(II)V", at = @At("RETURN"))
private static void flywheel$onBindBuffer(int target, int buffer, CallbackInfo ci) {
GlStateTracker._setBuffer(GlBufferType.fromTarget(target), buffer);

View File

@ -0,0 +1,21 @@
package com.jozufozu.flywheel.mixin;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import com.jozufozu.flywheel.extension.LevelExtension;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.entity.LevelEntityGetter;
@Mixin(Level.class)
abstract class LevelMixin implements LevelExtension {
@Shadow
protected abstract LevelEntityGetter<Entity> getEntities();
@Override
public Iterable<Entity> flywheel$getAllLoadedEntities() {
return getEntities().getAll();
}
}

View File

@ -14,7 +14,7 @@ import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import com.jozufozu.flywheel.api.event.BeginFrameEvent;
import com.jozufozu.flywheel.api.event.ReloadRenderersEvent;
import com.jozufozu.flywheel.api.event.ReloadLevelRendererEvent;
import com.jozufozu.flywheel.api.event.RenderContext;
import com.jozufozu.flywheel.api.event.RenderStage;
import com.jozufozu.flywheel.api.event.RenderStageEvent;
@ -32,7 +32,7 @@ import net.minecraft.server.level.BlockDestructionProgress;
import net.minecraftforge.common.MinecraftForge;
@Mixin(value = LevelRenderer.class, priority = 1001) // Higher priority to go after Sodium
public class LevelRendererMixin {
abstract class LevelRendererMixin {
@Shadow
private ClientLevel level;
@ -61,7 +61,7 @@ public class LevelRendererMixin {
@Inject(method = "allChanged", at = @At("RETURN"))
private void flywheel$refresh(CallbackInfo ci) {
MinecraftForge.EVENT_BUS.post(new ReloadRenderersEvent(level));
MinecraftForge.EVENT_BUS.post(new ReloadLevelRendererEvent(level));
}
@Inject(method = "renderLevel", at = @At(value = "INVOKE_STRING", target = "Lnet/minecraft/util/profiling/ProfilerFiller;popPush(Ljava/lang/String;)V", args = "ldc=destroyProgress"))

View File

@ -16,7 +16,7 @@ import net.minecraft.world.level.LightLayer;
import net.minecraft.world.level.chunk.ChunkSource;
@Mixin(ClientChunkCache.class)
public abstract class LightUpdateMixin extends ChunkSource {
abstract class LightUpdateMixin extends ChunkSource {
@Shadow
@Final
ClientLevel level;

View File

@ -0,0 +1,37 @@
package com.jozufozu.flywheel.mixin;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
import org.spongepowered.asm.mixin.Final;
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.api.event.EndClientResourceReloadEvent;
import com.mojang.realmsclient.client.RealmsClient;
import net.minecraft.client.Minecraft;
import net.minecraft.client.main.GameConfig;
import net.minecraft.server.packs.resources.ReloadInstance;
import net.minecraft.server.packs.resources.ReloadableResourceManager;
import net.minecraftforge.fml.ModLoader;
@Mixin(Minecraft.class)
abstract class MinecraftMixin {
@Shadow
@Final
private ReloadableResourceManager resourceManager;
@Inject(method = "lambda$new$5", at = @At("HEAD"))
private void flywheel$onEndInitialResourceReload(RealmsClient realmsClient, ReloadInstance reloadInstance, GameConfig gameConfig, Optional<Throwable> error, CallbackInfo ci) {
ModLoader.get().postEvent(new EndClientResourceReloadEvent((Minecraft) (Object) this, resourceManager, true, error));
}
@Inject(method = "lambda$reloadResourcePacks$28", at = @At("HEAD"))
private void flywheel$onEndManualResourceReload(boolean recovery, CompletableFuture<Void> future, Optional<Throwable> error, CallbackInfo ci) {
ModLoader.get().postEvent(new EndClientResourceReloadEvent((Minecraft) (Object) this, resourceManager, false, error));
}
}

View File

@ -7,7 +7,7 @@ import com.jozufozu.flywheel.lib.transform.TransformStack;
import com.mojang.blaze3d.vertex.PoseStack;
@Mixin(PoseStack.class)
public abstract class PoseStackMixin implements TransformStack {
abstract class PoseStackMixin implements TransformStack {
@Override
public TransformStack pushPose() {
((PoseStack) (Object) this).pushPose();

View File

@ -12,10 +12,11 @@ import com.jozufozu.flywheel.extension.RenderTypeExtension;
import net.minecraft.client.renderer.RenderType;
@Mixin(RenderType.class)
public class RenderTypeMixin implements RenderTypeExtension {
abstract class RenderTypeMixin implements RenderTypeExtension {
@Shadow
@Final
private boolean sortOnUpload;
@Unique
private DrawBufferSet flywheel$drawBufferSet;

View File

@ -8,7 +8,7 @@ import com.jozufozu.flywheel.extension.VertexFormatExtension;
import com.mojang.blaze3d.vertex.VertexFormat;
@Mixin(VertexFormat.class)
public class VertexFormatMixin implements VertexFormatExtension {
abstract class VertexFormatMixin implements VertexFormatExtension {
@Unique
private VertexListProvider flywheel$vertexListProvider;

View File

@ -10,7 +10,7 @@ import com.mojang.blaze3d.platform.GlStateManager;
import net.minecraft.client.renderer.LevelRenderer;
@Mixin(LevelRenderer.class)
public class FixFabulousDepthMixin {
abstract class FixFabulousDepthMixin {
@Inject(method = "renderLevel", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/PostChain;process(F)V", ordinal = 1))
private void flywheel$disableTransparencyShaderDepth(CallbackInfo ci) {
GlStateManager._depthMask(false);

View File

@ -10,7 +10,7 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import com.mojang.blaze3d.vertex.PoseStack;
@Mixin(PoseStack.class)
public class FixNormalScalingMixin {
abstract class FixNormalScalingMixin {
/**
* Minecraft negates the normal matrix if all scales are equal and negative, but
* does not return afterward. This allows the rest of the method's logic to be

View File

@ -12,7 +12,7 @@ import net.minecraft.client.renderer.blockentity.BlockEntityRenderer;
import net.minecraft.world.level.block.entity.BlockEntity;
@Mixin(value = ChunkBuilderMeshingTask.class, remap = false)
public class ChunkBuilderMeshingTaskMixin {
abstract class ChunkBuilderMeshingTaskMixin {
@Redirect(method = "execute", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/blockentity/BlockEntityRenderDispatcher;getRenderer(Lnet/minecraft/world/level/block/entity/BlockEntity;)Lnet/minecraft/client/renderer/blockentity/BlockEntityRenderer;", remap = true))
private BlockEntityRenderer<?> flywheel$redirectGetRenderer(BlockEntityRenderDispatcher dispatcher, BlockEntity blockEntity) {
if (VisualizationHelper.tryAddBlockEntity(blockEntity)) {
@ -20,4 +20,4 @@ public class ChunkBuilderMeshingTaskMixin {
}
return dispatcher.getRenderer(blockEntity);
}
}
}

View File

@ -11,7 +11,7 @@ import com.jozufozu.flywheel.impl.visualization.VisualizationHelper;
import net.minecraft.world.level.block.entity.BlockEntity;
@Mixin(targets = "net.minecraft.client.renderer.chunk.ChunkRenderDispatcher$RenderChunk$RebuildTask")
public class ChunkRebuildHooksMixin {
abstract class ChunkRebuildHooksMixin {
@Inject(method = "handleBlockEntity(Lnet/minecraft/client/renderer/chunk/ChunkRenderDispatcher$RenderChunk$RebuildTask$CompileResults;Lnet/minecraft/world/level/block/entity/BlockEntity;)V", at = @At("HEAD"), cancellable = true)
private void flywheel$tryAddBlockEntity(@Coerce Object compileResults, BlockEntity blockEntity, CallbackInfo ci) {
if (VisualizationHelper.tryAddBlockEntity(blockEntity)) {

View File

@ -14,7 +14,7 @@ import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.chunk.LevelChunk;
@Mixin(LevelChunk.class)
public class VisualAddMixin {
abstract class VisualAddMixin {
@Shadow
@Final
Level level;

View File

@ -13,7 +13,7 @@ import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.entity.BlockEntity;
@Mixin(BlockEntity.class)
public class VisualRemoveMixin {
abstract class VisualRemoveMixin {
@Shadow
@Nullable
protected Level level;

View File

@ -15,7 +15,7 @@ import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.state.BlockState;
@Mixin(LevelRenderer.class)
public class VisualUpdateMixin {
abstract class VisualUpdateMixin {
@Shadow
private ClientLevel level;

View File

@ -5,7 +5,7 @@ import java.util.List;
import org.joml.Vector3f;
import com.jozufozu.flywheel.api.event.ReloadRenderersEvent;
import com.jozufozu.flywheel.api.event.ReloadLevelRendererEvent;
import com.jozufozu.flywheel.api.event.RenderStage;
import com.jozufozu.flywheel.api.task.Plan;
import com.jozufozu.flywheel.api.visual.Effect;
@ -65,7 +65,7 @@ public class ExampleEffect implements Effect {
trySpawnNewEffect();
}
public static void onReload(ReloadRenderersEvent event) {
public static void onReloadLevelRenderer(ReloadLevelRendererEvent event) {
ALL_EFFECTS.clear();
}

View File

@ -11,8 +11,10 @@
"EntityTypeMixin",
"FogUpdateMixin",
"GlStateManagerMixin",
"LevelMixin",
"LevelRendererMixin",
"LightUpdateMixin",
"MinecraftMixin",
"PoseStackMixin",
"RenderTypeMixin",
"VertexFormatMixin",