Encode OpenEXR and PNG frames in parallel
This commit is contained in:
@@ -63,4 +63,9 @@ public class ScreenshotWriter implements FrameConsumer<BitmapFrame> {
|
||||
public void close() throws IOException {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isParallelCapable() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -135,4 +135,9 @@ public class EXRWriter implements FrameConsumer<BitmapFrame> {
|
||||
@Override
|
||||
public void close() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isParallelCapable() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -134,6 +134,11 @@ public class FFmpegWriter implements FrameConsumer<BitmapFrame> {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isParallelCapable() {
|
||||
return false;
|
||||
}
|
||||
|
||||
private void checkSize(ReadableDimension size) {
|
||||
checkSize(size.getWidth(), size.getHeight());
|
||||
}
|
||||
|
||||
@@ -71,4 +71,9 @@ public class PNGWriter implements FrameConsumer<BitmapFrame> {
|
||||
@Override
|
||||
public void close() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isParallelCapable() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,4 +7,6 @@ public interface FrameConsumer<P extends Frame> extends Closeable {
|
||||
|
||||
void consume(Map<Channel, P> channels);
|
||||
|
||||
boolean isParallelCapable();
|
||||
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ import net.minecraft.util.crash.CrashException;
|
||||
import net.minecraft.util.crash.CrashReport;
|
||||
import org.lwjgl.glfw.GLFW;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ArrayBlockingQueue;
|
||||
@@ -25,8 +26,6 @@ public class Pipeline<R extends Frame, P extends Frame> implements Runnable {
|
||||
private final FrameCapturer<R> capturer;
|
||||
private final FrameProcessor<R, P> processor;
|
||||
private final GlToAbsoluteDepthProcessor depthProcessor;
|
||||
private int consumerNextFrame;
|
||||
private final Object consumerLock = new Object();
|
||||
private final FrameConsumer<P> consumer;
|
||||
|
||||
private volatile boolean abort;
|
||||
@@ -35,7 +34,7 @@ public class Pipeline<R extends Frame, P extends Frame> implements Runnable {
|
||||
this.worldRenderer = worldRenderer;
|
||||
this.capturer = capturer;
|
||||
this.processor = processor;
|
||||
this.consumer = consumer;
|
||||
this.consumer = new ParallelSafeConsumer<>(consumer);
|
||||
|
||||
float near = 0.05f;
|
||||
float far = getMinecraft().options.viewDistance * 16 * 4;
|
||||
@@ -44,7 +43,6 @@ public class Pipeline<R extends Frame, P extends Frame> implements Runnable {
|
||||
|
||||
@Override
|
||||
public synchronized void run() {
|
||||
consumerNextFrame = 0;
|
||||
int processors = Runtime.getRuntime().availableProcessors();
|
||||
int processThreads = Math.max(1, processors - 2); // One processor for the main thread and one for ffmpeg, sorry OS :(
|
||||
ExecutorService processService = new ThreadPoolExecutor(processThreads, processThreads,
|
||||
@@ -106,7 +104,6 @@ public class Pipeline<R extends Frame, P extends Frame> implements Runnable {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
Integer frameId = null;
|
||||
Map<Channel, P> processedChannels = new HashMap<>();
|
||||
for (Map.Entry<Channel, R> entry : rawChannels.entrySet()) {
|
||||
P processedFrame = processor.process(entry.getValue());
|
||||
@@ -114,27 +111,57 @@ public class Pipeline<R extends Frame, P extends Frame> implements Runnable {
|
||||
depthProcessor.process((BitmapFrame) processedFrame);
|
||||
}
|
||||
processedChannels.put(entry.getKey(), processedFrame);
|
||||
frameId = processedFrame.getFrameId();
|
||||
}
|
||||
if (frameId == null) {
|
||||
if (processedChannels.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
synchronized (consumerLock) {
|
||||
while (consumerNextFrame != frameId) {
|
||||
try {
|
||||
consumerLock.wait();
|
||||
} catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
}
|
||||
consumer.consume(processedChannels);
|
||||
consumerNextFrame++;
|
||||
consumerLock.notifyAll();
|
||||
}
|
||||
consumer.consume(processedChannels);
|
||||
} catch (Throwable t) {
|
||||
CrashReport crashReport = CrashReport.create(t, "Processing frame");
|
||||
MCVer.getMinecraft().setCrashReport(crashReport);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static class ParallelSafeConsumer<P extends Frame> implements FrameConsumer<P> {
|
||||
private final FrameConsumer<P> inner;
|
||||
|
||||
private int nextFrame;
|
||||
private final Object lock = new Object();
|
||||
|
||||
private ParallelSafeConsumer(FrameConsumer<P> inner) {
|
||||
this.inner = inner;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void consume(Map<Channel, P> channels) {
|
||||
if (inner.isParallelCapable()) {
|
||||
inner.consume(channels);
|
||||
} else {
|
||||
int frameId = channels.values().iterator().next().getFrameId();
|
||||
synchronized (lock) {
|
||||
while (nextFrame != frameId) {
|
||||
try {
|
||||
lock.wait();
|
||||
} catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
}
|
||||
inner.consume(channels);
|
||||
nextFrame++;
|
||||
lock.notifyAll();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isParallelCapable() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
inner.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -128,6 +128,11 @@ public class Pipelines {
|
||||
@Override
|
||||
public void close() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isParallelCapable() {
|
||||
return true;
|
||||
}
|
||||
};
|
||||
return new Pipeline<>(worldRenderer, capturer, new DummyProcessor<>(), consumer);
|
||||
}
|
||||
|
||||
@@ -137,11 +137,19 @@ public class VideoRenderer implements RenderInfo {
|
||||
}
|
||||
ffmpegWriter = frameConsumer instanceof FFmpegWriter ? (FFmpegWriter) frameConsumer : null;
|
||||
FrameConsumer<BitmapFrame> previewingFrameConsumer = new FrameConsumer<BitmapFrame>() {
|
||||
private int lastFrameId = -1;
|
||||
|
||||
@Override
|
||||
public void consume(Map<Channel, BitmapFrame> channels) {
|
||||
BitmapFrame bgra = channels.get(Channel.BRGA);
|
||||
if (bgra != null) {
|
||||
gui.updatePreview(bgra.getByteBuffer(), bgra.getSize());
|
||||
synchronized (this) {
|
||||
int frameId = bgra.getFrameId();
|
||||
if (lastFrameId < frameId) {
|
||||
lastFrameId = frameId;
|
||||
gui.updatePreview(bgra.getByteBuffer(), bgra.getSize());
|
||||
}
|
||||
}
|
||||
}
|
||||
frameConsumer.consume(channels);
|
||||
}
|
||||
@@ -150,6 +158,11 @@ public class VideoRenderer implements RenderInfo {
|
||||
public void close() throws IOException {
|
||||
frameConsumer.close();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isParallelCapable() {
|
||||
return frameConsumer.isParallelCapable();
|
||||
}
|
||||
};
|
||||
this.renderingPipeline = Pipelines.newPipeline(settings.getRenderMethod(), this, previewingFrameConsumer);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user