Switching from GL_ARB_framebuffer to GL_EXT_framebuffer: FRAMEBUFFER_UNSUPPORTED_EXT.

At my company, we are working on huge library, which is a platform independent renderer (but it’s main pupropse is not to be used with games, but with professional and office apps). Everything is going fine, we have already a very efficient D3D11 and OpenGL renderers, as well as ‘software’ renderers for Windows and Linux (GDI/X11).

Recently, we have started to implement additional improvements in lower layers of our API. We thought it may be good idea to test them also on slower machines. Because update interacts with OpenGL-specific logic, this specific version needs to be tested.

We have implemented RTT (Render To Texture) in GL using core framebuffer object. Because older HW wasn’t able to work with this solution (no ARB_framebuffer support), we have created simple wrapper, which detects supported version of FBO (ARB_fb_object or EXT_fb_object) and wraps all cals in a transprent way. Example from creating depth stencil attachment:


    gxFBO::gen_renderbuffers(1, &stencil_buffer_id);
    gxFBO::bind_renderbuffer(GX_RENDERBUFFER, stencil_buffer_id);
    gxFBO::renderbuffer_storage(GX_RENDERBUFFER, GX_DEPTH24_STENCIL8, width, height);
    gxFBO::bind_renderbuffer(GX_RENDERBUFFER, 0);
    gxFBO::framebuffer_renderbuffer(GX_FRAMEBUFFER, GX_DEPTH_STENCIL_ATTACHMENT, stencil_buffer_id);
    gxFBO::check_framebuffer_status();

Quick explanation: functions calls are equivalent to gl* versions (for example, gxFBO::renderbuffer_storage() calls internally glRenderbufferStorage() or glRenderbufferStorageEXT(), based on supported version). Preprocessor constants are also the same (GX_DEPTH24_STENCIL8 = (GL_DEPTH24_STENCIL8 or GL_DEPTH24_STENCIL8_EXT)).

This works perfectly fine on hardware with 3.0+ support (everything renders correctly). It fails, however on older hardware (with GL 2.1 and EXT_framebuffer support).

Returned error in gxFBO::check_framebuffer_status() is: GL_FRAMEBUFFER_UNSUPPORTED_EXT.

I also tried to create separate renderbuffers for depth and stencil:


    gxFBO::gen_renderbuffers(1, &depth_buffer_id);
    gxFBO::gen_renderbuffers(1, &stencil_buffer_id);

    gxFBO::bind_renderbuffer(GX_RENDERBUFFER, depth_buffer_id);
    gxFBO::renderbuffer_storage(GX_RENDERBUFFER, GL_DEPTH_COMPONENT, width, height);

    gxFBO::bind_renderbuffer(GX_RENDERBUFFER, stencil_buffer_id);   
    gxFBO::renderbuffer_storage(GX_RENDERBUFFER, GX_STENCIL_INDEX8, width, height);
    gxFBO::bind_renderbuffer(GX_RENDERBUFFER, 0);

    gxFBO::framebuffer_renderbuffer(GX_FRAMEBUFFER,
                    GX_DEPTH_ATTACHMENT,
                    GX_RENDERBUFFER, 
                    depth_buffer_id);

    gxFBO::framebuffer_renderbuffer(GX_FRAMEBUFFER,
                    GX_STENCIL_ATTACHMENT,
                    GX_RENDERBUFFER, 
                    stencil_buffer_id);

    gxFBO::check_framebuffer_status();

Same error at framebuffer status check. Could anyone help with proper setup in this case? Is it possible to do what I’m trying to do using GL_EXT_framebuffer?

Thanks in advance for any help.

If you carefully read the entirety of the two extension specs, you’ll note these key differences in functionality:

  1. ARB_fbo allows mismatched attachment sizes. If your D/S attachment width/height isn’t the same as the color attachment(s), it will be incomplete in EXT_fbo.
  2. ditto for mismatched color formats attachments.
  3. changes to behavior of FBO zero. EXT_fbo says it is always complete, ARB_fbo says it is only complete if it exists. This affects the behavior of window-less contexts.
  4. _blit and _multisample extensions are merged into ARB_fbo, but with changes to error conditions (i.e. format conversions) during blit. Those errors are changed again in later core specs.
  5. depth_stencil attachment point is added with the merge of the _packed_depth_stencil extension. The equivalent in EXT_fbo would not be to create separate D and S8 renderbuffers as you show, it would be to create the same single D24_S8 renderbuffer, but attach it twice, to the D and S attachment points.

Likely, it is 5) that is your issue: there is no requirement to support rendering to separate depth and stencil attachments, particularly when the internalformat of the depth attachment is ambiguous as in your example. Some hardware will reject combinations such as D16 + S8. In general: if the renderer does not support ARB_fbo, then check for EXT_fbo. If it supports EXT_fbo and also supports EXT_packed_depth_stencil, then create a D24_S8 renderbuffer and attach it to both D and S. I expect that to work on all GL2.x-era hardware. Only if EXT_packed_depth_stencil is not supported, then create separate D and S renderbuffers, but be careful to use sized internalformat enums.

To have a robust EXT_fbo path, you need to be aware of all of the conditions I mentioned, not just 5).

Thank you very much for your answer.

I forgot to mention it, but I was able to find info about depth/stencil configuration before I even started to implement above solution. My code that test for FBO support looks like this:


bool is_arb_fbo_supported = Check_for_ARB_FBO_support();
if(is_arb_fbo_supported)
{
  gxFBO::_api_arb_supported = true;
  return true;
}
else
{
  bool is_ext_fbo_supported = Check_for_EXT_FBO_support();
  bool is_packed_ds_supported = Check_for_Packed_DS_support();
  if(is_ext_fbo_supported && is_packed_ds_supported)
  {
    gxFBO::_api_ext_supported = true;
    return true;
  }
  else
  {
    //I currently treat this case as a 'Critical: Requirements-Not-Met'
    return_false_break_execution_throw_exception_display_error_and_force_PC_to_explode(true, true, true, true, true); //This PC is a junk anyway...
  }
}

Names are slightly different, as I’m writing this without source code, but logic is exactly the same.

So, currently, my code executes only if both EXT_fbo and EXT_packed_DS are supported. In this case my code should work as expected:

  1. Create single buffer:
gxFBO::gen_renderbuffers(1, &stencil_buffer_id);
  1. Bind it:
gxFBO::bind_renderbuffer(GX_RENDERBUFFER, stencil_buffer_id);
  1. One buffer for both depth and stencil. ‘Width’ and ‘height’ are the dimensions of texture (successfuly created), that is bound as color attachment to FBO.
gxFBO::renderbuffer_storage(GX_RENDERBUFFER, GX_DEPTH24_STENCIL8, width, height);
  1. Unbind:
gxFBO::bind_renderbuffer(GX_RENDERBUFFER, 0);
  1. One buffer for both D and S:
gxFBO::framebuffer_renderbuffer(GX_FRAMEBUFFER, GX_DEPTH_STENCIL_ATTACHMENT, stencil_buffer_id);
  1. Check status:
gxFBO::check_framebuffer_status();

Important note: gxFBO::framebuffer_renderbuffer() checks what needs to be done:


class gxFBO
{
public:

  //.....

  static void framebuffer_renderbuffer(target, attachment, stencil_buffer_id)
  {
    if(gxFBO::_api_arb_supported)
    {
      glFramebufferRenderbuffer(target, attachment, GL_RENDERBUFFER, stencil_buffer_id);
    }
    else if(gxFBO::_api_ext_supported)
    {
      if(attachment == GX_DEPTH_STENCIL_ATTACHMENT)
      {
        glFramebufferRenderbufferEXT(target, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, stencil_buffer_id);
        glFramebufferRenderbufferEXT(target, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, stencil_buffer_id);
      }
      else
      {
        glFramebufferRenderbufferEXT(target, attachment, GL_RENDERBUFFER, stencil_buffer_id);
      }
    }
    else usual_PC_explosion();
  }

  //.....

};

Perhaps I miss something very subtle? Also, could you please specify, what particular internalFormats would be appropriate in this case? I’m quite confused now. I guess that by ‘ambiguous internalFormat’ you mean GL_DEPTH_COMPONENT? What format should I use in case of unsupported EXT_packed_depth_stencil?

That sequence looks reasonable to me, catching [LEFT]GX_DEPTH_STENCIL_ATTACHMENT [/LEFT]and attaching twice for older renderers. At least on OSX, I expect that to work on all vendors, all renderers.

As for “ambiguous”, yes, I was referring to GL_DEPTH_COMPONENT. The renderer can choose any of D16, D24, D32 in this situation (you can find out which by querying GL_RENDERBUFFER_INTERNAL_FORMAT, or GL_TEXTURE_INTERNAL_FORMAT appropriately) and there will be renderer-specific limitations on the color/depth/stencil format combinations which cause incomplete framebuffer. Generally, you should avoid unsized internalformat enums, always be specific.

I will apply some changes based on your suggestions.

You also made me think about depth/stencil configuration. In particular, about format of rendering context. On Windows, when creating RC, you need to specify pixel format, which includes depth and stencil buffers size. However, format that is returned is described in doc as “the most compatible with the specified configuration”. PC that makes problems has GPU integrated into Intel G45 chip. In my code I silently assumed D24_S8 (which is the most suitable for us and quite comfortable for GPU with size of 32 bits).
Is it possible that driver returns pixel format that is incompatible with configuration I 'm trying to create? I can’t find it, but yesterday I saw somewhere that, in case of GL_FRAMEBUFFER_UNSUPPORTED_EXT, app should try different configurations until it succeeds. I would be totally surprised in case of lack of support for D24S8, but - hey, that’s Intel…

I can’t speak to the Windows RC compatibility question, but per GL spec, the bit depths of the framebuffer planes (and other state like stereo, doublebuffer, aux buffers, samples etc) are FBO state. As soon as you bind a non-zero FBO, all of those values change, and framebuffer completeness is a per-FBO concept.

The only operation where compatibility with the window pixel format should be a concern is during blit. There are specific constraints on format conversions (i.e. float FBO -> 8888 window is OK in current core spec, but integer FBO -> 8888 window is not.) Multisample blits are more limited (i.e. 8888 4x FBO -> 8888 window is OK, but float 4x FBO -> 8888 window is not (at least prior to GL4.4; the spec authors keep changing their minds.)) Depth/stencil blits are problematic, because the spec requires the formats to “match”, but “match” is not defined (what is the internalformat of a window’s depth buffer? Is D24_S8 -> “24 bits of depth in pixel format” OK?)

As for Intel, packed depth stencil appears to be widely supported on their Windows drivers. On OSX, it is supported on all Intel drivers going back to the i945, so if a G45/GMA X4500 is not reporting the extension, it certainly is not a hardware limitation.