How can I use Multiple GLSurfaceView components in the same Layout?

10,715

Solution 1

[UPDATE: This answer is no longer correct, as of Android 5.0 (Lollipop). See fadden's answer for a discussion, and links. It was also incorrect as of Android 2.0, and apparently was only an issue for OVERLAPPING surfaces even before then.]

You cannot place 2 SurfaceViews(SV) into one Activity. For understand why you should know how SVs works.

When you create it and place on activity it doesnt actually will be placed in activity (or top of it), instead it will be created behind of current activity with "transparent" view created in that activity.

In Android 4.0 (API 14) there are new View called TextureView There are no way to create something like that View on older platforms.

Solution 2

What's the implementation for CoordinateSystemRenderer?

I met the same requirement today and tried, it actually works that means, you can put 2 GLSurfaceView into same activity.

Something need to be noticed,

  1. In GLRender, when onSurfaceChanged is invoked, you must resize your viewport
  2. With 2 GLSurfaceView, the render thread will be 2, so synchronize issue will be occurred. It depends on your implementation of onDrawFrame.

There is a quick test to use Android API demo in SDK GLSurfaceViewActivity

/*
 * Copyright (C) 2007 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.example.android.apis.graphics;

import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;

import android.opengl.GLSurfaceView;

/**
 * Render a pair of tumbling cubes.
 */

public class CubeRenderer implements GLSurfaceView.Renderer
{
    boolean isReverse = false;

    public CubeRenderer(boolean useTranslucentBackground, boolean isReverse)
    {
        mTranslucentBackground = useTranslucentBackground;
        mCube = new Cube();
        this.isReverse = isReverse;
    }

    public CubeRenderer(boolean useTranslucentBackground)
    {
        this(useTranslucentBackground, false);
    }

    public void onDrawFrame(GL10 gl)
    {
        /*
         * Usually, the first thing one might want to do is to clear the screen. The most efficient way of doing this is
         * to use glClear().
         */

        gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);

        /*
         * Now we're ready to draw some 3D objects
         */

        gl.glMatrixMode(GL10.GL_MODELVIEW);
        gl.glLoadIdentity();
        gl.glTranslatef(0, 0, -3.0f);
        gl.glRotatef(mAngle, 0, 1, 0);
        gl.glRotatef(mAngle * 0.25f, 1, 0, 0);

        gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
        gl.glEnableClientState(GL10.GL_COLOR_ARRAY);

        mCube.draw(gl);

        gl.glRotatef(mAngle * 2.0f, 0, 1, 1);
        gl.glTranslatef(0.5f, 0.5f, 0.5f);

        mCube.draw(gl);

        if (isReverse)
        {
            mAngle -= 1.2f;
        }
        else
        {
            mAngle += 1.2f;
        }
    }

    public void onSurfaceChanged(GL10 gl, int width, int height)
    {
        System.out.println("Joey's Log width : " + width + " height : " + height);
        gl.glViewport(0, 0, width, height);

        /*
         * Set our projection matrix. This doesn't have to be done each time we draw, but usually a new projection needs
         * to be set when the viewport is resized.
         */

        float ratio = (float) width / height;
        gl.glMatrixMode(GL10.GL_PROJECTION);
        gl.glLoadIdentity();
        gl.glFrustumf(-ratio, ratio, -1, 1, 1, 10);
    }

    public void onSurfaceCreated(GL10 gl, EGLConfig config)
    {
        /*
         * By default, OpenGL enables features that improve quality but reduce performance. One might want to tweak that
         * especially on software renderer.
         */
        gl.glDisable(GL10.GL_DITHER);

        /*
         * Some one-time OpenGL initialization can be made here probably based on features of this particular context
         */
        gl.glHint(GL10.GL_PERSPECTIVE_CORRECTION_HINT, GL10.GL_FASTEST);

        if (mTranslucentBackground)
        {
            gl.glClearColor(0, 0, 0, 0);
        }
        else
        {
            gl.glClearColor(1, 1, 1, 1);
        }
        gl.glEnable(GL10.GL_CULL_FACE);
        gl.glShadeModel(GL10.GL_SMOOTH);
        gl.glEnable(GL10.GL_DEPTH_TEST);
    }

    private boolean mTranslucentBackground;

    private Cube mCube;

    private float mAngle;
}


------------------------------------------------------------------------------------------
/*
 * Copyright (C) 2007 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.example.android.apis.graphics;

import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;

import android.opengl.GLSurfaceView;

/**
 * Render a pair of tumbling cubes.
 */

public class CubeRenderer implements GLSurfaceView.Renderer {
    boolean isReverse = false;
    public CubeRenderer(boolean useTranslucentBackground,boolean isReverse) {
        mTranslucentBackground = useTranslucentBackground;
        mCube = new Cube();
        this.isReverse = isReverse;
    }

    public CubeRenderer(boolean useTranslucentBackground)
    {
        this(useTranslucentBackground, false);
    }

    public void onDrawFrame(GL10 gl) {
        /*
         * Usually, the first thing one might want to do is to clear
         * the screen. The most efficient way of doing this is to use
         * glClear().
         */

        gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);

        /*
         * Now we're ready to draw some 3D objects
         */

        gl.glMatrixMode(GL10.GL_MODELVIEW);
        gl.glLoadIdentity();
        gl.glTranslatef(0, 0, -3.0f);
        gl.glRotatef(mAngle,        0, 1, 0);
        gl.glRotatef(mAngle*0.25f,  1, 0, 0);

        gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
        gl.glEnableClientState(GL10.GL_COLOR_ARRAY);

        mCube.draw(gl);

        gl.glRotatef(mAngle*2.0f, 0, 1, 1);
        gl.glTranslatef(0.5f, 0.5f, 0.5f);

        mCube.draw(gl);

        if (isReverse)
        {
            mAngle -= 1.2f;
        }
        else
        {
            mAngle += 1.2f;
        }
    }

    public void onSurfaceChanged(GL10 gl, int width, int height) {
        System.out.println("Joey's Log width : " + width + " height : " + height);
         gl.glViewport(0, 0, width, height);

         /*
          * Set our projection matrix. This doesn't have to be done
          * each time we draw, but usually a new projection needs to
          * be set when the viewport is resized.
          */

         float ratio = (float) width / height;
         gl.glMatrixMode(GL10.GL_PROJECTION);
         gl.glLoadIdentity();
         gl.glFrustumf(-ratio, ratio, -1, 1, 1, 10);
    }

    public void onSurfaceCreated(GL10 gl, EGLConfig config) {
        /*
         * By default, OpenGL enables features that improve quality
         * but reduce performance. One might want to tweak that
         * especially on software renderer.
         */
        gl.glDisable(GL10.GL_DITHER);

        /*
         * Some one-time OpenGL initialization can be made here
         * probably based on features of this particular context
         */
         gl.glHint(GL10.GL_PERSPECTIVE_CORRECTION_HINT,
                 GL10.GL_FASTEST);

         if (mTranslucentBackground) {
             gl.glClearColor(0,0,0,0);
         } else {
             gl.glClearColor(1,1,1,1);
         }
         gl.glEnable(GL10.GL_CULL_FACE);
         gl.glShadeModel(GL10.GL_SMOOTH);
         gl.glEnable(GL10.GL_DEPTH_TEST);
    }
    private boolean mTranslucentBackground;
    private Cube mCube;
    private float mAngle;
}
Share:
10,715

Related videos on Youtube

Rufio
Author by

Rufio

Updated on January 05, 2020

Comments

  • Rufio
    Rufio over 4 years

    I'm writing an Information Visualization API for Android and ran into a problem trying to place two units of a custom GLSurfaceView into a Layout. The Custom GLSurfaceView at this point is simply an extension of the GLSurfaceView to eliminate possible faults caused by custom methods.

    When I have both components added in layout and start the application it runs. But nothing is drawn, seems like it enters an infinite loop. because debug messages inside the Renderers are printed into the LogCat. However, It works perfectly fine if I only use one of the custom GLSurfaceView components.

    I read that there is a problem using GLSurfaceView in multiple Activities and I suppose it also applies when using two of those components at the same time. I have tried the workaround posted here but cant seem to get it to work either.

    I would appreciate any help. I choose to use openGL for the better performance, but if I cant use multiple components at once I guess I'll have to use Canvas instead.

    The manifest looks as follows:

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:orientation="vertical" android:layout_width="fill_parent"
        android:layout_height="fill_parent">
        <TextView android:text="@string/hello" android:id="@+id/TextView01"
            android:layout_width="wrap_content"
                android:layout_height="wrap_content" />
    
        <com.syntronic.vtadlib.VisualizationView android:id="@+id/glview"
            android:layout_width="fill_parent" android:layout_height="300px" />
    
    
        <TextView android:text="@string/hello" android:id="@+id/TextView02"
            android:layout_width="wrap_content"
                android:layout_height="wrap_content" />
    
        <LinearLayout 
            android:orientation="vertical" android:layout_width="fill_parent"
            android:layout_height="fill_parent">
    
            <com.syntronic.vtadlib.VisualizationView android:id="@+id/glview2"
                android:layout_width="fill_parent" android:layout_height="fill_parent" />
    
        </LinearLayout>
    
    </LinearLayout>
    

    From the Activity the code is like this:

    @Override
    public void onCreate(Bundle savedInstanceState) {
    
        super.onCreate(savedInstanceState);
    
        setContentView(R.layout.main);
    
        mSurfaceView = (VisualizationView) findViewById(R.id.glview);
        mSurfaceView2 = (VisualizationView) findViewById(R.id.glview2);
    
        //Enables debug flags for Errors
        //mSurfaceView.setDebugFlags(GLSurfaceView.DEBUG_CHECK_GL_ERROR);   
        //mSurfaceView2.setDebugFlags(GLSurfaceView.DEBUG_CHECK_GL_ERROR);  
    
        mSurfaceView.setRenderer(new CoordinateSystemRenderer());
        mSurfaceView2.setRenderer(new CoordinateSystemRenderer());
    
    }
    
    @Override
    protected void onPause() {
        // TODO Auto-generated method stub
        super.onPause();
        mSurfaceView.onPause();
        mSurfaceView2.onPause();
    }
    
    @Override
    protected void onResume() {
        // TODO Auto-generated method stub
        super.onResume();
        mSurfaceView.onResume();
        mSurfaceView2.onResume();
    }
    

    Am I missing something obvious? Or can someone explain why it doesn't work?

    • David Merriman
      David Merriman over 12 years
      The first thing that jumps out at me is that your nested LinearLayout is set to fill_parent. I would check to make sure that both of your GLSurfaceViews are being laid out with non-zero height. It may be the case that in they are both drawing, but one has a height of 0 pixels and is therefore invisible.
    • ToolmakerSteve
      ToolmakerSteve over 7 years
      Instead of two GLSurfaceViews, may be better to create/manage a GL thread and context yourself, applying that to two SurfaceViews (NOT GLSurfaceViews). See fadden's discussion and link to Grafika.
  • WarrenFaith
    WarrenFaith about 12 years
    Are you sure of that? I only thought that the limitation is, that you can't place them above each other or overlapping. Being said you cant show a videoview over an surfaceview as videoview extends the surfaceview...
  • pepyakin
    pepyakin about 12 years
    Yes, im sure. You can use only one instance of SV at time.
  • WarrenFaith
    WarrenFaith about 12 years
    Could you provide a source if you have one? I am very interested in the complete SV topic. Thanks!
  • pepyakin
    pepyakin about 12 years
  • ToolmakerSteve
    ToolmakerSteve over 7 years
    This answer is no longer correct. See fadden's answer.
  • ToolmakerSteve
    ToolmakerSteve over 7 years
    How is this code relevant? It doesn't demonstrate two GLSurfaceViews. It just shows a single view, drawing two cubes.
  • ToolmakerSteve
    ToolmakerSteve over 7 years
    The OP TRIED to use two GLSurfaceViews, and is having a problem getting it to work. How does this "answer" help?
  • ToolmakerSteve
    ToolmakerSteve over 7 years
    Please explain - how is this "an alternative way of doing it"? The "it" discussed by OP is rendering GL to two different views within a layout. What are you proposing as an alternative solution?