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