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:
johni0702
2015-07-15 14:21:18 +02:00
parent 3a2c35cb22
commit 7fd429e25f
44 changed files with 1142 additions and 1244 deletions

View File

@@ -3,8 +3,7 @@ package eu.crushedpixel.replaymod.video.entity;
import eu.crushedpixel.replaymod.renderer.SpectatorRenderer;
import eu.crushedpixel.replaymod.replay.ReplayHandler;
import eu.crushedpixel.replaymod.settings.RenderOptions;
import eu.crushedpixel.replaymod.utils.OpenGLUtils;
import eu.crushedpixel.replaymod.video.entity.strategy.*;
import eu.crushedpixel.replaymod.video.capturer.CaptureData;
import net.minecraft.client.Minecraft;
import net.minecraft.client.particle.EffectRenderer;
import net.minecraft.client.particle.EntityFX;
@@ -19,16 +18,17 @@ import net.minecraft.util.AxisAlignedBB;
import net.minecraft.util.EnumWorldBlockLayer;
import net.minecraft.util.MathHelper;
import net.minecraft.util.ResourceLocation;
import org.lwjgl.util.ReadableDimension;
import org.lwjgl.util.glu.Project;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.lang.reflect.Field;
import java.util.List;
import static net.minecraft.client.renderer.GlStateManager.*;
import static org.lwjgl.opengl.GL11.*;
public abstract class CustomEntityRenderer {
public class CustomEntityRenderer<D extends CaptureData> implements eu.crushedpixel.replaymod.video.capturer.WorldRenderer<D> {
public final Minecraft mc = Minecraft.getMinecraft();
public final EntityRenderer proxied = mc.entityRenderer;
protected final SpectatorRenderer spectatorRenderer = new SpectatorRenderer(){
@@ -37,43 +37,18 @@ public abstract class CustomEntityRenderer {
CustomEntityRenderer.this.gluPerspective(fovY, aspect, zNear, zFar);
}
};
protected final FrameRenderingStrategy renderingStrategy;
public GluPerspectiveHook gluPerspectiveHook;
public LoadShaderHook loadShaderHook;
protected int frameCount;
protected D data;
protected final RenderOptions options;
public final int resultWidth;
public final int resultHeight;
public CustomEntityRenderer(RenderOptions options) {
this(options, options.getWidth(), options.getHeight());
}
public CustomEntityRenderer(RenderOptions options, int resultWidth, int resultHeight) {
this.options = options;
this.resultWidth = resultWidth;
this.resultHeight = resultHeight;
if (OpenGlHelper.isFramebufferEnabled()) {
if (resultWidth > OpenGLUtils.VIEWPORT_MAX_WIDTH || resultHeight > OpenGLUtils.VIEWPORT_MAX_HEIGHT) {
// Video resolution only limited by available RAM and disk size
renderingStrategy = new TiledFrameBufferRenderingStrategy(this);
} else {
// This strategy supports shader
renderingStrategy = new VanillaFrameBufferRenderingStrategy(this);
}
} else {
if (resultWidth > mc.displayHeight || resultHeight > mc.displayHeight) {
// Video resolution only limited by available RAM and disk size
renderingStrategy = new TiledReadPixelsRenderingStrategy(this);
} else {
// Simplest and fastest strategy if frame buffers aren't supported
renderingStrategy = new VanillaReadPixelsRenderingStrategy(this);
}
}
System.out.println("CustomEntityRenderer using " + renderingStrategy);
// Install entity renderer hooks
try {
@@ -108,12 +83,6 @@ public abstract class CustomEntityRenderer {
}
}
public void renderFrame(float partialTicks, BufferedImage into, int x, int y) {
if (mc.theWorld != null) {
renderingStrategy.renderFrame(partialTicks, into, x, y);
}
}
public void withDisplaySize(int displayWidth, int displayHeight, Runnable runnable) {
final int prevWidth = mc.displayWidth;
final int prevHeight = mc.displayHeight;
@@ -366,6 +335,13 @@ public abstract class CustomEntityRenderer {
}
protected void setupCameraTransform(float partialTicks) {
Entity entity = mc.getRenderViewEntity();
float orgYaw = entity.rotationYaw;
float orgPitch = entity.rotationPitch;
float orgPrevYaw = entity.prevRotationYaw;
float orgPrevPitch = entity.prevRotationPitch;
float orgRoll = ReplayHandler.getCameraTilt();
proxied.farPlaneDistance = (float)(this.mc.gameSettings.renderDistanceChunks * 16);
matrixMode(GL_PROJECTION);
@@ -377,6 +353,12 @@ public abstract class CustomEntityRenderer {
loadIdentity();
orientCamera(partialTicks);
entity.rotationYaw = orgYaw;
entity.rotationPitch = orgPitch;
entity.prevRotationYaw = orgPrevYaw;
entity.prevRotationPitch = orgPrevPitch;
ReplayHandler.setCameraTilt(orgRoll);
}
protected void setupFog(int fogDistanceFlag, float partialTicks) {
@@ -386,6 +368,14 @@ public abstract class CustomEntityRenderer {
}
protected void orientCamera(float partialTicks) {
if (options.isIgnoreCameraRotation()) {
Entity entity = mc.getRenderViewEntity();
// Stop the minecraft code from doing any rotation
entity.prevRotationPitch = entity.rotationPitch = 0;
entity.prevRotationYaw = entity.rotationYaw = 0;
ReplayHandler.setCameraTilt(0);
}
proxied.orientCamera(partialTicks);
}
@@ -407,7 +397,8 @@ public abstract class CustomEntityRenderer {
}
}
public void cleanup() {
@Override
public void close() throws IOException {
try {
Field hookField = EntityRenderer.class.getField("hook");
hookField.set(proxied, null);
@@ -417,10 +408,30 @@ public abstract class CustomEntityRenderer {
throw new Error(e);
}
renderingStrategy.cleanup();
spectatorRenderer.cleanup();
}
@Override
public void renderWorld(ReadableDimension displaySize, final float partialTicks, D data) {
this.data = data;
withDisplaySize(displaySize.getWidth(), displaySize.getHeight(), new Runnable() {
@Override
public void run() {
renderWorld(partialTicks, 0);
if (OpenGlHelper.shadersSupported) {
if (proxied.theShaderGroup != null && proxied.useShader) {
matrixMode(GL_TEXTURE);
pushMatrix();
loadIdentity();
proxied.theShaderGroup.loadShaderGroup(partialTicks);
popMatrix();
}
}
}
});
}
public static final class NoCullingCamera implements ICamera {
@Override