nshct
nshct

Reputation: 1230

Android - GlSurfaceView in Fragment is running twice at the same time

I have an android app that uses a GlSurfaceView to render a 3D fullscreen scene inside a fragment. I have noticed in the profiler, that the GlSurfaceView is actually running twice (in two threads), hogging resources and tanking the FPS. I have confirmed the issue by rendering the same OpenGL scene (using the same Renderer implementation) to a live wallpaper and profiling it, which only runs it once.

Am I doing anything wrong here?


The code is as follows:

MySurfaceView

class MySurfaceView(ctx: Context): GLSurfaceView(ctx)
{
    init
    {
        setEGLContextClientVersion(3)
        preserveEGLContextOnPause = true
        setRenderer( /* instantiating the renderer class */ )
    }
}

OpenGLFragment

class OpenGLFragment: Fragment()
{
    private lateinit var glView: GLSurfaceView

    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
                              savedInstanceState: Bundle?): View?
    {
        this.glView = MySurfaceView(this.activity)
        return this.glView
    }
}

MainActivity

class MainActivity : FragmentActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        val fm = supportFragmentManager
        for (i in 0 until fm.getBackStackEntryCount()) {
            fm.popBackStack()
        }

        supportFragmentManager.beginTransaction().add(R.id.main_container, OpenGLFragment())
                    .addToBackStack(null).commit()
    }

}

Upvotes: 5

Views: 670

Answers (2)

kroegerama
kroegerama

Reputation: 891

There are a few things that you should check:

  • You are only allowed to call setRenderer once in the lifecycle of the GLSurfaceView. See GLSurfaceView.setRenderer.
  • Are you calling glView.onPause() and glView.onResume() to stop rendering, if the view is in background? See GLSurfaceView life-cycle.
  • You should use transaction.replace() instead of transaction.add(). You may end up having multiple OpenGlFragments active at the same time.

Upvotes: 2

Kostas Drak
Kostas Drak

Reputation: 3260

It seems that you are trying to add fragment instead of replacing the container.

Here is my code:

supportFragmentManager.beginTransaction()
            .replace(R.id.main_container, GLFragment())
            .addToBackStack(null)
            .commit()

Fragment(using your code does not compile with this.activity)

class GLFragment : Fragment() {

    private lateinit var glView: GLSurfaceView

    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
        this.glView = MySurfaceView(requireActivity())
        return this.glView
    }
}

GLView:

class MySurfaceView constructor(context: Context) : GLSurfaceView(context) {
    init {
        setEGLContextClientVersion(2) // I have used 2 because running on emulator
        preserveEGLContextOnPause = true
        setRenderer(ClearRenderer())
    }
}

internal class ClearRenderer : GLSurfaceView.Renderer {
    override fun onSurfaceCreated(gl: GL10?, config: EGLConfig?) {
    }

    override fun onSurfaceChanged(gl: GL10, w: Int, h: Int) {
        gl.glViewport(0, 0, w, h)
    }

    override fun onDrawFrame(gl: GL10) {
        gl.glClear(GL10.GL_COLOR_BUFFER_BIT or GL10.GL_DEPTH_BUFFER_BIT)
    }
}

and here is profiler screenshot:

enter image description here

So to recapitulate replace the add with replace, replace the this.activity with requireActivity() the requireActivity() also does not return null and it is guaranteed.

Hope it helps!!!

Upvotes: 3

Related Questions