mirror of
https://github.com/Jozufozu/Flywheel.git
synced 2025-01-06 04:16:36 +01:00
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:
parent
6add6c43b1
commit
95bd36b90d
42 changed files with 665 additions and 1166 deletions
|
@ -4,7 +4,7 @@ import org.jetbrains.annotations.Nullable;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
|
|
||||||
import com.jozufozu.flywheel.api.FlywheelLevel;
|
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.config.FlwConfig;
|
||||||
import com.jozufozu.flywheel.core.BackendTypes;
|
import com.jozufozu.flywheel.core.BackendTypes;
|
||||||
import com.mojang.logging.LogUtils;
|
import com.mojang.logging.LogUtils;
|
||||||
|
@ -20,7 +20,7 @@ public class Backend {
|
||||||
|
|
||||||
private static BackendType TYPE;
|
private static BackendType TYPE;
|
||||||
|
|
||||||
private static ParallelTaskEngine EXECUTOR;
|
private static ParallelTaskExecutor EXECUTOR;
|
||||||
|
|
||||||
private static final Loader LOADER = new Loader();
|
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.
|
* Get a thread pool for running Flywheel related work in parallel.
|
||||||
* @return A global Flywheel thread pool.
|
* @return A global Flywheel thread pool.
|
||||||
*/
|
*/
|
||||||
public static ParallelTaskEngine getTaskEngine() {
|
public static ParallelTaskExecutor getTaskExecutor() {
|
||||||
if (EXECUTOR == null) {
|
if (EXECUTOR == null) {
|
||||||
EXECUTOR = new ParallelTaskEngine("Flywheel");
|
EXECUTOR = new ParallelTaskExecutor("Flywheel");
|
||||||
EXECUTOR.startWorkers();
|
EXECUTOR.startWorkers();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -74,7 +74,7 @@ public abstract class InstanceManager<T> {
|
||||||
* Queued updates are processed.
|
* Queued updates are processed.
|
||||||
* </p>
|
* </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();
|
tick.tick();
|
||||||
processQueuedUpdates();
|
processQueuedUpdates();
|
||||||
|
|
||||||
|
@ -84,7 +84,7 @@ public abstract class InstanceManager<T> {
|
||||||
int cZ = (int) cameraZ;
|
int cZ = (int) cameraZ;
|
||||||
|
|
||||||
var instances = getStorage().getInstancesForTicking();
|
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) {
|
protected void tickInstance(TickableInstance instance, int cX, int cY, int cZ) {
|
||||||
|
@ -106,7 +106,7 @@ public abstract class InstanceManager<T> {
|
||||||
instance.tick();
|
instance.tick();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void beginFrame(TaskEngine taskEngine, RenderContext context) {
|
public void beginFrame(TaskExecutor executor, RenderContext context) {
|
||||||
frame.tick();
|
frame.tick();
|
||||||
processQueuedAdditions();
|
processQueuedAdditions();
|
||||||
|
|
||||||
|
@ -118,22 +118,22 @@ public abstract class InstanceManager<T> {
|
||||||
FrustumIntersection culler = context.culler();
|
FrustumIntersection culler = context.culler();
|
||||||
|
|
||||||
var instances = getStorage().getInstancesForUpdate();
|
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 size = instances.size();
|
||||||
final int threadCount = taskEngine.getThreadCount();
|
final int threadCount = executor.getThreadCount();
|
||||||
|
|
||||||
if (threadCount == 1) {
|
if (threadCount == 1) {
|
||||||
taskEngine.submit(() -> instances.forEach(action));
|
executor.execute(() -> instances.forEach(action));
|
||||||
} else {
|
} else {
|
||||||
final int stride = Math.max(size / (threadCount * 2), 1);
|
final int stride = Math.max(size / (threadCount * 2), 1);
|
||||||
for (int start = 0; start < size; start += stride) {
|
for (int start = 0; start < size; start += stride) {
|
||||||
int end = Math.min(start + stride, size);
|
int end = Math.min(start + stride, size);
|
||||||
|
|
||||||
var sub = instances.subList(start, end);
|
var sub = instances.subList(start, end);
|
||||||
taskEngine.submit(() -> sub.forEach(action));
|
executor.execute(() -> sub.forEach(action));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,7 +29,7 @@ public class InstanceWorld implements AutoCloseable {
|
||||||
protected final InstanceManager<Entity> entities;
|
protected final InstanceManager<Entity> entities;
|
||||||
protected final InstanceManager<BlockEntity> blockEntities;
|
protected final InstanceManager<BlockEntity> blockEntities;
|
||||||
|
|
||||||
public final ParallelTaskEngine taskEngine;
|
public final ParallelTaskExecutor taskExecutor;
|
||||||
private final InstanceManager<Effect> effects;
|
private final InstanceManager<Effect> effects;
|
||||||
|
|
||||||
public static InstanceWorld create(LevelAccessor level) {
|
public static InstanceWorld create(LevelAccessor level) {
|
||||||
|
@ -51,7 +51,7 @@ public class InstanceWorld implements AutoCloseable {
|
||||||
this.entities = entities;
|
this.entities = entities;
|
||||||
this.blockEntities = blockEntities;
|
this.blockEntities = blockEntities;
|
||||||
this.effects = effects;
|
this.effects = effects;
|
||||||
this.taskEngine = Backend.getTaskEngine();
|
this.taskExecutor = Backend.getTaskExecutor();
|
||||||
}
|
}
|
||||||
|
|
||||||
public InstanceManager<Entity> getEntities() {
|
public InstanceManager<Entity> getEntities() {
|
||||||
|
@ -87,15 +87,15 @@ public class InstanceWorld implements AutoCloseable {
|
||||||
RenderContext context = event.getContext();
|
RenderContext context = event.getContext();
|
||||||
boolean shifted = engine.maintainOriginCoordinate(context.camera());
|
boolean shifted = engine.maintainOriginCoordinate(context.camera());
|
||||||
|
|
||||||
taskEngine.syncPoint();
|
taskExecutor.syncPoint();
|
||||||
|
|
||||||
if (!shifted) {
|
if (!shifted) {
|
||||||
blockEntities.beginFrame(taskEngine, context);
|
blockEntities.beginFrame(taskExecutor, context);
|
||||||
entities.beginFrame(taskEngine, context);
|
entities.beginFrame(taskExecutor, context);
|
||||||
effects.beginFrame(taskEngine, 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 y = renderViewEntity.getY();
|
||||||
double z = renderViewEntity.getZ();
|
double z = renderViewEntity.getZ();
|
||||||
|
|
||||||
blockEntities.tick(taskEngine, x, y, z);
|
blockEntities.tick(taskExecutor, x, y, z);
|
||||||
entities.tick(taskEngine, x, y, z);
|
entities.tick(taskExecutor, x, y, z);
|
||||||
effects.tick(taskEngine, x, y, z);
|
effects.tick(taskExecutor, x, y, z);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Draw all instances for the given stage.
|
* Draw all instances for the given stage.
|
||||||
*/
|
*/
|
||||||
public void renderStage(RenderContext context, RenderStage stage) {
|
public void renderStage(RenderContext context, RenderStage stage) {
|
||||||
taskEngine.syncPoint();
|
taskExecutor.syncPoint();
|
||||||
engine.renderStage(taskEngine, context, stage);
|
engine.renderStage(taskExecutor, context, stage);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -7,9 +7,9 @@ import net.minecraft.client.Camera;
|
||||||
|
|
||||||
public interface RenderDispatcher {
|
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,
|
* Maintain the integer origin coordinate to be within a certain distance from the camera in all directions,
|
||||||
|
|
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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();
|
|
||||||
}
|
|
|
@ -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();
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,7 +1,8 @@
|
||||||
package com.jozufozu.flywheel.backend.instancing.batching;
|
package com.jozufozu.flywheel.backend.instancing.batching;
|
||||||
|
|
||||||
|
import java.util.EnumMap;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.Iterator;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
import com.jozufozu.flywheel.api.RenderStage;
|
import com.jozufozu.flywheel.api.RenderStage;
|
||||||
|
@ -12,7 +13,15 @@ import com.mojang.blaze3d.vertex.BufferBuilder;
|
||||||
import net.minecraft.client.renderer.RenderType;
|
import net.minecraft.client.renderer.RenderType;
|
||||||
|
|
||||||
public class BatchingDrawTracker {
|
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;
|
private final BufferBuilder scratch;
|
||||||
|
|
||||||
public BatchingDrawTracker() {
|
public BatchingDrawTracker() {
|
||||||
|
@ -22,22 +31,9 @@ public class BatchingDrawTracker {
|
||||||
}
|
}
|
||||||
|
|
||||||
public DrawBuffer getBuffer(RenderType renderType, RenderStage stage) {
|
public DrawBuffer getBuffer(RenderType renderType, RenderStage stage) {
|
||||||
return getBufferSet(renderType).getBuffer(stage);
|
DrawBuffer buffer = RenderTypeExtension.getDrawBufferSet(renderType).getBuffer(stage);
|
||||||
}
|
activeBuffers.get(stage).add(buffer);
|
||||||
|
return buffer;
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -45,59 +41,44 @@ public class BatchingDrawTracker {
|
||||||
* @param stage The RenderStage to draw.
|
* @param stage The RenderStage to draw.
|
||||||
*/
|
*/
|
||||||
public void draw(RenderStage stage) {
|
public void draw(RenderStage stage) {
|
||||||
Iterator<RenderType> iterator = activeTypes.iterator();
|
Set<DrawBuffer> buffers = activeBuffers.get(stage);
|
||||||
while (iterator.hasNext()) {
|
for (DrawBuffer buffer : buffers) {
|
||||||
RenderType renderType = iterator.next();
|
_draw(buffer);
|
||||||
DrawBufferSet bufferSet = RenderTypeExtension.getDrawBufferSet(renderType);
|
buffer.reset();
|
||||||
DrawBuffer buffer = bufferSet.deactivateBuffer(stage);
|
|
||||||
if (buffer == null) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (bufferSet.getActiveStagesView().isEmpty()) {
|
|
||||||
iterator.remove();
|
|
||||||
}
|
|
||||||
_draw(buffer, renderType);
|
|
||||||
}
|
}
|
||||||
|
buffers.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Draw and reset all active DrawBuffers.
|
* Draw and reset all active DrawBuffers.
|
||||||
*/
|
*/
|
||||||
public void drawAll() {
|
public void drawAll() {
|
||||||
for (RenderType renderType : activeTypes) {
|
for (Set<DrawBuffer> buffers : activeBuffers.values()) {
|
||||||
_draw(renderType);
|
for (DrawBuffer buffer : buffers) {
|
||||||
}
|
_draw(buffer);
|
||||||
|
buffer.reset();
|
||||||
activeTypes.clear();
|
}
|
||||||
}
|
buffers.clear();
|
||||||
|
|
||||||
private void _draw(RenderType renderType) {
|
|
||||||
DrawBufferSet bufferSet = RenderTypeExtension.getDrawBufferSet(renderType);
|
|
||||||
for (RenderStage stage : bufferSet.getActiveStagesView()) {
|
|
||||||
DrawBuffer buffer = bufferSet.deactivateBuffer(stage);
|
|
||||||
_draw(buffer, renderType);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void _draw(DrawBuffer buffer, RenderType renderType) {
|
private void _draw(DrawBuffer buffer) {
|
||||||
if (buffer.hasVertices()) {
|
if (buffer.hasVertices()) {
|
||||||
BufferBuilderExtension scratch = (BufferBuilderExtension) this.scratch;
|
BufferBuilderExtension scratch = (BufferBuilderExtension) this.scratch;
|
||||||
buffer.inject(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.
|
* Reset all active DrawBuffers.
|
||||||
*/
|
*/
|
||||||
public void reset() {
|
public void reset() {
|
||||||
for (RenderType type : activeTypes) {
|
for (Set<DrawBuffer> buffers : activeBuffers.values()) {
|
||||||
RenderTypeExtension.getDrawBufferSet(type)
|
for (DrawBuffer buffer : buffers) {
|
||||||
.reset();
|
buffer.reset();
|
||||||
|
}
|
||||||
|
buffers.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
activeTypes.clear();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,7 @@ import com.jozufozu.flywheel.api.instancer.Instancer;
|
||||||
import com.jozufozu.flywheel.api.struct.StructType;
|
import com.jozufozu.flywheel.api.struct.StructType;
|
||||||
import com.jozufozu.flywheel.backend.instancing.Engine;
|
import com.jozufozu.flywheel.backend.instancing.Engine;
|
||||||
import com.jozufozu.flywheel.backend.instancing.InstanceManager;
|
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.RenderContext;
|
||||||
import com.jozufozu.flywheel.core.model.Model;
|
import com.jozufozu.flywheel.core.model.Model;
|
||||||
import com.jozufozu.flywheel.util.FlwUtil;
|
import com.jozufozu.flywheel.util.FlwUtil;
|
||||||
|
@ -30,19 +30,19 @@ public class BatchingEngine implements Engine {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void beginFrame(TaskEngine taskEngine, RenderContext context) {
|
public void beginFrame(TaskExecutor executor, RenderContext context) {
|
||||||
transformManager.flush();
|
transformManager.flush();
|
||||||
|
|
||||||
Vec3 cameraPos = context.camera().getPosition();
|
Vec3 cameraPos = context.camera().getPosition();
|
||||||
var stack = FlwUtil.copyPoseStack(context.stack());
|
var stack = FlwUtil.copyPoseStack(context.stack());
|
||||||
stack.translate(-cameraPos.x, -cameraPos.y, -cameraPos.z);
|
stack.translate(-cameraPos.x, -cameraPos.y, -cameraPos.z);
|
||||||
|
|
||||||
// TODO: async task engine barriers
|
// TODO: async task executor barriers
|
||||||
taskEngine.syncPoint();
|
executor.syncPoint();
|
||||||
submitTasks(taskEngine, stack.last(), context.level());
|
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()) {
|
for (var transformSetEntry : transformManager.getTransformSetsView().entrySet()) {
|
||||||
var stage = transformSetEntry.getKey();
|
var stage = transformSetEntry.getKey();
|
||||||
var transformSet = transformSetEntry.getValue();
|
var transformSet = transformSetEntry.getValue();
|
||||||
|
@ -53,6 +53,7 @@ public class BatchingEngine implements Engine {
|
||||||
|
|
||||||
int vertices = 0;
|
int vertices = 0;
|
||||||
for (var transformCall : transformCalls) {
|
for (var transformCall : transformCalls) {
|
||||||
|
transformCall.setup();
|
||||||
vertices += transformCall.getTotalVertexCount();
|
vertices += transformCall.getTotalVertexCount();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -65,7 +66,7 @@ public class BatchingEngine implements Engine {
|
||||||
|
|
||||||
int startVertex = 0;
|
int startVertex = 0;
|
||||||
for (var transformCall : transformCalls) {
|
for (var transformCall : transformCalls) {
|
||||||
transformCall.submitTasks(taskEngine, buffer, startVertex, matrices, level);
|
transformCall.submitTasks(executor, buffer, startVertex, matrices, level);
|
||||||
startVertex += transformCall.getTotalVertexCount();
|
startVertex += transformCall.getTotalVertexCount();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -73,7 +74,7 @@ public class BatchingEngine implements Engine {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void renderStage(TaskEngine taskEngine, RenderContext context, RenderStage stage) {
|
public void renderStage(TaskExecutor executor, RenderContext context, RenderStage stage) {
|
||||||
drawTracker.draw(stage);
|
drawTracker.draw(stage);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -99,7 +99,7 @@ public class BatchingTransformManager {
|
||||||
public static class TransformSet implements Iterable<Map.Entry<RenderType, Collection<TransformCall<?>>>> {
|
public static class TransformSet implements Iterable<Map.Entry<RenderType, Collection<TransformCall<?>>>> {
|
||||||
public static final TransformSet EMPTY = new TransformSet(ImmutableListMultimap.of());
|
public static final TransformSet EMPTY = new TransformSet(ImmutableListMultimap.of());
|
||||||
|
|
||||||
final ListMultimap<RenderType, TransformCall<?>> transformCalls;
|
private final ListMultimap<RenderType, TransformCall<?>> transformCalls;
|
||||||
|
|
||||||
public TransformSet(RenderStage renderStage) {
|
public TransformSet(RenderStage renderStage) {
|
||||||
transformCalls = ArrayListMultimap.create();
|
transformCalls = ArrayListMultimap.create();
|
||||||
|
|
|
@ -10,7 +10,7 @@ public class CPUInstancer<D extends InstancedPart> extends AbstractInstancer<D>
|
||||||
super(type);
|
super(type);
|
||||||
}
|
}
|
||||||
|
|
||||||
void setup() {
|
void update() {
|
||||||
if (anyToRemove) {
|
if (anyToRemove) {
|
||||||
data.removeIf(InstancedPart::isRemoved);
|
data.removeIf(InstancedPart::isRemoved);
|
||||||
anyToRemove = false;
|
anyToRemove = false;
|
||||||
|
|
|
@ -11,6 +11,8 @@ import com.jozufozu.flywheel.event.ReloadRenderersEvent;
|
||||||
import com.jozufozu.flywheel.extension.BufferBuilderExtension;
|
import com.jozufozu.flywheel.extension.BufferBuilderExtension;
|
||||||
import com.mojang.blaze3d.vertex.VertexFormat;
|
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.
|
* 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 {
|
public class DrawBuffer {
|
||||||
private static final List<DrawBuffer> ALL = new ArrayList<>();
|
private static final List<DrawBuffer> ALL = new ArrayList<>();
|
||||||
|
|
||||||
|
private final RenderType renderType;
|
||||||
private final VertexFormat format;
|
private final VertexFormat format;
|
||||||
private final int stride;
|
private final int stride;
|
||||||
private final VertexListProvider provider;
|
private final VertexListProvider provider;
|
||||||
|
@ -29,7 +32,8 @@ public class DrawBuffer {
|
||||||
private boolean prepared;
|
private boolean prepared;
|
||||||
private int vertexCount;
|
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.format = format;
|
||||||
this.stride = stride;
|
this.stride = stride;
|
||||||
this.provider = provider;
|
this.provider = provider;
|
||||||
|
@ -62,7 +66,6 @@ public class DrawBuffer {
|
||||||
buffer = memory.asBuffer();
|
buffer = memory.asBuffer();
|
||||||
}
|
}
|
||||||
|
|
||||||
memory.clear();
|
|
||||||
prepared = true;
|
prepared = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -90,6 +93,10 @@ public class DrawBuffer {
|
||||||
bufferBuilder.flywheel$injectForRender(buffer, format, vertexCount);
|
bufferBuilder.flywheel$injectForRender(buffer, format, vertexCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public RenderType getRenderType() {
|
||||||
|
return renderType;
|
||||||
|
}
|
||||||
|
|
||||||
public VertexFormat getVertexFormat() {
|
public VertexFormat getVertexFormat() {
|
||||||
return format;
|
return format;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,66 +1,33 @@
|
||||||
package com.jozufozu.flywheel.backend.instancing.batching;
|
package com.jozufozu.flywheel.backend.instancing.batching;
|
||||||
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.EnumMap;
|
import java.util.EnumMap;
|
||||||
import java.util.EnumSet;
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
import org.jetbrains.annotations.ApiStatus;
|
import org.jetbrains.annotations.ApiStatus;
|
||||||
import org.jetbrains.annotations.Nullable;
|
|
||||||
|
|
||||||
import com.jozufozu.flywheel.api.RenderStage;
|
import com.jozufozu.flywheel.api.RenderStage;
|
||||||
import com.jozufozu.flywheel.api.vertex.VertexListProvider;
|
import com.jozufozu.flywheel.api.vertex.VertexListProvider;
|
||||||
import com.mojang.blaze3d.vertex.VertexFormat;
|
import com.mojang.blaze3d.vertex.VertexFormat;
|
||||||
|
|
||||||
|
import net.minecraft.client.renderer.RenderType;
|
||||||
|
|
||||||
public class DrawBufferSet {
|
public class DrawBufferSet {
|
||||||
|
private final RenderType renderType;
|
||||||
private final VertexFormat format;
|
private final VertexFormat format;
|
||||||
private final int stride;
|
private final int stride;
|
||||||
private final VertexListProvider provider;
|
private final VertexListProvider provider;
|
||||||
|
|
||||||
private final Map<RenderStage, DrawBuffer> buffers = new EnumMap<>(RenderStage.class);
|
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
|
@ApiStatus.Internal
|
||||||
public DrawBufferSet(VertexFormat format) {
|
public DrawBufferSet(RenderType renderType) {
|
||||||
this.format = format;
|
this.renderType = renderType;
|
||||||
|
format = renderType.format();
|
||||||
stride = format.getVertexSize();
|
stride = format.getVertexSize();
|
||||||
provider = VertexListProvider.get(format);
|
provider = VertexListProvider.get(format);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Set<RenderStage> getActiveStagesView() {
|
|
||||||
return activeStagesView;
|
|
||||||
}
|
|
||||||
|
|
||||||
public DrawBuffer getBuffer(RenderStage stage) {
|
public DrawBuffer getBuffer(RenderStage stage) {
|
||||||
activeStages.add(stage);
|
return buffers.computeIfAbsent(stage, $ -> new DrawBuffer(renderType, format, stride, provider));
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,7 +7,7 @@ import com.jozufozu.flywheel.api.material.Material;
|
||||||
import com.jozufozu.flywheel.api.struct.StructType;
|
import com.jozufozu.flywheel.api.struct.StructType;
|
||||||
import com.jozufozu.flywheel.api.vertex.MutableVertexList;
|
import com.jozufozu.flywheel.api.vertex.MutableVertexList;
|
||||||
import com.jozufozu.flywheel.api.vertex.ReusableVertexList;
|
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.blaze3d.vertex.PoseStack;
|
||||||
import com.mojang.math.Matrix3f;
|
import com.mojang.math.Matrix3f;
|
||||||
import com.mojang.math.Matrix4f;
|
import com.mojang.math.Matrix4f;
|
||||||
|
@ -37,9 +37,11 @@ public class TransformCall<D extends InstancedPart> {
|
||||||
return meshVertexCount * instancer.getInstanceCount();
|
return meshVertexCount * instancer.getInstanceCount();
|
||||||
}
|
}
|
||||||
|
|
||||||
void submitTasks(TaskEngine pool, DrawBuffer buffer, int startVertex, PoseStack.Pose matrices, ClientLevel level) {
|
void setup() {
|
||||||
instancer.setup();
|
instancer.update();
|
||||||
|
}
|
||||||
|
|
||||||
|
void submitTasks(TaskExecutor executor, DrawBuffer buffer, int startVertex, PoseStack.Pose matrices, ClientLevel level) {
|
||||||
int instances = instancer.getInstanceCount();
|
int instances = instancer.getInstanceCount();
|
||||||
|
|
||||||
while (instances > 0) {
|
while (instances > 0) {
|
||||||
|
@ -51,7 +53,7 @@ public class TransformCall<D extends InstancedPart> {
|
||||||
ReusableVertexList sub = buffer.slice(startVertex, vertexCount);
|
ReusableVertexList sub = buffer.slice(startVertex, vertexCount);
|
||||||
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.ptr(anchorPtr);
|
||||||
vertexList.vertexCount(totalVertexCount);
|
vertexList.vertexCount(totalVertexCount);
|
||||||
material.getVertexTransformer().transform(vertexList, level);
|
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();
|
Vector4f pos = new Vector4f();
|
||||||
Vector3f normal = new Vector3f();
|
Vector3f normal = new Vector3f();
|
||||||
|
|
||||||
|
|
|
@ -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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -2,6 +2,7 @@ package com.jozufozu.flywheel.backend.instancing.indirect;
|
||||||
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
import org.lwjgl.opengl.GL32;
|
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.gl.GlTextureUnit;
|
||||||
import com.jozufozu.flywheel.backend.instancing.Engine;
|
import com.jozufozu.flywheel.backend.instancing.Engine;
|
||||||
import com.jozufozu.flywheel.backend.instancing.InstanceManager;
|
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.RenderContext;
|
||||||
import com.jozufozu.flywheel.core.model.Model;
|
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 com.mojang.blaze3d.systems.RenderSystem;
|
||||||
|
|
||||||
import net.minecraft.client.Camera;
|
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.
|
* 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 ContextShader context;
|
||||||
protected final int sqrMaxOriginDistance;
|
protected final int sqrMaxOriginDistance;
|
||||||
|
@ -52,14 +53,14 @@ public class IndirectEngine implements Engine {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void beginFrame(TaskEngine taskEngine, RenderContext context) {
|
public void beginFrame(TaskExecutor executor, RenderContext context) {
|
||||||
try (var restoreState = GlStateTracker.getRestoreState()) {
|
try (var restoreState = GlStateTracker.getRestoreState()) {
|
||||||
drawManager.flush();
|
drawManager.flush();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void renderStage(TaskEngine taskEngine, RenderContext context, RenderStage stage) {
|
public void renderStage(TaskExecutor executor, RenderContext context, RenderStage stage) {
|
||||||
try (var restoreState = GlStateTracker.getRestoreState()) {
|
try (var restoreState = GlStateTracker.getRestoreState()) {
|
||||||
setup();
|
setup();
|
||||||
|
|
||||||
|
|
|
@ -99,7 +99,7 @@ public class InstancingDrawManager {
|
||||||
|
|
||||||
public static final DrawSet EMPTY = new DrawSet(ImmutableListMultimap.of());
|
public static final DrawSet EMPTY = new DrawSet(ImmutableListMultimap.of());
|
||||||
|
|
||||||
final ListMultimap<ShaderState, DrawCall> drawCalls;
|
private final ListMultimap<ShaderState, DrawCall> drawCalls;
|
||||||
|
|
||||||
public DrawSet(RenderStage renderStage) {
|
public DrawSet(RenderStage renderStage) {
|
||||||
drawCalls = ArrayListMultimap.create();
|
drawCalls = ArrayListMultimap.create();
|
||||||
|
|
|
@ -2,6 +2,7 @@ package com.jozufozu.flywheel.backend.instancing.instancing;
|
||||||
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
import org.lwjgl.opengl.GL32;
|
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.Engine;
|
||||||
import com.jozufozu.flywheel.backend.instancing.InstanceManager;
|
import com.jozufozu.flywheel.backend.instancing.InstanceManager;
|
||||||
import com.jozufozu.flywheel.backend.instancing.PipelineCompiler;
|
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.Components;
|
||||||
import com.jozufozu.flywheel.core.RenderContext;
|
import com.jozufozu.flywheel.core.RenderContext;
|
||||||
import com.jozufozu.flywheel.core.model.Model;
|
import com.jozufozu.flywheel.core.model.Model;
|
||||||
import com.jozufozu.flywheel.core.uniform.UniformBuffer;
|
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 com.mojang.blaze3d.systems.RenderSystem;
|
||||||
|
|
||||||
import net.minecraft.client.Camera;
|
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.
|
* 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 ContextShader context;
|
||||||
protected final int sqrMaxOriginDistance;
|
protected final int sqrMaxOriginDistance;
|
||||||
|
@ -55,14 +56,14 @@ public class InstancingEngine implements Engine {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void beginFrame(TaskEngine taskEngine, RenderContext context) {
|
public void beginFrame(TaskExecutor executor, RenderContext context) {
|
||||||
try (var restoreState = GlStateTracker.getRestoreState()) {
|
try (var restoreState = GlStateTracker.getRestoreState()) {
|
||||||
drawManager.flush();
|
drawManager.flush();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void renderStage(TaskEngine taskEngine, RenderContext context, RenderStage stage) {
|
public void renderStage(TaskExecutor executor, RenderContext context, RenderStage stage) {
|
||||||
var drawSet = drawManager.get(stage);
|
var drawSet = drawManager.get(stage);
|
||||||
|
|
||||||
if (drawSet.isEmpty()) {
|
if (drawSet.isEmpty()) {
|
||||||
|
|
|
@ -2,7 +2,7 @@ package com.jozufozu.flywheel.core;
|
||||||
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
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.jozufozu.flywheel.util.joml.FrustumIntersection;
|
||||||
import com.mojang.blaze3d.vertex.PoseStack;
|
import com.mojang.blaze3d.vertex.PoseStack;
|
||||||
import com.mojang.math.Matrix4f;
|
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) {
|
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);
|
proj.translate(camX, camY, camZ);
|
||||||
|
|
||||||
|
|
|
@ -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
|
* 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.
|
* @param vertexList The vertex list to which data is written to.
|
||||||
*/
|
*/
|
||||||
void write(MutableVertexList vertexList);
|
void write(MutableVertexList vertexList);
|
||||||
|
|
|
@ -2,7 +2,6 @@ package com.jozufozu.flywheel.core.structs;
|
||||||
|
|
||||||
import com.jozufozu.flywheel.api.instancer.InstancedPart;
|
import com.jozufozu.flywheel.api.instancer.InstancedPart;
|
||||||
import com.jozufozu.flywheel.api.struct.StructType;
|
import com.jozufozu.flywheel.api.struct.StructType;
|
||||||
import com.jozufozu.flywheel.util.Color;
|
|
||||||
|
|
||||||
import net.minecraft.client.renderer.LightTexture;
|
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);
|
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) {
|
public ColoredLitPart setColor(int color) {
|
||||||
return setColor(color, false);
|
return setColor(color, false);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
package com.jozufozu.flywheel.core.structs.transformed;
|
package com.jozufozu.flywheel.core.structs.transformed;
|
||||||
|
|
||||||
import com.jozufozu.flywheel.core.structs.ColoredLitWriter;
|
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 class TransformedWriter extends ColoredLitWriter<TransformedPart> {
|
||||||
public static final TransformedWriter INSTANCE = new TransformedWriter();
|
public static final TransformedWriter INSTANCE = new TransformedWriter();
|
||||||
|
@ -9,8 +9,8 @@ public class TransformedWriter extends ColoredLitWriter<TransformedPart> {
|
||||||
@Override
|
@Override
|
||||||
public void write(final long ptr, final TransformedPart d) {
|
public void write(final long ptr, final TransformedPart d) {
|
||||||
super.write(ptr, d);
|
super.write(ptr, d);
|
||||||
MatrixWrite.writeUnsafe(d.model, ptr + 8);
|
MatrixUtil.writeUnsafe(d.model, ptr + 8);
|
||||||
MatrixWrite.writeUnsafe(d.normal, ptr + 72);
|
MatrixUtil.writeUnsafe(d.normal, ptr + 72);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,7 @@ import com.jozufozu.flywheel.core.Components;
|
||||||
import com.jozufozu.flywheel.core.RenderContext;
|
import com.jozufozu.flywheel.core.RenderContext;
|
||||||
import com.jozufozu.flywheel.core.source.FileResolution;
|
import com.jozufozu.flywheel.core.source.FileResolution;
|
||||||
import com.jozufozu.flywheel.event.BeginFrameEvent;
|
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.client.multiplayer.ClientLevel;
|
||||||
import net.minecraft.core.Vec3i;
|
import net.minecraft.core.Vec3i;
|
||||||
|
@ -52,7 +52,7 @@ public class ViewProvider extends UniformProvider {
|
||||||
var vp = context.viewProjection().copy();
|
var vp = context.viewProjection().copy();
|
||||||
vp.multiplyWithTranslation(-camX, -camY, -camZ);
|
vp.multiplyWithTranslation(-camX, -camY, -camZ);
|
||||||
|
|
||||||
MatrixWrite.writeUnsafe(vp, ptr);
|
MatrixUtil.writeUnsafe(vp, ptr);
|
||||||
MemoryUtil.memPutFloat(ptr + 64, camX);
|
MemoryUtil.memPutFloat(ptr + 64, camX);
|
||||||
MemoryUtil.memPutFloat(ptr + 68, camY);
|
MemoryUtil.memPutFloat(ptr + 68, camY);
|
||||||
MemoryUtil.memPutFloat(ptr + 72, camZ);
|
MemoryUtil.memPutFloat(ptr + 72, camZ);
|
||||||
|
|
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -6,8 +6,9 @@ import java.util.concurrent.ConcurrentLinkedQueue;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
import com.jozufozu.flywheel.backend.Backend;
|
import com.jozufozu.flywheel.backend.Backend;
|
||||||
import com.jozufozu.flywheel.backend.instancing.ParallelTaskEngine;
|
import com.jozufozu.flywheel.backend.instancing.ParallelTaskExecutor;
|
||||||
import com.jozufozu.flywheel.util.WeakHashSet;
|
import com.jozufozu.flywheel.backend.instancing.WorkGroup;
|
||||||
|
import com.jozufozu.flywheel.util.FlwUtil;
|
||||||
import com.jozufozu.flywheel.util.WorldAttached;
|
import com.jozufozu.flywheel.util.WorldAttached;
|
||||||
import com.jozufozu.flywheel.util.box.GridAlignedBB;
|
import com.jozufozu.flywheel.util.box.GridAlignedBB;
|
||||||
import com.jozufozu.flywheel.util.box.ImmutableBox;
|
import com.jozufozu.flywheel.util.box.ImmutableBox;
|
||||||
|
@ -27,7 +28,7 @@ import net.minecraft.world.level.LightLayer;
|
||||||
public class LightUpdater {
|
public class LightUpdater {
|
||||||
|
|
||||||
private static final WorldAttached<LightUpdater> LEVELS = new WorldAttached<>(LightUpdater::new);
|
private static final WorldAttached<LightUpdater> LEVELS = new WorldAttached<>(LightUpdater::new);
|
||||||
private final ParallelTaskEngine taskEngine;
|
private final ParallelTaskExecutor taskExecutor;
|
||||||
|
|
||||||
public static LightUpdater get(LevelAccessor level) {
|
public static LightUpdater get(LevelAccessor level) {
|
||||||
if (LightUpdated.receivesLightUpdates(level)) {
|
if (LightUpdated.receivesLightUpdates(level)) {
|
||||||
|
@ -41,12 +42,12 @@ public class LightUpdater {
|
||||||
|
|
||||||
private final LevelAccessor level;
|
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> sections = new WeakContainmentMultiMap<>();
|
||||||
private final WeakContainmentMultiMap<LightListener> chunks = new WeakContainmentMultiMap<>();
|
private final WeakContainmentMultiMap<LightListener> chunks = new WeakContainmentMultiMap<>();
|
||||||
|
|
||||||
public LightUpdater(LevelAccessor level) {
|
public LightUpdater(LevelAccessor level) {
|
||||||
taskEngine = Backend.getTaskEngine();
|
taskExecutor = Backend.getTaskExecutor();
|
||||||
this.level = level;
|
this.level = level;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -66,14 +67,14 @@ public class LightUpdater {
|
||||||
private void tickParallel() {
|
private void tickParallel() {
|
||||||
Queue<LightListener> listeners = new ConcurrentLinkedQueue<>();
|
Queue<LightListener> listeners = new ConcurrentLinkedQueue<>();
|
||||||
|
|
||||||
taskEngine.group("LightUpdater")
|
WorkGroup.builder()
|
||||||
.addTasks(tickingLightListeners.stream(), listener -> {
|
.addTasks(tickingLightListeners.stream(), listener -> {
|
||||||
if (listener.tickLightListener()) {
|
if (listener.tickLightListener()) {
|
||||||
listeners.add(listener);
|
listeners.add(listener);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.onComplete(() -> listeners.forEach(this::addListener))
|
.onComplete(() -> listeners.forEach(this::addListener))
|
||||||
.submit();
|
.execute(taskExecutor);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -6,7 +6,7 @@ import java.util.Set;
|
||||||
import java.util.WeakHashMap;
|
import java.util.WeakHashMap;
|
||||||
import java.util.function.LongConsumer;
|
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.Long2ObjectMap;
|
||||||
import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap;
|
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> {
|
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;
|
private final WeakHashMap<T, LongSet> reverse;
|
||||||
|
|
||||||
public WeakContainmentMultiMap() {
|
public WeakContainmentMultiMap() {
|
||||||
|
@ -38,7 +38,7 @@ public class WeakContainmentMultiMap<T> extends AbstractCollection<T> {
|
||||||
LongSet containmentSet = reverse.computeIfAbsent(listener, $ -> new LongRBTreeSet());
|
LongSet containmentSet = reverse.computeIfAbsent(listener, $ -> new LongRBTreeSet());
|
||||||
|
|
||||||
containmentSet.forEach((LongConsumer) l -> {
|
containmentSet.forEach((LongConsumer) l -> {
|
||||||
WeakHashSet<T> listeners = forward.get(l);
|
Set<T> listeners = forward.get(l);
|
||||||
|
|
||||||
if (listeners != null) listeners.remove(listener);
|
if (listeners != null) listeners.remove(listener);
|
||||||
});
|
});
|
||||||
|
@ -53,7 +53,7 @@ public class WeakContainmentMultiMap<T> extends AbstractCollection<T> {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void put(long sectionPos, T listener) {
|
public void put(long sectionPos, T listener) {
|
||||||
forward.computeIfAbsent(sectionPos, $ -> new WeakHashSet<>()).add(listener);
|
forward.computeIfAbsent(sectionPos, $ -> FlwUtil.createWeakHashSet()).add(listener);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -62,7 +62,7 @@ public class WeakContainmentMultiMap<T> extends AbstractCollection<T> {
|
||||||
|
|
||||||
if (containmentSet != null) {
|
if (containmentSet != null) {
|
||||||
containmentSet.forEach((LongConsumer) l -> {
|
containmentSet.forEach((LongConsumer) l -> {
|
||||||
WeakHashSet<T> listeners = forward.get(l);
|
Set<T> listeners = forward.get(l);
|
||||||
|
|
||||||
if (listeners != null) listeners.remove(o);
|
if (listeners != null) listeners.remove(o);
|
||||||
});
|
});
|
||||||
|
|
|
@ -18,7 +18,7 @@ public class RenderTypeMixin implements RenderTypeExtension {
|
||||||
@NotNull
|
@NotNull
|
||||||
public DrawBufferSet flywheel$getDrawBufferSet() {
|
public DrawBufferSet flywheel$getDrawBufferSet() {
|
||||||
if (flywheel$drawBufferSet == null) {
|
if (flywheel$drawBufferSet == null) {
|
||||||
flywheel$drawBufferSet = new DrawBufferSet(((RenderType) (Object) this).format());
|
flywheel$drawBufferSet = new DrawBufferSet((RenderType) (Object) this);
|
||||||
}
|
}
|
||||||
return flywheel$drawBufferSet;
|
return flywheel$drawBufferSet;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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();
|
||||||
|
}
|
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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();
|
||||||
|
}
|
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,7 +1,10 @@
|
||||||
package com.jozufozu.flywheel.util;
|
package com.jozufozu.flywheel.util;
|
||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.WeakHashMap;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
import com.jozufozu.flywheel.mixin.BlockEntityRenderDispatcherAccessor;
|
import com.jozufozu.flywheel.mixin.BlockEntityRenderDispatcherAccessor;
|
||||||
|
@ -81,4 +84,8 @@ public class FlwUtil {
|
||||||
public static <R> Stream<R> mapValues(Map<?, R> map) {
|
public static <R> Stream<R> mapValues(Map<?, R> map) {
|
||||||
return map.values().stream();
|
return map.values().stream();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static <T> Set<T> createWeakHashSet() {
|
||||||
|
return Collections.newSetFromMap(new WeakHashMap<>());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
116
src/main/java/com/jozufozu/flywheel/util/MatrixUtil.java
Normal file
116
src/main/java/com/jozufozu/flywheel/util/MatrixUtil.java
Normal 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()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -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();
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -25,8 +25,8 @@
|
||||||
"VertexFormatMixin",
|
"VertexFormatMixin",
|
||||||
"light.LightUpdateMixin",
|
"light.LightUpdateMixin",
|
||||||
"light.NetworkLightUpdateMixin",
|
"light.NetworkLightUpdateMixin",
|
||||||
"matrix.Matrix3fMixin",
|
"matrix.Matrix3fAccessor",
|
||||||
"matrix.Matrix4fMixin",
|
"matrix.Matrix4fAccessor",
|
||||||
"matrix.PoseStackMixin"
|
"matrix.PoseStackMixin"
|
||||||
],
|
],
|
||||||
"injectors": {
|
"injectors": {
|
||||||
|
|
Loading…
Reference in a new issue