I have some experience with the SurfaceView. To prevent unneeded redrawing, I always use a countdown variable which tells the thread how often the view should be drawn.
countdown > 0 : draw ? doNothing
However I was thinking about changing that to use a "push surface". So just push a buffer to the surface and show that instead of setting a countdown variable everywhere in my project.
My problem is that I can't find a good tutorial/resource where the usage is described. Currently I have something like this:
// panel is the SurfaceView
c = panel.getHolder().lockCanvas(null);
synchronized (panel.getHolder()) {
panel.updatePhysics();
panel.onDraw(c);
}
panel.getHolder().unlockCanvasAndPost(c);
I have no idea on how to get the canvas to draw on when I can't use lockCanvas() as mentioned in the documentation.
This question is obsolete after making the surface types deprecated. So it isn't used anymore...
Related
So, I'm developing a game and I'm using Canvas with SurfaceHolder to update the screen every time an object is supposed to move. That much is working fine so far. Now, the problem happens when I want to stop drawing to the Canvas and just leave it as it is based on the last drawing commands.
So one way that I tried was to simply return from the function that I call when drawing when the end condition is met. However, when I do this, the canvas starts rapidly alternating between the commands sent right when the condition was met and the commands sent one iteration before. I have no idea how or why this is happening since the drawing function is not executing any of its draw commands after the condition is met. Can anyone explain how the canvas can keep refreshing itself when it doesn't get any draw commands?
The code in the thread for locking and unlocking is pretty simple:
public void run() {
Canvas c = null;
try {
c = sh.lockCanvas(null);
synchronized(sh) {
drawCan(c);
}
}
finally {
if(c!=null) {
sh.unlockCanvasAndPost(c);
}
}
}
and the drawCan function is structured like this:
public void drawCan(Canvas c) {
/* Check if user's health is greater than 0. Don't draw anything if it is less */
if(userHealth<=0) {
return;
}
/* Drawing commands - drawRect(), drawBitmap(), etc are run here */
}
Now normally, this runs fine. But when the userHealth condition is met, the Canvas constantly alternates between the last commands sent and the commands right before that. I know that the draw functions are not being called because I used Log.d() in that area of the code and no messages appeared on LogCat after the condition was met. Can someone explain why this is happening and what the solution would be?
The Canvas is double- or triple- buffered and not erased between frames. When you call lock/unlock, you're switching between previously-rendered buffers.
If you move your if(userHealth<=0) test into run() and use it to avoid calling lock/unlock, you should get the desired effect.
For a much longer explanation about what's going on, see this post.
Update: I realized today that I'd omitted a detail (from the answer and ensuing comments). It doesn't change the answer but it may be useful to know.
The lockCanvas() method takes an optional "dirty" rect that allows you to do partial updates. If you use this, and Surface is able to keep track of the "front" buffer you just rendered, the system will copy the non-dirty contents of the front buffer to the back buffer as part of locking the Surface (see copyBlt() in Surface.cpp).
The system doesn't guarantee that this will work, which is why the "dirty" rect is an in-out parameter. If the front buffer isn't available to copy from, the lock method will just expand the dirty rect to cover the entire screen. In either case, your app is responsible for updating every pixel in the "dirty" rect; if you don't, you get the effects you observed.
This does mean that the Surface is explicitly trying to be double-buffered when used with a Canvas, which would explain why you're seeing two frames alternating rather than three even though SurfaceView is generally triple-buffered. (Which is the thing that has been nagging at me since I wrote this up.) It's also possible to be double-buffered if you're just not generating frames fast enough to require triple-buffering.
Hi Android Developers,
What is the best way to interrupt a current rendering phase of GLSurfaceView and start a new one when mode is equal to "Render_when_dirty"? I artificially stop rendering in "onDraw" method by checking a flag and returning from actual rendering method which is called in "onDraw" method; then, in main thread's context i call "requestRender()" to refresh the scene. However, due to a reason that i am not aware of, some of the intermediary old frames are displayed for a very very short period of time(on the other hand, they endure for so long period of time that users can realize the transition); before actual scene is rendered by opengl es 2.x engine. It doesn't affect anything at all; but troublesome to be fixed. What do you suggest?
P.S. Throwing InterruptedException within onDraw method is useless due to the destruction of actual rendering thread of GLSurfaveView.
Kind Regards.
When you say some of the old frames are drawn - do you mean part of the frame that is drawn is old or multiple calls of onDraw() still lead to some of the old information being shown on the display.
There are a few things I can see happening here. If you have a onDraw() like this:
onDrawFrame(){
... stuff ...
if (stateVariableSet)
return;
... stuff ...
my understanding is that when the function is done being run, that the back/front buffer get swapped and drawn. One thing that could be happening here is that you see a few calls of onDrawFrame() being rendered while you try to update the state/State variable.
On the other hand, if you have something like this:
onDrawFrame(){
... stuff..
semaphore.acquire(); // lock the thread waiting for the state to update
... stuff ...
then the things that have been drawn before the lock will be stale (for that frame only though - at least that's what I'd anticipate).
Also are you running on a multi-core system?
I am developing a game in Android using AndEngine GLES-2. I am facing a problem while resetting a scene after player has completed a level. When I reset the scene, all the sprites lose their positions and tend to appear in each other's positions i.e. they swap their positions.
I have tried all the things like setting all the sprites etc. to null and calling methods like clearUpdateHandlers() and clearEventModifiers() etc. but no success yet.
I found out after a lot of googling that engineOptions.getRenderOptions().disableExtensionVertexBufferObjects(); method can fix this problem. So I am trying to invoke it but compiler gives error saying that this method in not defined for RenderOptions class.
I checked the RenderOptions class in org.andengine.engine.options package and the method really does not exist in that class. Am I missing any plug-in or is there any other problem? Please help, I am stuck.
you need to manually restart the scene, for example:
To restart a Scene you can Finish the activity and Start again but different level with SharedPreferences, or Tag's in intents, or you can set position of each Sprite and cler the Scene with:
//detachChild this Sprites that you do not use
Scene.detachChild(SpriteX);
//clear the space of memory of each sprite that you do not use
SpriteX.dispose();
//unload the bitmaps that you do not use
BitMapsX.unload();
this method have a seconds to run, but you can use a elegant "hud" in you game, and while charging set in the hud a logo or animation with "loading", best regards
I have got some strange and unexpected results from my program in OpenGL ES for android for example in the code below:
matrix = ThisRot.get();
gl.glMultMatrixf(matrix, 0);
currentRotation.toMatrix(matrix);
temp.set(matrix);
I set the matrix value before I use it as an argument for gl.glMultMatrixf and after that I change the value of matrix and use it for another purpose, but it has effect an the way the object rotate so it should have effect on gl.glMultMatrixf(). and that's not the only one, some other places in my code I had this unexpected results. so I have thought maybe these happen due to mutual exclusion and multitreading and those kind of things.
am I right? should we worry about multithreading when we code in Opengl ES for android? How can I avoid these kind of problems.
Of course you should worry about multithreading. In particular, Android creates its own GLThread for rendering, when you attach a GLRenderer-derived class to a GLSurfaceView using the setRenderer() function.
In fact, multithreading can cause crashes (not only unexpected behavior) in your programs especially when you loop through arrays adding/removing objects and such.
Check if you are modifying the same data inside the onDrawFrame function of your GLRenderer and your own thread. If you are, try adding the following around the modified code (in both threads):
synchronize(variable) {
modify(variable);
}
This will lock the variable throughout the modify() function until it ends the block. Try not to overuse it, though, only in places where you need it. One thread will block the other one until it's finished!
I'm currently doing a test with a LiveWallpaper in Android. I am drawing something on the canvas using code that looks something like this:
final SurfaceHolder holder = getSurfaceHolder();
Canvas c = new Canvas();
c = holder.lockCanvas(); // c becomes null
c.save();
c.drawBitmap(currentBitmap);
c.restore();
holder.unlockCanvasAndPost(c);
This part is working fine under normal circumstances. However, I have a listener that executes this code whenever a setting is changed in the Settings that correspond to this service. It seems that whenever I execute this code from the settings activity, I am getting a NullPointer on the c.save() method.
It seems that only when the Wallpaper is not in the foreground, the holder.lockCanvas(). Is it impossible to draw to this surface when it's not in the foreground?
That's right. A common way to avoid this is to unregister your listener in onPause or onVisibilityChanged(false), and reregister in onResume or onVisibilityChanged(true), since you shouldn't react to settings changes when your canvas isn't visible.
Another solution would be to simply surround that section of code with a null check, and forget about it. I'd recommend against this, though, since what you really want to do is prevent your code from even attempting to draw to the surface when it's not in view.
I realize that this question is ancient, but having had the same question and having found a different answer that worked for me, I thought I'd share for posterity.
Check if SurfaceHolder.getSurface.isValid() before getting the canvas from the surface. Fixed my null canvas issues.