TaskEngine and batching changes

- Rename TaskEngine to TaskExecutor
- Make TaskExecutor extend Executor
- Simplify ParallelTaskExecutor
- Move WorkGroup code to separate class
- Fix CPUInstancers being updated too late
- Do not unnecessarily clear memory when preparing DrawBuffer
- Simplify BatchingDrawTracker and DrawBufferSet by storing RenderType
in DrawBuffer
- Move all additional Mojang matrix operations to MatrixUtil and expose
fields using accessors
- Remove Color
- Remove WeakHashSet
This commit is contained in:
PepperCode1 2023-03-28 10:58:22 -07:00
parent 6add6c43b1
commit 95bd36b90d
42 changed files with 665 additions and 1166 deletions

View file

@ -4,7 +4,7 @@ import org.jetbrains.annotations.Nullable;
import org.slf4j.Logger;
import com.jozufozu.flywheel.api.FlywheelLevel;
import com.jozufozu.flywheel.backend.instancing.ParallelTaskEngine;
import com.jozufozu.flywheel.backend.instancing.ParallelTaskExecutor;
import com.jozufozu.flywheel.config.FlwConfig;
import com.jozufozu.flywheel.core.BackendTypes;
import com.mojang.logging.LogUtils;
@ -20,7 +20,7 @@ public class Backend {
private static BackendType TYPE;
private static ParallelTaskEngine EXECUTOR;
private static ParallelTaskExecutor EXECUTOR;
private static final Loader LOADER = new Loader();
@ -35,9 +35,9 @@ public class Backend {
* Get a thread pool for running Flywheel related work in parallel.
* @return A global Flywheel thread pool.
*/
public static ParallelTaskEngine getTaskEngine() {
public static ParallelTaskExecutor getTaskExecutor() {
if (EXECUTOR == null) {
EXECUTOR = new ParallelTaskEngine("Flywheel");
EXECUTOR = new ParallelTaskExecutor("Flywheel");
EXECUTOR.startWorkers();
}

View file

@ -74,7 +74,7 @@ public abstract class InstanceManager<T> {
* Queued updates are processed.
* </p>
*/
public void tick(TaskEngine taskEngine, double cameraX, double cameraY, double cameraZ) {
public void tick(TaskExecutor executor, double cameraX, double cameraY, double cameraZ) {
tick.tick();
processQueuedUpdates();
@ -84,7 +84,7 @@ public abstract class InstanceManager<T> {
int cZ = (int) cameraZ;
var instances = getStorage().getInstancesForTicking();
distributeWork(taskEngine, instances, instance -> tickInstance(instance, cX, cY, cZ));
distributeWork(executor, instances, instance -> tickInstance(instance, cX, cY, cZ));
}
protected void tickInstance(TickableInstance instance, int cX, int cY, int cZ) {
@ -106,7 +106,7 @@ public abstract class InstanceManager<T> {
instance.tick();
}
public void beginFrame(TaskEngine taskEngine, RenderContext context) {
public void beginFrame(TaskExecutor executor, RenderContext context) {
frame.tick();
processQueuedAdditions();
@ -118,22 +118,22 @@ public abstract class InstanceManager<T> {
FrustumIntersection culler = context.culler();
var instances = getStorage().getInstancesForUpdate();
distributeWork(taskEngine, instances, instance -> updateInstance(instance, culler, cX, cY, cZ));
distributeWork(executor, instances, instance -> updateInstance(instance, culler, cX, cY, cZ));
}
private static <I> void distributeWork(TaskEngine taskEngine, List<I> instances, Consumer<I> action) {
private static <I> void distributeWork(TaskExecutor executor, List<I> instances, Consumer<I> action) {
final int size = instances.size();
final int threadCount = taskEngine.getThreadCount();
final int threadCount = executor.getThreadCount();
if (threadCount == 1) {
taskEngine.submit(() -> instances.forEach(action));
executor.execute(() -> instances.forEach(action));
} else {
final int stride = Math.max(size / (threadCount * 2), 1);
for (int start = 0; start < size; start += stride) {
int end = Math.min(start + stride, size);
var sub = instances.subList(start, end);
taskEngine.submit(() -> sub.forEach(action));
executor.execute(() -> sub.forEach(action));
}
}
}

View file

@ -29,7 +29,7 @@ public class InstanceWorld implements AutoCloseable {
protected final InstanceManager<Entity> entities;
protected final InstanceManager<BlockEntity> blockEntities;
public final ParallelTaskEngine taskEngine;
public final ParallelTaskExecutor taskExecutor;
private final InstanceManager<Effect> effects;
public static InstanceWorld create(LevelAccessor level) {
@ -51,7 +51,7 @@ public class InstanceWorld implements AutoCloseable {
this.entities = entities;
this.blockEntities = blockEntities;
this.effects = effects;
this.taskEngine = Backend.getTaskEngine();
this.taskExecutor = Backend.getTaskExecutor();
}
public InstanceManager<Entity> getEntities() {
@ -87,15 +87,15 @@ public class InstanceWorld implements AutoCloseable {
RenderContext context = event.getContext();
boolean shifted = engine.maintainOriginCoordinate(context.camera());
taskEngine.syncPoint();
taskExecutor.syncPoint();
if (!shifted) {
blockEntities.beginFrame(taskEngine, context);
entities.beginFrame(taskEngine, context);
effects.beginFrame(taskEngine, context);
blockEntities.beginFrame(taskExecutor, context);
entities.beginFrame(taskExecutor, context);
effects.beginFrame(taskExecutor, context);
}
engine.beginFrame(taskEngine, context);
engine.beginFrame(taskExecutor, context);
}
/**
@ -117,17 +117,17 @@ public class InstanceWorld implements AutoCloseable {
double y = renderViewEntity.getY();
double z = renderViewEntity.getZ();
blockEntities.tick(taskEngine, x, y, z);
entities.tick(taskEngine, x, y, z);
effects.tick(taskEngine, x, y, z);
blockEntities.tick(taskExecutor, x, y, z);
entities.tick(taskExecutor, x, y, z);
effects.tick(taskExecutor, x, y, z);
}
/**
* Draw all instances for the given stage.
*/
public void renderStage(RenderContext context, RenderStage stage) {
taskEngine.syncPoint();
engine.renderStage(taskEngine, context, stage);
taskExecutor.syncPoint();
engine.renderStage(taskExecutor, context, stage);
}
/**

View file

@ -1,283 +0,0 @@
package com.jozufozu.flywheel.backend.instancing;
import java.util.ArrayList;
import java.util.Deque;
import java.util.List;
import java.util.concurrent.ConcurrentLinkedDeque;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Consumer;
import java.util.stream.Stream;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.jozufozu.flywheel.Flywheel;
import com.jozufozu.flywheel.backend.instancing.batching.WaitGroup;
import net.minecraft.util.Mth;
// https://github.com/CaffeineMC/sodium-fabric/blob/5d364ed5ba63f9067fcf72a078ca310bff4db3e9/src/main/java/me/jellysquid/mods/sodium/client/render/chunk/compile/ChunkBuilder.java
public class ParallelTaskEngine implements TaskEngine {
private static final Logger LOGGER = LoggerFactory.getLogger("BatchExecutor");
private final String name;
/**
* If set to false, the engine will shut down.
*/
private final AtomicBoolean running = new AtomicBoolean(false);
private final WaitGroup wg = new WaitGroup();
private final Deque<Runnable> syncTasks = new ConcurrentLinkedDeque<>();
private final Deque<Runnable> jobQueue = new ConcurrentLinkedDeque<>();
private final List<Thread> threads = new ArrayList<>();
private final Object jobNotifier = new Object();
private final int threadCount;
public ParallelTaskEngine(String name) {
this.name = name;
threadCount = getOptimalThreadCount();
}
@Override
public int getThreadCount() {
return threadCount;
}
public WorkGroupBuilder group(String name) {
return new WorkGroupBuilder(name);
}
/**
* Spawns a number of work-stealing threads to process results in the build queue. If the builder is already
* running, this method does nothing and exits.
*/
public void startWorkers() {
if (this.running.getAndSet(true)) {
return;
}
if (!this.threads.isEmpty()) {
throw new IllegalStateException("Threads are still alive while in the STOPPED state");
}
for (int i = 0; i < this.threadCount; i++) {
Thread thread = new Thread(new WorkerRunnable(), name + " " + i);
thread.setPriority(Math.max(0, Thread.NORM_PRIORITY - 2));
thread.start();
this.threads.add(thread);
}
LOGGER.info("Started {} worker threads", this.threads.size());
}
public void stopWorkers() {
if (!this.running.getAndSet(false)) {
return;
}
if (this.threads.isEmpty()) {
throw new IllegalStateException("No threads are alive but the executor is in the RUNNING state");
}
synchronized (this.jobNotifier) {
this.jobNotifier.notifyAll();
}
try {
for (Thread thread : this.threads) {
thread.join();
}
} catch (InterruptedException ignored) {
}
this.threads.clear();
this.jobQueue.clear();
}
/**
* Submit a task to the pool.
*/
@Override
public void submit(@NotNull Runnable command) {
this.jobQueue.add(command);
this.wg.add(1);
synchronized (this.jobNotifier) {
this.jobNotifier.notify();
}
}
/**
* Wait for all running jobs to finish.
*/
@Override
public void syncPoint() {
Runnable job;
// Finish everyone else's work...
while ((job = this.jobQueue.pollLast()) != null) {
processTask(job);
}
// and wait for any stragglers.
try {
this.wg.await();
} catch (InterruptedException ignored) {
}
while ((job = this.syncTasks.pollLast()) != null) {
job.run();
}
}
@Nullable
private Runnable getNextTask() {
Runnable job = this.jobQueue.pollFirst();
if (job == null) {
synchronized (ParallelTaskEngine.this.jobNotifier) {
try {
ParallelTaskEngine.this.jobNotifier.wait();
} catch (InterruptedException ignored) {
}
}
}
return job;
}
// TODO: job context
private void processTask(Runnable job) {
try {
job.run();
} catch (Exception e) {
Flywheel.LOGGER.error("Error running job", e);
} finally {
ParallelTaskEngine.this.wg.done();
}
}
/**
* Returns the "optimal" number of threads to be used for chunk build tasks. This will always return at least one
* thread.
*/
private static int getOptimalThreadCount() {
return Mth.clamp(Math.max(getMaxThreadCount() / 3, getMaxThreadCount() - 6), 1, 10);
}
private static int getMaxThreadCount() {
return Runtime.getRuntime().availableProcessors();
}
private class WorkerRunnable implements Runnable {
private final AtomicBoolean running = ParallelTaskEngine.this.running;
@Override
public void run() {
// Run until the chunk builder shuts down
while (this.running.get()) {
Runnable job = ParallelTaskEngine.this.getNextTask();
if (job == null) {
continue;
}
ParallelTaskEngine.this.processTask(job);
}
}
}
public class WorkGroupBuilder {
final String name;
@Nullable
Runnable finalizer;
Stream<Runnable> tasks;
public WorkGroupBuilder(String name) {
this.name = name;
}
public <T> WorkGroupBuilder addTasks(Stream<T> iterable, Consumer<T> consumer) {
return addTasks(iterable.map(it -> () -> consumer.accept(it)));
}
public WorkGroupBuilder addTasks(Stream<Runnable> tasks) {
if (this.tasks == null) {
this.tasks = tasks;
} else {
this.tasks = Stream.concat(this.tasks, tasks);
}
return this;
}
public WorkGroupBuilder onComplete(Runnable runnable) {
this.finalizer = runnable;
return this;
}
public void submit() {
if (this.tasks == null) {
return;
}
WorkGroup workGroup = new WorkGroup(name, finalizer);
tasks.map(task -> new WorkGroupTask(workGroup, task)).forEach(ParallelTaskEngine.this::submit);
}
}
private static class WorkGroupTask implements Runnable {
private final WorkGroup parent;
private final Runnable wrapped;
public WorkGroupTask(WorkGroup parent, Runnable wrapped) {
this.parent = parent;
this.wrapped = wrapped;
this.parent.running.incrementAndGet();
}
@Override
public void run() {
this.wrapped.run();
this.parent.oneDown();
}
}
private class WorkGroup {
final String name;
final Runnable finalizer;
final AtomicInteger running = new AtomicInteger(0);
public WorkGroup(String name, @Nullable Runnable finalizer) {
this.name = name;
this.finalizer = finalizer;
}
public void oneDown() {
if (finalizer != null) {
if (running.decrementAndGet() == 0) {
ParallelTaskEngine.this.syncTasks.add(finalizer);
}
}
}
}
}

View file

@ -0,0 +1,212 @@
package com.jozufozu.flywheel.backend.instancing;
import java.util.ArrayList;
import java.util.Deque;
import java.util.List;
import java.util.concurrent.ConcurrentLinkedDeque;
import java.util.concurrent.atomic.AtomicBoolean;
import org.jetbrains.annotations.Nullable;
import org.slf4j.Logger;
import com.jozufozu.flywheel.Flywheel;
import com.mojang.logging.LogUtils;
import net.minecraft.util.Mth;
// https://github.com/CaffeineMC/sodium-fabric/blob/5d364ed5ba63f9067fcf72a078ca310bff4db3e9/src/main/java/me/jellysquid/mods/sodium/client/render/chunk/compile/ChunkBuilder.java
// https://stackoverflow.com/questions/29655531
public class ParallelTaskExecutor implements TaskExecutor {
private static final Logger LOGGER = LogUtils.getLogger();
private final String name;
private final int threadCount;
/**
* If set to false, the executor will shut down.
*/
private final AtomicBoolean running = new AtomicBoolean(false);
/**
* Synchronized via {@link #tasksCompletedNotifier}.
*/
private int incompleteTaskCounter = 0;
private final List<WorkerThread> threads = new ArrayList<>();
private final Deque<Runnable> taskQueue = new ConcurrentLinkedDeque<>();
private final Object taskNotifier = new Object();
private final Object tasksCompletedNotifier = new Object();
public ParallelTaskExecutor(String name) {
this.name = name;
threadCount = getOptimalThreadCount();
}
@Override
public int getThreadCount() {
return threadCount;
}
/**
* Spawns a number of work-stealing threads to process results in the task queue. If the executor is already
* running, this method does nothing and exits.
*/
public void startWorkers() {
if (running.getAndSet(true)) {
return;
}
if (!threads.isEmpty()) {
throw new IllegalStateException("Threads are still alive while in the STOPPED state");
}
for (int i = 0; i < threadCount; i++) {
WorkerThread thread = new WorkerThread(name + " Task Executor #" + i);
thread.setPriority(Mth.clamp(Thread.NORM_PRIORITY - 2, Thread.MIN_PRIORITY, Thread.MAX_PRIORITY));
thread.start();
threads.add(thread);
}
LOGGER.info("Started {} worker threads", threads.size());
}
public void stopWorkers() {
if (!running.getAndSet(false)) {
return;
}
if (threads.isEmpty()) {
throw new IllegalStateException("No threads are alive but the executor is in the RUNNING state");
}
LOGGER.info("Stopping worker threads");
// Notify all worker threads to wake up, where they will then terminate
synchronized (taskNotifier) {
taskNotifier.notifyAll();
}
// Wait for every remaining thread to terminate
for (Thread thread : threads) {
try {
thread.join();
} catch (InterruptedException e) {
//
}
}
threads.clear();
taskQueue.clear();
synchronized (tasksCompletedNotifier) {
incompleteTaskCounter = 0;
tasksCompletedNotifier.notifyAll();
}
}
@Override
public void execute(Runnable task) {
if (!running.get()) {
throw new IllegalStateException("Executor is stopped");
}
taskQueue.add(task);
synchronized (tasksCompletedNotifier) {
incompleteTaskCounter++;
}
synchronized (taskNotifier) {
taskNotifier.notify();
}
}
/**
* Wait for all running tasks to finish.
*/
@Override
public void syncPoint() {
Runnable task;
// Finish everyone else's work...
while ((task = taskQueue.pollLast()) != null) {
processTask(task);
}
// and wait for any stragglers.
synchronized (tasksCompletedNotifier) {
while (incompleteTaskCounter > 0) {
try {
tasksCompletedNotifier.wait();
} catch (InterruptedException e) {
//
}
}
}
}
@Nullable
private Runnable getNextTask() {
Runnable task = taskQueue.pollFirst();
if (task == null) {
synchronized (taskNotifier) {
try {
taskNotifier.wait();
} catch (InterruptedException e) {
//
}
}
}
return task;
}
// TODO: task context
private void processTask(Runnable task) {
try {
task.run();
} catch (Exception e) {
Flywheel.LOGGER.error("Error running task", e);
} finally {
synchronized (tasksCompletedNotifier) {
if (--incompleteTaskCounter == 0) {
tasksCompletedNotifier.notifyAll();
}
}
}
}
/**
* Returns the "optimal" number of threads to be used for tasks. This will always return at least one thread.
*/
private static int getOptimalThreadCount() {
return Mth.clamp(Math.max(getMaxThreadCount() / 3, getMaxThreadCount() - 6), 1, 10);
}
private static int getMaxThreadCount() {
return Runtime.getRuntime().availableProcessors();
}
private class WorkerThread extends Thread {
private final AtomicBoolean running = ParallelTaskExecutor.this.running;
public WorkerThread(String name) {
super(name);
}
@Override
public void run() {
// Run until the executor shuts down
while (running.get()) {
Runnable task = getNextTask();
if (task == null) {
continue;
}
processTask(task);
}
}
}
}

View file

@ -7,9 +7,9 @@ import net.minecraft.client.Camera;
public interface RenderDispatcher {
void beginFrame(TaskEngine taskEngine, RenderContext context);
void beginFrame(TaskExecutor executor, RenderContext context);
void renderStage(TaskEngine taskEngine, RenderContext context, RenderStage stage);
void renderStage(TaskExecutor executor, RenderContext context, RenderStage stage);
/**
* Maintain the integer origin coordinate to be within a certain distance from the camera in all directions,

View file

@ -1,26 +0,0 @@
package com.jozufozu.flywheel.backend.instancing;
import org.jetbrains.annotations.NotNull;
public class SerialTaskEngine implements TaskEngine {
public static final SerialTaskEngine INSTANCE = new SerialTaskEngine();
private SerialTaskEngine() {
}
@Override
public void submit(@NotNull Runnable command) {
command.run();
}
@Override
public void syncPoint() {
// noop
}
@Override
public int getThreadCount() {
return 1;
}
}

View file

@ -0,0 +1,23 @@
package com.jozufozu.flywheel.backend.instancing;
public class SerialTaskExecutor implements TaskExecutor {
public static final SerialTaskExecutor INSTANCE = new SerialTaskExecutor();
private SerialTaskExecutor() {
}
@Override
public void execute(Runnable task) {
task.run();
}
@Override
public void syncPoint() {
// noop
}
@Override
public int getThreadCount() {
return 1;
}
}

View file

@ -1,14 +0,0 @@
package com.jozufozu.flywheel.backend.instancing;
import org.jetbrains.annotations.NotNull;
public interface TaskEngine {
void submit(@NotNull Runnable command);
/**
* Wait for all running jobs to finish.
*/
void syncPoint();
int getThreadCount();
}

View file

@ -0,0 +1,12 @@
package com.jozufozu.flywheel.backend.instancing;
import java.util.concurrent.Executor;
public interface TaskExecutor extends Executor {
/**
* Wait for all running tasks to finish.
*/
void syncPoint();
int getThreadCount();
}

View file

@ -0,0 +1,72 @@
package com.jozufozu.flywheel.backend.instancing;
import java.util.Iterator;
import java.util.concurrent.Executor;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Consumer;
import java.util.stream.Stream;
import org.jetbrains.annotations.Nullable;
public class WorkGroup {
public static void run(Iterator<Runnable> tasks, Executor executor) {
tasks.forEachRemaining(executor::execute);
}
public static void run(Iterator<Runnable> tasks, @Nullable Runnable finalizer, Executor executor) {
if (finalizer == null) {
run(tasks, executor);
return;
}
AtomicInteger incompleteTaskCounter = new AtomicInteger(0);
tasks.forEachRemaining(task -> {
incompleteTaskCounter.incrementAndGet();
executor.execute(() -> {
task.run();
if (incompleteTaskCounter.decrementAndGet() == 0) {
executor.execute(finalizer);
}
});
});
}
public static Builder builder() {
return new Builder();
}
public static class Builder {
@Nullable
private Runnable finalizer;
private Stream<Runnable> tasks;
public Builder() {
}
public <T> Builder addTasks(Stream<T> iterable, Consumer<T> consumer) {
return addTasks(iterable.map(it -> () -> consumer.accept(it)));
}
public Builder addTasks(Stream<Runnable> tasks) {
if (this.tasks == null) {
this.tasks = tasks;
} else {
this.tasks = Stream.concat(this.tasks, tasks);
}
return this;
}
public Builder onComplete(Runnable runnable) {
this.finalizer = runnable;
return this;
}
public void execute(Executor executor) {
if (tasks == null) {
return;
}
run(tasks.iterator(), finalizer, executor);
}
}
}

View file

@ -1,7 +1,8 @@
package com.jozufozu.flywheel.backend.instancing.batching;
import java.util.EnumMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import com.jozufozu.flywheel.api.RenderStage;
@ -12,7 +13,15 @@ import com.mojang.blaze3d.vertex.BufferBuilder;
import net.minecraft.client.renderer.RenderType;
public class BatchingDrawTracker {
private final Set<RenderType> activeTypes = new HashSet<>();
private static final RenderStage[] RENDER_STAGES = RenderStage.values();
private final Map<RenderStage, Set<DrawBuffer>> activeBuffers = new EnumMap<>(RenderStage.class);
{
for (RenderStage stage : RENDER_STAGES) {
activeBuffers.put(stage, new HashSet<>());
}
}
private final BufferBuilder scratch;
public BatchingDrawTracker() {
@ -22,22 +31,9 @@ public class BatchingDrawTracker {
}
public DrawBuffer getBuffer(RenderType renderType, RenderStage stage) {
return getBufferSet(renderType).getBuffer(stage);
}
public DrawBufferSet getBufferSet(RenderType renderType) {
activeTypes.add(renderType);
return RenderTypeExtension.getDrawBufferSet(renderType);
}
/**
* Draw and reset all DrawBuffers for the given RenderType.
* @param renderType The RenderType to draw.
*/
public void draw(RenderType renderType) {
_draw(renderType);
activeTypes.remove(renderType);
DrawBuffer buffer = RenderTypeExtension.getDrawBufferSet(renderType).getBuffer(stage);
activeBuffers.get(stage).add(buffer);
return buffer;
}
/**
@ -45,59 +41,44 @@ public class BatchingDrawTracker {
* @param stage The RenderStage to draw.
*/
public void draw(RenderStage stage) {
Iterator<RenderType> iterator = activeTypes.iterator();
while (iterator.hasNext()) {
RenderType renderType = iterator.next();
DrawBufferSet bufferSet = RenderTypeExtension.getDrawBufferSet(renderType);
DrawBuffer buffer = bufferSet.deactivateBuffer(stage);
if (buffer == null) {
continue;
}
if (bufferSet.getActiveStagesView().isEmpty()) {
iterator.remove();
}
_draw(buffer, renderType);
Set<DrawBuffer> buffers = activeBuffers.get(stage);
for (DrawBuffer buffer : buffers) {
_draw(buffer);
buffer.reset();
}
buffers.clear();
}
/**
* Draw and reset all active DrawBuffers.
*/
public void drawAll() {
for (RenderType renderType : activeTypes) {
_draw(renderType);
}
activeTypes.clear();
}
private void _draw(RenderType renderType) {
DrawBufferSet bufferSet = RenderTypeExtension.getDrawBufferSet(renderType);
for (RenderStage stage : bufferSet.getActiveStagesView()) {
DrawBuffer buffer = bufferSet.deactivateBuffer(stage);
_draw(buffer, renderType);
for (Set<DrawBuffer> buffers : activeBuffers.values()) {
for (DrawBuffer buffer : buffers) {
_draw(buffer);
buffer.reset();
}
buffers.clear();
}
}
private void _draw(DrawBuffer buffer, RenderType renderType) {
private void _draw(DrawBuffer buffer) {
if (buffer.hasVertices()) {
BufferBuilderExtension scratch = (BufferBuilderExtension) this.scratch;
buffer.inject(scratch);
renderType.end(this.scratch, 0, 0, 0);
buffer.getRenderType().end(this.scratch, 0, 0, 0);
}
buffer.reset();
}
/**
* Reset all active DrawBuffers.
*/
public void reset() {
for (RenderType type : activeTypes) {
RenderTypeExtension.getDrawBufferSet(type)
.reset();
for (Set<DrawBuffer> buffers : activeBuffers.values()) {
for (DrawBuffer buffer : buffers) {
buffer.reset();
}
buffers.clear();
}
activeTypes.clear();
}
}

View file

@ -8,7 +8,7 @@ import com.jozufozu.flywheel.api.instancer.Instancer;
import com.jozufozu.flywheel.api.struct.StructType;
import com.jozufozu.flywheel.backend.instancing.Engine;
import com.jozufozu.flywheel.backend.instancing.InstanceManager;
import com.jozufozu.flywheel.backend.instancing.TaskEngine;
import com.jozufozu.flywheel.backend.instancing.TaskExecutor;
import com.jozufozu.flywheel.core.RenderContext;
import com.jozufozu.flywheel.core.model.Model;
import com.jozufozu.flywheel.util.FlwUtil;
@ -30,19 +30,19 @@ public class BatchingEngine implements Engine {
}
@Override
public void beginFrame(TaskEngine taskEngine, RenderContext context) {
public void beginFrame(TaskExecutor executor, RenderContext context) {
transformManager.flush();
Vec3 cameraPos = context.camera().getPosition();
var stack = FlwUtil.copyPoseStack(context.stack());
stack.translate(-cameraPos.x, -cameraPos.y, -cameraPos.z);
// TODO: async task engine barriers
taskEngine.syncPoint();
submitTasks(taskEngine, stack.last(), context.level());
// TODO: async task executor barriers
executor.syncPoint();
submitTasks(executor, stack.last(), context.level());
}
public void submitTasks(TaskEngine taskEngine, PoseStack.Pose matrices, ClientLevel level) {
public void submitTasks(TaskExecutor executor, PoseStack.Pose matrices, ClientLevel level) {
for (var transformSetEntry : transformManager.getTransformSetsView().entrySet()) {
var stage = transformSetEntry.getKey();
var transformSet = transformSetEntry.getValue();
@ -53,6 +53,7 @@ public class BatchingEngine implements Engine {
int vertices = 0;
for (var transformCall : transformCalls) {
transformCall.setup();
vertices += transformCall.getTotalVertexCount();
}
@ -65,7 +66,7 @@ public class BatchingEngine implements Engine {
int startVertex = 0;
for (var transformCall : transformCalls) {
transformCall.submitTasks(taskEngine, buffer, startVertex, matrices, level);
transformCall.submitTasks(executor, buffer, startVertex, matrices, level);
startVertex += transformCall.getTotalVertexCount();
}
}
@ -73,7 +74,7 @@ public class BatchingEngine implements Engine {
}
@Override
public void renderStage(TaskEngine taskEngine, RenderContext context, RenderStage stage) {
public void renderStage(TaskExecutor executor, RenderContext context, RenderStage stage) {
drawTracker.draw(stage);
}

View file

@ -99,7 +99,7 @@ public class BatchingTransformManager {
public static class TransformSet implements Iterable<Map.Entry<RenderType, Collection<TransformCall<?>>>> {
public static final TransformSet EMPTY = new TransformSet(ImmutableListMultimap.of());
final ListMultimap<RenderType, TransformCall<?>> transformCalls;
private final ListMultimap<RenderType, TransformCall<?>> transformCalls;
public TransformSet(RenderStage renderStage) {
transformCalls = ArrayListMultimap.create();

View file

@ -10,7 +10,7 @@ public class CPUInstancer<D extends InstancedPart> extends AbstractInstancer<D>
super(type);
}
void setup() {
void update() {
if (anyToRemove) {
data.removeIf(InstancedPart::isRemoved);
anyToRemove = false;

View file

@ -11,6 +11,8 @@ import com.jozufozu.flywheel.event.ReloadRenderersEvent;
import com.jozufozu.flywheel.extension.BufferBuilderExtension;
import com.mojang.blaze3d.vertex.VertexFormat;
import net.minecraft.client.renderer.RenderType;
/**
* A byte buffer that can be used to draw vertices through multiple {@link ReusableVertexList}s.
*
@ -19,6 +21,7 @@ import com.mojang.blaze3d.vertex.VertexFormat;
public class DrawBuffer {
private static final List<DrawBuffer> ALL = new ArrayList<>();
private final RenderType renderType;
private final VertexFormat format;
private final int stride;
private final VertexListProvider provider;
@ -29,7 +32,8 @@ public class DrawBuffer {
private boolean prepared;
private int vertexCount;
DrawBuffer(VertexFormat format, int stride, VertexListProvider provider) {
DrawBuffer(RenderType renderType, VertexFormat format, int stride, VertexListProvider provider) {
this.renderType = renderType;
this.format = format;
this.stride = stride;
this.provider = provider;
@ -62,7 +66,6 @@ public class DrawBuffer {
buffer = memory.asBuffer();
}
memory.clear();
prepared = true;
}
@ -90,6 +93,10 @@ public class DrawBuffer {
bufferBuilder.flywheel$injectForRender(buffer, format, vertexCount);
}
public RenderType getRenderType() {
return renderType;
}
public VertexFormat getVertexFormat() {
return format;
}

View file

@ -1,66 +1,33 @@
package com.jozufozu.flywheel.backend.instancing.batching;
import java.util.Collections;
import java.util.EnumMap;
import java.util.EnumSet;
import java.util.Map;
import java.util.Set;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.Nullable;
import com.jozufozu.flywheel.api.RenderStage;
import com.jozufozu.flywheel.api.vertex.VertexListProvider;
import com.mojang.blaze3d.vertex.VertexFormat;
import net.minecraft.client.renderer.RenderType;
public class DrawBufferSet {
private final RenderType renderType;
private final VertexFormat format;
private final int stride;
private final VertexListProvider provider;
private final Map<RenderStage, DrawBuffer> buffers = new EnumMap<>(RenderStage.class);
private final Set<RenderStage> activeStages = EnumSet.noneOf(RenderStage.class);
private final Set<RenderStage> activeStagesView = Collections.unmodifiableSet(activeStages);
@ApiStatus.Internal
public DrawBufferSet(VertexFormat format) {
this.format = format;
public DrawBufferSet(RenderType renderType) {
this.renderType = renderType;
format = renderType.format();
stride = format.getVertexSize();
provider = VertexListProvider.get(format);
}
public Set<RenderStage> getActiveStagesView() {
return activeStagesView;
}
public DrawBuffer getBuffer(RenderStage stage) {
activeStages.add(stage);
return buffers.computeIfAbsent(stage, $ -> createBuffer());
}
@Nullable
public DrawBuffer deactivateBuffer(RenderStage stage) {
if (activeStages.remove(stage)) {
return buffers.get(stage);
}
return null;
}
public void reset(RenderStage stage) {
if (activeStages.remove(stage)) {
buffers.get(stage).reset();
}
}
public void reset() {
for (RenderStage stage : activeStages) {
buffers.get(stage).reset();
}
activeStages.clear();
}
private DrawBuffer createBuffer() {
return new DrawBuffer(format, stride, provider);
return buffers.computeIfAbsent(stage, $ -> new DrawBuffer(renderType, format, stride, provider));
}
}

View file

@ -7,7 +7,7 @@ import com.jozufozu.flywheel.api.material.Material;
import com.jozufozu.flywheel.api.struct.StructType;
import com.jozufozu.flywheel.api.vertex.MutableVertexList;
import com.jozufozu.flywheel.api.vertex.ReusableVertexList;
import com.jozufozu.flywheel.backend.instancing.TaskEngine;
import com.jozufozu.flywheel.backend.instancing.TaskExecutor;
import com.mojang.blaze3d.vertex.PoseStack;
import com.mojang.math.Matrix3f;
import com.mojang.math.Matrix4f;
@ -37,9 +37,11 @@ public class TransformCall<D extends InstancedPart> {
return meshVertexCount * instancer.getInstanceCount();
}
void submitTasks(TaskEngine pool, DrawBuffer buffer, int startVertex, PoseStack.Pose matrices, ClientLevel level) {
instancer.setup();
void setup() {
instancer.update();
}
void submitTasks(TaskExecutor executor, DrawBuffer buffer, int startVertex, PoseStack.Pose matrices, ClientLevel level) {
int instances = instancer.getInstanceCount();
while (instances > 0) {
@ -51,7 +53,7 @@ public class TransformCall<D extends InstancedPart> {
ReusableVertexList sub = buffer.slice(startVertex, vertexCount);
startVertex += vertexCount;
pool.submit(() -> transformRange(sub, start, end, matrices, level));
executor.execute(() -> transformRange(sub, start, end, matrices, level));
}
}
@ -82,10 +84,10 @@ public class TransformCall<D extends InstancedPart> {
vertexList.ptr(anchorPtr);
vertexList.vertexCount(totalVertexCount);
material.getVertexTransformer().transform(vertexList, level);
applyPoseStack(vertexList, matrices);
applyMatrices(vertexList, matrices);
}
private static void applyPoseStack(MutableVertexList vertexList, PoseStack.Pose matrices) {
private static void applyMatrices(MutableVertexList vertexList, PoseStack.Pose matrices) {
Vector4f pos = new Vector4f();
Vector3f normal = new Vector3f();

View file

@ -1,34 +0,0 @@
package com.jozufozu.flywheel.backend.instancing.batching;
import java.util.concurrent.atomic.AtomicInteger;
// https://stackoverflow.com/questions/29655531
public class WaitGroup {
private final AtomicInteger counter = new AtomicInteger(0);
public synchronized void add(int i) {
if (i == 0) {
return;
}
if (i == 1) {
this.counter.incrementAndGet();
} else {
this.counter.addAndGet(i);
}
}
public synchronized void done() {
if (this.counter.decrementAndGet() == 0) {
this.notifyAll();
}
}
public synchronized void await() throws InterruptedException {
while (this.counter.get() > 0) {
this.wait();
}
}
}

View file

@ -2,6 +2,7 @@ package com.jozufozu.flywheel.backend.instancing.indirect;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import org.lwjgl.opengl.GL32;
@ -14,10 +15,10 @@ import com.jozufozu.flywheel.backend.gl.GlStateTracker;
import com.jozufozu.flywheel.backend.gl.GlTextureUnit;
import com.jozufozu.flywheel.backend.instancing.Engine;
import com.jozufozu.flywheel.backend.instancing.InstanceManager;
import com.jozufozu.flywheel.backend.instancing.TaskEngine;
import com.jozufozu.flywheel.backend.instancing.TaskExecutor;
import com.jozufozu.flywheel.core.RenderContext;
import com.jozufozu.flywheel.core.model.Model;
import com.jozufozu.flywheel.util.WeakHashSet;
import com.jozufozu.flywheel.util.FlwUtil;
import com.mojang.blaze3d.systems.RenderSystem;
import net.minecraft.client.Camera;
@ -34,7 +35,7 @@ public class IndirectEngine implements Engine {
/**
* The set of instance managers that are attached to this engine.
*/
private final WeakHashSet<InstanceManager<?>> instanceManagers = new WeakHashSet<>();
private final Set<InstanceManager<?>> instanceManagers = FlwUtil.createWeakHashSet();
protected final ContextShader context;
protected final int sqrMaxOriginDistance;
@ -52,14 +53,14 @@ public class IndirectEngine implements Engine {
}
@Override
public void beginFrame(TaskEngine taskEngine, RenderContext context) {
public void beginFrame(TaskExecutor executor, RenderContext context) {
try (var restoreState = GlStateTracker.getRestoreState()) {
drawManager.flush();
}
}
@Override
public void renderStage(TaskEngine taskEngine, RenderContext context, RenderStage stage) {
public void renderStage(TaskExecutor executor, RenderContext context, RenderStage stage) {
try (var restoreState = GlStateTracker.getRestoreState()) {
setup();

View file

@ -99,7 +99,7 @@ public class InstancingDrawManager {
public static final DrawSet EMPTY = new DrawSet(ImmutableListMultimap.of());
final ListMultimap<ShaderState, DrawCall> drawCalls;
private final ListMultimap<ShaderState, DrawCall> drawCalls;
public DrawSet(RenderStage renderStage) {
drawCalls = ArrayListMultimap.create();

View file

@ -2,6 +2,7 @@ package com.jozufozu.flywheel.backend.instancing.instancing;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import org.lwjgl.opengl.GL32;
@ -15,12 +16,12 @@ import com.jozufozu.flywheel.backend.gl.GlTextureUnit;
import com.jozufozu.flywheel.backend.instancing.Engine;
import com.jozufozu.flywheel.backend.instancing.InstanceManager;
import com.jozufozu.flywheel.backend.instancing.PipelineCompiler;
import com.jozufozu.flywheel.backend.instancing.TaskEngine;
import com.jozufozu.flywheel.backend.instancing.TaskExecutor;
import com.jozufozu.flywheel.core.Components;
import com.jozufozu.flywheel.core.RenderContext;
import com.jozufozu.flywheel.core.model.Model;
import com.jozufozu.flywheel.core.uniform.UniformBuffer;
import com.jozufozu.flywheel.util.WeakHashSet;
import com.jozufozu.flywheel.util.FlwUtil;
import com.mojang.blaze3d.systems.RenderSystem;
import net.minecraft.client.Camera;
@ -37,7 +38,7 @@ public class InstancingEngine implements Engine {
/**
* The set of instance managers that are attached to this engine.
*/
private final WeakHashSet<InstanceManager<?>> instanceManagers = new WeakHashSet<>();
private final Set<InstanceManager<?>> instanceManagers = FlwUtil.createWeakHashSet();
protected final ContextShader context;
protected final int sqrMaxOriginDistance;
@ -55,14 +56,14 @@ public class InstancingEngine implements Engine {
}
@Override
public void beginFrame(TaskEngine taskEngine, RenderContext context) {
public void beginFrame(TaskExecutor executor, RenderContext context) {
try (var restoreState = GlStateTracker.getRestoreState()) {
drawManager.flush();
}
}
@Override
public void renderStage(TaskEngine taskEngine, RenderContext context, RenderStage stage) {
public void renderStage(TaskExecutor executor, RenderContext context, RenderStage stage) {
var drawSet = drawManager.get(stage);
if (drawSet.isEmpty()) {

View file

@ -2,7 +2,7 @@ package com.jozufozu.flywheel.core;
import org.jetbrains.annotations.NotNull;
import com.jozufozu.flywheel.extension.Matrix4fExtension;
import com.jozufozu.flywheel.util.MatrixUtil;
import com.jozufozu.flywheel.util.joml.FrustumIntersection;
import com.mojang.blaze3d.vertex.PoseStack;
import com.mojang.math.Matrix4f;
@ -23,7 +23,7 @@ public record RenderContext(LevelRenderer renderer, ClientLevel level, PoseStack
}
public static FrustumIntersection createCuller(Matrix4f viewProjection, float camX, float camY, float camZ) {
com.jozufozu.flywheel.util.joml.Matrix4f proj = Matrix4fExtension.clone(viewProjection);
com.jozufozu.flywheel.util.joml.Matrix4f proj = MatrixUtil.toJoml(viewProjection);
proj.translate(camX, camY, camZ);

View file

@ -42,7 +42,7 @@ public interface Mesh {
/**
* Write this mesh into a vertex list. Vertices with index {@literal <}0 or {@literal >=}{@link #getVertexCount()} will not be
* modified.
* read or modified.
* @param vertexList The vertex list to which data is written to.
*/
void write(MutableVertexList vertexList);

View file

@ -2,7 +2,6 @@ package com.jozufozu.flywheel.core.structs;
import com.jozufozu.flywheel.api.instancer.InstancedPart;
import com.jozufozu.flywheel.api.struct.StructType;
import com.jozufozu.flywheel.util.Color;
import net.minecraft.client.renderer.LightTexture;
@ -39,15 +38,6 @@ public abstract class ColoredLitPart extends InstancedPart implements FlatLit<Co
return LightTexture.pack(this.blockLight, this.skyLight);
}
public ColoredLitPart setColor(Color color) {
this.r = (byte) color.getRed();
this.g = (byte) color.getGreen();
this.b = (byte) color.getBlue();
this.a = (byte) color.getAlpha();
markDirty();
return this;
}
public ColoredLitPart setColor(int color) {
return setColor(color, false);
}

View file

@ -1,7 +1,7 @@
package com.jozufozu.flywheel.core.structs.transformed;
import com.jozufozu.flywheel.core.structs.ColoredLitWriter;
import com.jozufozu.flywheel.extension.MatrixWrite;
import com.jozufozu.flywheel.util.MatrixUtil;
public class TransformedWriter extends ColoredLitWriter<TransformedPart> {
public static final TransformedWriter INSTANCE = new TransformedWriter();
@ -9,8 +9,8 @@ public class TransformedWriter extends ColoredLitWriter<TransformedPart> {
@Override
public void write(final long ptr, final TransformedPart d) {
super.write(ptr, d);
MatrixWrite.writeUnsafe(d.model, ptr + 8);
MatrixWrite.writeUnsafe(d.normal, ptr + 72);
MatrixUtil.writeUnsafe(d.model, ptr + 8);
MatrixUtil.writeUnsafe(d.normal, ptr + 72);
}
}

View file

@ -8,7 +8,7 @@ import com.jozufozu.flywheel.core.Components;
import com.jozufozu.flywheel.core.RenderContext;
import com.jozufozu.flywheel.core.source.FileResolution;
import com.jozufozu.flywheel.event.BeginFrameEvent;
import com.jozufozu.flywheel.extension.MatrixWrite;
import com.jozufozu.flywheel.util.MatrixUtil;
import net.minecraft.client.multiplayer.ClientLevel;
import net.minecraft.core.Vec3i;
@ -52,7 +52,7 @@ public class ViewProvider extends UniformProvider {
var vp = context.viewProjection().copy();
vp.multiplyWithTranslation(-camX, -camY, -camZ);
MatrixWrite.writeUnsafe(vp, ptr);
MatrixUtil.writeUnsafe(vp, ptr);
MemoryUtil.memPutFloat(ptr + 64, camX);
MemoryUtil.memPutFloat(ptr + 68, camY);
MemoryUtil.memPutFloat(ptr + 72, camZ);

View file

@ -1,16 +0,0 @@
package com.jozufozu.flywheel.extension;
import com.jozufozu.flywheel.util.joml.Matrix3f;
public interface Matrix3fExtension {
Matrix3f flywheel$store(Matrix3f matrix);
static Matrix3f clone(com.mojang.math.Matrix3f moj) {
return ((Matrix3fExtension)(Object) moj).flywheel$store(new Matrix3f());
}
static void store(com.mojang.math.Matrix3f moj, Matrix3f joml) {
((Matrix3fExtension)(Object) moj).flywheel$store(joml);
}
}

View file

@ -1,16 +0,0 @@
package com.jozufozu.flywheel.extension;
import com.jozufozu.flywheel.util.joml.Matrix4f;
public interface Matrix4fExtension {
Matrix4f flywheel$store(Matrix4f matrix);
static Matrix4f clone(com.mojang.math.Matrix4f moj) {
return ((Matrix4fExtension)(Object) moj).flywheel$store(new Matrix4f());
}
static void store(com.mojang.math.Matrix4f moj, Matrix4f joml) {
((Matrix4fExtension)(Object) moj).flywheel$store(joml);
}
}

View file

@ -1,32 +0,0 @@
package com.jozufozu.flywheel.extension;
import java.nio.ByteBuffer;
import com.mojang.math.Matrix3f;
import com.mojang.math.Matrix4f;
/**
* @see com.jozufozu.flywheel.mixin.matrix.Matrix3fMixin
* @see com.jozufozu.flywheel.mixin.matrix.Matrix4fMixin
*/
public interface MatrixWrite {
/**
* Write the contents of this object into sequential memory starting at the given address.
*/
void flywheel$writeUnsafe(long ptr);
void flywheel$write(ByteBuffer buf);
static void write(Matrix4f matrix, ByteBuffer buf) {
((MatrixWrite) (Object) matrix).flywheel$write(buf);
}
static void writeUnsafe(Matrix4f matrix, long ptr) {
((MatrixWrite) (Object) matrix).flywheel$writeUnsafe(ptr);
}
static void writeUnsafe(Matrix3f matrix, long ptr) {
((MatrixWrite) (Object) matrix).flywheel$writeUnsafe(ptr);
}
}

View file

@ -6,8 +6,9 @@ import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.stream.Stream;
import com.jozufozu.flywheel.backend.Backend;
import com.jozufozu.flywheel.backend.instancing.ParallelTaskEngine;
import com.jozufozu.flywheel.util.WeakHashSet;
import com.jozufozu.flywheel.backend.instancing.ParallelTaskExecutor;
import com.jozufozu.flywheel.backend.instancing.WorkGroup;
import com.jozufozu.flywheel.util.FlwUtil;
import com.jozufozu.flywheel.util.WorldAttached;
import com.jozufozu.flywheel.util.box.GridAlignedBB;
import com.jozufozu.flywheel.util.box.ImmutableBox;
@ -27,7 +28,7 @@ import net.minecraft.world.level.LightLayer;
public class LightUpdater {
private static final WorldAttached<LightUpdater> LEVELS = new WorldAttached<>(LightUpdater::new);
private final ParallelTaskEngine taskEngine;
private final ParallelTaskExecutor taskExecutor;
public static LightUpdater get(LevelAccessor level) {
if (LightUpdated.receivesLightUpdates(level)) {
@ -41,12 +42,12 @@ public class LightUpdater {
private final LevelAccessor level;
private final WeakHashSet<TickingLightListener> tickingLightListeners = new WeakHashSet<>();
private final Set<TickingLightListener> tickingLightListeners = FlwUtil.createWeakHashSet();
private final WeakContainmentMultiMap<LightListener> sections = new WeakContainmentMultiMap<>();
private final WeakContainmentMultiMap<LightListener> chunks = new WeakContainmentMultiMap<>();
public LightUpdater(LevelAccessor level) {
taskEngine = Backend.getTaskEngine();
taskExecutor = Backend.getTaskExecutor();
this.level = level;
}
@ -66,14 +67,14 @@ public class LightUpdater {
private void tickParallel() {
Queue<LightListener> listeners = new ConcurrentLinkedQueue<>();
taskEngine.group("LightUpdater")
WorkGroup.builder()
.addTasks(tickingLightListeners.stream(), listener -> {
if (listener.tickLightListener()) {
listeners.add(listener);
}
})
.onComplete(() -> listeners.forEach(this::addListener))
.submit();
.execute(taskExecutor);
}
/**

View file

@ -6,7 +6,7 @@ import java.util.Set;
import java.util.WeakHashMap;
import java.util.function.LongConsumer;
import com.jozufozu.flywheel.util.WeakHashSet;
import com.jozufozu.flywheel.util.FlwUtil;
import it.unimi.dsi.fastutil.longs.Long2ObjectMap;
import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap;
@ -15,7 +15,7 @@ import it.unimi.dsi.fastutil.longs.LongSet;
public class WeakContainmentMultiMap<T> extends AbstractCollection<T> {
private final Long2ObjectMap<WeakHashSet<T>> forward;
private final Long2ObjectMap<Set<T>> forward;
private final WeakHashMap<T, LongSet> reverse;
public WeakContainmentMultiMap() {
@ -38,7 +38,7 @@ public class WeakContainmentMultiMap<T> extends AbstractCollection<T> {
LongSet containmentSet = reverse.computeIfAbsent(listener, $ -> new LongRBTreeSet());
containmentSet.forEach((LongConsumer) l -> {
WeakHashSet<T> listeners = forward.get(l);
Set<T> listeners = forward.get(l);
if (listeners != null) listeners.remove(listener);
});
@ -53,7 +53,7 @@ public class WeakContainmentMultiMap<T> extends AbstractCollection<T> {
}
public void put(long sectionPos, T listener) {
forward.computeIfAbsent(sectionPos, $ -> new WeakHashSet<>()).add(listener);
forward.computeIfAbsent(sectionPos, $ -> FlwUtil.createWeakHashSet()).add(listener);
}
@Override
@ -62,7 +62,7 @@ public class WeakContainmentMultiMap<T> extends AbstractCollection<T> {
if (containmentSet != null) {
containmentSet.forEach((LongConsumer) l -> {
WeakHashSet<T> listeners = forward.get(l);
Set<T> listeners = forward.get(l);
if (listeners != null) listeners.remove(o);
});

View file

@ -18,7 +18,7 @@ public class RenderTypeMixin implements RenderTypeExtension {
@NotNull
public DrawBufferSet flywheel$getDrawBufferSet() {
if (flywheel$drawBufferSet == null) {
flywheel$drawBufferSet = new DrawBufferSet(((RenderType) (Object) this).format());
flywheel$drawBufferSet = new DrawBufferSet((RenderType) (Object) this);
}
return flywheel$drawBufferSet;
}

View file

@ -0,0 +1,36 @@
package com.jozufozu.flywheel.mixin.matrix;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.gen.Accessor;
import com.mojang.math.Matrix3f;
@Mixin(Matrix3f.class)
public interface Matrix3fAccessor {
@Accessor("m00")
float flywheel$m00();
@Accessor("m01")
float flywheel$m01();
@Accessor("m02")
float flywheel$m02();
@Accessor("m10")
float flywheel$m10();
@Accessor("m11")
float flywheel$m11();
@Accessor("m12")
float flywheel$m12();
@Accessor("m20")
float flywheel$m20();
@Accessor("m21")
float flywheel$m21();
@Accessor("m22")
float flywheel$m22();
}

View file

@ -1,55 +0,0 @@
package com.jozufozu.flywheel.mixin.matrix;
import java.nio.ByteBuffer;
import org.lwjgl.system.MemoryUtil;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import com.jozufozu.flywheel.extension.Matrix3fExtension;
import com.jozufozu.flywheel.extension.MatrixWrite;
import com.mojang.math.Matrix3f;
@Mixin(Matrix3f.class)
public abstract class Matrix3fMixin implements MatrixWrite, Matrix3fExtension {
@Shadow protected float m00;
@Shadow protected float m01;
@Shadow protected float m02;
@Shadow protected float m10;
@Shadow protected float m11;
@Shadow protected float m12;
@Shadow protected float m20;
@Shadow protected float m21;
@Shadow protected float m22;
@Override
public void flywheel$writeUnsafe(long ptr) {
MemoryUtil.memPutFloat(ptr, m00);
MemoryUtil.memPutFloat(ptr + 4, m10);
MemoryUtil.memPutFloat(ptr + 8, m20);
MemoryUtil.memPutFloat(ptr + 12, m01);
MemoryUtil.memPutFloat(ptr + 16, m11);
MemoryUtil.memPutFloat(ptr + 20, m21);
MemoryUtil.memPutFloat(ptr + 24, m02);
MemoryUtil.memPutFloat(ptr + 28, m12);
MemoryUtil.memPutFloat(ptr + 32, m22);
}
@Override
public void flywheel$write(ByteBuffer buffer) {
buffer.putFloat(m00);
buffer.putFloat(m10);
buffer.putFloat(m20);
buffer.putFloat(m01);
buffer.putFloat(m11);
buffer.putFloat(m21);
buffer.putFloat(m02);
buffer.putFloat(m12);
buffer.putFloat(m22);
}
@Override
public com.jozufozu.flywheel.util.joml.Matrix3f flywheel$store(com.jozufozu.flywheel.util.joml.Matrix3f matrix) {
return matrix.set(m00, m10, m20, m01, m11, m21, m02, m12, m22);
}
}

View file

@ -0,0 +1,57 @@
package com.jozufozu.flywheel.mixin.matrix;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.gen.Accessor;
import com.mojang.math.Matrix4f;
@Mixin(Matrix4f.class)
public interface Matrix4fAccessor {
@Accessor("m00")
float flywheel$m00();
@Accessor("m01")
float flywheel$m01();
@Accessor("m02")
float flywheel$m02();
@Accessor("m03")
float flywheel$m03();
@Accessor("m10")
float flywheel$m10();
@Accessor("m11")
float flywheel$m11();
@Accessor("m12")
float flywheel$m12();
@Accessor("m13")
float flywheel$m13();
@Accessor("m20")
float flywheel$m20();
@Accessor("m21")
float flywheel$m21();
@Accessor("m22")
float flywheel$m22();
@Accessor("m23")
float flywheel$m23();
@Accessor("m30")
float flywheel$m30();
@Accessor("m31")
float flywheel$m31();
@Accessor("m32")
float flywheel$m32();
@Accessor("m33")
float flywheel$m33();
}

View file

@ -1,80 +0,0 @@
package com.jozufozu.flywheel.mixin.matrix;
import java.nio.ByteBuffer;
import org.lwjgl.system.MemoryUtil;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import com.jozufozu.flywheel.extension.Matrix4fExtension;
import com.jozufozu.flywheel.extension.MatrixWrite;
import com.mojang.math.Matrix4f;
@Mixin(Matrix4f.class)
public abstract class Matrix4fMixin implements MatrixWrite, Matrix4fExtension {
@Shadow protected float m00;
@Shadow protected float m01;
@Shadow protected float m02;
@Shadow protected float m03;
@Shadow protected float m10;
@Shadow protected float m11;
@Shadow protected float m12;
@Shadow protected float m13;
@Shadow protected float m20;
@Shadow protected float m21;
@Shadow protected float m22;
@Shadow protected float m23;
@Shadow protected float m30;
@Shadow protected float m31;
@Shadow protected float m32;
@Shadow protected float m33;
@Override
public void flywheel$writeUnsafe(long ptr) {
MemoryUtil.memPutFloat(ptr, m00);
MemoryUtil.memPutFloat(ptr + 4, m10);
MemoryUtil.memPutFloat(ptr + 8, m20);
MemoryUtil.memPutFloat(ptr + 12, m30);
MemoryUtil.memPutFloat(ptr + 16, m01);
MemoryUtil.memPutFloat(ptr + 20, m11);
MemoryUtil.memPutFloat(ptr + 24, m21);
MemoryUtil.memPutFloat(ptr + 28, m31);
MemoryUtil.memPutFloat(ptr + 32, m02);
MemoryUtil.memPutFloat(ptr + 36, m12);
MemoryUtil.memPutFloat(ptr + 40, m22);
MemoryUtil.memPutFloat(ptr + 44, m32);
MemoryUtil.memPutFloat(ptr + 48, m03);
MemoryUtil.memPutFloat(ptr + 52, m13);
MemoryUtil.memPutFloat(ptr + 56, m23);
MemoryUtil.memPutFloat(ptr + 60, m33);
}
@Override
public void flywheel$write(ByteBuffer buf) {
buf.putFloat(m00);
buf.putFloat(m10);
buf.putFloat(m20);
buf.putFloat(m30);
buf.putFloat(m01);
buf.putFloat(m11);
buf.putFloat(m21);
buf.putFloat(m31);
buf.putFloat(m02);
buf.putFloat(m12);
buf.putFloat(m22);
buf.putFloat(m32);
buf.putFloat(m03);
buf.putFloat(m13);
buf.putFloat(m23);
buf.putFloat(m33);
}
@Override
public com.jozufozu.flywheel.util.joml.Matrix4f flywheel$store(com.jozufozu.flywheel.util.joml.Matrix4f matrix) {
return matrix.set(
m00, m10, m20, m30,
m01, m11, m21, m31,
m02, m12, m22, m32,
m03, m13, m23, m33);
}
}

View file

@ -1,309 +0,0 @@
package com.jozufozu.flywheel.util;
import java.util.function.UnaryOperator;
import org.jetbrains.annotations.NotNull;
import com.google.common.hash.Hashing;
import com.mojang.math.Vector3f;
import net.minecraft.util.Mth;
import net.minecraft.world.phys.Vec3;
@SuppressWarnings("PointlessBitwiseExpression")
public class Color {
public final static Color TRANSPARENT_BLACK = new Color(0, 0, 0, 0).setImmutable();
public final static Color BLACK = new Color(0, 0, 0).setImmutable();
public final static Color WHITE = new Color(255, 255, 255).setImmutable();
public final static Color RED = new Color(255, 0, 0).setImmutable();
public final static Color GREEN = new Color(0, 255, 0).setImmutable();
public final static Color SPRING_GREEN = new Color(0, 255, 187).setImmutable();
protected boolean mutable = true;
protected int value;
public Color(int r, int g, int b) {
this(r, g, b, 0xff);
}
public Color(int r, int g, int b, int a) {
value = ((a & 0xff) << 24) |
((r & 0xff) << 16) |
((g & 0xff) << 8) |
((b & 0xff) << 0);
}
public Color(float r, float g, float b, float a) {
this(
(int) (0.5 + 0xff * Mth.clamp(r, 0, 1)),
(int) (0.5 + 0xff * Mth.clamp(g, 0, 1)),
(int) (0.5 + 0xff * Mth.clamp(b, 0, 1)),
(int) (0.5 + 0xff * Mth.clamp(a, 0, 1))
);
}
public Color(int rgba) {
value = rgba;
}
public Color(int rgb, boolean hasAlpha) {
if (hasAlpha) {
value = rgb;
} else {
value = rgb | 0xff_000000;
}
}
public Color copy() {
return copy(true);
}
public Color copy(boolean mutable) {
if (mutable)
return new Color(value);
else
return new Color(value).setImmutable();
}
/**
* Mark this color as immutable. Attempting to mutate this color in the future
* will instead cause a copy to be created that can me modified.
*/
public Color setImmutable() {
this.mutable = false;
return this;
}
/**
* @return the red component in the range 0-255.
* @see #getRGB
*/
public int getRed() {
return (getRGB() >> 16) & 0xff;
}
/**
* @return the green component in the range 0-255.
* @see #getRGB
*/
public int getGreen() {
return (getRGB() >> 8) & 0xff;
}
/**
* @return the blue component in the range 0-255.
* @see #getRGB
*/
public int getBlue() {
return (getRGB() >> 0) & 0xff;
}
/**
* @return the alpha component in the range 0-255.
* @see #getRGB
*/
public int getAlpha() {
return (getRGB() >> 24) & 0xff;
}
/**
* @return the red component in the range 0-1f.
*/
public float getRedAsFloat() {
return getRed() / 255f;
}
/**
* @return the green component in the range 0-1f.
*/
public float getGreenAsFloat() {
return getGreen() / 255f;
}
/**
* @return the blue component in the range 0-1f.
*/
public float getBlueAsFloat() {
return getBlue() / 255f;
}
/**
* @return the alpha component in the range 0-1f.
*/
public float getAlphaAsFloat() {
return getAlpha() / 255f;
}
/**
* Returns the RGB value representing this color
* (Bits 24-31 are alpha, 16-23 are red, 8-15 are green, 0-7 are blue).
* @return the RGB value of the color
*/
public int getRGB() {
return value;
}
public Vec3 asVector() {
return new Vec3(getRedAsFloat(), getGreenAsFloat(), getBlueAsFloat());
}
public Vector3f asVectorF() {
return new Vector3f(getRedAsFloat(), getGreenAsFloat(), getBlueAsFloat());
}
public Color setRed(int r) {
return ensureMutable().setRedUnchecked(r);
}
public Color setGreen(int g) {
return ensureMutable().setGreenUnchecked(g);
}
public Color setBlue(int b) {
return ensureMutable().setBlueUnchecked(b);
}
public Color setAlpha(int a) {
return ensureMutable().setAlphaUnchecked(a);
}
public Color setRed(float r) {
return ensureMutable().setRedUnchecked((int) (0xff * Mth.clamp(r, 0, 1)));
}
public Color setGreen(float g) {
return ensureMutable().setGreenUnchecked((int) (0xff * Mth.clamp(g, 0, 1)));
}
public Color setBlue(float b) {
return ensureMutable().setBlueUnchecked((int) (0xff * Mth.clamp(b, 0, 1)));
}
public Color setAlpha(float a) {
return ensureMutable().setAlphaUnchecked((int) (0xff * Mth.clamp(a, 0, 1)));
}
public Color scaleAlpha(float factor) {
return ensureMutable().setAlphaUnchecked((int) (getAlpha() * Mth.clamp(factor, 0, 1)));
}
public Color mixWith(Color other, float weight) {
return ensureMutable()
.setRedUnchecked((int) (getRed() + (other.getRed() - getRed()) * weight))
.setGreenUnchecked((int) (getGreen() + (other.getGreen() - getGreen()) * weight))
.setBlueUnchecked((int) (getBlue() + (other.getBlue() - getBlue()) * weight))
.setAlphaUnchecked((int) (getAlpha() + (other.getAlpha() - getAlpha()) * weight));
}
public Color darker() {
int a = getAlpha();
return ensureMutable().mixWith(BLACK, .25f).setAlphaUnchecked(a);
}
public Color brighter() {
int a = getAlpha();
return ensureMutable().mixWith(WHITE, .25f).setAlphaUnchecked(a);
}
public Color setValue(int value) {
return ensureMutable().setValueUnchecked(value);
}
public Color modifyValue(UnaryOperator<Integer> function) {
int newValue = function.apply(value);
if (newValue == value)
return this;
return ensureMutable().setValueUnchecked(newValue);
}
// ********* //
protected Color ensureMutable() {
if (this.mutable)
return this;
return new Color(this.value);
}
protected Color setRedUnchecked(int r) {
this.value = (this.value & 0xff_00ffff) | ((r & 0xff) << 16);
return this;
}
protected Color setGreenUnchecked(int g) {
this.value = (this.value & 0xff_ff00ff) | ((g & 0xff) << 8);
return this;
}
protected Color setBlueUnchecked(int b) {
this.value = (this.value & 0xff_ffff00) | ((b & 0xff) << 0);
return this;
}
protected Color setAlphaUnchecked(int a) {
this.value = (this.value & 0x00_ffffff) | ((a & 0xff) << 24);
return this;
}
protected Color setValueUnchecked(int value) {
this.value = value;
return this;
}
// ********* //
public static Color mixColors(@NotNull Color c1, @NotNull Color c2, float w) {
return new Color(
(int) (c1.getRed() + (c2.getRed() - c1.getRed()) * w),
(int) (c1.getGreen() + (c2.getGreen() - c1.getGreen()) * w),
(int) (c1.getBlue() + (c2.getBlue() - c1.getBlue()) * w),
(int) (c1.getAlpha() + (c2.getAlpha() - c1.getAlpha()) * w)
);
}
public static int mixColors(int color1, int color2, float w) {
int a1 = (color1 >> 24);
int r1 = (color1 >> 16) & 0xFF;
int g1 = (color1 >> 8) & 0xFF;
int b1 = color1 & 0xFF;
int a2 = (color2 >> 24);
int r2 = (color2 >> 16) & 0xFF;
int g2 = (color2 >> 8) & 0xFF;
int b2 = color2 & 0xFF;
return
((int) (a1 + (a2 - a1) * w) << 24) +
((int) (r1 + (r2 - r1) * w) << 16) +
((int) (g1 + (g2 - g1) * w) << 8) +
((int) (b1 + (b2 - b1) * w) << 0);
}
public static Color rainbowColor(int timeStep) {
int localTimeStep = Math.abs(timeStep) % 1536;
int timeStepInPhase = localTimeStep % 256;
int phaseBlue = localTimeStep / 256;
int red = colorInPhase(phaseBlue + 4, timeStepInPhase);
int green = colorInPhase(phaseBlue + 2, timeStepInPhase);
int blue = colorInPhase(phaseBlue, timeStepInPhase);
return new Color(red, green, blue);
}
private static int colorInPhase(int phase, int progress) {
phase = phase % 6;
if (phase <= 1)
return 0;
if (phase == 2)
return progress;
if (phase <= 4)
return 255;
else
return 255 - progress;
}
public static Color generateFromLong(long l) {
return rainbowColor(Hashing.crc32().hashLong(l).asInt())
.mixWith(WHITE, 0.5f);
}
}

View file

@ -1,7 +1,10 @@
package com.jozufozu.flywheel.util;
import java.util.Arrays;
import java.util.Collections;
import java.util.Map;
import java.util.Set;
import java.util.WeakHashMap;
import java.util.stream.Stream;
import com.jozufozu.flywheel.mixin.BlockEntityRenderDispatcherAccessor;
@ -81,4 +84,8 @@ public class FlwUtil {
public static <R> Stream<R> mapValues(Map<?, R> map) {
return map.values().stream();
}
public static <T> Set<T> createWeakHashSet() {
return Collections.newSetFromMap(new WeakHashMap<>());
}
}

View file

@ -0,0 +1,116 @@
package com.jozufozu.flywheel.util;
import java.nio.ByteBuffer;
import org.lwjgl.system.MemoryUtil;
import com.jozufozu.flywheel.mixin.matrix.Matrix3fAccessor;
import com.jozufozu.flywheel.mixin.matrix.Matrix4fAccessor;
import com.mojang.math.Matrix3f;
import com.mojang.math.Matrix4f;
public class MatrixUtil {
public static void write(Matrix4f matrix, ByteBuffer buf) {
Matrix4fAccessor m = (Matrix4fAccessor) (Object) matrix;
buf.putFloat(m.flywheel$m00());
buf.putFloat(m.flywheel$m10());
buf.putFloat(m.flywheel$m20());
buf.putFloat(m.flywheel$m30());
buf.putFloat(m.flywheel$m01());
buf.putFloat(m.flywheel$m11());
buf.putFloat(m.flywheel$m21());
buf.putFloat(m.flywheel$m31());
buf.putFloat(m.flywheel$m02());
buf.putFloat(m.flywheel$m12());
buf.putFloat(m.flywheel$m22());
buf.putFloat(m.flywheel$m32());
buf.putFloat(m.flywheel$m03());
buf.putFloat(m.flywheel$m13());
buf.putFloat(m.flywheel$m23());
buf.putFloat(m.flywheel$m33());
}
public static void writeUnsafe(Matrix4f matrix, long ptr) {
Matrix4fAccessor m = (Matrix4fAccessor) (Object) matrix;
MemoryUtil.memPutFloat(ptr, m.flywheel$m00());
MemoryUtil.memPutFloat(ptr + 4, m.flywheel$m10());
MemoryUtil.memPutFloat(ptr + 8, m.flywheel$m20());
MemoryUtil.memPutFloat(ptr + 12, m.flywheel$m30());
MemoryUtil.memPutFloat(ptr + 16, m.flywheel$m01());
MemoryUtil.memPutFloat(ptr + 20, m.flywheel$m11());
MemoryUtil.memPutFloat(ptr + 24, m.flywheel$m21());
MemoryUtil.memPutFloat(ptr + 28, m.flywheel$m31());
MemoryUtil.memPutFloat(ptr + 32, m.flywheel$m02());
MemoryUtil.memPutFloat(ptr + 36, m.flywheel$m12());
MemoryUtil.memPutFloat(ptr + 40, m.flywheel$m22());
MemoryUtil.memPutFloat(ptr + 44, m.flywheel$m32());
MemoryUtil.memPutFloat(ptr + 48, m.flywheel$m03());
MemoryUtil.memPutFloat(ptr + 52, m.flywheel$m13());
MemoryUtil.memPutFloat(ptr + 56, m.flywheel$m23());
MemoryUtil.memPutFloat(ptr + 60, m.flywheel$m33());
}
public static void write(Matrix3f matrix, ByteBuffer buf) {
Matrix3fAccessor m = (Matrix3fAccessor) (Object) matrix;
buf.putFloat(m.flywheel$m00());
buf.putFloat(m.flywheel$m10());
buf.putFloat(m.flywheel$m20());
buf.putFloat(m.flywheel$m01());
buf.putFloat(m.flywheel$m11());
buf.putFloat(m.flywheel$m21());
buf.putFloat(m.flywheel$m02());
buf.putFloat(m.flywheel$m12());
buf.putFloat(m.flywheel$m22());
}
public static void store(Matrix4f matrix, com.jozufozu.flywheel.util.joml.Matrix4f jomlMatrix) {
Matrix4fAccessor m = (Matrix4fAccessor) (Object) matrix;
jomlMatrix.set(
m.flywheel$m00(), m.flywheel$m10(), m.flywheel$m20(), m.flywheel$m30(),
m.flywheel$m01(), m.flywheel$m11(), m.flywheel$m21(), m.flywheel$m31(),
m.flywheel$m02(), m.flywheel$m12(), m.flywheel$m22(), m.flywheel$m32(),
m.flywheel$m03(), m.flywheel$m13(), m.flywheel$m23(), m.flywheel$m33()
);
}
public static com.jozufozu.flywheel.util.joml.Matrix4f toJoml(Matrix4f matrix) {
Matrix4fAccessor m = (Matrix4fAccessor) (Object) matrix;
return new com.jozufozu.flywheel.util.joml.Matrix4f(
m.flywheel$m00(), m.flywheel$m10(), m.flywheel$m20(), m.flywheel$m30(),
m.flywheel$m01(), m.flywheel$m11(), m.flywheel$m21(), m.flywheel$m31(),
m.flywheel$m02(), m.flywheel$m12(), m.flywheel$m22(), m.flywheel$m32(),
m.flywheel$m03(), m.flywheel$m13(), m.flywheel$m23(), m.flywheel$m33()
);
}
public static void writeUnsafe(Matrix3f matrix, long ptr) {
Matrix3fAccessor m = (Matrix3fAccessor) (Object) matrix;
MemoryUtil.memPutFloat(ptr, m.flywheel$m00());
MemoryUtil.memPutFloat(ptr + 4, m.flywheel$m10());
MemoryUtil.memPutFloat(ptr + 8, m.flywheel$m20());
MemoryUtil.memPutFloat(ptr + 12, m.flywheel$m01());
MemoryUtil.memPutFloat(ptr + 16, m.flywheel$m11());
MemoryUtil.memPutFloat(ptr + 20, m.flywheel$m21());
MemoryUtil.memPutFloat(ptr + 24, m.flywheel$m02());
MemoryUtil.memPutFloat(ptr + 28, m.flywheel$m12());
MemoryUtil.memPutFloat(ptr + 32, m.flywheel$m22());
}
public static void store(Matrix3f matrix, com.jozufozu.flywheel.util.joml.Matrix3f jomlMatrix) {
Matrix3fAccessor m = (Matrix3fAccessor) (Object) matrix;
jomlMatrix.set(
m.flywheel$m00(), m.flywheel$m10(), m.flywheel$m20(),
m.flywheel$m01(), m.flywheel$m11(), m.flywheel$m21(),
m.flywheel$m02(), m.flywheel$m12(), m.flywheel$m22()
);
}
public static com.jozufozu.flywheel.util.joml.Matrix3f toJoml(Matrix3f matrix) {
Matrix3fAccessor m = (Matrix3fAccessor) (Object) matrix;
return new com.jozufozu.flywheel.util.joml.Matrix3f(
m.flywheel$m00(), m.flywheel$m10(), m.flywheel$m20(),
m.flywheel$m01(), m.flywheel$m11(), m.flywheel$m21(),
m.flywheel$m02(), m.flywheel$m12(), m.flywheel$m22()
);
}
}

View file

@ -1,122 +0,0 @@
package com.jozufozu.flywheel.util;
import java.util.AbstractSet;
import java.util.Collection;
import java.util.Iterator;
import java.util.WeakHashMap;
import net.minecraft.util.Unit;
public class WeakHashSet<T> extends AbstractSet<T> {
WeakHashMap<T, Unit> map;
public WeakHashSet() {
map = new WeakHashMap<>();
}
/**
* Constructs a new set containing the elements in the specified
* collection. The <tt>HashMap</tt> is created with default load factor
* (0.75) and an initial capacity sufficient to contain the elements in
* the specified collection.
*
* @param c the collection whose elements are to be placed into this set
* @throws NullPointerException if the specified collection is null
*/
public WeakHashSet(Collection<? extends T> c) {
map = new WeakHashMap<>(Math.max((int) (c.size() / .75f) + 1, 16));
addAll(c);
}
/**
* Constructs a new, empty set; the backing <tt>HashMap</tt> instance has
* the specified initial capacity and the specified load factor.
*
* @param initialCapacity the initial capacity of the hash map
* @param loadFactor the load factor of the hash map
* @throws IllegalArgumentException if the initial capacity is less
* than zero, or if the load factor is nonpositive
*/
public WeakHashSet(int initialCapacity, float loadFactor) {
map = new WeakHashMap<>(initialCapacity, loadFactor);
}
/**
* Constructs a new, empty set; the backing <tt>HashMap</tt> instance has
* the specified initial capacity and default load factor (0.75).
*
* @param initialCapacity the initial capacity of the hash table
* @throws IllegalArgumentException if the initial capacity is less
* than zero
*/
public WeakHashSet(int initialCapacity) {
map = new WeakHashMap<>(initialCapacity);
}
@Override
public Iterator<T> iterator() {
return map.keySet()
.iterator();
}
@Override
public int size() {
return map.size();
}
@Override
public boolean add(T t) {
return map.put(t, Unit.INSTANCE) == null;
}
@Override
public boolean remove(Object o) {
return map.remove(o) != null;
}
@Override
public boolean isEmpty() {
return map.isEmpty();
}
@Override
public boolean contains(Object o) {
return map.containsKey(o);
}
@Override
public Object[] toArray() {
return map.keySet()
.toArray();
}
@Override
public boolean containsAll(Collection<?> c) {
return stream().allMatch(map::containsKey);
}
@Override
public boolean addAll(Collection<? extends T> c) {
boolean out = false;
for (T t : c) {
out |= add(t);
}
return out;
}
@Override
public boolean retainAll(Collection<?> c) {
return false;
}
@Override
public boolean removeAll(Collection<?> c) {
return false;
}
@Override
public void clear() {
map.clear();
}
}

View file

@ -25,8 +25,8 @@
"VertexFormatMixin",
"light.LightUpdateMixin",
"light.NetworkLightUpdateMixin",
"matrix.Matrix3fMixin",
"matrix.Matrix4fMixin",
"matrix.Matrix3fAccessor",
"matrix.Matrix4fAccessor",
"matrix.PoseStackMixin"
],
"injectors": {