Hi!
I’ve came across some other complicate stuff. According to the specs most floating point textures are supported in GLES3 and newer although with some restrictions so I tried to add them (or at least some of them) to jme so we have full HDR, PBR and so working in android.
Just adding them to the GLImageFormats was giving failures (incomplete framebuffer attachment in the engine) but the issue was that setting the texture (glTexImage2D call) to use those formats failed with GL_INVALID_OPERATION (1282 - 0x0500).
At first I though it was some device related issue (as said in my last post) but then, once I was able to test other devices I found that all behaved the same, so it was not a device issue but a code issue.
I found this resource: GitHub - ARM-software/opengl-es-sdk-for-android: OpenGL ES SDK for Android which among others, it has a multisample fbo example using rgba16f and rgba32f which worked in my devices not having texture_float and texture_half_float extensions (on gles30 and newer it just adds filtering and blending capability). Then I got deeper into it and ported this sample to java just in case it was a C++ vs java issue in android (weird but possible) and it worked. Then I also created a small test for jme just creating a framebuffer with the same format just to check if there was any aditional gl call or any different parameter that could make it fail but everything is the same.
Here’s the code and output for all the tests
Simple java gles floating point texture sample (working):
package com.fbotest;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.IntBuffer;
import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;
import android.content.Context;
import android.opengl.GLES30;
import android.opengl.GLES20;
import android.opengl.GLSurfaceView;
import android.opengl.GLSurfaceView.Renderer;
public class GLRendererClean implements Renderer
{
private Context context;
private GLSurfaceView view;
private boolean testFBO=false;
private int [] formatsToTest={GLES30.GL_RGB16F, GLES30.GL_RGBA16F, GLES30.GL_RGBA32F, GLES30.GL_RGB10_A2};
private int currentTestFormat=2;
private int frame=0;
public GLRendererClean(Context context, GLSurfaceView view)
{
this.context=context;
this.view=view;
}
@Override
public void onSurfaceCreated(GL10 gl, EGLConfig config)
{
GLES20.glEnable(GLES20.GL_DEPTH_TEST);
GLES20.glDepthFunc(GLES20.GL_LEQUAL);
GLES20.glPixelStorei(GLES20.GL_UNPACK_ALIGNMENT, 1);
}
@Override
public void onDrawFrame(GL10 gl)
{
++frame;
if(frame%1000==0)
{
this.runTestFBO();
}
drawGL2();
if(testFBO)
{
this.realTestFBO();
testFBO=false;
}
}
private void drawGL1(GL10 gl)
{
}
private void drawGL2()
{
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT | GLES20.GL_DEPTH_BUFFER_BIT | GLES20.GL_STENCIL_BUFFER_BIT);
GLES20.glClearColor(1.0f, 1.0f, 0.0f, 1.0f);
//Set Scene information to the renderer
GLES20.glEnable(GLES20.GL_CULL_FACE);
GLES20.glCullFace(GLES20.GL_BACK);
GLES20.glEnable(GLES20.GL_DEPTH_TEST);
GLES20.glDepthRangef(0,1);
GLES20.glDepthFunc(GLES20.GL_LEQUAL);
GLES20.glEnable(GLES20.GL_BLEND);
GLES20.glBlendFunc(GLES20.GL_SRC_ALPHA, GLES20.GL_ONE_MINUS_SRC_ALPHA);
}
@Override
public void onSurfaceChanged(GL10 gl, int width, int height)
{
gl.glViewport(0, 0, width, height);
gl.glMatrixMode(GL10.GL_PROJECTION);
float ratio = (float)width / (float)height;
gl.glLoadIdentity();
gl.glFrustumf(-ratio, ratio, -1, 1, 3, 7);
GLES20.glViewport(0, 0, width, height);
}
void setTextureFiltering(int texture)
{
/* If the new texture format doesn't support linear filtering, disable it. */
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, texture);
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_NEAREST);
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_NEAREST);
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, 0);
}
boolean setupFBO(IntBuffer fbo, IntBuffer colorBuffer, IntBuffer depthBuffer, int textureSize, int internalFormat, int samples)
{
GLES20.glGenFramebuffers(1, fbo);
GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, fbo.get(0));
/* Generate and attach depth buffer. */
GLES20.glGenRenderbuffers(1, depthBuffer);
GLES20.glBindRenderbuffer(GLES20.GL_RENDERBUFFER, depthBuffer.get(0));
System.err.println("Error post getRbDepth: " + GLES20.glGetError());
GLES20.glRenderbufferStorage(GLES20.GL_RENDERBUFFER, GLES20.GL_DEPTH_COMPONENT16, textureSize, textureSize);
System.err.println("Error post RbStorage: " + GLES20.glGetError());
GLES20.glFramebufferRenderbuffer(GLES20.GL_FRAMEBUFFER, GLES20.GL_DEPTH_ATTACHMENT, GLES20.GL_RENDERBUFFER, depthBuffer.get(0));
/* Generate and attach texture for color buffer. */
GLES20.glGenTextures(1, colorBuffer);
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, colorBuffer.get(0));
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES30.GL_TEXTURE_MAX_LEVEL, 0);
if(internalFormat==GLES30.GL_RGBA16F)
{
System.err.println("RGBA16F ");
System.err.println(GLES20.GL_TEXTURE_2D + ","+ 0 + "," + internalFormat + "," + textureSize + "," + textureSize + "," + 0 + "," + GLES20.GL_RGBA + "," + GLES30.GL_HALF_FLOAT + "," + null);
GLES20.glTexImage2D(GLES20.GL_TEXTURE_2D, 0, internalFormat, textureSize, textureSize, 0, GLES20.GL_RGBA, GLES30.GL_HALF_FLOAT, null);
}
else if(internalFormat==GLES30.GL_RGB16F)
{
System.err.println("RGB16F ");
System.err.println(GLES20.GL_TEXTURE_2D + ","+ 0 + "," + internalFormat + "," + textureSize + "," + textureSize + "," + 0 + "," + GLES20.GL_RGB + "," + GLES30.GL_HALF_FLOAT + "," + null);
GLES20.glTexImage2D(GLES20.GL_TEXTURE_2D, 0, internalFormat, textureSize, textureSize, 0, GLES20.GL_RGB, GLES30.GL_HALF_FLOAT, null);
}
else if(internalFormat==GLES30.GL_RGBA32F)
{
System.err.println("RGBA32F ");
System.err.println(GLES20.GL_TEXTURE_2D + ","+ 0 + "," + internalFormat + "," + textureSize + "," + textureSize + "," + 0 + "," + GLES20.GL_RGBA + "," + GLES20.GL_FLOAT + "," + null);
GLES20.glTexImage2D(GLES20.GL_TEXTURE_2D, 0, internalFormat, textureSize, textureSize, 0, GLES20.GL_RGBA, GLES20.GL_FLOAT, null);
}
else if(internalFormat==GLES30.GL_RGB10_A2)
{
System.err.println("GL_RGB10_A2 ");
System.err.println(GLES20.GL_TEXTURE_2D + ","+ 0 + "," + internalFormat + "," + textureSize + "," + textureSize + "," + 0 + "," + GLES20.GL_RGBA + "," + GLES30.GL_UNSIGNED_INT_2_10_10_10_REV + "," + null);
GLES20.glTexImage2D(GLES20.GL_TEXTURE_2D, 0, internalFormat, textureSize, textureSize, 0, GLES20.GL_RGBA, GLES30.GL_UNSIGNED_INT_2_10_10_10_REV, null);
}
System.err.println("Error post TexImage2D: " + GLES20.glGetError());
setTextureFiltering(colorBuffer.get(0));
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_CLAMP_TO_EDGE);
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_CLAMP_TO_EDGE);
GLES20.glFramebufferTexture2D(GLES20.GL_FRAMEBUFFER, GLES20.GL_COLOR_ATTACHMENT0, GLES20.GL_TEXTURE_2D, colorBuffer.get(0), 0);
System.err.println("Error post FbTex2D: " + GLES20.glGetError());
/* Ensure the framebuffer is 'complete'. */
int fbStatus=GLES20.glCheckFramebufferStatus(GLES20.GL_FRAMEBUFFER);
if (fbStatus != GLES20.GL_FRAMEBUFFER_COMPLETE)
{
System.err.println((samples>1 ? "Multisampled" : "Non-multisampled") + " framebuffer is incomplete! " + fbStatus + " Error code " + GLES20.glGetError());
}
else
{
System.err.println("Framebuffer complete!");
}
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, 0);
GLES20.glBindRenderbuffer(GLES20.GL_RENDERBUFFER, 0);
System.err.println("Error post unbind: " + GLES20.glGetError());
return true;
}
public boolean realTestFBO()
{
IntBuffer fbo=ByteBuffer.allocateDirect(1*4).order(ByteOrder.nativeOrder()).asIntBuffer();
fbo.put(0,0);
fbo.position(0);
IntBuffer colorBuffer=ByteBuffer.allocateDirect(1*4).order(ByteOrder.nativeOrder()).asIntBuffer();
colorBuffer.put(0,0);
colorBuffer.position(0);
IntBuffer depthBuffer=ByteBuffer.allocateDirect(1*4).order(ByteOrder.nativeOrder()).asIntBuffer();
depthBuffer.put(0,0);
depthBuffer.position(0);
return setupFBO(fbo, colorBuffer, depthBuffer, 1024, formatsToTest[currentTestFormat], 1);
}
public void runTestFBO()
{
this.testFBO=true;
currentTestFormat=(currentTestFormat+1)%formatsToTest.length;
}
}
Output of the test:
07-13 03:22:14.517 19285 19358 W System.err: Error post getRbDepth: 1282
07-13 03:22:14.524 19285 19358 W System.err: Error post RbStorage: 0
07-13 03:22:14.525 19285 19358 W System.err: GL_RGB10_A2
07-13 03:22:14.526 19285 19358 W System.err: 3553,0,32857,1024,1024,0,6408,33640,null
07-13 03:22:14.531 19285 19358 W System.err: Error post TexImage2D: 0
07-13 03:22:14.531 19285 19358 W System.err: Error post FbTex2D: 0
07-13 03:22:14.532 19285 19358 W System.err: Framebuffer complete!
07-13 03:22:14.532 19285 19358 W System.err: Error post unbind: 0
07-13 03:22:31.394 19285 19358 W System.err: Error post getRbDepth: 0
07-13 03:22:31.400 19285 19358 W System.err: Error post RbStorage: 0
07-13 03:22:31.401 19285 19358 W System.err: RGB16F
07-13 03:22:31.401 19285 19358 W System.err: 3553,0,34843,1024,1024,0,6407,5131,null
07-13 03:22:31.408 19285 19358 W System.err: Error post TexImage2D: 0
07-13 03:22:31.408 19285 19358 W System.err: Error post FbTex2D: 0
07-13 03:22:31.408 19285 19358 W System.err: Framebuffer complete!
07-13 03:22:31.409 19285 19358 W System.err: Error post unbind: 0
07-13 03:22:48.273 19285 19358 W System.err: Error post getRbDepth: 0
07-13 03:22:48.283 19285 19358 W System.err: Error post RbStorage: 0
07-13 03:22:48.285 19285 19358 W System.err: RGBA16F
07-13 03:22:48.285 19285 19358 W System.err: 3553,0,34842,1024,1024,0,6408,5131,null
07-13 03:22:48.299 19285 19358 W System.err: Error post TexImage2D: 0
07-13 03:22:48.300 19285 19358 W System.err: Error post FbTex2D: 0
07-13 03:22:48.301 19285 19358 W System.err: Framebuffer complete!
07-13 03:22:48.301 19285 19358 W System.err: Error post unbind: 0
07-13 03:23:05.157 19285 19358 W System.err: Error post getRbDepth: 0
07-13 03:23:05.167 19285 19358 W System.err: Error post RbStorage: 0
07-13 03:23:05.168 19285 19358 W System.err: RGBA32F
07-13 03:23:05.168 19285 19358 W System.err: 3553,0,34836,1024,1024,0,6408,5126,null
07-13 03:23:05.191 19285 19358 W System.err: Error post TexImage2D: 0
07-13 03:23:05.191 19285 19358 W System.err: Error post FbTex2D: 0
07-13 03:23:05.191 19285 19358 W System.err: Framebuffer complete!
07-13 03:23:05.191 19285 19358 W System.err: Error post unbind: 0
The jme3 fbo test (not working):
package com.jme.test;
import com.jme3.app.SimpleApplication;
import com.jme3.renderer.opengl.GLRenderer;
import com.jme3.texture.FrameBuffer;
import com.jme3.texture.Image;
import com.jme3.texture.Texture2D;
public class TextFBO extends SimpleApplication {
FrameBuffer fb;
int frame=0;
@Override
public void simpleInitApp() {
System.out.println("JOLIVER - Starting");
fb = new FrameBuffer(1024, 1024, 1);
fb.setDepthBuffer(Image.Format.Depth);
Texture2D tex=new Texture2D(1024,1024,Image.Format.RGBA16F);
fb.setColorTexture(tex);
fb.setUpdateNeeded();
}
@Override
public void simpleUpdate(float tpf) {
if(frame<1)
{
((GLRenderer)this.getRenderer()).updateFrameBuffer(fb);
}
frame++;
}
}
It’s trace and exception output:
07-13 03:03:10.021 10190 10239 I System.out: GetString(VERSION) = "OpenGL ES 3.2 v1.r15p0-00rel0.68b65ac7cf15907aeb95fa944f39eef2"
07-13 03:03:10.022 10190 10239 I System.out: GetString(EXTENSIONS) = "GL_EXT_debug_marker GL_ARM_rgba8 GL_ARM_mali_shader_binary GL_OES_depth24 GL_OES_depth_texture GL_OES_depth_texture_cube_map GL_OES_packed_depth_stenc..."
07-13 03:03:10.022 10190 10239 I System.out: GetInteger(MAX_VERTEX_TEXTURE_IMAGE_UNITS, out=16)
07-13 03:03:10.023 10190 10239 I System.out: GetInteger(MAX_TEXTURE_IMAGE_UNITS, out=16)
07-13 03:03:10.023 10190 10239 I System.out: GetInteger(MAX_FRAGMENT_UNIFORM_VECTORS, out=1024)
07-13 03:03:10.023 10190 10239 I System.out: GetInteger(MAX_VERTEX_UNIFORM_VECTORS, out=1024)
07-13 03:03:10.023 10190 10239 I System.out: GetInteger(MAX_VERTEX_ATTRIBS, out=16)
07-13 03:03:10.023 10190 10239 I System.out: GetInteger(MAX_TEXTURE_SIZE, out=8192)
07-13 03:03:10.023 10190 10239 I System.out: GetInteger(MAX_CUBE_MAP_TEXTURE_SIZE, out=4096)
07-13 03:03:10.024 10190 10239 I System.out: GetInteger(MAX_RENDERBUFFER_SIZE, out=8192)
07-13 03:03:10.024 10190 10239 I System.out: GetInteger(MAX_COLOR_ATTACHMENTS, out=4)
07-13 03:03:10.024 10190 10239 I System.out: GetString(VENDOR) = "ARM"
07-13 03:03:10.024 10190 10239 I System.out: GetString(RENDERER) = "Mali-T760"
07-13 03:03:10.025 10190 10239 I System.out: GetString(VERSION) = "OpenGL ES 3.2 v1.r15p0-00rel0.68b65ac7cf15907aeb95fa944f39eef2"
07-13 03:03:10.025 10190 10239 I System.out: GetString(SHADING_LANGUAGE_VERSION) = "OpenGL ES GLSL ES 3.20"
07-13 03:03:10.026 10190 10239 I System.out: PixelStorei(UNPACK_ALIGNMENT, 1)
07-13 03:03:10.390 10190 10239 I System.out: GenFramebuffers(out=1)
07-13 03:03:10.390 10190 10239 I System.out: BindFramebuffer(FRAMEBUFFER, 1)
07-13 03:03:10.391 10190 10239 I System.out: GenRenderbuffers(out=1)
07-13 03:03:10.391 10190 10239 I System.out: BindRenderbuffer(RENDERBUFFER, 1)
07-13 03:03:10.400 10190 10239 I System.out: RenderbufferStorage(RENDERBUFFER, DEPTH_COMPONENT16, 1024, 1024)
07-13 03:03:10.400 10190 10239 I System.out: FramebufferRenderbuffer(FRAMEBUFFER, DEPTH_ATTACHMENT, RENDERBUFFER, 1)
07-13 03:03:10.400 10190 10239 I System.out: GenTextures(out=1)
07-13 03:03:10.401 10190 10239 I System.out: BindTexture(TEXTURE_2D, 1)
07-13 03:03:10.401 10190 10239 I System.out: TexParameteri(TEXTURE_2D, TEXTURE_MAX_LEVEL, 0)
07-13 03:03:10.402 10190 10239 I System.out: GetError() = ZERO
07-13 03:03:10.402 10190 10239 I System.out: TexImage2D(TEXTURE_2D, 0, RGBA16F, 1024, 1024, 0, RGBA, HALF_FLOAT, null)
07-13 03:03:10.402 10190 10239 I System.out: GetError() = INVALID_OPERATION
07-13 03:03:10.403 10190 10239 I System.out: TexParameteri(TEXTURE_2D, TEXTURE_MAG_FILTER, NEAREST)
07-13 03:03:10.403 10190 10239 I System.out: TexParameteri(TEXTURE_2D, TEXTURE_MIN_FILTER, NEAREST)
07-13 03:03:10.403 10190 10239 I System.out: TexParameteri(TEXTURE_2D, TEXTURE_WRAP_T, CLAMP_TO_EDGE)
07-13 03:03:10.403 10190 10239 I System.out: TexParameteri(TEXTURE_2D, TEXTURE_WRAP_S, CLAMP_TO_EDGE)
07-13 03:03:10.404 10190 10239 I System.out: FramebufferTexture2D(FRAMEBUFFER, COLOR_ATTACHMENT0, TEXTURE_2D, 1, 0)
07-13 03:03:10.404 10190 10239 I System.out: CheckFramebufferStatus(FRAMEBUFFER) = FRAMEBUFFER_INCOMPLETE_ATTACHMENT
07-13 03:03:10.412 10190 10239 E com.jme3.app.AndroidHarnessFragment: Grave Exception thrown in Thread[GLThread 19989,5,main]
07-13 03:03:10.412 10190 10239 E com.jme3.app.AndroidHarnessFragment: java.lang.IllegalStateException: Framebuffer has erronous attachment.
....
The parameters in both executions are exactly the same although the first one is outputted in decimal format instead of GL constants
I’ve also checked if the EGL configuration was the same, if the gles3 context was properly defined and initialized and so on but I’ve just run out of ideas. Any help is appreciated
Sorry for the long post
Thanks