Similar to glReadPixels, using the GPU to blit back into the client's
buffer is preferrable to using a coherent mmaping (but not manual
detiling for several reasons).

Signed-off-by: Chris Wilson <ch...@chris-wilson.co.uk>
---
 src/mesa/drivers/dri/i965/intel_tex_image.c | 368 ++++++++++++++++++++++++++--
 1 file changed, 353 insertions(+), 15 deletions(-)

diff --git a/src/mesa/drivers/dri/i965/intel_tex_image.c 
b/src/mesa/drivers/dri/i965/intel_tex_image.c
index a1d14ed02b..334e323d74 100644
--- a/src/mesa/drivers/dri/i965/intel_tex_image.c
+++ b/src/mesa/drivers/dri/i965/intel_tex_image.c
@@ -8,6 +8,7 @@
 #include "main/glformats.h"
 #include "main/image.h"
 #include "main/pbo.h"
+#include "main/readpix.h"
 #include "main/renderbuffer.h"
 #include "main/texcompress.h"
 #include "main/texgetimage.h"
@@ -25,6 +26,7 @@
 #include "intel_fbo.h"
 #include "intel_image.h"
 #include "intel_tiled_memcpy.h"
+#include "brw_blorp.h"
 #include "brw_context.h"
 
 #define FILE_DEBUG_FLAG DEBUG_TEXTURE
@@ -493,6 +495,343 @@ intel_gettexsubimage_tiled_memcpy(struct gl_context *ctx,
    return true;
 }
 
+#if 0
+static bool
+intel_gettexsubimage_userptr(struct gl_context *ctx,
+                             GLint x, GLint y, GLint z,
+                             GLsizei width, GLsizei height, GLsizei depth,
+                             GLenum format, GLenum type,
+                             GLvoid * pixels,
+                             struct gl_texture_image *texImage)
+{
+   struct brw_context *brw = brw_context(ctx);
+   struct intel_texture_image *image = intel_texture_image(texImage);
+   const struct gl_pixelstore_attrib *pack = &ctx->Pack;
+
+   if (_mesa_is_bufferobj(pack->BufferObj))
+      return false;
+
+   if (pack->SwapBytes || pack->LsbFirst) {
+      DBG("%s: bad packing params\n", __func__);
+      return false;
+   }
+
+   /* Don't allow conversion between formats as the internal storage of the
+    * image (image->mt->format) may itself not match the original and
+    * allow false transforms.
+    */
+   if (texImage->_BaseFormat !=
+       _mesa_get_format_base_format(texImage->TexFormat))
+      return false;
+
+   struct intel_mipmap_tree *src = image->mt;
+   if (format == GL_STENCIL_INDEX && src->stencil_mt)
+      src = src->stencil_mt;
+
+   mesa_format src_format = src->format;
+   if (!brw->screen->mesa_format_supports_texture[src_format])
+      return false;
+
+   mesa_format dst_format = _mesa_format_from_format_and_type(format, type);
+   if (_mesa_format_is_mesa_array_format(dst_format))
+      dst_format = _mesa_format_from_array_format(dst_format);
+   if (!brw->mesa_format_supports_render[dst_format]) {
+      dst_format= _mesa_format_fallback_rgbx_to_rgba(dst_format);
+      if (!brw->mesa_format_supports_render[dst_format])
+         return false;
+   }
+
+   switch (format) {
+   case GL_DEPTH_STENCIL_EXT:
+   case GL_STENCIL_INDEX:
+      if (src_format != dst_format) /* XXX blorp! */
+         return false;
+   default:
+      break;
+   }
+
+   const int dimensions =
+      _mesa_get_texture_dimensions(texImage->TexObject->Target);
+   pixels = _mesa_image_address(dimensions, pack, pixels, width, height,
+                                format, type, 0, 0, 0);
+   int size =  _mesa_image_offset(dimensions, pack, width, height,
+                                  format, type, depth-1, height-1, width);
+
+   int dst_offset = (GLintptr)pixels & 4095;
+   size = 2*ALIGN(size + dst_offset, 4096);
+
+   struct brw_bo *dst_buffer =
+      brw_bo_alloc_userptr(brw->bufmgr, "getteximage(userptr)",
+                           (void *)((GLintptr)pixels & ~4095),
+                           size, 0);
+   if (!dst_buffer)
+      return false;
+
+   bool dst_flip = false;
+   int dst_stride = _mesa_image_row_stride(pack, width, format, type);
+   if (pack->Invert) {
+      dst_stride = -dst_stride;
+      dst_flip = true;
+   }
+
+   int image_height = pack->ImageHeight == 0 ? height : pack->ImageHeight;
+   if (texImage->TexObject->Target == GL_TEXTURE_1D_ARRAY) {
+      assert(depth == 1);
+      assert(z == 0);
+      depth = height;
+      height = 1;
+      image_height = 1;
+      z = y;
+      y = 0;
+   }
+
+   struct intel_mipmap_tree *dst =
+      intel_miptree_create_for_bo(brw,
+                                  dst_buffer,
+                                  dst_format,
+                                  dst_offset,
+                                  width, image_height * depth, 1,
+                                  dst_stride,
+                                  MIPTREE_LAYOUT_DISABLE_AUX);
+   if (!dst) {
+      brw_bo_unreference(dst_buffer);
+      return false;
+   }
+
+   int src_level = texImage->Level + texImage->TexObject->MinLevel;
+   int src_slice = texImage->Face + texImage->TexObject->MinLayer + z;
+
+   bool blit = intel_miptree_blit_compatible_formats(src_format, dst_format);
+   for (z = 0; depth--; z++) {
+      if (!(blit && intel_miptree_blit(brw,
+                                       src, src_level, src_slice + z,
+                                       x, y, false,
+                                       dst, 0, 0,
+                                       0, z * image_height, dst_flip,
+                                       width, height, GL_COPY)))
+         brw_blorp_blit_miptrees(brw,
+                                 src, src_level, src_slice + z,
+                                 src_format, SWIZZLE_XYZW,
+                                 dst, 0, 0, dst_format,
+                                 x, y, width, height,
+                                 0, z * image_height, width, height,
+                                 GL_NEAREST, false, dst_flip,
+                                 false, false);
+   }
+   intel_miptree_release(&dst);
+
+   intel_batchbuffer_flush(brw);
+   brw_bo_wait_rendering(dst_buffer);
+   brw_bo_unreference(dst_buffer);
+
+   return true;
+}
+#endif
+
+static int
+get_texture_swizzle(const struct gl_texture_image *tex)
+{
+   if (tex->_BaseFormat == GL_LUMINANCE ||
+       tex->_BaseFormat == GL_INTENSITY)
+      return MAKE_SWIZZLE4(SWIZZLE_X, SWIZZLE_ZERO, SWIZZLE_ZERO, SWIZZLE_ONE);
+
+   if (tex->_BaseFormat == GL_LUMINANCE_ALPHA)
+      return MAKE_SWIZZLE4(SWIZZLE_X, SWIZZLE_ZERO, SWIZZLE_ZERO, SWIZZLE_W);
+
+   if (tex->_BaseFormat == GL_ALPHA)
+      return MAKE_SWIZZLE4(SWIZZLE_ZERO, SWIZZLE_ZERO, SWIZZLE_ZERO, 
SWIZZLE_W);
+
+   if (tex->_BaseFormat == GL_RGB)
+      return MAKE_SWIZZLE4(SWIZZLE_X, SWIZZLE_Y, SWIZZLE_Z, SWIZZLE_ONE);
+
+   return SWIZZLE_XYZW;
+}
+
+static bool
+need_signed_unsigned_int_conversion(mesa_format mesaFormat,
+                                    GLenum format, GLenum type)
+{
+   const GLenum mesaFormatType = _mesa_get_format_datatype(mesaFormat);
+   const bool is_format_integer = _mesa_is_enum_format_integer(format);
+   return (mesaFormatType == GL_INT &&
+           is_format_integer &&
+           (type == GL_UNSIGNED_INT ||
+            type == GL_UNSIGNED_SHORT ||
+            type == GL_UNSIGNED_BYTE)) ||
+          (mesaFormatType == GL_UNSIGNED_INT &&
+           is_format_integer &&
+           (type == GL_INT ||
+            type == GL_SHORT ||
+            type == GL_BYTE));
+}
+
+static bool
+intel_gettexsubimage_pbo(struct gl_context *ctx,
+                         GLint x, GLint y, GLint z,
+                         GLsizei width, GLsizei height, GLsizei depth,
+                         GLenum format, GLenum type,
+                         GLvoid *pixels,
+                         struct gl_texture_image *tex)
+{
+   struct brw_context *brw = brw_context(ctx);
+   struct intel_texture_image *image = intel_texture_image(tex);
+   const struct gl_pixelstore_attrib *pack = &ctx->Pack;
+   bool ok = false;
+
+   if (ctx->_ImageTransferState)
+      return false;
+
+   if (pack->SwapBytes || pack->LsbFirst) {
+      DBG("%s: bad packing params\n", __func__);
+      return false;
+   }
+
+   if (format == GL_COLOR_INDEX)
+      return false;
+
+   if (_mesa_need_rgb_to_luminance_conversion(tex->_BaseFormat, format))
+      return false;
+
+   struct intel_mipmap_tree *src = image->mt;
+   if (format == GL_STENCIL_INDEX && src->stencil_mt)
+      src = src->stencil_mt;
+
+   if (need_signed_unsigned_int_conversion(src->format, format, type))
+      return false;
+
+   mesa_format dst_format = _mesa_format_from_format_and_type(format, type);
+   if (_mesa_format_is_mesa_array_format(dst_format))
+      dst_format = _mesa_format_from_array_format(dst_format);
+   dst_format = _mesa_get_srgb_format_linear(dst_format);
+
+   if (!brw->mesa_format_supports_render[dst_format]) {
+      dst_format = _mesa_format_fallback_rgbx_to_rgba(dst_format);
+      if (!brw->mesa_format_supports_render[dst_format])
+         return false;
+   }
+
+   bool dst_flip = false;
+   int dst_stride = _mesa_image_row_stride(pack, width, format, type);
+   if (pack->Invert) {
+      dst_stride = -dst_stride;
+      dst_flip = true;
+   }
+
+   int image_height = pack->ImageHeight == 0 ? height : pack->ImageHeight;
+   int full_height = image_height * (depth - 1) + height;
+   int bpp = _mesa_bytes_per_pixel(format, type);
+   pixels += (pack->SkipImages * image_height + pack->SkipRows) * dst_stride;
+   pixels += pack->SkipPixels * bpp;
+   int dst_size = (full_height - 1) * dst_stride + width * bpp;
+
+   struct brw_bo *dst_buffer;
+   int dst_offset;
+   if (_mesa_is_bufferobj(pack->BufferObj)) {
+      dst_offset = (GLintptr)pixels;
+      if ((dst_offset | dst_stride) % bpp)
+         return false;
+
+      dst_buffer = intel_bufferobj_buffer(brw,
+                                          intel_buffer_object(pack->BufferObj),
+                                          dst_offset,
+                                          dst_size,
+                                          false);
+   } else {
+      dst_offset = (GLintptr)pixels & 4095;
+      if ((dst_offset | dst_stride) % bpp)
+         return false;
+
+      dst_buffer = brw_bo_alloc_userptr(brw->bufmgr, "readpixels",
+                                        (void *)((GLintptr)pixels & -4096),
+                                        ALIGN(dst_size + dst_offset, 4096), 0);
+   }
+   if (!dst_buffer)
+      return false;
+
+   if (tex->TexObject->Target == GL_TEXTURE_1D_ARRAY) {
+      assert(depth == 1);
+      assert(z == 0);
+      depth = height;
+      height = 1;
+      image_height = 1;
+      z = y;
+      y = 0;
+   }
+
+   struct intel_mipmap_tree *dst =
+      intel_miptree_create_for_bo(brw,
+                                  dst_buffer,
+                                  dst_format,
+                                  dst_offset,
+                                  width, full_height, 1,
+                                  dst_stride,
+                                  MIPTREE_LAYOUT_TILING_NONE |
+                                  MIPTREE_LAYOUT_DISABLE_AUX);
+   if (!dst)
+      goto err_dst_buffer;
+
+   assert(dst->surf.tiling == ISL_TILING_LINEAR);
+
+   int src_level = tex->Level + tex->TexObject->MinLevel;
+   int src_slice = tex->Face + tex->TexObject->MinLayer + z;
+
+   /* blorp restrictions; see try_blorp_blit() */
+   mesa_format src_format = src->format;
+   switch (format) {
+   case GL_STENCIL_INDEX:
+      if (src_format != dst_format)
+         goto err_dst_mt;
+
+      /* fallthrough */
+   case GL_DEPTH_COMPONENT:
+   case GL_DEPTH_STENCIL_EXT:
+      if ((src_format == MESA_FORMAT_Z24_UNORM_X8_UINT) !=
+          (dst_format == MESA_FORMAT_Z24_UNORM_X8_UINT))
+         goto err_dst_mt;
+
+      if (_mesa_get_format_base_format(src_format) == GL_DEPTH_STENCIL ||
+          _mesa_get_format_base_format(dst_format) == GL_DEPTH_STENCIL)
+         goto err_dst_mt;
+
+      src_format = MESA_FORMAT_NONE;
+      dst_format = MESA_FORMAT_NONE;
+      break;
+
+   default:
+      break;
+   }
+
+   for (z = 0; depth--; z++) {
+      if (!intel_miptree_blit(brw,
+                              src, src_level, src_slice + z,
+                              x, y, false,
+                              dst, 0, 0,
+                              0, z * image_height, dst_flip,
+                              width, height, GL_COPY)) {
+         brw_blorp_blit_miptrees(brw,
+                                 src, src_level, src_slice + z,
+                                 src_format, get_texture_swizzle(tex),
+                                 dst, 0, 0, dst_format,
+                                 x, y, x + width, y + height,
+                                 0, z * image_height, width, z * image_height 
+ height,
+                                 GL_NEAREST, false, dst_flip,
+                                 false, false);
+      }
+   }
+   if (!_mesa_is_bufferobj(pack->BufferObj)) {
+      intel_batchbuffer_flush(brw);
+      brw_bo_wait_rendering(dst_buffer);
+   }
+
+   ok = true;
+err_dst_mt:
+   intel_miptree_release(&dst);
+err_dst_buffer:
+   if (!_mesa_is_bufferobj(pack->BufferObj))
+      brw_bo_unreference(dst_buffer);
+   return ok;
+}
+
 static void
 intel_get_tex_sub_image(struct gl_context *ctx,
                         GLint xoffset, GLint yoffset, GLint zoffset,
@@ -501,33 +840,32 @@ intel_get_tex_sub_image(struct gl_context *ctx,
                         struct gl_texture_image *texImage)
 {
    struct brw_context *brw = brw_context(ctx);
-   bool ok;
 
    DBG("%s\n", __func__);
 
-   if (_mesa_is_bufferobj(ctx->Pack.BufferObj)) {
-      if (_mesa_meta_pbo_GetTexSubImage(ctx, 3, texImage,
-                                        xoffset, yoffset, zoffset,
-                                        width, height, depth, format, type,
-                                        pixels, &ctx->Pack)) {
+   if (!_mesa_is_bufferobj(ctx->Pack.BufferObj)) {
+      if (intel_gettexsubimage_tiled_memcpy(ctx, texImage, xoffset, yoffset,
+                                            width, height,
+                                            format, type, pixels, &ctx->Pack))
+         return;
+   }
+
+   if (intel_gettexsubimage_pbo(ctx, xoffset, yoffset, zoffset,
+                                width, height, depth,
+                                format, type, pixels, texImage)) {
+      if (_mesa_is_bufferobj(ctx->Pack.BufferObj)) {
          /* Flush to guarantee coherency between the render cache and other
           * caches the PBO could potentially be bound to after this point.
           * See the related comment in intelReadPixels() for a more detailed
           * explanation.
           */
          brw_emit_mi_flush(brw);
-         return;
       }
-
-      perf_debug("%s: fallback to CPU mapping in PBO case\n", __func__);
+      return;
    }
 
-   ok = intel_gettexsubimage_tiled_memcpy(ctx, texImage, xoffset, yoffset,
-                                          width, height,
-                                          format, type, pixels, &ctx->Pack);
-
-   if(ok)
-      return;
+   perf_debug("%s: fallback to CPU mapping%s\n", __func__,
+              _mesa_is_bufferobj(ctx->Pack.BufferObj) ? " for PBO" : "");
 
    _mesa_meta_GetTexSubImage(ctx, xoffset, yoffset, zoffset,
                              width, height, depth,
-- 
2.13.3

_______________________________________________
mesa-dev mailing list
mesa-dev@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/mesa-dev

Reply via email to