When controlling OpenGL from other threads, if it is GLSurfaceView, we use queueEvent(). Then, in cases where OpenGL is created on SurfaceView (use eglCreateWindowSurface, eglMakeCurrent, etc.), how should I implement for operating OpenGL from other threads?
You can implement your own queueEvent.
Make a queue. (ArrayList<Runnable> mQueue)
queueEvent() adds Runnable instances to this queue(mQueue).
Your SurfaceView's rendering thread pops event from the queue.
invokes event.run()
Use locks (synchronized or explicit locks) to avoid problems.
In GLSurfaceView, there are 3 and 4 steps in a loop of GLThread and mQueue is protected by synchronized blocks.
see also: GLSurfaceView
PS: sorry for my poor english.
Related
What is different between this:
queueEvent(new Runnable(){
#Override
public void run() {
mRenderer.method();
}});
And this:
mRenderer.method();
And what is better for OpenGL FPS?
GLSurfaceView creates a separate rendering thread. In OpenGL, you need a current context for making any OpenGL calls. The "current context" state is per thread. GLSurfaceView creates an OpenGL context for you, and makes it current for any of the GLSurfaceView.Renderer overrides you implement. So as long as you make OpenGL calls in those methods, you don't have to worry about any of that, it just works like pure magic (well, it's not really magic, but hides a lot of complexity).
Based on this, you can't make OpenGL calls from the UI thread without jumping through hoops. So simply calling a method on the Renderer in something that is e.g. triggered by user input, and then making OpenGL calls in that method, will fail. Beyond that, even if you don't make OpenGL calls in the method, you have to worry about thread safety if the method accesses/modifies member variables of the Renderer that are also used by the rendering thread.
Using queueEvent() provides a convenient way of executing a method in the rendering thread. So you don't have to worry about thread safety of Renderer member variables, because all access will happen in the rendering thread.
I believe you might also be able to make OpenGL calls in that method if you submit it through queueEvent(). But I'm not totally sure if the OpenGL context is always current in the rendering thread, or if that's only guaranteed while the Renderer method overrides are called. It's much more typically to just change state in the Renderer in response to user input, and then use that new state in your override of Renderer.onDrawFrame().
When creating a SurfaceView it's normal to also create a separate thread to draw onto the surface. Is it better programming practice to have the thread be created and destroyed at the same time the activity is, or at the same time the surface is?
What are some of the advantages/pitfalls of either way?
The Activity and the View are created at essentially the same time. The Surface is created later, and that's what the SufaceHolder callbacks are for.
You can't render on the Surface before it exists or after it's destroyed, so there's no point in starting your rendering thread before then or leaving it running after. The tricky part is that the callbacks happen on the main UI thread (since that's where you set it up), so the surfaceDestroyed() callback could be called while your render thread is doing work.
EDIT:
Some notes about the SurfaceView / Activity lifecycle are included below. These are now part of the official Android documentation; see Appendix B in the System-Level Graphics doc. The original post is available below for historical purposes.
You can see examples of both approaches in Grafika. Approach #1 (create/destroy thread in onResume/onPause) can be seen in TextureFromCameraActivity, approach #2 (create/destroy thread in surfaceCreated/surfaceDestroyed) can be seen in HardwareScalerActivity and RecordFBOActivity.
A few thoughts about app life cycle and SurfaceView.
There are two somewhat independent things going on:
Application onCreate / onResume / onPause
Surface created / changed / destroyed
When the Activity starts, you get callbacks in this order:
onCreate
onResume
surfaceCreated
surfaceChanged
If you hit "back", you get:
onPause
surfaceDestroyed (called just before the Surface goes away)
If you rotate the screen, the Activity is torn down and recreated, so you get
the full cycle. (You can tell it's a "quick" restart by checking isFinishing().) It might be possible to start / stop an activity so quickly that surfaceCreated() might happen after onPause(), but I'm not sure about that.
If you tap the power button to blank the screen, however, you only get onPause() --
no surfaceDestroyed(). The Surface remains alive, and rendering can continue (you
even keep getting Choreographer events if you continue to request them). If you have
a lock screen that forces a specific orientation your Activity can get kicked, but
if not you can come out of screen-blank with the same Surface you had before.
This raises a fundamental question when using a separate renderer thread with
SurfaceView: should the lifespan of the thread be tied to the Surface or to the
Activity? The answer is: it depends on what you want to have happen when the screen
goes blank. There are two basic approaches: (1) start/stop the thread on Activity
start/stop; (2) start/stop the thread on Surface create/destroy.
#1 interacts well with the app lifecycle. We start the renderer thread in onResume() and
stop it in onPause(). It gets a bit awkward when creating and configuring the thread
because sometimes the Surface will already exist and sometimes it won't. We can't simply
forward the Surface callbacks to the thread, because they won't fire again if the
Surface already exists. So we need to query or cache the Surface state, and forward it
to the renderer thread. Note we have to be a little careful here passing objects between
threads -- best to pass the Surface or SurfaceHolder through a Handler message, rather
than just stuffing it into the thread, to avoid issues on multi-core systems (cf.
Android SMP Primer).
#2 has a certain appeal because the Surface and the renderer are logically intertwined.
We start the thread after the Surface has been created, which avoids the inter-thread
communication concerns. Surface created / changed messages are simply forwarded. We
need to make sure rendering stops when the screen goes blank, and resumes when it
un-blanks; this could be a simple matter of telling Choreographer to stop invoking the
frame draw callback. Our onResume() will need to resume the callbacks if and only if
the renderer thread is running. It may not be so trivial though -- if we animate based
on elapsed time between frames, we could have a very large gap when the next event
arrives, so an explicit pause/resume message may be desirable.
The above is primarily concerned with how the renderer thread is configured and whether
it's executing. A related concern is extracting state from the thread when the
Activity is killed (in onPause() or onSaveInstanceState()). Approach #1 will work
best for that, because once the renderer thread has been joined its state can be
accessed without synchronization primitives.
I have create a screen with two kind of views : normal view (buttons to receive up/down action ) and surfaceview. The SurfaceView implements SurfaceHolder.Callback and run on another thread and have a method name Tick().
When those buttons receive action, they will call method Tick(), and I want this method will run same thread with SurfaceView (for synchronize purpose), but don't know how to.
Please give me some idea for my issues.
Thanks :)
If you really want to run Tick() method in separate thread which also draws on the surface you can use HandlerThread for it. So you will be able to create Handler for it and post runnables which will be executed in this thread. But this also will put some restrictions on your drawing routine - you need to prevent it from sleeping or waiting because thread need to process message queue.
But actually I suppose any other reasonable way of synchronization will be easier than running this method on the same thread.
I am interested in creating a 2D drawing application for Android using touch input. I will be using a SurfaceView to accomplish this as it is the most efficient next to OpenGL, but for this application, it should now be an issue.
After reading the documentation and overview (http://developer.android.com/guide/topics/graphics/2d-graphics.html) of SurfaceView and Canvas drawing, it seems they recommend creating an inner class which extends Thread to handle the work for drawing.
This makes sense of course as to not lock up UI and offload the work to a secondary thread. However, I am unsure why they do not use an external class which extends Thread, or even better, why not use a Runnable that is external?
Is there a benefit to leaving this as an inner class? And is there a benefit to extending Thread as opposed to implementing Runnable for Canvas drawing?
Thanks!
Well in their scenario they got one thing right and one thing wrong IMO:
The decision to make the thread class an inner class is ok, because that class is only required in that particular part of the code, so no need to expose it to the outside.
I don't really agree with extending Thread. The best practice is to implement Runnable and override the run method. This is because you can use that Runnable in several different ways, like starting a normal thread, queuing it up in a thread pool, etc. It also allows you to extend a different class if you need, since Runnable is just an interface.
What is better for a android game to use:
a SurfaceView with a rendering thread
or
a SurfaceView with a thread that calls the SurfaceView function doDraw()
Thanks.
The drawing in a SurfaceView is already handled in a separate thread. You do not need to spawn a new one.
See the API doc about it:
One of the purposes of this class is to provide a surface in which a secondary thread can render into the screen. If you are going to use it this way, you need to be aware of some threading semantics:
All SurfaceView and SurfaceHolder.Callback methods will be called from the thread running the SurfaceView's window (typically the main thread of the application). They thus need to correctly synchronize with any state that is also touched by the drawing thread.
You must ensure that the drawing thread only touches the underlying Surface while it is valid -- between SurfaceHolder.Callback.surfaceCreated() and SurfaceHolder.Callback.surfaceDestroyed().