While writing unit tests for a simple NDK Opengl ES 3.0 demo, I encountered an issue in using multiple render targets. Consider this simple Fragment shader with two outputs, declared in a C++11 string literal.
const static std::string multipleOutputsFragment = R"(#version 300 es
precision mediump float;
layout(location = 0) out vec3 out_color1;
layout(location = 1) out vec3 out_color2;
void main()
{
out_color1 = vec3(1.0, 0.0, 0.0);
out_color2 = vec3(0.0, 0.0, 1.0);
}
)";
I have correctly setup an FBO with two color attachments (via glFramebufferTexture2D) and the glCheckFramebufferStatus comes back as GL_FRAMEBUFFER_COMPLETE. I also call glDrawBuffers(2, &attachments[0]), where attachments is a vector of GL_COLOR_ATTACHMENTi enums. Afterwards, I compile and link the shader, without any linking or compile errors (just used a simple vertex passthrough that is irrevelant to this post).
Is there any reason why I can get the fragment location for out_color1 but not for out_color2, using the following opengles function?
auto location = glGetFragDataLocation(m_programId, name.c_str());
The correct location is returned for out_color1 of 0, but when providing "out_color2" to glGetFragDataLocation, the function returns -1.
I'm testing this on a physical device, Galaxy S4, Android 4.4.4 Adreno 320, Opengl ES 3.0.
if you call:
glReadBuffer(COLOR_ATTACHMENT_1);
glReadPixels(*);
can you see the data that you write to the fbo?
Related
I've been spending the better part of the last 2 days hunting down an OpenGL ES bug I've encountered only on some devices. In detail:
I'm trying to implement skeletal animations, using the following GLSL code:
#ifndef NR_BONES_INC
#define NR_BONES_INC
#ifndef NR_MAX_BONES
#define NR_MAX_BONES 256
#endif
in ivec4 aBoneIds;
in vec4 aBoneWeights;
layout (std140) uniform boneOffsets { mat4 offsets[NR_MAX_BONES]; } bones;
mat4 bones_get_matrix() {
mat4 mat = bones.offsets[aBoneIds.x] * aBoneWeights.x;
mat += bones.offsets[aBoneIds.y] * aBoneWeights.y;
mat += bones.offsets[aBoneIds.z] * aBoneWeights.z;
mat += bones.offsets[aBoneIds.w] * aBoneWeights.w;
return mat;
}
#endif
This is then included in the vertex shader and used as such:
vec4 viewPos = mv * bones_get_matrix() * vec4(aPos, 1.0);
gl_Position = projection * viewPos;
The desired output, achieved for example on the Android Emulator (armv8) running on my M1 MacBook Pro is this:
I can't actually capture the output of the faulting device (Espon Moverio BT-350, Android on x86 Intel Atom) sadly, but it's basically the same picture without head, arms or legs.
The uniform buffer bound to boneOffsets, for testing, is created as a std::vector<glm::mat4> with size 256 and is created/bound as such:
GLint buffer = 0;
std::vector<glm::mat4> testData(256, glm::mat4(1.0));
glGenBuffers(1, &buffer);
glBindBuffer(GL_UNIFORM_BUFFER, buffer);
glBufferData(GL_UNIFORM_BUFFER, sizeof(glm::mat4) * testData.size(), &testData[0], GL_DYNAMIC_DRAW);
glBindBufferBase(GL_UNIFORM_BUFFER, 0, buffer);
glUniformBlockBinding(programId, glGetUniformBlockIndex(programId, "boneOffsets"), 0);
Am I missing a step somewhere in my setup? Is this a GPU bug I'm encountering? Have I misunderstood the std140 layout?
P.S.: After every OpenGL call, I run glGetError(), but nothing shows up. Also nothing in the various info logs for Shaders and Programs.
EDIT
It's the next day, and I've tried skipping the UBO and using a plain uniform array (100 elements instead of 256, my model has 70-ish bones anyway). Same result.
I've also just tried with a "texture". It's a 4*256 GL_RGBAF teture, which is "sampled" as such:
uniform sampler2D bonesTxt;
mat4 read_bones_txt(int id) {
return mat4(
texelFetch(bonesTxt, ivec2(0, id), 0),
texelFetch(bonesTxt, ivec2(1, id), 0),
texelFetch(bonesTxt, ivec2(2, id), 0),
texelFetch(bonesTxt, ivec2(3, id), 0));
}
Still no dice. As a comment suggested, I've checked my bone IDs and Weights. What I send to glBufferData() is ok, but I can't actually check what's on the GPU because I can't get RenderDoc (or anything else) to work on my device.
I finally figured it out.
When binding my bone IDs I used glVertexAttribPointer() instead of glVertexAttribIPointer().
I was sending the correct type (GL_INT) to glVertexAttribPointer(), but I didn't read this line in the docs:
For glVertexAttribPointer() [...] values will be converted to floats [...]
As usual, RTFM people.
I am using OpenGL ES to run some shaders on Android.
On some older/cheap devices they do not support highp precision so the shader output is incorrect.
I need to know when the app starts if the device can support high precision. That way I can tell the user "forget it, your device does not support high precision floats" rather than have it output garbage for them.
I found this query code online, but it seems to only be for WebGL
var highp = gl.getShaderPrecisionFormat(gl.FRAGMENT_SHADER, gl.HIGH_FLOAT);
var highpSupported = highp.precision != 0;
Does anyone have a way I can query an android device (KitKat or higher) to see what precision the GLES shaders will support?
This is the final code I now use, but contents of range and precision are always -999 no matter where I run the code in my app. Before, during or after the GLSurfaceView has been created and GLES output has run.
IntBuffer range = IntBuffer.allocate(2);
IntBuffer precision = IntBuffer.allocate(1);
range.put(0,-999);
range.put(1,-999);
precision.put(0,-999);
android.opengl.GLES20.glGetShaderPrecisionFormat(android.opengl.GLES20.GL_FRAGMENT_SHADER, android.opengl.GLES20.GL_HIGH_FLOAT,range,precision);
String toastText="Range[0]="+String.valueOf(range.get(0))+" Range[1]="+String.valueOf(range.get(1))+" Precision[0]="+String.valueOf(precision.get(0));
Toast.makeText(getApplicationContext(),toastText, Toast.LENGTH_SHORT).show();
The above code always returns -999 for all 3 values, and the kronos doco states if an error occurs then the values will be unchanged. So it looks like there is an error or I am not calling it at the right time.
I'm writing shader codes in the GPUImage framework in Android. Then I encounter a problem of array indexing in the fragment shader.
According to Appendix of The OpenGL ES Shading Language, in vertex shader, uniform arrays can be indexed by any integer, and varying arrays can be indexed by constant-index-expression. In fragment shader, both (uniform/varying) arrays can only be indexed by constant-index-expression. Under the definition of constant-index-expression, the for-loop index should be able to used as an array index.
However, things go wrong when I use the loop index as array index in fragment shader. There is no compile error and the shader codes can be run, but it seems that the program treats all index value to 0 during every round in the loop.
Here is my fragment shader codes:
uniform sampler2D inputImageTexture;
uniform highp float sample_weights[9]; // passed by glUniform1fv
varying highp vec2 texture_coordinate;
varying highp vec2 sample_coordinates[9]; // computed in the vertex shader
...
void main()
{
lowp vec3 sum = vec3(0.0);
lowp vec4 fragment_color = texture2D(inputImageTexture, texture_coordinate);
for (int i = 0; i < 9; i++)
{
sum += texture2D(inputImageTexture, sample_coordinates[i]).rgb * sample_weights[i];
}
gl_FragColor = vec4(sum, fragment_color.a);
}
The result will be correct if I unroll the loop and access [0] to [8] for the arrays.
However, when using the loop index, the result is wrong and becomes the same as running
sum += texture2D(inputImageTexture, sample_coordinates[0]).rgb * sample_weights[0];
by 9 times, and there is no compile error reported during the process.
I've only tested one device, which is Nexus 7 with Android version 4.3. The GPUImage framework uses android.opengl.GLES20, but not GLES30.
Is this an additional restriction on shader codes in Android devices, or in OpenGL ES 2.0, or it is a device-dependent issue?
Updated:
After testing more Android devices (4.1~4.4), It seems that only that Nexus 7 device has this issue. The results on other devices are correct. It's weird. Is this an implementation issue on individual devices?
This is a little known fact, but texture lookups inside loops are an undefined thing in GLSL. Specifically the tiny sentence: "Derivatives are undefined within non-uniform control flow", see section 8.9 of the GLSL ES spec. And see section 3.9.2 to find out what non uniform control flow. The fact that this works on other devices is by chance.
Your only option may be to un roll the loop unfortunately.
I'm having another OpenGL ES driver error. This time I'm trying to compile the following lines:
precision mediump float;
varying highp vec2 textureCoordinate;
void main() {
highp vec4 color = texture2D(input0, textureCoordinate);
vec3 color3 = color.rgb;
vec2 tc = (2.0 * textureCoordinate) - 1.0;
float d = dot(tc, tc);
vec2 lookup = vec2(d, color3.r);
..
..
}
but I'm getting after the line:
GLES20.glLinkProgram(program);
native crash : "Fatal signal 11(SIGDEV) at 0x00000060(code = 1), thread 1231 "
I'm guessing that it happens because LG nexus 4 uses GPU Adreno, and it also crashes for me with error code 14 on a different crash - using too many macros.
After you compile the shader, using glGetShaderiv get the status of the shader compilation. Like:
GLint compiled;
glGetShaderiv(index, GL_COMPILE_STATUS, &compiled); //index is the shader value
Then, if compiled is returned as zero, get the info length first, and then the error message as follows:
GLint infoLen = 0;
glGetShaderiv(index, GL_INFO_LOG_LENGTH, &infoLen);
if(infoLen > 1)
{
char* infoLog = new char(infoLen);
glGetShaderInfoLog(index, infoLen, NULL, infoLog);
}
Check infoLog finally to see the error message that returned from shader compilation. Segmentation fault message in your original post does not give anything useful to solve the problem.
As far as I can see from your short code excerpt in your fragment shader you haven't specified float precision. In ES 2.0 you must explicitly specify float precision.
precision mediump float;
Please read about this in specs, p. 4.5.3 Default Precision Qualifiers.
Shader may work without specifying float precision on certain OpenGL ES drivers and may fail to compile on another ones.
However, full source code is needed to find out exact cause of your issue.
I'd suggest you to start commenting out parts of shader code until it starts compiling correctly. This way you will narrow down a problematic line (I believe even faster than waiting for answer here on SO).
I've been starting out using LibGDX to develop for PC as well as Android. I'm currently working on a little project requiring me to write custom shaders in GLSL (based on full-screen quads). Coming from a HLSL environment I had a little trouble setting the whole shader system up in combination with SpriteBatch (as I want to keep the code simple where I can).
I have the following shader code working on desktop:
Vertexshader:
attribute vec4 a_position;
uniform mat4 u_projectionViewMatrix;
void main()
{
gl_Position = a_position * u_projectionViewMatrix;
}
Fragmentshader:
uniform vec2 effectOrigin;
uniform vec2 effectDir;
void main()
{
float distToOrigin = distance(effectOrigin.xy, gl_FragCoord.xy);
gl_FragColor = vec4(mod(distToOrigin+effectDir.x*30, 30)-15, 0.0, 0.0, 1.0);
}
As I said it works on desktop (Windows), and gives me a nice circle pattern. On Android however it doesn't compile and gives me just the cleared background color. I suspected it had something to do with OpenGL ES 2 permissions on Android so I added this line to the manifest:
<uses-feature android:glEsVersion="0x00020000" android:required="true" />, but this didn't seem to make a difference.
I also thought it might have something to do with the precision of the floats and vectors, but I wasn't able to figure out how I would have to change them in order to fix it.
Is there someone who can help me on this? I haven't been able to find an answer elsewhere!
Thanks in advance,
Yuri
P.S. Is there a way to see what went wrong during compiling of the shaders? I know debugging is hard on shaders, but it would be awesome to have at least some idea of where it could've gone wrong during compilation.
Fixed it using P.T.´s suggestion on logging the compilation!
There were two problems in the fragment shader, the fixed shader looks like this:
precision mediump float;
uniform vec2 effectOrigin;
uniform vec2 effectDir;
void main()
{
float distToOrigin = distance(effectOrigin.xy, gl_FragCoord.xy);
gl_FragColor = vec4(mod(distToOrigin+effectDir.x*30.0, 30.0)-15.0, 0.0, 0.0, 1.0);
}
I added the precision definition at the top, and also changed the constant values from integers to floats (e.g. 15 to 15.0) to fix it!
Thanks P.T.!