3

I am creating a layout of type FrameLayout, in which I am adding two views. Two views are objects of GLSurfaceView and SurfaceView respectively. According to Android Developers Documentation regarding SurfaceView,

"The surface is Z ordered so that it is behind the window holding its SurfaceView; the SurfaceView punches a hole in its window to allow its surface to be displayed."

It works well for me and SurfaceView always stays behind my GLSurfaceView (used for opneGL drawings). But resuming after external event the behavior is odd for a following configuration,

Android Version: 4.3 Device Model Number : Nexus 7 Kernel Version 3.4.0.g1f57c39 Jun 13 Build Number: JWR66N

For this configuration, resuming after external event puts my GLSurfaceView behind SurfaceView. In other words, SurfaceView is placed at top in ZOrder and my OpenGL drawings are no more visible. On versions greater that Android 4.3, this behavior is not seen.

I can replicate this behavior on all versions by calling SurfaceView's following method with true as a parameter.

void setZOrderOnTop

Is this known issue. Anybody can help me on this?

Regards, Sumedh

2 Answers2

9

SurfaceViews have two parts, the Surface and the View. The Surface is a completely independent layer. The View is there so the UI layout code has something to work with. Generally the View is just transparent black, so you can see through to whatever is behind it.

GLSurfaceView is just SurfaceView with some code to manage EGL contexts and threading. Underneath it's just a SurfaceView. So if you have both a SurfaceView and a GLSurfaceView, and they have the same dimensions and Z-order, then one of them is going to "win" and the other is going to "lose" because they're trying to occupy the same space at the same time. There is no defined value for which one will "win", so inconsistent behavior is expected.

One way to avoid clashes is to leave one set to the default Z, and call setZOrderMediaOverlay() on the other. The "media overlay" is still behind the UI, but above the default Surface position. If you use setZOrderOnTop(), the Surface will be positioned above the UI as well.

The upper Surface will need to be rendered with transparent pixels if you want to see something behind it (the same way that the View needs to be transparent to see the Surface).

The most efficient way to avoid this issue is to not have this issue: use one SurfaceView for everything, rendering all of your non-UI-element content to it. This requires a bit more work (and probably a SurfaceTexture) if you're rendering video or showing a camera preview on one of the Surfaces.

You can find some examples in Grafika. The "multi-surface exerciser" demonstrates three overlapping SurfaceViews rendered in software, overlapping with UI elements. Other activities show ways to work with Surfaces, GLES, the camera, and video.

See also the Android System-Level Graphics Architecture doc, which explains all this in much greater detail.

fadden
  • 51,356
  • 5
  • 116
  • 166
  • Thanks. I did exactly as you explained. Here is what I am doing, I have two views 1. GLSurfaceView (draws graphics produced by Cocos2dx framework), 2. SurfaceView (Renders Video, Media Player). I want my cocos2dx graphics to be always on top of the video, hence I call setZOrderMediaOverlay() for GLSurfaceView.I assume as I am not setting any ZOrder parameter for SurfaceView, it is defaulted and will be always Z ordered below GLSurfaceView. The layering works correctly with devices using OS which is higher than Jelly bean, for Jelly Bean it doesn't work. Am I missing something..or otherways.. – Sumedh Shantaram Dec 23 '14 at 12:42
  • I went through the System-Level Graphics Architecture doc also. Only way I can relate it to Jelly bean is "The data structure is currently always created and "owned" by the consumer. In Android 4.3 only the producer side was "binderized", i.e. the producer could be in a remote process but the consumer had to live in the process where the queue was created. This evolved a bit in 4.4, moving toward a more general implementation.", but I don't think this would have anything with Z order.. am I correct – Sumedh Shantaram Dec 23 '14 at 12:56
  • 1
    If you want the two to overlay you'll need to make sure that the one on top (the GLSurfaceView) is using the right color format and rendering pixels with non-opaque alpha. You can confirm the ordering and color format with `adb shell dumpsys SurfaceFlinger`, looking at the hardware composer summary section (a cut-down version appears in a couple places in the graphics architecture doc -- surfaces are listed in back-to-front order). The Z-order call must happen *before* the surface is created; I don't remember if GLSurfaceView lets you do that. Can you use a plain SurfaceView with Cocos2dx? – fadden Dec 23 '14 at 16:58
  • Thanks. I had some problem with layering, now I am setting my GLSurfaceView with Z order set to top, and SurfaceView with setZOrderMediaOverlay(), and it works on Jelly bean too. But now on resuming from external event, Z order changes and GLSurfaceView somehow goes on lower z order than SurfaceView, and I am back in the whole again. Open GL graphics goes behind the Video in Surfaceview. And this happens only on Jelly bean. Please see next comment which has dumps from SurfaceFlinger. – Sumedh Shantaram Dec 24 '14 at 11:27
  • adb shell dumpsys here http://binbox.io/HXA7f#qwCvZNMk, As you can see SurfaceFlinger does not consider my GLSurfaceView (with Open GL drawings) for composite. And this happens on Jelly bean.... would appreciate your inputs.. – Sumedh Shantaram Dec 24 '14 at 11:44
  • Check farther up in the dumpsys output to see the list of windows and Z-ordering -- see if it got recreated at a different depth. If two surfaces occupy the same space, HWC may completely ignore one. GLSurfaceView does its own management of the Surface lifecycle, and I don't remember offhand how it interacts with the activity lifecycle (which is why I was asking if you can use a plain SurfaceView... life may be easier if you just do the EGL and thread management yourself). See also https://source.android.com/devices/graphics/architecture.html#activity . – fadden Dec 24 '14 at 16:34
  • HWC behavior has changed with every release. It's possible the colliding surfaces were handled differently in older releases, i.e. it went ahead and drew both of them anyway. GLSurfaceView is pretty aggressive about discarding state, so you may need to be equally aggressive about resetting the Z-order (maybe in onResume()?) – fadden Dec 24 '14 at 16:35
  • How do I set Z-Order of GLSurfaceView in onResume(), Android documentation has following to say about method setZOrderOnTop, "Note that this must be set before the surface view's containing window is attached to the window manager." Doing it in onResume will have no effect because widow was already attached. Am I correct on this, is there any other way to reset the Z-order. – Sumedh Shantaram Dec 26 '14 at 10:49
  • I can see similar type of post here, but no one has answered it. http://stackoverflow.com/questions/22407039/surface-view-z-index-changing-after-onresume-onpause-android – Sumedh Shantaram Dec 26 '14 at 11:02
  • do u have any inputs on this? – Sumedh Shantaram Dec 30 '14 at 17:08
  • Hi Fadden, From more analysis I can confirm that GLSurfaceView is invisible because it gets drawn at different depth. I am not sure why this happens even after setting setZOrderMediaOverlay() which should make sure the view is on top of all other views. I understand your suggestion to use SurfaceView instead of GLSurfaceView, but It does not look feasible to me, as the cocos2dx works with GLSurfaceView. If you can point me to good example of using SurfaceView to draw OpenGL drawings, I may try, but it is still a try. Your other suggestion was to set the Z order aggressively. How can I do this? – Sumedh Shantaram Jan 06 '15 at 17:51
  • I don't remember what exactly GLSurfaceView does and when -- you'd need to dig into the source to figure out if what you want to do is even possible. I'm surprised a graphics engine would use GLSurfaceView. Grafika has a few examples of GLES on SurfaceView and TextureView, but it's mostly of use for the EGL setup -- it doesn't do much beyond textured quads. The "media overlay" is in the middle, above the default SurfaceView layer, but below Views. – fadden Jan 06 '15 at 19:35
  • fadden, Removing and adding the view in onResume solves this issue..have a look at https://code.google.com/p/android/issues/detail?can=2&start=0&num=100&q=&colspec=ID%20Type%20Status%20Owner%20Summary%20Stars&groupby=&sort=&id=94038 – Sumedh Shantaram Jan 19 '15 at 18:38
0

Dont use "setZOrderOnTop" as true. That will get it over all the other layouts.

If you are using multiple surfaceviews. use this for each surfaceview

yourSurfaceView.setZOrderMediaOverlay(true);

then set this setZOrderOnTop as false for the surfaceview you initiated later and wanted it to get back to the other surfaceviews

secondSurfaceview.setZOrderOnTop(false);
Sajid Zeb
  • 1,806
  • 18
  • 32