Search code examples
macosopengljoglframebufferstencil-buffer

How to find valid OpenGL framebuffer configuration/documentation


I'm using an early 2011 MacBook Pro with macOS Sierra 10.12.4 and Intel HD Graphics 3000 512 MB, and I'm using JOGL. According to OS X 10.9 Core Profile OpenGL Information (unfortunately they don't have info for newer versions yet), my system supports 0, 16, or 24 bpp depth buffers and 0 or 8 bpp stencil buffers. Yet it seems like what actually works when I create a user framebuffer is anyone's guess:

  • I can get it to work with a GL_DEPTH_COMPONENT_32 render buffer.
  • I can't get it to work with a GL_STENCIL_INDEX8 render buffer -- I always get GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT or GL_FRAMEBUFFER_UNSUPPORTED errors, regardless of whether I use a 16-, 24- or 32-bit depth buffer.
  • But I can get it to work with a GL_DEPTH24_STENCIL8 render buffer.

I haven't been able to find a specific mention of GL_DEPTH24_STENCIL8 for OS X anywhere. I want to ensure my program will work on a wide variety of systems (Windows and Linux included, since it's a JOGL app), but it seems like all I can do is guess and check a variety of framebuffer configurations until I find one that works.

Is there really no better way to decide what framebuffer configuration to use? Are there sites with better documentation I just haven't found? Is it hard for vendors to document for some reason?

Code samples

Unsuccessful configuration
    int[] temps = new int[3];
    this.width = width;
    this.height = height;

    gl3.glGenFramebuffers(1, temps, 0);
    framebuffer = temps[0];
    gl3.glBindFramebuffer(GL.GL_FRAMEBUFFER, framebuffer);

    int numBuffers = 2;
    if (desiredUseStencilBuffer) {
        numBuffers++;
    }
    gl3.glGenRenderbuffers(numBuffers, temps, 0);
    colorBuffer = temps[0];
    depthBuffer = temps[1];
    stencilBuffer = temps[2];

    numSamples = targetNumSamples;
    useStencilBuffer = desiredUseStencilBuffer;

    if (numSamples > 1) {
        gl3.glBindRenderbuffer(GL_RENDERBUFFER, colorBuffer);
        gl3.glRenderbufferStorageMultisample(GL_RENDERBUFFER, numSamples, GL.GL_RGBA8,
                width, height);

        gl3.glBindRenderbuffer(GL_RENDERBUFFER, depthBuffer);
        gl3.glRenderbufferStorageMultisample(GL_RENDERBUFFER, numSamples,
                GL.GL_DEPTH_COMPONENT24, width, height);
        if (useStencilBuffer) {
            gl3.glBindRenderbuffer(GL_RENDERBUFFER, stencilBuffer);
            gl3.glRenderbufferStorageMultisample(GL_RENDERBUFFER, numSamples,
                    GL3.GL_STENCIL_INDEX8, width, height);
        }
    } else {
        gl3.glBindRenderbuffer(GL_RENDERBUFFER, colorBuffer);
        gl3.glRenderbufferStorage(GL_RENDERBUFFER, GL.GL_RGBA8, width, height);

        gl3.glBindRenderbuffer(GL_RENDERBUFFER, depthBuffer);
        gl3.glRenderbufferStorage(GL_RENDERBUFFER, GL.GL_DEPTH_COMPONENT24, width, height);
        if (useStencilBuffer) {
            gl3.glBindRenderbuffer(GL_RENDERBUFFER, stencilBuffer);
            gl3.glRenderbufferStorage(GL_RENDERBUFFER, GL3.GL_STENCIL_INDEX8,
                    width, height);
        }
    }

    gl3.glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, colorBuffer);
    gl3.glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL2ES3.GL_DEPTH_ATTACHMENT,
            GL_RENDERBUFFER, depthBuffer);
    if (useStencilBuffer) {
        gl3.glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL2ES3.GL_STENCIL_ATTACHMENT,
                GL_RENDERBUFFER, stencilBuffer);
    }

    int status = gl3.glCheckFramebufferStatus(GL_FRAMEBUFFER);
    switch (status) {
    case GL.GL_FRAMEBUFFER_COMPLETE:
        break;

    case GL.GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT:
        throw new RuntimeException("An attachment could not be bound to frame buffer object!");
Successful configuration
    int[] temps = new int[3];
    this.width = width;
    this.height = height;

    gl3.glGenFramebuffers(1, temps, 0);
    framebuffer = temps[0];
    gl3.glBindFramebuffer(GL.GL_FRAMEBUFFER, framebuffer);

    gl3.glGenRenderbuffers(2, temps, 0);
    colorBuffer = temps[0];
    depthBuffer = temps[1];

    numSamples = targetNumSamples;
    useStencilBuffer = desiredUseStencilBuffer;

    if (numSamples > 1) {
        gl3.glBindRenderbuffer(GL_RENDERBUFFER, colorBuffer);
        gl3.glRenderbufferStorageMultisample(GL_RENDERBUFFER, numSamples, GL.GL_RGBA8,
                width, height);

        gl3.glBindRenderbuffer(GL_RENDERBUFFER, depthBuffer);
        if (useStencilBuffer) {
            gl3.glRenderbufferStorageMultisample(GL_RENDERBUFFER, numSamples,
                    GL.GL_DEPTH24_STENCIL8, width, height);
        } else {
            gl3.glRenderbufferStorageMultisample(GL_RENDERBUFFER, numSamples,
                    GL.GL_DEPTH_COMPONENT32, width, height);
        }
    } else {
        gl3.glBindRenderbuffer(GL_RENDERBUFFER, colorBuffer);
        gl3.glRenderbufferStorage(GL_RENDERBUFFER, GL.GL_RGBA8, width, height);

        gl3.glBindRenderbuffer(GL_RENDERBUFFER, depthBuffer);
        if (useStencilBuffer) {
            gl3.glRenderbufferStorage(GL_RENDERBUFFER, GL.GL_DEPTH24_STENCIL8,
                    width, height);
        } else {
            gl3.glRenderbufferStorage(GL_RENDERBUFFER, GL.GL_DEPTH_COMPONENT32,
                    width, height);
        }
    }

    gl3.glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, colorBuffer);
    if (useStencilBuffer) {
        gl3.glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL2ES3.GL_DEPTH_STENCIL_ATTACHMENT,
                GL_RENDERBUFFER, depthBuffer);
    } else {
        gl3.glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT,
                GL_RENDERBUFFER, depthBuffer);
    }

    int status = gl3.glCheckFramebufferStatus(GL_FRAMEBUFFER);
    switch (status) {
    case GL.GL_FRAMEBUFFER_COMPLETE:
        break;

    case GL.GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT:
        throw new RuntimeException("An attachment could not be bound to frame buffer object!");
    ...

Solution

  • The OpenGL specification does not require that you can attach separate depth and stencil images to the framebuffer. The API lets you try it, but individual implementations can choose to disallow it by giving you an unsupported completion state.

    The 3.0 specification added the concept of required image formats, which implementations are required to accept for their framebuffer configurations. Among them is GL_DEPTH24_STENCIL8. So any compliant OpenGL implementation is required to support combined depth/stencil buffers.