Rework rendering pipeline to make better use of multithreading
Move OpenGL frame to ARGB conversion to processing threads Move video exporting to processing threads Skip creation of BufferedImage and instead use ByteBuffer with ARGB content directly
This commit is contained in:
@@ -0,0 +1,4 @@
|
||||
package eu.crushedpixel.replaymod.video.capturer;
|
||||
|
||||
public interface CaptureData {
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
package eu.crushedpixel.replaymod.video.capturer;
|
||||
|
||||
import eu.crushedpixel.replaymod.video.frame.CubicOpenGlFrame;
|
||||
|
||||
public class CubicOpenGlFrameCapturer extends OpenGlFrameCapturer<CubicOpenGlFrame, CubicOpenGlFrameCapturer.Data> {
|
||||
public enum Data implements CaptureData {
|
||||
LEFT, RIGHT, FRONT, BACK, TOP, BOTTOM
|
||||
}
|
||||
|
||||
private final int frameSize;
|
||||
public CubicOpenGlFrameCapturer(WorldRenderer<Data> worldRenderer, RenderInfo renderInfo, int frameSize) {
|
||||
super(worldRenderer, renderInfo);
|
||||
this.frameSize = frameSize;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getFrameWidth() {
|
||||
return frameSize;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getFrameHeight() {
|
||||
return frameSize;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CubicOpenGlFrame process() {
|
||||
float partialTicks = renderInfo.updateForNextFrame();
|
||||
int frameId = framesDone++;
|
||||
return new CubicOpenGlFrame(renderFrame(frameId, partialTicks, Data.LEFT),
|
||||
renderFrame(frameId, partialTicks, Data.RIGHT),
|
||||
renderFrame(frameId, partialTicks, Data.FRONT),
|
||||
renderFrame(frameId, partialTicks, Data.BACK),
|
||||
renderFrame(frameId, partialTicks, Data.TOP),
|
||||
renderFrame(frameId, partialTicks, Data.BOTTOM));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,108 @@
|
||||
package eu.crushedpixel.replaymod.video.capturer;
|
||||
|
||||
import eu.crushedpixel.replaymod.utils.ByteBufferPool;
|
||||
import eu.crushedpixel.replaymod.video.frame.OpenGlFrame;
|
||||
import eu.crushedpixel.replaymod.video.rendering.Frame;
|
||||
import eu.crushedpixel.replaymod.video.rendering.FrameCapturer;
|
||||
import net.minecraft.client.renderer.OpenGlHelper;
|
||||
import net.minecraft.client.shader.Framebuffer;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
import org.lwjgl.util.Dimension;
|
||||
import org.lwjgl.util.ReadableDimension;
|
||||
import org.lwjgl.util.WritableDimension;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
import static net.minecraft.client.renderer.GlStateManager.*;
|
||||
import static org.lwjgl.opengl.GL11.GL_COLOR_BUFFER_BIT;
|
||||
import static org.lwjgl.opengl.GL11.GL_DEPTH_BUFFER_BIT;
|
||||
|
||||
public abstract class OpenGlFrameCapturer<F extends Frame, D extends CaptureData> implements FrameCapturer<F> {
|
||||
protected final WorldRenderer<D> worldRenderer;
|
||||
protected final RenderInfo renderInfo;
|
||||
protected int framesDone;
|
||||
private Framebuffer frameBuffer;
|
||||
|
||||
public OpenGlFrameCapturer(WorldRenderer<D> worldRenderer, RenderInfo renderInfo) {
|
||||
this.worldRenderer = worldRenderer;
|
||||
this.renderInfo = renderInfo;
|
||||
}
|
||||
|
||||
protected final ReadableDimension frameSize = new ReadableDimension() {
|
||||
@Override
|
||||
public int getWidth() {
|
||||
return getFrameWidth();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getHeight() {
|
||||
return getFrameHeight();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void getSize(WritableDimension dest) {
|
||||
dest.setSize(getWidth(), getHeight());
|
||||
}
|
||||
};
|
||||
|
||||
protected int getFrameWidth() {
|
||||
return renderInfo.getFrameSize().getWidth();
|
||||
}
|
||||
|
||||
protected int getFrameHeight() {
|
||||
return renderInfo.getFrameSize().getHeight();
|
||||
}
|
||||
|
||||
private Framebuffer frameBuffer() {
|
||||
if (frameBuffer == null) {
|
||||
frameBuffer = new Framebuffer(getFrameWidth(), getFrameHeight(), true);
|
||||
}
|
||||
return frameBuffer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDone() {
|
||||
return framesDone >= renderInfo.getTotalFrames();
|
||||
}
|
||||
|
||||
protected OpenGlFrame renderFrame(int frameId, float partialTicks) {
|
||||
return renderFrame(frameId, partialTicks, null);
|
||||
}
|
||||
|
||||
protected OpenGlFrame renderFrame(int frameId, float partialTicks, D captureData) {
|
||||
pushMatrix();
|
||||
frameBuffer().bindFramebuffer(true);
|
||||
|
||||
clear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||||
enableTexture2D();
|
||||
|
||||
worldRenderer.renderWorld(frameSize, partialTicks, captureData);
|
||||
|
||||
frameBuffer().unbindFramebuffer();
|
||||
popMatrix();
|
||||
|
||||
GL11.glPixelStorei(GL11.GL_PACK_ALIGNMENT, 1);
|
||||
GL11.glPixelStorei(GL11.GL_UNPACK_ALIGNMENT, 1);
|
||||
|
||||
ByteBuffer buffer = ByteBufferPool.allocate(getFrameWidth() * getFrameHeight() * 3);
|
||||
if (OpenGlHelper.isFramebufferEnabled()) {
|
||||
frameBuffer().bindFramebufferTexture();
|
||||
GL11.glGetTexImage(GL11.GL_TEXTURE_2D, 0, GL11.GL_RGB, GL11.GL_UNSIGNED_BYTE, buffer);
|
||||
frameBuffer().unbindFramebufferTexture();
|
||||
} else {
|
||||
GL11.glReadPixels(0, 0, getFrameWidth(), getFrameHeight(), GL11.GL_RGB, GL11.GL_UNSIGNED_BYTE, buffer);
|
||||
}
|
||||
buffer.rewind();
|
||||
|
||||
return new OpenGlFrame(frameId, new Dimension(getFrameWidth(), getFrameHeight()), buffer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
worldRenderer.close();
|
||||
if (frameBuffer != null) {
|
||||
frameBuffer.deleteFramebuffer();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package eu.crushedpixel.replaymod.video.capturer;
|
||||
|
||||
import eu.crushedpixel.replaymod.settings.RenderOptions;
|
||||
import org.lwjgl.util.ReadableDimension;
|
||||
|
||||
public interface RenderInfo {
|
||||
ReadableDimension getFrameSize();
|
||||
|
||||
int getTotalFrames();
|
||||
|
||||
float updateForNextFrame();
|
||||
|
||||
RenderOptions getRenderOptions();
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package eu.crushedpixel.replaymod.video.capturer;
|
||||
|
||||
import eu.crushedpixel.replaymod.video.frame.OpenGlFrame;
|
||||
|
||||
public class SimpleOpenGlFrameCapturer extends OpenGlFrameCapturer<OpenGlFrame, CaptureData> {
|
||||
|
||||
public SimpleOpenGlFrameCapturer(WorldRenderer<CaptureData> worldRenderer, RenderInfo renderInfo) {
|
||||
super(worldRenderer, renderInfo);
|
||||
}
|
||||
|
||||
@Override
|
||||
public OpenGlFrame process() {
|
||||
float partialTicks = renderInfo.updateForNextFrame();
|
||||
return renderFrame(framesDone++, partialTicks);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package eu.crushedpixel.replaymod.video.capturer;
|
||||
|
||||
import eu.crushedpixel.replaymod.video.frame.OpenGlFrame;
|
||||
import eu.crushedpixel.replaymod.video.frame.StereoscopicOpenGlFrame;
|
||||
|
||||
public class StereoscopicOpenGlFrameCapturer
|
||||
extends OpenGlFrameCapturer<StereoscopicOpenGlFrame, StereoscopicOpenGlFrameCapturer.Data> {
|
||||
public enum Data implements CaptureData {
|
||||
LEFT_EYE, RIGHT_EYE
|
||||
}
|
||||
|
||||
public StereoscopicOpenGlFrameCapturer(WorldRenderer<Data> worldRenderer, RenderInfo renderInfo) {
|
||||
super(worldRenderer, renderInfo);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getFrameWidth() {
|
||||
return super.getFrameWidth() / 2;
|
||||
}
|
||||
|
||||
@Override
|
||||
public StereoscopicOpenGlFrame process() {
|
||||
float partialTicks = renderInfo.updateForNextFrame();
|
||||
int frameId = framesDone++;
|
||||
OpenGlFrame left = renderFrame(frameId, partialTicks, Data.LEFT_EYE);
|
||||
OpenGlFrame right = renderFrame(frameId, partialTicks, Data.RIGHT_EYE);
|
||||
return new StereoscopicOpenGlFrame(left, right);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package eu.crushedpixel.replaymod.video.capturer;
|
||||
|
||||
import org.lwjgl.util.ReadableDimension;
|
||||
|
||||
import java.io.Closeable;
|
||||
|
||||
public interface WorldRenderer<D extends CaptureData> extends Closeable {
|
||||
void renderWorld(ReadableDimension displaySize, float partialTicks, D data);
|
||||
}
|
||||
Reference in New Issue
Block a user