Render to BGRA instead of RGB format for better performance on most GPUs

This commit is contained in:
Jonas Herzig
2019-03-13 15:27:53 +01:00
parent 27efc3c717
commit 443c1ca2af
11 changed files with 30 additions and 51 deletions

View File

@@ -72,7 +72,7 @@ public class RenderSettings {
} }
public String getValue() { public String getValue() {
return "-y -f rawvideo -pix_fmt rgb24 -s %WIDTH%x%HEIGHT% -r %FPS% -i - %FILTERS%" + preset; return "-y -f rawvideo -pix_fmt bgra -s %WIDTH%x%HEIGHT% -r %FPS% -i - %FILTERS%" + preset;
} }
public String getFileExtension() { public String getFileExtension() {

View File

@@ -4,8 +4,8 @@ import com.replaymod.render.frame.OpenGlFrame;
import com.replaymod.render.rendering.Frame; import com.replaymod.render.rendering.Frame;
import com.replaymod.render.utils.ByteBufferPool; import com.replaymod.render.utils.ByteBufferPool;
import com.replaymod.render.utils.PixelBufferObject; import com.replaymod.render.utils.PixelBufferObject;
import net.minecraft.client.renderer.OpenGlHelper;
import org.lwjgl.opengl.GL11; import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL12;
import java.io.IOException; import java.io.IOException;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
@@ -19,7 +19,7 @@ public abstract class MultiFramePboOpenGlFrameCapturer<F extends Frame, D extend
super(worldRenderer, renderInfo); super(worldRenderer, renderInfo);
data = type.getEnumConstants(); data = type.getEnumConstants();
int bufferSize = framePixels * 3 * data.length; int bufferSize = framePixels * 4 * data.length;
pbo = new PixelBufferObject(bufferSize, PixelBufferObject.Usage.READ); pbo = new PixelBufferObject(bufferSize, PixelBufferObject.Usage.READ);
otherPBO = new PixelBufferObject(bufferSize, PixelBufferObject.Usage.READ); otherPBO = new PixelBufferObject(bufferSize, PixelBufferObject.Usage.READ);
} }
@@ -47,7 +47,7 @@ public abstract class MultiFramePboOpenGlFrameCapturer<F extends Frame, D extend
ByteBuffer pboBuffer = pbo.mapReadOnly(); ByteBuffer pboBuffer = pbo.mapReadOnly();
OpenGlFrame[] frames = new OpenGlFrame[data.length]; OpenGlFrame[] frames = new OpenGlFrame[data.length];
int frameBufferSize = getFrameWidth() * getFrameHeight() * 3; int frameBufferSize = getFrameWidth() * getFrameHeight() * 4;
for (int i = 0; i < frames.length; i++) { for (int i = 0; i < frames.length; i++) {
ByteBuffer frameBuffer = ByteBufferPool.allocate(frameBufferSize); ByteBuffer frameBuffer = ByteBufferPool.allocate(frameBufferSize);
pboBuffer.limit(pboBuffer.position() + frameBufferSize); pboBuffer.limit(pboBuffer.position() + frameBufferSize);
@@ -79,17 +79,10 @@ public abstract class MultiFramePboOpenGlFrameCapturer<F extends Frame, D extend
protected OpenGlFrame captureFrame(int frameId, D captureData) { protected OpenGlFrame captureFrame(int frameId, D captureData) {
pbo.bind(); pbo.bind();
GL11.glPixelStorei(GL11.GL_PACK_ALIGNMENT, 1); int offset = captureData.ordinal() * getFrameWidth() * getFrameHeight() * 4;
GL11.glPixelStorei(GL11.GL_UNPACK_ALIGNMENT, 1); frameBuffer().bindFramebuffer(true);
GL11.glReadPixels(0, 0, getFrameWidth(), getFrameHeight(), GL12.GL_BGRA, GL11.GL_UNSIGNED_BYTE, offset);
int offset = captureData.ordinal() * getFrameWidth() * getFrameHeight() * 3; frameBuffer().unbindFramebuffer();
if (OpenGlHelper.isFramebufferEnabled()) {
frameBuffer().bindFramebufferTexture();
GL11.glGetTexImage(GL11.GL_TEXTURE_2D, 0, GL11.GL_RGB, GL11.GL_UNSIGNED_BYTE, offset);
frameBuffer().unbindFramebufferTexture();
} else {
GL11.glReadPixels(0, 0, getFrameWidth(), getFrameHeight(), GL11.GL_RGB, GL11.GL_UNSIGNED_BYTE, offset);
}
pbo.unbind(); pbo.unbind();
return null; return null;

View File

@@ -9,9 +9,9 @@ import de.johni0702.minecraft.gui.utils.lwjgl.Dimension;
import de.johni0702.minecraft.gui.utils.lwjgl.ReadableDimension; import de.johni0702.minecraft.gui.utils.lwjgl.ReadableDimension;
import de.johni0702.minecraft.gui.utils.lwjgl.WritableDimension; import de.johni0702.minecraft.gui.utils.lwjgl.WritableDimension;
import net.minecraft.client.Minecraft; import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.OpenGlHelper;
import net.minecraft.client.shader.Framebuffer; import net.minecraft.client.shader.Framebuffer;
import org.lwjgl.opengl.GL11; import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL12;
import java.io.IOException; import java.io.IOException;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
@@ -97,17 +97,10 @@ public abstract class OpenGlFrameCapturer<F extends Frame, D extends CaptureData
} }
protected OpenGlFrame captureFrame(int frameId, D captureData) { protected OpenGlFrame captureFrame(int frameId, D captureData) {
GL11.glPixelStorei(GL11.GL_PACK_ALIGNMENT, 1); ByteBuffer buffer = ByteBufferPool.allocate(getFrameWidth() * getFrameHeight() * 4);
GL11.glPixelStorei(GL11.GL_UNPACK_ALIGNMENT, 1); frameBuffer().bindFramebuffer(true);
GL11.glReadPixels(0, 0, getFrameWidth(), getFrameHeight(), GL12.GL_BGRA, GL11.GL_UNSIGNED_BYTE, buffer);
ByteBuffer buffer = ByteBufferPool.allocate(getFrameWidth() * getFrameHeight() * 3); frameBuffer().unbindFramebuffer();
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(); buffer.rewind();
return new OpenGlFrame(frameId, new Dimension(getFrameWidth(), getFrameHeight()), buffer); return new OpenGlFrame(frameId, new Dimension(getFrameWidth(), getFrameHeight()), buffer);

View File

@@ -4,8 +4,8 @@ import com.replaymod.render.frame.OpenGlFrame;
import com.replaymod.render.utils.ByteBufferPool; import com.replaymod.render.utils.ByteBufferPool;
import com.replaymod.render.utils.PixelBufferObject; import com.replaymod.render.utils.PixelBufferObject;
import de.johni0702.minecraft.gui.utils.lwjgl.ReadableDimension; import de.johni0702.minecraft.gui.utils.lwjgl.ReadableDimension;
import net.minecraft.client.renderer.OpenGlHelper;
import org.lwjgl.opengl.GL11; import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL12;
import java.io.IOException; import java.io.IOException;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
@@ -18,7 +18,7 @@ public class SimplePboOpenGlFrameCapturer extends OpenGlFrameCapturer<OpenGlFram
super(worldRenderer, renderInfo); super(worldRenderer, renderInfo);
ReadableDimension size = renderInfo.getFrameSize(); ReadableDimension size = renderInfo.getFrameSize();
bufferSize = size.getHeight() * size.getWidth() * 3; bufferSize = size.getHeight() * size.getWidth() * 4;
pbo = new PixelBufferObject(bufferSize, PixelBufferObject.Usage.READ); pbo = new PixelBufferObject(bufferSize, PixelBufferObject.Usage.READ);
otherPBO = new PixelBufferObject(bufferSize, PixelBufferObject.Usage.READ); otherPBO = new PixelBufferObject(bufferSize, PixelBufferObject.Usage.READ);
} }
@@ -64,16 +64,9 @@ public class SimplePboOpenGlFrameCapturer extends OpenGlFrameCapturer<OpenGlFram
protected OpenGlFrame captureFrame(int frameId, CaptureData data) { protected OpenGlFrame captureFrame(int frameId, CaptureData data) {
pbo.bind(); pbo.bind();
GL11.glPixelStorei(GL11.GL_PACK_ALIGNMENT, 1); frameBuffer().bindFramebuffer(true);
GL11.glPixelStorei(GL11.GL_UNPACK_ALIGNMENT, 1); GL11.glReadPixels(0, 0, getFrameWidth(), getFrameHeight(), GL12.GL_BGRA, GL11.GL_UNSIGNED_BYTE, 0);
frameBuffer().unbindFramebuffer();
if (OpenGlHelper.isFramebufferEnabled()) {
frameBuffer().bindFramebufferTexture();
GL11.glGetTexImage(GL11.GL_TEXTURE_2D, 0, GL11.GL_RGB, GL11.GL_UNSIGNED_BYTE, 0);
frameBuffer().unbindFramebufferTexture();
} else {
GL11.glReadPixels(0, 0, getFrameWidth(), getFrameHeight(), GL11.GL_RGB, GL11.GL_UNSIGNED_BYTE, 0);
}
pbo.unbind(); pbo.unbind();
return null; return null;

View File

@@ -18,9 +18,9 @@ public class RGBFrame implements Frame {
private final ByteBuffer byteBuffer; private final ByteBuffer byteBuffer;
public RGBFrame(int frameId, ReadableDimension size, ByteBuffer byteBuffer) { public RGBFrame(int frameId, ReadableDimension size, ByteBuffer byteBuffer) {
Validate.isTrue(size.getWidth() * size.getHeight() * 3 == byteBuffer.remaining(), Validate.isTrue(size.getWidth() * size.getHeight() * 4 == byteBuffer.remaining(),
"Buffer size is %d (cap: %d) but should be %d", "Buffer size is %d (cap: %d) but should be %d",
byteBuffer.remaining(), byteBuffer.capacity(), size.getWidth() * size.getHeight() * 3); byteBuffer.remaining(), byteBuffer.capacity(), size.getWidth() * size.getHeight() * 4);
this.frameId = frameId; this.frameId = frameId;
this.size = size; this.size = size;
this.byteBuffer = byteBuffer; this.byteBuffer = byteBuffer;

View File

@@ -15,7 +15,7 @@ public class CubicToRGBProcessor extends AbstractFrameProcessor<CubicOpenGlFrame
int size = rawFrame.getLeft().getSize().getWidth(); int size = rawFrame.getLeft().getSize().getWidth();
int width = size * 4; int width = size * 4;
int height = size * 3; int height = size * 3;
ByteBuffer result = ByteBufferPool.allocate(width * height * 3); ByteBuffer result = ByteBufferPool.allocate(width * height * 4);
openGlBytesToRBG(rawFrame.getLeft().getByteBuffer(), size, 0, size, result, width); openGlBytesToRBG(rawFrame.getLeft().getByteBuffer(), size, 0, size, result, width);
openGlBytesToRBG(rawFrame.getFront().getByteBuffer(), size, size, size, result, width); openGlBytesToRBG(rawFrame.getFront().getByteBuffer(), size, size, size, result, width);
openGlBytesToRBG(rawFrame.getRight().getByteBuffer(), size, size * 2, size, result, width); openGlBytesToRBG(rawFrame.getRight().getByteBuffer(), size, size * 2, size, result, width);

View File

@@ -108,13 +108,13 @@ public class EquirectangularToRGBProcessor extends AbstractFrameProcessor<CubicO
public RGBFrame process(CubicOpenGlFrame rawFrame) { public RGBFrame process(CubicOpenGlFrame rawFrame) {
Validate.isTrue(rawFrame.getLeft().getSize().getWidth() == frameSize, "Frame size must be %d but was %d", Validate.isTrue(rawFrame.getLeft().getSize().getWidth() == frameSize, "Frame size must be %d but was %d",
frameSize, rawFrame.getLeft().getSize().getWidth()); frameSize, rawFrame.getLeft().getSize().getWidth());
ByteBuffer result = ByteBufferPool.allocate(width * height * 3); ByteBuffer result = ByteBufferPool.allocate(width * height * 4);
ByteBuffer[] images = { ByteBuffer[] images = {
rawFrame.getBack().getByteBuffer(), rawFrame.getFront().getByteBuffer(), rawFrame.getBack().getByteBuffer(), rawFrame.getFront().getByteBuffer(),
rawFrame.getLeft().getByteBuffer(), rawFrame.getRight().getByteBuffer(), rawFrame.getLeft().getByteBuffer(), rawFrame.getRight().getByteBuffer(),
rawFrame.getTop().getByteBuffer(), rawFrame.getBottom().getByteBuffer() rawFrame.getTop().getByteBuffer(), rawFrame.getBottom().getByteBuffer()
}; };
byte[] pixel = new byte[3]; byte[] pixel = new byte[4];
byte[] image; byte[] image;
int[] imageX, imageY; int[] imageX, imageY;
for (int y = 0; y < height; y++) { for (int y = 0; y < height; y++) {
@@ -123,7 +123,7 @@ public class EquirectangularToRGBProcessor extends AbstractFrameProcessor<CubicO
imageY = this.imageY[y]; imageY = this.imageY[y];
for (int x = 0; x < width; x++) { for (int x = 0; x < width; x++) {
ByteBuffer source = images[image[x]]; ByteBuffer source = images[image[x]];
source.position((imageX[x] + imageY[x] * frameSize) * 3); source.position((imageX[x] + imageY[x] * frameSize) * 4);
source.get(pixel); source.get(pixel);
result.put(pixel); result.put(pixel);
} }

View File

@@ -21,7 +21,7 @@ public class ODSToRGBProcessor extends AbstractFrameProcessor<ODSOpenGlFrame, RG
RGBFrame leftFrame = processor.process(rawFrame.getLeft()); RGBFrame leftFrame = processor.process(rawFrame.getLeft());
RGBFrame rightFrame = processor.process(rawFrame.getRight()); RGBFrame rightFrame = processor.process(rawFrame.getRight());
ReadableDimension size = new Dimension(leftFrame.getSize().getWidth(), leftFrame.getSize().getHeight() * 2); ReadableDimension size = new Dimension(leftFrame.getSize().getWidth(), leftFrame.getSize().getHeight() * 2);
ByteBuffer result = ByteBufferPool.allocate(size.getWidth() * size.getHeight() * 3); ByteBuffer result = ByteBufferPool.allocate(size.getWidth() * size.getHeight() * 4);
result.put(leftFrame.getByteBuffer()); result.put(leftFrame.getByteBuffer());
result.put(rightFrame.getByteBuffer()); result.put(rightFrame.getByteBuffer());
result.rewind(); result.rewind();

View File

@@ -15,7 +15,7 @@ public class OpenGlToRGBProcessor extends AbstractFrameProcessor<OpenGlFrame, RG
// Flip whole image in place // Flip whole image in place
ReadableDimension size = rawFrame.getSize(); ReadableDimension size = rawFrame.getSize();
int rowSize = size.getWidth() * 3; int rowSize = size.getWidth() * 4;
if (row == null || row.length < rowSize) { if (row == null || row.length < rowSize) {
row = new byte[rowSize]; row = new byte[rowSize];
rowSwap = new byte[rowSize]; rowSwap = new byte[rowSize];

View File

@@ -17,7 +17,7 @@ public class StereoscopicToRGBProcessor extends AbstractFrameProcessor<Stereosco
int width = size.getWidth(); int width = size.getWidth();
ByteBuffer leftBuffer = rawFrame.getLeft().getByteBuffer(); ByteBuffer leftBuffer = rawFrame.getLeft().getByteBuffer();
ByteBuffer rightBuffer = rawFrame.getRight().getByteBuffer(); ByteBuffer rightBuffer = rawFrame.getRight().getByteBuffer();
ByteBuffer result = ByteBufferPool.allocate(width * 2 * size.getHeight() * 3); ByteBuffer result = ByteBufferPool.allocate(width * 2 * size.getHeight() * 4);
openGlBytesToRBG(leftBuffer, width, 0, 0, result, width * 2); openGlBytesToRBG(leftBuffer, width, 0, 0, result, width * 2);
openGlBytesToRBG(rightBuffer, width, size.getWidth(), 0, result, width * 2); openGlBytesToRBG(rightBuffer, width, size.getWidth(), 0, result, width * 2);
ByteBufferPool.release(leftBuffer); ByteBufferPool.release(leftBuffer);

View File

@@ -13,12 +13,12 @@ public class Utils {
* @param width Target image width * @param width Target image width
*/ */
public static void openGlBytesToRBG(ByteBuffer buffer, int bufferWidth, int xOffset, int yOffset, ByteBuffer to, int width) { public static void openGlBytesToRBG(ByteBuffer buffer, int bufferWidth, int xOffset, int yOffset, ByteBuffer to, int width) {
byte[] rowBuf = new byte[bufferWidth * 3]; byte[] rowBuf = new byte[bufferWidth * 4];
// Copy image flipped vertically to target buffer // Copy image flipped vertically to target buffer
int rows = buffer.remaining() / 3 / bufferWidth; int rows = buffer.remaining() / 4 / bufferWidth;
for (int i = 0; i < rows; i++) { for (int i = 0; i < rows; i++) {
buffer.get(rowBuf); buffer.get(rowBuf);
to.position(((yOffset + rows - i - 1) * width + xOffset) * 3); to.position(((yOffset + rows - i - 1) * width + xOffset) * 4);
to.put(rowBuf); to.put(rowBuf);
} }
to.rewind(); to.rewind();