Last active
August 2, 2025 07:11
-
-
Save WinterAlexander/9d321557bb7a98a34f3e78352ab8ed45 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| public class SSAOFrameBuffer extends FrameBuffer { | |
| public SSAOFrameBuffer(int width, int height, int samples) { | |
| super(makeBuilder(width, height, samples)); | |
| } | |
| private void checkValidBuilder() { | |
| if(getBuilder().getSamples() > 0 && !Gdx.graphics.isGL30Available()) | |
| throw new GdxRuntimeException("Framebuffer multisample requires GLES 3.0"); | |
| if(getBuilder().getSamples() > 0 && getBuilder().getTextureAttachmentSpecs().size > 0) | |
| throw new GdxRuntimeException("Framebuffer multisample with texture attachments not " + | |
| "yet supported"); | |
| boolean runningGL30 = Gdx.graphics.isGL30Available(); | |
| if(!runningGL30) { | |
| final boolean supportsPackedDepthStencil = Gdx.graphics.supportsExtension( | |
| "GL_OES_packed_depth_stencil") || Gdx.graphics.supportsExtension( | |
| "GL_EXT_packed_depth_stencil"); | |
| if(getBuilder().hasPackedStencilDepthRenderBuffer() && !supportsPackedDepthStencil) { | |
| throw new GdxRuntimeException("Packed Stencil/Render render buffers are not " + | |
| "available on GLES 2.0"); | |
| } | |
| if(getBuilder().getTextureAttachmentSpecs().size > 1) | |
| throw new GdxRuntimeException("Multiple render targets not available on GLES 2.0"); | |
| for(FrameBufferTextureAttachmentSpec spec : getBuilder().getTextureAttachmentSpecs()) { | |
| if(ReflectionUtil.get(spec, "isDepth")) | |
| throw new GdxRuntimeException("Depth texture FrameBuffer Attachment not " + | |
| "available on GLES 2.0"); | |
| if(ReflectionUtil.get(spec, "isSencil")) | |
| throw new GdxRuntimeException("Stencil texture FrameBuffer Attachment not " + | |
| "available on GLES 2.0"); | |
| if(ReflectionUtil.get(spec, "isFloat")) { | |
| if(!Gdx.graphics.supportsExtension("OES_texture_float")) { | |
| throw new GdxRuntimeException("Float texture FrameBuffer Attachment not " + | |
| "available on GLES 2.0"); | |
| } | |
| } | |
| } | |
| } | |
| if(getBuilder().hasPackedStencilDepthRenderBuffer()) { | |
| if(getBuilder().hasDepthRenderBuffer() || getBuilder().hasStencilRenderBuffer()) | |
| throw new GdxRuntimeException("Frame buffer couldn't be constructed: packed " + | |
| "stencil depth buffer cannot be specified together with separated depth or" + | |
| " stencil buffer"); | |
| } | |
| } | |
| protected void build() { | |
| GL20 gl = Gdx.gl20; | |
| checkValidBuilder(); | |
| // iOS uses a different framebuffer handle! (not necessarily 0) | |
| if(!defaultFramebufferHandleInitialized) { | |
| defaultFramebufferHandleInitialized = true; | |
| if(Gdx.app.getType() == Application.ApplicationType.iOS) { | |
| IntBuffer intbuf = | |
| ByteBuffer.allocateDirect(16 * Integer.SIZE / 8).order(ByteOrder.nativeOrder()).asIntBuffer(); | |
| gl.glGetIntegerv(GL20.GL_FRAMEBUFFER_BINDING, intbuf); | |
| defaultFramebufferHandle = intbuf.get(0); | |
| } else { | |
| defaultFramebufferHandle = 0; | |
| } | |
| } | |
| framebufferHandle = gl.glGenFramebuffer(); | |
| gl.glBindFramebuffer(GL20.GL_FRAMEBUFFER, framebufferHandle); | |
| int width = getBuilder().getWidth(); | |
| int height = getBuilder().getHeight(); | |
| int samples = getBuilder().getSamples(); | |
| if(getBuilder().hasDepthRenderBuffer()) { | |
| depthbufferHandle = gl.glGenRenderbuffer(); | |
| gl.glBindRenderbuffer(GL20.GL_RENDERBUFFER, depthbufferHandle); | |
| if(samples > 0) { | |
| org.lwjgl.opengl.GL30.glRenderbufferStorageMultisample(GL20.GL_RENDERBUFFER, | |
| samples, ReflectionUtil.get(getBuilder().getDepthRenderBufferSpec(), "internalFormat"), width, | |
| height); | |
| } else { | |
| gl.glRenderbufferStorage(GL20.GL_RENDERBUFFER, | |
| ReflectionUtil.get(getBuilder().getDepthRenderBufferSpec(), "internalFormat"), width, height); | |
| } | |
| } | |
| if(getBuilder().hasStencilRenderBuffer()) { | |
| stencilbufferHandle = gl.glGenRenderbuffer(); | |
| gl.glBindRenderbuffer(GL20.GL_RENDERBUFFER, stencilbufferHandle); | |
| if(samples > 0) { | |
| org.lwjgl.opengl.GL30.glRenderbufferStorageMultisample(GL20.GL_RENDERBUFFER, | |
| samples, | |
| ReflectionUtil.get(getBuilder().getStencilRenderBufferSpec(), "internalFormat"), width, | |
| height); | |
| } else { | |
| gl.glRenderbufferStorage(GL20.GL_RENDERBUFFER, | |
| ReflectionUtil.get(getBuilder().getStencilRenderBufferSpec(), "internalFormat"), | |
| width, height); | |
| } | |
| } | |
| if(getBuilder().hasPackedStencilDepthRenderBuffer()) { | |
| depthStencilPackedBufferHandle = gl.glGenRenderbuffer(); | |
| gl.glBindRenderbuffer(GL20.GL_RENDERBUFFER, depthStencilPackedBufferHandle); | |
| if(samples > 0) { | |
| org.lwjgl.opengl.GL30.glRenderbufferStorageMultisample(GL20.GL_RENDERBUFFER, samples, | |
| ReflectionUtil.get(getBuilder().getPackedStencilDepthRenderBufferSpec(), "internalFormat"), width, | |
| height); | |
| } else { | |
| gl.glRenderbufferStorage(GL20.GL_RENDERBUFFER, | |
| ReflectionUtil.get(getBuilder().getPackedStencilDepthRenderBufferSpec(), "internalFormat"), width, | |
| height); | |
| } | |
| hasDepthStencilPackedBuffer = true; | |
| } | |
| isMRT = getBuilder().getTextureAttachmentSpecs().size > 1; | |
| int colorAttachmentCounter = 0; | |
| if(isMRT) { | |
| for(FrameBufferTextureAttachmentSpec attachmentSpec : | |
| getBuilder().getTextureAttachmentSpecs()) { | |
| Texture texture = createTexture(attachmentSpec); | |
| textureAttachments.add(texture); | |
| if(attachmentSpec.isColorTexture()) { | |
| gl.glFramebufferTexture2D(GL20.GL_FRAMEBUFFER, | |
| GL30.GL_COLOR_ATTACHMENT0 + colorAttachmentCounter, GL30.GL_TEXTURE_2D | |
| , texture.getTextureObjectHandle(), 0); | |
| colorAttachmentCounter++; | |
| } else if(ReflectionUtil.get(attachmentSpec, "isDepth")) { | |
| gl.glFramebufferTexture2D(GL20.GL_FRAMEBUFFER, GL20.GL_DEPTH_ATTACHMENT, | |
| GL20.GL_TEXTURE_2D, texture.getTextureObjectHandle(), 0); | |
| } else if(ReflectionUtil.get(attachmentSpec, "isStencil")) { | |
| gl.glFramebufferTexture2D(GL20.GL_FRAMEBUFFER, GL20.GL_STENCIL_ATTACHMENT, | |
| GL20.GL_TEXTURE_2D, texture.getTextureObjectHandle(), 0); | |
| } | |
| } | |
| } else if(getBuilder().getTextureAttachmentSpecs().size > 0) { | |
| Texture texture = createTexture(getBuilder().getTextureAttachmentSpecs().first()); | |
| textureAttachments.add(texture); | |
| gl.glBindTexture(texture.glTarget, texture.getTextureObjectHandle()); | |
| } | |
| for(FrameBufferRenderBufferAttachmentSpec colorBufferSpec : | |
| getBuilder().getColorRenderBufferSpecs()) { | |
| int colorbufferHandle = gl.glGenRenderbuffer(); | |
| gl.glBindRenderbuffer(GL20.GL_RENDERBUFFER, colorbufferHandle); | |
| if(samples > 0) { | |
| org.lwjgl.opengl.GL30.glRenderbufferStorageMultisample(GL20.GL_RENDERBUFFER, | |
| samples, ReflectionUtil.get(colorBufferSpec, "internalFormat"), width, height); | |
| } else { | |
| gl.glRenderbufferStorage(GL20.GL_RENDERBUFFER, | |
| ReflectionUtil.get(colorBufferSpec, "internalFormat"), | |
| width, height); | |
| } | |
| Gdx.gl.glFramebufferRenderbuffer(GL20.GL_FRAMEBUFFER, | |
| GL20.GL_COLOR_ATTACHMENT0 + colorAttachmentCounter, GL20.GL_RENDERBUFFER, | |
| colorbufferHandle); | |
| colorBufferHandles.add(colorbufferHandle); | |
| colorAttachmentCounter++; | |
| } | |
| if(isMRT || getBuilder().getSamples() > 0) { | |
| IntBuffer buffer = BufferUtils.newIntBuffer(colorAttachmentCounter); | |
| ReflectionUtil.set(this, "defaultDrawBuffers", buffer); | |
| for(int i = 0; i < colorAttachmentCounter; i++) { | |
| buffer.put(GL30.GL_COLOR_ATTACHMENT0 + i); | |
| } | |
| ((Buffer)buffer).position(0); | |
| Gdx.gl30.glDrawBuffers(colorAttachmentCounter, buffer); | |
| } else if(getBuilder().getTextureAttachmentSpecs().size > 0) { | |
| attachFrameBufferColorTexture(textureAttachments.first()); | |
| } | |
| if(getBuilder().hasDepthRenderBuffer()) { | |
| gl.glFramebufferRenderbuffer(GL20.GL_FRAMEBUFFER, GL20.GL_DEPTH_ATTACHMENT, | |
| GL20.GL_RENDERBUFFER, depthbufferHandle); | |
| } | |
| if(getBuilder().hasStencilRenderBuffer()) { | |
| gl.glFramebufferRenderbuffer(GL20.GL_FRAMEBUFFER, GL20.GL_STENCIL_ATTACHMENT, | |
| GL20.GL_RENDERBUFFER, stencilbufferHandle); | |
| } | |
| if(getBuilder().hasPackedStencilDepthRenderBuffer()) { | |
| gl.glFramebufferRenderbuffer(GL20.GL_FRAMEBUFFER, GL30.GL_DEPTH_STENCIL_ATTACHMENT, | |
| GL20.GL_RENDERBUFFER, depthStencilPackedBufferHandle); | |
| } | |
| gl.glBindRenderbuffer(GL20.GL_RENDERBUFFER, 0); | |
| for(Texture texture : textureAttachments) { | |
| gl.glBindTexture(texture.glTarget, 0); | |
| } | |
| int result = gl.glCheckFramebufferStatus(GL20.GL_FRAMEBUFFER); | |
| if(result == GL20.GL_FRAMEBUFFER_UNSUPPORTED | |
| && getBuilder().hasDepthRenderBuffer() | |
| && getBuilder().hasStencilRenderBuffer() | |
| && (Gdx.graphics.supportsExtension("GL_OES_packed_depth_stencil") | |
| || Gdx.graphics.supportsExtension("GL_EXT_packed_depth_stencil"))) { | |
| if(getBuilder().hasDepthRenderBuffer()) { | |
| gl.glDeleteRenderbuffer(depthbufferHandle); | |
| depthbufferHandle = 0; | |
| } | |
| if(getBuilder().hasStencilRenderBuffer()) { | |
| gl.glDeleteRenderbuffer(stencilbufferHandle); | |
| stencilbufferHandle = 0; | |
| } | |
| if(getBuilder().hasPackedStencilDepthRenderBuffer()) { | |
| gl.glDeleteRenderbuffer(depthStencilPackedBufferHandle); | |
| depthStencilPackedBufferHandle = 0; | |
| } | |
| depthStencilPackedBufferHandle = gl.glGenRenderbuffer(); | |
| hasDepthStencilPackedBuffer = true; | |
| gl.glBindRenderbuffer(GL20.GL_RENDERBUFFER, depthStencilPackedBufferHandle); | |
| if(samples > 0) { | |
| org.lwjgl.opengl.GL30.glRenderbufferStorageMultisample(GL20.GL_RENDERBUFFER, | |
| samples, GL_DEPTH24_STENCIL8_OES, width, height); | |
| } else { | |
| gl.glRenderbufferStorage(GL20.GL_RENDERBUFFER, GL_DEPTH24_STENCIL8_OES, width, | |
| height); | |
| } | |
| gl.glBindRenderbuffer(GL20.GL_RENDERBUFFER, 0); | |
| gl.glFramebufferRenderbuffer(GL20.GL_FRAMEBUFFER, GL20.GL_DEPTH_ATTACHMENT, | |
| GL20.GL_RENDERBUFFER, depthStencilPackedBufferHandle); | |
| gl.glFramebufferRenderbuffer(GL20.GL_FRAMEBUFFER, GL20.GL_STENCIL_ATTACHMENT, | |
| GL20.GL_RENDERBUFFER, depthStencilPackedBufferHandle); | |
| result = gl.glCheckFramebufferStatus(GL20.GL_FRAMEBUFFER); | |
| } | |
| gl.glBindFramebuffer(GL20.GL_FRAMEBUFFER, defaultFramebufferHandle); | |
| if(result != GL20.GL_FRAMEBUFFER_COMPLETE) { | |
| for(Texture texture : textureAttachments) { | |
| disposeColorTexture(texture); | |
| } | |
| if(hasDepthStencilPackedBuffer) { | |
| gl.glDeleteBuffer(depthStencilPackedBufferHandle); | |
| } else { | |
| if(getBuilder().hasDepthRenderBuffer()) | |
| gl.glDeleteRenderbuffer(depthbufferHandle); | |
| if(getBuilder().hasStencilRenderBuffer()) | |
| gl.glDeleteRenderbuffer(stencilbufferHandle); | |
| } | |
| gl.glDeleteFramebuffer(framebufferHandle); | |
| if(result == GL20.GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT) | |
| throw new IllegalStateException("Frame buffer couldn't be constructed: incomplete " + | |
| "attachment"); | |
| if(result == GL20.GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS) | |
| throw new IllegalStateException("Frame buffer couldn't be constructed: incomplete " + | |
| "dimensions"); | |
| if(result == GL20.GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT) | |
| throw new IllegalStateException("Frame buffer couldn't be constructed: missing " + | |
| "attachment"); | |
| if(result == GL20.GL_FRAMEBUFFER_UNSUPPORTED) | |
| throw new IllegalStateException("Frame buffer couldn't be constructed: unsupported" + | |
| " combination of formats"); | |
| if(result == GL31.GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE) | |
| throw new IllegalStateException("Frame buffer couldn't be constructed: multisample" + | |
| " mismatch"); | |
| throw new IllegalStateException("Frame buffer couldn't be constructed: unknown error " + result); | |
| } | |
| addManagedFrameBuffer(Gdx.app, this); | |
| } | |
| public PublicFrameBufferBuilder getBuilder() { | |
| return (PublicFrameBufferBuilder)bufferBuilder; | |
| } | |
| @SuppressWarnings("rawtypes") | |
| private static void addManagedFrameBuffer(Application app, GLFrameBuffer frameBuffer) { | |
| Array<GLFrameBuffer> managedResources = buffers.get(app); | |
| if(managedResources == null) | |
| managedResources = new Array<>(); | |
| managedResources.add(frameBuffer); | |
| buffers.put(app, managedResources); | |
| } | |
| private static FrameBufferBuilder makeBuilder(int width, int height, int samples) { | |
| PublicFrameBufferBuilder frameBufferBuilder = new PublicFrameBufferBuilder(width, height, | |
| samples); | |
| frameBufferBuilder.addBasicColorTextureAttachment(Pixmap.Format.RGB888); | |
| frameBufferBuilder.addBasicDepthRenderBuffer(); | |
| return frameBufferBuilder; | |
| } | |
| public static class PublicFrameBufferBuilder extends FrameBufferBuilder { | |
| public PublicFrameBufferBuilder(int width, int height, int samples) { | |
| super(width, height, samples); | |
| } | |
| public int getWidth() { | |
| return width; | |
| } | |
| public int getHeight() { | |
| return height; | |
| } | |
| public int getSamples() { | |
| return samples; | |
| } | |
| public boolean hasDepthRenderBuffer() { | |
| return hasDepthRenderBuffer; | |
| } | |
| public boolean hasStencilRenderBuffer() { | |
| return hasStencilRenderBuffer; | |
| } | |
| public boolean hasPackedStencilDepthRenderBuffer() { | |
| return hasPackedStencilDepthRenderBuffer; | |
| } | |
| public Array<FrameBufferTextureAttachmentSpec> getTextureAttachmentSpecs() { | |
| return textureAttachmentSpecs; | |
| } | |
| public Array<FrameBufferRenderBufferAttachmentSpec> getColorRenderBufferSpecs() { | |
| return colorRenderBufferSpecs; | |
| } | |
| public FrameBufferRenderBufferAttachmentSpec getStencilRenderBufferSpec() { | |
| return stencilRenderBufferSpec; | |
| } | |
| public FrameBufferRenderBufferAttachmentSpec getDepthRenderBufferSpec() { | |
| return depthRenderBufferSpec; | |
| } | |
| public FrameBufferRenderBufferAttachmentSpec getPackedStencilDepthRenderBufferSpec() { | |
| return packedStencilDepthRenderBufferSpec; | |
| } | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment