Add PBOs and PBO support for default rendering
This commit is contained in:
@@ -0,0 +1,160 @@
|
|||||||
|
package eu.crushedpixel.replaymod.opengl;
|
||||||
|
|
||||||
|
import eu.crushedpixel.replaymod.utils.Api;
|
||||||
|
import eu.crushedpixel.replaymod.utils.Objects;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import net.minecraft.client.Minecraft;
|
||||||
|
import org.apache.logging.log4j.LogManager;
|
||||||
|
import org.lwjgl.opengl.ARBBufferObject;
|
||||||
|
import org.lwjgl.opengl.GLContext;
|
||||||
|
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
|
||||||
|
import static org.lwjgl.opengl.ARBPixelBufferObject.*;
|
||||||
|
import static org.lwjgl.opengl.GL15.*;
|
||||||
|
import static org.lwjgl.opengl.GL21.GL_PIXEL_PACK_BUFFER;
|
||||||
|
|
||||||
|
@Api
|
||||||
|
public class PixelBufferObject {
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public enum Usage {
|
||||||
|
COPY(GL_STREAM_COPY_ARB, GL_STREAM_COPY),
|
||||||
|
DRAW(GL_STREAM_DRAW_ARB, GL_STREAM_DRAW),
|
||||||
|
READ(GL_STREAM_READ_ARB, GL_STREAM_READ);
|
||||||
|
|
||||||
|
private final int arb, gl15;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final boolean SUPPORTED = GLContext.getCapabilities().GL_ARB_pixel_buffer_object || GLContext.getCapabilities().OpenGL15;
|
||||||
|
private static final boolean arb = !GLContext.getCapabilities().OpenGL15;
|
||||||
|
|
||||||
|
private static ThreadLocal<Integer> bound = new ThreadLocal<Integer>();
|
||||||
|
private static ThreadLocal<Integer> mapped = new ThreadLocal<Integer>();
|
||||||
|
|
||||||
|
private final long size;
|
||||||
|
private long handle;
|
||||||
|
|
||||||
|
public PixelBufferObject(long size, Usage usage) {
|
||||||
|
if (!SUPPORTED) {
|
||||||
|
throw new UnsupportedOperationException("PBOs not supported.");
|
||||||
|
}
|
||||||
|
|
||||||
|
this.size = size;
|
||||||
|
this.handle = arb ? ARBBufferObject.glGenBuffersARB() : glGenBuffers();
|
||||||
|
|
||||||
|
bind();
|
||||||
|
|
||||||
|
if (arb) {
|
||||||
|
ARBBufferObject.glBufferDataARB(GL_PIXEL_PACK_BUFFER_ARB, size, usage.arb);
|
||||||
|
} else {
|
||||||
|
glBufferData(GL_PIXEL_PACK_BUFFER, size, usage.gl15);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Api
|
||||||
|
private int getHandle() {
|
||||||
|
if (handle == -1) {
|
||||||
|
throw new IllegalStateException("PBO not allocated.");
|
||||||
|
}
|
||||||
|
return (int) handle;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Api
|
||||||
|
public void bind() {
|
||||||
|
if (arb) {
|
||||||
|
ARBBufferObject.glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, getHandle());
|
||||||
|
} else {
|
||||||
|
glBindBuffer(GL_PIXEL_PACK_BUFFER, getHandle());
|
||||||
|
}
|
||||||
|
bound.set(getHandle());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Api
|
||||||
|
public void unbind() {
|
||||||
|
checkBound();
|
||||||
|
if (arb) {
|
||||||
|
ARBBufferObject.glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0);
|
||||||
|
} else {
|
||||||
|
glBindBuffer(GL_PIXEL_PACK_BUFFER, 0);
|
||||||
|
}
|
||||||
|
bound.set(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void checkBound() {
|
||||||
|
if (!Objects.equals(getHandle(), bound.get())) {
|
||||||
|
throw new IllegalStateException("Buffer not bound.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Api
|
||||||
|
public ByteBuffer mapReadOnly() {
|
||||||
|
checkBound();
|
||||||
|
mapped.set(getHandle());
|
||||||
|
if (arb) {
|
||||||
|
return ARBBufferObject.glMapBufferARB(GL_PIXEL_PACK_BUFFER_ARB, GL_READ_ONLY_ARB, size, null);
|
||||||
|
} else {
|
||||||
|
return glMapBuffer(GL_PIXEL_PACK_BUFFER, GL_READ_ONLY, size, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Api
|
||||||
|
public ByteBuffer mapWriteOnly() {
|
||||||
|
checkBound();
|
||||||
|
mapped.set(getHandle());
|
||||||
|
if (arb) {
|
||||||
|
return ARBBufferObject.glMapBufferARB(GL_PIXEL_PACK_BUFFER_ARB, GL_WRITE_ONLY_ARB, size, null);
|
||||||
|
} else {
|
||||||
|
return glMapBuffer(GL_PIXEL_PACK_BUFFER, GL_WRITE_ONLY, size, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Api
|
||||||
|
public ByteBuffer mapReadWrite() {
|
||||||
|
checkBound();
|
||||||
|
mapped.set(getHandle());
|
||||||
|
if (arb) {
|
||||||
|
return ARBBufferObject.glMapBufferARB(GL_PIXEL_PACK_BUFFER_ARB, GL_READ_WRITE_ARB, size, null);
|
||||||
|
} else {
|
||||||
|
return glMapBuffer(GL_PIXEL_PACK_BUFFER, GL_READ_WRITE, size, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Api
|
||||||
|
public void unmap() {
|
||||||
|
checkBound();
|
||||||
|
if (!Objects.equals(mapped.get(), getHandle())) {
|
||||||
|
throw new IllegalStateException("Buffer not mapped.");
|
||||||
|
}
|
||||||
|
if (arb) {
|
||||||
|
ARBBufferObject.glUnmapBufferARB(GL_PIXEL_PACK_BUFFER_ARB);
|
||||||
|
} else {
|
||||||
|
glUnmapBuffer(GL_PIXEL_PACK_BUFFER);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Api
|
||||||
|
public void delete() {
|
||||||
|
if (handle != -1) {
|
||||||
|
if (arb) {
|
||||||
|
ARBBufferObject.glDeleteBuffersARB(getHandle());
|
||||||
|
} else {
|
||||||
|
glDeleteBuffers(getHandle());
|
||||||
|
}
|
||||||
|
handle = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void finalize() throws Throwable {
|
||||||
|
super.finalize();
|
||||||
|
if (handle != -1) {
|
||||||
|
LogManager.getLogger().warn("PBO garbage collected before deleted!");
|
||||||
|
Minecraft.getMinecraft().addScheduledTask(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
delete();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
10
src/main/java/eu/crushedpixel/replaymod/utils/Api.java
Normal file
10
src/main/java/eu/crushedpixel/replaymod/utils/Api.java
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
package eu.crushedpixel.replaymod.utils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Members annotated with @Api are considered API and should not be removed even if their unused as they might
|
||||||
|
* be of use at a later point in time.
|
||||||
|
* The purpose of this annotation is to be added to the IntelliJ inspections exceptions.
|
||||||
|
*/
|
||||||
|
@Api
|
||||||
|
public @interface Api {
|
||||||
|
}
|
||||||
@@ -54,7 +54,7 @@ public abstract class OpenGlFrameCapturer<F extends Frame, D extends CaptureData
|
|||||||
return renderInfo.getFrameSize().getHeight();
|
return renderInfo.getFrameSize().getHeight();
|
||||||
}
|
}
|
||||||
|
|
||||||
private Framebuffer frameBuffer() {
|
protected Framebuffer frameBuffer() {
|
||||||
if (frameBuffer == null) {
|
if (frameBuffer == null) {
|
||||||
frameBuffer = new Framebuffer(getFrameWidth(), getFrameHeight(), true);
|
frameBuffer = new Framebuffer(getFrameWidth(), getFrameHeight(), true);
|
||||||
}
|
}
|
||||||
@@ -82,6 +82,10 @@ public abstract class OpenGlFrameCapturer<F extends Frame, D extends CaptureData
|
|||||||
frameBuffer().unbindFramebuffer();
|
frameBuffer().unbindFramebuffer();
|
||||||
popMatrix();
|
popMatrix();
|
||||||
|
|
||||||
|
return captureFrame(frameId);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected OpenGlFrame captureFrame(int frameId) {
|
||||||
GL11.glPixelStorei(GL11.GL_PACK_ALIGNMENT, 1);
|
GL11.glPixelStorei(GL11.GL_PACK_ALIGNMENT, 1);
|
||||||
GL11.glPixelStorei(GL11.GL_UNPACK_ALIGNMENT, 1);
|
GL11.glPixelStorei(GL11.GL_UNPACK_ALIGNMENT, 1);
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,87 @@
|
|||||||
|
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 net.minecraft.client.renderer.OpenGlHelper;
|
||||||
|
import org.lwjgl.opengl.GL11;
|
||||||
|
import org.lwjgl.util.ReadableDimension;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
|
||||||
|
public class SimplePboOpenGlFrameCapturer extends OpenGlFrameCapturer<OpenGlFrame, CaptureData> {
|
||||||
|
private final int bufferSize;
|
||||||
|
private PixelBufferObject pbo, otherPBO;
|
||||||
|
|
||||||
|
public SimplePboOpenGlFrameCapturer(WorldRenderer<CaptureData> worldRenderer, RenderInfo renderInfo) {
|
||||||
|
super(worldRenderer, renderInfo);
|
||||||
|
|
||||||
|
ReadableDimension size = renderInfo.getFrameSize();
|
||||||
|
bufferSize = size.getHeight() * size.getWidth() * 3;
|
||||||
|
pbo = new PixelBufferObject(bufferSize, PixelBufferObject.Usage.READ);
|
||||||
|
otherPBO = new PixelBufferObject(bufferSize, PixelBufferObject.Usage.READ);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void swapPBOs() {
|
||||||
|
PixelBufferObject old = pbo;
|
||||||
|
pbo = otherPBO;
|
||||||
|
otherPBO = old;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isDone() {
|
||||||
|
return framesDone >= renderInfo.getTotalFrames() + 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public OpenGlFrame process() {
|
||||||
|
OpenGlFrame frame = null;
|
||||||
|
|
||||||
|
if (framesDone > 1) {
|
||||||
|
// Read pbo to memory
|
||||||
|
pbo.bind();
|
||||||
|
ByteBuffer pboBuffer = pbo.mapReadOnly();
|
||||||
|
ByteBuffer buffer = ByteBufferPool.allocate(bufferSize);
|
||||||
|
buffer.put(pboBuffer);
|
||||||
|
buffer.rewind();
|
||||||
|
pbo.unmap();
|
||||||
|
pbo.unbind();
|
||||||
|
frame = new OpenGlFrame(framesDone - 2, frameSize, buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (framesDone < renderInfo.getTotalFrames()) {
|
||||||
|
// Then fill it again
|
||||||
|
renderFrame(framesDone, renderInfo.updateForNextFrame());
|
||||||
|
}
|
||||||
|
|
||||||
|
framesDone++;
|
||||||
|
swapPBOs();
|
||||||
|
return frame;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected OpenGlFrame captureFrame(int frameId) {
|
||||||
|
pbo.bind();
|
||||||
|
|
||||||
|
GL11.glPixelStorei(GL11.GL_PACK_ALIGNMENT, 1);
|
||||||
|
GL11.glPixelStorei(GL11.GL_UNPACK_ALIGNMENT, 1);
|
||||||
|
|
||||||
|
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();
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close() throws IOException {
|
||||||
|
pbo.delete();
|
||||||
|
otherPBO.delete();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
package eu.crushedpixel.replaymod.video.rendering;
|
package eu.crushedpixel.replaymod.video.rendering;
|
||||||
|
|
||||||
|
import eu.crushedpixel.replaymod.opengl.PixelBufferObject;
|
||||||
import eu.crushedpixel.replaymod.settings.RenderOptions;
|
import eu.crushedpixel.replaymod.settings.RenderOptions;
|
||||||
import eu.crushedpixel.replaymod.video.capturer.*;
|
import eu.crushedpixel.replaymod.video.capturer.*;
|
||||||
import eu.crushedpixel.replaymod.video.entity.CubicEntityRenderer;
|
import eu.crushedpixel.replaymod.video.entity.CubicEntityRenderer;
|
||||||
@@ -37,11 +38,13 @@ public class Pipelines {
|
|||||||
|
|
||||||
public static Pipeline<OpenGlFrame, ARGBFrame> newDefaultPipeline(RenderInfo renderInfo, FrameConsumer<ARGBFrame> consumer) {
|
public static Pipeline<OpenGlFrame, ARGBFrame> newDefaultPipeline(RenderInfo renderInfo, FrameConsumer<ARGBFrame> consumer) {
|
||||||
RenderOptions options = renderInfo.getRenderOptions();
|
RenderOptions options = renderInfo.getRenderOptions();
|
||||||
return new Pipeline<OpenGlFrame, ARGBFrame>(
|
FrameCapturer<OpenGlFrame> capturer;
|
||||||
new SimpleOpenGlFrameCapturer(new CustomEntityRenderer<CaptureData>(options), renderInfo),
|
if (PixelBufferObject.SUPPORTED) {
|
||||||
new OpenGlToARGBProcessor(),
|
capturer = new SimplePboOpenGlFrameCapturer(new CustomEntityRenderer<CaptureData>(options), renderInfo);
|
||||||
consumer
|
} else {
|
||||||
);
|
capturer = new SimpleOpenGlFrameCapturer(new CustomEntityRenderer<CaptureData>(options), renderInfo);
|
||||||
|
}
|
||||||
|
return new Pipeline<OpenGlFrame, ARGBFrame>(capturer, new OpenGlToARGBProcessor(), consumer);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Pipeline<StereoscopicOpenGlFrame, ARGBFrame> newStereoscopicPipeline(RenderInfo renderInfo, FrameConsumer<ARGBFrame> consumer) {
|
public static Pipeline<StereoscopicOpenGlFrame, ARGBFrame> newStereoscopicPipeline(RenderInfo renderInfo, FrameConsumer<ARGBFrame> consumer) {
|
||||||
|
|||||||
Reference in New Issue
Block a user