[Mesa-dev] [PATCH v2 2/2] st/mesa: provide GL_OES_copy_image support by caching the original ETC data

Ilia Mirkin imirkin at alum.mit.edu
Sat Jul 16 16:21:23 UTC 2016


The additional provision of GL_OES_copy_image is that it work for ETC.
However many desktop GPUs don't have native ETC support, so st/mesa does
the decoding by hand. Instead of discarding the compressed data, keep it
around in CPU memory. Use it when performing image copies.

Signed-off-by: Ilia Mirkin <imirkin at alum.mit.edu>
---
 docs/GL3.txt                             |  2 +-
 docs/relnotes/12.1.0.html                |  1 +
 src/mesa/state_tracker/st_cb_copyimage.c | 96 +++++++++++++++++++++++++++++++-
 src/mesa/state_tracker/st_cb_texture.c   | 77 +++++++++++++++++--------
 src/mesa/state_tracker/st_extensions.c   | 12 +---
 src/mesa/state_tracker/st_texture.h      |  7 ++-
 6 files changed, 156 insertions(+), 39 deletions(-)

diff --git a/docs/GL3.txt b/docs/GL3.txt
index ce34869..6da5225 100644
--- a/docs/GL3.txt
+++ b/docs/GL3.txt
@@ -255,7 +255,7 @@ GLES3.2, GLSL ES 3.2
   GL_KHR_debug                                          DONE (all drivers)
   GL_KHR_robustness                                     DONE (i965)
   GL_KHR_texture_compression_astc_ldr                   DONE (i965/gen9+)
-  GL_OES_copy_image                                     DONE (i965)
+  GL_OES_copy_image                                     DONE (all drivers)
   GL_OES_draw_buffers_indexed                           DONE (all drivers that support GL_ARB_draw_buffers_blend)
   GL_OES_draw_elements_base_vertex                      DONE (all drivers)
   GL_OES_geometry_shader                                started (idr)
diff --git a/docs/relnotes/12.1.0.html b/docs/relnotes/12.1.0.html
index 096f551..abdd83af 100644
--- a/docs/relnotes/12.1.0.html
+++ b/docs/relnotes/12.1.0.html
@@ -47,6 +47,7 @@ Note: some of the new features are only available with certain drivers.
 <li>GL_ARB_shader_group_vote on nvc0</li>
 <li>GL_ARB_ES3_1_compatibility on i965</li>
 <li>GL_EXT_window_rectangles on nv50, nvc0</li>
+<li>GL_OES_copy_image on nv50, nvc0, r600, radeonsi, softpipe, llvmpipe</li>
 </ul>
 
 <h2>Bug fixes</h2>
diff --git a/src/mesa/state_tracker/st_cb_copyimage.c b/src/mesa/state_tracker/st_cb_copyimage.c
index f670bd9..d160c8c 100644
--- a/src/mesa/state_tracker/st_cb_copyimage.c
+++ b/src/mesa/state_tracker/st_cb_copyimage.c
@@ -532,6 +532,90 @@ copy_image(struct pipe_context *pipe,
                  src_box);
 }
 
+/* Note, the only allowable compressed format for this function is ETC */
+static void
+fallback_copy_image(struct st_context *st,
+                    struct gl_texture_image *dst_image,
+                    struct pipe_resource *dst_res,
+                    int dst_x, int dst_y, int dst_z,
+                    struct gl_texture_image *src_image,
+                    struct pipe_resource *src_res,
+                    int src_x, int src_y, int src_z,
+                    int src_w, int src_h)
+{
+   uint8_t *dst, *src;
+   int dst_stride, src_stride;
+   struct pipe_transfer *dst_transfer, *src_transfer;
+   unsigned line_bytes;
+
+   bool dst_is_compressed = dst_image && _mesa_is_format_compressed(dst_image->TexFormat);
+   bool src_is_compressed = src_image && _mesa_is_format_compressed(src_image->TexFormat);
+
+   unsigned dst_w = src_w;
+   unsigned dst_h = src_h;
+   unsigned lines = src_h;
+
+   if (src_is_compressed && !dst_is_compressed) {
+      dst_w = DIV_ROUND_UP(dst_w, 4);
+      dst_h = DIV_ROUND_UP(dst_h, 4);
+   } else if (!src_is_compressed && dst_is_compressed) {
+      dst_w *= 4;
+      dst_h *= 4;
+   }
+   if (src_is_compressed) {
+      lines = DIV_ROUND_UP(lines, 4);
+   }
+
+   if (src_image)
+      line_bytes = _mesa_format_row_stride(src_image->TexFormat, src_w);
+   else
+      line_bytes = _mesa_format_row_stride(dst_image->TexFormat, dst_w);
+
+   if (dst_image) {
+      st->ctx->Driver.MapTextureImage(
+            st->ctx, dst_image, dst_z,
+            dst_x, dst_y, dst_w, dst_h,
+            GL_MAP_WRITE_BIT, &dst, &dst_stride);
+   } else {
+      dst = pipe_transfer_map(st->pipe, dst_res, 0, dst_z,
+                              PIPE_TRANSFER_WRITE,
+                              dst_x, dst_y, dst_w, dst_h,
+                              &dst_transfer);
+      dst_stride = dst_transfer->stride;
+   }
+
+   if (src_image) {
+      st->ctx->Driver.MapTextureImage(
+            st->ctx, src_image, src_z,
+            src_x, src_y, src_w, src_h,
+            GL_MAP_READ_BIT, &src, &src_stride);
+   } else {
+      src = pipe_transfer_map(st->pipe, src_res, 0, src_z,
+                              PIPE_TRANSFER_READ,
+                              src_x, src_y, src_w, src_h,
+                              &src_transfer);
+      src_stride = src_transfer->stride;
+   }
+
+   for (int y = 0; y < lines; y++) {
+      memcpy(dst, src, line_bytes);
+      dst += dst_stride;
+      src += src_stride;
+   }
+
+   if (dst_image) {
+      st->ctx->Driver.UnmapTextureImage(st->ctx, dst_image, dst_z);
+   } else {
+      pipe_transfer_unmap(st->pipe, dst_transfer);
+   }
+
+   if (src_image) {
+      st->ctx->Driver.UnmapTextureImage(st->ctx, src_image, src_z);
+   } else {
+      pipe_transfer_unmap(st->pipe, src_transfer);
+   }
+}
+
 static void
 st_CopyImageSubData(struct gl_context *ctx,
                     struct gl_texture_image *src_image,
@@ -547,6 +631,7 @@ st_CopyImageSubData(struct gl_context *ctx,
    struct pipe_resource *src_res, *dst_res;
    struct pipe_box box;
    int src_level, dst_level;
+   int orig_src_z = src_z, orig_dst_z = dst_z;
 
    st_flush_bitmap_cache(st);
    st_invalidate_readpix_cache(st);
@@ -583,8 +668,15 @@ st_CopyImageSubData(struct gl_context *ctx,
 
    u_box_2d_zslice(src_x, src_y, src_z, src_width, src_height, &box);
 
-   copy_image(pipe, dst_res, dst_level, dst_x, dst_y, dst_z,
-              src_res, src_level, &box);
+   if ((src_image && st_etc_fallback(st, src_image)) ||
+       (dst_image && st_etc_fallback(st, dst_image))) {
+      fallback_copy_image(st, dst_image, dst_res, dst_x, dst_y, orig_dst_z,
+                          src_image, src_res, src_x, src_y, orig_src_z,
+                          src_width, src_height);
+   } else {
+      copy_image(pipe, dst_res, dst_level, dst_x, dst_y, dst_z,
+                 src_res, src_level, &box);
+   }
 }
 
 void
diff --git a/src/mesa/state_tracker/st_cb_texture.c b/src/mesa/state_tracker/st_cb_texture.c
index 54f338f..8db11ed 100644
--- a/src/mesa/state_tracker/st_cb_texture.c
+++ b/src/mesa/state_tracker/st_cb_texture.c
@@ -187,6 +187,11 @@ st_FreeTextureImageBuffer(struct gl_context *ctx,
    free(stImage->transfer);
    stImage->transfer = NULL;
    stImage->num_transfers = 0;
+
+   if (stImage->etc_data) {
+      free(stImage->etc_data);
+      stImage->etc_data = NULL;
+   }
 }
 
 bool
@@ -196,6 +201,26 @@ st_etc_fallback(struct st_context *st, struct gl_texture_image *texImage)
           (texImage->TexFormat == MESA_FORMAT_ETC1_RGB8 && !st->has_etc1);
 }
 
+static void
+etc_fallback_allocate(struct st_context *st, struct st_texture_image *stImage)
+{
+   struct gl_texture_image *texImage = &stImage->base;
+
+   if (!st_etc_fallback(st, texImage))
+      return;
+
+   if (stImage->etc_data)
+      free(stImage->etc_data);
+
+   unsigned data_size = _mesa_format_image_size(texImage->TexFormat,
+                                                texImage->Width2,
+                                                texImage->Height2,
+                                                texImage->Depth2);
+
+   stImage->etc_data =
+      malloc(data_size * _mesa_num_tex_faces(texImage->TexObject->Target));
+}
+
 /** called via ctx->Driver.MapTextureImage() */
 static void
 st_MapTextureImage(struct gl_context *ctx,
@@ -222,24 +247,23 @@ st_MapTextureImage(struct gl_context *ctx,
                               &transfer);
    if (map) {
       if (st_etc_fallback(st, texImage)) {
-         /* ETC isn't supported by gallium and it's represented
-          * by uncompressed formats. Only write transfers with precompressed
-          * data are supported by ES3, which makes this really simple.
+         /* ETC isn't supported by all gallium drivers, where it's represented
+          * by uncompressed formats. We store the compressed data (as it's
+          * needed for image copies in OES_copy_image), and decompress as
+          * necessary in Unmap.
           *
-          * Just create a temporary storage where the ETC texture will
-          * be stored. It will be decompressed in the Unmap function.
+          * Note: all ETC1/ETC2 formats have 4x4 block sizes.
           */
          unsigned z = transfer->box.z;
          struct st_texture_image_transfer *itransfer = &stImage->transfer[z];
 
-         itransfer->temp_data =
-            malloc(_mesa_format_image_size(texImage->TexFormat, w, h, 1));
-         itransfer->temp_stride =
-            _mesa_format_row_stride(texImage->TexFormat, w);
+         unsigned bytes = _mesa_get_format_bytes(texImage->TexFormat);
+         unsigned stride = *rowStrideOut = itransfer->temp_stride =
+            _mesa_format_row_stride(texImage->TexFormat, texImage->Width2);
+         *mapOut = itransfer->temp_data =
+            stImage->etc_data + ((x / 4) * bytes + (y / 4) * stride) +
+            z * stride * texImage->Height2 / 4;
          itransfer->map = map;
-
-         *mapOut = itransfer->temp_data;
-         *rowStrideOut = itransfer->temp_stride;
       }
       else {
          /* supported mapping */
@@ -271,20 +295,21 @@ st_UnmapTextureImage(struct gl_context *ctx,
 
       assert(z == transfer->box.z);
 
-      if (texImage->TexFormat == MESA_FORMAT_ETC1_RGB8) {
-         _mesa_etc1_unpack_rgba8888(itransfer->map, transfer->stride,
-                                    itransfer->temp_data,
-                                    itransfer->temp_stride,
-                                    transfer->box.width, transfer->box.height);
-      }
-      else {
-         _mesa_unpack_etc2_format(itransfer->map, transfer->stride,
-                                  itransfer->temp_data, itransfer->temp_stride,
-                                  transfer->box.width, transfer->box.height,
-                                  texImage->TexFormat);
+      if (transfer->usage & PIPE_TRANSFER_WRITE) {
+         if (texImage->TexFormat == MESA_FORMAT_ETC1_RGB8) {
+            _mesa_etc1_unpack_rgba8888(itransfer->map, transfer->stride,
+                                       itransfer->temp_data,
+                                       itransfer->temp_stride,
+                                       transfer->box.width, transfer->box.height);
+         }
+         else {
+            _mesa_unpack_etc2_format(itransfer->map, transfer->stride,
+                                     itransfer->temp_data, itransfer->temp_stride,
+                                     transfer->box.width, transfer->box.height,
+                                     texImage->TexFormat);
+         }
       }
 
-      free(itransfer->temp_data);
       itransfer->temp_data = NULL;
       itransfer->temp_stride = 0;
       itransfer->map = 0;
@@ -572,6 +597,8 @@ st_AllocTextureImageBuffer(struct gl_context *ctx,
 
    assert(!stImage->pt); /* xxx this might be wrong */
 
+   etc_fallback_allocate(st, stImage);
+
    /* Look if the parent texture object has space for this image */
    if (stObj->pt &&
        level <= stObj->pt->last_level &&
@@ -2680,6 +2707,8 @@ st_AllocTextureStorage(struct gl_context *ctx,
          struct st_texture_image *stImage =
             st_texture_image(texObj->Image[face][level]);
          pipe_resource_reference(&stImage->pt, stObj->pt);
+
+         etc_fallback_allocate(st, stImage);
       }
    }
 
diff --git a/src/mesa/state_tracker/st_extensions.c b/src/mesa/state_tracker/st_extensions.c
index 4033452..493db12 100644
--- a/src/mesa/state_tracker/st_extensions.c
+++ b/src/mesa/state_tracker/st_extensions.c
@@ -579,6 +579,7 @@ void st_init_extensions(struct pipe_screen *screen,
       { o(ARB_color_buffer_float),           PIPE_CAP_VERTEX_COLOR_UNCLAMPED           },
       { o(ARB_conditional_render_inverted),  PIPE_CAP_CONDITIONAL_RENDER_INVERTED      },
       { o(ARB_copy_image),                   PIPE_CAP_COPY_BETWEEN_COMPRESSED_AND_PLAIN_FORMATS },
+      { o(OES_copy_image),                   PIPE_CAP_COPY_BETWEEN_COMPRESSED_AND_PLAIN_FORMATS },
       { o(ARB_cull_distance),                PIPE_CAP_CULL_DISTANCE                    },
       { o(ARB_depth_clamp),                  PIPE_CAP_DEPTH_CLIP_DISABLE               },
       { o(ARB_depth_texture),                PIPE_CAP_TEXTURE_SHADOW_MAP               },
@@ -951,17 +952,6 @@ void st_init_extensions(struct pipe_screen *screen,
    extensions->OES_sample_variables = extensions->ARB_sample_shading &&
       extensions->ARB_gpu_shader5;
 
-   /* If we don't have native ETC2 support, we don't keep track of the
-    * original ETC2 data. This is necessary to be able to copy images between
-    * compatible view classes.
-    */
-   if (extensions->ARB_copy_image && screen->is_format_supported(
-             screen, PIPE_FORMAT_ETC2_RGB8,
-             PIPE_TEXTURE_2D, 0,
-             PIPE_BIND_SAMPLER_VIEW)) {
-      extensions->OES_copy_image = GL_TRUE;
-   }
-
    /* Maximum sample count. */
    {
       enum pipe_format color_formats[] = {
diff --git a/src/mesa/state_tracker/st_texture.h b/src/mesa/state_tracker/st_texture.h
index d5e8281..b2ddc14 100644
--- a/src/mesa/state_tracker/st_texture.h
+++ b/src/mesa/state_tracker/st_texture.h
@@ -65,7 +65,12 @@ struct st_texture_image
     */
    struct st_texture_image_transfer *transfer;
    unsigned num_transfers;
-};
+
+   /* For ETC images, keep track of the original data. This is necessary for
+    * mapping/unmapping, as well as image copies.
+    */
+   GLubyte *etc_data;
+ };
 
 
 /**
-- 
2.7.3



More information about the mesa-dev mailing list