1
votes

I just copy pasted the code from this tutorial on the LWJGL wiki, which I will now paste here for your convenience.

import org.lwjgl.BufferUtils;
import org.lwjgl.LWJGLException;
import org.lwjgl.opengl.*;
import org.lwjgl.util.glu.GLU;

import java.nio.FloatBuffer;

public class TheQuadExampleDrawArrays {
  // Entry point for the application
  public static void main(String[] args) {
    new TheQuadExampleDrawArrays();
  }

  // Setup variables
  private final String WINDOW_TITLE = "The Quad: glDrawArrays";
  private final int WIDTH = 320;
  private final int HEIGHT = 240;
  // Quad variables
  private int vaoId = 0;
  private int vboId = 0;
  private int vertexCount = 0;

  public TheQuadExampleDrawArrays() {
    // Initialize OpenGL (Display)
    this.setupOpenGL();

    this.setupQuad();

    while (!Display.isCloseRequested()) {
      // Do a single loop (logic/render)
      this.loopCycle();

      // Force a maximum FPS of about 60
      Display.sync(60);
      // Let the CPU synchronize with the GPU if GPU is tagging behind
      Display.update();
    }

    // Destroy OpenGL (Display)
    this.destroyOpenGL();
  }

  public void setupOpenGL() {
    // Setup an OpenGL context with API version 3.2
    try {
      PixelFormat pixelFormat = new PixelFormat();
      ContextAttribs contextAtrributes = new ContextAttribs(3, 2)
        .withForwardCompatible(true)
        .withProfileCore(true);

      Display.setDisplayMode(new DisplayMode(WIDTH, HEIGHT));
      Display.setTitle(WINDOW_TITLE);
      Display.create(pixelFormat, contextAtrributes);

      GL11.glViewport(0, 0, WIDTH, HEIGHT);
    } catch (LWJGLException e) {
      e.printStackTrace();
      System.exit(-1);
    }

    // Setup an XNA like background color
    GL11.glClearColor(0.4f, 0.6f, 0.9f, 0f);

    // Map the internal OpenGL coordinate system to the entire screen
    GL11.glViewport(0, 0, WIDTH, HEIGHT);

    this.exitOnGLError("Error in setupOpenGL");
  }

  public void setupQuad() {
    // OpenGL expects vertices to be defined counter clockwise by default
    float[] vertices = {
      // Left bottom triangle
      -0.5f, 0.5f, 0f,
      -0.5f, -0.5f, 0f,
      0.5f, -0.5f, 0f,
      // Right top triangle
      0.5f, -0.5f, 0f,
      0.5f, 0.5f, 0f,
      -0.5f, 0.5f, 0f
    };
    // Sending data to OpenGL requires the usage of (flipped) byte buffers
    FloatBuffer verticesBuffer = BufferUtils.createFloatBuffer(vertices.length);
    verticesBuffer.put(vertices);
    verticesBuffer.flip();

    vertexCount = 6;

    // Create a new Vertex Array Object in memory and select it (bind)
    // A VAO can have up to 16 attributes (VBO's) assigned to it by default
    vaoId = GL30.glGenVertexArrays();
    GL30.glBindVertexArray(vaoId);

    // Create a new Vertex Buffer Object in memory and select it (bind)
    // A VBO is a collection of Vectors which in this case resemble the location of each vertex.
    vboId = GL15.glGenBuffers();
    GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, vboId);
    GL15.glBufferData(GL15.GL_ARRAY_BUFFER, verticesBuffer, GL15.GL_STATIC_DRAW);
    // Put the VBO in the attributes list at index 0
    GL20.glVertexAttribPointer(0, 3, GL11.GL_FLOAT, false, 0, 0);
    // Deselect (bind to 0) the VBO
    GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);

    // Deselect (bind to 0) the VAO
    GL30.glBindVertexArray(0);

    this.exitOnGLError("Error in setupQuad");
  }

  public void loopCycle() {
    GL11.glClear(GL11.GL_COLOR_BUFFER_BIT);

    // Bind to the VAO that has all the information about the quad vertices
    GL30.glBindVertexArray(vaoId);
    GL20.glEnableVertexAttribArray(0);

    // Draw the vertices
    GL11.glDrawArrays(GL11.GL_TRIANGLES, 0, vertexCount);
    /**
     * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
     * I found that the GL_INVALID_OPERATION flag was being raised here,
     * at the call to glDrawArrays().
     * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
     */

    // Put everything back to default (deselect)
    GL20.glDisableVertexAttribArray(0);
    GL30.glBindVertexArray(0);

    this.exitOnGLError("Error in loopCycle");
  }

  public void destroyOpenGL() {
    // Disable the VBO index from the VAO attributes list
    GL20.glDisableVertexAttribArray(0);

    // Delete the VBO
    GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
    GL15.glDeleteBuffers(vboId);

    // Delete the VAO
    GL30.glBindVertexArray(0);
    GL30.glDeleteVertexArrays(vaoId);

    Display.destroy();
  }

  public void exitOnGLError(String errorMessage) {
    int errorValue = GL11.glGetError();

    if (errorValue != GL11.GL_NO_ERROR) {
      String errorString = GLU.gluErrorString(errorValue);
      System.err.println("ERROR - " + errorMessage + ": " + errorString);

      if (Display.isCreated()) Display.destroy();
      System.exit(-1);
    }
  }
}

When I ran it, it threw an error that read

ERROR - Error in loopCycle: Invalid operation

I narrowed it down to the call to glDrawArrays() in the loopCycle() method, then hit up Google to find out what that might mean, and uncovered this SO question, which lists a whole ton of possible reasons (listed here for convenience).

  1. GL_INVALID_OPERATION is generated if a non-zero buffer object name is bound to an enabled array or to the GL_DRAW_INDIRECT_BUFFER binding and the buffer object's data store is currently mapped.

  2. GL_INVALID_OPERATION is generated if glDrawArrays is executed between the execution of glBegin and the corresponding glEnd.

  3. GL_INVALID_OPERATION will be generated by glDrawArrays or glDrawElements if any two active samplers in the current program object are of different types, but refer to the same texture image unit.

  4. GL_INVALID_OPERATION is generated if a geometry shader is active and mode is incompatible with the input primitive type of the geometry shader in the currently installed program object.

  5. GL_INVALID_OPERATION is generated if mode is GL_PATCHES and no tessellation control shader is active.

  6. GL_INVALID_OPERATION is generated if recording the vertices of a primitive to the buffer objects being used for transform feedback purposes would result in either exceeding the limits of any buffer object’s size, or in exceeding the end position offset + size - 1, as set by glBindBufferRange.

  7. GL_INVALID_OPERATION is generated by glDrawArrays() if no geometry shader is present, transform feedback is active and mode is not one of the allowed modes.

  8. GL_INVALID_OPERATION is generated by glDrawArrays() if a geometry shader is present, transform feedback is active and the output primitive type of the geometry shader does not match the transform feedback primitiveMode.

  9. GL_INVALID_OPERATION is generated if the bound shader program is invalid.

  10. GL_INVALID_OPERATION is generated if transform feedback is in use, and the buffer bound to the transform feedback binding point is also bound to the array buffer binding point.

Most of these make no sense to me, and after a fair amount of time reading through them I'm no closer to finding out what's wrong with this code. Could someone who knows more about this than me please point out the reason that the GL_INVALID_OPERATION flag is being raised?

1

1 Answers

2
votes

Item 9. Looks like you have no shader program bound.

You're creating a context using the Core Profile:

  ContextAttribs contextAtrributes = new ContextAttribs(3, 2)
    .withForwardCompatible(true)
    .withProfileCore(true);

With the Core Profile, it's required that you provide a shader program. You will typically write at least a vertex and a fragment shader in GLSL, and then use calls like the following to build and bind a shader program:

glCreateShader
glShaderSource
glCompileShader
glCreateProgram
glAttachShader
glLinkProgram
glUseProgram