diff --git a/CHANGES b/CHANGES index 2b23dbbd316..d324c1bf283 100644 --- a/CHANGES +++ b/CHANGES @@ -8,6 +8,7 @@ - Fixed Timer#stop, remember time spent stopped and delay tasks when started again. #7281 - Android: Add configuration option to render under the cutout if available on the device. - Fix: Keep SelectBox popup from extending past right edge of stage. +- Added Framebuffer multisample support (see GL31FrameBufferMultisampleTest.java for basic usage) [1.12.1] - LWJGL3 Improvement: Audio device is automatically switched if it was changed in the operating system. diff --git a/gdx/src/com/badlogic/gdx/graphics/glutils/GLFrameBuffer.java b/gdx/src/com/badlogic/gdx/graphics/glutils/GLFrameBuffer.java index bfe4241f085..75208b07310 100644 --- a/gdx/src/com/badlogic/gdx/graphics/glutils/GLFrameBuffer.java +++ b/gdx/src/com/badlogic/gdx/graphics/glutils/GLFrameBuffer.java @@ -28,12 +28,14 @@ import com.badlogic.gdx.Gdx; import com.badlogic.gdx.graphics.GL20; import com.badlogic.gdx.graphics.GL30; +import com.badlogic.gdx.graphics.GL31; import com.badlogic.gdx.graphics.GLTexture; import com.badlogic.gdx.graphics.Pixmap; import com.badlogic.gdx.utils.Array; import com.badlogic.gdx.utils.BufferUtils; import com.badlogic.gdx.utils.Disposable; import com.badlogic.gdx.utils.GdxRuntimeException; +import com.badlogic.gdx.utils.IntArray; /** *

@@ -76,12 +78,16 @@ public abstract class GLFrameBuffer implements Disposable { protected int depthStencilPackedBufferHandle; /** if has depth stencil packed buffer **/ protected boolean hasDepthStencilPackedBuffer; + /** the colorbuffer render object handles **/ + protected final IntArray colorBufferHandles = new IntArray(); /** if multiple texture attachments are present **/ protected boolean isMRT; protected GLFrameBufferBuilder> bufferBuilder; + private IntBuffer defaultDrawBuffers; + GLFrameBuffer () { } @@ -136,33 +142,48 @@ protected void build () { if (bufferBuilder.hasDepthRenderBuffer) { depthbufferHandle = gl.glGenRenderbuffer(); gl.glBindRenderbuffer(GL20.GL_RENDERBUFFER, depthbufferHandle); - gl.glRenderbufferStorage(GL20.GL_RENDERBUFFER, bufferBuilder.depthRenderBufferSpec.internalFormat, width, height); + if (bufferBuilder.samples > 0) { + Gdx.gl31.glRenderbufferStorageMultisample(GL20.GL_RENDERBUFFER, bufferBuilder.samples, + bufferBuilder.depthRenderBufferSpec.internalFormat, width, height); + } else { + gl.glRenderbufferStorage(GL20.GL_RENDERBUFFER, bufferBuilder.depthRenderBufferSpec.internalFormat, width, height); + } } if (bufferBuilder.hasStencilRenderBuffer) { stencilbufferHandle = gl.glGenRenderbuffer(); gl.glBindRenderbuffer(GL20.GL_RENDERBUFFER, stencilbufferHandle); - gl.glRenderbufferStorage(GL20.GL_RENDERBUFFER, bufferBuilder.stencilRenderBufferSpec.internalFormat, width, height); + if (bufferBuilder.samples > 0) { + Gdx.gl31.glRenderbufferStorageMultisample(GL20.GL_RENDERBUFFER, bufferBuilder.samples, + bufferBuilder.stencilRenderBufferSpec.internalFormat, width, height); + } else { + gl.glRenderbufferStorage(GL20.GL_RENDERBUFFER, bufferBuilder.stencilRenderBufferSpec.internalFormat, width, height); + } } if (bufferBuilder.hasPackedStencilDepthRenderBuffer) { depthStencilPackedBufferHandle = gl.glGenRenderbuffer(); gl.glBindRenderbuffer(GL20.GL_RENDERBUFFER, depthStencilPackedBufferHandle); - gl.glRenderbufferStorage(GL20.GL_RENDERBUFFER, bufferBuilder.packedStencilDepthRenderBufferSpec.internalFormat, width, - height); + if (bufferBuilder.samples > 0) { + Gdx.gl31.glRenderbufferStorageMultisample(GL20.GL_RENDERBUFFER, bufferBuilder.samples, + bufferBuilder.packedStencilDepthRenderBufferSpec.internalFormat, width, height); + } else { + gl.glRenderbufferStorage(GL20.GL_RENDERBUFFER, bufferBuilder.packedStencilDepthRenderBufferSpec.internalFormat, width, + height); + } hasDepthStencilPackedBuffer = true; } isMRT = bufferBuilder.textureAttachmentSpecs.size > 1; - int colorTextureCounter = 0; + int colorAttachmentCounter = 0; if (isMRT) { for (FrameBufferTextureAttachmentSpec attachmentSpec : bufferBuilder.textureAttachmentSpecs) { T texture = createTexture(attachmentSpec); textureAttachments.add(texture); if (attachmentSpec.isColorTexture()) { - gl.glFramebufferTexture2D(GL20.GL_FRAMEBUFFER, GL30.GL_COLOR_ATTACHMENT0 + colorTextureCounter, GL30.GL_TEXTURE_2D, - texture.getTextureObjectHandle(), 0); - colorTextureCounter++; + gl.glFramebufferTexture2D(GL20.GL_FRAMEBUFFER, GL30.GL_COLOR_ATTACHMENT0 + colorAttachmentCounter, + GL30.GL_TEXTURE_2D, texture.getTextureObjectHandle(), 0); + colorAttachmentCounter++; } else if (attachmentSpec.isDepth) { gl.glFramebufferTexture2D(GL20.GL_FRAMEBUFFER, GL20.GL_DEPTH_ATTACHMENT, GL20.GL_TEXTURE_2D, texture.getTextureObjectHandle(), 0); @@ -171,20 +192,35 @@ protected void build () { texture.getTextureObjectHandle(), 0); } } - } else { + } else if (bufferBuilder.textureAttachmentSpecs.size > 0) { T texture = createTexture(bufferBuilder.textureAttachmentSpecs.first()); textureAttachments.add(texture); gl.glBindTexture(texture.glTarget, texture.getTextureObjectHandle()); } - if (isMRT) { - IntBuffer buffer = BufferUtils.newIntBuffer(colorTextureCounter); - for (int i = 0; i < colorTextureCounter; i++) { - buffer.put(GL30.GL_COLOR_ATTACHMENT0 + i); + for (FrameBufferRenderBufferAttachmentSpec colorBufferSpec : bufferBuilder.colorRenderBufferSpecs) { + int colorbufferHandle = gl.glGenRenderbuffer(); + gl.glBindRenderbuffer(GL20.GL_RENDERBUFFER, colorbufferHandle); + if (bufferBuilder.samples > 0) { + Gdx.gl31.glRenderbufferStorageMultisample(GL20.GL_RENDERBUFFER, bufferBuilder.samples, colorBufferSpec.internalFormat, + width, height); + } else { + gl.glRenderbufferStorage(GL20.GL_RENDERBUFFER, colorBufferSpec.internalFormat, width, height); } - ((Buffer)buffer).position(0); - Gdx.gl30.glDrawBuffers(colorTextureCounter, buffer); - } else { + Gdx.gl.glFramebufferRenderbuffer(GL20.GL_FRAMEBUFFER, GL20.GL_COLOR_ATTACHMENT0 + colorAttachmentCounter, + GL20.GL_RENDERBUFFER, colorbufferHandle); + colorBufferHandles.add(colorbufferHandle); + colorAttachmentCounter++; + } + + if (isMRT || bufferBuilder.samples > 0) { + defaultDrawBuffers = BufferUtils.newIntBuffer(colorAttachmentCounter); + for (int i = 0; i < colorAttachmentCounter; i++) { + defaultDrawBuffers.put(GL30.GL_COLOR_ATTACHMENT0 + i); + } + ((Buffer)defaultDrawBuffers).position(0); + Gdx.gl30.glDrawBuffers(colorAttachmentCounter, defaultDrawBuffers); + } else if (bufferBuilder.textureAttachmentSpecs.size > 0) { attachFrameBufferColorTexture(textureAttachments.first()); } @@ -227,7 +263,12 @@ protected void build () { depthStencilPackedBufferHandle = gl.glGenRenderbuffer(); hasDepthStencilPackedBuffer = true; gl.glBindRenderbuffer(GL20.GL_RENDERBUFFER, depthStencilPackedBufferHandle); - gl.glRenderbufferStorage(GL20.GL_RENDERBUFFER, GL_DEPTH24_STENCIL8_OES, width, height); + if (bufferBuilder.samples > 0) { + Gdx.gl31.glRenderbufferStorageMultisample(GL20.GL_RENDERBUFFER, bufferBuilder.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, @@ -261,6 +302,8 @@ protected void build () { 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); } @@ -268,6 +311,14 @@ protected void build () { } private void checkValidBuilder () { + + if (bufferBuilder.samples > 0 && !Gdx.graphics.isGL31Available()) { + throw new GdxRuntimeException("Framebuffer multisample requires GLES 3.1+"); + } + if (bufferBuilder.samples > 0 && bufferBuilder.textureAttachmentSpecs.size > 0) { + throw new GdxRuntimeException("Framebuffer multisample with texture attachments not yet supported"); + } + boolean runningGL30 = Gdx.graphics.isGL30Available(); if (!runningGL30) { @@ -350,6 +401,78 @@ public void end (int x, int y, int width, int height) { Gdx.gl20.glViewport(x, y, width, height); } + static final IntBuffer singleInt = BufferUtils.newIntBuffer(1); + + /** Transfer pixels from this frame buffer to the destination frame buffer. Usually used when using multisample, it resolves + * samples from this multisample FBO to a non-multisample as destination in order to be used as textures. This is a convenient + * method that automatically choose which of stencil, depth, and colors buffers attachment to be copied. + * @param destination the destination of the copy. */ + public void transfer (GLFrameBuffer destination) { + + int copyBits = 0; + for (FrameBufferTextureAttachmentSpec attachment : destination.bufferBuilder.textureAttachmentSpecs) { + if (attachment.isDepth && (bufferBuilder.hasDepthRenderBuffer || bufferBuilder.hasPackedStencilDepthRenderBuffer)) { + copyBits |= GL20.GL_DEPTH_BUFFER_BIT; + } else if (attachment.isStencil + && (bufferBuilder.hasStencilRenderBuffer || bufferBuilder.hasPackedStencilDepthRenderBuffer)) { + copyBits |= GL20.GL_STENCIL_BUFFER_BIT; + } else if (colorBufferHandles.size > 0) { + copyBits |= GL20.GL_COLOR_BUFFER_BIT; + } + } + + transfer(destination, copyBits); + } + + /** Transfer pixels from this frame buffer to the destination frame buffer. Usually used when using multisample, it resolves + * samples from this multisample FBO to a non-multisample as destination in order to be used as textures. + * @param destination the destination of the copy (should be same size as this frame buffer). + * @param copyBits combination of GL20.GL_COLOR_BUFFER_BIT, GL20.GL_STENCIL_BUFFER_BIT, and GL20.GL_DEPTH_BUFFER_BIT. When + * GL20.GL_COLOR_BUFFER_BIT is present, every color buffers will be copied to each corresponding color texture + * buffers in the destination framebuffer. */ + public void transfer (GLFrameBuffer destination, int copyBits) { + + if (destination.getWidth() != getWidth() || destination.getHeight() != getHeight()) { + throw new IllegalArgumentException("source and destination frame buffers must have same size."); + } + + Gdx.gl.glBindFramebuffer(GL30.GL_READ_FRAMEBUFFER, framebufferHandle); + Gdx.gl.glBindFramebuffer(GL30.GL_DRAW_FRAMEBUFFER, destination.framebufferHandle); + + int colorBufferIndex = 0; + int attachmentIndex = 0; + for (FrameBufferTextureAttachmentSpec attachment : destination.bufferBuilder.textureAttachmentSpecs) { + if (attachment.isColorTexture()) { + Gdx.gl30.glReadBuffer(GL30.GL_COLOR_ATTACHMENT0 + colorBufferIndex); + + singleInt.clear(); + singleInt.put(GL30.GL_COLOR_ATTACHMENT0 + attachmentIndex); + singleInt.flip(); + Gdx.gl30.glDrawBuffers(1, singleInt); + + Gdx.gl30.glBlitFramebuffer(0, 0, getWidth(), getHeight(), 0, 0, destination.getWidth(), destination.getHeight(), + copyBits, GL20.GL_NEAREST); + + copyBits = GL20.GL_COLOR_BUFFER_BIT; + colorBufferIndex++; + } + attachmentIndex++; + } + // case of depth and/or stencil only + if (copyBits != GL20.GL_COLOR_BUFFER_BIT) { + Gdx.gl30.glBlitFramebuffer(0, 0, getWidth(), getHeight(), 0, 0, destination.getWidth(), destination.getHeight(), + copyBits, GL20.GL_NEAREST); + } + + // restore draw buffers for destination (in case of MRT only) + if (destination.defaultDrawBuffers != null) { + Gdx.gl30.glDrawBuffers(destination.defaultDrawBuffers.limit(), destination.defaultDrawBuffers); + } + + Gdx.gl.glBindFramebuffer(GL30.GL_READ_FRAMEBUFFER, 0); + Gdx.gl.glBindFramebuffer(GL30.GL_DRAW_FRAMEBUFFER, 0); + } + /** @return The OpenGL handle of the framebuffer (see {@link GL20#glGenFramebuffer()}) */ public int getFramebufferHandle () { return framebufferHandle; @@ -361,6 +484,12 @@ public int getDepthBufferHandle () { return depthbufferHandle; } + /** @param n index of the color buffer as added to the frame buffer builder. + * @return The OpenGL handle of a color buffer (see {@link GL20#glGenRenderbuffer()}). **/ + public int getColorBufferHandle (int n) { + return colorBufferHandles.get(n); + } + /** @return The OpenGL handle of the (optional) stencil buffer (see {@link GL20#glGenRenderbuffer()}). May return 0 even if * stencil buffer enabled */ public int getStencilBufferHandle () { @@ -445,9 +574,10 @@ public FrameBufferRenderBufferAttachmentSpec (int internalFormat) { } public static abstract class GLFrameBufferBuilder> { - protected int width, height; + protected int width, height, samples; protected Array textureAttachmentSpecs = new Array(); + protected Array colorRenderBufferSpecs = new Array(); protected FrameBufferRenderBufferAttachmentSpec stencilRenderBufferSpec; protected FrameBufferRenderBufferAttachmentSpec depthRenderBufferSpec; @@ -458,8 +588,13 @@ public static abstract class GLFrameBufferBuilder addColorTextureAttachment (int internalFormat, int format, int type) { @@ -503,6 +638,11 @@ public GLFrameBufferBuilder addDepthRenderBuffer (int internalFormat) { return this; } + public GLFrameBufferBuilder addColorRenderBuffer (int internalFormat) { + colorRenderBufferSpecs.add(new FrameBufferRenderBufferAttachmentSpec(internalFormat)); + return this; + } + public GLFrameBufferBuilder addStencilRenderBuffer (int internalFormat) { stencilRenderBufferSpec = new FrameBufferRenderBufferAttachmentSpec(internalFormat); hasStencilRenderBuffer = true; @@ -535,6 +675,10 @@ public FrameBufferBuilder (int width, int height) { super(width, height); } + public FrameBufferBuilder (int width, int height, int samples) { + super(width, height, samples); + } + @Override public FrameBuffer build () { return new FrameBuffer(this); @@ -546,6 +690,10 @@ public FloatFrameBufferBuilder (int width, int height) { super(width, height); } + public FloatFrameBufferBuilder (int width, int height, int samples) { + super(width, height, samples); + } + @Override public FloatFrameBuffer build () { return new FloatFrameBuffer(this); @@ -557,6 +705,10 @@ public FrameBufferCubemapBuilder (int width, int height) { super(width, height); } + public FrameBufferCubemapBuilder (int width, int height, int samples) { + super(width, height, samples); + } + @Override public FrameBufferCubemap build () { return new FrameBufferCubemap(this); diff --git a/tests/gdx-tests-android/assets/data/shaders/shape-renderer-mrt-frag.glsl b/tests/gdx-tests-android/assets/data/shaders/shape-renderer-mrt-frag.glsl new file mode 100644 index 00000000000..4e4cd244ead --- /dev/null +++ b/tests/gdx-tests-android/assets/data/shaders/shape-renderer-mrt-frag.glsl @@ -0,0 +1,11 @@ +#ifdef GL_ES +precision mediump float; +#endif +varying vec4 v_col; +layout(location = 0) out vec4 colorOut; +layout(location = 1) out vec4 redOut; + +void main() { + colorOut = v_col; + redOut = vec4(1.0, 0.0, 0.0, 1.0); +} diff --git a/tests/gdx-tests-android/assets/data/shaders/shape-renderer-mrt-vert.glsl b/tests/gdx-tests-android/assets/data/shaders/shape-renderer-mrt-vert.glsl new file mode 100644 index 00000000000..a998e98d7e3 --- /dev/null +++ b/tests/gdx-tests-android/assets/data/shaders/shape-renderer-mrt-vert.glsl @@ -0,0 +1,10 @@ +in vec4 a_position; +in vec4 a_color; +uniform mat4 u_projModelView; +out vec4 v_col; +void main() { + gl_Position = u_projModelView * a_position; + v_col = a_color; + v_col.a *= 255.0 / 254.0; + gl_PointSize = 1.0; +} diff --git a/tests/gdx-tests/src/com/badlogic/gdx/tests/gles31/GL31FrameBufferMultisampleMRTTest.java b/tests/gdx-tests/src/com/badlogic/gdx/tests/gles31/GL31FrameBufferMultisampleMRTTest.java new file mode 100644 index 00000000000..3a545331782 --- /dev/null +++ b/tests/gdx-tests/src/com/badlogic/gdx/tests/gles31/GL31FrameBufferMultisampleMRTTest.java @@ -0,0 +1,126 @@ +/******************************************************************************* + * Copyright 2022 See AUTHORS file. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ******************************************************************************/ + +package com.badlogic.gdx.tests.gles31; + +import com.badlogic.gdx.Application; +import com.badlogic.gdx.Gdx; +import com.badlogic.gdx.graphics.Color; +import com.badlogic.gdx.graphics.GL20; +import com.badlogic.gdx.graphics.GL30; +import com.badlogic.gdx.graphics.Texture.TextureFilter; +import com.badlogic.gdx.graphics.g2d.SpriteBatch; +import com.badlogic.gdx.graphics.glutils.FrameBuffer; +import com.badlogic.gdx.graphics.glutils.GLFrameBuffer.FrameBufferBuilder; +import com.badlogic.gdx.graphics.glutils.ShaderProgram; +import com.badlogic.gdx.graphics.glutils.ShapeRenderer; +import com.badlogic.gdx.graphics.glutils.ShapeRenderer.ShapeType; +import com.badlogic.gdx.tests.utils.GdxTest; +import com.badlogic.gdx.tests.utils.GdxTestConfig; +import com.badlogic.gdx.utils.ScreenUtils; + +@GdxTestConfig(requireGL31 = true) +public class GL31FrameBufferMultisampleMRTTest extends GdxTest { + + private FrameBuffer fbo; + private FrameBuffer fboMS; + private SpriteBatch batch; + private ShapeRenderer shapes; + private ShaderProgram shader; + + @Override + public void create () { + + int nbSamples = 4; + + fboMS = new FrameBufferBuilder(64, 64, nbSamples).addColorRenderBuffer(GL30.GL_RGBA8).addColorRenderBuffer(GL30.GL_RGBA8) + .addDepthRenderBuffer(GL30.GL_DEPTH_COMPONENT24).build(); + + fbo = new FrameBufferBuilder(64, 64).addColorTextureAttachment(GL30.GL_RGBA8, GL20.GL_RGBA, GL30.GL_UNSIGNED_BYTE) + .addColorTextureAttachment(GL30.GL_RGBA8, GL20.GL_RGBA, GL20.GL_UNSIGNED_BYTE) + .addDepthTextureAttachment(GL30.GL_DEPTH_COMPONENT24, GL30.GL_UNSIGNED_INT).build(); + + fbo.getTextureAttachments().get(0).setFilter(TextureFilter.Nearest, TextureFilter.Nearest); + fbo.getTextureAttachments().get(1).setFilter(TextureFilter.Nearest, TextureFilter.Nearest); + fbo.getTextureAttachments().get(2).setFilter(TextureFilter.Nearest, TextureFilter.Nearest); + + batch = new SpriteBatch(); + + ShaderProgram.prependVertexCode = Gdx.app.getType().equals(Application.ApplicationType.Desktop) + ? "#version 140\n #extension GL_ARB_explicit_attrib_location : enable\n" + : "#version 300 es\n"; + ShaderProgram.prependFragmentCode = Gdx.app.getType().equals(Application.ApplicationType.Desktop) + ? "#version 140\n #extension GL_ARB_explicit_attrib_location : enable\n" + : "#version 300 es\n"; + + shader = new ShaderProgram(Gdx.files.internal("data/shaders/shape-renderer-mrt-vert.glsl").readString(), + Gdx.files.internal("data/shaders/shape-renderer-mrt-frag.glsl").readString()); + + shapes = new ShapeRenderer(3, shader); + + } + + @Override + public void dispose () { + fboMS.dispose(); + fbo.dispose(); + shader.dispose(); + batch.dispose(); + shapes.dispose(); + } + + @Override + public void render () { + + Gdx.gl.glEnable(GL20.GL_DEPTH_TEST); + ScreenUtils.clear(Color.CLEAR, true); + batch.getProjectionMatrix().setToOrtho2D(0, 0, 2, 3); + + // render a shape into the non-multisample FBO and display it on the left + fbo.begin(); + ScreenUtils.clear(Color.CLEAR, true); + shapes.getProjectionMatrix().setToOrtho2D(0, 0, 1, 1); + shapes.begin(ShapeType.Filled); + shapes.triangle(0.2f, 0.3f, .9f, .9f, .8f, 0.5f); + shapes.end(); + fbo.end(); + + batch.begin(); + batch.draw(fbo.getTextureAttachments().get(0), 0, 0, 1, 1, 0, 0, 1, 1); + batch.draw(fbo.getTextureAttachments().get(1), 0, 1, 1, 1, 0, 0, 1, 1); + batch.draw(fbo.getTextureAttachments().get(2), 0, 2, 1, 1, 0, 0, 1, 1); + batch.end(); + + // render a shape into the multisample FBO, transfer to the other one and display it on the right + fboMS.begin(); + ScreenUtils.clear(Color.CLEAR, true); + shapes.getProjectionMatrix().setToOrtho2D(0, 0, 1, 1); + shapes.begin(ShapeType.Filled); + shapes.triangle(0.2f, 0.3f, .9f, .9f, .8f, 0.5f); + shapes.end(); + fboMS.end(); + + fboMS.transfer(fbo); + + batch.begin(); + batch.draw(fbo.getTextureAttachments().get(0), 1, 0, 1, 1, 0, 0, 1, 1); + batch.draw(fbo.getTextureAttachments().get(1), 1, 1, 1, 1, 0, 0, 1, 1); + batch.draw(fbo.getTextureAttachments().get(2), 1, 2, 1, 1, 0, 0, 1, 1); + batch.end(); + + Gdx.gl.glDisable(GL20.GL_DEPTH_TEST); + } +} diff --git a/tests/gdx-tests/src/com/badlogic/gdx/tests/gles31/GL31FrameBufferMultisampleTest.java b/tests/gdx-tests/src/com/badlogic/gdx/tests/gles31/GL31FrameBufferMultisampleTest.java index 919f73b95b0..5b4f52ad516 100644 --- a/tests/gdx-tests/src/com/badlogic/gdx/tests/gles31/GL31FrameBufferMultisampleTest.java +++ b/tests/gdx-tests/src/com/badlogic/gdx/tests/gles31/GL31FrameBufferMultisampleTest.java @@ -16,110 +16,30 @@ package com.badlogic.gdx.tests.gles31; -import com.badlogic.gdx.Gdx; import com.badlogic.gdx.graphics.Color; -import com.badlogic.gdx.graphics.GL20; import com.badlogic.gdx.graphics.GL30; import com.badlogic.gdx.graphics.Pixmap.Format; import com.badlogic.gdx.graphics.Texture.TextureFilter; import com.badlogic.gdx.graphics.g2d.SpriteBatch; import com.badlogic.gdx.graphics.glutils.FrameBuffer; +import com.badlogic.gdx.graphics.glutils.GLFrameBuffer.FrameBufferBuilder; import com.badlogic.gdx.graphics.glutils.ShapeRenderer; import com.badlogic.gdx.graphics.glutils.ShapeRenderer.ShapeType; import com.badlogic.gdx.tests.utils.GdxTest; import com.badlogic.gdx.tests.utils.GdxTestConfig; -import com.badlogic.gdx.utils.Disposable; -import com.badlogic.gdx.utils.GdxRuntimeException; import com.badlogic.gdx.utils.ScreenUtils; @GdxTestConfig(requireGL31 = true) public class GL31FrameBufferMultisampleTest extends GdxTest { - private static class FrameBufferMS implements Disposable { - public int framebufferHandle; - public int width, height; - private int colorBufferHandle; - - public FrameBufferMS (Format format, int width, int height, int samples) { - this.width = width; - this.height = height; - - // create render buffer - colorBufferHandle = Gdx.gl.glGenRenderbuffer(); - Gdx.gl.glBindRenderbuffer(GL20.GL_RENDERBUFFER, colorBufferHandle); - Gdx.gl31.glRenderbufferStorageMultisample(GL20.GL_RENDERBUFFER, samples, GL30.GL_RGBA8, width, height); - - // create frame buffer - framebufferHandle = Gdx.gl.glGenFramebuffer(); - Gdx.gl.glBindFramebuffer(GL20.GL_FRAMEBUFFER, framebufferHandle); - - // attach render buffer - Gdx.gl.glFramebufferRenderbuffer(GL20.GL_FRAMEBUFFER, GL20.GL_COLOR_ATTACHMENT0, GL20.GL_RENDERBUFFER, - colorBufferHandle); - - int result = Gdx.gl.glCheckFramebufferStatus(GL20.GL_FRAMEBUFFER); - - Gdx.gl.glBindRenderbuffer(GL20.GL_RENDERBUFFER, 0); - Gdx.gl.glBindTexture(GL20.GL_TEXTURE_2D, 0); - Gdx.gl.glBindFramebuffer(GL20.GL_FRAMEBUFFER, 0); - - if (result != GL20.GL_FRAMEBUFFER_COMPLETE) { - throw new GdxRuntimeException("error"); - } - } - - @Override - public void dispose () { - Gdx.gl.glDeleteFramebuffer(framebufferHandle); - Gdx.gl.glDeleteRenderbuffer(colorBufferHandle); - } - - public void begin () { - bind(); - setFrameBufferViewport(); - } - - protected void setFrameBufferViewport () { - Gdx.gl20.glViewport(0, 0, width, height); - } - - public void end () { - end(0, 0, Gdx.graphics.getBackBufferWidth(), Gdx.graphics.getBackBufferHeight()); - } - - public void end (int x, int y, int width, int height) { - unbind(); - Gdx.gl20.glViewport(x, y, width, height); - } - - public void bind () { - Gdx.gl20.glBindFramebuffer(GL20.GL_FRAMEBUFFER, framebufferHandle); - } - - public static void unbind () { - Gdx.gl20.glBindFramebuffer(GL20.GL_FRAMEBUFFER, 0); - } - - public int getHeight () { - return height; - } - - public int getWidth () { - return width; - } - - public int getFramebufferHandle () { - return framebufferHandle; - } - } private FrameBuffer fbo; - private FrameBufferMS fboMS; + private FrameBuffer fboMS; private SpriteBatch batch; private ShapeRenderer shapes; @Override public void create () { - fboMS = new FrameBufferMS(Format.RGBA8888, 64, 64, 4); + fboMS = new FrameBufferBuilder(64, 64, 4).addColorRenderBuffer(GL30.GL_RGBA8).build(); fbo = new FrameBuffer(Format.RGBA8888, 64, 64, false); fbo.getColorBufferTexture().setFilter(TextureFilter.Nearest, TextureFilter.Nearest); batch = new SpriteBatch(); @@ -137,6 +57,8 @@ public void dispose () { @Override public void render () { + ScreenUtils.clear(Color.CLEAR); + batch.getProjectionMatrix().setToOrtho2D(0, 0, 2, 2); // render a line into the non multisample FBO and display it @@ -161,12 +83,7 @@ public void render () { shapes.end(); fboMS.end(); - Gdx.gl.glBindFramebuffer(GL30.GL_READ_FRAMEBUFFER, fboMS.getFramebufferHandle()); - Gdx.gl.glBindFramebuffer(GL30.GL_DRAW_FRAMEBUFFER, fbo.getFramebufferHandle()); - Gdx.gl30.glBlitFramebuffer(0, 0, fboMS.getWidth(), fboMS.getHeight(), 0, 0, fbo.getWidth(), fbo.getHeight(), - GL20.GL_COLOR_BUFFER_BIT, GL20.GL_NEAREST); - Gdx.gl.glBindFramebuffer(GL30.GL_READ_FRAMEBUFFER, 0); - Gdx.gl.glBindFramebuffer(GL30.GL_DRAW_FRAMEBUFFER, 0); + fboMS.transfer(fbo); batch.begin(); batch.draw(fbo.getColorBufferTexture(), 1, 0, 1, 1, 0, 0, 1, 1); diff --git a/tests/gdx-tests/src/com/badlogic/gdx/tests/utils/GdxTests.java b/tests/gdx-tests/src/com/badlogic/gdx/tests/utils/GdxTests.java index 19d7fca4cbf..5b6ee13485d 100644 --- a/tests/gdx-tests/src/com/badlogic/gdx/tests/utils/GdxTests.java +++ b/tests/gdx-tests/src/com/badlogic/gdx/tests/utils/GdxTests.java @@ -86,6 +86,7 @@ import com.badlogic.gdx.tests.gles3.InstancedRenderingTest; import com.badlogic.gdx.tests.gles3.ModelInstancedRenderingTest; import com.badlogic.gdx.tests.gles3.PixelBufferObjectTest; +import com.badlogic.gdx.tests.gles31.GL31FrameBufferMultisampleMRTTest; import com.badlogic.gdx.tests.gles31.GL31FrameBufferMultisampleTest; import com.badlogic.gdx.tests.gles31.GL31IndirectDrawingIndexedTest; import com.badlogic.gdx.tests.gles31.GL31IndirectDrawingNonIndexedTest; @@ -174,6 +175,7 @@ public class GdxTests { GLES30Test.class, GL31IndirectDrawingIndexedTest.class, GL31IndirectDrawingNonIndexedTest.class, + GL31FrameBufferMultisampleMRTTest.class, GL31FrameBufferMultisampleTest.class, GL31ProgramIntrospectionTest.class, GL32AdvancedBlendingTest.class,