mesa: new cases in _mesa_base_fbo_format()
[mesa.git] / src / mesa / main / fbobject.c
index 9a84e5a79cff835aa3f14fd8c680f1d8decd3ddb..ff9dcc9ca6621143af1fdcabfebe1b7bf84f99a4 100644 (file)
@@ -40,6 +40,8 @@
 #include "framebuffer.h"
 #include "hash.h"
 #include "macros.h"
+#include "mfeatures.h"
+#include "mtypes.h"
 #include "renderbuffer.h"
 #include "state.h"
 #include "teximage.h"
 static struct gl_framebuffer DummyFramebuffer;
 static struct gl_renderbuffer DummyRenderbuffer;
 
+/* We bind this framebuffer when applications pass a NULL
+ * drawable/surface in make current. */
+static struct gl_framebuffer IncompleteFramebuffer;
+
 
 #define IS_CUBE_FACE(TARGET) \
    ((TARGET) >= GL_TEXTURE_CUBE_MAP_POSITIVE_X && \
@@ -91,23 +97,27 @@ delete_dummy_framebuffer(struct gl_framebuffer *fb)
 
 
 void
-_mesa_init_fbobjects(GLcontext *ctx)
+_mesa_init_fbobjects(struct gl_context *ctx)
 {
+   _glthread_INIT_MUTEX(DummyFramebuffer.Mutex);
+   _glthread_INIT_MUTEX(DummyRenderbuffer.Mutex);
+   _glthread_INIT_MUTEX(IncompleteFramebuffer.Mutex);
    DummyFramebuffer.Delete = delete_dummy_framebuffer;
    DummyRenderbuffer.Delete = delete_dummy_renderbuffer;
+   IncompleteFramebuffer.Delete = delete_dummy_framebuffer;
 }
 
 struct gl_framebuffer *
 _mesa_get_incomplete_framebuffer(void)
 {
-   return &DummyFramebuffer;
+   return &IncompleteFramebuffer;
 }
 
 /**
  * Helper routine for getting a gl_renderbuffer.
  */
 struct gl_renderbuffer *
-_mesa_lookup_renderbuffer(GLcontext *ctx, GLuint id)
+_mesa_lookup_renderbuffer(struct gl_context *ctx, GLuint id)
 {
    struct gl_renderbuffer *rb;
 
@@ -124,7 +134,7 @@ _mesa_lookup_renderbuffer(GLcontext *ctx, GLuint id)
  * Helper routine for getting a gl_framebuffer.
  */
 struct gl_framebuffer *
-_mesa_lookup_framebuffer(GLcontext *ctx, GLuint id)
+_mesa_lookup_framebuffer(struct gl_context *ctx, GLuint id)
 {
    struct gl_framebuffer *fb;
 
@@ -158,7 +168,7 @@ invalidate_framebuffer(struct gl_framebuffer *fb)
  * the depth buffer attachment point.
  */
 struct gl_renderbuffer_attachment *
-_mesa_get_attachment(GLcontext *ctx, struct gl_framebuffer *fb,
+_mesa_get_attachment(struct gl_context *ctx, struct gl_framebuffer *fb,
                      GLenum attachment)
 {
    GLuint i;
@@ -208,7 +218,7 @@ _mesa_get_attachment(GLcontext *ctx, struct gl_framebuffer *fb,
  * window-system framebuffer (not user-created framebuffer objects).
  */
 static struct gl_renderbuffer_attachment *
-_mesa_get_fb0_attachment(GLcontext *ctx, struct gl_framebuffer *fb,
+_mesa_get_fb0_attachment(struct gl_context *ctx, struct gl_framebuffer *fb,
                          GLenum attachment)
 {
    assert(fb->Name == 0);
@@ -247,7 +257,8 @@ _mesa_get_fb0_attachment(GLcontext *ctx, struct gl_framebuffer *fb,
  * point.  Update reference counts, etc.
  */
 void
-_mesa_remove_attachment(GLcontext *ctx, struct gl_renderbuffer_attachment *att)
+_mesa_remove_attachment(struct gl_context *ctx,
+                        struct gl_renderbuffer_attachment *att)
 {
    if (att->Type == GL_TEXTURE) {
       ASSERT(att->Texture);
@@ -273,7 +284,7 @@ _mesa_remove_attachment(GLcontext *ctx, struct gl_renderbuffer_attachment *att)
  * The previous binding, if any, will be removed first.
  */
 void
-_mesa_set_texture_attachment(GLcontext *ctx,
+_mesa_set_texture_attachment(struct gl_context *ctx,
                              struct gl_framebuffer *fb,
                              struct gl_renderbuffer_attachment *att,
                              struct gl_texture_object *texObj,
@@ -314,7 +325,7 @@ _mesa_set_texture_attachment(GLcontext *ctx,
  * The previous binding, if any, will be removed first.
  */
 void
-_mesa_set_renderbuffer_attachment(GLcontext *ctx,
+_mesa_set_renderbuffer_attachment(struct gl_context *ctx,
                                   struct gl_renderbuffer_attachment *att,
                                   struct gl_renderbuffer *rb)
 {
@@ -332,7 +343,8 @@ _mesa_set_renderbuffer_attachment(GLcontext *ctx,
  * Attach a renderbuffer object to a framebuffer object.
  */
 void
-_mesa_framebuffer_renderbuffer(GLcontext *ctx, struct gl_framebuffer *fb,
+_mesa_framebuffer_renderbuffer(struct gl_context *ctx,
+                               struct gl_framebuffer *fb,
                                GLenum attachment, struct gl_renderbuffer *rb)
 {
    struct gl_renderbuffer_attachment *att;
@@ -389,6 +401,44 @@ fbo_incomplete(const char *msg, int index)
 }
 
 
+/**
+ * Is the given base format a legal format for a color renderbuffer?
+ */
+GLboolean
+_mesa_is_legal_color_format(const struct gl_context *ctx, GLenum baseFormat)
+{
+   switch (baseFormat) {
+   case GL_RGB:
+   case GL_RGBA:
+      return GL_TRUE;
+   case GL_LUMINANCE:
+   case GL_LUMINANCE_ALPHA:
+   case GL_INTENSITY:
+   case GL_ALPHA:
+      return ctx->Extensions.ARB_framebuffer_object;
+   case GL_RED:
+   case GL_RG:
+      return ctx->Extensions.ARB_texture_rg;
+   default:
+      return GL_FALSE;
+   }
+}
+
+
+/**
+ * Is the given base format a legal format for a depth/stencil renderbuffer?
+ */
+static GLboolean
+is_legal_depth_format(const struct gl_context *ctx, GLenum baseFormat)
+{
+   switch (baseFormat) {
+   case GL_DEPTH_COMPONENT:
+   case GL_DEPTH_STENCIL_EXT:
+      return GL_TRUE;
+   default:
+      return GL_FALSE;
+   }
+}
 
 
 /**
@@ -398,7 +448,7 @@ fbo_incomplete(const char *msg, int index)
  *               if GL_STENCIL, this is a stencil component attachment point.
  */
 static void
-test_attachment_completeness(const GLcontext *ctx, GLenum format,
+test_attachment_completeness(const struct gl_context *ctx, GLenum format,
                              struct gl_renderbuffer_attachment *att)
 {
    assert(format == GL_COLOR || format == GL_DEPTH || format == GL_STENCIL);
@@ -440,10 +490,7 @@ test_attachment_completeness(const GLcontext *ctx, GLenum format,
       baseFormat = _mesa_get_format_base_format(texImage->TexFormat);
 
       if (format == GL_COLOR) {
-         if (baseFormat != GL_RGB &&
-             baseFormat != GL_RGBA &&
-            (!ctx->Extensions.ARB_framebuffer_object ||
-             baseFormat != GL_ALPHA)) {
+         if (!_mesa_is_legal_color_format(ctx, baseFormat)) {
             att_incomplete("bad format");
             att->Complete = GL_FALSE;
             return;
@@ -497,8 +544,7 @@ test_attachment_completeness(const GLcontext *ctx, GLenum format,
          return;
       }
       if (format == GL_COLOR) {
-         if (baseFormat != GL_RGB &&
-             baseFormat != GL_RGBA) {
+         if (!_mesa_is_legal_color_format(ctx, baseFormat)) {
             att_incomplete("bad renderbuffer color format");
             att->Complete = GL_FALSE;
             return;
@@ -551,7 +597,8 @@ test_attachment_completeness(const GLcontext *ctx, GLenum format,
  * framebuffer is complete.
  */
 void
-_mesa_test_framebuffer_completeness(GLcontext *ctx, struct gl_framebuffer *fb)
+_mesa_test_framebuffer_completeness(struct gl_context *ctx,
+                                    struct gl_framebuffer *fb)
 {
    GLuint numImages;
    GLenum intFormat = GL_NONE; /* color buffers' internal format */
@@ -574,6 +621,7 @@ _mesa_test_framebuffer_completeness(GLcontext *ctx, struct gl_framebuffer *fb)
    for (i = -2; i < (GLint) ctx->Const.MaxColorAttachments; i++) {
       struct gl_renderbuffer_attachment *att;
       GLenum f;
+      gl_format mesaFormat;
 
       /*
        * XXX for ARB_fbo, only check color buffers that are named by
@@ -620,10 +668,10 @@ _mesa_test_framebuffer_completeness(GLcontext *ctx, struct gl_framebuffer *fb)
          minHeight = MIN2(minHeight, texImg->Height);
          maxHeight = MAX2(maxHeight, texImg->Height);
          f = texImg->_BaseFormat;
+         mesaFormat = texImg->TexFormat;
          numImages++;
-         if (f != GL_RGB && f != GL_RGBA && f != GL_DEPTH_COMPONENT
-             && f != GL_DEPTH_STENCIL_EXT
-            && (!ctx->Extensions.ARB_framebuffer_object || f != GL_ALPHA)) {
+         if (!_mesa_is_legal_color_format(ctx, f) &&
+             !is_legal_depth_format(ctx, f)) {
             fb->_Status = GL_FRAMEBUFFER_INCOMPLETE_FORMATS_EXT;
             fbo_incomplete("texture attachment incomplete", -1);
             return;
@@ -635,6 +683,7 @@ _mesa_test_framebuffer_completeness(GLcontext *ctx, struct gl_framebuffer *fb)
          minHeight = MIN2(minHeight, att->Renderbuffer->Height);
          maxHeight = MAX2(minHeight, att->Renderbuffer->Height);
          f = att->Renderbuffer->InternalFormat;
+         mesaFormat = att->Renderbuffer->Format;
          numImages++;
       }
       else {
@@ -647,6 +696,9 @@ _mesa_test_framebuffer_completeness(GLcontext *ctx, struct gl_framebuffer *fb)
          numSamples = att->Renderbuffer->NumSamples;
       }
 
+      /* check if integer color */
+      fb->_IntegerColor = _mesa_is_format_integer_color(mesaFormat);
+
       /* Error-check width, height, format, samples
        */
       if (numImages == 1) {
@@ -742,7 +794,7 @@ _mesa_test_framebuffer_completeness(GLcontext *ctx, struct gl_framebuffer *fb)
       fb->Height = minHeight;
 
       /* finally, update the visual info for the framebuffer */
-      _mesa_update_framebuffer_visual(fb);
+      _mesa_update_framebuffer_visual(ctx, fb);
    }
 }
 
@@ -819,7 +871,7 @@ _mesa_BindRenderbufferEXT(GLenum target, GLuint renderbuffer)
  * The spec calls for unbinding.
  */
 static void
-detach_renderbuffer(GLcontext *ctx,
+detach_renderbuffer(struct gl_context *ctx,
                     struct gl_framebuffer *fb,
                     struct gl_renderbuffer *rb)
 {
@@ -909,20 +961,53 @@ _mesa_GenRenderbuffersEXT(GLsizei n, GLuint *renderbuffers)
 
 /**
  * Given an internal format token for a render buffer, return the
- * corresponding base format.
- * This is very similar to _mesa_base_tex_format() but the set of valid
- * internal formats is somewhat different.
+ * corresponding base format (one of GL_RGB, GL_RGBA, GL_STENCIL_INDEX,
+ * GL_DEPTH_COMPONENT, GL_DEPTH_STENCIL_EXT, GL_ALPHA, GL_LUMINANCE,
+ * GL_LUMINANCE_ALPHA, GL_INTENSITY, etc).
+ *
+ * This is similar to _mesa_base_tex_format() but the set of valid
+ * internal formats is different.
  *
- * \return one of GL_RGB, GL_RGBA, GL_STENCIL_INDEX, GL_DEPTH_COMPONENT
- *  GL_DEPTH_STENCIL_EXT or zero if error.
+ * Note that even if a format is determined to be legal here, validation
+ * of the FBO may fail if the format is not suppoted by the driver/GPU.
  *
- * XXX in the future when we support red-only and red-green formats
- * we'll also return GL_RED and GL_RG.
+ * \param internalFormat  as passed to glRenderbufferStorage()
+ * \return the base internal format, or 0 if internalFormat is illegal
  */
 GLenum
-_mesa_base_fbo_format(GLcontext *ctx, GLenum internalFormat)
+_mesa_base_fbo_format(struct gl_context *ctx, GLenum internalFormat)
 {
+   /*
+    * Notes: some formats such as alpha, luminance, etc. were added
+    * with GL_ARB_framebuffer_object.
+    */
    switch (internalFormat) {
+   case GL_ALPHA:
+   case GL_ALPHA4:
+   case GL_ALPHA8:
+   case GL_ALPHA12:
+   case GL_ALPHA16:
+      return ctx->Extensions.ARB_framebuffer_object ? GL_ALPHA : 0;
+   case GL_LUMINANCE:
+   case GL_LUMINANCE4:
+   case GL_LUMINANCE8:
+   case GL_LUMINANCE12:
+   case GL_LUMINANCE16:
+      return ctx->Extensions.ARB_framebuffer_object ? GL_LUMINANCE : 0;
+   case GL_LUMINANCE_ALPHA:
+   case GL_LUMINANCE4_ALPHA4:
+   case GL_LUMINANCE6_ALPHA2:
+   case GL_LUMINANCE8_ALPHA8:
+   case GL_LUMINANCE12_ALPHA4:
+   case GL_LUMINANCE12_ALPHA12:
+   case GL_LUMINANCE16_ALPHA16:
+      return ctx->Extensions.ARB_framebuffer_object ? GL_LUMINANCE_ALPHA : 0;
+   case GL_INTENSITY:
+   case GL_INTENSITY4:
+   case GL_INTENSITY8:
+   case GL_INTENSITY12:
+   case GL_INTENSITY16:
+      return ctx->Extensions.ARB_framebuffer_object ? GL_INTENSITY : 0;
    case GL_RGB:
    case GL_R3_G3_B2:
    case GL_RGB4:
@@ -931,6 +1016,7 @@ _mesa_base_fbo_format(GLcontext *ctx, GLenum internalFormat)
    case GL_RGB10:
    case GL_RGB12:
    case GL_RGB16:
+   case GL_SRGB8_EXT:
       return GL_RGB;
    case GL_RGBA:
    case GL_RGBA2:
@@ -941,6 +1027,7 @@ _mesa_base_fbo_format(GLcontext *ctx, GLenum internalFormat)
    case GL_RGBA12:
    case GL_RGBA16:
    case GL_RGBA16_SNORM:
+   case GL_SRGB8_ALPHA8_EXT:
       return GL_RGBA;
    case GL_STENCIL_INDEX:
    case GL_STENCIL_INDEX1_EXT:
@@ -1020,7 +1107,7 @@ renderbuffer_storage(GLenum target, GLenum internalFormat,
 
    rb = ctx->CurrentRenderbuffer;
    if (!rb) {
-      _mesa_error(ctx, GL_INVALID_OPERATION, func);
+      _mesa_error(ctx, GL_INVALID_OPERATION, "%s", func);
       return;
    }
 
@@ -1115,8 +1202,17 @@ get_component_bits(GLenum pname, GLenum baseFormat, gl_format format)
    switch (pname) {
    case GL_RENDERBUFFER_RED_SIZE_EXT:
    case GL_FRAMEBUFFER_ATTACHMENT_RED_SIZE:
+      if (baseFormat == GL_RGB || baseFormat == GL_RGBA ||
+         baseFormat == GL_RG || baseFormat == GL_RED)
+         return _mesa_get_format_bits(format, pname);
+      else
+         return 0;
    case GL_RENDERBUFFER_GREEN_SIZE_EXT:
    case GL_FRAMEBUFFER_ATTACHMENT_GREEN_SIZE:
+      if (baseFormat == GL_RGB || baseFormat == GL_RGBA || baseFormat == GL_RG)
+         return _mesa_get_format_bits(format, pname);
+      else
+         return 0;
    case GL_RENDERBUFFER_BLUE_SIZE_EXT:
    case GL_FRAMEBUFFER_ATTACHMENT_BLUE_SIZE:
       if (baseFormat == GL_RGB || baseFormat == GL_RGBA)
@@ -1125,7 +1221,8 @@ get_component_bits(GLenum pname, GLenum baseFormat, gl_format format)
          return 0;
    case GL_RENDERBUFFER_ALPHA_SIZE_EXT:
    case GL_FRAMEBUFFER_ATTACHMENT_ALPHA_SIZE:
-      if (baseFormat == GL_RGBA || baseFormat == GL_ALPHA)
+      if (baseFormat == GL_RGBA || baseFormat == GL_ALPHA ||
+         baseFormat == GL_LUMINANCE_ALPHA)
          return _mesa_get_format_bits(format, pname);
       else
          return 0;
@@ -1267,7 +1364,7 @@ _mesa_IsFramebufferEXT(GLuint framebuffer)
  * attachments.
  */
 static void
-check_begin_texture_render(GLcontext *ctx, struct gl_framebuffer *fb)
+check_begin_texture_render(struct gl_context *ctx, struct gl_framebuffer *fb)
 {
    GLuint i;
    ASSERT(ctx->Driver.RenderTexture);
@@ -1292,7 +1389,7 @@ check_begin_texture_render(GLcontext *ctx, struct gl_framebuffer *fb)
  * notify the device driver that the texture image may have changed.
  */
 static void
-check_end_texture_render(GLcontext *ctx, struct gl_framebuffer *fb)
+check_end_texture_render(struct gl_context *ctx, struct gl_framebuffer *fb)
 {
    if (fb->Name == 0)
       return; /* can't render to texture with winsys framebuffers */
@@ -1586,7 +1683,7 @@ _mesa_CheckFramebufferStatusEXT(GLenum target)
  * Common code called by glFramebufferTexture1D/2D/3DEXT().
  */
 static void
-framebuffer_texture(GLcontext *ctx, const char *caller, GLenum target, 
+framebuffer_texture(struct gl_context *ctx, const char *caller, GLenum target, 
                     GLenum attachment, GLenum textarget, GLuint texture,
                     GLint level, GLint zoffset)
 {
@@ -1856,6 +1953,13 @@ _mesa_FramebufferRenderbufferEXT(GLenum target, GLenum attachment,
                      " renderbuffer %u)", renderbuffer);
         return;
       }
+      else if (rb == &DummyRenderbuffer) {
+         /* This is what NVIDIA does */
+        _mesa_error(ctx, GL_INVALID_VALUE,
+                    "glFramebufferRenderbufferEXT(renderbuffer %u)",
+                     renderbuffer);
+        return;
+      }
    }
    else {
       /* remove renderbuffer attachment */
@@ -1883,7 +1987,7 @@ _mesa_FramebufferRenderbufferEXT(GLenum target, GLenum attachment,
    /* Some subsequent GL commands may depend on the framebuffer's visual
     * after the binding is updated.  Update visual info now.
     */
-   _mesa_update_framebuffer_visual(fb);
+   _mesa_update_framebuffer_visual(ctx, fb);
 }
 
 
@@ -1957,7 +2061,7 @@ _mesa_GetFramebufferAttachmentParameterivEXT(GLenum target, GLenum attachment,
 
    switch (pname) {
    case GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE_EXT:
-      *params = att->Type;
+      *params = buffer->Name == 0 ? GL_FRAMEBUFFER_DEFAULT : att->Type;
       return;
    case GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME_EXT:
       if (att->Type == GL_RENDERBUFFER_EXT) {
@@ -1967,8 +2071,8 @@ _mesa_GetFramebufferAttachmentParameterivEXT(GLenum target, GLenum attachment,
         *params = att->Texture->Name;
       }
       else {
-        _mesa_error(ctx, GL_INVALID_ENUM,
-                    "glGetFramebufferAttachmentParameterivEXT(pname)");
+         assert(att->Type == GL_NONE);
+         *params = 0;
       }
       return;
    case GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL_EXT:
@@ -2014,7 +2118,14 @@ _mesa_GetFramebufferAttachmentParameterivEXT(GLenum target, GLenum attachment,
                      "glGetFramebufferAttachmentParameterivEXT(pname)");
       }
       else {
-         *params = _mesa_get_format_color_encoding(att->Renderbuffer->Format);
+         if (ctx->Extensions.EXT_framebuffer_sRGB) {
+            *params = _mesa_get_format_color_encoding(att->Renderbuffer->Format);
+         }
+         else {
+            /* According to ARB_framebuffer_sRGB, we should return LINEAR
+             * if the sRGB conversion is unsupported. */
+            *params = GL_LINEAR;
+         }
       }
       return;
    case GL_FRAMEBUFFER_ATTACHMENT_COMPONENT_TYPE:
@@ -2089,6 +2200,7 @@ _mesa_GenerateMipmapEXT(GLenum target)
       /* OK, legal value */
       break;
    default:
+      /* XXX need to implement GL_TEXTURE_1D_ARRAY and GL_TEXTURE_2D_ARRAY */
       _mesa_error(ctx, GL_INVALID_ENUM, "glGenerateMipmapEXT(target)");
       return;
    }
@@ -2100,6 +2212,13 @@ _mesa_GenerateMipmapEXT(GLenum target)
       return;
    }
 
+   if (texObj->Target == GL_TEXTURE_CUBE_MAP &&
+       !_mesa_cube_complete(texObj)) {
+      _mesa_error(ctx, GL_INVALID_OPERATION,
+                  "glGenerateMipmap(incomplete cube map)");
+      return;
+   }
+
    _mesa_lock_texture(ctx, texObj);
    if (target == GL_TEXTURE_CUBE_MAP) {
       GLuint face;