OpenGL Shader Compilation Issue -- Unexpected EOF
Asked Answered
V

3

9

So I decided to try writing a simple OpenGL app using Java, just to see how it compared to my other efforts, and I'm running into an issue where my shaders refuse to compile. They really couldn't get much simpler, here's my vertex shader to demonstrate what I mean:

//Minimal vertex shader

#version 330

in vec3 in_Position;
in vec3 in_Color;
out vec3 var_Color;

void main(void)
{
    gl_Position = vec4(in_Position, 1.0);
    var_Color = in_Color;
}

The fragment shader is just as simple, so I won't bother posting it unless someone asks for it. When I check the logs, I get back the following error (for both shaders):

(0) : error C0000: syntax error, unexpected $end at token "<EOF>"

I'm not sure this is relevant... but I'm developing on Linux (Ubuntu 11.04) using Java. The only libraries I'm using are JOGL (for the openGL bindings) and the standard Java library (if that even counts...) My graphics card is a Nvidia GeForce 9600M GS, and I checked the extensions and it has full support for OpenGL 3.3.

Help me Stack Overflow, you're my only hope.

EDIT:

As requested, here is the function that is responsible for loading and compiling the shader source. Also, when it comes to GLSL I'm a super n00b, so I really don't know what to look for as far as making sure things are formatted properly for OpenGL. A link to a recent (i.e. dealing with OpenGL 3.x) tutorial on the subject would be appreciated.

private int CreateCompiledShader(File source, int shader, GL3 gl){
        int shaderloc = gl.glCreateShader(shader);

        BufferedReader input = null;
        ArrayList<String> lines = new ArrayList<String>();
        ArrayList<Integer> lengths = new ArrayList<Integer>();
        try{
            input = new BufferedReader(new FileReader(source));
            String buffer;

            while(input.ready()){
                buffer = input.readLine();
                lines.add(buffer);
                lengths.add(buffer.length());
            }
        }catch(Exception e){
            e.printStackTrace();
        }finally{
            if(input != null){
                try {
                    input.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }

        int[] iLengths = new int[lengths.size()];
        for(int i = 0; i < lengths.size(); i++){
        iLengths[i] = lengths.get(i).intValue();
        }

        gl.glShaderSource(shaderloc, lines.size(), lines.toArray(new String[0]), iLengths, 0);
        gl.glCompileShader(shaderloc);

        int error = gl.glGetError();
        if(error != GL3.GL_NO_ERROR){
            Logger.log(new GLU().gluErrorString(error), Logger.ERROR, "Shader compilation");
        }

        return shaderloc;
    }

As an aside, the if statement towards the end where I check glGetError() isn't actually where the error gets caught, that doesn't happen until execution returns to the calling function and I check the shader logs. Could be relevant, but then again I could also be rambling.

Volscian answered 19/6, 2011 at 6:32 Comment(2)
Try adding empty line after the last bracket, that might help sometimes.Paginal
That was one of the first things I tried, however it didn't show up when I posted the code.Volscian
D
14

OK, now I see the problem. Your loading code doesn't work. But don't worry; a lot of people get confused when they see that glShaderSource takes an array of strings. I'm guessing that you saw someone write their shaders in C/C++ like this:

const char *myShader[] = {
    "#version 330\n",
    "\n",
    "in vec3 in_position;\n",
    ...
};

And they uploaded the shader with, glShaderSource(shader, ARRAY_COUNT(myShader), myShader, NULL, 0);

While this is legal, that's not really what the feature is for. Because GLSL does not have a #include mechanism, glShaderSource can take multiple strings. Each string is intended to be a shader file. The shader compiler then effectively concatenates the strings together like #include does before compiling.

Now, because of this, you can have each line as a separate string. However, look back that that C/C++ code. See what is at the end of each line? That '\n' character.

That's what is not at the end of the lines you are loading. Because I'm pretty sure BufferedReader.readline does not keep the end-of-line character. So your shader looks like:

//Minimal vertex shader#version 330in vec3 in_Position;in vec3 in_Color;out vec3 var_Color;...

Your entire shader is seen as one big single-line comment. Hence the unexpected EOF: OpenGL never saw anything to compile ;)

You shouldn't be reading the file line by line. Just load the whole thing up into a single string. Then pass it to OpenGL. Alternatively, you can look at this previous answer about JOGL; it should show you how to do it correctly (though I would hope that BufferedReader would have some way to read the whole file as a string rather than line-by-line.

Duyne answered 19/6, 2011 at 7:23 Comment(1)
Aha! That makes sense. I've done this project before in C/C++, so when I saw in the javadoc that this function took String[], somehow I assumed that char* == String[] (which obviously isn't correct). Thanks for the helpful answer.Volscian
M
3

Although an answer already has peen provided and accepted, I'll just write here how I'd prefer it:

// Create shader from one or multiple source files
private int CreateCompiledShader(File[] source_files, int shader, GL3 gl){
    int shaderloc = gl.glCreateShader(shader);
    int nSources = source_files.size();

    // the number of shader sources it known from the beginning
    // so we can allocate the arrays right here
    String[] sources = new String[nSources];
    int[] sources_lengths = new int[nSources];
    for(int i = 0; i < nSources; i++) {
        // We don't need a buffered reader as we're reading the file as a whole
        FileReader input = new FileReader(source_file[i]);
        String buffer;

        buffer = input.read();
        sources[i] = buffer;
        sources_lengths[i] = buffer.length();

        if(input != null){
            input.close();
        }
    }

    // Frankly I really don't understand why you have to pass sources_lengths here
    // It would take only 3 LOC in the binding's code
    // to extract that from the sources array, Oh, well...
    gl.glShaderSource(shaderloc, nSources, sources, sources_lengths, 0);
    gl.glCompileShader(shaderloc);

    // Actually if glGetError() returns an error you must call it in a loop
    // as OpenGL errors can accumulate, and you have to pop them all from the list.
    int error = gl.glGetError();
    if(error != GL3.GL_NO_ERROR){
        Logger.log(new GLU().gluErrorString(error), Logger.ERROR, "Shader compilation");
    }

    return shaderloc;
}

I took the liberty to remove all try/catch/finally blocks, as they were misplaced a bit: If reading any source file fails, shader loading cannot complete, so it makes no sense to continur gracefully. The propper way to deal with this would be a large try/catch block arround this, that cleans up OpenGL objects of shader compilation does not complete.

Mallon answered 19/6, 2011 at 8:59 Comment(1)
If you were not programming in Java but in a language that had pointers (C, C++, D, Go), then another viable method would be trying to memory map the file instead of reading it to a buffer.Mallon
D
1

Try moving the comment to after the #version declaration. It shouldn't matter, but there can be driver bugs.

Also, try putting an extra empty line at the end of the file. Again, just to be sure.

Lastly, make sure that you are in fact loading the string properly. Check it in the debugger. And make sure you're passing the string to OpenGL correctly. What does that code look like?

Duyne answered 19/6, 2011 at 6:38 Comment(1)
You might also want to make sure there's no BOM at the start of the file. (Thats 3 byte thing most editors would hide from you, that defines unicode encoding in file, even if it uses only latin chars)Paginal

© 2022 - 2024 — McMap. All rights reserved.