6

I am developing an android application in opengl ES2.0.In this Application I used to draw multiple lines and circles by touch event in GL surfaceView.

As opengl depends on GPU, Currently it works fine in Google Nexus 7(ULP GeForce).

In Samsung Galaxy Note 2(MALI 400MP) I'm trying to draw more than one line, but it clears the previous line and draw current line as new.

In Sony Xperia Neo V(Adreno 205) I'm trying to draw a new line, it crashes the surface as shown in below image.enter image description here

Is it possible to make it work on all devices or do I need to write code for Individual GPU?


Source code

MainActivity.java

//in OnCreate method of my activity, i set the glsurfaceview and renderer

final ActivityManager activityManager =
    ( ActivityManager ) getSystemService( Context.ACTIVITY_SERVICE );
final ConfigurationInfo configurationInfo =
    activityManager.getDeviceConfigurationInfo(  );
final boolean supportsEs2 = ( configurationInfo.reqGlEsVersion >= 0x20000
                  || Build.FINGERPRINT.startsWith( "generic" ) );

if( supportsEs2 ) {
    Log.i( "JO", "configurationInfo.reqGlEsVersion:"
           + configurationInfo.reqGlEsVersion + "supportsEs2:"
           + supportsEs2 );
// Request an OpenGL ES 2.0 compatible context.
    myGlsurfaceView.setEGLContextClientVersion( 2 );

    final DisplayMetrics displayMetrics = new DisplayMetrics(  );
    getWindowManager(  ).getDefaultDisplay(  ).getMetrics( displayMetrics );

// Set the renderer to our demo renderer, defined below.
    myRenderer = new MyRenderer( this, myGlsurfaceView );
    myGlsurfaceView.setRenderer( myRenderer, displayMetrics.density );
    myGlsurfaceView.setRenderMode( GLSurfaceView.RENDERMODE_CONTINUOUSLY );

    MyGLSurfaceView.java
//in this im getting the coordinates of my touch on the glSurfaceView to draw the line and //passing those points to the renderer class
        public MyGLsurfaceview( Context context ) {
        super( context );
        Log.i( "JO", "MyGLsurfaceview1" );

    }

    public MyGLsurfaceview(
    Context context,
    AttributeSet attrs )
    {
        super( context, attrs );
        con = context;
        mActivity = new MainActivity(  );
        mActivity.myGlsurfaceView = this;
        Log.i( "JO", "MyGLsurfaceview2" );
    }

    public void setRenderer(
    MyRenderer renderer,
    float density )
    {
        Log.i( "JO", "setRenderer" );
        myRenderer = renderer;
        myDensity = density;
        mGestureDetector = new GestureDetector( con, mGestureListener );
        super.setRenderer( renderer );
        setRenderMode( GLSurfaceView.RENDERMODE_CONTINUOUSLY );

    }
    @Override public boolean onTouchEvent( MotionEvent ev ) {

        boolean retVal = mGestureDetector.onTouchEvent( ev );

        if( myline ) {

            switch ( ev.getAction(  ) ) {

            case MotionEvent.ACTION_DOWN:

                isLUp = false;

                if( count == 1 ) {
                    dx = ev.getX(  );
                    dy = ev.getY(  );
                    dx = ( dx / ( getWidth(  ) / 2 ) ) - 1;
                    dy = 1 - ( dy / ( getHeight(  ) / 2 ) );

                    firstX = dx;
                    firstY = dy;
                } else if( count == 2 ) {

                    ux = ev.getX(  );
                    uy = ev.getY(  );
                    ux = ( ux / ( getWidth(  ) / 2 ) ) - 1;
                    uy = 1 - ( uy / ( getHeight(  ) / 2 ) );

                    secondX = ux;
                    secondY = uy;

                    myRenderer.dx = firstX;
                    myRenderer.dy = firstY;
                    myRenderer.ux = secondX;
                    myRenderer.uy = secondY;

                    midX = ( firstX + secondX ) / 2;
                    midY = ( firstY + secondY ) / 2;
                    Log.e( "JO",
                           "Line:firstX" + firstX +
                           "firstY" + firstY );
                    lp = new LinePoints( firstX, firstY,
                                 secondX, secondY,
                                 midX, midY );
                    lineArray.add( lp );

                    myRenderer.isNewClick = false;
                    myRenderer.isEnteredAngle = false;
                    myRenderer.myline = true;
                    myRenderer.mycircle = false;
                    myRenderer.mydashedline = false;
                    myRenderer.eraseCircle = false;
                    myRenderer.eraseLine = false;
                    myRenderer.eraseSelCir = false;
                    myRenderer.angle = angle;
                    myRenderer.length = length;
                    requestRender(  );
                    count = 0;

                }
                count++;

                break;
            case MotionEvent.ACTION_MOVE:

                isLUp = true;

                break;

            case MotionEvent.ACTION_UP:

                if( isLUp ) {

                    ux = ev.getX(  );
                    uy = ev.getY(  );
                    ux = ( ux / ( getWidth(  ) / 2 ) ) - 1;
                    uy = 1 - ( uy / ( getHeight(  ) / 2 ) );
                    Log.i( "JO", "line2:" + ux + "," + uy );

                    secondX = ux;
                    secondY = uy;
                    myRenderer.dx = firstX;
                    myRenderer.dy = firstY;
                    myRenderer.ux = secondX;
                    myRenderer.uy = secondY;

                    midX = ( firstX + secondX ) / 2;
                    midY = ( firstY + secondY ) / 2;
                    Log.e( "JO",
                           "Line:firstX" + firstX +
                           "firstY" + firstY );
                    lp = new LinePoints( firstX, firstY,
                                 secondX, secondY,
                                 midX, midY );
                    lineArray.add( lp );

                    myRenderer.isNewClick = false;
                    myRenderer.isEnteredAngle = false;
                    myRenderer.myline = true;
                    myRenderer.mycircle = false;
                    myRenderer.mydashedline = false;
                    myRenderer.mysnaptoedge = false;
                    myRenderer.mysnaptoMiddle = false;
                    myRenderer.eraseCircle = false;
                    myRenderer.eraseLine = false;
                    myRenderer.eraseSelCir = false;
                    count = 1;
                    requestRender(  );
                }

                break;

            }
        }
    }
}

MyRenderer.java

//renderer class to render the line to the glsurfaceview
Lines line;
public MyRenderer(
    MainActivity mainActivity,
    MyGLsurfaceview myGlsurfaceView )
{
    Log.i( "JO", "MyRenderer" );
    this.main = mainActivity;
    myGlsurface = myGlsurfaceView;

}

public void onDrawFrame(
    GL10 gl )
{
    line.draw( dx, dy, ux, uy );
}

@Override public void onSurfaceCreated(
    GL10 gl,
    EGLConfig config )
{
    Log.i( "JO", "onSurfaceCreated" );
// Set the background frame color
    GLES20.glClearColor( 0.0f, 0.0f, 0.0f, 1.0f );
// Create the GLText
    glText = new GLText( main.getAssets(  ) );

// Load the font from file (set size + padding), creates the texture
// NOTE: after a successful call to this the font is ready for
// rendering!
    glText.load( "Roboto-Regular.ttf", 14, 2, 2 );  // Create Font (Height: 14
// Pixels / X+Y Padding
// 2 Pixels)
// enable texture + alpha blending
    GLES20.glEnable( GLES20.GL_BLEND );
    GLES20.glBlendFunc( GLES20.GL_ONE, GLES20.GL_ONE_MINUS_SRC_ALPHA );
}

@Override public void onSurfaceChanged(
    GL10 gl,
    int width,
    int height )
{
// Adjust the viewport based on geometry changes,
// such as screen rotation
    GLES20.glViewport( 0, 0, width, height );

    ratio = ( float ) width / height;

    width_surface = width;
    height_surface = height;

/*
* // this projection matrix is applied to object coordinates // in the
* onDrawFrame() method Matrix.frustumM(mProjMatrix, 0, -ratio, ratio,
* -1, 1, 3, 7);
*/
// Take into account device orientation
    if( width > height ) {
        Matrix.frustumM( mProjMatrix, 0, -ratio, ratio, -1, 1, 1, 10 );
    } else {
        Matrix.frustumM( mProjMatrix, 0, -1, 1, -1 / ratio, 1 / ratio,
                 1, 10 );
    }

// Save width and height
    this.width = width; // Save Current Width
    this.height = height;   // Save Current Height

    int useForOrtho = Math.min( width, height );

// TODO: Is this wrong?
    Matrix.orthoM( mVMatrix, 0, -useForOrtho / 2, useForOrtho / 2,
               -useForOrtho / 2, useForOrtho / 2, 0.1f, 100f );
}

Line.java

//Line class to draw line

public class Lines
{

    final String vertexShaderCode = "attribute vec4 vPosition;"
        + "void main() {" + " gl_Position = vPosition;" + "}";

    final String fragmentShaderCode = "precision mediump float;"
        + "uniform vec4 vColor;" + "void main() {"
        + " gl_FragColor = vColor;" + "}";

    final FloatBuffer vertexBuffer;
    final int mProgram;
    int mPositionHandle;
    int mColorHandle;

// number of coordinates per vertex in this array
    final int COORDS_PER_VERTEX = 3;
    float lineCoords[] = new float[6];
    final int vertexCount = lineCoords.length / COORDS_PER_VERTEX;
    final int vertexStride = COORDS_PER_VERTEX * 4; // bytes per vertex
// Set color with red, green, blue and alpha (opacity) values
    float lcolor[] = { 1.0f, 1.0f, 1.0f, 1.0f };

    public Lines(
         )
    {

// initialize vertex byte buffer for shape coordinates
        ByteBuffer bb = ByteBuffer.allocateDirect(
// (number of coordinate values * 4 bytes per float)
                                  lineCoords.
                                  length * 4 );
// use the device hardware's native byte order
        bb.order( ByteOrder.nativeOrder(  ) );

// create a floating point buffer from the ByteBuffer
        vertexBuffer = bb.asFloatBuffer(  );

// prepare shaders and OpenGL program
        int vertexShader =
            MyRenderer.loadShader( GLES20.GL_VERTEX_SHADER,
                           vertexShaderCode );
        int fragmentShader =
            MyRenderer.loadShader( GLES20.GL_FRAGMENT_SHADER,
                           fragmentShaderCode );

        mProgram = GLES20.glCreateProgram(  );  // create empty OpenGL Program
        GLES20.glAttachShader( mProgram, vertexShader );    // add the vertex shader
// to program
        GLES20.glAttachShader( mProgram, fragmentShader );  // add the fragment
// shader to program
        GLES20.glLinkProgram( mProgram );   // create OpenGL program executables
    }

    public void draw(
    float dX,
    float dY,
    float uX,
    float uY )
    {

        lineCoords[0] = dX;
        lineCoords[1] = dY;
        lineCoords[2] = 0.0f;
        lineCoords[3] = uX;
        lineCoords[4] = uY;
        lineCoords[5] = 0.0f;
        Log.i( "JO",
               "lineCoords:" + lineCoords[0] + "," + lineCoords[1] +
               "," + lineCoords[3] + "," + lineCoords[4] );

        vertexBuffer.put( lineCoords );
        vertexBuffer.position( 0 );
// Add program to OpenGL environment
        GLES20.glUseProgram( mProgram );

// get handle to vertex shader's vPosition member
        mPositionHandle =
            GLES20.glGetAttribLocation( mProgram, "vPosition" );

// Enable a handle to the triangle vertices
        GLES20.glEnableVertexAttribArray( mPositionHandle );

// Prepare the triangle coordinate data
        GLES20.glVertexAttribPointer( mPositionHandle,
                          COORDS_PER_VERTEX,
                          GLES20.GL_FLOAT, false,
                          vertexStride, vertexBuffer );

// get handle to fragment shader's vColor member
        mColorHandle =
            GLES20.glGetUniformLocation( mProgram, "vColor" );

// Set color for drawing the triangle
        GLES20.glUniform4fv( mColorHandle, 1, lcolor, 0 );
        GLES20.glLineWidth( 3 );
// Draw the triangle
        GLES20.glDrawArrays( GLES20.GL_LINES, 0, vertexCount );

// Disable vertex array
        GLES20.glDisableVertexAttribArray( mPositionHandle );
    }

}
Community
  • 1
  • 1
deiva
  • 127
  • 6
  • 2
    How about you show us some of your drawing code? The idea of OpenGL(-ES) is to be device **independent**, so you either found several driver bugs, or you're doing something very wrong. – datenwolf Jun 21 '13 at 08:05
  • 1
    You can see my code [here](http://tuxbalaji.wordpress.com/2013/06/19/draw-a-line-using-opengles2-0-by-touch-event-in-android/) – deiva Jun 21 '13 at 08:46
  • 1
    How about putting the relevant code in the question so it will still be around if the external link dies. – Grimmy Jun 21 '13 at 10:35
  • How about you show us some of your drawing code? The idea of OpenGL(-ES) is to be device independent, so you either found several driver bugs, or you're doing something very wrong. -- @datenwolf This is not correct. cf my answer – rcbevans Jul 16 '13 at 14:21

5 Answers5

4

Okay, here it goes again: ^1

OpenGL is not a scene graph. OpenGL does not maintain a scene, knows about objects or keeps tracks of geometry. OpenGL is a drawing API. You give it a canvas (in form of a Window or a PBuffer) and order it to draw points, lines or triangles and OpenGL does exactly that. Once a primitive (=point, line, triangle) has been drawn, OpenGL has no recollection about it whatsoever. If something changes, you have to redraw the whole thing.

The proper steps to redraw a scene are:

  1. Disable the stencil test, so that the following step operates on the whole window.

  2. Clear the framebuffer using glClear(bits), where bits is a bitmask specifying which parts of the canvas to clear. When rendering a new frame you want to clear everything so bits = GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT;

  3. set the viewport, build an apropriate projection matrix

  4. for each object in the scene load the right modelview matrix, set uniforms, select the vertex arrays and make the drawing call.

  5. finish the rendering by flushing the pipeline. If using a single buffered window glFinish(), if using a double buffered window call SwapBuffers. In case of higher level frameworks this may be performed by the framework.

Important Once the drawing has been finished on a double buffered window, you must not continue to send drawing operations, as by performing the buffer swap the contents of the back buffer you're drawing to are undefined. Hence you must start the drawing anew, beginning with clearing the framebuffer (steps 1 and 2).

What your code misses are exactly those two steps. Also I have the impression that you're performing OpenGL drawing calls in direct reaction to input events, possibly in the input event handlers themself. Don't do this!. Instead use the input events to add to a list of primitives (lines in your case) to draw, then send a redraw event, which makes the framework call the drawing function. In the drawing function iterate over that list to draw the desired lines.

Redrawing the whole scene is canonical in OpenGL!


[1] (geesh, I'm getting tired of having to write this every 3rd question or so…)

datenwolf
  • 159,371
  • 13
  • 185
  • 298
  • This works fine to draw single line in all types of GPU mentioned above.But this case not works for me to draw multiple lines. It would be fine if you refer some links or give me some codes to get a clear understanding of this. – deiva Jun 24 '13 at 10:19
  • Take a look on these tutorials: http://nehe.gamedev.net/ It is C and not Java, but you should be able to understand. – Adrian Maire Jun 28 '13 at 09:55
  • It is not neccesarry to redraw the whole scene if the EGL surface is configured correctly and you don't want to, cf my answer – rcbevans Jul 16 '13 at 14:19
3

Taking a punt here, but are you ever actually clearing the screen? The kinds of behaviour you are seeing suggest that you are not, and that in different scenaries you are seeing different errors - uninitialised memory, reusing an old buffer, implicitly clearing, etc.

GL requires you to be specific about what you want, so you need to explicitly clear.

JasonD
  • 16,464
  • 2
  • 29
  • 44
  • The issue is that GL DOESN'T require you to be specific. Because of what the API defines, it allows flexibility in the low level implementation, causing the issues defined, cf my answer – rcbevans Jul 16 '13 at 14:20
1

OpenGL is just a standard. The actual implementation of the API is up to the graphics card manufacturer. So yes, OpenGL development can be GPU dependant sometimes. However, all implementations should provide the same result (what happens behind the scenes can be really different). If your code gives a different result with different GPUs, there is probably a version difference in the OpenGL implementation.

You can use these functions to get the supported OpenGL version:

glGetIntegerv​(GL_MAJOR_VERSION​, *); //version 3.0+
glGetIntegerv​(GL_MINOR_VERSION​, *); //version 3.0+
glGetString​(GL_VERSION​); //all versions
Kevin
  • 2,739
  • 33
  • 57
  • 1
    Please have a look at my code [here](http://tuxbalaji.wordpress.com/2013/06/19/draw-a-line-using-opengles2-0-by-touch-event-in-android/). – deiva Jun 21 '13 at 08:53
  • @deiva I never used OpenGL-ES, only C. I also didn't use shaders much, so I really wouldn't know if there is something wrong with that code, sorry. I took a quick look at and it seems alright, but don't take my word on this. – Kevin Jun 21 '13 at 09:51
  • In particular - an area that has bit me in the butt a few times was the optimization of shader code. Some vendor's drivers would look at a variable, decide it wasn't used (change the output state) and optimize it away when in fact it was being used. You can expect that this will come up (even on different cards from the same vendor). – paulczak Jun 25 '13 at 20:53
  • No this is not correct. Two GPUs supporting the same EGL API can yield different visual results. The Spec does not define end results. See my answer – rcbevans Jul 16 '13 at 14:13
  • 1
    @o0rebelious0o Opengl is a standard, and that will never change. It's really up to the GPU manufacturer to implement that standard. If two different GPU's produce different results with the same code, one of them (or both) uses a driver that is implementing the standard wrong. This can be a difference in the driver, or a difference in how the hardware executes the code, but either way, it's the GPU manufacturer's fault. – Kevin Jul 16 '13 at 14:25
  • No, I'm sorry, I completely disagree. I cannot say too much but as someone heavily involved with this particular topic, I can assure you that neither driver is doing anything "wrong". The spec is left open so that GPU implementations can optimise their default config to target their strengths. Mali is Tile based and so trying to be as bandwidth efficient as possible, GeForce is Immediate so is always going to burn through bandwidth, Mali turns off EGL Preserve, Geforce don't. Mali renders what it is told each frame, the forced buffering of past buffers unless cleared is NOT part of the spec – rcbevans Jul 16 '13 at 15:16
  • 1
    To clarify, the end result must be the same given the identical configuration of the EGL environment, i.e if you explicitly state preserve buffer, but it is not guaranteed if config is left ambiguous as the spec permits default values to be assigned freely by the gpu. Neither vendor are doing anything wrong either in the driver or in hardware, it is YOUR fault as a programmer for not being explicit. – rcbevans Jul 16 '13 at 15:21
0
  1. Why don´t you provide one working example, so people actually could help?

  2. From your code: I can´t see where do you create your line? Something like:

    @Override public void onSurfaceCreated(GL10 gl, EGLConfig config){
        ...
        mLine = new Lines();
        ...
    }
    
  3. As others already mentioned, in onDrawFrame always clear the buffer:

    public void onDrawFrame(GL10 gl )
    {
        // Erase CL_COLOR_BUFFER
        GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
    
  4. Set the camera:

    // Set the camera position (View matrix)
    Matrix.setLookAtM(mViewMatrix, 0, 0, 0, 3, 0f, 0f, 0f, 0f, 1.0f, 0.0f);
    //
    // Calculate the projection and view transformation
    Matrix.multiplyMM(mMVPMatrix, 0, mProjMatrix, 0, mViewMatrix, 0);
    
  5. Draw:

    line.draw( dx, dy, ux, uy );
    
escalator
  • 888
  • 9
  • 14
  • hi escalator,here is my complete code to draw a line. http://www.mediafire.com/?09f9q51xnqhq2l7 i need to draw multiple lines using touch event to draw more objects.. And also i need to pan my drawing screen so that i can view or draw more objects.please tell me any solution if u have idea on this.. – deiva Jul 05 '13 at 10:03
0

Crossposted from my answer to a similar question Why my opengl output differs for various devices?:

Should we take into account of GPU while Coding ? No way, The OpenGL API is a layer between your application and the hardware.

This is largely correct for desktop graphics as all GPUs are immediate renderers, however, this is NOT the case in mobile graphics.

The Mali GPUs use tile-based immediate-mode rendering. For this type of rendering, the framebuffer is divided into tiles of size 16 by 16 pixels. The Polygon List Builder (PLB) organizes input data from the application into polygon lists. There is a polygon list for each tile. When a primitive covers part of a tile, an entry, called a polygon list command, is added to the polygon list for the tile. The pixel processor takes the polygon list for one tile and computes values for all pixels in that tile before starting work on the next tile. Because this tile-based approach uses a fast, on-chip tile buffer, the GPU only writes the tile buffer contents to the framebuffer in main memory at the end of each tile. Non-tiled-based, immediate-mode renderers generally require many more framebuffer accesses. The tile-based method therefore consumes less memory bandwidth, and supports operations such as depth testing, blending and anti-aliasing efficiently.

Another difference is the treatment of rendered buffers. Immediate renderers will "save" the content of your buffer, effectively allowing you to only draw differences in the rendered scene on top of what previously existed. This IS available in Mali, however, is not enabled by default as it can cause undesired effects if used incorrectly.

There is a Mali GLES2 SDK example on how to use "EGL Preserve" Correctly available in the GLES2 SDK here

The reason the Geforce ULP based nexus 7 works as intended is that, as an immediate based renderer, it defaults to preserving the buffers, whereas Mali does not.

From the Khronos EGL specification:

EGL_SWAP_BEHAVIOR

Specifies the effect on the color buffer of posting a surface with eglSwapBuffers. A value of EGL_BUFFER_PRESERVED indicates that color buffer contents are unaffected, while EGL_BUFFER_DESTROYED indicates that color buffer contents may be destroyed or changed by the operation.

The initial value of EGL_SWAP_BEHAVIOR is chosen by the implementation.

The default value for EGL_SWAP_BEHAVIOUR on the Mali platform is EGL_BUFFER_DESTROYED. This is due to the performance hit associated with having to fetch the previous buffer from memory before rendering the new frame, and storing it at the end as well as the consumption of bandwidth (which is also incredibly bad for battery life on mobile devices). I am unable to comment with certainty as to the default behavior of the Tegra SoCs however, it is apparent to me that their default is EGL_BUFFER_PRESERVED.

To clarify Mali's position with regards to the Khronos GLES specifications - Mali is fully compliant.

Community
  • 1
  • 1
rcbevans
  • 7,101
  • 4
  • 30
  • 46