i965: Clamp texture buffer size to GL_MAX_TEXTURE_BUFFER_SIZE.
[mesa.git] / src / mesa / state_tracker / st_texture.c
index 6a42789b824f259ea1a0420d95d13845428363c5..7b72ffd0501ba07ed6ab5b6af315c68b717c1b21 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
  * 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.
  * 
  **************************************************************************/
 
+#include <stdio.h>
+
 #include "st_context.h"
 #include "st_format.h"
 #include "st_texture.h"
 #include "st_cb_fbo.h"
-#include "st_inlines.h"
 #include "main/enums.h"
 
-#undef Elements  /* fix re-defined macro warning */
-
 #include "pipe/p_state.h"
 #include "pipe/p_context.h"
 #include "pipe/p_defines.h"
@@ -41,6 +40,7 @@
 #include "util/u_format.h"
 #include "util/u_rect.h"
 #include "util/u_math.h"
+#include "util/u_memory.h"
 
 
 #define DBG if(0) printf
@@ -60,20 +60,26 @@ st_texture_create(struct st_context *st,
                  GLuint width0,
                  GLuint height0,
                  GLuint depth0,
+                  GLuint layers,
+                  GLuint nr_samples,
                   GLuint bind )
 {
    struct pipe_resource pt, *newtex;
    struct pipe_screen *screen = st->pipe->screen;
 
-   assert(target <= PIPE_TEXTURE_CUBE);
+   assert(target < PIPE_MAX_TEXTURE_TYPES);
+   assert(width0 > 0);
+   assert(height0 > 0);
+   assert(depth0 > 0);
+   if (target == PIPE_TEXTURE_CUBE)
+      assert(layers == 6);
 
-   DBG("%s target %s format %s last_level %d\n", __FUNCTION__,
-       _mesa_lookup_enum_by_nr(target),
-       _mesa_lookup_enum_by_nr(format), last_level);
+   DBG("%s target %d format %s last_level %d\n", __func__,
+       (int) target, util_format_name(format), last_level);
 
    assert(format);
-   assert(screen->is_format_supported(screen, format, target, 
-                                      PIPE_BIND_SAMPLER_VIEW, 0));
+   assert(screen->is_format_supported(screen, format, target, 0,
+                                      PIPE_BIND_SAMPLER_VIEW));
 
    memset(&pt, 0, sizeof(pt));
    pt.target = target;
@@ -82,9 +88,12 @@ st_texture_create(struct st_context *st,
    pt.width0 = width0;
    pt.height0 = height0;
    pt.depth0 = depth0;
+   pt.array_size = layers;
    pt.usage = PIPE_USAGE_DEFAULT;
    pt.bind = bind;
-   pt.flags = 0;
+   /* only set this for OpenGL textures, not renderbuffers */
+   pt.flags = PIPE_RESOURCE_FLAG_TEXTURING_MORE_LIKELY;
+   pt.nr_samples = nr_samples;
 
    newtex = screen->resource_create(screen, &pt);
 
@@ -94,14 +103,107 @@ st_texture_create(struct st_context *st,
 }
 
 
+/**
+ * In OpenGL the number of 1D array texture layers is the "height" and
+ * the number of 2D array texture layers is the "depth".  In Gallium the
+ * number of layers in an array texture is a separate 'array_size' field.
+ * This function converts dimensions from the former to the later.
+ */
+void
+st_gl_texture_dims_to_pipe_dims(GLenum texture,
+                                GLuint widthIn,
+                                GLuint heightIn,
+                                GLuint depthIn,
+                                GLuint *widthOut,
+                                GLuint *heightOut,
+                                GLuint *depthOut,
+                                GLuint *layersOut)
+{
+   switch (texture) {
+   case GL_TEXTURE_1D:
+   case GL_PROXY_TEXTURE_1D:
+      assert(heightIn == 1);
+      assert(depthIn == 1);
+      *widthOut = widthIn;
+      *heightOut = 1;
+      *depthOut = 1;
+      *layersOut = 1;
+      break;
+   case GL_TEXTURE_1D_ARRAY:
+   case GL_PROXY_TEXTURE_1D_ARRAY:
+      assert(depthIn == 1);
+      *widthOut = widthIn;
+      *heightOut = 1;
+      *depthOut = 1;
+      *layersOut = heightIn;
+      break;
+   case GL_TEXTURE_2D:
+   case GL_PROXY_TEXTURE_2D:
+   case GL_TEXTURE_RECTANGLE:
+   case GL_PROXY_TEXTURE_RECTANGLE:
+   case GL_TEXTURE_EXTERNAL_OES:
+   case GL_PROXY_TEXTURE_2D_MULTISAMPLE:
+   case GL_TEXTURE_2D_MULTISAMPLE:
+      assert(depthIn == 1);
+      *widthOut = widthIn;
+      *heightOut = heightIn;
+      *depthOut = 1;
+      *layersOut = 1;
+      break;
+   case GL_TEXTURE_CUBE_MAP:
+   case GL_PROXY_TEXTURE_CUBE_MAP:
+   case GL_TEXTURE_CUBE_MAP_POSITIVE_X:
+   case GL_TEXTURE_CUBE_MAP_NEGATIVE_X:
+   case GL_TEXTURE_CUBE_MAP_POSITIVE_Y:
+   case GL_TEXTURE_CUBE_MAP_NEGATIVE_Y:
+   case GL_TEXTURE_CUBE_MAP_POSITIVE_Z:
+   case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z:
+      assert(depthIn == 1);
+      *widthOut = widthIn;
+      *heightOut = heightIn;
+      *depthOut = 1;
+      *layersOut = 6;
+      break;
+   case GL_TEXTURE_2D_ARRAY:
+   case GL_TEXTURE_2D_MULTISAMPLE_ARRAY:
+   case GL_PROXY_TEXTURE_2D_ARRAY:
+   case GL_PROXY_TEXTURE_2D_MULTISAMPLE_ARRAY:
+      *widthOut = widthIn;
+      *heightOut = heightIn;
+      *depthOut = 1;
+      *layersOut = depthIn;
+      break;
+   case GL_TEXTURE_CUBE_MAP_ARRAY:
+   case GL_PROXY_TEXTURE_CUBE_MAP_ARRAY:
+      *widthOut = widthIn;
+      *heightOut = heightIn;
+      *depthOut = 1;
+      *layersOut = util_align_npot(depthIn, 6);
+      break;
+   default:
+      assert(0 && "Unexpected texture in st_gl_texture_dims_to_pipe_dims()");
+      /* fall-through */
+   case GL_TEXTURE_3D:
+   case GL_PROXY_TEXTURE_3D:
+      *widthOut = widthIn;
+      *heightOut = heightIn;
+      *depthOut = depthIn;
+      *layersOut = 1;
+      break;
+   }
+}
+
+
 /**
  * Check if a texture image can be pulled into a unified mipmap texture.
  */
 GLboolean
-st_texture_match_image(const struct pipe_resource *pt,
-                       const struct gl_texture_image *image,
-                       GLuint face, GLuint level)
+st_texture_match_image(struct st_context *st,
+                       const struct pipe_resource *pt,
+                       const struct gl_texture_image *image)
 {
+   GLuint ptWidth, ptHeight, ptDepth, ptLayers;
+
    /* Images with borders are never pulled into mipmap textures. 
     */
    if (image->Border) 
@@ -109,15 +211,23 @@ st_texture_match_image(const struct pipe_resource *pt,
 
    /* Check if this image's format matches the established texture's format.
     */
-   if (st_mesa_format_to_pipe_format(image->TexFormat) != pt->format)
+   if (st_mesa_format_to_pipe_format(st, image->TexFormat) != pt->format)
       return GL_FALSE;
 
+   st_gl_texture_dims_to_pipe_dims(image->TexObject->Target,
+                                   image->Width, image->Height, image->Depth,
+                                   &ptWidth, &ptHeight, &ptDepth, &ptLayers);
+
    /* Test if this image's size matches what's expected in the
     * established texture.
     */
-   if (image->Width != u_minify(pt->width0, level) ||
-       image->Height != u_minify(pt->height0, level) ||
-       image->Depth != u_minify(pt->depth0, level))
+   if (ptWidth != u_minify(pt->width0, image->Level) ||
+       ptHeight != u_minify(pt->height0, image->Level) ||
+       ptDepth != u_minify(pt->depth0, image->Level) ||
+       ptLayers != pt->array_size)
+      return GL_FALSE;
+
+   if (image->Level > pt->last_level)
       return GL_FALSE;
 
    return GL_TRUE;
@@ -134,188 +244,178 @@ st_texture_match_image(const struct pipe_resource *pt,
  */
 GLubyte *
 st_texture_image_map(struct st_context *st, struct st_texture_image *stImage,
-                    GLuint zoffset, enum pipe_transfer_usage usage,
-                     GLuint x, GLuint y, GLuint w, GLuint h)
+                     enum pipe_transfer_usage usage,
+                     GLuint x, GLuint y, GLuint z,
+                     GLuint w, GLuint h, GLuint d,
+                     struct pipe_transfer **transfer)
 {
-   struct pipe_context *pipe = st->pipe;
-   struct pipe_resource *pt = stImage->pt;
+   struct st_texture_object *stObj =
+      st_texture_object(stImage->base.TexObject);
+   GLuint level;
+   void *map;
 
-   DBG("%s \n", __FUNCTION__);
+   DBG("%s \n", __func__);
 
-   stImage->transfer = st_no_flush_get_tex_transfer(st, pt, stImage->face,
-                                                   stImage->level, zoffset,
-                                                   usage, x, y, w, h);
+   if (!stImage->pt)
+      return NULL;
 
-   if (stImage->transfer)
-      return pipe_transfer_map(pipe, stImage->transfer);
+   if (stObj->pt != stImage->pt)
+      level = 0;
    else
-      return NULL;
+      level = stImage->base.Level;
+
+   if (stObj->base.Immutable) {
+      level += stObj->base.MinLevel;
+      z += stObj->base.MinLayer;
+      if (stObj->pt->array_size > 1)
+         d = MIN2(d, stObj->base.NumLayers);
+   }
+
+   z += stImage->base.Face;
+
+   map = pipe_transfer_map_3d(st->pipe, stImage->pt, level, usage,
+                              x, y, z, w, h, d, transfer);
+   if (map) {
+      /* Enlarge the transfer array if it's not large enough. */
+      if (z >= stImage->num_transfers) {
+         unsigned new_size = z + 1;
+
+         stImage->transfer = realloc(stImage->transfer,
+                     new_size * sizeof(struct st_texture_image_transfer));
+         memset(&stImage->transfer[stImage->num_transfers], 0,
+                (new_size - stImage->num_transfers) *
+                sizeof(struct st_texture_image_transfer));
+         stImage->num_transfers = new_size;
+      }
+
+      assert(!stImage->transfer[z].transfer);
+      stImage->transfer[z].transfer = *transfer;
+   }
+   return map;
 }
 
 
 void
 st_texture_image_unmap(struct st_context *st,
-                       struct st_texture_image *stImage)
+                       struct st_texture_image *stImage, unsigned slice)
 {
    struct pipe_context *pipe = st->pipe;
+   struct st_texture_object *stObj =
+      st_texture_object(stImage->base.TexObject);
+   struct pipe_transfer **transfer;
 
-   DBG("%s\n", __FUNCTION__);
+   if (stObj->base.Immutable)
+      slice += stObj->base.MinLayer;
+   transfer = &stImage->transfer[slice + stImage->base.Face].transfer;
 
-   pipe_transfer_unmap(pipe, stImage->transfer);
+   DBG("%s\n", __func__);
 
-   pipe->transfer_destroy(pipe, stImage->transfer);
+   pipe_transfer_unmap(pipe, *transfer);
+   *transfer = NULL;
 }
 
-
-
 /**
- * Upload data to a rectangular sub-region.  Lots of choices how to do this:
- *
- * - memcpy by span to current destination
- * - upload data as new buffer and blit
- *
- * Currently always memcpy.
+ * For debug only: get/print center pixel in the src resource.
  */
 static void
-st_surface_data(struct pipe_context *pipe,
-               struct pipe_transfer *dst,
-               unsigned dstx, unsigned dsty,
-               const void *src, unsigned src_stride,
-               unsigned srcx, unsigned srcy, unsigned width, unsigned height)
-{
-   void *map = pipe_transfer_map(pipe, dst);
-
-   assert(dst->resource);
-   util_copy_rect(map,
-                  dst->resource->format,
-                  dst->stride,
-                  dstx, dsty, 
-                  width, height, 
-                  src, src_stride, 
-                  srcx, srcy);
-
-   pipe_transfer_unmap(pipe, dst);
-}
-
-
-/* Upload data for a particular image.
- */
-void
-st_texture_image_data(struct st_context *st,
-                      struct pipe_resource *dst,
-                      GLuint face,
-                      GLuint level,
-                      void *src,
-                      GLuint src_row_stride, GLuint src_image_stride)
+print_center_pixel(struct pipe_context *pipe, struct pipe_resource *src)
 {
-   struct pipe_context *pipe = st->pipe;
-   GLuint depth = u_minify(dst->depth0, level);
-   GLuint i;
-   const GLubyte *srcUB = src;
-   struct pipe_transfer *dst_transfer;
+   struct pipe_transfer *xfer;
+   struct pipe_box region;
+   ubyte *map;
 
-   DBG("%s\n", __FUNCTION__);
+   region.x = src->width0 / 2;
+   region.y = src->height0 / 2;
+   region.z = 0;
+   region.width = 1;
+   region.height = 1;
+   region.depth = 1;
 
-   for (i = 0; i < depth; i++) {
-      dst_transfer = st_no_flush_get_tex_transfer(st, dst, face, level, i,
-                                                 PIPE_TRANSFER_WRITE, 0, 0,
-                                                 u_minify(dst->width0, level),
-                                                  u_minify(dst->height0, level));
+   map = pipe->transfer_map(pipe, src, 0, PIPE_TRANSFER_READ, &region, &xfer);
 
-      st_surface_data(pipe, dst_transfer,
-                     0, 0,                             /* dstx, dsty */
-                     srcUB,
-                     src_row_stride,
-                     0, 0,                             /* source x, y */
-                     u_minify(dst->width0, level),
-                      u_minify(dst->height0, level));      /* width, height */
+   printf("center pixel: %d %d %d %d\n", map[0], map[1], map[2], map[3]);
 
-      pipe->transfer_destroy(pipe, dst_transfer);
-
-      srcUB += src_image_stride;
-   }
+   pipe->transfer_unmap(pipe, xfer);
 }
 
 
-/* Copy mipmap image between textures
+/**
+ * Copy the image at level=0 in 'src' to the 'dst' resource at 'dstLevel'.
+ * This is used to copy mipmap images from one texture buffer to another.
+ * This typically happens when our initial guess at the total texture size
+ * is incorrect (see the guess_and_alloc_texture() function).
  */
 void
 st_texture_image_copy(struct pipe_context *pipe,
                       struct pipe_resource *dst, GLuint dstLevel,
-                      struct pipe_resource *src,
+                      struct pipe_resource *src, GLuint srcLevel,
                       GLuint face)
 {
-   struct pipe_screen *screen = pipe->screen;
-   GLuint width = u_minify(dst->width0, dstLevel); 
-   GLuint height = u_minify(dst->height0, dstLevel); 
-   GLuint depth = u_minify(dst->depth0, dstLevel); 
-   struct pipe_surface *src_surface;
-   struct pipe_surface *dst_surface;
+   GLuint width = u_minify(dst->width0, dstLevel);
+   GLuint height = u_minify(dst->height0, dstLevel);
+   GLuint depth = u_minify(dst->depth0, dstLevel);
+   struct pipe_box src_box;
    GLuint i;
 
-   assert(src->width0 == dst->width0);
-   assert(src->height0 == dst->height0);
-
-   for (i = 0; i < depth; i++) {
-      GLuint srcLevel;
-
-      /* find src texture level of needed size */
-      for (srcLevel = 0; srcLevel <= src->last_level; srcLevel++) {
-         if (u_minify(src->width0, srcLevel) == width &&
-             u_minify(src->height0, srcLevel) == height) {
-            break;
-         }
-      }
-      assert(u_minify(src->width0, srcLevel) == width);
-      assert(u_minify(src->height0, srcLevel) == height);
-
-#if 0
-      {
-         src_surface = screen->get_tex_surface(screen, src, face, srcLevel, i,
-                                               PIPE_BUFFER_USAGE_CPU_READ);
-         ubyte *map = screen->surface_map(screen, src_surface, PIPE_BUFFER_USAGE_CPU_READ);
-         map += src_surface->width * src_surface->height * 4 / 2;
-         printf("%s center pixel: %d %d %d %d (pt %p[%d] -> %p[%d])\n",
-                __FUNCTION__,
-                map[0], map[1], map[2], map[3],
-                src, srcLevel, dst, dstLevel);
-
-         screen->surface_unmap(screen, src_surface);
-         pipe_surface_reference(&src_surface, NULL);
-      }
-#endif
+   if (u_minify(src->width0, srcLevel) != width ||
+       u_minify(src->height0, srcLevel) != height ||
+       u_minify(src->depth0, srcLevel) != depth) {
+      /* The source image size doesn't match the destination image size.
+       * This can happen in some degenerate situations such as rendering to a
+       * cube map face which was set up with mismatched texture sizes.
+       */
+      return;
+   }
 
-      dst_surface = screen->get_tex_surface(screen, dst, face, dstLevel, i,
-                                            PIPE_BIND_BLIT_DESTINATION);
+   src_box.x = 0;
+   src_box.y = 0;
+   src_box.width = width;
+   src_box.height = height;
+   src_box.depth = 1;
+
+   if (src->target == PIPE_TEXTURE_1D_ARRAY ||
+       src->target == PIPE_TEXTURE_2D_ARRAY ||
+       src->target == PIPE_TEXTURE_CUBE_ARRAY) {
+      face = 0;
+      depth = src->array_size;
+   }
 
-      src_surface = screen->get_tex_surface(screen, src, face, srcLevel, i,
-                                            PIPE_BIND_BLIT_SOURCE);
+   /* Loop over 3D image slices */
+   /* could (and probably should) use "true" 3d box here -
+      but drivers can't quite handle it yet */
+   for (i = face; i < face + depth; i++) {
+      src_box.z = i;
 
-      pipe->surface_copy(pipe,
-                         dst_surface,
-                         0, 0, /* destX, Y */
-                         src_surface,
-                         0, 0, /* srcX, Y */
-                         width, height);
+      if (0)  {
+         print_center_pixel(pipe, src);
+      }
 
-      pipe_surface_reference(&src_surface, NULL);
-      pipe_surface_reference(&dst_surface, NULL);
+      pipe->resource_copy_region(pipe,
+                                 dst,
+                                 dstLevel,
+                                 0, 0, i,/* destX, Y, Z */
+                                 src,
+                                 srcLevel,
+                                 &src_box);
    }
 }
 
 
-void
-st_teximage_flush_before_map(struct st_context *st,
-                            struct pipe_resource *pt,
-                            unsigned int face,
-                            unsigned int level,
-                            enum pipe_transfer_usage usage)
+struct pipe_resource *
+st_create_color_map_texture(struct gl_context *ctx)
 {
-   struct pipe_context *pipe = st->pipe;
-   unsigned referenced =
-      pipe->is_resource_referenced(pipe, pt, face, level);
-
-   if (referenced && ((referenced & PIPE_REFERENCED_FOR_WRITE) ||
-                     (usage & PIPE_TRANSFER_WRITE)))
-      st->pipe->flush(st->pipe, PIPE_FLUSH_RENDER_CACHE, NULL);
+   struct st_context *st = st_context(ctx);
+   struct pipe_resource *pt;
+   enum pipe_format format;
+   const uint texSize = 256; /* simple, and usually perfect */
+
+   /* find an RGBA texture format */
+   format = st_choose_format(st, GL_RGBA, GL_NONE, GL_NONE,
+                             PIPE_TEXTURE_2D, 0, PIPE_BIND_SAMPLER_VIEW,
+                             FALSE);
+
+   /* create texture for color map/table */
+   pt = st_texture_create(st, PIPE_TEXTURE_2D, format, 0,
+                          texSize, texSize, 1, 1, 0, PIPE_BIND_SAMPLER_VIEW);
+   return pt;
 }