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

@@ -1,18 +1,32 @@
package eu.crushedpixel.replaymod.gui;
import eu.crushedpixel.replaymod.gui.elements.GuiProgressBar;
import eu.crushedpixel.replaymod.utils.BoundingUtils;
import eu.crushedpixel.replaymod.utils.DurationUtils;
import eu.crushedpixel.replaymod.video.VideoRenderer;
import eu.crushedpixel.replaymod.video.frame.FrameRenderer;
import eu.crushedpixel.replaymod.video.frame.ARGBFrame;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.GuiButton;
import net.minecraft.client.gui.GuiScreen;
import net.minecraft.client.renderer.texture.DynamicTexture;
import net.minecraft.client.renderer.texture.TextureUtil;
import net.minecraft.client.resources.I18n;
import net.minecraft.util.ResourceLocation;
import net.minecraftforge.fml.client.config.GuiCheckBox;
import org.lwjgl.util.Dimension;
import org.lwjgl.util.ReadableDimension;
import java.awt.*;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import static net.minecraft.client.renderer.GlStateManager.bindTexture;
import static net.minecraft.client.renderer.GlStateManager.color;
public class GuiVideoRenderer extends GuiScreen {
private static final ResourceLocation noPreviewTexture = new ResourceLocation("replaymod", "logo.jpg");
private final VideoRenderer renderer;
private final String SCREEN_TITLE = I18n.format("replaymod.gui.rendering.title");
@@ -27,6 +41,9 @@ public class GuiVideoRenderer extends GuiScreen {
private GuiCheckBox previewCheckBox;
private GuiProgressBar progressBar;
private DynamicTexture previewTexture;
private boolean previewTextureDirty;
private int renderTimeTaken = 0;
private long prevTime = -1;
private long prevRenderTime = -1;
@@ -87,8 +104,6 @@ public class GuiVideoRenderer extends GuiScreen {
} else if (CANCEL_CONFIRM.equals(cancelButton.displayString)) {
renderer.cancel();
}
} else if (button == previewCheckBox) {
renderer.getFrameRenderer().setPreviewActive(previewCheckBox.isChecked());
}
}
@@ -139,7 +154,6 @@ public class GuiVideoRenderer extends GuiScreen {
String takenString = I18n.format("replaymod.gui.rendering.timetaken")+": "+DurationUtils.convertSecondsToString(renderTimeTaken/1000);
String leftString = I18n.format("replaymod.gui.rendering.timeleft")+": "+DurationUtils.convertSecondsToString(renderTimeLeft);
FrameRenderer frameRenderer = renderer.getFrameRenderer();
int centerX = width / 2;
drawBackground(0);
@@ -159,9 +173,9 @@ public class GuiVideoRenderer extends GuiScreen {
int previewY = previewCheckBox.yPosition - 10 - previewHeight;
if(previewCheckBox.isChecked()) {
frameRenderer.renderPreview(previewX, previewY, previewWidth, previewHeight);
renderPreview(previewX, previewY, previewWidth, previewHeight);
} else {
FrameRenderer.renderNoPreview(previewX, previewY, previewWidth, previewHeight);
renderNoPreview(previewX, previewY, previewWidth, previewHeight);
}
drawString(fontRendererObj, takenString, 12, previewCheckBox.yPosition + 5 + 20, Color.WHITE.getRGB());
@@ -170,4 +184,75 @@ public class GuiVideoRenderer extends GuiScreen {
super.drawScreen(mouseX, mouseY, partialTicks);
}
private synchronized void renderPreview(int x, int y, int width, int height) {
ReadableDimension videoSize = renderer.getFrameSize();
final int videoWidth = videoSize.getWidth();
final int videoHeight = videoSize.getHeight();
if (previewTexture == null) {
previewTexture = new DynamicTexture(videoWidth, videoHeight) {
@Override
public void updateDynamicTexture() {
bindTexture(getGlTextureId());
TextureUtil.uploadTextureSub(0, getTextureData(), videoWidth, videoHeight, 0, 0, true, false, false);
}
};
}
if (previewTextureDirty) {
previewTexture.updateDynamicTexture();
previewTextureDirty = false;
}
Dimension dimension = BoundingUtils.fitIntoBounds(new Dimension(videoSize), new Dimension(width, height));
x += (width - dimension.getWidth()) / 2;
y += (height - dimension.getHeight()) / 2;
width = dimension.getWidth();
height = dimension.getHeight();
color(1, 1, 1, 1);
bindTexture(previewTexture.getGlTextureId());
drawScaledCustomSizeModalRect(x, y, 0, 0, videoWidth, videoHeight, width, height, videoWidth, videoHeight);
}
private void renderNoPreview(int x, int y, int width, int height) {
int actualWidth = width;
int actualHeight = height;
if (width / height > 1280 / 720) {
actualWidth = height * 1280 / 720;
} else {
actualHeight = width * 720 / 1280;
}
x += (width - actualWidth) / 2;
y += (height - actualHeight) / 2;
Minecraft.getMinecraft().getTextureManager().bindTexture(noPreviewTexture);
color(1, 1, 1, 1);
drawScaledCustomSizeModalRect(x, y, 0, 0, 1280, 720, actualWidth, actualHeight, 1280, 720);
}
public void updatePreview(ARGBFrame frame) {
if (previewCheckBox.isChecked() && previewTexture != null) {
ByteBuffer buffer = frame.getByteBuffer();
buffer.mark();
synchronized (this) {
swapEndianness(buffer);
buffer.asIntBuffer().get(previewTexture.getTextureData());
swapEndianness(buffer);
previewTextureDirty = true;
}
buffer.reset();
}
}
private void swapEndianness(ByteBuffer buffer) {
if (buffer.order() == ByteOrder.BIG_ENDIAN) {
buffer.order(ByteOrder.LITTLE_ENDIAN);
} else {
buffer.order(ByteOrder.BIG_ENDIAN);
}
}
}