Take the 2-minute tour ×
Game Development Stack Exchange is a question and answer site for professional and independent game developers. It's 100% free, no registration required.

I'm making a 2D game with OpenGL ES 2.0 on Android, as an excuse to learn a bunch of things. I'm drawing many lines to make a grid, but setting everything up takes time and I'd like to render a loading screen while I set everything up in the background.

While trying to tackle this issue, I'm facing a problem that I can't understand so my question is why am I seeing this behavior.

The code:

This is the class whose instances will represent each line:

This is just a slightly modified example I found on the web. There's a call to Thread.sleep that's only meant to increase the time it takes to create it, so that I can show the problem I'm facing.

import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;

import android.opengl.GLES20;

public class LineExample {

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

    private final String fragmentShaderCode =
                "precision mediump float;" +
                "void main() {" +
                "  gl_FragColor = vec4(0.0, 0.0, 1.0, 1.0);" +
                "}";

    private int mProgram;
    private FloatBuffer vertexBuffer;
    private ByteBuffer bb;



    // number of coordinates per vertex in this array
    static final int COORDS_PER_VERTEX = 2;
    private final int vertexStride = COORDS_PER_VERTEX * 4; // bytes per vertex
    private float lineCoords[] = new float[2*COORDS_PER_VERTEX];
    private int vertexCount = lineCoords.length / COORDS_PER_VERTEX;

    public LineExample(float[] lineCoords) {

        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

        this.lineCoords = lineCoords;

        // initialize vertex byte buffer for shape coordinates
        bb = ByteBuffer.allocateDirect(
        // (# of coordinate values * 4 bytes per float)
                lineCoords.length * 4);
        bb.order(ByteOrder.nativeOrder());
        vertexBuffer = bb.asFloatBuffer();
        vertexBuffer.put(lineCoords);
        vertexBuffer.position(0);

        // prepare shaders and OpenGL program
        int vertexShader = loadShader(GLES20.GL_VERTEX_SHADER,
                                                   vertexShaderCode);
        int fragmentShader = 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() {

        // Add program to OpenGL environment
        GLES20.glUseProgram(mProgram);

        // get handle to vertex shader's vPosition member
        int 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);

        // Draw
        GLES20.glDrawArrays(GLES20.GL_LINES, 0, vertexCount);

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

    private static int loadShader(int type, String shaderCode){

        // create a vertex shader type (GLES20.GL_VERTEX_SHADER)
        // or a fragment shader type (GLES20.GL_FRAGMENT_SHADER)
        int shader = GLES20.glCreateShader(type);

        // add the source code to the shader and compile it
        GLES20.glShaderSource(shader, shaderCode);
        GLES20.glCompileShader(shader);

        return shader;
    }

}

Here's the renderer:

public class MyRenderer2 implements Renderer {

    LineExample currentLineToRender = null;

    @Override
    public void onDrawFrame(GL10 unused) {
        GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
        if (currentLineToRender!=null){
            currentLineToRender.draw();
        }
    }

    @Override
    public void onSurfaceCreated(GL10 gl, EGLConfig config) {
        //initializeLines();
        initializeLinesSameThread();
    }

    @Override
    public void onSurfaceChanged(GL10 gl, int width, int height) {
        GLES20.glViewport(0, 0, width, height);
        GLES20.glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
    }

    private void initializeLines(){
        float[] waitingLineVerts = {-0.5f,0.0f , 0.5f,0.0f};
        currentLineToRender = new LineExample(waitingLineVerts);
        new Thread(){
            public void run() {
                float[] doneLineVerts = {0.0f,-0.5f , 0.0f,0.5f};
                currentLineToRender = new LineExample(doneLineVerts);
            }
        }.start();
    }

    private void initializeLinesSameThread(){
        float[] waitingLineVerts = {-0.5f,0.0f , 0.5f,0.0f};
        currentLineToRender = new LineExample(waitingLineVerts);
        float[] doneLineVerts = {0.0f,-0.5f , 0.0f,0.5f};
        currentLineToRender = new LineExample(doneLineVerts);
    }

}

The goal is to see the an horizontal line for 5 seconds (analogous to a loading screen) then see a vertical line (analogous to the actual game grid). However, with this code I get a dark screen for ~10 seconds and then the second line is drawn.

I figured that the thread is busy with the initialization code so it's not drawing the first line, so to allow it to get to the onDrawFrame method and draw the first line, I decided to call the initialization of the second line on another thread (by calling initializeLines() instead of initializeLinesSameThread() on onSurfaceCreated), but by doing that, I only see the first line and I never get to see the second.

My guess is that some things that are done in the other thread aren't accessible by the renderer thread when the line's draw method is called.

My question is why is this happening?

(and I'd be grateful if you could give me a suggestion on how to achieve what I want to do)

share|improve this question
1  
You seemed to have pointed out yourself that the first line is not being drawn on the renderer thread. I'm not exactly an android genius, but my guess is that android is thread safe and so something done on one thread like that will not be shared across the threads. –  Polar Mar 18 '13 at 23:16

2 Answers 2

If you don't care that the loading screen is not drawn in OpenGL, you can just make a plain View which displays the image corresponding to your loading screen, display that view and then start the view that holds your OpenGL surface. Once that view is loaded, hide the loading screen view.

share|improve this answer
up vote 1 down vote accepted

It turns out you can't use GLES20 static methods outside of the renderer's thread. Even though the lines are being "drawn" in the renderer thread, I was using OpenGL calls to prepare their programs in their initialization, which was taking place in another thread. Those calls seem to always return 0, that's why the line wouldn't be drawn later, even if drawn in the renderer thread.

I've had success using the renderer's onDrawFrame method to initialize some assets if needed and show the loading screen at the same time but Shivan Dragon's suggestion is probably much more reasonable for most cases.

share|improve this answer
    
Actually, you've discovered something which I haven't manage to find.I initially wanted to have my loading screen drawn by OpenGL as well,but failing to find a way to only partially load resources,I've switched to the "non-open-gl-loading-screen" variant which I've described in my answer.I've actually managed to make this work.The downside is basically that of not using OpenGL:resizing of images is more difficult(and actually it's best to have separate images for each targeted resolution),loading screen animations are slower(and possibly harder to code),etc.For a simple loading screen it's ok. –  Shivan Dragon Mar 20 '13 at 12:47

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.