replace _mesa_logbase2 with util_logbase2
[mesa.git] / src / mesa / state_tracker / st_atom_texture.c
index e8941da8d79f44a0ebd9b5f4eee015e220a33f0a..5a0f91ccb1403ee50a90cc3c88775034e9abe17b 100644 (file)
@@ -1,6 +1,6 @@
 /**************************************************************************
  *
- * Copyright 2007 Tungsten Graphics, Inc., Cedar Park, Texas.
+ * Copyright 2007 VMware, Inc.
  * All Rights Reserved.
  *
  * Permission is hereby granted, free of charge, to any person obtaining a
@@ -18,7 +18,7 @@
  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
- * IN NO EVENT SHALL TUNGSTEN GRAPHICS AND/OR ITS SUPPLIERS BE LIABLE FOR
+ * IN NO EVENT SHALL VMWARE AND/OR ITS SUPPLIERS BE LIABLE FOR
  * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
  * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
  * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 
  /*
   * Authors:
-  *   Keith Whitwell <keith@tungstengraphics.com>
+  *   Keith Whitwell <keithw@vmware.com>
   *   Brian Paul
   */
 
 
+#include "main/context.h"
 #include "main/macros.h"
 #include "main/mtypes.h"
 #include "main/samplerobj.h"
+#include "main/teximage.h"
+#include "main/texobj.h"
 #include "program/prog_instruction.h"
 
 #include "st_context.h"
 #include "st_atom.h"
+#include "st_sampler_view.h"
 #include "st_texture.h"
 #include "st_format.h"
 #include "st_cb_texture.h"
 #include "pipe/p_context.h"
-#include "util/u_format.h"
+#include "util/format/u_format.h"
 #include "util/u_inlines.h"
 #include "cso_cache/cso_context.h"
 
 
 /**
- * Combine depth texture mode with "swizzle" so that depth mode swizzling
- * takes place before texture swizzling, and return the resulting swizzle.
- * If the format is not a depth format, return "swizzle" unchanged.
- *
- * \param format     PIPE_FORMAT_*.
- * \param swizzle    Texture swizzle, a bitmask computed using MAKE_SWIZZLE4.
- * \param depthmode  One of GL_LUMINANCE, GL_INTENSITY, GL_ALPHA, GL_RED.
+ * Get a pipe_sampler_view object from a texture unit.
  */
-static GLuint
-apply_depthmode(enum pipe_format format, GLuint swizzle, GLenum depthmode)
+void
+st_update_single_texture(struct st_context *st,
+                         struct pipe_sampler_view **sampler_view,
+                         GLuint texUnit, bool glsl130_or_later,
+                         bool ignore_srgb_decode)
 {
-   const struct util_format_description *desc =
-         util_format_description(format);
-   unsigned char swiz[4];
-   unsigned i;
-
-   if (desc->colorspace != UTIL_FORMAT_COLORSPACE_ZS ||
-       desc->swizzle[0] == UTIL_FORMAT_SWIZZLE_NONE) {
-      /* Not a depth format. */
-      return swizzle;
-   }
-
-   for (i = 0; i < 4; i++)
-      swiz[i] = GET_SWZ(swizzle, i);
-
-   switch (depthmode) {
-      case GL_LUMINANCE:
-         /* Rewrite reads from W to ONE, and reads from XYZ to XXX. */
-         for (i = 0; i < 4; i++)
-            if (swiz[i] == SWIZZLE_W)
-               swiz[i] = SWIZZLE_ONE;
-            else if (swiz[i] < SWIZZLE_W)
-               swiz[i] = SWIZZLE_X;
-         break;
-
-      case GL_INTENSITY:
-         /* Rewrite reads from XYZW to XXXX. */
-         for (i = 0; i < 4; i++)
-            if (swiz[i] <= SWIZZLE_W)
-               swiz[i] = SWIZZLE_X;
-         break;
-
-      case GL_ALPHA:
-         /* Rewrite reads from W to X, and reads from XYZ to 000. */
-         for (i = 0; i < 4; i++)
-            if (swiz[i] == SWIZZLE_W)
-               swiz[i] = SWIZZLE_X;
-            else if (swiz[i] < SWIZZLE_W)
-               swiz[i] = SWIZZLE_ZERO;
-         break;
-      case GL_RED:
-        /* Rewrite reads W to 1, XYZ to X00 */
-        for (i = 0; i < 4; i++)
-           if (swiz[i] == SWIZZLE_W)
-              swiz[i] = SWIZZLE_ONE;
-           else if (swiz[i] == SWIZZLE_Y || swiz[i] == SWIZZLE_Z)
-              swiz[i] = SWIZZLE_ZERO;
-        break;
-   }
-
-   return MAKE_SWIZZLE4(swiz[0], swiz[1], swiz[2], swiz[3]);
-}
-
-
-/**
- * Return TRUE if the swizzling described by "swizzle" and
- * "depthmode" (for depth textures only) is different from the swizzling
- * set in the given sampler view.
- *
- * \param sv         A sampler view.
- * \param swizzle    Texture swizzle, a bitmask computed using MAKE_SWIZZLE4.
- * \param depthmode  One of GL_LUMINANCE, GL_INTENSITY, GL_ALPHA.
- */
-static boolean
-check_sampler_swizzle(struct pipe_sampler_view *sv,
-                      GLuint swizzle, GLenum depthmode)
-{
-   swizzle = apply_depthmode(sv->texture->format, swizzle, depthmode);
-
-   if ((sv->swizzle_r != GET_SWZ(swizzle, 0)) ||
-       (sv->swizzle_g != GET_SWZ(swizzle, 1)) ||
-       (sv->swizzle_b != GET_SWZ(swizzle, 2)) ||
-       (sv->swizzle_a != GET_SWZ(swizzle, 3)))
-      return TRUE;
-   return FALSE;
-}
-
-
-static INLINE struct pipe_sampler_view *
-st_create_texture_sampler_view_from_stobj(struct pipe_context *pipe,
-                                         struct st_texture_object *stObj,
-                                          const struct gl_sampler_object *samp,
-                                         enum pipe_format format)
-{
-   struct pipe_sampler_view templ;
-   GLuint swizzle = apply_depthmode(stObj->pt->format,
-                                    stObj->base._Swizzle,
-                                    samp->DepthMode);
-
-   u_sampler_view_default_template(&templ,
-                                   stObj->pt,
-                                   format);
-   templ.u.tex.first_level = stObj->base.BaseLevel;
-
-   if (swizzle != SWIZZLE_NOOP) {
-      templ.swizzle_r = GET_SWZ(swizzle, 0);
-      templ.swizzle_g = GET_SWZ(swizzle, 1);
-      templ.swizzle_b = GET_SWZ(swizzle, 2);
-      templ.swizzle_a = GET_SWZ(swizzle, 3);
-   }
-
-   return pipe->create_sampler_view(pipe, stObj->pt, &templ);
-}
-
-
-static INLINE struct pipe_sampler_view *
-st_get_texture_sampler_view_from_stobj(struct st_texture_object *stObj,
-                                      struct pipe_context *pipe,
-                                       const struct gl_sampler_object *samp,
-                                      enum pipe_format format)
-{
-   if (!stObj || !stObj->pt) {
-      return NULL;
-   }
-
-   if (!stObj->sampler_view) {
-      stObj->sampler_view =
-         st_create_texture_sampler_view_from_stobj(pipe, stObj, samp, format);
-   }
-
-   return stObj->sampler_view;
-}
-
-
-static GLboolean
-update_single_texture(struct st_context *st,
-                      struct pipe_sampler_view **sampler_view,
-                     GLuint texUnit)
-{
-   struct pipe_context *pipe = st->pipe;
    struct gl_context *ctx = st->ctx;
    const struct gl_sampler_object *samp;
    struct gl_texture_object *texObj;
    struct st_texture_object *stObj;
-   enum pipe_format st_view_format;
-   GLboolean retval;
 
    samp = _mesa_get_samplerobj(ctx, texUnit);
 
    texObj = ctx->Texture.Unit[texUnit]._Current;
+   assert(texObj);
 
-   if (!texObj) {
-      texObj = st_get_default_texture(st);
-      samp = &texObj->Sampler;
-   }
    stObj = st_texture_object(texObj);
 
-   retval = st_finalize_texture(ctx, st->pipe, texObj);
-   if (!retval) {
-      /* out of mem */
-      return GL_FALSE;
+   if (unlikely(texObj->Target == GL_TEXTURE_BUFFER)) {
+      *sampler_view = st_get_buffer_sampler_view_from_stobj(st, stObj);
+      return;
    }
 
-   /* Determine the format of the texture sampler view */
-   st_view_format = stObj->pt->format;
-   {
-      const struct st_texture_image *firstImage =
-        st_texture_image(stObj->base.Image[0][stObj->base.BaseLevel]);
-      const gl_format texFormat = firstImage->base.TexFormat;
-      enum pipe_format firstImageFormat =
-        st_mesa_format_to_pipe_format(texFormat);
-
-      if ((samp->sRGBDecode == GL_SKIP_DECODE_EXT) &&
-         (_mesa_get_format_color_encoding(texFormat) == GL_SRGB)) {
-         /* Don't do sRGB->RGB conversion.  Interpret the texture data as
-          * linear values.
-          */
-        const gl_format linearFormat =
-           _mesa_get_srgb_format_linear(texFormat);
-        firstImageFormat = st_mesa_format_to_pipe_format(linearFormat);
-      }
-
-      if (firstImageFormat != stObj->pt->format)
-        st_view_format = firstImageFormat;
+   if (!st_finalize_texture(ctx, st->pipe, texObj, 0) ||
+       !stObj->pt) {
+      /* out of mem */
+      *sampler_view = NULL;
+      return;
    }
 
-   /* if sampler view has changed dereference it */
-   if (stObj->sampler_view) {
-      if (check_sampler_swizzle(stObj->sampler_view,
-                               stObj->base._Swizzle,
-                               samp->DepthMode) ||
-         (st_view_format != stObj->sampler_view->format) ||
-         stObj->base.BaseLevel != stObj->sampler_view->u.tex.first_level) {
-        pipe_sampler_view_reference(&stObj->sampler_view, NULL);
-      }
-   }
+   if (texObj->TargetIndex == TEXTURE_EXTERNAL_INDEX &&
+       stObj->pt->screen->resource_changed)
+         stObj->pt->screen->resource_changed(stObj->pt->screen, stObj->pt);
 
-   *sampler_view = st_get_texture_sampler_view_from_stobj(stObj, pipe,
-                                                         samp,
-                                                         st_view_format);
-   return GL_TRUE;
+   *sampler_view =
+      st_get_texture_sampler_view_from_stobj(st, stObj, samp,
+                                             glsl130_or_later,
+                                             ignore_srgb_decode);
 }
 
 
+
 static void
-update_vertex_textures(struct st_context *st)
+update_textures(struct st_context *st,
+                enum pipe_shader_type shader_stage,
+                const struct gl_program *prog,
+                struct pipe_sampler_view **sampler_views)
 {
-   const struct gl_context *ctx = st->ctx;
-   struct gl_vertex_program *vprog = ctx->VertexProgram._Current;
-   GLuint su;
-
-   if (!vprog->Base.SamplersUsed && st->state.num_vertex_textures == 0)
+   const GLuint old_max = st->state.num_sampler_views[shader_stage];
+   GLbitfield samplers_used = prog->SamplersUsed;
+   GLbitfield texel_fetch_samplers = prog->info.textures_used_by_txf;
+   GLbitfield free_slots = ~prog->SamplersUsed;
+   GLbitfield external_samplers_used = prog->ExternalSamplersUsed;
+   GLuint unit;
+
+   if (samplers_used == 0x0 && old_max == 0)
       return;
 
-   st->state.num_vertex_textures = 0;
+   unsigned num_textures = 0;
+
+   /* prog->sh.data is NULL if it's ARB_fragment_program */
+   bool glsl130 = (prog->sh.data ? prog->sh.data->Version : 0) >= 130;
 
    /* loop over sampler units (aka tex image units) */
-   for (su = 0; su < ctx->Const.MaxTextureImageUnits; su++) {
+   for (unit = 0; samplers_used || unit < old_max;
+        unit++, samplers_used >>= 1, texel_fetch_samplers >>= 1) {
       struct pipe_sampler_view *sampler_view = NULL;
-      if (vprog->Base.SamplersUsed & (1 << su)) {
-         GLboolean retval;
-         GLuint texUnit;
 
-         texUnit = vprog->Base.SamplerUnits[su];
+      if (samplers_used & 1) {
+         const GLuint texUnit = prog->SamplerUnits[unit];
+
+         /* The EXT_texture_sRGB_decode extension says:
+          *
+          *    "The conversion of sRGB color space components to linear color
+          *     space is always performed if the texel lookup function is one
+          *     of the texelFetch builtin functions.
+          *
+          *     Otherwise, if the texel lookup function is one of the texture
+          *     builtin functions or one of the texture gather functions, the
+          *     conversion of sRGB color space components to linear color space
+          *     is controlled by the TEXTURE_SRGB_DECODE_EXT parameter.
+          *
+          *     If the TEXTURE_SRGB_DECODE_EXT parameter is DECODE_EXT, the
+          *     conversion of sRGB color space components to linear color space
+          *     is performed.
+          *
+          *     If the TEXTURE_SRGB_DECODE_EXT parameter is SKIP_DECODE_EXT,
+          *     the value is returned without decoding. However, if the texture
+          *     is also [statically] accessed with a texelFetch function, then
+          *     the result of texture builtin functions and/or texture gather
+          *     functions may be returned with decoding or without decoding."
+          *
+          * Note: the "statically" will be added to the language per
+          *       https://cvs.khronos.org/bugzilla/show_bug.cgi?id=14934
+          *
+          * So we simply ignore the setting entirely for samplers that are
+          * (statically) accessed with a texelFetch function.
+          */
+         st_update_single_texture(st, &sampler_view, texUnit, glsl130,
+                                  texel_fetch_samplers & 1);
+         num_textures = unit + 1;
+      }
 
-         retval = update_single_texture(st, &sampler_view, texUnit);
-         if (retval == GL_FALSE)
-            continue;
+      pipe_sampler_view_reference(&(sampler_views[unit]), sampler_view);
+   }
 
-         st->state.num_vertex_textures = su + 1;
+   /* For any external samplers with multiplaner YUV, stuff the additional
+    * sampler views we need at the end.
+    *
+    * Trying to cache the sampler view in the stObj looks painful, so just
+    * re-create the sampler view for the extra planes each time.  Main use
+    * case is video playback (ie. fps games wouldn't be using this) so I
+    * guess no point to try to optimize this feature.
+    */
+   while (unlikely(external_samplers_used)) {
+      GLuint unit = u_bit_scan(&external_samplers_used);
+      GLuint extra = 0;
+      struct st_texture_object *stObj =
+            st_get_texture_object(st->ctx, prog, unit);
+      struct pipe_sampler_view tmpl;
+
+      if (!stObj)
+         continue;
+
+      /* use original view as template: */
+      tmpl = *sampler_views[unit];
+
+      /* if resource format matches then YUV wasn't lowered */
+      if (st_get_view_format(stObj) == stObj->pt->format)
+         continue;
+
+      switch (st_get_view_format(stObj)) {
+      case PIPE_FORMAT_NV12:
+         /* we need one additional R8G8 view: */
+         tmpl.format = PIPE_FORMAT_RG88_UNORM;
+         tmpl.swizzle_g = PIPE_SWIZZLE_Y;   /* tmpl from Y plane is R8 */
+         extra = u_bit_scan(&free_slots);
+         sampler_views[extra] =
+               st->pipe->create_sampler_view(st->pipe, stObj->pt->next, &tmpl);
+         break;
+      case PIPE_FORMAT_P010:
+      case PIPE_FORMAT_P016:
+         /* we need one additional R16G16 view: */
+         tmpl.format = PIPE_FORMAT_RG1616_UNORM;
+         tmpl.swizzle_g = PIPE_SWIZZLE_Y;   /* tmpl from Y plane is R16 */
+         extra = u_bit_scan(&free_slots);
+         sampler_views[extra] =
+               st->pipe->create_sampler_view(st->pipe, stObj->pt->next, &tmpl);
+         break;
+      case PIPE_FORMAT_IYUV:
+         /* we need two additional R8 views: */
+         tmpl.format = PIPE_FORMAT_R8_UNORM;
+         extra = u_bit_scan(&free_slots);
+         sampler_views[extra] =
+               st->pipe->create_sampler_view(st->pipe, stObj->pt->next, &tmpl);
+         extra = u_bit_scan(&free_slots);
+         sampler_views[extra] =
+               st->pipe->create_sampler_view(st->pipe, stObj->pt->next->next, &tmpl);
+         break;
+      case PIPE_FORMAT_YUYV:
+         /* we need one additional BGRA8888 view: */
+         tmpl.format = PIPE_FORMAT_BGRA8888_UNORM;
+         tmpl.swizzle_b = PIPE_SWIZZLE_Z;
+         tmpl.swizzle_a = PIPE_SWIZZLE_W;
+         extra = u_bit_scan(&free_slots);
+         sampler_views[extra] =
+               st->pipe->create_sampler_view(st->pipe, stObj->pt->next, &tmpl);
+         break;
+      case PIPE_FORMAT_UYVY:
+         /* we need one additional RGBA8888 view: */
+         tmpl.format = PIPE_FORMAT_RGBA8888_UNORM;
+         tmpl.swizzle_b = PIPE_SWIZZLE_Z;
+         tmpl.swizzle_a = PIPE_SWIZZLE_W;
+         extra = u_bit_scan(&free_slots);
+         sampler_views[extra] =
+               st->pipe->create_sampler_view(st->pipe, stObj->pt->next, &tmpl);
+         break;
+      default:
+         break;
       }
-      pipe_sampler_view_reference(&st->state.sampler_vertex_views[su],
-                                  sampler_view);
-   }
 
-   if (ctx->Const.MaxVertexTextureImageUnits > 0) {
-      GLuint numUnits = MIN2(st->state.num_vertex_textures,
-                             ctx->Const.MaxVertexTextureImageUnits);
-      cso_set_vertex_sampler_views(st->cso_context,
-                                   numUnits,
-                                   st->state.sampler_vertex_views);
+      num_textures = MAX2(num_textures, extra + 1);
    }
-}
 
+   cso_set_sampler_views(st->cso_context,
+                         shader_stage,
+                         num_textures,
+                         sampler_views);
+   st->state.num_sampler_views[shader_stage] = num_textures;
+}
 
+/* Same as update_textures, but don't store the views in st_context. */
 static void
-update_fragment_textures(struct st_context *st)
+update_textures_local(struct st_context *st,
+                      enum pipe_shader_type shader_stage,
+                      const struct gl_program *prog)
 {
-   const struct gl_context *ctx = st->ctx;
-   struct gl_fragment_program *fprog = ctx->FragmentProgram._Current;
-   GLuint su;
-   int old_max = st->state.num_textures;
-   GLbitfield samplers_used = fprog->Base.SamplersUsed;
-
-   st->state.num_textures = 0;
+   struct pipe_sampler_view *local_views[PIPE_MAX_SAMPLERS] = {0};
 
-   /* loop over sampler units (aka tex image units) */
-   for (su = 0; su < ctx->Const.MaxTextureImageUnits; su++, samplers_used >>= 1) {
-      struct pipe_sampler_view *sampler_view = NULL;
+   update_textures(st, shader_stage, prog, local_views);
 
-      if (samplers_used & 1) {
-         GLboolean retval;
-         GLuint texUnit;
+   unsigned num = st->state.num_sampler_views[shader_stage];
+   for (unsigned i = 0; i < num; i++)
+      pipe_sampler_view_reference(&local_views[i], NULL);
+}
 
-         texUnit = fprog->Base.SamplerUnits[su];
+void
+st_update_vertex_textures(struct st_context *st)
+{
+   const struct gl_context *ctx = st->ctx;
 
-         retval = update_single_texture(st, &sampler_view, texUnit);
-         if (retval == GL_FALSE)
-            continue;
+   if (ctx->Const.Program[MESA_SHADER_VERTEX].MaxTextureImageUnits > 0) {
+      update_textures(st,
+                      PIPE_SHADER_VERTEX,
+                      ctx->VertexProgram._Current,
+                      st->state.vert_sampler_views);
+   }
+}
 
-         st->state.num_textures = su + 1;
-      } else if (samplers_used == 0 && su >= old_max) {
-         /* if we've reset all the old views and we have no more new ones */
-         break;
-      }
 
-      pipe_sampler_view_reference(&st->state.sampler_views[su], sampler_view);
-   }
+void
+st_update_fragment_textures(struct st_context *st)
+{
+   const struct gl_context *ctx = st->ctx;
 
-   cso_set_fragment_sampler_views(st->cso_context,
-                                  st->state.num_textures,
-                                  st->state.sampler_views);
+   update_textures(st,
+                   PIPE_SHADER_FRAGMENT,
+                   ctx->FragmentProgram._Current,
+                   st->state.frag_sampler_views);
 }
 
 
-const struct st_tracked_state st_update_texture = {
-   "st_update_texture",                                        /* name */
-   {                                                   /* dirty */
-      _NEW_TEXTURE,                                    /* mesa */
-      ST_NEW_FRAGMENT_PROGRAM,                         /* st */
-   },
-   update_fragment_textures                            /* update */
-};
+void
+st_update_geometry_textures(struct st_context *st)
+{
+   const struct gl_context *ctx = st->ctx;
+
+   if (ctx->GeometryProgram._Current) {
+      update_textures_local(st, PIPE_SHADER_GEOMETRY,
+                            ctx->GeometryProgram._Current);
+   }
+}
 
 
-const struct st_tracked_state st_update_vertex_texture = {
-   "st_update_vertex_texture",                         /* name */
-   {                                                   /* dirty */
-      _NEW_TEXTURE,                                    /* mesa */
-      ST_NEW_VERTEX_PROGRAM,                           /* st */
-   },
-   update_vertex_textures                              /* update */
-};
+void
+st_update_tessctrl_textures(struct st_context *st)
+{
+   const struct gl_context *ctx = st->ctx;
 
+   if (ctx->TessCtrlProgram._Current) {
+      update_textures_local(st, PIPE_SHADER_TESS_CTRL,
+                            ctx->TessCtrlProgram._Current);
+   }
+}
 
 
-static void
-finalize_textures(struct st_context *st)
+void
+st_update_tesseval_textures(struct st_context *st)
 {
-   struct gl_context *ctx = st->ctx;
-   struct gl_fragment_program *fprog = ctx->FragmentProgram._Current;
-   const GLboolean prev_missing_textures = st->missing_textures;
-   GLuint su;
-
-   st->missing_textures = GL_FALSE;
-
-   for (su = 0; su < ctx->Const.MaxTextureCoordUnits; su++) {
-      if (fprog->Base.SamplersUsed & (1 << su)) {
-         const GLuint texUnit = fprog->Base.SamplerUnits[su];
-         struct gl_texture_object *texObj
-            = ctx->Texture.Unit[texUnit]._Current;
-
-         if (texObj) {
-            GLboolean retval;
-
-            retval = st_finalize_texture(ctx, st->pipe, texObj);
-            if (!retval) {
-               /* out of mem */
-               st->missing_textures = GL_TRUE;
-               continue;
-            }
-         }
-      }
-   }
+   const struct gl_context *ctx = st->ctx;
 
-   if (prev_missing_textures != st->missing_textures)
-      st->dirty.st |= ST_NEW_FRAGMENT_PROGRAM;
+   if (ctx->TessEvalProgram._Current) {
+      update_textures_local(st, PIPE_SHADER_TESS_EVAL,
+                            ctx->TessEvalProgram._Current);
+   }
 }
 
 
-const struct st_tracked_state st_finalize_textures = {
-   "st_finalize_textures",             /* name */
-   {                                   /* dirty */
-      _NEW_TEXTURE,                    /* mesa */
-      0,                               /* st */
-   },
-   finalize_textures                   /* update */
-};
+void
+st_update_compute_textures(struct st_context *st)
+{
+   const struct gl_context *ctx = st->ctx;
+
+   if (ctx->ComputeProgram._Current) {
+      update_textures_local(st, PIPE_SHADER_COMPUTE,
+                            ctx->ComputeProgram._Current);
+   }
+}