[Mesa-dev,3/3] st/mesa: implement ARB_copy_image

Submitted by Marek Olšák on Oct. 25, 2015, 5:25 p.m.

Details

Message ID 1445793959-18903-3-git-send-email-maraeo@gmail.com
State New
Headers show
Series "Series without cover letter" ( rev: 1 ) in Mesa

Not browsing as part of any series.

Commit Message

Marek Olšák Oct. 25, 2015, 5:25 p.m.
From: Marek Olšák <marek.olsak@amd.com>

I wonder if the craziness was worth it.
---
 src/mesa/Makefile.sources                |   2 +
 src/mesa/state_tracker/st_cb_copyimage.c | 609 +++++++++++++++++++++++++++++++
 src/mesa/state_tracker/st_cb_copyimage.h |  33 ++
 src/mesa/state_tracker/st_cb_texture.c   |  51 ---
 src/mesa/state_tracker/st_context.c      |   2 +
 src/mesa/state_tracker/st_extensions.c   |   1 +
 6 files changed, 647 insertions(+), 51 deletions(-)
 create mode 100644 src/mesa/state_tracker/st_cb_copyimage.c
 create mode 100644 src/mesa/state_tracker/st_cb_copyimage.h

Patch hide | download patch | download mbox

diff --git a/src/mesa/Makefile.sources b/src/mesa/Makefile.sources
index 4bcaa62..de0e330 100644
--- a/src/mesa/Makefile.sources
+++ b/src/mesa/Makefile.sources
@@ -423,6 +423,8 @@  STATETRACKER_FILES = \
 	state_tracker/st_cb_clear.h \
 	state_tracker/st_cb_condrender.c \
 	state_tracker/st_cb_condrender.h \
+	state_tracker/st_cb_copyimage.c \
+	state_tracker/st_cb_copyimage.h \
 	state_tracker/st_cb_drawpixels.c \
 	state_tracker/st_cb_drawpixels.h \
 	state_tracker/st_cb_drawpixels_shader.c \
diff --git a/src/mesa/state_tracker/st_cb_copyimage.c b/src/mesa/state_tracker/st_cb_copyimage.c
new file mode 100644
index 0000000..1740aaf
--- /dev/null
+++ b/src/mesa/state_tracker/st_cb_copyimage.c
@@ -0,0 +1,609 @@ 
+/*
+ * Copyright 2015 Advanced Micro Devices, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * on the rights to use, copy, modify, merge, publish, distribute, sub
+ * license, and/or sell copies of the Software, and to permit persons to whom
+ * the Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * 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
+ * THE AUTHOR(S) AND/OR THEIR 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 "state_tracker/st_context.h"
+#include "state_tracker/st_cb_copyimage.h"
+#include "state_tracker/st_cb_fbo.h"
+#include "state_tracker/st_texture.h"
+
+#include "util/u_box.h"
+#include "util/u_format.h"
+#include "util/u_inlines.h"
+
+
+/**
+ * Return an equivalent canonical format without "X" channels.
+ *
+ * Copying between incompatible formats is easier when the format is
+ * canonicalized, meaning that it is in a standard form.
+ *
+ * The returned format has the same component sizes and swizzles as
+ * the source format, the type is changed to UINT or UNORM, depending on
+ * which one has the most swizzle combinations in their group.
+ *
+ * If it's not an array format, return a memcpy-equivalent array format.
+ *
+ * The key feature is that swizzled versions of formats of the same
+ * component size always return the same component type.
+ *
+ * X returns A.
+ * Luminance, intensity, alpha, depth, stencil, and 8-bit and 16-bit packed
+ * formats are not supported. (same as ARB_copy_image)
+ */
+static enum pipe_format
+get_canonical_format(enum pipe_format format)
+{
+   const struct util_format_description *desc =
+      util_format_description(format);
+
+   /* Packed formats. Return the equivalent array format. */
+   if (format == PIPE_FORMAT_R11G11B10_FLOAT ||
+       format == PIPE_FORMAT_R9G9B9E5_FLOAT)
+      return get_canonical_format(PIPE_FORMAT_R8G8B8A8_UINT);
+
+   if (desc->nr_channels == 4 &&
+       desc->channel[0].size == 10 &&
+       desc->channel[1].size == 10 &&
+       desc->channel[2].size == 10 &&
+       desc->channel[3].size == 2) {
+      if (desc->swizzle[0] == UTIL_FORMAT_SWIZZLE_X &&
+          desc->swizzle[1] == UTIL_FORMAT_SWIZZLE_Y &&
+          desc->swizzle[2] == UTIL_FORMAT_SWIZZLE_Z)
+         return get_canonical_format(PIPE_FORMAT_R8G8B8A8_UINT);
+
+      return PIPE_FORMAT_NONE;
+   }
+
+#define RETURN_FOR_SWIZZLE1(x, format) \
+   if (desc->swizzle[0] == UTIL_FORMAT_SWIZZLE_##x) \
+      return format
+
+#define RETURN_FOR_SWIZZLE2(x, y, format) \
+   if (desc->swizzle[0] == UTIL_FORMAT_SWIZZLE_##x && \
+       desc->swizzle[1] == UTIL_FORMAT_SWIZZLE_##y) \
+      return format
+
+#define RETURN_FOR_SWIZZLE3(x, y, z, format) \
+   if (desc->swizzle[0] == UTIL_FORMAT_SWIZZLE_##x && \
+       desc->swizzle[1] == UTIL_FORMAT_SWIZZLE_##y && \
+       desc->swizzle[2] == UTIL_FORMAT_SWIZZLE_##z) \
+      return format
+
+#define RETURN_FOR_SWIZZLE4(x, y, z, w, format) \
+   if (desc->swizzle[0] == UTIL_FORMAT_SWIZZLE_##x && \
+       desc->swizzle[1] == UTIL_FORMAT_SWIZZLE_##y && \
+       desc->swizzle[2] == UTIL_FORMAT_SWIZZLE_##z && \
+       desc->swizzle[3] == UTIL_FORMAT_SWIZZLE_##w) \
+      return format
+
+   /* Array formats. */
+   if (desc->is_array) {
+      switch (desc->nr_channels) {
+      case 1:
+         switch (desc->channel[0].size) {
+         case 8:
+            RETURN_FOR_SWIZZLE1(X, PIPE_FORMAT_R8_UINT);
+            break;
+
+         case 16:
+            RETURN_FOR_SWIZZLE1(X, PIPE_FORMAT_R16_UINT);
+            break;
+
+         case 32:
+            RETURN_FOR_SWIZZLE1(X, PIPE_FORMAT_R32_UINT);
+            break;
+         }
+         break;
+
+      case 2:
+         switch (desc->channel[0].size) {
+         case 8:
+            /* All formats in each group must be of the same type.
+             * We can't use UINT for R8G8 while using UNORM for G8R8.
+             */
+            RETURN_FOR_SWIZZLE2(X, Y, PIPE_FORMAT_R8G8_UNORM);
+            RETURN_FOR_SWIZZLE2(Y, X, PIPE_FORMAT_G8R8_UNORM);
+            break;
+
+         case 16:
+            RETURN_FOR_SWIZZLE2(X, Y, PIPE_FORMAT_R16G16_UNORM);
+            RETURN_FOR_SWIZZLE2(Y, X, PIPE_FORMAT_G16R16_UNORM);
+            break;
+
+         case 32:
+            RETURN_FOR_SWIZZLE2(X, Y, PIPE_FORMAT_R32G32_UINT);
+            break;
+         }
+         break;
+
+      case 3:
+         switch (desc->channel[0].size) {
+         case 8:
+            RETURN_FOR_SWIZZLE3(X, Y, Z, PIPE_FORMAT_R8G8B8_UINT);
+            break;
+
+         case 16:
+            RETURN_FOR_SWIZZLE3(X, Y, Z, PIPE_FORMAT_R16G16B16_UINT);
+            break;
+
+         case 32:
+            RETURN_FOR_SWIZZLE3(X, Y, Z, PIPE_FORMAT_R32G32B32_UINT);
+            break;
+         }
+         break;
+
+      case 4:
+         switch (desc->channel[0].size) {
+         case 8:
+            RETURN_FOR_SWIZZLE4(X, Y, Z, W, PIPE_FORMAT_R8G8B8A8_UNORM);
+            RETURN_FOR_SWIZZLE4(X, Y, Z, 1, PIPE_FORMAT_R8G8B8A8_UNORM);
+            RETURN_FOR_SWIZZLE4(Z, Y, X, W, PIPE_FORMAT_B8G8R8A8_UNORM);
+            RETURN_FOR_SWIZZLE4(Z, Y, X, 1, PIPE_FORMAT_B8G8R8A8_UNORM);
+            RETURN_FOR_SWIZZLE4(W, Z, Y, X, PIPE_FORMAT_A8B8G8R8_UNORM);
+            RETURN_FOR_SWIZZLE4(1, Z, Y, X, PIPE_FORMAT_A8B8G8R8_UNORM);
+            RETURN_FOR_SWIZZLE4(W, X, Y, Z, PIPE_FORMAT_A8R8G8B8_UNORM);
+            RETURN_FOR_SWIZZLE4(1, X, Y, Z, PIPE_FORMAT_A8R8G8B8_UNORM);
+            break;
+
+         case 16:
+            RETURN_FOR_SWIZZLE4(X, Y, Z, W, PIPE_FORMAT_R16G16B16A16_UINT);
+            RETURN_FOR_SWIZZLE4(X, Y, Z, 1, PIPE_FORMAT_R16G16B16A16_UINT);
+            break;
+
+         case 32:
+            RETURN_FOR_SWIZZLE4(X, Y, Z, W, PIPE_FORMAT_R32G32B32A32_UINT);
+            RETURN_FOR_SWIZZLE4(X, Y, Z, 1, PIPE_FORMAT_R32G32B32A32_UINT);
+            break;
+         }
+      }
+
+      unreachable("unknown array format");
+      return PIPE_FORMAT_NONE;
+   }
+
+   unreachable("unknown packed format");
+   return PIPE_FORMAT_NONE;
+}
+
+/**
+ * Return true if the swizzle is XYZW in case of a 4-channel format,
+ * XY in case of a 2-channel format, or X in case of a 1-channel format.
+ */
+static bool
+has_identity_swizzle(const struct util_format_description *desc)
+{
+   int i;
+
+   for (i = 0; i < desc->nr_channels; i++)
+      if (desc->swizzle[i] != UTIL_FORMAT_SWIZZLE_X + i)
+         return false;
+
+   return true;
+}
+
+/**
+ * Return a canonical format for the given bits and channel size.
+ */
+static enum pipe_format
+canonical_format_from_bits(unsigned bits, unsigned channel_size)
+{
+   switch (bits) {
+   case 8:
+      if (channel_size == 8)
+         return get_canonical_format(PIPE_FORMAT_R8_UINT);
+      break;
+
+   case 16:
+      if (channel_size == 8)
+         return get_canonical_format(PIPE_FORMAT_R8G8_UINT);
+      if (channel_size == 16)
+         return get_canonical_format(PIPE_FORMAT_R16_UINT);
+      break;
+
+   case 32:
+      if (channel_size == 8)
+         return get_canonical_format(PIPE_FORMAT_R8G8B8A8_UINT);
+      if (channel_size == 16)
+         return get_canonical_format(PIPE_FORMAT_R16G16_UINT);
+      if (channel_size == 32)
+         return get_canonical_format(PIPE_FORMAT_R32_UINT);
+      break;
+
+   case 64:
+      if (channel_size == 16)
+         return get_canonical_format(PIPE_FORMAT_R16G16B16A16_UINT);
+      if (channel_size == 32)
+         return get_canonical_format(PIPE_FORMAT_R32G32_UINT);
+      break;
+
+   case 128:
+      if (channel_size == 32)
+         return get_canonical_format(PIPE_FORMAT_R32G32B32A32_UINT);
+      break;
+   }
+
+   unreachable("impossible format");
+   return PIPE_FORMAT_NONE;
+}
+
+static void
+blit(struct pipe_context *pipe,
+     struct pipe_resource *dst,
+     enum pipe_format dst_format,
+     unsigned dst_level,
+     unsigned dstx, unsigned dsty, unsigned dstz,
+     struct pipe_resource *src,
+     enum pipe_format src_format,
+     unsigned src_level,
+     const struct pipe_box *src_box)
+{
+   struct pipe_blit_info blit = {{0}};
+
+   blit.src.resource = src;
+   blit.dst.resource = dst;
+   blit.src.format = src_format;
+   blit.dst.format = dst_format;
+   blit.src.level = src_level;
+   blit.dst.level = dst_level;
+   blit.src.box = *src_box;
+   u_box_3d(dstx, dsty, dstz, src_box->width, src_box->height,
+            src_box->depth, &blit.dst.box);
+   blit.mask = PIPE_MASK_RGBA;
+   blit.filter = PIPE_TEX_FILTER_NEAREST;
+
+   pipe->blit(pipe, &blit);
+}
+
+static void
+swizzled_copy(struct pipe_context *pipe,
+              struct pipe_resource *dst,
+              unsigned dst_level,
+              unsigned dstx, unsigned dsty, unsigned dstz,
+              struct pipe_resource *src,
+              unsigned src_level,
+              const struct pipe_box *src_box)
+{
+   const struct util_format_description *src_desc, *dst_desc;
+   unsigned bits;
+   enum pipe_format blit_src_format, blit_dst_format;
+
+   /* Get equivalent canonical formats. Those are always array formats and
+    * copying between compatible canonical formats behaves either like
+    * memcpy or like swizzled memcpy. The idea is that we won't have to care
+    * about the channel type from this point on.
+    * Only the swizzle and channel size.
+    */
+   blit_src_format = get_canonical_format(src->format);
+   blit_dst_format = get_canonical_format(dst->format);
+
+   assert(blit_src_format != PIPE_FORMAT_NONE);
+   assert(blit_dst_format != PIPE_FORMAT_NONE);
+
+   src_desc = util_format_description(blit_src_format);
+   dst_desc = util_format_description(blit_dst_format);
+
+   assert(src_desc->block.bits == dst_desc->block.bits);
+   bits = src_desc->block.bits;
+
+   if (dst_desc->channel[0].size == src_desc->channel[0].size) {
+      /* Only the swizzle is different, which means we can just blit,
+       * e.g. RGBA -> BGRA.
+       */
+   } else if (has_identity_swizzle(src_desc)) {
+      /* Src is unswizzled and dst can be swizzled, so src is typecast
+       * to an equivalent dst-compatible format.
+       * e.g. R32 -> BGRA8 is realized as RGBA8 -> BGRA8
+       */
+      blit_src_format =
+         canonical_format_from_bits(bits, dst_desc->channel[0].size);
+   } else if (has_identity_swizzle(dst_desc)) {
+      /* Dst is unswizzled and src can be swizzled, so dst is typecast
+       * to an equivalent src-compatible format.
+       * e.g. BGRA8 -> R32 is realized as BGRA8 -> RGBA8
+       */
+      blit_dst_format =
+         canonical_format_from_bits(bits, src_desc->channel[0].size);
+   } else {
+      unreachable("This should have been handled by handle_complex_copy.");
+      return;
+   }
+
+   blit(pipe, dst, blit_dst_format, dst_level, dstx, dsty, dstz,
+        src, blit_src_format, src_level, src_box);
+}
+
+static bool
+same_size_and_swizzle(const struct util_format_description *d1,
+                      const struct util_format_description *d2)
+{
+   int i;
+
+   if (d1->layout != d2->layout ||
+       d1->nr_channels != d2->nr_channels ||
+       d1->is_array != d2->is_array)
+      return false;
+
+   for (i = 0; i < d1->nr_channels; i++) {
+      if (d1->channel[i].size != d2->channel[i].size)
+         return false;
+
+      if (d1->swizzle[i] <= UTIL_FORMAT_SWIZZLE_W &&
+          d2->swizzle[i] <= UTIL_FORMAT_SWIZZLE_W &&
+          d1->swizzle[i] != d2->swizzle[i])
+         return false;
+   }
+
+   return true;
+}
+
+static bool
+is_bgr10_a2_variant(const struct util_format_description *desc)
+{
+   return desc->nr_channels == 4 &&
+          desc->channel[0].size == 10 &&
+          desc->channel[1].size == 10 &&
+          desc->channel[2].size == 10 &&
+          desc->channel[3].size == 2 &&
+          desc->swizzle[0] == UTIL_FORMAT_SWIZZLE_Z &&
+          desc->swizzle[1] == UTIL_FORMAT_SWIZZLE_Y &&
+          desc->swizzle[2] == UTIL_FORMAT_SWIZZLE_X;
+}
+
+static bool
+is_rgb10_a2_variant(const struct util_format_description *desc)
+{
+   return desc->nr_channels == 4 &&
+          desc->channel[0].size == 10 &&
+          desc->channel[1].size == 10 &&
+          desc->channel[2].size == 10 &&
+          desc->channel[3].size == 2 &&
+          desc->swizzle[0] == UTIL_FORMAT_SWIZZLE_X &&
+          desc->swizzle[1] == UTIL_FORMAT_SWIZZLE_Y &&
+          desc->swizzle[2] == UTIL_FORMAT_SWIZZLE_Z;
+}
+
+static struct pipe_resource *
+create_texture(struct pipe_screen *screen, enum pipe_format format,
+               unsigned width, unsigned height, unsigned depth)
+{
+   struct pipe_resource templ;
+
+   memset(&templ, 0, sizeof(templ));
+   templ.format = format;
+   templ.width0 = width;
+   templ.height0 = height;
+   templ.depth0 = 1;
+   templ.array_size = depth;
+   templ.usage = PIPE_USAGE_DEFAULT;
+   templ.bind = PIPE_BIND_SAMPLER_VIEW | PIPE_BIND_RENDER_TARGET;
+
+   if (depth > 1)
+           templ.target = PIPE_TEXTURE_2D_ARRAY;
+   else
+           templ.target = PIPE_TEXTURE_2D;
+
+   return screen->resource_create(screen, &templ);
+}
+
+/**
+ * Handle complex format conversions using 2 blits with a temporary texture
+ * in between, e.g. blitting from B10G10R10A2 to G16R16.
+ *
+ * This example is implemented this way:
+ * 1) First, blit from B10G10R10A2 to R10G10B10A2, which is canonical, so it
+ *    can be reinterpreted as a different canonical format of the same bpp,
+ *    such as R16G16. This blit only swaps R and B 10-bit components.
+ * 2) Finnaly, blit the result, which is R10G10B10A2, as R16G16 to G16R16.
+ *    This blit only swaps R and G 16-bit components.
+ */
+static bool
+handle_complex_copy(struct pipe_context *pipe,
+                    struct pipe_resource *dst,
+                    unsigned dst_level,
+                    unsigned dstx, unsigned dsty, unsigned dstz,
+                    struct pipe_resource *src,
+                    unsigned src_level,
+                    const struct pipe_box *src_box,
+                    enum pipe_format noncanon_format,
+                    enum pipe_format canon_format)
+{
+   struct pipe_box temp_box;
+   struct pipe_resource *temp = NULL;
+   const struct util_format_description *src_desc, *dst_desc;
+   const struct util_format_description *canon_desc, *noncanon_desc;
+   bool src_is_canon;
+   bool src_is_noncanon;
+   bool dst_is_canon;
+   bool dst_is_noncanon;
+
+   src_desc = util_format_description(src->format);
+   dst_desc = util_format_description(dst->format);
+   canon_desc = util_format_description(canon_format);
+   noncanon_desc = util_format_description(noncanon_format);
+
+   src_is_canon = same_size_and_swizzle(src_desc, canon_desc);
+   dst_is_canon = same_size_and_swizzle(dst_desc, canon_desc);
+   src_is_noncanon = same_size_and_swizzle(src_desc, noncanon_desc);
+   dst_is_noncanon = same_size_and_swizzle(dst_desc, noncanon_desc);
+
+   src_is_canon = is_rgb10_a2_variant(src_desc);
+   dst_is_canon = is_rgb10_a2_variant(dst_desc);
+   src_is_noncanon = is_bgr10_a2_variant(src_desc);
+   dst_is_noncanon = is_bgr10_a2_variant(dst_desc);
+
+   if (src_is_noncanon) {
+      /* Simple case - only types differ (e.g. UNORM and UINT). */
+      if (dst_is_noncanon) {
+         blit(pipe, dst, noncanon_format, dst_level, dstx, dsty, dstz, src,
+              noncanon_format, src_level, src_box);
+         return true;
+      }
+
+      /* Simple case - only types and swizzles differ. */
+      if (dst_is_canon) {
+         blit(pipe, dst, canon_format, dst_level, dstx, dsty, dstz, src,
+              noncanon_format, src_level, src_box);
+         return true;
+      }
+
+      /* Use the temporary texture. Src is converted to a canonical format,
+       * then proceed the generic swizzled_copy.
+       */
+      temp = create_texture(pipe->screen, canon_format, src_box->width,
+                            src_box->height, src_box->depth);
+
+      u_box_3d(0, 0, 0, src_box->width, src_box->height, src_box->depth,
+               &temp_box);
+
+      blit(pipe, temp, canon_format, 0, 0, 0, 0, src, noncanon_format,
+           src_level, src_box);
+      swizzled_copy(pipe, dst, dst_level, dstx, dsty, dstz, temp, 0,
+                    &temp_box);
+      pipe_resource_reference(&temp, NULL);
+      return true;
+   }
+
+   if (dst_is_noncanon) {
+      /* Simple case - only types and swizzles differ. */
+      if (src_is_canon) {
+         blit(pipe, dst, noncanon_format, dst_level, dstx, dsty, dstz, src,
+              canon_format, src_level, src_box);
+         return true;
+      }
+
+      /* Use the temporary texture. First, use the generic copy, but use
+       * a canonical format in the destination. Then convert */
+      temp = create_texture(pipe->screen, canon_format, src_box->width,
+                            src_box->height, src_box->depth);
+
+      u_box_3d(0, 0, 0, src_box->width, src_box->height, src_box->depth,
+               &temp_box);
+
+      swizzled_copy(pipe, temp, 0, 0, 0, 0, src, src_level, src_box);
+      blit(pipe, dst, noncanon_format, dst_level, dstx, dsty, dstz, temp,
+           canon_format, 0, &temp_box);
+      pipe_resource_reference(&temp, NULL);
+      return true;
+   }
+
+   return false;
+}
+
+static void
+copy_image(struct pipe_context *pipe,
+           struct pipe_resource *dst,
+           unsigned dst_level,
+           unsigned dstx, unsigned dsty, unsigned dstz,
+           struct pipe_resource *src,
+           unsigned src_level,
+           const struct pipe_box *src_box)
+{
+   if (src->format == dst->format ||
+       util_format_is_compressed(src->format) ||
+       util_format_is_compressed(dst->format)) {
+      pipe->resource_copy_region(pipe, dst, dst_level, dstx, dsty, dstz,
+                                 src, src_level, src_box);
+      return;
+   }
+
+   /* Copying to/from B10G10R10*2 needs 2 blits with R10G10B10A2
+    * as a temporary texture in between.
+    */
+   if (handle_complex_copy(pipe, dst, dst_level, dstx, dsty, dstz, src,
+                           src_level, src_box, PIPE_FORMAT_B10G10R10A2_UINT,
+                           PIPE_FORMAT_R10G10B10A2_UINT))
+      return;
+
+   /* Copying to/from G8R8 needs 2 blits with R8G8 as a temporary texture
+    * in between.
+    */
+   if (handle_complex_copy(pipe, dst, dst_level, dstx, dsty, dstz, src,
+                           src_level, src_box, PIPE_FORMAT_G8R8_UNORM,
+                           PIPE_FORMAT_R8G8_UNORM))
+      return;
+
+   /* Copying to/from G16R16 needs 2 blits with R16G16 as a temporary texture
+    * in between.
+    */
+   if (handle_complex_copy(pipe, dst, dst_level, dstx, dsty, dstz, src,
+                           src_level, src_box, PIPE_FORMAT_G16R16_UNORM,
+                           PIPE_FORMAT_R16G16_UNORM))
+      return;
+
+   /* Only allow non-identity swizzling on RGBA8 formats. */
+
+   /* Simple copy, memcpy with swizzling, no format conversion. */
+   swizzled_copy(pipe, dst, dst_level, dstx, dsty, dstz, src, src_level,
+                 src_box);
+}
+
+static void
+st_CopyImageSubData(struct gl_context *ctx,
+                    struct gl_texture_image *src_image,
+                    struct gl_renderbuffer *src_renderbuffer,
+                    int src_x, int src_y, int src_z,
+                    struct gl_texture_image *dst_image,
+                    struct gl_renderbuffer *dst_renderbuffer,
+                    int dst_x, int dst_y, int dst_z,
+                    int src_width, int src_height)
+{
+   struct st_context *st = st_context(ctx);
+   struct pipe_context *pipe = st->pipe;
+   struct pipe_resource *src_res, *dst_res;
+   struct pipe_box box;
+   int src_level, dst_level;
+
+   if (src_image) {
+      struct st_texture_image *src = st_texture_image(src_image);
+      src_res = src->pt;
+      src_level = src_image->Level;
+      src_z += src_image->Face;
+   } else {
+      struct st_renderbuffer *src = st_renderbuffer(src_renderbuffer);
+      src_res = src->texture;
+      src_level = 0;
+   }
+
+   if (dst_image) {
+      struct st_texture_image *dst = st_texture_image(dst_image);
+      dst_res = dst->pt;
+      dst_level = dst_image->Level;
+      dst_z += dst_image->Face;
+   } else {
+      struct st_renderbuffer *dst = st_renderbuffer(dst_renderbuffer);
+      dst_res = dst->texture;
+      dst_level = 0;
+   }
+
+   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);
+}
+
+void
+st_init_copy_image_functions(struct dd_function_table *functions)
+{
+   functions->CopyImageSubData = st_CopyImageSubData;
+}
diff --git a/src/mesa/state_tracker/st_cb_copyimage.h b/src/mesa/state_tracker/st_cb_copyimage.h
new file mode 100644
index 0000000..d17f35c
--- /dev/null
+++ b/src/mesa/state_tracker/st_cb_copyimage.h
@@ -0,0 +1,33 @@ 
+/*
+ * Copyright 2015 Advanced Micro Devices, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * on the rights to use, copy, modify, merge, publish, distribute, sub
+ * license, and/or sell copies of the Software, and to permit persons to whom
+ * the Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * 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
+ * THE AUTHOR(S) AND/OR THEIR 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.
+ *
+ */
+
+#ifndef ST_CB_COPY_IMAGE_H
+#define ST_CB_COPY_IMAGE_H
+
+struct dd_function_table;
+
+extern void
+st_init_copy_image_functions(struct dd_function_table *functions);
+
+#endif /* ST_CB_COPY_IMAGE_H */
diff --git a/src/mesa/state_tracker/st_cb_texture.c b/src/mesa/state_tracker/st_cb_texture.c
index 5d25fed..d4c916e 100644
--- a/src/mesa/state_tracker/st_cb_texture.c
+++ b/src/mesa/state_tracker/st_cb_texture.c
@@ -1873,55 +1873,6 @@  st_TextureView(struct gl_context *ctx,
    return GL_TRUE;
 }
 
-/* HACK: this is only enough for the most basic uses of CopyImage. Must fix
- * before actually exposing the extension.
- */
-static void
-st_CopyImageSubData(struct gl_context *ctx,
-                    struct gl_texture_image *src_image,
-                    struct gl_renderbuffer *src_renderbuffer,
-                    int src_x, int src_y, int src_z,
-                    struct gl_texture_image *dst_image,
-                    struct gl_renderbuffer *dst_renderbuffer,
-                    int dst_x, int dst_y, int dst_z,
-                    int src_width, int src_height)
-{
-   struct st_context *st = st_context(ctx);
-   struct pipe_context *pipe = st->pipe;
-   struct pipe_resource *src_res, *dst_res;
-   struct pipe_box box;
-   int src_level, dst_level;
-
-   if (src_image) {
-      struct st_texture_image *src = st_texture_image(src_image);
-      src_res = src->pt;
-      src_level = src_image->Level;
-   }
-   else {
-      struct st_renderbuffer *src = st_renderbuffer(src_renderbuffer);
-      src_res = src->texture;
-      src_level = 0;
-   }
-
-   if (dst_image) {
-      struct st_texture_image *dst = st_texture_image(dst_image);
-      dst_res = dst->pt;
-      dst_level = dst_image->Level;
-   }
-   else {
-      struct st_renderbuffer *dst = st_renderbuffer(dst_renderbuffer);
-      dst_res = dst->texture;
-      dst_level = 0;
-   }
-
-   u_box_2d_zslice(src_x, src_y, src_z, src_width, src_height, &box);
-   pipe->resource_copy_region(pipe, dst_res, dst_level,
-                              dst_x, dst_y, dst_z,
-                              src_res, src_level,
-                              &box);
-}
-
-
 void
 st_init_texture_functions(struct dd_function_table *functions)
 {
@@ -1953,6 +1904,4 @@  st_init_texture_functions(struct dd_function_table *functions)
 
    functions->AllocTextureStorage = st_AllocTextureStorage;
    functions->TextureView = st_TextureView;
-
-   functions->CopyImageSubData = st_CopyImageSubData;
 }
diff --git a/src/mesa/state_tracker/st_context.c b/src/mesa/state_tracker/st_context.c
index 5abb173..6e20fd1 100644
--- a/src/mesa/state_tracker/st_context.c
+++ b/src/mesa/state_tracker/st_context.c
@@ -44,6 +44,7 @@ 
 #include "st_cb_bufferobjects.h"
 #include "st_cb_clear.h"
 #include "st_cb_condrender.h"
+#include "st_cb_copyimage.h"
 #include "st_cb_drawpixels.h"
 #include "st_cb_rasterpos.h"
 #include "st_cb_drawtex.h"
@@ -430,6 +431,7 @@  void st_init_driver_functions(struct pipe_screen *screen,
    st_init_bufferobject_functions(functions);
    st_init_clear_functions(functions);
    st_init_bitmap_functions(functions);
+   st_init_copy_image_functions(functions);
    st_init_drawpixels_functions(functions);
    st_init_rasterpos_functions(functions);
 
diff --git a/src/mesa/state_tracker/st_extensions.c b/src/mesa/state_tracker/st_extensions.c
index d4724b4..bd7cbcc 100644
--- a/src/mesa/state_tracker/st_extensions.c
+++ b/src/mesa/state_tracker/st_extensions.c
@@ -439,6 +439,7 @@  void st_init_extensions(struct pipe_screen *screen,
       { o(ARB_base_instance),                PIPE_CAP_START_INSTANCE                   },
       { o(ARB_buffer_storage),               PIPE_CAP_BUFFER_MAP_PERSISTENT_COHERENT   },
       { o(ARB_color_buffer_float),           PIPE_CAP_VERTEX_COLOR_UNCLAMPED           },
+      { o(ARB_copy_image),                   PIPE_CAP_COPY_BETWEEN_COMPRESSED_AND_PLAIN_FORMATS },
       { o(ARB_depth_clamp),                  PIPE_CAP_DEPTH_CLIP_DISABLE               },
       { o(ARB_depth_texture),                PIPE_CAP_TEXTURE_SHADOW_MAP               },
       { o(ARB_draw_buffers_blend),           PIPE_CAP_INDEP_BLEND_FUNC                 },

Comments

Looks good to me.  Yeah, it is kind of crazy.  I think softpipe/llvmpipe 
just need one of my previous patches, plus minor updates.  I'll take 
care of that.

Just one minor nit below.

Oh, and you could probably mention support for GL_ARB_copy_image in the 
release notes file and GL3.txt.

Reviewed-by: Brian Paul <brianp@vmware.com>


On 10/25/2015 11:25 AM, Marek Olšák wrote:
> From: Marek Olšák <marek.olsak@amd.com>
>
> I wonder if the craziness was worth it.
> ---
>   src/mesa/Makefile.sources                |   2 +
>   src/mesa/state_tracker/st_cb_copyimage.c | 609 +++++++++++++++++++++++++++++++
>   src/mesa/state_tracker/st_cb_copyimage.h |  33 ++
>   src/mesa/state_tracker/st_cb_texture.c   |  51 ---
>   src/mesa/state_tracker/st_context.c      |   2 +
>   src/mesa/state_tracker/st_extensions.c   |   1 +
>   6 files changed, 647 insertions(+), 51 deletions(-)
>   create mode 100644 src/mesa/state_tracker/st_cb_copyimage.c
>   create mode 100644 src/mesa/state_tracker/st_cb_copyimage.h
>
> diff --git a/src/mesa/Makefile.sources b/src/mesa/Makefile.sources
> index 4bcaa62..de0e330 100644
> --- a/src/mesa/Makefile.sources
> +++ b/src/mesa/Makefile.sources
> @@ -423,6 +423,8 @@ STATETRACKER_FILES = \
>   	state_tracker/st_cb_clear.h \
>   	state_tracker/st_cb_condrender.c \
>   	state_tracker/st_cb_condrender.h \
> +	state_tracker/st_cb_copyimage.c \
> +	state_tracker/st_cb_copyimage.h \
>   	state_tracker/st_cb_drawpixels.c \
>   	state_tracker/st_cb_drawpixels.h \
>   	state_tracker/st_cb_drawpixels_shader.c \
> diff --git a/src/mesa/state_tracker/st_cb_copyimage.c b/src/mesa/state_tracker/st_cb_copyimage.c
> new file mode 100644
> index 0000000..1740aaf
> --- /dev/null
> +++ b/src/mesa/state_tracker/st_cb_copyimage.c
> @@ -0,0 +1,609 @@
> +/*
> + * Copyright 2015 Advanced Micro Devices, Inc.
> + *
> + * Permission is hereby granted, free of charge, to any person obtaining a
> + * copy of this software and associated documentation files (the "Software"),
> + * to deal in the Software without restriction, including without limitation
> + * on the rights to use, copy, modify, merge, publish, distribute, sub
> + * license, and/or sell copies of the Software, and to permit persons to whom
> + * the Software is furnished to do so, subject to the following conditions:
> + *
> + * The above copyright notice and this permission notice (including the next
> + * paragraph) shall be included in all copies or substantial portions of the
> + * Software.
> + *
> + * 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
> + * THE AUTHOR(S) AND/OR THEIR 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 "state_tracker/st_context.h"
> +#include "state_tracker/st_cb_copyimage.h"
> +#include "state_tracker/st_cb_fbo.h"
> +#include "state_tracker/st_texture.h"
> +
> +#include "util/u_box.h"
> +#include "util/u_format.h"
> +#include "util/u_inlines.h"
> +
> +
> +/**
> + * Return an equivalent canonical format without "X" channels.
> + *
> + * Copying between incompatible formats is easier when the format is
> + * canonicalized, meaning that it is in a standard form.
> + *
> + * The returned format has the same component sizes and swizzles as
> + * the source format, the type is changed to UINT or UNORM, depending on
> + * which one has the most swizzle combinations in their group.
> + *
> + * If it's not an array format, return a memcpy-equivalent array format.
> + *
> + * The key feature is that swizzled versions of formats of the same
> + * component size always return the same component type.
> + *
> + * X returns A.
> + * Luminance, intensity, alpha, depth, stencil, and 8-bit and 16-bit packed
> + * formats are not supported. (same as ARB_copy_image)
> + */
> +static enum pipe_format
> +get_canonical_format(enum pipe_format format)
> +{
> +   const struct util_format_description *desc =
> +      util_format_description(format);
> +
> +   /* Packed formats. Return the equivalent array format. */
> +   if (format == PIPE_FORMAT_R11G11B10_FLOAT ||
> +       format == PIPE_FORMAT_R9G9B9E5_FLOAT)
> +      return get_canonical_format(PIPE_FORMAT_R8G8B8A8_UINT);
> +
> +   if (desc->nr_channels == 4 &&
> +       desc->channel[0].size == 10 &&
> +       desc->channel[1].size == 10 &&
> +       desc->channel[2].size == 10 &&
> +       desc->channel[3].size == 2) {
> +      if (desc->swizzle[0] == UTIL_FORMAT_SWIZZLE_X &&
> +          desc->swizzle[1] == UTIL_FORMAT_SWIZZLE_Y &&
> +          desc->swizzle[2] == UTIL_FORMAT_SWIZZLE_Z)
> +         return get_canonical_format(PIPE_FORMAT_R8G8B8A8_UINT);
> +
> +      return PIPE_FORMAT_NONE;
> +   }
> +
> +#define RETURN_FOR_SWIZZLE1(x, format) \
> +   if (desc->swizzle[0] == UTIL_FORMAT_SWIZZLE_##x) \
> +      return format
> +
> +#define RETURN_FOR_SWIZZLE2(x, y, format) \
> +   if (desc->swizzle[0] == UTIL_FORMAT_SWIZZLE_##x && \
> +       desc->swizzle[1] == UTIL_FORMAT_SWIZZLE_##y) \
> +      return format
> +
> +#define RETURN_FOR_SWIZZLE3(x, y, z, format) \
> +   if (desc->swizzle[0] == UTIL_FORMAT_SWIZZLE_##x && \
> +       desc->swizzle[1] == UTIL_FORMAT_SWIZZLE_##y && \
> +       desc->swizzle[2] == UTIL_FORMAT_SWIZZLE_##z) \
> +      return format
> +
> +#define RETURN_FOR_SWIZZLE4(x, y, z, w, format) \
> +   if (desc->swizzle[0] == UTIL_FORMAT_SWIZZLE_##x && \
> +       desc->swizzle[1] == UTIL_FORMAT_SWIZZLE_##y && \
> +       desc->swizzle[2] == UTIL_FORMAT_SWIZZLE_##z && \
> +       desc->swizzle[3] == UTIL_FORMAT_SWIZZLE_##w) \
> +      return format
> +
> +   /* Array formats. */
> +   if (desc->is_array) {
> +      switch (desc->nr_channels) {
> +      case 1:
> +         switch (desc->channel[0].size) {
> +         case 8:
> +            RETURN_FOR_SWIZZLE1(X, PIPE_FORMAT_R8_UINT);
> +            break;
> +
> +         case 16:
> +            RETURN_FOR_SWIZZLE1(X, PIPE_FORMAT_R16_UINT);
> +            break;
> +
> +         case 32:
> +            RETURN_FOR_SWIZZLE1(X, PIPE_FORMAT_R32_UINT);
> +            break;
> +         }
> +         break;
> +
> +      case 2:
> +         switch (desc->channel[0].size) {
> +         case 8:
> +            /* All formats in each group must be of the same type.
> +             * We can't use UINT for R8G8 while using UNORM for G8R8.
> +             */
> +            RETURN_FOR_SWIZZLE2(X, Y, PIPE_FORMAT_R8G8_UNORM);
> +            RETURN_FOR_SWIZZLE2(Y, X, PIPE_FORMAT_G8R8_UNORM);
> +            break;
> +
> +         case 16:
> +            RETURN_FOR_SWIZZLE2(X, Y, PIPE_FORMAT_R16G16_UNORM);
> +            RETURN_FOR_SWIZZLE2(Y, X, PIPE_FORMAT_G16R16_UNORM);
> +            break;
> +
> +         case 32:
> +            RETURN_FOR_SWIZZLE2(X, Y, PIPE_FORMAT_R32G32_UINT);
> +            break;
> +         }
> +         break;
> +
> +      case 3:
> +         switch (desc->channel[0].size) {
> +         case 8:
> +            RETURN_FOR_SWIZZLE3(X, Y, Z, PIPE_FORMAT_R8G8B8_UINT);
> +            break;
> +
> +         case 16:
> +            RETURN_FOR_SWIZZLE3(X, Y, Z, PIPE_FORMAT_R16G16B16_UINT);
> +            break;
> +
> +         case 32:
> +            RETURN_FOR_SWIZZLE3(X, Y, Z, PIPE_FORMAT_R32G32B32_UINT);
> +            break;
> +         }
> +         break;
> +
> +      case 4:
> +         switch (desc->channel[0].size) {
> +         case 8:
> +            RETURN_FOR_SWIZZLE4(X, Y, Z, W, PIPE_FORMAT_R8G8B8A8_UNORM);
> +            RETURN_FOR_SWIZZLE4(X, Y, Z, 1, PIPE_FORMAT_R8G8B8A8_UNORM);
> +            RETURN_FOR_SWIZZLE4(Z, Y, X, W, PIPE_FORMAT_B8G8R8A8_UNORM);
> +            RETURN_FOR_SWIZZLE4(Z, Y, X, 1, PIPE_FORMAT_B8G8R8A8_UNORM);
> +            RETURN_FOR_SWIZZLE4(W, Z, Y, X, PIPE_FORMAT_A8B8G8R8_UNORM);
> +            RETURN_FOR_SWIZZLE4(1, Z, Y, X, PIPE_FORMAT_A8B8G8R8_UNORM);
> +            RETURN_FOR_SWIZZLE4(W, X, Y, Z, PIPE_FORMAT_A8R8G8B8_UNORM);
> +            RETURN_FOR_SWIZZLE4(1, X, Y, Z, PIPE_FORMAT_A8R8G8B8_UNORM);
> +            break;
> +
> +         case 16:
> +            RETURN_FOR_SWIZZLE4(X, Y, Z, W, PIPE_FORMAT_R16G16B16A16_UINT);
> +            RETURN_FOR_SWIZZLE4(X, Y, Z, 1, PIPE_FORMAT_R16G16B16A16_UINT);
> +            break;
> +
> +         case 32:
> +            RETURN_FOR_SWIZZLE4(X, Y, Z, W, PIPE_FORMAT_R32G32B32A32_UINT);
> +            RETURN_FOR_SWIZZLE4(X, Y, Z, 1, PIPE_FORMAT_R32G32B32A32_UINT);
> +            break;
> +         }
> +      }
> +
> +      unreachable("unknown array format");
> +      return PIPE_FORMAT_NONE;
> +   }
> +
> +   unreachable("unknown packed format");
> +   return PIPE_FORMAT_NONE;
> +}
> +
> +/**
> + * Return true if the swizzle is XYZW in case of a 4-channel format,
> + * XY in case of a 2-channel format, or X in case of a 1-channel format.
> + */
> +static bool
> +has_identity_swizzle(const struct util_format_description *desc)
> +{
> +   int i;
> +
> +   for (i = 0; i < desc->nr_channels; i++)
> +      if (desc->swizzle[i] != UTIL_FORMAT_SWIZZLE_X + i)
> +         return false;
> +
> +   return true;
> +}
> +
> +/**
> + * Return a canonical format for the given bits and channel size.
> + */
> +static enum pipe_format
> +canonical_format_from_bits(unsigned bits, unsigned channel_size)
> +{
> +   switch (bits) {
> +   case 8:
> +      if (channel_size == 8)
> +         return get_canonical_format(PIPE_FORMAT_R8_UINT);
> +      break;
> +
> +   case 16:
> +      if (channel_size == 8)
> +         return get_canonical_format(PIPE_FORMAT_R8G8_UINT);
> +      if (channel_size == 16)
> +         return get_canonical_format(PIPE_FORMAT_R16_UINT);
> +      break;
> +
> +   case 32:
> +      if (channel_size == 8)
> +         return get_canonical_format(PIPE_FORMAT_R8G8B8A8_UINT);
> +      if (channel_size == 16)
> +         return get_canonical_format(PIPE_FORMAT_R16G16_UINT);
> +      if (channel_size == 32)
> +         return get_canonical_format(PIPE_FORMAT_R32_UINT);
> +      break;
> +
> +   case 64:
> +      if (channel_size == 16)
> +         return get_canonical_format(PIPE_FORMAT_R16G16B16A16_UINT);
> +      if (channel_size == 32)
> +         return get_canonical_format(PIPE_FORMAT_R32G32_UINT);
> +      break;
> +
> +   case 128:
> +      if (channel_size == 32)
> +         return get_canonical_format(PIPE_FORMAT_R32G32B32A32_UINT);
> +      break;
> +   }
> +
> +   unreachable("impossible format");
> +   return PIPE_FORMAT_NONE;
> +}
> +
> +static void
> +blit(struct pipe_context *pipe,
> +     struct pipe_resource *dst,
> +     enum pipe_format dst_format,
> +     unsigned dst_level,
> +     unsigned dstx, unsigned dsty, unsigned dstz,
> +     struct pipe_resource *src,
> +     enum pipe_format src_format,
> +     unsigned src_level,
> +     const struct pipe_box *src_box)
> +{
> +   struct pipe_blit_info blit = {{0}};
> +
> +   blit.src.resource = src;
> +   blit.dst.resource = dst;
> +   blit.src.format = src_format;
> +   blit.dst.format = dst_format;
> +   blit.src.level = src_level;
> +   blit.dst.level = dst_level;
> +   blit.src.box = *src_box;
> +   u_box_3d(dstx, dsty, dstz, src_box->width, src_box->height,
> +            src_box->depth, &blit.dst.box);
> +   blit.mask = PIPE_MASK_RGBA;
> +   blit.filter = PIPE_TEX_FILTER_NEAREST;
> +
> +   pipe->blit(pipe, &blit);
> +}
> +
> +static void
> +swizzled_copy(struct pipe_context *pipe,
> +              struct pipe_resource *dst,
> +              unsigned dst_level,
> +              unsigned dstx, unsigned dsty, unsigned dstz,
> +              struct pipe_resource *src,
> +              unsigned src_level,
> +              const struct pipe_box *src_box)
> +{
> +   const struct util_format_description *src_desc, *dst_desc;
> +   unsigned bits;
> +   enum pipe_format blit_src_format, blit_dst_format;
> +
> +   /* Get equivalent canonical formats. Those are always array formats and
> +    * copying between compatible canonical formats behaves either like
> +    * memcpy or like swizzled memcpy. The idea is that we won't have to care
> +    * about the channel type from this point on.
> +    * Only the swizzle and channel size.
> +    */
> +   blit_src_format = get_canonical_format(src->format);
> +   blit_dst_format = get_canonical_format(dst->format);
> +
> +   assert(blit_src_format != PIPE_FORMAT_NONE);
> +   assert(blit_dst_format != PIPE_FORMAT_NONE);
> +
> +   src_desc = util_format_description(blit_src_format);
> +   dst_desc = util_format_description(blit_dst_format);
> +
> +   assert(src_desc->block.bits == dst_desc->block.bits);
> +   bits = src_desc->block.bits;
> +
> +   if (dst_desc->channel[0].size == src_desc->channel[0].size) {
> +      /* Only the swizzle is different, which means we can just blit,
> +       * e.g. RGBA -> BGRA.
> +       */
> +   } else if (has_identity_swizzle(src_desc)) {
> +      /* Src is unswizzled and dst can be swizzled, so src is typecast
> +       * to an equivalent dst-compatible format.
> +       * e.g. R32 -> BGRA8 is realized as RGBA8 -> BGRA8
> +       */
> +      blit_src_format =
> +         canonical_format_from_bits(bits, dst_desc->channel[0].size);
> +   } else if (has_identity_swizzle(dst_desc)) {
> +      /* Dst is unswizzled and src can be swizzled, so dst is typecast
> +       * to an equivalent src-compatible format.
> +       * e.g. BGRA8 -> R32 is realized as BGRA8 -> RGBA8
> +       */
> +      blit_dst_format =
> +         canonical_format_from_bits(bits, src_desc->channel[0].size);
> +   } else {
> +      unreachable("This should have been handled by handle_complex_copy.");
> +      return;
> +   }
> +
> +   blit(pipe, dst, blit_dst_format, dst_level, dstx, dsty, dstz,
> +        src, blit_src_format, src_level, src_box);
> +}
> +
> +static bool
> +same_size_and_swizzle(const struct util_format_description *d1,
> +                      const struct util_format_description *d2)
> +{
> +   int i;
> +
> +   if (d1->layout != d2->layout ||
> +       d1->nr_channels != d2->nr_channels ||
> +       d1->is_array != d2->is_array)
> +      return false;
> +
> +   for (i = 0; i < d1->nr_channels; i++) {
> +      if (d1->channel[i].size != d2->channel[i].size)
> +         return false;
> +
> +      if (d1->swizzle[i] <= UTIL_FORMAT_SWIZZLE_W &&
> +          d2->swizzle[i] <= UTIL_FORMAT_SWIZZLE_W &&
> +          d1->swizzle[i] != d2->swizzle[i])
> +         return false;
> +   }
> +
> +   return true;
> +}
> +
> +static bool
> +is_bgr10_a2_variant(const struct util_format_description *desc)
> +{
> +   return desc->nr_channels == 4 &&
> +          desc->channel[0].size == 10 &&
> +          desc->channel[1].size == 10 &&
> +          desc->channel[2].size == 10 &&
> +          desc->channel[3].size == 2 &&
> +          desc->swizzle[0] == UTIL_FORMAT_SWIZZLE_Z &&
> +          desc->swizzle[1] == UTIL_FORMAT_SWIZZLE_Y &&
> +          desc->swizzle[2] == UTIL_FORMAT_SWIZZLE_X;
> +}
> +
> +static bool
> +is_rgb10_a2_variant(const struct util_format_description *desc)
> +{
> +   return desc->nr_channels == 4 &&
> +          desc->channel[0].size == 10 &&
> +          desc->channel[1].size == 10 &&
> +          desc->channel[2].size == 10 &&
> +          desc->channel[3].size == 2 &&
> +          desc->swizzle[0] == UTIL_FORMAT_SWIZZLE_X &&
> +          desc->swizzle[1] == UTIL_FORMAT_SWIZZLE_Y &&
> +          desc->swizzle[2] == UTIL_FORMAT_SWIZZLE_Z;
> +}
> +
> +static struct pipe_resource *
> +create_texture(struct pipe_screen *screen, enum pipe_format format,
> +               unsigned width, unsigned height, unsigned depth)
> +{
> +   struct pipe_resource templ;
> +
> +   memset(&templ, 0, sizeof(templ));
> +   templ.format = format;
> +   templ.width0 = width;
> +   templ.height0 = height;
> +   templ.depth0 = 1;
> +   templ.array_size = depth;
> +   templ.usage = PIPE_USAGE_DEFAULT;
> +   templ.bind = PIPE_BIND_SAMPLER_VIEW | PIPE_BIND_RENDER_TARGET;
> +
> +   if (depth > 1)
> +           templ.target = PIPE_TEXTURE_2D_ARRAY;
> +   else
> +           templ.target = PIPE_TEXTURE_2D;

Too much indentation for those two clauses.


> +
> +   return screen->resource_create(screen, &templ);
> +}
> +
> +/**
> + * Handle complex format conversions using 2 blits with a temporary texture
> + * in between, e.g. blitting from B10G10R10A2 to G16R16.
> + *
> + * This example is implemented this way:
> + * 1) First, blit from B10G10R10A2 to R10G10B10A2, which is canonical, so it
> + *    can be reinterpreted as a different canonical format of the same bpp,
> + *    such as R16G16. This blit only swaps R and B 10-bit components.
> + * 2) Finnaly, blit the result, which is R10G10B10A2, as R16G16 to G16R16.
> + *    This blit only swaps R and G 16-bit components.
> + */
> +static bool
> +handle_complex_copy(struct pipe_context *pipe,
> +                    struct pipe_resource *dst,
> +                    unsigned dst_level,
> +                    unsigned dstx, unsigned dsty, unsigned dstz,
> +                    struct pipe_resource *src,
> +                    unsigned src_level,
> +                    const struct pipe_box *src_box,
> +                    enum pipe_format noncanon_format,
> +                    enum pipe_format canon_format)
> +{
> +   struct pipe_box temp_box;
> +   struct pipe_resource *temp = NULL;
> +   const struct util_format_description *src_desc, *dst_desc;
> +   const struct util_format_description *canon_desc, *noncanon_desc;
> +   bool src_is_canon;
> +   bool src_is_noncanon;
> +   bool dst_is_canon;
> +   bool dst_is_noncanon;
> +
> +   src_desc = util_format_description(src->format);
> +   dst_desc = util_format_description(dst->format);
> +   canon_desc = util_format_description(canon_format);
> +   noncanon_desc = util_format_description(noncanon_format);
> +
> +   src_is_canon = same_size_and_swizzle(src_desc, canon_desc);
> +   dst_is_canon = same_size_and_swizzle(dst_desc, canon_desc);
> +   src_is_noncanon = same_size_and_swizzle(src_desc, noncanon_desc);
> +   dst_is_noncanon = same_size_and_swizzle(dst_desc, noncanon_desc);
> +
> +   src_is_canon = is_rgb10_a2_variant(src_desc);
> +   dst_is_canon = is_rgb10_a2_variant(dst_desc);
> +   src_is_noncanon = is_bgr10_a2_variant(src_desc);
> +   dst_is_noncanon = is_bgr10_a2_variant(dst_desc);
> +
> +   if (src_is_noncanon) {
> +      /* Simple case - only types differ (e.g. UNORM and UINT). */
> +      if (dst_is_noncanon) {
> +         blit(pipe, dst, noncanon_format, dst_level, dstx, dsty, dstz, src,
> +              noncanon_format, src_level, src_box);
> +         return true;
> +      }
> +
> +      /* Simple case - only types and swizzles differ. */
> +      if (dst_is_canon) {
> +         blit(pipe, dst, canon_format, dst_level, dstx, dsty, dstz, src,
> +              noncanon_format, src_level, src_box);
> +         return true;
> +      }
> +
> +      /* Use the temporary texture. Src is converted to a canonical format,
> +       * then proceed the generic swizzled_copy.
> +       */
> +      temp = create_texture(pipe->screen, canon_format, src_box->width,
> +                            src_box->height, src_box->depth);
> +
> +      u_box_3d(0, 0, 0, src_box->width, src_box->height, src_box->depth,
> +               &temp_box);
> +
> +      blit(pipe, temp, canon_format, 0, 0, 0, 0, src, noncanon_format,
> +           src_level, src_box);
> +      swizzled_copy(pipe, dst, dst_level, dstx, dsty, dstz, temp, 0,
> +                    &temp_box);
> +      pipe_resource_reference(&temp, NULL);
> +      return true;
> +   }
> +
> +   if (dst_is_noncanon) {
> +      /* Simple case - only types and swizzles differ. */
> +      if (src_is_canon) {
> +         blit(pipe, dst, noncanon_format, dst_level, dstx, dsty, dstz, src,
> +              canon_format, src_level, src_box);
> +         return true;
> +      }
> +
> +      /* Use the temporary texture. First, use the generic copy, but use
> +       * a canonical format in the destination. Then convert */
> +      temp = create_texture(pipe->screen, canon_format, src_box->width,
> +                            src_box->height, src_box->depth);
> +
> +      u_box_3d(0, 0, 0, src_box->width, src_box->height, src_box->depth,
> +               &temp_box);
> +
> +      swizzled_copy(pipe, temp, 0, 0, 0, 0, src, src_level, src_box);
> +      blit(pipe, dst, noncanon_format, dst_level, dstx, dsty, dstz, temp,
> +           canon_format, 0, &temp_box);
> +      pipe_resource_reference(&temp, NULL);
> +      return true;
> +   }
> +
> +   return false;
> +}
> +
> +static void
> +copy_image(struct pipe_context *pipe,
> +           struct pipe_resource *dst,
> +           unsigned dst_level,
> +           unsigned dstx, unsigned dsty, unsigned dstz,
> +           struct pipe_resource *src,
> +           unsigned src_level,
> +           const struct pipe_box *src_box)
> +{
> +   if (src->format == dst->format ||
> +       util_format_is_compressed(src->format) ||
> +       util_format_is_compressed(dst->format)) {
> +      pipe->resource_copy_region(pipe, dst, dst_level, dstx, dsty, dstz,
> +                                 src, src_level, src_box);
> +      return;
> +   }
> +
> +   /* Copying to/from B10G10R10*2 needs 2 blits with R10G10B10A2
> +    * as a temporary texture in between.
> +    */
> +   if (handle_complex_copy(pipe, dst, dst_level, dstx, dsty, dstz, src,
> +                           src_level, src_box, PIPE_FORMAT_B10G10R10A2_UINT,
> +                           PIPE_FORMAT_R10G10B10A2_UINT))
> +      return;
> +
> +   /* Copying to/from G8R8 needs 2 blits with R8G8 as a temporary texture
> +    * in between.
> +    */
> +   if (handle_complex_copy(pipe, dst, dst_level, dstx, dsty, dstz, src,
> +                           src_level, src_box, PIPE_FORMAT_G8R8_UNORM,
> +                           PIPE_FORMAT_R8G8_UNORM))
> +      return;
> +
> +   /* Copying to/from G16R16 needs 2 blits with R16G16 as a temporary texture
> +    * in between.
> +    */
> +   if (handle_complex_copy(pipe, dst, dst_level, dstx, dsty, dstz, src,
> +                           src_level, src_box, PIPE_FORMAT_G16R16_UNORM,
> +                           PIPE_FORMAT_R16G16_UNORM))
> +      return;
> +
> +   /* Only allow non-identity swizzling on RGBA8 formats. */
> +
> +   /* Simple copy, memcpy with swizzling, no format conversion. */
> +   swizzled_copy(pipe, dst, dst_level, dstx, dsty, dstz, src, src_level,
> +                 src_box);
> +}
> +
> +static void
> +st_CopyImageSubData(struct gl_context *ctx,
> +                    struct gl_texture_image *src_image,
> +                    struct gl_renderbuffer *src_renderbuffer,
> +                    int src_x, int src_y, int src_z,
> +                    struct gl_texture_image *dst_image,
> +                    struct gl_renderbuffer *dst_renderbuffer,
> +                    int dst_x, int dst_y, int dst_z,
> +                    int src_width, int src_height)
> +{
> +   struct st_context *st = st_context(ctx);
> +   struct pipe_context *pipe = st->pipe;
> +   struct pipe_resource *src_res, *dst_res;
> +   struct pipe_box box;
> +   int src_level, dst_level;
> +
> +   if (src_image) {
> +      struct st_texture_image *src = st_texture_image(src_image);
> +      src_res = src->pt;
> +      src_level = src_image->Level;
> +      src_z += src_image->Face;
> +   } else {
> +      struct st_renderbuffer *src = st_renderbuffer(src_renderbuffer);
> +      src_res = src->texture;
> +      src_level = 0;
> +   }
> +
> +   if (dst_image) {
> +      struct st_texture_image *dst = st_texture_image(dst_image);
> +      dst_res = dst->pt;
> +      dst_level = dst_image->Level;
> +      dst_z += dst_image->Face;
> +   } else {
> +      struct st_renderbuffer *dst = st_renderbuffer(dst_renderbuffer);
> +      dst_res = dst->texture;
> +      dst_level = 0;
> +   }
> +
> +   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);
> +}
> +
> +void
> +st_init_copy_image_functions(struct dd_function_table *functions)
> +{
> +   functions->CopyImageSubData = st_CopyImageSubData;
> +}
> diff --git a/src/mesa/state_tracker/st_cb_copyimage.h b/src/mesa/state_tracker/st_cb_copyimage.h
> new file mode 100644
> index 0000000..d17f35c
> --- /dev/null
> +++ b/src/mesa/state_tracker/st_cb_copyimage.h
> @@ -0,0 +1,33 @@
> +/*
> + * Copyright 2015 Advanced Micro Devices, Inc.
> + *
> + * Permission is hereby granted, free of charge, to any person obtaining a
> + * copy of this software and associated documentation files (the "Software"),
> + * to deal in the Software without restriction, including without limitation
> + * on the rights to use, copy, modify, merge, publish, distribute, sub
> + * license, and/or sell copies of the Software, and to permit persons to whom
> + * the Software is furnished to do so, subject to the following conditions:
> + *
> + * The above copyright notice and this permission notice (including the next
> + * paragraph) shall be included in all copies or substantial portions of the
> + * Software.
> + *
> + * 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
> + * THE AUTHOR(S) AND/OR THEIR 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.
> + *
> + */
> +
> +#ifndef ST_CB_COPY_IMAGE_H
> +#define ST_CB_COPY_IMAGE_H
> +
> +struct dd_function_table;
> +
> +extern void
> +st_init_copy_image_functions(struct dd_function_table *functions);
> +
> +#endif /* ST_CB_COPY_IMAGE_H */
> diff --git a/src/mesa/state_tracker/st_cb_texture.c b/src/mesa/state_tracker/st_cb_texture.c
> index 5d25fed..d4c916e 100644
> --- a/src/mesa/state_tracker/st_cb_texture.c
> +++ b/src/mesa/state_tracker/st_cb_texture.c
> @@ -1873,55 +1873,6 @@ st_TextureView(struct gl_context *ctx,
>      return GL_TRUE;
>   }
>
> -/* HACK: this is only enough for the most basic uses of CopyImage. Must fix
> - * before actually exposing the extension.
> - */
> -static void
> -st_CopyImageSubData(struct gl_context *ctx,
> -                    struct gl_texture_image *src_image,
> -                    struct gl_renderbuffer *src_renderbuffer,
> -                    int src_x, int src_y, int src_z,
> -                    struct gl_texture_image *dst_image,
> -                    struct gl_renderbuffer *dst_renderbuffer,
> -                    int dst_x, int dst_y, int dst_z,
> -                    int src_width, int src_height)
> -{
> -   struct st_context *st = st_context(ctx);
> -   struct pipe_context *pipe = st->pipe;
> -   struct pipe_resource *src_res, *dst_res;
> -   struct pipe_box box;
> -   int src_level, dst_level;
> -
> -   if (src_image) {
> -      struct st_texture_image *src = st_texture_image(src_image);
> -      src_res = src->pt;
> -      src_level = src_image->Level;
> -   }
> -   else {
> -      struct st_renderbuffer *src = st_renderbuffer(src_renderbuffer);
> -      src_res = src->texture;
> -      src_level = 0;
> -   }
> -
> -   if (dst_image) {
> -      struct st_texture_image *dst = st_texture_image(dst_image);
> -      dst_res = dst->pt;
> -      dst_level = dst_image->Level;
> -   }
> -   else {
> -      struct st_renderbuffer *dst = st_renderbuffer(dst_renderbuffer);
> -      dst_res = dst->texture;
> -      dst_level = 0;
> -   }
> -
> -   u_box_2d_zslice(src_x, src_y, src_z, src_width, src_height, &box);
> -   pipe->resource_copy_region(pipe, dst_res, dst_level,
> -                              dst_x, dst_y, dst_z,
> -                              src_res, src_level,
> -                              &box);
> -}
> -
> -
>   void
>   st_init_texture_functions(struct dd_function_table *functions)
>   {
> @@ -1953,6 +1904,4 @@ st_init_texture_functions(struct dd_function_table *functions)
>
>      functions->AllocTextureStorage = st_AllocTextureStorage;
>      functions->TextureView = st_TextureView;
> -
> -   functions->CopyImageSubData = st_CopyImageSubData;
>   }
> diff --git a/src/mesa/state_tracker/st_context.c b/src/mesa/state_tracker/st_context.c
> index 5abb173..6e20fd1 100644
> --- a/src/mesa/state_tracker/st_context.c
> +++ b/src/mesa/state_tracker/st_context.c
> @@ -44,6 +44,7 @@
>   #include "st_cb_bufferobjects.h"
>   #include "st_cb_clear.h"
>   #include "st_cb_condrender.h"
> +#include "st_cb_copyimage.h"
>   #include "st_cb_drawpixels.h"
>   #include "st_cb_rasterpos.h"
>   #include "st_cb_drawtex.h"
> @@ -430,6 +431,7 @@ void st_init_driver_functions(struct pipe_screen *screen,
>      st_init_bufferobject_functions(functions);
>      st_init_clear_functions(functions);
>      st_init_bitmap_functions(functions);
> +   st_init_copy_image_functions(functions);
>      st_init_drawpixels_functions(functions);
>      st_init_rasterpos_functions(functions);
>
> diff --git a/src/mesa/state_tracker/st_extensions.c b/src/mesa/state_tracker/st_extensions.c
> index d4724b4..bd7cbcc 100644
> --- a/src/mesa/state_tracker/st_extensions.c
> +++ b/src/mesa/state_tracker/st_extensions.c
> @@ -439,6 +439,7 @@ void st_init_extensions(struct pipe_screen *screen,
>         { o(ARB_base_instance),                PIPE_CAP_START_INSTANCE                   },
>         { o(ARB_buffer_storage),               PIPE_CAP_BUFFER_MAP_PERSISTENT_COHERENT   },
>         { o(ARB_color_buffer_float),           PIPE_CAP_VERTEX_COLOR_UNCLAMPED           },
> +      { o(ARB_copy_image),                   PIPE_CAP_COPY_BETWEEN_COMPRESSED_AND_PLAIN_FORMATS },
>         { o(ARB_depth_clamp),                  PIPE_CAP_DEPTH_CLIP_DISABLE               },
>         { o(ARB_depth_texture),                PIPE_CAP_TEXTURE_SHADOW_MAP               },
>         { o(ARB_draw_buffers_blend),           PIPE_CAP_INDEP_BLEND_FUNC                 },
>
On Sun, Oct 25, 2015 at 1:25 PM, Marek Olšák <maraeo@gmail.com> wrote:
> From: Marek Olšák <marek.olsak@amd.com>
>
> I wonder if the craziness was worth it.
> ---
>  src/mesa/Makefile.sources                |   2 +
>  src/mesa/state_tracker/st_cb_copyimage.c | 609 +++++++++++++++++++++++++++++++
>  src/mesa/state_tracker/st_cb_copyimage.h |  33 ++
>  src/mesa/state_tracker/st_cb_texture.c   |  51 ---
>  src/mesa/state_tracker/st_context.c      |   2 +
>  src/mesa/state_tracker/st_extensions.c   |   1 +
>  6 files changed, 647 insertions(+), 51 deletions(-)
>  create mode 100644 src/mesa/state_tracker/st_cb_copyimage.c
>  create mode 100644 src/mesa/state_tracker/st_cb_copyimage.h
>
> diff --git a/src/mesa/Makefile.sources b/src/mesa/Makefile.sources
> index 4bcaa62..de0e330 100644
> --- a/src/mesa/Makefile.sources
> +++ b/src/mesa/Makefile.sources
> @@ -423,6 +423,8 @@ STATETRACKER_FILES = \
>         state_tracker/st_cb_clear.h \
>         state_tracker/st_cb_condrender.c \
>         state_tracker/st_cb_condrender.h \
> +       state_tracker/st_cb_copyimage.c \
> +       state_tracker/st_cb_copyimage.h \
>         state_tracker/st_cb_drawpixels.c \
>         state_tracker/st_cb_drawpixels.h \
>         state_tracker/st_cb_drawpixels_shader.c \
> diff --git a/src/mesa/state_tracker/st_cb_copyimage.c b/src/mesa/state_tracker/st_cb_copyimage.c
> new file mode 100644
> index 0000000..1740aaf
> --- /dev/null
> +++ b/src/mesa/state_tracker/st_cb_copyimage.c
> @@ -0,0 +1,609 @@
> +/*
> + * Copyright 2015 Advanced Micro Devices, Inc.
> + *
> + * Permission is hereby granted, free of charge, to any person obtaining a
> + * copy of this software and associated documentation files (the "Software"),
> + * to deal in the Software without restriction, including without limitation
> + * on the rights to use, copy, modify, merge, publish, distribute, sub
> + * license, and/or sell copies of the Software, and to permit persons to whom
> + * the Software is furnished to do so, subject to the following conditions:
> + *
> + * The above copyright notice and this permission notice (including the next
> + * paragraph) shall be included in all copies or substantial portions of the
> + * Software.
> + *
> + * 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
> + * THE AUTHOR(S) AND/OR THEIR 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 "state_tracker/st_context.h"
> +#include "state_tracker/st_cb_copyimage.h"
> +#include "state_tracker/st_cb_fbo.h"
> +#include "state_tracker/st_texture.h"
> +
> +#include "util/u_box.h"
> +#include "util/u_format.h"
> +#include "util/u_inlines.h"
> +
> +
> +/**
> + * Return an equivalent canonical format without "X" channels.
> + *
> + * Copying between incompatible formats is easier when the format is
> + * canonicalized, meaning that it is in a standard form.
> + *
> + * The returned format has the same component sizes and swizzles as
> + * the source format, the type is changed to UINT or UNORM, depending on
> + * which one has the most swizzle combinations in their group.
> + *
> + * If it's not an array format, return a memcpy-equivalent array format.
> + *
> + * The key feature is that swizzled versions of formats of the same
> + * component size always return the same component type.
> + *
> + * X returns A.
> + * Luminance, intensity, alpha, depth, stencil, and 8-bit and 16-bit packed
> + * formats are not supported. (same as ARB_copy_image)
> + */
> +static enum pipe_format
> +get_canonical_format(enum pipe_format format)
> +{
> +   const struct util_format_description *desc =
> +      util_format_description(format);
> +
> +   /* Packed formats. Return the equivalent array format. */
> +   if (format == PIPE_FORMAT_R11G11B10_FLOAT ||
> +       format == PIPE_FORMAT_R9G9B9E5_FLOAT)
> +      return get_canonical_format(PIPE_FORMAT_R8G8B8A8_UINT);
> +
> +   if (desc->nr_channels == 4 &&
> +       desc->channel[0].size == 10 &&
> +       desc->channel[1].size == 10 &&
> +       desc->channel[2].size == 10 &&
> +       desc->channel[3].size == 2) {
> +      if (desc->swizzle[0] == UTIL_FORMAT_SWIZZLE_X &&
> +          desc->swizzle[1] == UTIL_FORMAT_SWIZZLE_Y &&
> +          desc->swizzle[2] == UTIL_FORMAT_SWIZZLE_Z)
> +         return get_canonical_format(PIPE_FORMAT_R8G8B8A8_UINT);
> +
> +      return PIPE_FORMAT_NONE;
> +   }
> +
> +#define RETURN_FOR_SWIZZLE1(x, format) \
> +   if (desc->swizzle[0] == UTIL_FORMAT_SWIZZLE_##x) \
> +      return format
> +
> +#define RETURN_FOR_SWIZZLE2(x, y, format) \
> +   if (desc->swizzle[0] == UTIL_FORMAT_SWIZZLE_##x && \
> +       desc->swizzle[1] == UTIL_FORMAT_SWIZZLE_##y) \
> +      return format
> +
> +#define RETURN_FOR_SWIZZLE3(x, y, z, format) \
> +   if (desc->swizzle[0] == UTIL_FORMAT_SWIZZLE_##x && \
> +       desc->swizzle[1] == UTIL_FORMAT_SWIZZLE_##y && \
> +       desc->swizzle[2] == UTIL_FORMAT_SWIZZLE_##z) \
> +      return format
> +
> +#define RETURN_FOR_SWIZZLE4(x, y, z, w, format) \
> +   if (desc->swizzle[0] == UTIL_FORMAT_SWIZZLE_##x && \
> +       desc->swizzle[1] == UTIL_FORMAT_SWIZZLE_##y && \
> +       desc->swizzle[2] == UTIL_FORMAT_SWIZZLE_##z && \
> +       desc->swizzle[3] == UTIL_FORMAT_SWIZZLE_##w) \
> +      return format
> +
> +   /* Array formats. */
> +   if (desc->is_array) {
> +      switch (desc->nr_channels) {
> +      case 1:
> +         switch (desc->channel[0].size) {
> +         case 8:
> +            RETURN_FOR_SWIZZLE1(X, PIPE_FORMAT_R8_UINT);
> +            break;
> +
> +         case 16:
> +            RETURN_FOR_SWIZZLE1(X, PIPE_FORMAT_R16_UINT);
> +            break;
> +
> +         case 32:
> +            RETURN_FOR_SWIZZLE1(X, PIPE_FORMAT_R32_UINT);
> +            break;
> +         }
> +         break;
> +
> +      case 2:
> +         switch (desc->channel[0].size) {
> +         case 8:
> +            /* All formats in each group must be of the same type.
> +             * We can't use UINT for R8G8 while using UNORM for G8R8.
> +             */
> +            RETURN_FOR_SWIZZLE2(X, Y, PIPE_FORMAT_R8G8_UNORM);
> +            RETURN_FOR_SWIZZLE2(Y, X, PIPE_FORMAT_G8R8_UNORM);
> +            break;
> +
> +         case 16:
> +            RETURN_FOR_SWIZZLE2(X, Y, PIPE_FORMAT_R16G16_UNORM);
> +            RETURN_FOR_SWIZZLE2(Y, X, PIPE_FORMAT_G16R16_UNORM);
> +            break;
> +
> +         case 32:
> +            RETURN_FOR_SWIZZLE2(X, Y, PIPE_FORMAT_R32G32_UINT);
> +            break;
> +         }
> +         break;
> +
> +      case 3:
> +         switch (desc->channel[0].size) {
> +         case 8:
> +            RETURN_FOR_SWIZZLE3(X, Y, Z, PIPE_FORMAT_R8G8B8_UINT);
> +            break;
> +
> +         case 16:
> +            RETURN_FOR_SWIZZLE3(X, Y, Z, PIPE_FORMAT_R16G16B16_UINT);
> +            break;
> +
> +         case 32:
> +            RETURN_FOR_SWIZZLE3(X, Y, Z, PIPE_FORMAT_R32G32B32_UINT);
> +            break;
> +         }
> +         break;
> +
> +      case 4:
> +         switch (desc->channel[0].size) {
> +         case 8:
> +            RETURN_FOR_SWIZZLE4(X, Y, Z, W, PIPE_FORMAT_R8G8B8A8_UNORM);
> +            RETURN_FOR_SWIZZLE4(X, Y, Z, 1, PIPE_FORMAT_R8G8B8A8_UNORM);
> +            RETURN_FOR_SWIZZLE4(Z, Y, X, W, PIPE_FORMAT_B8G8R8A8_UNORM);
> +            RETURN_FOR_SWIZZLE4(Z, Y, X, 1, PIPE_FORMAT_B8G8R8A8_UNORM);
> +            RETURN_FOR_SWIZZLE4(W, Z, Y, X, PIPE_FORMAT_A8B8G8R8_UNORM);
> +            RETURN_FOR_SWIZZLE4(1, Z, Y, X, PIPE_FORMAT_A8B8G8R8_UNORM);
> +            RETURN_FOR_SWIZZLE4(W, X, Y, Z, PIPE_FORMAT_A8R8G8B8_UNORM);
> +            RETURN_FOR_SWIZZLE4(1, X, Y, Z, PIPE_FORMAT_A8R8G8B8_UNORM);
> +            break;
> +
> +         case 16:
> +            RETURN_FOR_SWIZZLE4(X, Y, Z, W, PIPE_FORMAT_R16G16B16A16_UINT);
> +            RETURN_FOR_SWIZZLE4(X, Y, Z, 1, PIPE_FORMAT_R16G16B16A16_UINT);
> +            break;
> +
> +         case 32:
> +            RETURN_FOR_SWIZZLE4(X, Y, Z, W, PIPE_FORMAT_R32G32B32A32_UINT);
> +            RETURN_FOR_SWIZZLE4(X, Y, Z, 1, PIPE_FORMAT_R32G32B32A32_UINT);
> +            break;
> +         }
> +      }
> +
> +      unreachable("unknown array format");

Unreachable is *not* the same thing as assert. It's an infinite loop.
Anything after it is dead code. (You do get the assert should you hit
it with assertions enabled, but there's no fallthrough.) So either
remove the return below or flip it into an assert. (Both here and
elsewhere)

> +      return PIPE_FORMAT_NONE;
> +   }
> +
> +   unreachable("unknown packed format");
> +   return PIPE_FORMAT_NONE;
> +}
> +
> +/**
> + * Return true if the swizzle is XYZW in case of a 4-channel format,
> + * XY in case of a 2-channel format, or X in case of a 1-channel format.
> + */
> +static bool
> +has_identity_swizzle(const struct util_format_description *desc)
> +{
> +   int i;
> +
> +   for (i = 0; i < desc->nr_channels; i++)
> +      if (desc->swizzle[i] != UTIL_FORMAT_SWIZZLE_X + i)
> +         return false;
> +
> +   return true;
> +}
> +
> +/**
> + * Return a canonical format for the given bits and channel size.
> + */
> +static enum pipe_format
> +canonical_format_from_bits(unsigned bits, unsigned channel_size)
> +{
> +   switch (bits) {
> +   case 8:
> +      if (channel_size == 8)
> +         return get_canonical_format(PIPE_FORMAT_R8_UINT);
> +      break;
> +
> +   case 16:
> +      if (channel_size == 8)
> +         return get_canonical_format(PIPE_FORMAT_R8G8_UINT);
> +      if (channel_size == 16)
> +         return get_canonical_format(PIPE_FORMAT_R16_UINT);
> +      break;
> +
> +   case 32:
> +      if (channel_size == 8)
> +         return get_canonical_format(PIPE_FORMAT_R8G8B8A8_UINT);
> +      if (channel_size == 16)
> +         return get_canonical_format(PIPE_FORMAT_R16G16_UINT);
> +      if (channel_size == 32)
> +         return get_canonical_format(PIPE_FORMAT_R32_UINT);
> +      break;
> +
> +   case 64:
> +      if (channel_size == 16)
> +         return get_canonical_format(PIPE_FORMAT_R16G16B16A16_UINT);
> +      if (channel_size == 32)
> +         return get_canonical_format(PIPE_FORMAT_R32G32_UINT);
> +      break;
> +
> +   case 128:
> +      if (channel_size == 32)
> +         return get_canonical_format(PIPE_FORMAT_R32G32B32A32_UINT);
> +      break;
> +   }
> +
> +   unreachable("impossible format");
> +   return PIPE_FORMAT_NONE;
> +}
> +
> +static void
> +blit(struct pipe_context *pipe,
> +     struct pipe_resource *dst,
> +     enum pipe_format dst_format,
> +     unsigned dst_level,
> +     unsigned dstx, unsigned dsty, unsigned dstz,
> +     struct pipe_resource *src,
> +     enum pipe_format src_format,
> +     unsigned src_level,
> +     const struct pipe_box *src_box)
> +{
> +   struct pipe_blit_info blit = {{0}};
> +
> +   blit.src.resource = src;
> +   blit.dst.resource = dst;
> +   blit.src.format = src_format;
> +   blit.dst.format = dst_format;
> +   blit.src.level = src_level;
> +   blit.dst.level = dst_level;
> +   blit.src.box = *src_box;
> +   u_box_3d(dstx, dsty, dstz, src_box->width, src_box->height,
> +            src_box->depth, &blit.dst.box);
> +   blit.mask = PIPE_MASK_RGBA;
> +   blit.filter = PIPE_TEX_FILTER_NEAREST;

Is glCopyImageSubData supposed to respect render conditions? If so,
don't forget to set the blit.render_condition_enable flag.

> +
> +   pipe->blit(pipe, &blit);
> +}
> +
> +static void
> +swizzled_copy(struct pipe_context *pipe,
> +              struct pipe_resource *dst,
> +              unsigned dst_level,
> +              unsigned dstx, unsigned dsty, unsigned dstz,
> +              struct pipe_resource *src,
> +              unsigned src_level,
> +              const struct pipe_box *src_box)
> +{
> +   const struct util_format_description *src_desc, *dst_desc;
> +   unsigned bits;
> +   enum pipe_format blit_src_format, blit_dst_format;
> +
> +   /* Get equivalent canonical formats. Those are always array formats and
> +    * copying between compatible canonical formats behaves either like
> +    * memcpy or like swizzled memcpy. The idea is that we won't have to care
> +    * about the channel type from this point on.
> +    * Only the swizzle and channel size.
> +    */
> +   blit_src_format = get_canonical_format(src->format);
> +   blit_dst_format = get_canonical_format(dst->format);
> +
> +   assert(blit_src_format != PIPE_FORMAT_NONE);
> +   assert(blit_dst_format != PIPE_FORMAT_NONE);
> +
> +   src_desc = util_format_description(blit_src_format);
> +   dst_desc = util_format_description(blit_dst_format);
> +
> +   assert(src_desc->block.bits == dst_desc->block.bits);
> +   bits = src_desc->block.bits;
> +
> +   if (dst_desc->channel[0].size == src_desc->channel[0].size) {
> +      /* Only the swizzle is different, which means we can just blit,
> +       * e.g. RGBA -> BGRA.
> +       */
> +   } else if (has_identity_swizzle(src_desc)) {
> +      /* Src is unswizzled and dst can be swizzled, so src is typecast
> +       * to an equivalent dst-compatible format.
> +       * e.g. R32 -> BGRA8 is realized as RGBA8 -> BGRA8
> +       */
> +      blit_src_format =
> +         canonical_format_from_bits(bits, dst_desc->channel[0].size);
> +   } else if (has_identity_swizzle(dst_desc)) {
> +      /* Dst is unswizzled and src can be swizzled, so dst is typecast
> +       * to an equivalent src-compatible format.
> +       * e.g. BGRA8 -> R32 is realized as BGRA8 -> RGBA8
> +       */
> +      blit_dst_format =
> +         canonical_format_from_bits(bits, src_desc->channel[0].size);
> +   } else {
> +      unreachable("This should have been handled by handle_complex_copy.");
> +      return;
> +   }
> +
> +   blit(pipe, dst, blit_dst_format, dst_level, dstx, dsty, dstz,
> +        src, blit_src_format, src_level, src_box);
> +}
> +
> +static bool
> +same_size_and_swizzle(const struct util_format_description *d1,
> +                      const struct util_format_description *d2)
> +{
> +   int i;
> +
> +   if (d1->layout != d2->layout ||
> +       d1->nr_channels != d2->nr_channels ||
> +       d1->is_array != d2->is_array)
> +      return false;
> +
> +   for (i = 0; i < d1->nr_channels; i++) {
> +      if (d1->channel[i].size != d2->channel[i].size)
> +         return false;
> +
> +      if (d1->swizzle[i] <= UTIL_FORMAT_SWIZZLE_W &&
> +          d2->swizzle[i] <= UTIL_FORMAT_SWIZZLE_W &&
> +          d1->swizzle[i] != d2->swizzle[i])
> +         return false;
> +   }
> +
> +   return true;
> +}
> +
> +static bool
> +is_bgr10_a2_variant(const struct util_format_description *desc)
> +{
> +   return desc->nr_channels == 4 &&
> +          desc->channel[0].size == 10 &&
> +          desc->channel[1].size == 10 &&
> +          desc->channel[2].size == 10 &&
> +          desc->channel[3].size == 2 &&
> +          desc->swizzle[0] == UTIL_FORMAT_SWIZZLE_Z &&
> +          desc->swizzle[1] == UTIL_FORMAT_SWIZZLE_Y &&
> +          desc->swizzle[2] == UTIL_FORMAT_SWIZZLE_X;
> +}
> +
> +static bool
> +is_rgb10_a2_variant(const struct util_format_description *desc)
> +{
> +   return desc->nr_channels == 4 &&
> +          desc->channel[0].size == 10 &&
> +          desc->channel[1].size == 10 &&
> +          desc->channel[2].size == 10 &&
> +          desc->channel[3].size == 2 &&
> +          desc->swizzle[0] == UTIL_FORMAT_SWIZZLE_X &&
> +          desc->swizzle[1] == UTIL_FORMAT_SWIZZLE_Y &&
> +          desc->swizzle[2] == UTIL_FORMAT_SWIZZLE_Z;
> +}
> +
> +static struct pipe_resource *
> +create_texture(struct pipe_screen *screen, enum pipe_format format,
> +               unsigned width, unsigned height, unsigned depth)
> +{
> +   struct pipe_resource templ;
> +
> +   memset(&templ, 0, sizeof(templ));
> +   templ.format = format;
> +   templ.width0 = width;
> +   templ.height0 = height;
> +   templ.depth0 = 1;
> +   templ.array_size = depth;
> +   templ.usage = PIPE_USAGE_DEFAULT;
> +   templ.bind = PIPE_BIND_SAMPLER_VIEW | PIPE_BIND_RENDER_TARGET;
> +
> +   if (depth > 1)
> +           templ.target = PIPE_TEXTURE_2D_ARRAY;
> +   else
> +           templ.target = PIPE_TEXTURE_2D;
> +
> +   return screen->resource_create(screen, &templ);
> +}
> +
> +/**
> + * Handle complex format conversions using 2 blits with a temporary texture
> + * in between, e.g. blitting from B10G10R10A2 to G16R16.
> + *
> + * This example is implemented this way:
> + * 1) First, blit from B10G10R10A2 to R10G10B10A2, which is canonical, so it
> + *    can be reinterpreted as a different canonical format of the same bpp,
> + *    such as R16G16. This blit only swaps R and B 10-bit components.
> + * 2) Finnaly, blit the result, which is R10G10B10A2, as R16G16 to G16R16.
> + *    This blit only swaps R and G 16-bit components.
> + */
> +static bool
> +handle_complex_copy(struct pipe_context *pipe,
> +                    struct pipe_resource *dst,
> +                    unsigned dst_level,
> +                    unsigned dstx, unsigned dsty, unsigned dstz,
> +                    struct pipe_resource *src,
> +                    unsigned src_level,
> +                    const struct pipe_box *src_box,
> +                    enum pipe_format noncanon_format,
> +                    enum pipe_format canon_format)
> +{
> +   struct pipe_box temp_box;
> +   struct pipe_resource *temp = NULL;
> +   const struct util_format_description *src_desc, *dst_desc;
> +   const struct util_format_description *canon_desc, *noncanon_desc;
> +   bool src_is_canon;
> +   bool src_is_noncanon;
> +   bool dst_is_canon;
> +   bool dst_is_noncanon;
> +
> +   src_desc = util_format_description(src->format);
> +   dst_desc = util_format_description(dst->format);
> +   canon_desc = util_format_description(canon_format);
> +   noncanon_desc = util_format_description(noncanon_format);
> +
> +   src_is_canon = same_size_and_swizzle(src_desc, canon_desc);
> +   dst_is_canon = same_size_and_swizzle(dst_desc, canon_desc);
> +   src_is_noncanon = same_size_and_swizzle(src_desc, noncanon_desc);
> +   dst_is_noncanon = same_size_and_swizzle(dst_desc, noncanon_desc);
> +
> +   src_is_canon = is_rgb10_a2_variant(src_desc);
> +   dst_is_canon = is_rgb10_a2_variant(dst_desc);
> +   src_is_noncanon = is_bgr10_a2_variant(src_desc);
> +   dst_is_noncanon = is_bgr10_a2_variant(dst_desc);
> +
> +   if (src_is_noncanon) {
> +      /* Simple case - only types differ (e.g. UNORM and UINT). */
> +      if (dst_is_noncanon) {
> +         blit(pipe, dst, noncanon_format, dst_level, dstx, dsty, dstz, src,
> +              noncanon_format, src_level, src_box);
> +         return true;
> +      }
> +
> +      /* Simple case - only types and swizzles differ. */
> +      if (dst_is_canon) {
> +         blit(pipe, dst, canon_format, dst_level, dstx, dsty, dstz, src,
> +              noncanon_format, src_level, src_box);
> +         return true;
> +      }
> +
> +      /* Use the temporary texture. Src is converted to a canonical format,
> +       * then proceed the generic swizzled_copy.
> +       */
> +      temp = create_texture(pipe->screen, canon_format, src_box->width,
> +                            src_box->height, src_box->depth);
> +
> +      u_box_3d(0, 0, 0, src_box->width, src_box->height, src_box->depth,
> +               &temp_box);
> +
> +      blit(pipe, temp, canon_format, 0, 0, 0, 0, src, noncanon_format,
> +           src_level, src_box);
> +      swizzled_copy(pipe, dst, dst_level, dstx, dsty, dstz, temp, 0,
> +                    &temp_box);
> +      pipe_resource_reference(&temp, NULL);
> +      return true;
> +   }
> +
> +   if (dst_is_noncanon) {
> +      /* Simple case - only types and swizzles differ. */
> +      if (src_is_canon) {
> +         blit(pipe, dst, noncanon_format, dst_level, dstx, dsty, dstz, src,
> +              canon_format, src_level, src_box);
> +         return true;
> +      }
> +
> +      /* Use the temporary texture. First, use the generic copy, but use
> +       * a canonical format in the destination. Then convert */
> +      temp = create_texture(pipe->screen, canon_format, src_box->width,
> +                            src_box->height, src_box->depth);
> +
> +      u_box_3d(0, 0, 0, src_box->width, src_box->height, src_box->depth,
> +               &temp_box);
> +
> +      swizzled_copy(pipe, temp, 0, 0, 0, 0, src, src_level, src_box);
> +      blit(pipe, dst, noncanon_format, dst_level, dstx, dsty, dstz, temp,
> +           canon_format, 0, &temp_box);
> +      pipe_resource_reference(&temp, NULL);
> +      return true;
> +   }
> +
> +   return false;
> +}
> +
> +static void
> +copy_image(struct pipe_context *pipe,
> +           struct pipe_resource *dst,
> +           unsigned dst_level,
> +           unsigned dstx, unsigned dsty, unsigned dstz,
> +           struct pipe_resource *src,
> +           unsigned src_level,
> +           const struct pipe_box *src_box)
> +{
> +   if (src->format == dst->format ||
> +       util_format_is_compressed(src->format) ||
> +       util_format_is_compressed(dst->format)) {

If only :(

Situation: src internal format = GL_RGBA, dst internal format =
GL_BGRA, but both resources end up with PIPE_FORMAT_RGBA8.

You end up not flipping the channels here. All of these decisions must
be done based on the GL format, not the PIPE format...

  -ilia

> +      pipe->resource_copy_region(pipe, dst, dst_level, dstx, dsty, dstz,
> +                                 src, src_level, src_box);
> +      return;
> +   }
> +
> +   /* Copying to/from B10G10R10*2 needs 2 blits with R10G10B10A2
> +    * as a temporary texture in between.
> +    */
> +   if (handle_complex_copy(pipe, dst, dst_level, dstx, dsty, dstz, src,
> +                           src_level, src_box, PIPE_FORMAT_B10G10R10A2_UINT,
> +                           PIPE_FORMAT_R10G10B10A2_UINT))
> +      return;
> +
> +   /* Copying to/from G8R8 needs 2 blits with R8G8 as a temporary texture
> +    * in between.
> +    */
> +   if (handle_complex_copy(pipe, dst, dst_level, dstx, dsty, dstz, src,
> +                           src_level, src_box, PIPE_FORMAT_G8R8_UNORM,
> +                           PIPE_FORMAT_R8G8_UNORM))
> +      return;
> +
> +   /* Copying to/from G16R16 needs 2 blits with R16G16 as a temporary texture
> +    * in between.
> +    */
> +   if (handle_complex_copy(pipe, dst, dst_level, dstx, dsty, dstz, src,
> +                           src_level, src_box, PIPE_FORMAT_G16R16_UNORM,
> +                           PIPE_FORMAT_R16G16_UNORM))
> +      return;
> +
> +   /* Only allow non-identity swizzling on RGBA8 formats. */
> +
> +   /* Simple copy, memcpy with swizzling, no format conversion. */
> +   swizzled_copy(pipe, dst, dst_level, dstx, dsty, dstz, src, src_level,
> +                 src_box);
> +}
> +
> +static void
> +st_CopyImageSubData(struct gl_context *ctx,
> +                    struct gl_texture_image *src_image,
> +                    struct gl_renderbuffer *src_renderbuffer,
> +                    int src_x, int src_y, int src_z,
> +                    struct gl_texture_image *dst_image,
> +                    struct gl_renderbuffer *dst_renderbuffer,
> +                    int dst_x, int dst_y, int dst_z,
> +                    int src_width, int src_height)
> +{
> +   struct st_context *st = st_context(ctx);
> +   struct pipe_context *pipe = st->pipe;
> +   struct pipe_resource *src_res, *dst_res;
> +   struct pipe_box box;
> +   int src_level, dst_level;
> +
> +   if (src_image) {
> +      struct st_texture_image *src = st_texture_image(src_image);
> +      src_res = src->pt;
> +      src_level = src_image->Level;
> +      src_z += src_image->Face;
> +   } else {
> +      struct st_renderbuffer *src = st_renderbuffer(src_renderbuffer);
> +      src_res = src->texture;
> +      src_level = 0;
> +   }
> +
> +   if (dst_image) {
> +      struct st_texture_image *dst = st_texture_image(dst_image);
> +      dst_res = dst->pt;
> +      dst_level = dst_image->Level;
> +      dst_z += dst_image->Face;
> +   } else {
> +      struct st_renderbuffer *dst = st_renderbuffer(dst_renderbuffer);
> +      dst_res = dst->texture;
> +      dst_level = 0;
> +   }
> +
> +   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);
> +}
> +
> +void
> +st_init_copy_image_functions(struct dd_function_table *functions)
> +{
> +   functions->CopyImageSubData = st_CopyImageSubData;
> +}
> diff --git a/src/mesa/state_tracker/st_cb_copyimage.h b/src/mesa/state_tracker/st_cb_copyimage.h
> new file mode 100644
> index 0000000..d17f35c
> --- /dev/null
> +++ b/src/mesa/state_tracker/st_cb_copyimage.h
> @@ -0,0 +1,33 @@
> +/*
> + * Copyright 2015 Advanced Micro Devices, Inc.
> + *
> + * Permission is hereby granted, free of charge, to any person obtaining a
> + * copy of this software and associated documentation files (the "Software"),
> + * to deal in the Software without restriction, including without limitation
> + * on the rights to use, copy, modify, merge, publish, distribute, sub
> + * license, and/or sell copies of the Software, and to permit persons to whom
> + * the Software is furnished to do so, subject to the following conditions:
> + *
> + * The above copyright notice and this permission notice (including the next
> + * paragraph) shall be included in all copies or substantial portions of the
> + * Software.
> + *
> + * 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
> + * THE AUTHOR(S) AND/OR THEIR 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.
> + *
> + */
> +
> +#ifndef ST_CB_COPY_IMAGE_H
> +#define ST_CB_COPY_IMAGE_H
> +
> +struct dd_function_table;
> +
> +extern void
> +st_init_copy_image_functions(struct dd_function_table *functions);
> +
> +#endif /* ST_CB_COPY_IMAGE_H */
> diff --git a/src/mesa/state_tracker/st_cb_texture.c b/src/mesa/state_tracker/st_cb_texture.c
> index 5d25fed..d4c916e 100644
> --- a/src/mesa/state_tracker/st_cb_texture.c
> +++ b/src/mesa/state_tracker/st_cb_texture.c
> @@ -1873,55 +1873,6 @@ st_TextureView(struct gl_context *ctx,
>     return GL_TRUE;
>  }
>
> -/* HACK: this is only enough for the most basic uses of CopyImage. Must fix
> - * before actually exposing the extension.
> - */
> -static void
> -st_CopyImageSubData(struct gl_context *ctx,
> -                    struct gl_texture_image *src_image,
> -                    struct gl_renderbuffer *src_renderbuffer,
> -                    int src_x, int src_y, int src_z,
> -                    struct gl_texture_image *dst_image,
> -                    struct gl_renderbuffer *dst_renderbuffer,
> -                    int dst_x, int dst_y, int dst_z,
> -                    int src_width, int src_height)
> -{
> -   struct st_context *st = st_context(ctx);
> -   struct pipe_context *pipe = st->pipe;
> -   struct pipe_resource *src_res, *dst_res;
> -   struct pipe_box box;
> -   int src_level, dst_level;
> -
> -   if (src_image) {
> -      struct st_texture_image *src = st_texture_image(src_image);
> -      src_res = src->pt;
> -      src_level = src_image->Level;
> -   }
> -   else {
> -      struct st_renderbuffer *src = st_renderbuffer(src_renderbuffer);
> -      src_res = src->texture;
> -      src_level = 0;
> -   }
> -
> -   if (dst_image) {
> -      struct st_texture_image *dst = st_texture_image(dst_image);
> -      dst_res = dst->pt;
> -      dst_level = dst_image->Level;
> -   }
> -   else {
> -      struct st_renderbuffer *dst = st_renderbuffer(dst_renderbuffer);
> -      dst_res = dst->texture;
> -      dst_level = 0;
> -   }
> -
> -   u_box_2d_zslice(src_x, src_y, src_z, src_width, src_height, &box);
> -   pipe->resource_copy_region(pipe, dst_res, dst_level,
> -                              dst_x, dst_y, dst_z,
> -                              src_res, src_level,
> -                              &box);
> -}
> -
> -
>  void
>  st_init_texture_functions(struct dd_function_table *functions)
>  {
> @@ -1953,6 +1904,4 @@ st_init_texture_functions(struct dd_function_table *functions)
>
>     functions->AllocTextureStorage = st_AllocTextureStorage;
>     functions->TextureView = st_TextureView;
> -
> -   functions->CopyImageSubData = st_CopyImageSubData;
>  }
> diff --git a/src/mesa/state_tracker/st_context.c b/src/mesa/state_tracker/st_context.c
> index 5abb173..6e20fd1 100644
> --- a/src/mesa/state_tracker/st_context.c
> +++ b/src/mesa/state_tracker/st_context.c
> @@ -44,6 +44,7 @@
>  #include "st_cb_bufferobjects.h"
>  #include "st_cb_clear.h"
>  #include "st_cb_condrender.h"
> +#include "st_cb_copyimage.h"
>  #include "st_cb_drawpixels.h"
>  #include "st_cb_rasterpos.h"
>  #include "st_cb_drawtex.h"
> @@ -430,6 +431,7 @@ void st_init_driver_functions(struct pipe_screen *screen,
>     st_init_bufferobject_functions(functions);
>     st_init_clear_functions(functions);
>     st_init_bitmap_functions(functions);
> +   st_init_copy_image_functions(functions);
>     st_init_drawpixels_functions(functions);
>     st_init_rasterpos_functions(functions);
>
> diff --git a/src/mesa/state_tracker/st_extensions.c b/src/mesa/state_tracker/st_extensions.c
> index d4724b4..bd7cbcc 100644
> --- a/src/mesa/state_tracker/st_extensions.c
> +++ b/src/mesa/state_tracker/st_extensions.c
> @@ -439,6 +439,7 @@ void st_init_extensions(struct pipe_screen *screen,
>        { o(ARB_base_instance),                PIPE_CAP_START_INSTANCE                   },
>        { o(ARB_buffer_storage),               PIPE_CAP_BUFFER_MAP_PERSISTENT_COHERENT   },
>        { o(ARB_color_buffer_float),           PIPE_CAP_VERTEX_COLOR_UNCLAMPED           },
> +      { o(ARB_copy_image),                   PIPE_CAP_COPY_BETWEEN_COMPRESSED_AND_PLAIN_FORMATS },
>        { o(ARB_depth_clamp),                  PIPE_CAP_DEPTH_CLIP_DISABLE               },
>        { o(ARB_depth_texture),                PIPE_CAP_TEXTURE_SHADOW_MAP               },
>        { o(ARB_draw_buffers_blend),           PIPE_CAP_INDEP_BLEND_FUNC                 },
> --
> 2.1.4
>
> _______________________________________________
> mesa-dev mailing list
> mesa-dev@lists.freedesktop.org
> http://lists.freedesktop.org/mailman/listinfo/mesa-dev
On 25.10.2015 18:25, Marek Olšák wrote:
> +/**
> + * Handle complex format conversions using 2 blits with a temporary texture
> + * in between, e.g. blitting from B10G10R10A2 to G16R16.
> + *
> + * This example is implemented this way:
> + * 1) First, blit from B10G10R10A2 to R10G10B10A2, which is canonical, so it
> + *    can be reinterpreted as a different canonical format of the same bpp,
> + *    such as R16G16. This blit only swaps R and B 10-bit components.
> + * 2) Finnaly, blit the result, which is R10G10B10A2, as R16G16 to G16R16.
> + *    This blit only swaps R and G 16-bit components.
> + */

Typo: Finally

Nicolai
On Tue, Oct 27, 2015 at 4:26 AM, Ilia Mirkin <imirkin@alum.mit.edu> wrote:
>> +
>> +static void
>> +copy_image(struct pipe_context *pipe,
>> +           struct pipe_resource *dst,
>> +           unsigned dst_level,
>> +           unsigned dstx, unsigned dsty, unsigned dstz,
>> +           struct pipe_resource *src,
>> +           unsigned src_level,
>> +           const struct pipe_box *src_box)
>> +{
>> +   if (src->format == dst->format ||
>> +       util_format_is_compressed(src->format) ||
>> +       util_format_is_compressed(dst->format)) {
>
> If only :(
>
> Situation: src internal format = GL_RGBA, dst internal format =
> GL_BGRA, but both resources end up with PIPE_FORMAT_RGBA8.
>
> You end up not flipping the channels here. All of these decisions must
> be done based on the GL format, not the PIPE format...

I don't understand. The GL format doesn't specify component ordering,
only the pipe format does.

Marek
On 26.10.2015 02:25, Marek Olšák wrote:
> 
> +/**
> + * Handle complex format conversions using 2 blits with a temporary texture
> + * in between, e.g. blitting from B10G10R10A2 to G16R16.
> + *
> + * This example is implemented this way:
> + * 1) First, blit from B10G10R10A2 to R10G10B10A2, which is canonical, so it
> + *    can be reinterpreted as a different canonical format of the same bpp,
> + *    such as R16G16. This blit only swaps R and B 10-bit components.
> + * 2) Finnaly, blit the result, which is R10G10B10A2, as R16G16 to G16R16.
> + *    This blit only swaps R and G 16-bit components.

In this example, some of the G10 bits will be covered by R16 and some of
them by G16, so step 2) will tear the G10 bits apart and shuffle them
around, won't it? Is that really what's supposed to happen in that case?
Maybe I'm misunderstanding the example.
On Tue, Oct 27, 2015 at 10:33 AM, Michel Dänzer <michel@daenzer.net> wrote:
> On 26.10.2015 02:25, Marek Olšák wrote:
>>
>> +/**
>> + * Handle complex format conversions using 2 blits with a temporary texture
>> + * in between, e.g. blitting from B10G10R10A2 to G16R16.
>> + *
>> + * This example is implemented this way:
>> + * 1) First, blit from B10G10R10A2 to R10G10B10A2, which is canonical, so it
>> + *    can be reinterpreted as a different canonical format of the same bpp,
>> + *    such as R16G16. This blit only swaps R and B 10-bit components.
>> + * 2) Finnaly, blit the result, which is R10G10B10A2, as R16G16 to G16R16.
>> + *    This blit only swaps R and G 16-bit components.
>
> In this example, some of the G10 bits will be covered by R16 and some of
> them by G16, so step 2) will tear the G10 bits apart and shuffle them
> around, won't it? Is that really what's supposed to happen in that case?
> Maybe I'm misunderstanding the example.

Yes, that's correct. ARB_copy_image is memcpy-like. RGB10A2 -> R16G16
must be memcpy. BGRA channel ordering doesn't exist in OpenGL. The
code only deals with GR and BGRA swizzling in gallium and reorders
those to standard RGBA, so that the memcpy can be executed.

Marek
On Sun, Oct 25, 2015 at 1:25 PM, Marek Olšák <maraeo@gmail.com> wrote:
> +static void
> +st_CopyImageSubData(struct gl_context *ctx,
> +                    struct gl_texture_image *src_image,
> +                    struct gl_renderbuffer *src_renderbuffer,
> +                    int src_x, int src_y, int src_z,
> +                    struct gl_texture_image *dst_image,
> +                    struct gl_renderbuffer *dst_renderbuffer,
> +                    int dst_x, int dst_y, int dst_z,
> +                    int src_width, int src_height)
> +{
> +   struct st_context *st = st_context(ctx);
> +   struct pipe_context *pipe = st->pipe;
> +   struct pipe_resource *src_res, *dst_res;
> +   struct pipe_box box;
> +   int src_level, dst_level;
> +
> +   if (src_image) {
> +      struct st_texture_image *src = st_texture_image(src_image);
> +      src_res = src->pt;
> +      src_level = src_image->Level;
> +      src_z += src_image->Face;
> +   } else {
> +      struct st_renderbuffer *src = st_renderbuffer(src_renderbuffer);
> +      src_res = src->texture;
> +      src_level = 0;
> +   }
> +
> +   if (dst_image) {
> +      struct st_texture_image *dst = st_texture_image(dst_image);
> +      dst_res = dst->pt;
> +      dst_level = dst_image->Level;
> +      dst_z += dst_image->Face;
> +   } else {
> +      struct st_renderbuffer *dst = st_renderbuffer(dst_renderbuffer);
> +      dst_res = dst->texture;
> +      dst_level = 0;
> +   }
> +
> +   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);
> +}

I know you've already pushed this, but the thought just occurred to me
-- does this deal properly with texture views? I always get immensely
confused by those, but shouldn't the levels/layers be offset by the
texture object's minlayer/baselevel/minlevel somewhere? Possibly in
_mesa_CopyImageSubData?

  -ilia
On Wed, Oct 28, 2015 at 5:25 PM, Ilia Mirkin <imirkin@alum.mit.edu> wrote:
> On Sun, Oct 25, 2015 at 1:25 PM, Marek Olšák <maraeo@gmail.com> wrote:
>> +static void
>> +st_CopyImageSubData(struct gl_context *ctx,
>> +                    struct gl_texture_image *src_image,
>> +                    struct gl_renderbuffer *src_renderbuffer,
>> +                    int src_x, int src_y, int src_z,
>> +                    struct gl_texture_image *dst_image,
>> +                    struct gl_renderbuffer *dst_renderbuffer,
>> +                    int dst_x, int dst_y, int dst_z,
>> +                    int src_width, int src_height)
>> +{
>> +   struct st_context *st = st_context(ctx);
>> +   struct pipe_context *pipe = st->pipe;
>> +   struct pipe_resource *src_res, *dst_res;
>> +   struct pipe_box box;
>> +   int src_level, dst_level;
>> +
>> +   if (src_image) {
>> +      struct st_texture_image *src = st_texture_image(src_image);
>> +      src_res = src->pt;
>> +      src_level = src_image->Level;
>> +      src_z += src_image->Face;
>> +   } else {
>> +      struct st_renderbuffer *src = st_renderbuffer(src_renderbuffer);
>> +      src_res = src->texture;
>> +      src_level = 0;
>> +   }
>> +
>> +   if (dst_image) {
>> +      struct st_texture_image *dst = st_texture_image(dst_image);
>> +      dst_res = dst->pt;
>> +      dst_level = dst_image->Level;
>> +      dst_z += dst_image->Face;
>> +   } else {
>> +      struct st_renderbuffer *dst = st_renderbuffer(dst_renderbuffer);
>> +      dst_res = dst->texture;
>> +      dst_level = 0;
>> +   }
>> +
>> +   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);
>> +}
>
> I know you've already pushed this, but the thought just occurred to me
> -- does this deal properly with texture views? I always get immensely
> confused by those, but shouldn't the levels/layers be offset by the
> texture object's minlayer/baselevel/minlevel somewhere? Possibly in
> _mesa_CopyImageSubData?

I don't know, maybe? Intel's intel_copy_image.c doesn't ignore
MinLevel, but ignores MinLayer. Meta ignores both. So does
main/copyimage.c when it checks region bounds.

Marek