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:
GL_DEPTH_COMPONENT_32
render buffer.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.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?
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!");
...
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.