From d61e82ef3bc694acc2056c8f70584cac288d866a Mon Sep 17 00:00:00 2001 From: johni0702 Date: Sat, 18 Jul 2015 16:36:54 +0200 Subject: [PATCH] Add PBO frame capturer for stereoscopic, cubic and equirectangular rendering --- .../capturer/CubicPboOpenGlFrameCapturer.java | 30 +++++ .../MultiFramePboOpenGlFrameCapturer.java | 103 ++++++++++++++++++ .../video/capturer/OpenGlFrameCapturer.java | 4 +- .../SimplePboOpenGlFrameCapturer.java | 2 +- .../StereoscopicPboOpenGlFrameCapturer.java | 24 ++++ .../replaymod/video/rendering/Pipelines.java | 36 +++--- 6 files changed, 181 insertions(+), 18 deletions(-) create mode 100644 src/main/java/eu/crushedpixel/replaymod/video/capturer/CubicPboOpenGlFrameCapturer.java create mode 100644 src/main/java/eu/crushedpixel/replaymod/video/capturer/MultiFramePboOpenGlFrameCapturer.java create mode 100644 src/main/java/eu/crushedpixel/replaymod/video/capturer/StereoscopicPboOpenGlFrameCapturer.java diff --git a/src/main/java/eu/crushedpixel/replaymod/video/capturer/CubicPboOpenGlFrameCapturer.java b/src/main/java/eu/crushedpixel/replaymod/video/capturer/CubicPboOpenGlFrameCapturer.java new file mode 100644 index 00000000..193d5496 --- /dev/null +++ b/src/main/java/eu/crushedpixel/replaymod/video/capturer/CubicPboOpenGlFrameCapturer.java @@ -0,0 +1,30 @@ +package eu.crushedpixel.replaymod.video.capturer; + +import eu.crushedpixel.replaymod.video.frame.CubicOpenGlFrame; +import eu.crushedpixel.replaymod.video.frame.OpenGlFrame; + +public class CubicPboOpenGlFrameCapturer extends + MultiFramePboOpenGlFrameCapturer { + + private final int frameSize; + public CubicPboOpenGlFrameCapturer(WorldRenderer worldRenderer, + RenderInfo renderInfo, int frameSize) { + super(worldRenderer, renderInfo, CubicOpenGlFrameCapturer.Data.class, frameSize * frameSize); + this.frameSize = frameSize; + } + + @Override + protected int getFrameWidth() { + return frameSize; + } + + @Override + protected int getFrameHeight() { + return frameSize; + } + + @Override + protected CubicOpenGlFrame create(OpenGlFrame[] from) { + return new CubicOpenGlFrame(from[0], from[1], from[2], from[3], from[4], from[5]); + } +} diff --git a/src/main/java/eu/crushedpixel/replaymod/video/capturer/MultiFramePboOpenGlFrameCapturer.java b/src/main/java/eu/crushedpixel/replaymod/video/capturer/MultiFramePboOpenGlFrameCapturer.java new file mode 100644 index 00000000..1673a26e --- /dev/null +++ b/src/main/java/eu/crushedpixel/replaymod/video/capturer/MultiFramePboOpenGlFrameCapturer.java @@ -0,0 +1,103 @@ +package eu.crushedpixel.replaymod.video.capturer; + +import eu.crushedpixel.replaymod.opengl.PixelBufferObject; +import eu.crushedpixel.replaymod.utils.ByteBufferPool; +import eu.crushedpixel.replaymod.video.frame.OpenGlFrame; +import eu.crushedpixel.replaymod.video.rendering.Frame; +import net.minecraft.client.renderer.OpenGlHelper; +import org.lwjgl.opengl.GL11; + +import java.io.IOException; +import java.nio.ByteBuffer; + +public abstract class MultiFramePboOpenGlFrameCapturer & CaptureData> + extends OpenGlFrameCapturer { + private final D[] data; + private PixelBufferObject pbo, otherPBO; + + public MultiFramePboOpenGlFrameCapturer(WorldRenderer worldRenderer, RenderInfo renderInfo, Class type, int framePixels) { + super(worldRenderer, renderInfo); + + data = type.getEnumConstants(); + int bufferSize = framePixels * 3 * data.length; + pbo = new PixelBufferObject(bufferSize, PixelBufferObject.Usage.READ); + otherPBO = new PixelBufferObject(bufferSize, PixelBufferObject.Usage.READ); + } + + protected abstract F create(OpenGlFrame[] from); + + private void swapPBOs() { + PixelBufferObject old = pbo; + pbo = otherPBO; + otherPBO = old; + } + + @Override + public boolean isDone() { + return framesDone >= renderInfo.getTotalFrames() + 2; + } + + @Override + public F process() { + F frame = null; + + if (framesDone > 1) { + // Read pbo to memory + pbo.bind(); + ByteBuffer pboBuffer = pbo.mapReadOnly(); + + OpenGlFrame[] frames = new OpenGlFrame[data.length]; + int frameBufferSize = getFrameWidth() * getFrameHeight() * 3; + for (int i = 0; i < frames.length; i++) { + ByteBuffer frameBuffer = ByteBufferPool.allocate(frameBufferSize); + pboBuffer.limit(pboBuffer.position() + frameBufferSize); + frameBuffer.put(pboBuffer); + frameBuffer.rewind(); + frames[i] = new OpenGlFrame(framesDone - 2, frameSize, frameBuffer); + } + + pbo.unmap(); + pbo.unbind(); + + frame = create(frames); + } + + if (framesDone < renderInfo.getTotalFrames()) { + float partialTicks = renderInfo.updateForNextFrame(); + // Then fill it again + for (D data : this.data) { + renderFrame(framesDone, partialTicks, data); + } + } + + framesDone++; + swapPBOs(); + return frame; + } + + @Override + protected OpenGlFrame captureFrame(int frameId, D captureData) { + pbo.bind(); + + GL11.glPixelStorei(GL11.GL_PACK_ALIGNMENT, 1); + GL11.glPixelStorei(GL11.GL_UNPACK_ALIGNMENT, 1); + + int offset = captureData.ordinal() * getFrameWidth() * getFrameHeight() * 3; + 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(); + return null; + } + + @Override + public void close() throws IOException { + pbo.delete(); + otherPBO.delete(); + } +} diff --git a/src/main/java/eu/crushedpixel/replaymod/video/capturer/OpenGlFrameCapturer.java b/src/main/java/eu/crushedpixel/replaymod/video/capturer/OpenGlFrameCapturer.java index 0e8c9b2c..0fe0b5a5 100644 --- a/src/main/java/eu/crushedpixel/replaymod/video/capturer/OpenGlFrameCapturer.java +++ b/src/main/java/eu/crushedpixel/replaymod/video/capturer/OpenGlFrameCapturer.java @@ -82,10 +82,10 @@ public abstract class OpenGlFrameCapturer { + + public StereoscopicPboOpenGlFrameCapturer(WorldRenderer worldRenderer, + RenderInfo renderInfo) { + super(worldRenderer, renderInfo, StereoscopicOpenGlFrameCapturer.Data.class, + renderInfo.getFrameSize().getWidth() / 2 * renderInfo.getFrameSize().getHeight()); + } + + @Override + protected int getFrameWidth() { + return super.getFrameWidth() / 2; + } + + @Override + protected StereoscopicOpenGlFrame create(OpenGlFrame[] from) { + return new StereoscopicOpenGlFrame(from[0], from[1]); + } +} diff --git a/src/main/java/eu/crushedpixel/replaymod/video/rendering/Pipelines.java b/src/main/java/eu/crushedpixel/replaymod/video/rendering/Pipelines.java index c85da75d..a212c605 100644 --- a/src/main/java/eu/crushedpixel/replaymod/video/rendering/Pipelines.java +++ b/src/main/java/eu/crushedpixel/replaymod/video/rendering/Pipelines.java @@ -49,28 +49,34 @@ public class Pipelines { public static Pipeline newStereoscopicPipeline(RenderInfo renderInfo, FrameConsumer consumer) { RenderOptions options = renderInfo.getRenderOptions(); - return new Pipeline( - new StereoscopicOpenGlFrameCapturer(new StereoscopicEntityRenderer(options), renderInfo), - new StereoscopicToRGBProcessor(), - consumer - ); + FrameCapturer capturer; + if (PixelBufferObject.SUPPORTED) { + capturer = new StereoscopicPboOpenGlFrameCapturer(new StereoscopicEntityRenderer(options), renderInfo); + } else { + capturer = new StereoscopicOpenGlFrameCapturer(new StereoscopicEntityRenderer(options), renderInfo); + } + return new Pipeline(capturer, new StereoscopicToRGBProcessor(), consumer); } public static Pipeline newCubicPipeline(RenderInfo renderInfo, FrameConsumer consumer) { RenderOptions options = renderInfo.getRenderOptions(); - return new Pipeline( - new CubicOpenGlFrameCapturer(new CubicEntityRenderer(options), renderInfo, options.getWidth() / 4), - new CubicToRGBProcessor(), - consumer - ); + FrameCapturer capturer; + if (PixelBufferObject.SUPPORTED) { + capturer = new CubicPboOpenGlFrameCapturer(new CubicEntityRenderer(options), renderInfo, options.getWidth() / 4); + } else { + capturer = new CubicOpenGlFrameCapturer(new CubicEntityRenderer(options), renderInfo, options.getWidth() / 4); + } + return new Pipeline(capturer, new CubicToRGBProcessor(), consumer); } public static Pipeline newEquirectangularPipeline(RenderInfo renderInfo, FrameConsumer consumer) { RenderOptions options = renderInfo.getRenderOptions(); - return new Pipeline( - new CubicOpenGlFrameCapturer(new CubicEntityRenderer(options), renderInfo, options.getWidth() / 4), - new EquirectangularToRGBProcessor(options.getWidth() / 4), - consumer - ); + FrameCapturer capturer; + if (PixelBufferObject.SUPPORTED) { + capturer = new CubicPboOpenGlFrameCapturer(new CubicEntityRenderer(options), renderInfo, options.getWidth() / 4); + } else { + capturer = new CubicOpenGlFrameCapturer(new CubicEntityRenderer(options), renderInfo, options.getWidth() / 4); + } + return new Pipeline(capturer, new EquirectangularToRGBProcessor(options.getWidth() / 4), consumer); } }