I'm trying to build an overlay for an Android application that uses GLESv2.
I've hooked eglSwapBuffers in order to insert my rending code just before the frame finishes.
I'm able to do simple things like drawing a square with the scissor test:
glEnable(GL_SCISSOR_TEST);
glScissor(0, 0, 200, 200);
glClearColor(1, 0, 0, 1);
glClear(GL_COLOR_BUFFER_BIT);
glDisable(GL_SCISSOR_TEST);
I've also had success drawing simple shapes with the following code, but as soon as I start using vertex attrib pointers the application stops rending correctly and shows a mostly-black screen with a small section that still displays correctly. I'm sure there's some open-gl state that I'm clobbering here but I'm not sure what it is. What would I need to save/restore before/after my draw calls in order to allow the app to continue to render correctly with my overlay?
// Save application state
GLint prev_program;
glGetIntegerv(GL_CURRENT_PROGRAM, &prev_program);
// Do overlay drawing
glUseProgram(program);
glVertexAttribPointer(vPosition, 2, GL_FLOAT, GL_FALSE, 0, RectangleVertices);
glEnableVertexAttribArray(vPosition);
glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
glDisableVertexAttribArray(vPosition);
// Trying to restore application state here - there are probably more things that I'm missing.
glUseProgram(prevProgram);
What would I need to save/restore before/after my draw calls in order to allow the app to continue to render correctly with my overlay?
Everything that you modified ...
Note that even full state restoration can be inadequate for some developer use cases. Normally this is a bug in the application (application making assumptions about object ID assignments), but there are use cases in development tooling such as verbatim API trace replay tools where such assumptions are made.
Related
I have an app which has to create Sprite-instances on the fly based on data contained in byte arrays (PNGs and JPGs). The following code is used to create the sprites:
Texture2D texture = new Texture2D(2, 2, TextureFormat.RGBA32,false,false);
texture.LoadImage(data);
Vector2 pivot = new Vector2(0.5f, 0.5f);
Rect tRect = new Rect(0, 0, texture.width, texture.height);
return Sprite.Create(texture, tRect, pivot);
This works fine, however, depending on the device and the size of the images, after a random number of images, the app freezes and then will be shut down by the OS. Its always another image, which fails. Also, the data source is irrelevant.
Looking into the logs of the app via adb shows nothing. If I write to the debug log, I can see, that the last statement which gets called is texture.LoadImage. However, there is no exception or another information about the error. Catching the exception does also not work.
The error does not occur in the editor. The error occurs on the android devices (2) in development build and in production build.
Searching the web, I found the below entry, which states the very same problem, but no solution has been posted (they circled around the www-part, however the problem is not with that):
https://forum.unity.com/threads/android-crash-when-using-multiple-www.483941/
UPDATE
One interesting finding is, that if I set the markNonReadable-Parameter of the texture.LoadImage() method to true, the error occurs less frequently, but still is there.
texture.LoadImage(data,true);
Textures are not garbage collected. So if you create a texture using new Texture then you need to destroy the texture with Destroy(texture) when you no longer need it. I believe Sprite object also needs to be destroyed.
In your case, textures that were loaded stayed in memory until Android OS closed your app because of memory pressure.
UnloadUnusedAssets() should also destroy all the textures and sprites that are no longer referenced, but it takes a lot of time (about 1 second), so it only makes sense to call that when changing scenes.
the keywords about this topic:
CustomSurfaceView: three custom surfaceview for three different levels.
Canvas: lock/unlockAndPost method (i'm not using custom bitmap )
Multi thread ( each surface is a separate thread )
Shapes ( shapes on canvas )
Client/Server ( architecture )
Flickering ( IS WHY I'M HERE )
We are developing a client/server application and I'm working on the client side. I'm receiving messages from the server containing general data (coordinates, color, width [...] ) about paths like, circle, rectangle, line and other shapes. The web application allows the user to send these data drawing on HTML5 Canvas, to an android device that receiving this messages and parsing it, will be able to redraw all the shapes. From my own experience on this subjects, I learned that the best way to keep in control all the things you draw on the canvas, is saving everything into a buffer, array, list or something like that, then reuse it when you want (for example, you can use the older path for show, hide, move or simply change something on the canvas). In my opinion, the android application follows the best practice of android development and OOP paradigm so I'm not assuming errors related to the bad architecture. In this case, I'm saving the messages on web client side. When the user draws on HTML5 Canvas, the messages which contain shape info are perfectly reported to the android canvas, but the problem appears when:
[example]
Consider you draw 10 objects (10 messages) and you want delete only one object on web app canvas, so the only way is clearing all the canvas, and redraw all the previous shapes without the deleted shape (so resend to the client 9 messages by loop the messages buffer ). This method works perfectly for the web app but cause flickering problem on android client. So after too many experiments I found a workaround, using a Thread.sleep(100)(Whooo! 100ms is too much), in order to parse slowly the messages and let the surfaceview thread to read correctly the data (data access through singleton pattern) and write on the double-buffer of the canvas.Well, it's slow and ugly but it works ! Actually I don't like this “horrible” workaround so please help me to see an exit strategy.
This is a piece of code where the canvas get data from shapes containers and draw if data are present. The data of each containers came from server messages.
#Override
public void run() {
Canvas canvas = null;
while (running) {
//this is the surface's canvas
try {
canvas = shapesSurfaceHolder.lockCanvas();
synchronized (shapesSurfaceHolder) {
if (shapesSurfaceHolder.getSurface().isValid()) {
if(!Parser.cmdClear){
//draw all the data present
canvas.drawPath(PencilData.getInstance().getPencilPath(),
PencilData.getInstance().getPaint());
canvas.drawPath(RectData.getInstance().getRectPath(),
RectData.getInstance().getPaint());
canvas.drawPath(CircleData.getInstance().getCirclePath(),
CircleData.getInstance().getPaint());
canvas.drawPath(LineData.getInstance().getLinePath(),
LineData.getInstance().getPaint());
canvas.drawText(TextData.getInstance().getText(),
TextData.getInstance().getX(),
TextData.getInstance().getY(),
TextData.getInstance().getPaint());
} else {
//remove all canvas content and clear data.
canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);
for (int i = 0; i < AbstractFactory.SHAPE_NUM; i++) {
abstracFactory.getShape(i).clearData();
}
}
}
}
} finally {
if (canvas != null) {
shapesSurfaceHolder.unlockCanvasAndPost(canvas);
}
}
}
}//end_run()
I can summarize that, apparently, my problem is to draw too quickly
Note:
Similar concept: Android thread controlling multiple texture views causes strange flickering
Hardware acceleration is enabled.
minSdkVersion 17
Tested on
Tablet Samsung SM-T113
Google Nexus 5
The TextureView issue was due to a bug specific to TextureView. You're using SurfaceView, so that does not apply here.
When drawing on a SurfaceView's Surface, you must update every pixel inside the dirty rect (i.e. the optional arg passed to lockCanvas()) every time. If you don't provide a dirty rect, that means the entire screen must be updated. This is because the Surface is double- or triple-buffered, and swapped when you call unlockCanvasAndPost(). If you lock / clear / unlock, then the next time you lock / draw / unlock, you will not be drawing into the buffer you previously cleared.
If you want to do incremental rendering, you should point your Canvas at an off-screen Bitmap and do all your rendering there. Then just blit the entire bitmap between lock and unlock. The alternative is to store up the drawing commands, starting with the initial clear, and play them all back between lock/unlock.
The phrase "three custom surfaceview" is somewhat concerning if they're all on screen at once. If you have them all at different Z depths (default, media overlay, top) then they will behave correctly, but the system is generally more efficient if you can put everything on one.
I am working on a small game which uses OpenGL.
I was getting a gl_stack_underflow error. I have gone through the code, and there is one glPushMatrix for each glPopMatrix. Any ideas what else could be causing this error?
Did you maybe do a
glMatrixMode(GL_MODELVIEW);
/* ... */
glPushMatrix();
"balanced" by a
glMatrixMode(GL_PROJECTION);
/* ... */
glPopMatrix();
It matters which matrix is active at the time of push/pop operation.
Anyway, you shouldn't use the OpenGL built-in matrix operations at all. Use something like GLM, Eigen or linmath.h to build the matrices as part of your programs data structures and just load the matrices you require with glLoadMatrix or, when you finally go for shaders, glUniform.
No, OpenGL built-in matrix operations are not GPU accelerated, so there's no benefit at all in using them.
EDIT:
More debugging led me to the fact that glGetAttribLocation returns -1, except for the first start of the Application. Program ID is valid (I guess?), it was 12 in my testing right now. I also tried to retrieve attribute location right before drawing again, but this did not work out neither.
My shader "architecture" now looks like this:
I've turned the shader into a singleton. I.e. only one instance. Using it:
public void useProgram() {
GLES20.glUseProgram(iProgram);
getUniformLocations();
getAttributeLocations();
}
I.e. program will be sent to OpenGL, afterwards I'm retrieving uniform and attribute locations for all my variables, they are stored within a HashMap (one for each shader):
#Override
protected void getAttributeLocations() {
addToGLMap(A_NORMAL, GLES20.glGetAttribLocation(iProgram, A_NORMAL));
addToGLMap(A_POSITION, GLES20.glGetAttribLocation(iProgram, A_POSITION));
addToGLMap(A_COLOR, GLES20.glGetAttribLocation(iProgram, A_COLOR));
}
I don't understand, why the program's ID is for example 12, but all the attribute locations are non-existent in the second and the following run of my Application...
In my Application, I am loading a Wavefront object, as well as I am drawing several lines and cubes, just to try something. After starting the Application "clean", i.e. after rebooting or installing it, everything looks as intended. But if I close the Application and re-open it, it looks weird, screenshot is at the bottom.
What I'm currently doing:
onSurfaceCreated:
Taking care of culling, clear color, etc, etc.
Clear all loaded objects (just for testing, will of course not delete memory in later phase).
Reload objects (threaded).
My objects are stored like this:
public class WavefrontObject {
private FloatBuffer mPositionBuffer = null;
private FloatBuffer mColorBuffer = null;
private FloatBuffer mNormalBuffer = null;
private ShortBuffer mIndexBuffer = null;
}
Buffers are filled upon creation of the element.
They are drawn:
mColorBuffer.position(0);
mNormalBuffer.position(0);
mIndexBuffer.position(0);
mPositionBuffer.position(0);
GLES20.glVertexAttribPointer(mShader.getGLLocation(BaseShader.A_POSITION), 3, GLES20.GL_FLOAT, false,
0, mPositionBuffer);
GLES20.glEnableVertexAttribArray(mShader.getGLLocation(BaseShader.A_POSITION));
// etc...
GLES20.glDrawElements(GLES20.GL_TRIANGLES, mIndexBuffer.capacity(), GLES20.GL_UNSIGNED_SHORT, mIndexBuffer);
Do I need to disable the VertexAttribArrays after drawing them? I am currently overwriting the buffer for each drawing loop, but do they maybe interact with other models being drawn?
The model I am loading displays a small toy-plane. After restarting the Application, it looks like this (loading the object, all colors are set to white (for testing)):
So to me it looks like the buffers either have left-over stuff in them? What's the "best practice" for using these buffers? Disable the arrays? Does OpenGL ES2.0 offer some sort of "clear buffer" method that I can use before putting my values in them?
What was expected to be drawn: At the point where the "weird triangles" and colors origin from, there should be the plane-model. All in white.
When your application loses context its OpenGL context is destroyed.
So all objects (programs and its uniform/attribute handles, etc) are invalidated.
During reopening you have to clear/invalidate all singleton objects like yours...
I can successfully create and load ETC textures in Android,
using the calls:
ETC1Texture etc1tex = new ETC1Texture(...);
gl11.glCompressedTexImage2D(GL10.GL_TEXTURE_2D, 0/*level*/,
ETC1.ETC1_RGB8_OES/*internal format*/,
etc1tex.getWidth(), etc1tex.getHeight(),
0/*border*/,
etc1tex.getData().capacity()/*imagesize*/,
etc1tex.getData());
But now i need to update this texture with new image data.
I am using the call to SubImage :
GL11.glCompressedTexSubImage2D( GL10.GL_TEXTURE_2D, 0/*level*/,
0, 0, etc1tex.getWidth(), etc1tex.getHeight(),
ETC1.ETC1_RGB8_OES,
etc1tex.getData().capacity(),
etc1tex.getData());
which takes more or less the same paramaters, as previous call.
But its not working, my texture does'nt even change a bit.
But If i simply replace the SubImage call with the first one, i can see some distortion in the texture when it updates...
Does anyone know how i can use this CompressedTexSubImage call
yeah ,i meet the same problem.
i use glCompressedTexImage2D(texinfo.glTarget + face, level,glInternalFormat, pixelWidth, pixelHeight, 0,faceLodSize, data);
it works.
but when i use
glCompressedTexImage2D(texinfo.glTarget + face, level,glInternalFormat, pixelWidth, pixelHeight, 0,faceLodSize, NULL);
and then
glCompressedTexSubImage2D(texinfo.glTarget + face, level, 0, 0, pixelWidth , pixelHeight , glInternalFormat,faceLodSize, data);
it dose not work.
gl error is GL_INVALID_OPERATION
i need to use glCompressedTexSubImage2D,because i load one texture not in one buffer .
may be load into more than one tile buffer.
if one tile loaded completed, than call glCompressedTexSubImage2D to handle it.
According to the API (https://www.khronos.org/opengles/sdk/1.1/docs/man/glCompressedTexSubImage2D.xml)
"The required paletted formats do not allow subimage updates, but
other formats defined by extensions may."
I assume this means that for ETC1 compression, subimage just isn't allowed.