[Mesa-dev] [PATCH v2 10/11] anv: enable multiple planes per image/imageView

Lionel Landwerlin lionel.g.landwerlin at intel.com
Tue Oct 3 16:29:42 UTC 2017


This change introduce the concept of planes for image & views. It
matches the planes available in new formats.

We also refactor depth & stencil support through the usage of planes
for the sake of uniformity. In the backend (genX_cmd_buffer.c) we have
to take some care though with regard to auxilliary surfaces.
Multiplanar color buffers can have multiple auxilliary surfaces but
depth & stencil share the same HiZ one (only store in the depth
plane).

Signed-off-by: Lionel Landwerlin <lionel.g.landwerlin at intel.com>
---
 src/intel/vulkan/anv_blorp.c       | 302 ++++++++++++------
 src/intel/vulkan/anv_dump.c        |  17 +-
 src/intel/vulkan/anv_formats.c     |   3 +-
 src/intel/vulkan/anv_image.c       | 632 +++++++++++++++++++++++--------------
 src/intel/vulkan/anv_intel.c       |   4 +-
 src/intel/vulkan/anv_private.h     | 244 ++++++++++----
 src/intel/vulkan/anv_wsi.c         |   8 +-
 src/intel/vulkan/gen8_cmd_buffer.c |   2 +-
 src/intel/vulkan/genX_cmd_buffer.c | 312 ++++++++++--------
 9 files changed, 985 insertions(+), 539 deletions(-)

diff --git a/src/intel/vulkan/anv_blorp.c b/src/intel/vulkan/anv_blorp.c
index 72f482625aa..a181630a2f9 100644
--- a/src/intel/vulkan/anv_blorp.c
+++ b/src/intel/vulkan/anv_blorp.c
@@ -177,30 +177,34 @@ get_blorp_surf_for_anv_buffer(struct anv_device *device,
 
 static void
 get_blorp_surf_for_anv_image(const struct anv_image *image,
+                             uint32_t plane,
                              VkImageAspectFlags aspect,
                              enum isl_aux_usage aux_usage,
                              struct blorp_surf *blorp_surf)
 {
+   /* For the stencil surface aux_usage is always NONE. */
    if (aspect == VK_IMAGE_ASPECT_STENCIL_BIT ||
        aux_usage == ISL_AUX_USAGE_HIZ)
       aux_usage = ISL_AUX_USAGE_NONE;
 
-   const struct anv_surface *surface =
-      anv_image_get_surface_for_aspect_mask(image, aspect);
+   const struct anv_surface *surface = &image->planes[plane].surface;
 
    *blorp_surf = (struct blorp_surf) {
       .surf = &surface->isl,
       .addr = {
-         .buffer = image->bo,
-         .offset = image->offset + surface->offset,
+         .buffer = image->planes[plane].bo,
+         .offset = image->planes[plane].bo_offset + surface->offset,
       },
    };
 
    if (aux_usage != ISL_AUX_USAGE_NONE) {
-      blorp_surf->aux_surf = &image->aux_surface.isl,
+      const struct anv_surface *aux_surface =
+         &image->planes[plane].aux_surface;
+
+      blorp_surf->aux_surf = &aux_surface->isl,
       blorp_surf->aux_addr = (struct blorp_address) {
-         .buffer = image->bo,
-         .offset = image->offset + image->aux_surface.offset,
+         .buffer = image->planes[plane].bo,
+         .offset = image->planes[plane].bo_offset + aux_surface->offset,
       };
       blorp_surf->aux_usage = aux_usage;
    }
@@ -249,17 +253,44 @@ void anv_CmdCopyImage(
                 anv_get_layerCount(src_image, &pRegions[r].srcSubresource));
       }
 
-      assert(pRegions[r].srcSubresource.aspectMask ==
-             pRegions[r].dstSubresource.aspectMask);
-
-      uint32_t a;
-      for_each_bit(a, pRegions[r].dstSubresource.aspectMask) {
-         VkImageAspectFlagBits aspect = (1 << a);
-
+      VkImageAspectFlags src_mask = pRegions[r].srcSubresource.aspectMask,
+         dst_mask = pRegions[r].dstSubresource.aspectMask;
+
+      assert(anv_image_aspects_compatible(src_mask, dst_mask));
+
+      if (_mesa_bitcount(src_mask) > 1) {
+         uint32_t plane, bit_aspect;
+         anv_foreach_plane_aspect_bit(plane, bit_aspect, src_mask,
+                                      src_image->aspects) {
+            struct blorp_surf src_surf, dst_surf;
+            get_blorp_surf_for_anv_image(src_image, plane, 1UL << bit_aspect,
+                                         src_image->planes[plane].aux_usage,
+                                         &src_surf);
+            get_blorp_surf_for_anv_image(dst_image, plane, 1UL << bit_aspect,
+                                         dst_image->planes[plane].aux_usage,
+                                         &dst_surf);
+
+            for (unsigned i = 0; i < layer_count; i++) {
+               blorp_copy(&batch, &src_surf, pRegions[r].srcSubresource.mipLevel,
+                          src_base_layer + i,
+                          &dst_surf, pRegions[r].dstSubresource.mipLevel,
+                          dst_base_layer + i,
+                          srcOffset.x, srcOffset.y,
+                          dstOffset.x, dstOffset.y,
+                          extent.width, extent.height);
+            }
+         }
+      } else {
+         uint32_t src_plane = anv_image_aspect_to_plane(src_image->aspects,
+                                                        src_mask),
+            dst_plane = anv_image_aspect_to_plane(dst_image->aspects,
+                                                  dst_mask);
          struct blorp_surf src_surf, dst_surf;
-         get_blorp_surf_for_anv_image(src_image, aspect, src_image->aux_usage,
+         get_blorp_surf_for_anv_image(src_image, src_plane, src_mask,
+                                      src_image->planes[src_plane].aux_usage,
                                       &src_surf);
-         get_blorp_surf_for_anv_image(dst_image, aspect, dst_image->aux_usage,
+         get_blorp_surf_for_anv_image(dst_image, dst_plane, dst_mask,
+                                      dst_image->planes[dst_plane].aux_usage,
                                       &dst_surf);
 
          for (unsigned i = 0; i < layer_count; i++) {
@@ -307,8 +338,10 @@ copy_buffer_to_image(struct anv_cmd_buffer *cmd_buffer,
 
    for (unsigned r = 0; r < regionCount; r++) {
       const VkImageAspectFlags aspect = pRegions[r].imageSubresource.aspectMask;
+      uint32_t plane = anv_image_aspect_to_plane(anv_image->aspects, aspect);
 
-      get_blorp_surf_for_anv_image(anv_image, aspect, anv_image->aux_usage,
+      get_blorp_surf_for_anv_image(anv_image, plane, aspect,
+                                   anv_image->planes[plane].aux_usage,
                                    &image.surf);
       image.offset =
          anv_sanitize_image_offset(anv_image->type, pRegions[r].imageOffset);
@@ -455,10 +488,15 @@ void anv_CmdBlitImage(
       const VkImageSubresourceLayers *src_res = &pRegions[r].srcSubresource;
       const VkImageSubresourceLayers *dst_res = &pRegions[r].dstSubresource;
 
-      get_blorp_surf_for_anv_image(src_image, src_res->aspectMask,
-                                   src_image->aux_usage, &src);
-      get_blorp_surf_for_anv_image(dst_image, dst_res->aspectMask,
-                                   dst_image->aux_usage, &dst);
+      const uint32_t src_plane = anv_image_aspect_to_plane(src_image->aspects,
+                                                           src_res->aspectMask),
+         dst_plane = anv_image_aspect_to_plane(dst_image->aspects,
+                                               dst_res->aspectMask);
+
+      get_blorp_surf_for_anv_image(src_image, src_plane, src_res->aspectMask,
+                                   src_image->planes[src_plane].aux_usage, &src);
+      get_blorp_surf_for_anv_image(dst_image, dst_plane, dst_res->aspectMask,
+                                   dst_image->planes[dst_plane].aux_usage, &dst);
 
       struct anv_format_plane src_format =
          anv_get_plane_format(&cmd_buffer->device->info, src_image->vk_format,
@@ -749,15 +787,19 @@ void anv_CmdClearColorImage(
    struct blorp_batch batch;
    blorp_batch_init(&cmd_buffer->device->blorp, &batch, cmd_buffer, 0);
 
-   struct blorp_surf surf;
-   get_blorp_surf_for_anv_image(image, VK_IMAGE_ASPECT_COLOR_BIT,
-                                image->aux_usage, &surf);
 
    for (unsigned r = 0; r < rangeCount; r++) {
       if (pRanges[r].aspectMask == 0)
          continue;
 
-      assert(pRanges[r].aspectMask == VK_IMAGE_ASPECT_COLOR_BIT);
+      assert(pRanges[r].aspectMask & VK_IMAGE_ASPECT_ANY_COLOR_BIT);
+
+      const uint32_t plane = anv_image_aspect_to_plane(image->aspects,
+                                                       pRanges[r].aspectMask);
+
+      struct blorp_surf surf;
+      get_blorp_surf_for_anv_image(image, plane, pRanges[r].aspectMask,
+                                   image->planes[plane].aux_usage, &surf);
 
       struct anv_format_plane src_format =
          anv_get_plane_format(&cmd_buffer->device->info, image->vk_format,
@@ -803,14 +845,20 @@ void anv_CmdClearDepthStencilImage(
 
    struct blorp_surf depth, stencil;
    if (image->aspects & VK_IMAGE_ASPECT_DEPTH_BIT) {
-      get_blorp_surf_for_anv_image(image, VK_IMAGE_ASPECT_DEPTH_BIT,
+      uint32_t depth_plane =
+         anv_image_aspect_to_plane(image->aspects, VK_IMAGE_ASPECT_DEPTH_BIT);
+      get_blorp_surf_for_anv_image(image, depth_plane,
+                                   VK_IMAGE_ASPECT_DEPTH_BIT,
                                    ISL_AUX_USAGE_NONE, &depth);
    } else {
       memset(&depth, 0, sizeof(depth));
    }
 
    if (image->aspects & VK_IMAGE_ASPECT_STENCIL_BIT) {
-      get_blorp_surf_for_anv_image(image, VK_IMAGE_ASPECT_STENCIL_BIT,
+      uint32_t stencil_plane =
+         anv_image_aspect_to_plane(image->aspects, VK_IMAGE_ASPECT_STENCIL_BIT);
+      get_blorp_surf_for_anv_image(image, stencil_plane,
+                                   VK_IMAGE_ASPECT_STENCIL_BIT,
                                    ISL_AUX_USAGE_NONE, &stencil);
    } else {
       memset(&stencil, 0, sizeof(stencil));
@@ -1044,7 +1092,7 @@ void anv_CmdClearAttachments(
                     BLORP_BATCH_NO_EMIT_DEPTH_STENCIL);
 
    for (uint32_t a = 0; a < attachmentCount; ++a) {
-      if (pAttachments[a].aspectMask == VK_IMAGE_ASPECT_COLOR_BIT) {
+      if (pAttachments[a].aspectMask & VK_IMAGE_ASPECT_ANY_COLOR_BIT) {
          clear_color_attachment(cmd_buffer, &batch,
                                 &pAttachments[a],
                                 rectCount, pRects);
@@ -1130,7 +1178,7 @@ anv_cmd_buffer_clear_subpass(struct anv_cmd_buffer *cmd_buffer)
       struct anv_image_view *iview = fb->attachments[a];
       const struct anv_image *image = iview->image;
       struct blorp_surf surf;
-      get_blorp_surf_for_anv_image(image, VK_IMAGE_ASPECT_COLOR_BIT,
+      get_blorp_surf_for_anv_image(image, 0, VK_IMAGE_ASPECT_COLOR_BIT,
                                    att_state->aux_usage, &surf);
 
       if (att_state->fast_clear) {
@@ -1155,24 +1203,26 @@ anv_cmd_buffer_clear_subpass(struct anv_cmd_buffer *cmd_buffer)
          cmd_buffer->state.pending_pipe_bits |=
             ANV_PIPE_RENDER_TARGET_CACHE_FLUSH_BIT | ANV_PIPE_CS_STALL_BIT;
 
-         blorp_fast_clear(&batch, &surf, iview->isl.format,
-                          iview->isl.base_level,
-                          iview->isl.base_array_layer, fb->layers,
-                          render_area.offset.x, render_area.offset.y,
-                          render_area.offset.x + render_area.extent.width,
-                          render_area.offset.y + render_area.extent.height);
+         for (unsigned p = 0; p < iview->n_planes; p++)
+            blorp_fast_clear(&batch, &surf, iview->planes[p].isl.format,
+                             iview->planes[p].isl.base_level,
+                             iview->planes[p].isl.base_array_layer, fb->layers,
+                             render_area.offset.x, render_area.offset.y,
+                             render_area.offset.x + render_area.extent.width,
+                             render_area.offset.y + render_area.extent.height);
 
          cmd_buffer->state.pending_pipe_bits |=
             ANV_PIPE_RENDER_TARGET_CACHE_FLUSH_BIT | ANV_PIPE_CS_STALL_BIT;
       } else {
-         blorp_clear(&batch, &surf, iview->isl.format,
-                     anv_swizzle_for_render(iview->isl.swizzle),
-                     iview->isl.base_level,
-                     iview->isl.base_array_layer, fb->layers,
-                     render_area.offset.x, render_area.offset.y,
-                     render_area.offset.x + render_area.extent.width,
-                     render_area.offset.y + render_area.extent.height,
-                     vk_to_isl_color(att_state->clear_value.color), NULL);
+         for (unsigned p = 0; p < iview->n_planes; p++)
+            blorp_clear(&batch, &surf, iview->planes[p].isl.format,
+                        anv_swizzle_for_render(iview->planes[p].isl.swizzle),
+                        iview->planes[p].isl.base_level,
+                        iview->planes[p].isl.base_array_layer, fb->layers,
+                        render_area.offset.x, render_area.offset.y,
+                        render_area.offset.x + render_area.extent.width,
+                        render_area.offset.y + render_area.extent.height,
+                        vk_to_isl_color(att_state->clear_value.color), NULL);
       }
 
       att_state->pending_clear_aspects = 0;
@@ -1207,7 +1257,7 @@ anv_cmd_buffer_clear_subpass(struct anv_cmd_buffer *cmd_buffer)
           * a stencil clear in addition to using the BLORP-fallback for depth.
           */
          if (clear_depth) {
-            if (!blorp_can_hiz_clear_depth(gen, iview->isl.format,
+            if (!blorp_can_hiz_clear_depth(gen, iview->planes[0].isl.format,
                                            iview->image->samples,
                                            render_area.offset.x,
                                            render_area.offset.y,
@@ -1224,8 +1274,7 @@ anv_cmd_buffer_clear_subpass(struct anv_cmd_buffer *cmd_buffer)
                clear_with_hiz = false;
             } else if (gen == 8 &&
                        anv_can_sample_with_hiz(&cmd_buffer->device->info,
-                                               iview->aspect_mask,
-                                               iview->image->samples)) {
+                                               iview->image)) {
                /* Only gen9+ supports returning ANV_HZ_FC_VAL when sampling a
                 * fast-cleared portion of a HiZ buffer. Testing has revealed
                 * that Gen8 only supports returning 0.0f. Gens prior to gen8 do
@@ -1276,13 +1325,30 @@ anv_cmd_buffer_clear_subpass(struct anv_cmd_buffer *cmd_buffer)
    blorp_batch_finish(&batch);
 }
 
+static void
+resolve_surface(struct blorp_batch *batch,
+                struct blorp_surf *src_surf,
+                uint32_t src_level, uint32_t src_layer,
+                struct blorp_surf *dst_surf,
+                uint32_t dst_level, uint32_t dst_layer,
+                uint32_t src_x, uint32_t src_y, uint32_t dst_x, uint32_t dst_y,
+                uint32_t width, uint32_t height)
+{
+   blorp_blit(batch,
+              src_surf, src_level, src_layer,
+              ISL_FORMAT_UNSUPPORTED, ISL_SWIZZLE_IDENTITY,
+              dst_surf, dst_level, dst_layer,
+              ISL_FORMAT_UNSUPPORTED, ISL_SWIZZLE_IDENTITY,
+              src_x, src_y, src_x + width, src_y + height,
+              dst_x, dst_y, dst_x + width, dst_y + height,
+              0x2600 /* GL_NEAREST */, false, false);
+}
+
 static void
 resolve_image(struct blorp_batch *batch,
               const struct anv_image *src_image,
-              enum isl_aux_usage src_aux_usage,
               uint32_t src_level, uint32_t src_layer,
               const struct anv_image *dst_image,
-              enum isl_aux_usage dst_aux_usage,
               uint32_t dst_level, uint32_t dst_layer,
               VkImageAspectFlags aspect_mask,
               uint32_t src_x, uint32_t src_y, uint32_t dst_x, uint32_t dst_y,
@@ -1292,25 +1358,31 @@ resolve_image(struct blorp_batch *batch,
    assert(src_image->samples > 1);
    assert(dst_image->type == VK_IMAGE_TYPE_2D);
    assert(dst_image->samples == 1);
+   assert(src_image->n_planes == dst_image->n_planes);
 
-   uint32_t a;
-   for_each_bit(a, aspect_mask) {
-      VkImageAspectFlagBits aspect = 1 << a;
-
+   uint32_t plane, aspect_bit;
+   anv_foreach_plane_aspect_bit(plane, aspect_bit, aspect_mask,
+                                src_image->aspects) {
       struct blorp_surf src_surf, dst_surf;
-      get_blorp_surf_for_anv_image(src_image, aspect,
-                                   src_aux_usage, &src_surf);
-      get_blorp_surf_for_anv_image(dst_image, aspect,
-                                   dst_aux_usage, &dst_surf);
-
-      blorp_blit(batch,
-                 &src_surf, src_level, src_layer,
-                 ISL_FORMAT_UNSUPPORTED, ISL_SWIZZLE_IDENTITY,
-                 &dst_surf, dst_level, dst_layer,
-                 ISL_FORMAT_UNSUPPORTED, ISL_SWIZZLE_IDENTITY,
-                 src_x, src_y, src_x + width, src_y + height,
-                 dst_x, dst_y, dst_x + width, dst_y + height,
-                 0x2600 /* GL_NEAREST */, false, false);
+      get_blorp_surf_for_anv_image(src_image, plane, 1UL << aspect_bit,
+                                   src_image->planes[plane].aux_usage,
+                                   &src_surf);
+      get_blorp_surf_for_anv_image(dst_image, plane, 1UL << aspect_bit,
+                                   dst_image->planes[plane].aux_usage,
+                                   &dst_surf);
+
+      uint32_t width_div = src_image->format->planes[plane].denominator_scales[0],
+         height_div = src_image->format->planes[plane].denominator_scales[1];
+
+      resolve_surface(batch,
+                      &src_surf, src_level, src_layer,
+                      &dst_surf, dst_level, dst_layer,
+                      src_x / width_div,
+                      src_y / height_div,
+                      dst_x / width_div,
+                      dst_y / height_div,
+                      width / width_div,
+                      height / height_div);
    }
 }
 
@@ -1339,12 +1411,17 @@ void anv_CmdResolveImage(
       const uint32_t layer_count =
          anv_get_layerCount(dst_image, &pRegions[r].dstSubresource);
 
+      VkImageAspectFlags src_mask = pRegions[r].srcSubresource.aspectMask,
+         dst_mask = pRegions[r].dstSubresource.aspectMask;
+
+      assert(anv_image_aspects_compatible(src_mask, dst_mask));
+
       for (uint32_t layer = 0; layer < layer_count; layer++) {
          resolve_image(&batch,
-                       src_image, src_image->aux_usage,
+                       src_image,
                        pRegions[r].srcSubresource.mipLevel,
                        pRegions[r].srcSubresource.baseArrayLayer + layer,
-                       dst_image, dst_image->aux_usage,
+                       dst_image,
                        pRegions[r].dstSubresource.mipLevel,
                        pRegions[r].dstSubresource.baseArrayLayer + layer,
                        pRegions[r].dstSubresource.aspectMask,
@@ -1360,6 +1437,7 @@ void anv_CmdResolveImage(
 void
 anv_image_fast_clear(struct anv_cmd_buffer *cmd_buffer,
                      const struct anv_image *image,
+                     const uint32_t plane,
                      const uint32_t base_level, const uint32_t level_count,
                      const uint32_t base_layer, uint32_t layer_count)
 {
@@ -1374,9 +1452,9 @@ anv_image_fast_clear(struct anv_cmd_buffer *cmd_buffer,
    blorp_batch_init(&cmd_buffer->device->blorp, &batch, cmd_buffer, 0);
 
    struct blorp_surf surf;
-   get_blorp_surf_for_anv_image(image, VK_IMAGE_ASPECT_COLOR_BIT,
-                                image->aux_usage == ISL_AUX_USAGE_NONE ?
-                                ISL_AUX_USAGE_CCS_D : image->aux_usage,
+   get_blorp_surf_for_anv_image(image, plane, VK_IMAGE_ASPECT_COLOR_BIT,
+                                image->planes[plane].aux_usage == ISL_AUX_USAGE_NONE ?
+                                ISL_AUX_USAGE_CCS_D : image->planes[plane].aux_usage,
                                 &surf);
 
    /* From the Sky Lake PRM Vol. 7, "Render Target Fast Clear":
@@ -1397,6 +1475,9 @@ anv_image_fast_clear(struct anv_cmd_buffer *cmd_buffer,
    cmd_buffer->state.pending_pipe_bits |=
       ANV_PIPE_RENDER_TARGET_CACHE_FLUSH_BIT | ANV_PIPE_CS_STALL_BIT;
 
+   uint32_t width_div = image->format->planes[plane].denominator_scales[0],
+      height_div = image->format->planes[plane].denominator_scales[1];
+
    for (uint32_t l = 0; l < level_count; l++) {
       const uint32_t level = base_level + l;
 
@@ -1409,11 +1490,13 @@ anv_image_fast_clear(struct anv_cmd_buffer *cmd_buffer,
       if (image->type == VK_IMAGE_TYPE_3D)
          layer_count = extent.depth;
 
-      assert(level < anv_image_aux_levels(image));
-      assert(base_layer + layer_count <= anv_image_aux_layers(image, level));
+      assert(level < anv_image_aux_levels(image, plane));
+      assert(base_layer + layer_count <= anv_image_aux_layers(image, plane, level));
       blorp_fast_clear(&batch, &surf, surf.surf->format,
                        level, base_layer, layer_count,
-                       0, 0, extent.width, extent.height);
+                       0, 0,
+                       extent.width / width_div,
+                       extent.height / height_div);
    }
 
    cmd_buffer->state.pending_pipe_bits |=
@@ -1470,17 +1553,34 @@ anv_cmd_buffer_resolve_subpass(struct anv_cmd_buffer *cmd_buffer)
          const VkRect2D render_area = cmd_buffer->state.render_area;
 
          assert(src_iview->aspect_mask == dst_iview->aspect_mask);
+         assert(src_iview->n_planes == 1);
+         assert(dst_iview->n_planes == 1);
 
-         resolve_image(&batch, src_iview->image, src_aux_usage,
-                       src_iview->isl.base_level,
-                       src_iview->isl.base_array_layer,
-                       dst_iview->image, dst_aux_usage,
-                       dst_iview->isl.base_level,
-                       dst_iview->isl.base_array_layer,
-                       src_iview->aspect_mask,
-                       render_area.offset.x, render_area.offset.y,
-                       render_area.offset.x, render_area.offset.y,
-                       render_area.extent.width, render_area.extent.height);
+         const struct anv_format *format = src_iview->image->format;
+
+         uint32_t width_div = format->planes[0].denominator_scales[0],
+            height_div = format->planes[0].denominator_scales[1];
+
+         struct blorp_surf src_surf, dst_surf;
+         get_blorp_surf_for_anv_image(src_iview->image, 0,
+                                      src_iview->aspect_mask,
+                                      src_aux_usage, &src_surf);
+         get_blorp_surf_for_anv_image(dst_iview->image, 0,
+                                      dst_iview->aspect_mask,
+                                      dst_aux_usage, &dst_surf);
+
+         resolve_surface(&batch, &src_surf,
+                         src_iview->planes[0].isl.base_level,
+                         src_iview->planes[0].isl.base_array_layer,
+                         &dst_surf,
+                         dst_iview->planes[0].isl.base_level,
+                         dst_iview->planes[0].isl.base_array_layer,
+                         render_area.offset.x / width_div,
+                         render_area.offset.y / height_div,
+                         render_area.offset.x / width_div,
+                         render_area.offset.y / height_div,
+                         render_area.extent.width / width_div,
+                         render_area.extent.height / height_div);
       }
 
       blorp_batch_finish(&batch);
@@ -1490,7 +1590,7 @@ anv_cmd_buffer_resolve_subpass(struct anv_cmd_buffer *cmd_buffer)
 void
 anv_image_copy_to_shadow(struct anv_cmd_buffer *cmd_buffer,
                          const struct anv_image *image,
-                         VkImageAspectFlagBits aspect,
+                         uint32_t plane,
                          uint32_t base_level, uint32_t level_count,
                          uint32_t base_layer, uint32_t layer_count)
 {
@@ -1498,14 +1598,15 @@ anv_image_copy_to_shadow(struct anv_cmd_buffer *cmd_buffer,
    blorp_batch_init(&cmd_buffer->device->blorp, &batch, cmd_buffer, 0);
 
    struct blorp_surf surf;
-   get_blorp_surf_for_anv_image(image, VK_IMAGE_ASPECT_COLOR_BIT,
+   get_blorp_surf_for_anv_image(image, plane, VK_IMAGE_ASPECT_COLOR_BIT,
                                 ISL_AUX_USAGE_NONE, &surf);
 
    struct blorp_surf shadow_surf = {
-      .surf = &image->shadow_surface.isl,
+      .surf = &image->planes[plane].shadow_surface.isl,
       .addr = {
-         .buffer = image->bo,
-         .offset = image->offset + image->shadow_surface.offset,
+         .buffer = image->planes[plane].bo,
+         .offset = image->planes[plane].bo_offset +
+                   image->planes[plane].shadow_surface.offset,
       },
    };
 
@@ -1544,7 +1645,7 @@ anv_gen8_hiz_op_resolve(struct anv_cmd_buffer *cmd_buffer,
     * don't perform such a resolve on gens that don't support it.
     */
    if (cmd_buffer->device->info.gen < 8 ||
-       image->aux_usage != ISL_AUX_USAGE_HIZ)
+       image->planes[0].aux_usage != ISL_AUX_USAGE_HIZ)
       return;
 
    assert(op == BLORP_HIZ_OP_HIZ_RESOLVE ||
@@ -1554,14 +1655,15 @@ anv_gen8_hiz_op_resolve(struct anv_cmd_buffer *cmd_buffer,
    blorp_batch_init(&cmd_buffer->device->blorp, &batch, cmd_buffer, 0);
 
    struct blorp_surf surf;
-   get_blorp_surf_for_anv_image(image, VK_IMAGE_ASPECT_DEPTH_BIT,
+   get_blorp_surf_for_anv_image(image, 0, VK_IMAGE_ASPECT_DEPTH_BIT,
                                 ISL_AUX_USAGE_NONE, &surf);
 
    /* Manually add the aux HiZ surf */
-   surf.aux_surf = &image->aux_surface.isl,
+   surf.aux_surf = &image->planes[0].aux_surface.isl,
    surf.aux_addr = (struct blorp_address) {
-      .buffer = image->bo,
-      .offset = image->offset + image->aux_surface.offset,
+      .buffer = image->planes[0].bo,
+      .offset = image->planes[0].bo_offset +
+                image->planes[0].aux_surface.offset,
    };
    surf.aux_usage = ISL_AUX_USAGE_HIZ;
 
@@ -1575,15 +1677,16 @@ void
 anv_ccs_resolve(struct anv_cmd_buffer * const cmd_buffer,
                 const struct anv_state surface_state,
                 const struct anv_image * const image,
+                const uint32_t plane,
                 const uint8_t level, const uint32_t layer_count,
                 const enum blorp_fast_clear_op op)
 {
    assert(cmd_buffer && image);
 
    /* The resolved subresource range must have a CCS buffer. */
-   assert(level < anv_image_aux_levels(image));
-   assert(layer_count <= anv_image_aux_layers(image, level));
-   assert(image->aspects == VK_IMAGE_ASPECT_COLOR_BIT && image->samples == 1);
+   assert(level < anv_image_aux_levels(image, plane));
+   assert(layer_count <= anv_image_aux_layers(image, plane, level));
+   assert(image->aspects & VK_IMAGE_ASPECT_ANY_COLOR_BIT && image->samples == 1);
 
    /* Create a binding table for this surface state. */
    uint32_t binding_table;
@@ -1598,13 +1701,14 @@ anv_ccs_resolve(struct anv_cmd_buffer * const cmd_buffer,
                     BLORP_BATCH_PREDICATE_ENABLE);
 
    struct blorp_surf surf;
-   get_blorp_surf_for_anv_image(image, VK_IMAGE_ASPECT_COLOR_BIT,
-                                image->aux_usage == ISL_AUX_USAGE_CCS_E ?
+   get_blorp_surf_for_anv_image(image, plane, VK_IMAGE_ASPECT_COLOR_BIT,
+                                image->planes[plane].aux_usage == ISL_AUX_USAGE_CCS_E ?
                                 ISL_AUX_USAGE_CCS_E : ISL_AUX_USAGE_CCS_D,
                                 &surf);
 
    blorp_ccs_resolve_attachment(&batch, binding_table, &surf, level,
-                                layer_count, image->color_surface.isl.format,
+                                layer_count,
+                                image->planes[plane].surface.isl.format,
                                 op);
 
    blorp_batch_finish(&batch);
diff --git a/src/intel/vulkan/anv_dump.c b/src/intel/vulkan/anv_dump.c
index 0608904219e..160c18c4f17 100644
--- a/src/intel/vulkan/anv_dump.c
+++ b/src/intel/vulkan/anv_dump.c
@@ -424,20 +424,25 @@ anv_dump_add_framebuffer(struct anv_cmd_buffer *cmd_buffer,
       uint32_t b;
       for_each_bit(b, iview->image->aspects) {
          VkImageAspectFlagBits aspect = (1 << b);
-         char suffix;
+         const char *suffix;
          switch (aspect) {
-         case VK_IMAGE_ASPECT_COLOR_BIT:     suffix = 'c'; break;
-         case VK_IMAGE_ASPECT_DEPTH_BIT:     suffix = 'd'; break;
-         case VK_IMAGE_ASPECT_STENCIL_BIT:   suffix = 's'; break;
+         case VK_IMAGE_ASPECT_COLOR_BIT:       suffix = "c"; break;
+         case VK_IMAGE_ASPECT_DEPTH_BIT:       suffix = "d"; break;
+         case VK_IMAGE_ASPECT_STENCIL_BIT:     suffix = "s"; break;
+         case VK_IMAGE_ASPECT_PLANE_0_BIT_KHR: suffix = "c0"; break;
+         case VK_IMAGE_ASPECT_PLANE_1_BIT_KHR: suffix = "c1"; break;
+         case VK_IMAGE_ASPECT_PLANE_2_BIT_KHR: suffix = "c2"; break;
          default:
             unreachable("Invalid aspect");
          }
 
-         char *filename = ralloc_asprintf(dump_ctx, "framebuffer%04d-%d%c.ppm",
+         char *filename = ralloc_asprintf(dump_ctx, "framebuffer%04d-%d%s.ppm",
                                           dump_idx, i, suffix);
 
+         unsigned plane = anv_image_aspect_to_plane(iview->image->aspects, aspect);
          dump_add_image(cmd_buffer, (struct anv_image *)iview->image, aspect,
-                        iview->isl.base_level, iview->isl.base_array_layer,
+                        iview->planes[plane].isl.base_level,
+                        iview->planes[plane].isl.base_array_layer,
                         filename);
       }
    }
diff --git a/src/intel/vulkan/anv_formats.c b/src/intel/vulkan/anv_formats.c
index 303531a6ab9..3a941d4bd06 100644
--- a/src/intel/vulkan/anv_formats.c
+++ b/src/intel/vulkan/anv_formats.c
@@ -423,8 +423,7 @@ anv_get_plane_format(const struct gen_device_info *devinfo, VkFormat vk_format,
       return plane_format;
    }
 
-   assert(aspect == VK_IMAGE_ASPECT_COLOR_BIT);
-   assert(vk_format_aspects(vk_format) == VK_IMAGE_ASPECT_COLOR_BIT);
+   assert((aspect & ~VK_IMAGE_ASPECT_ANY_COLOR_BIT) == 0);
 
    const struct isl_format_layout *isl_layout =
       isl_format_get_layout(plane_format.isl_format);
diff --git a/src/intel/vulkan/anv_image.c b/src/intel/vulkan/anv_image.c
index 262be68575a..6b3b70803d0 100644
--- a/src/intel/vulkan/anv_image.c
+++ b/src/intel/vulkan/anv_image.c
@@ -68,6 +68,9 @@ choose_isl_surf_usage(VkImageCreateFlags vk_create_flags,
       isl_usage |= ISL_SURF_USAGE_STENCIL_BIT;
       break;
    case VK_IMAGE_ASPECT_COLOR_BIT:
+   case VK_IMAGE_ASPECT_PLANE_0_BIT_KHR:
+   case VK_IMAGE_ASPECT_PLANE_1_BIT_KHR:
+   case VK_IMAGE_ASPECT_PLANE_2_BIT_KHR:
       break;
    default:
       unreachable("bad VkImageAspect");
@@ -95,26 +98,31 @@ choose_isl_surf_usage(VkImageCreateFlags vk_create_flags,
 static struct anv_surface *
 get_surface(struct anv_image *image, VkImageAspectFlags aspect)
 {
-   switch (aspect) {
-   default:
-      unreachable("bad VkImageAspect");
-   case VK_IMAGE_ASPECT_COLOR_BIT:
-      return &image->color_surface;
-   case VK_IMAGE_ASPECT_DEPTH_BIT:
-      return &image->depth_surface;
-   case VK_IMAGE_ASPECT_STENCIL_BIT:
-      return &image->stencil_surface;
-   }
+   uint32_t plane = anv_image_aspect_to_plane(image->aspects, aspect);
+   return &image->planes[plane].surface;
 }
 
 static void
-add_surface(struct anv_image *image, struct anv_surface *surf)
+add_surface(struct anv_image *image, struct anv_surface *surf, uint32_t plane)
 {
    assert(surf->isl.size > 0); /* isl surface must be initialized */
 
-   surf->offset = align_u32(image->size, surf->isl.alignment);
+   if (image->disjoint) {
+      surf->offset = align_u32(image->planes[plane].size, surf->isl.alignment);
+      /* Plane offset is always 0 when it's disjoint. */
+   } else {
+      surf->offset = align_u32(image->size, surf->isl.alignment);
+      /* Determine plane's offset only once when the first surface is added. */
+      if (image->planes[plane].size == 0)
+         image->planes[plane].offset = image->size;
+   }
+
    image->size = surf->offset + surf->isl.size;
+   image->planes[plane].size = (surf->offset + surf->isl.size) - image->planes[plane].offset;
+
    image->alignment = MAX2(image->alignment, surf->isl.alignment);
+   image->planes[plane].alignment = MAX2(image->planes[plane].alignment,
+                                         surf->isl.alignment);
 }
 
 
@@ -194,11 +202,12 @@ all_formats_ccs_e_compatible(const struct gen_device_info *devinfo,
  */
 static void
 add_fast_clear_state_buffer(struct anv_image *image,
+                            uint32_t plane,
                             const struct anv_device *device)
 {
    assert(image && device);
-   assert(image->aux_surface.isl.size > 0 &&
-          image->aspects == VK_IMAGE_ASPECT_COLOR_BIT);
+   assert(image->planes[plane].aux_surface.isl.size > 0 &&
+          image->aspects & VK_IMAGE_ASPECT_ANY_COLOR_BIT);
 
    /* The offset to the buffer of clear values must be dword-aligned for GPU
     * memcpy operations. It is located immediately after the auxiliary surface.
@@ -212,18 +221,31 @@ add_fast_clear_state_buffer(struct anv_image *image,
    /* Auxiliary buffers should be a multiple of 4K, so the start of the clear
     * values buffer should already be dword-aligned.
     */
-   assert(image->aux_surface.isl.size % 4 == 0);
+   assert((image->planes[plane].offset + image->planes[plane].size) % 4 == 0);
 
-   /* This buffer should be at the very end of the image. */
-   assert(image->size ==
-          image->aux_surface.offset + image->aux_surface.isl.size);
+   /* This buffer should be at the very end of the plane. */
+   if (image->disjoint) {
+      assert(image->planes[plane].size ==
+             (image->planes[plane].offset + image->planes[plane].size));
+   } else {
+      assert(image->size ==
+             (image->planes[plane].offset + image->planes[plane].size));
+   }
 
    const unsigned entry_size = anv_fast_clear_state_entry_size(device);
    /* There's no padding between entries, so ensure that they're always a
     * multiple of 32 bits in order to enable GPU memcpy operations.
     */
    assert(entry_size % 4 == 0);
-   image->size += entry_size * anv_image_aux_levels(image);
+
+   const unsigned plane_state_size =
+      entry_size * anv_image_aux_levels(image, plane);
+
+   image->planes[plane].fast_clear_state_offset =
+      image->planes[plane].offset + image->planes[plane].size;
+
+   image->planes[plane].size += plane_state_size;
+   image->size += plane_state_size;
 }
 
 /**
@@ -259,15 +281,13 @@ make_surface(const struct anv_device *dev,
 
    assert(tiling_flags);
 
-   struct anv_surface *anv_surf = get_surface(image, aspect);
-
    image->extent = anv_sanitize_image_extent(vk_info->imageType,
                                              vk_info->extent);
 
-   enum isl_format format =
-      anv_get_isl_plane_format(&dev->info, vk_info->format,
-                               aspect, vk_info->tiling);
-   assert(format != ISL_FORMAT_UNSUPPORTED);
+   const unsigned plane = anv_image_aspect_to_plane(image->aspects, aspect);
+   const  struct anv_format_plane plane_format =
+      anv_get_plane_format(&dev->info, image->vk_format, aspect, image->tiling);
+   struct anv_surface *anv_surf = &image->planes[plane].surface;
 
    /* If an image is created as BLOCK_TEXEL_VIEW_COMPATIBLE, then we need to
     * fall back to linear on Broadwell and earlier because we aren't
@@ -279,16 +299,16 @@ make_surface(const struct anv_device *dev,
    if (dev->info.gen <= 8 &&
        (vk_info->flags & VK_IMAGE_CREATE_BLOCK_TEXEL_VIEW_COMPATIBLE_BIT_KHR) &&
        vk_info->tiling == VK_IMAGE_TILING_OPTIMAL) {
-      assert(isl_format_is_compressed(format));
+      assert(isl_format_is_compressed(plane_format.isl_format));
       tiling_flags = ISL_TILING_LINEAR_BIT;
       needs_shadow = true;
    }
 
    ok = isl_surf_init(&dev->isl_dev, &anv_surf->isl,
       .dim = vk_to_isl_surf_dim[vk_info->imageType],
-      .format = format,
-      .width = image->extent.width,
-      .height = image->extent.height,
+      .format = plane_format.isl_format,
+      .width = image->extent.width / plane_format.denominator_scales[0],
+      .height = image->extent.height / plane_format.denominator_scales[1],
       .depth = image->extent.depth,
       .levels = vk_info->mipLevels,
       .array_len = vk_info->arrayLayers,
@@ -303,7 +323,18 @@ make_surface(const struct anv_device *dev,
     */
    assert(ok);
 
-   add_surface(image, anv_surf);
+   image->planes[plane].aux_usage = ISL_AUX_USAGE_NONE;
+
+   if (!image->disjoint) {
+      /* With non disjoint planes, align the plane to its surface alignment.
+       */
+      uint32_t previous_plane_size = plane == 0 ? 0 :
+         (image->planes[plane - 1].offset + image->planes[plane - 1].size);
+      image->planes[plane].offset = align_u32(previous_plane_size,
+                                              anv_surf->isl.alignment);
+   }
+
+   add_surface(image, anv_surf, plane);
 
    /* If an image is created as BLOCK_TEXEL_VIEW_COMPATIBLE, then we need to
     * create an identical tiled shadow surface for use while texturing so we
@@ -313,9 +344,9 @@ make_surface(const struct anv_device *dev,
       assert(aspect == VK_IMAGE_ASPECT_COLOR_BIT);
       assert(tiling_flags == ISL_TILING_LINEAR_BIT);
 
-      ok = isl_surf_init(&dev->isl_dev, &image->shadow_surface.isl,
+      ok = isl_surf_init(&dev->isl_dev, &image->planes[plane].shadow_surface.isl,
          .dim = vk_to_isl_surf_dim[vk_info->imageType],
-         .format = format,
+         .format = plane_format.isl_format,
          .width = image->extent.width,
          .height = image->extent.height,
          .depth = image->extent.depth,
@@ -332,7 +363,7 @@ make_surface(const struct anv_device *dev,
        */
       assert(ok);
 
-      add_surface(image, &image->shadow_surface);
+      add_surface(image, &image->planes[plane].shadow_surface, plane);
    }
 
    /* Add a HiZ surface to a depth buffer that will be used for rendering.
@@ -358,24 +389,43 @@ make_surface(const struct anv_device *dev,
       } else if (dev->info.gen == 8 && vk_info->samples > 1) {
          anv_perf_warn(dev->instance, image, "Enable gen8 multisampled HiZ");
       } else if (!unlikely(INTEL_DEBUG & DEBUG_NO_HIZ)) {
-         assert(image->aux_surface.isl.size == 0);
-         ok = isl_surf_get_hiz_surf(&dev->isl_dev, &image->depth_surface.isl,
-                                    &image->aux_surface.isl);
+         assert(image->planes[plane].aux_surface.isl.size == 0);
+         ok = isl_surf_get_hiz_surf(&dev->isl_dev,
+                                    &image->planes[plane].surface.isl,
+                                    &image->planes[plane].aux_surface.isl);
          assert(ok);
-         add_surface(image, &image->aux_surface);
-         image->aux_usage = ISL_AUX_USAGE_HIZ;
+         add_surface(image, &image->planes[plane].aux_surface, plane);
+         image->planes[plane].aux_usage = ISL_AUX_USAGE_HIZ;
       }
-   } else if (aspect == VK_IMAGE_ASPECT_COLOR_BIT && vk_info->samples == 1) {
-      if (!unlikely(INTEL_DEBUG & DEBUG_NO_RBC)) {
-         assert(image->aux_surface.isl.size == 0);
-         ok = isl_surf_get_ccs_surf(&dev->isl_dev, &anv_surf->isl,
-                                    &image->aux_surface.isl, 0);
+   } else if ((aspect & VK_IMAGE_ASPECT_ANY_COLOR_BIT) && vk_info->samples == 1) {
+      /* TODO: Allow compression on :
+       *
+       *     1) non multiplanar images (We appear to hit a sampler bug with CCS &
+       *          R16G16 format. Putting the clear state a page (4096 bytes)
+       *          further fixes the issue.
+       *
+       *     2) alias images, because they might be aliases of images described
+       *        in 1)
+       *
+       *     3) if compression is disabled by debug
+       */
+      const bool allow_compression =
+         image->n_planes == 1 &&
+         (vk_info->flags & VK_IMAGE_CREATE_ALIAS_BIT_KHR) == 0 &&
+         likely((INTEL_DEBUG & DEBUG_NO_RBC) == 0);
+
+      if (allow_compression) {
+         assert(image->planes[plane].aux_surface.isl.size == 0);
+         ok = isl_surf_get_ccs_surf(&dev->isl_dev,
+                                    &image->planes[plane].surface.isl,
+                                    &image->planes[plane].aux_surface.isl, 0);
          if (ok) {
 
             /* Disable CCS when it is not useful (i.e., when you can't render
              * to the image with CCS enabled).
              */
-            if (!isl_format_supports_rendering(&dev->info, format)) {
+            if (!isl_format_supports_rendering(&dev->info,
+                                               plane_format.isl_format)) {
                /* While it may be technically possible to enable CCS for this
                 * image, we currently don't have things hooked up to get it
                 * working.
@@ -383,12 +433,12 @@ make_surface(const struct anv_device *dev,
                anv_perf_warn(dev->instance, image,
                              "This image format doesn't support rendering. "
                              "Not allocating an CCS buffer.");
-               image->aux_surface.isl.size = 0;
+               image->planes[plane].aux_surface.isl.size = 0;
                return VK_SUCCESS;
             }
 
-            add_surface(image, &image->aux_surface);
-            add_fast_clear_state_buffer(image, dev);
+            add_surface(image, &image->planes[plane].aux_surface, plane);
+            add_fast_clear_state_buffer(image, plane, dev);
 
             /* For images created without MUTABLE_FORMAT_BIT set, we know that
              * they will always be used with the original format.  In
@@ -400,22 +450,42 @@ make_surface(const struct anv_device *dev,
              */
             if (!(vk_info->usage & VK_IMAGE_USAGE_STORAGE_BIT) &&
                 all_formats_ccs_e_compatible(&dev->info, vk_info)) {
-               image->aux_usage = ISL_AUX_USAGE_CCS_E;
+               image->planes[plane].aux_usage = ISL_AUX_USAGE_CCS_E;
             }
          }
       }
-   } else if (aspect == VK_IMAGE_ASPECT_COLOR_BIT && vk_info->samples > 1) {
-      assert(image->aux_surface.isl.size == 0);
+   } else if ((aspect & VK_IMAGE_ASPECT_ANY_COLOR_BIT) && vk_info->samples > 1) {
       assert(!(vk_info->usage & VK_IMAGE_USAGE_STORAGE_BIT));
-      ok = isl_surf_get_mcs_surf(&dev->isl_dev, &anv_surf->isl,
-                                 &image->aux_surface.isl);
+      assert(image->planes[plane].aux_surface.isl.size == 0);
+      ok = isl_surf_get_mcs_surf(&dev->isl_dev,
+                                 &image->planes[plane].surface.isl,
+                                 &image->planes[plane].aux_surface.isl);
       if (ok) {
-         add_surface(image, &image->aux_surface);
-         add_fast_clear_state_buffer(image, dev);
-         image->aux_usage = ISL_AUX_USAGE_MCS;
+         add_surface(image, &image->planes[plane].aux_surface, plane);
+         add_fast_clear_state_buffer(image, plane, dev);
+         image->planes[plane].aux_usage = ISL_AUX_USAGE_MCS;
       }
    }
 
+   assert((image->planes[plane].offset + image->planes[plane].size) == image->size);
+
+   /* Upper bound of the last surface should be smaller than the plane's
+    * size.
+    */
+   assert((MAX2(image->planes[plane].surface.offset,
+                image->planes[plane].aux_surface.offset) +
+           (image->planes[plane].aux_surface.isl.size > 0 ?
+            image->planes[plane].aux_surface.isl.size :
+            image->planes[plane].surface.isl.size)) <=
+          (image->planes[plane].offset + image->planes[plane].size));
+
+   if (image->planes[plane].aux_surface.isl.size) {
+      /* assert(image->planes[plane].fast_clear_state_offset == */
+      /*        (image->planes[plane].aux_surface.offset + image->planes[plane].aux_surface.isl.size)); */
+      assert(image->planes[plane].fast_clear_state_offset <
+             (image->planes[plane].offset + image->planes[plane].size));
+   }
+
    return VK_SUCCESS;
 }
 
@@ -447,13 +517,18 @@ anv_image_create(VkDevice _device,
    image->type = pCreateInfo->imageType;
    image->extent = pCreateInfo->extent;
    image->vk_format = pCreateInfo->format;
+   image->format = anv_get_format(pCreateInfo->format);
    image->aspects = vk_format_aspects(image->vk_format);
    image->levels = pCreateInfo->mipLevels;
    image->array_size = pCreateInfo->arrayLayers;
    image->samples = pCreateInfo->samples;
    image->usage = pCreateInfo->usage;
    image->tiling = pCreateInfo->tiling;
-   image->aux_usage = ISL_AUX_USAGE_NONE;
+
+   const struct anv_format *format = anv_get_format(image->vk_format);
+   assert(format != NULL);
+
+   image->n_planes = format->n_planes;
 
    uint32_t b;
    for_each_bit(b, image->aspects) {
@@ -500,37 +575,41 @@ anv_DestroyImage(VkDevice _device, VkImage _image,
    vk_free2(&device->alloc, pAllocator, image);
 }
 
-static void
-anv_bind_image_memory(const VkBindImageMemoryInfoKHR *pBindInfo)
+static VkResult anv_image_bind_memory_plane(struct anv_device *device,
+                                            struct anv_image *image,
+                                            uint32_t plane,
+                                            struct anv_device_memory *memory,
+                                            uint32_t memory_offset)
 {
-   ANV_FROM_HANDLE(anv_device_memory, mem, pBindInfo->memory);
-   ANV_FROM_HANDLE(anv_image, image, pBindInfo->image);
-
-   assert(pBindInfo->sType == VK_STRUCTURE_TYPE_BIND_IMAGE_MEMORY_INFO_KHR);
-
-   if (mem == NULL) {
-      image->bo = NULL;
-      image->offset = 0;
-      return;
+   if (memory) {
+      image->planes[plane].bo = memory->bo;
+      image->planes[plane].bo_offset = memory_offset;
+   } else {
+      image->planes[plane].bo = NULL;
+      image->planes[plane].bo_offset = 0;
    }
 
-   image->bo = mem->bo;
-   image->offset = pBindInfo->memoryOffset;
+   return VK_SUCCESS;
 }
 
 VkResult anv_BindImageMemory(
-    VkDevice                                    device,
-    VkImage                                     image,
-    VkDeviceMemory                              memory,
+    VkDevice                                    _device,
+    VkImage                                     _image,
+    VkDeviceMemory                              _memory,
     VkDeviceSize                                memoryOffset)
 {
-   anv_bind_image_memory(
-      &(VkBindImageMemoryInfoKHR) {
-         .sType         = VK_STRUCTURE_TYPE_BIND_IMAGE_MEMORY_INFO_KHR,
-         .image         = image,
-         .memory        = memory,
-         .memoryOffset  = memoryOffset,
-      });
+   ANV_FROM_HANDLE(anv_device, device, _device);
+   ANV_FROM_HANDLE(anv_device_memory, mem, _memory);
+   ANV_FROM_HANDLE(anv_image, image, _image);
+
+   uint32_t plane, aspect_bit;
+   anv_foreach_plane_aspect_bit(plane, aspect_bit,
+                                image->aspects, image->aspects) {
+      VkResult result = anv_image_bind_memory_plane(device, image, plane,
+                                                    mem, memoryOffset);
+      if (result != VK_SUCCESS)
+         return vk_error(result);
+   }
 
    return VK_SUCCESS;
 }
@@ -540,10 +619,27 @@ VkResult anv_BindImageMemory2KHR(
     uint32_t                                    bindInfoCount,
     const VkBindImageMemoryInfoKHR*             pBindInfos)
 {
-   for (uint32_t i = 0; i < bindInfoCount; i++)
-      anv_bind_image_memory(&pBindInfos[i]);
+   VkResult result = VK_SUCCESS;
 
-   return VK_SUCCESS;
+   for (uint32_t i = 0; i < bindInfoCount; i++) {
+      const VkBindImageMemoryInfoKHR *bind_info = &pBindInfos[i];
+
+      if (pBindInfos->pNext == NULL) {
+         result = anv_BindImageMemory(device, bind_info->image,
+                                      bind_info->memory,
+                                      bind_info->memoryOffset);
+      } else {
+         vk_foreach_struct_const(s, bind_info->pNext) {
+            switch (s->sType) {
+            default:
+               anv_debug_ignored_stype(s->sType);
+               break;
+            }
+         }
+      }
+   }
+
+   return result;
 }
 
 static void
@@ -575,22 +671,10 @@ void anv_GetImageSubresourceLayout(
 
    assert(__builtin_popcount(pSubresource->aspectMask) == 1);
 
-   switch (pSubresource->aspectMask) {
-   case VK_IMAGE_ASPECT_COLOR_BIT:
-      anv_surface_get_subresource_layout(image, &image->color_surface,
-                                         pSubresource, pLayout);
-      break;
-   case VK_IMAGE_ASPECT_DEPTH_BIT:
-      anv_surface_get_subresource_layout(image, &image->depth_surface,
-                                         pSubresource, pLayout);
-      break;
-   case VK_IMAGE_ASPECT_STENCIL_BIT:
-      anv_surface_get_subresource_layout(image, &image->stencil_surface,
-                                         pSubresource, pLayout);
-      break;
-   default:
-      assert(!"Invalid image aspect");
-   }
+   anv_surface_get_subresource_layout(image,
+                                      get_surface(image,
+                                                  pSubresource->aspectMask),
+                                      pSubresource, pLayout);
 }
 
 /**
@@ -601,7 +685,7 @@ void anv_GetImageSubresourceLayout(
  *
  * @param devinfo The device information of the Intel GPU.
  * @param image The image that may contain a collection of buffers.
- * @param aspects The aspect(s) of the image to be accessed.
+ * @param plane The plane of the image to be accessed.
  * @param layout The current layout of the image aspect(s).
  *
  * @return The primary buffer that should be used for the given layout.
@@ -609,7 +693,7 @@ void anv_GetImageSubresourceLayout(
 enum isl_aux_usage
 anv_layout_to_aux_usage(const struct gen_device_info * const devinfo,
                         const struct anv_image * const image,
-                        const VkImageAspectFlags aspects,
+                        const uint32_t plane,
                         const VkImageLayout layout)
 {
    /* Validate the inputs. */
@@ -620,35 +704,41 @@ anv_layout_to_aux_usage(const struct gen_device_info * const devinfo,
    /* The layout of a NULL image is not properly defined. */
    assert(image != NULL);
 
-   /* The aspects must be a subset of the image aspects. */
-   assert(aspects & image->aspects && aspects <= image->aspects);
-
    /* Determine the optimal buffer. */
 
-   /* If there is no auxiliary surface allocated, we must use the one and only
-    * main buffer.
+   /* Depth & stencil buffers share the same auxilliary buffer, we need to
+    * process them differently from color buffers.
     */
-   if (image->aux_surface.isl.size == 0)
-      return ISL_AUX_USAGE_NONE;
+   if (image->aspects & VK_IMAGE_ASPECT_ANY_COLOR_BIT) {
+      /* If there is no auxiliary surface allocated, we must use the one and
+       * only main buffer.
+       */
+      if (image->planes[plane].aux_surface.isl.size == 0)
+         return ISL_AUX_USAGE_NONE;
+   } else {
+      /* If there is no auxiliary surface allocated, we must use the one and
+       * only main buffer.
+       */
+      if (image->planes[plane].aux_surface.isl.size == 0)
+         return ISL_AUX_USAGE_NONE;
+
+      /* On BDW+, when clearing the stencil aspect of a depth stencil image,
+       * the HiZ buffer allows us to record the clear with a relatively small
+       * number of packets. Prior to BDW, the HiZ buffer provides no known
+       * benefit to the stencil aspect.
+       */
+      if (devinfo->gen < 8 && plane != 0 /* aka stencil */)
+         return ISL_AUX_USAGE_NONE;
+   }
 
    /* All images that use an auxiliary surface are required to be tiled. */
    assert(image->tiling == VK_IMAGE_TILING_OPTIMAL);
 
-   /* On BDW+, when clearing the stencil aspect of a depth stencil image,
-    * the HiZ buffer allows us to record the clear with a relatively small
-    * number of packets. Prior to BDW, the HiZ buffer provides no known benefit
-    * to the stencil aspect.
-    */
-   if (devinfo->gen < 8 && aspects == VK_IMAGE_ASPECT_STENCIL_BIT)
-      return ISL_AUX_USAGE_NONE;
-
-   const bool color_aspect = aspects == VK_IMAGE_ASPECT_COLOR_BIT;
-
    /* The following switch currently only handles depth stencil aspects.
     * TODO: Handle the color aspect.
     */
-   if (color_aspect)
-      return image->aux_usage;
+   if (image->aspects & VK_IMAGE_ASPECT_ANY_COLOR_BIT)
+      return image->planes[plane].aux_usage;
 
    switch (layout) {
 
@@ -682,17 +772,17 @@ anv_layout_to_aux_usage(const struct gen_device_info * const devinfo,
 
    /* Sampling Layouts */
    case VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL:
-      assert(!color_aspect);
+      assert((image->aspects & VK_IMAGE_ASPECT_ANY_COLOR_BIT) == 0);
       /* Fall-through */
    case VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL:
    case VK_IMAGE_LAYOUT_DEPTH_READ_ONLY_STENCIL_ATTACHMENT_OPTIMAL_KHR:
-      if (anv_can_sample_with_hiz(devinfo, aspects, image->samples))
+      if (anv_can_sample_with_hiz(devinfo, image))
          return ISL_AUX_USAGE_HIZ;
       else
          return ISL_AUX_USAGE_NONE;
 
    case VK_IMAGE_LAYOUT_PRESENT_SRC_KHR:
-      assert(color_aspect);
+      assert(image->aspects & VK_IMAGE_ASPECT_ANY_COLOR_BIT);
 
       /* On SKL+, the render buffer can be decompressed by the presentation
        * engine. Support for this feature has not yet landed in the wider
@@ -714,12 +804,12 @@ anv_layout_to_aux_usage(const struct gen_device_info * const devinfo,
 
    /* Rendering Layouts */
    case VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL:
-      assert(color_aspect);
+      assert(image->aspects & VK_IMAGE_ASPECT_ANY_COLOR_BIT);
       unreachable("Color images are not yet supported.");
 
    case VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL:
    case VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_STENCIL_READ_ONLY_OPTIMAL_KHR:
-      assert(!color_aspect);
+      assert((image->aspects & VK_IMAGE_ASPECT_ANY_COLOR_BIT) == 0);
       return ISL_AUX_USAGE_HIZ;
 
    case VK_IMAGE_LAYOUT_SHARED_PRESENT_KHR:
@@ -761,7 +851,7 @@ remap_swizzle(VkComponentSwizzle swizzle, VkComponentSwizzle component,
 void
 anv_image_fill_surface_state(struct anv_device *device,
                              const struct anv_image *image,
-                             VkImageAspectFlagBits aspect,
+                             uint32_t plane,
                              const struct isl_view *view_in,
                              isl_surf_usage_flags_t view_usage,
                              enum isl_aux_usage aux_usage,
@@ -770,8 +860,8 @@ anv_image_fill_surface_state(struct anv_device *device,
                              struct anv_surface_state *state_inout,
                              struct brw_image_param *image_param_out)
 {
-   const struct anv_surface *surface =
-      anv_image_get_surface_for_aspect_mask(image, aspect);
+   const struct anv_surface *surface = &image->planes[plane].surface,
+      *aux_surface = &image->planes[plane].aux_surface;
 
    struct isl_view view = *view_in;
    view.usage |= view_usage;
@@ -781,13 +871,13 @@ anv_image_fill_surface_state(struct anv_device *device,
     * the primary surface.  The shadow surface will be tiled, unlike the main
     * surface, so it should get significantly better performance.
     */
-   if (image->shadow_surface.isl.size > 0 &&
+   if (image->planes[plane].shadow_surface.isl.size > 0 &&
        isl_format_is_compressed(view.format) &&
        (flags & ANV_IMAGE_VIEW_STATE_TEXTURE_OPTIMAL)) {
       assert(isl_format_is_compressed(surface->isl.format));
       assert(surface->isl.tiling == ISL_TILING_LINEAR);
-      assert(image->shadow_surface.isl.tiling != ISL_TILING_LINEAR);
-      surface = &image->shadow_surface;
+      assert(image->planes[plane].shadow_surface.isl.tiling != ISL_TILING_LINEAR);
+      surface = &image->planes[plane].shadow_surface;
    }
 
    if (view_usage == ISL_SURF_USAGE_RENDER_TARGET_BIT)
@@ -802,9 +892,9 @@ anv_image_fill_surface_state(struct anv_device *device,
    if (!clear_color)
       clear_color = &default_clear_color;
 
-   const uint64_t address = image->offset + surface->offset;
-   const uint64_t aux_address = (aux_usage == ISL_AUX_USAGE_NONE) ? 0 :
-                                image->offset + image->aux_surface.offset;
+   const uint64_t address = image->planes[plane].bo_offset + surface->offset;
+   const uint64_t aux_address = aux_usage == ISL_AUX_USAGE_NONE ?
+      0 : (image->planes[plane].bo_offset + aux_surface->offset);
 
    if (view_usage == ISL_SURF_USAGE_STORAGE_BIT &&
        !(flags & ANV_IMAGE_VIEW_STATE_STORAGE_WRITE_ONLY) &&
@@ -893,7 +983,7 @@ anv_image_fill_surface_state(struct anv_device *device,
                           .view = &view,
                           .address = address + offset_B,
                           .clear_color = *clear_color,
-                          .aux_surf = &image->aux_surface.isl,
+                          .aux_surf = &aux_surface->isl,
                           .aux_usage = aux_usage,
                           .aux_address = aux_address,
                           .mocs = device->default_mocs,
@@ -924,6 +1014,22 @@ anv_image_fill_surface_state(struct anv_device *device,
    }
 }
 
+static VkImageAspectFlags
+remap_aspect_flags(VkImageAspectFlags view_aspects)
+{
+   if (view_aspects & VK_IMAGE_ASPECT_ANY_COLOR_BIT) {
+      if (_mesa_bitcount(view_aspects) == 1)
+         return VK_IMAGE_ASPECT_COLOR_BIT;
+
+      VkImageAspectFlags color_aspects = 0;
+      for (uint32_t i = 0; i < _mesa_bitcount(view_aspects); i++)
+         color_aspects |= VK_IMAGE_ASPECT_PLANE_0_BIT_KHR << i;
+      return color_aspects;
+   }
+   /* No special remapping needed for depth & stencil aspects. */
+   return view_aspects;
+}
+
 VkResult
 anv_CreateImageView(VkDevice _device,
                     const VkImageViewCreateInfo *pCreateInfo,
@@ -968,106 +1074,125 @@ anv_CreateImageView(VkDevice _device,
       break;
    }
 
+   /* First expand aspects to the image's ones (for example
+    * VK_IMAGE_ASPECT_COLOR_BIT will be converted to
+    * VK_IMAGE_ASPECT_PLANE_0_BIT_KHR | VK_IMAGE_ASPECT_PLANE_1_BIT_KHR |
+    * VK_IMAGE_ASPECT_PLANE_2_BIT_KHR for an image of format
+    * VK_FORMAT_G8_B8_R8_3PLANE_420_UNORM_KHR.
+    */
+   VkImageAspectFlags expanded_aspects =
+      anv_image_color_aspects(image, range->aspectMask);
+
    iview->image = image;
 
-   iview->aspect_mask = pCreateInfo->subresourceRange.aspectMask;
+   /* Remap the expanded aspects for the image view. For example if only
+    * VK_IMAGE_ASPECT_PLANE_1_BIT_KHR was given in range->aspectMask, we will
+    * convert it to VK_IMAGE_ASPECT_COLOR_BIT since from the point of view of
+    * the image view, it only has a single plane.
+    */
+   iview->aspect_mask = remap_aspect_flags(expanded_aspects);
+   iview->n_planes = anv_image_aspect_get_planes(iview->aspect_mask);
    iview->vk_format = pCreateInfo->format;
 
-   struct anv_format_plane format =
-      anv_get_plane_format(&device->info, pCreateInfo->format,
-                           range->aspectMask == (VK_IMAGE_ASPECT_DEPTH_BIT |
-                                                 VK_IMAGE_ASPECT_STENCIL_BIT) ?
-                           VK_IMAGE_ASPECT_DEPTH_BIT : range->aspectMask,
-                           image->tiling);
-
-   iview->isl = (struct isl_view) {
-      .format = format.isl_format,
-      .base_level = range->baseMipLevel,
-      .levels = anv_get_levelCount(image, range),
-      .base_array_layer = range->baseArrayLayer,
-      .array_len = anv_get_layerCount(image, range),
-      .swizzle = {
-         .r = remap_swizzle(pCreateInfo->components.r,
-                            VK_COMPONENT_SWIZZLE_R, format.swizzle),
-         .g = remap_swizzle(pCreateInfo->components.g,
-                            VK_COMPONENT_SWIZZLE_G, format.swizzle),
-         .b = remap_swizzle(pCreateInfo->components.b,
-                            VK_COMPONENT_SWIZZLE_B, format.swizzle),
-         .a = remap_swizzle(pCreateInfo->components.a,
-                            VK_COMPONENT_SWIZZLE_A, format.swizzle),
-      },
-   };
-
    iview->extent = (VkExtent3D) {
       .width  = anv_minify(image->extent.width , range->baseMipLevel),
       .height = anv_minify(image->extent.height, range->baseMipLevel),
       .depth  = anv_minify(image->extent.depth , range->baseMipLevel),
    };
 
-   if (pCreateInfo->viewType == VK_IMAGE_VIEW_TYPE_3D) {
-      iview->isl.base_array_layer = 0;
-      iview->isl.array_len = iview->extent.depth;
-   }
+   /* Now go through the underlying image selected planes (computed in
+    * expanded_aspects) and map them to planes in the image view.
+    */
+   uint32_t iaspect_bit, vplane = 0, iplane;
+   anv_foreach_plane_aspect_bit(iplane, iaspect_bit,
+                                expanded_aspects, image->aspects) {
+      VkImageAspectFlags vplane_aspect =
+         anv_plane_to_aspect(iview->aspect_mask, vplane);
+      struct anv_format_plane format =
+         anv_get_plane_format(&device->info, pCreateInfo->format,
+                              vplane_aspect, image->tiling);
+
+      iview->planes[vplane].image_plane = iplane;
+
+      iview->planes[vplane].isl = (struct isl_view) {
+         .format = format.isl_format,
+         .base_level = range->baseMipLevel,
+         .levels = anv_get_levelCount(image, range),
+         .base_array_layer = range->baseArrayLayer,
+         .array_len = anv_get_layerCount(image, range),
+         .swizzle = {
+            .r = remap_swizzle(pCreateInfo->components.r,
+                               VK_COMPONENT_SWIZZLE_R, format.swizzle),
+            .g = remap_swizzle(pCreateInfo->components.g,
+                               VK_COMPONENT_SWIZZLE_G, format.swizzle),
+            .b = remap_swizzle(pCreateInfo->components.b,
+                               VK_COMPONENT_SWIZZLE_B, format.swizzle),
+            .a = remap_swizzle(pCreateInfo->components.a,
+                               VK_COMPONENT_SWIZZLE_A, format.swizzle),
+         },
+      };
+
+      if (pCreateInfo->viewType == VK_IMAGE_VIEW_TYPE_3D) {
+         iview->planes[vplane].isl.base_array_layer = 0;
+         iview->planes[vplane].isl.array_len = iview->extent.depth;
+      }
 
-   if (pCreateInfo->viewType == VK_IMAGE_VIEW_TYPE_CUBE ||
-       pCreateInfo->viewType == VK_IMAGE_VIEW_TYPE_CUBE_ARRAY) {
-      iview->isl.usage = ISL_SURF_USAGE_CUBE_BIT;
-   } else {
-      iview->isl.usage = 0;
-   }
+      if (pCreateInfo->viewType == VK_IMAGE_VIEW_TYPE_CUBE ||
+          pCreateInfo->viewType == VK_IMAGE_VIEW_TYPE_CUBE_ARRAY) {
+         iview->planes[vplane].isl.usage = ISL_SURF_USAGE_CUBE_BIT;
+      } else {
+         iview->planes[vplane].isl.usage = 0;
+      }
 
-   /* Input attachment surfaces for color are allocated and filled
-    * out at BeginRenderPass time because they need compression information.
-    * Compression is not yet enabled for depth textures and stencil doesn't
-    * allow compression so we can just use the texture surface state from the
-    * view.
-    */
-   if (view_usage & VK_IMAGE_USAGE_SAMPLED_BIT ||
-       (view_usage & VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT &&
-        !(iview->aspect_mask & VK_IMAGE_ASPECT_COLOR_BIT))) {
-      iview->optimal_sampler_surface_state.state = alloc_surface_state(device);
-      iview->general_sampler_surface_state.state = alloc_surface_state(device);
-
-      enum isl_aux_usage general_aux_usage =
-         anv_layout_to_aux_usage(&device->info, image, iview->aspect_mask,
-                                 VK_IMAGE_LAYOUT_GENERAL);
-      enum isl_aux_usage optimal_aux_usage =
-         anv_layout_to_aux_usage(&device->info, image, iview->aspect_mask,
-                                 VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
-
-      anv_image_fill_surface_state(device, image, iview->aspect_mask,
-                                   &iview->isl, ISL_SURF_USAGE_TEXTURE_BIT,
-                                   optimal_aux_usage, NULL,
-                                   ANV_IMAGE_VIEW_STATE_TEXTURE_OPTIMAL,
-                                   &iview->optimal_sampler_surface_state,
-                                   NULL);
-
-      anv_image_fill_surface_state(device, image, iview->aspect_mask,
-                                   &iview->isl, ISL_SURF_USAGE_TEXTURE_BIT,
-                                   general_aux_usage, NULL,
-                                   0,
-                                   &iview->general_sampler_surface_state,
-                                   NULL);
-   }
+      if (view_usage & VK_IMAGE_USAGE_SAMPLED_BIT ||
+          (view_usage & VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT &&
+           !(iview->aspect_mask & VK_IMAGE_ASPECT_COLOR_BIT))) {
+         iview->planes[vplane].optimal_sampler_surface_state.state = alloc_surface_state(device);
+         iview->planes[vplane].general_sampler_surface_state.state = alloc_surface_state(device);
+
+         enum isl_aux_usage general_aux_usage =
+            anv_layout_to_aux_usage(&device->info, image, iplane,
+                                    VK_IMAGE_LAYOUT_GENERAL);
+         enum isl_aux_usage optimal_aux_usage =
+            anv_layout_to_aux_usage(&device->info, image, iplane,
+                                    VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
+
+         anv_image_fill_surface_state(device, image, iplane,
+                                      &iview->planes[vplane].isl, ISL_SURF_USAGE_TEXTURE_BIT,
+                                      optimal_aux_usage, NULL,
+                                      ANV_IMAGE_VIEW_STATE_TEXTURE_OPTIMAL,
+                                      &iview->planes[vplane].optimal_sampler_surface_state,
+                                      NULL);
+
+         anv_image_fill_surface_state(device, image, iplane,
+                                      &iview->planes[vplane].isl, ISL_SURF_USAGE_TEXTURE_BIT,
+                                      general_aux_usage, NULL,
+                                      0,
+                                      &iview->planes[vplane].general_sampler_surface_state,
+                                      NULL);
+      }
+
+      /* NOTE: This one needs to go last since it may stomp isl_view.format */
+      if (view_usage & VK_IMAGE_USAGE_STORAGE_BIT) {
+         iview->planes[vplane].storage_surface_state.state = alloc_surface_state(device);
+         iview->planes[vplane].writeonly_storage_surface_state.state = alloc_surface_state(device);
+
+         anv_image_fill_surface_state(device, image, iplane,
+                                      &iview->planes[vplane].isl, ISL_SURF_USAGE_STORAGE_BIT,
+                                      ISL_AUX_USAGE_NONE, NULL,
+                                      0,
+                                      &iview->planes[vplane].storage_surface_state,
+                                      &iview->planes[vplane].storage_image_param);
+
+         anv_image_fill_surface_state(device, image, iplane,
+                                      &iview->planes[vplane].isl, ISL_SURF_USAGE_STORAGE_BIT,
+                                      ISL_AUX_USAGE_NONE, NULL,
+                                      ANV_IMAGE_VIEW_STATE_STORAGE_WRITE_ONLY,
+                                      &iview->planes[vplane].writeonly_storage_surface_state,
+                                      NULL);
+      }
 
-   /* NOTE: This one needs to go last since it may stomp isl_view.format */
-   if (view_usage & VK_IMAGE_USAGE_STORAGE_BIT) {
-      iview->storage_surface_state.state = alloc_surface_state(device);
-      iview->writeonly_storage_surface_state.state = alloc_surface_state(device);
-
-      anv_image_fill_surface_state(device, image, iview->aspect_mask,
-                                   &iview->isl, ISL_SURF_USAGE_STORAGE_BIT,
-                                   ISL_AUX_USAGE_NONE, NULL,
-                                   0,
-                                   &iview->storage_surface_state,
-                                   &iview->storage_image_param);
-
-      anv_image_fill_surface_state(device, image, iview->aspect_mask,
-                                   &iview->isl, ISL_SURF_USAGE_STORAGE_BIT,
-                                   ISL_AUX_USAGE_NONE, NULL,
-                                   ANV_IMAGE_VIEW_STATE_STORAGE_WRITE_ONLY,
-                                   &iview->writeonly_storage_surface_state,
-                                   NULL);
+      vplane++;
    }
 
    *pView = anv_image_view_to_handle(iview);
@@ -1085,24 +1210,26 @@ anv_DestroyImageView(VkDevice _device, VkImageView _iview,
    if (!iview)
       return;
 
-   if (iview->optimal_sampler_surface_state.state.alloc_size > 0) {
-      anv_state_pool_free(&device->surface_state_pool,
-                          iview->optimal_sampler_surface_state.state);
-   }
+   for (uint32_t plane = 0; plane < iview->n_planes; plane++) {
+      if (iview->planes[plane].optimal_sampler_surface_state.state.alloc_size > 0) {
+         anv_state_pool_free(&device->surface_state_pool,
+                             iview->planes[plane].optimal_sampler_surface_state.state);
+      }
 
-   if (iview->general_sampler_surface_state.state.alloc_size > 0) {
-      anv_state_pool_free(&device->surface_state_pool,
-                          iview->general_sampler_surface_state.state);
-   }
+      if (iview->planes[plane].general_sampler_surface_state.state.alloc_size > 0) {
+         anv_state_pool_free(&device->surface_state_pool,
+                             iview->planes[plane].general_sampler_surface_state.state);
+      }
 
-   if (iview->storage_surface_state.state.alloc_size > 0) {
-      anv_state_pool_free(&device->surface_state_pool,
-                          iview->storage_surface_state.state);
-   }
+      if (iview->planes[plane].storage_surface_state.state.alloc_size > 0) {
+         anv_state_pool_free(&device->surface_state_pool,
+                             iview->planes[plane].storage_surface_state.state);
+      }
 
-   if (iview->writeonly_storage_surface_state.state.alloc_size > 0) {
-      anv_state_pool_free(&device->surface_state_pool,
-                          iview->writeonly_storage_surface_state.state);
+      if (iview->planes[plane].writeonly_storage_surface_state.state.alloc_size > 0) {
+         anv_state_pool_free(&device->surface_state_pool,
+                             iview->planes[plane].writeonly_storage_surface_state.state);
+      }
    }
 
    vk_free2(&device->alloc, pAllocator, iview);
@@ -1210,16 +1337,21 @@ const struct anv_surface *
 anv_image_get_surface_for_aspect_mask(const struct anv_image *image,
                                       VkImageAspectFlags aspect_mask)
 {
+   VkImageAspectFlags sanitized_mask;
+
    switch (aspect_mask) {
    case VK_IMAGE_ASPECT_COLOR_BIT:
       assert(image->aspects == VK_IMAGE_ASPECT_COLOR_BIT);
-      return &image->color_surface;
+      sanitized_mask = VK_IMAGE_ASPECT_COLOR_BIT;
+      break;
    case VK_IMAGE_ASPECT_DEPTH_BIT:
       assert(image->aspects & VK_IMAGE_ASPECT_DEPTH_BIT);
-      return &image->depth_surface;
+      sanitized_mask = VK_IMAGE_ASPECT_DEPTH_BIT;
+      break;
    case VK_IMAGE_ASPECT_STENCIL_BIT:
       assert(image->aspects & VK_IMAGE_ASPECT_STENCIL_BIT);
-      return &image->stencil_surface;
+      sanitized_mask = VK_IMAGE_ASPECT_STENCIL_BIT;
+      break;
    case VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT:
       /* FINISHME: The Vulkan spec (git a511ba2) requires support for
        * combined depth stencil formats. Specifically, it states:
@@ -1233,13 +1365,29 @@ anv_image_get_surface_for_aspect_mask(const struct anv_image *image,
        * stencil surfaces from the underlying surface.
        */
       if (image->aspects & VK_IMAGE_ASPECT_DEPTH_BIT) {
-         return &image->depth_surface;
+         sanitized_mask = VK_IMAGE_ASPECT_DEPTH_BIT;
       } else {
          assert(image->aspects == VK_IMAGE_ASPECT_STENCIL_BIT);
-         return &image->stencil_surface;
+         sanitized_mask = VK_IMAGE_ASPECT_STENCIL_BIT;
       }
-    default:
+      break;
+   case VK_IMAGE_ASPECT_PLANE_0_BIT_KHR:
+      assert((image->aspects & ~VK_IMAGE_ASPECT_ANY_COLOR_BIT) == 0);
+      sanitized_mask = VK_IMAGE_ASPECT_PLANE_0_BIT_KHR;
+      break;
+   case VK_IMAGE_ASPECT_PLANE_1_BIT_KHR:
+      assert((image->aspects & ~VK_IMAGE_ASPECT_ANY_COLOR_BIT) == 0);
+      sanitized_mask = VK_IMAGE_ASPECT_PLANE_1_BIT_KHR;
+      break;
+   case VK_IMAGE_ASPECT_PLANE_2_BIT_KHR:
+      assert((image->aspects & ~VK_IMAGE_ASPECT_ANY_COLOR_BIT) == 0);
+      sanitized_mask = VK_IMAGE_ASPECT_PLANE_2_BIT_KHR;
+      break;
+   default:
        unreachable("image does not have aspect");
        return NULL;
    }
+
+   uint32_t plane = anv_image_aspect_to_plane(image->aspects, sanitized_mask);
+   return &image->planes[plane].surface;
 }
diff --git a/src/intel/vulkan/anv_intel.c b/src/intel/vulkan/anv_intel.c
index 991a93542d2..d4ef3beb743 100644
--- a/src/intel/vulkan/anv_intel.c
+++ b/src/intel/vulkan/anv_intel.c
@@ -80,8 +80,8 @@ VkResult anv_CreateDmaBufImageINTEL(
       pAllocator, &image_h);
 
    image = anv_image_from_handle(image_h);
-   image->bo = mem->bo;
-   image->offset = 0;
+   image->planes[0].bo = mem->bo;
+   image->planes[0].bo_offset = 0;
 
    assert(image->extent.width > 0);
    assert(image->extent.height > 0);
diff --git a/src/intel/vulkan/anv_private.h b/src/intel/vulkan/anv_private.h
index 5e14f49b196..cb324a3995e 100644
--- a/src/intel/vulkan/anv_private.h
+++ b/src/intel/vulkan/anv_private.h
@@ -1566,6 +1566,16 @@ anv_pipe_invalidate_bits_for_access_flags(VkAccessFlags flags)
    return pipe_bits;
 }
 
+#define VK_IMAGE_ASPECT_ANY_COLOR_BIT (         \
+   VK_IMAGE_ASPECT_COLOR_BIT | \
+   VK_IMAGE_ASPECT_PLANE_0_BIT_KHR | \
+   VK_IMAGE_ASPECT_PLANE_1_BIT_KHR | \
+   VK_IMAGE_ASPECT_PLANE_2_BIT_KHR)
+#define VK_IMAGE_ASPECT_PLANES_BITS ( \
+   VK_IMAGE_ASPECT_PLANE_0_BIT_KHR | \
+   VK_IMAGE_ASPECT_PLANE_1_BIT_KHR | \
+   VK_IMAGE_ASPECT_PLANE_2_BIT_KHR)
+
 struct anv_vertex_binding {
    struct anv_buffer *                          buffer;
    VkDeviceSize                                 offset;
@@ -2236,10 +2246,48 @@ anv_image_aspect_to_plane(VkImageAspectFlags image_aspects,
    case VK_IMAGE_ASPECT_PLANE_2_BIT_KHR:
       return 2;
    default:
+      /* Purposefully assert with depth/stencil aspects. */
       unreachable("invalid image aspect");
    }
 }
 
+static inline uint32_t
+anv_image_aspect_get_planes(VkImageAspectFlags aspect_mask)
+{
+   uint32_t planes = 0;
+
+   if (aspect_mask & (VK_IMAGE_ASPECT_COLOR_BIT |
+                      VK_IMAGE_ASPECT_DEPTH_BIT |
+                      VK_IMAGE_ASPECT_STENCIL_BIT |
+                      VK_IMAGE_ASPECT_PLANE_0_BIT_KHR))
+      planes++;
+   if (aspect_mask & VK_IMAGE_ASPECT_PLANE_1_BIT_KHR)
+      planes++;
+   if (aspect_mask & VK_IMAGE_ASPECT_PLANE_2_BIT_KHR)
+      planes++;
+
+   return planes;
+}
+
+static inline VkImageAspectFlags
+anv_plane_to_aspect(VkImageAspectFlags image_aspects,
+                    uint32_t plane)
+{
+   if (image_aspects & VK_IMAGE_ASPECT_ANY_COLOR_BIT) {
+      if (_mesa_bitcount(image_aspects) > 1)
+         return VK_IMAGE_ASPECT_PLANE_0_BIT_KHR << plane;
+      return VK_IMAGE_ASPECT_COLOR_BIT;
+   }
+   if (image_aspects & VK_IMAGE_ASPECT_DEPTH_BIT)
+      return VK_IMAGE_ASPECT_DEPTH_BIT << plane;
+   assert(image_aspects == VK_IMAGE_ASPECT_STENCIL_BIT);
+   return VK_IMAGE_ASPECT_STENCIL_BIT;
+}
+
+#define anv_foreach_plane_aspect_bit(p, b, aspects, image_aspects) \
+   for_each_bit(b, aspects) \
+      for (p = anv_image_aspect_to_plane(image_aspects, 1 << b); p != ~0; p = ~0)
+
 const struct anv_format *
 anv_get_format(VkFormat format);
 
@@ -2300,72 +2348,115 @@ struct anv_image {
     * of the actual surface formats.
     */
    VkFormat vk_format;
+   const struct anv_format *format;
+
    VkImageAspectFlags aspects;
    VkExtent3D extent;
    uint32_t levels;
    uint32_t array_size;
    uint32_t samples; /**< VkImageCreateInfo::samples */
+   uint32_t n_planes;
    VkImageUsageFlags usage; /**< Superset of VkImageCreateInfo::usage. */
    VkImageTiling tiling; /** VkImageCreateInfo::tiling */
 
    VkDeviceSize size;
    uint32_t alignment;
 
-   /* Set when bound */
-   struct anv_bo *bo;
-   VkDeviceSize offset;
+   bool disjoint;
 
    /**
     * Image subsurfaces
     *
-    * For each foo, anv_image::foo_surface is valid if and only if
-    * anv_image::aspects has a foo aspect.
+    * For each foo, anv_image::planes[x].surface is valid if and only if
+    * anv_image::aspects has a x aspect. Refer to anv_image_aspect_to_plane()
+    * to figure the number associated with a given aspect.
     *
     * The hardware requires that the depth buffer and stencil buffer be
     * separate surfaces.  From Vulkan's perspective, though, depth and stencil
     * reside in the same VkImage.  To satisfy both the hardware and Vulkan, we
     * allocate the depth and stencil buffers as separate surfaces in the same
     * bo.
+    *
+    * Memory layout :
+    *
+    * -----------------------
+    * |     surface0        |   /|\
+    * -----------------------    |
+    * |   shadow surface0   |    |
+    * -----------------------    | Plane 0
+    * |    aux surface0     |    |
+    * -----------------------    |
+    * | fast clear colors0  |   \|/
+    * -----------------------
+    * |     surface1        |   /|\
+    * -----------------------    |
+    * |   shadow surface1   |    |
+    * -----------------------    | Plane 1
+    * |    aux surface1     |    |
+    * -----------------------    |
+    * | fast clear colors1  |   \|/
+    * -----------------------
+    * |        ...          |
+    * |                     |
+    * -----------------------
     */
-   union {
-      struct anv_surface color_surface;
+   struct {
+      /**
+       * Offset of the entire plane (whenever the image is disjoint this is
+       * set to 0).
+       */
+      uint32_t offset;
 
-      struct {
-         struct anv_surface depth_surface;
-         struct anv_surface stencil_surface;
-      };
-   };
+      VkDeviceSize size;
+      uint32_t alignment;
 
-   /**
-    * A surface which shadows the main surface and may have different tiling.
-    * This is used for sampling using a tiling that isn't supported for other
-    * operations.
-    */
-   struct anv_surface shadow_surface;
+      struct anv_surface surface;
 
-   /**
-    * For color images, this is the aux usage for this image when not used as a
-    * color attachment.
-    *
-    * For depth/stencil images, this is set to ISL_AUX_USAGE_HIZ if the image
-    * has a HiZ buffer.
-    */
-   enum isl_aux_usage aux_usage;
+      /**
+       * A surface which shadows the main surface and may have different
+       * tiling. This is used for sampling using a tiling that isn't supported
+       * for other operations.
+       */
+      struct anv_surface shadow_surface;
+
+      /**
+       * For color images, this is the aux usage for this image when not used
+       * as a color attachment.
+       *
+       * For depth/stencil images, this is set to ISL_AUX_USAGE_HIZ if the
+       * image has a HiZ buffer.
+       */
+      enum isl_aux_usage aux_usage;
 
-   struct anv_surface aux_surface;
+      struct anv_surface aux_surface;
+
+      /**
+       * Offset of the fast clear state (used to compute the
+       * fast_clear_state_offset of the following planes).
+       */
+      uint32_t fast_clear_state_offset;
+
+      /**
+       * BO associated with this plane, set when bound.
+       */
+      struct anv_bo *bo;
+      VkDeviceSize bo_offset;
+   } planes[3];
 };
 
 /* Returns the number of auxiliary buffer levels attached to an image. */
 static inline uint8_t
-anv_image_aux_levels(const struct anv_image * const image)
+anv_image_aux_levels(const struct anv_image * const image, uint8_t plane)
 {
    assert(image);
-   return image->aux_surface.isl.size > 0 ? image->aux_surface.isl.levels : 0;
+   return image->planes[plane].aux_surface.isl.size > 0 ?
+          image->planes[plane].aux_surface.isl.levels : 0;
 }
 
 /* Returns the number of auxiliary buffer layers attached to an image. */
 static inline uint32_t
 anv_image_aux_layers(const struct anv_image * const image,
+                     const uint32_t plane,
                      const uint8_t miplevel)
 {
    assert(image);
@@ -2373,14 +2464,14 @@ anv_image_aux_layers(const struct anv_image * const image,
    /* The miplevel must exist in the main buffer. */
    assert(miplevel < image->levels);
 
-   if (miplevel >= anv_image_aux_levels(image)) {
+   if (miplevel >= anv_image_aux_levels(image, plane)) {
       /* There are no layers with auxiliary data because the miplevel has no
        * auxiliary data.
        */
       return 0;
    } else {
-      return MAX2(image->aux_surface.isl.logical_level0_px.array_len,
-                  image->aux_surface.isl.logical_level0_px.depth >> miplevel);
+      return MAX2(image->planes[plane].aux_surface.isl.logical_level0_px.array_len,
+                  image->planes[plane].aux_surface.isl.logical_level0_px.depth >> miplevel);
    }
 }
 
@@ -2404,13 +2495,12 @@ anv_fast_clear_state_entry_size(const struct anv_device *device)
 /* Returns true if a HiZ-enabled depth buffer can be sampled from. */
 static inline bool
 anv_can_sample_with_hiz(const struct gen_device_info * const devinfo,
-                        const VkImageAspectFlags aspect_mask,
-                        const uint32_t samples)
+                        const struct anv_image *image)
 {
    /* Validate the inputs. */
-   assert(devinfo && aspect_mask && samples);
-   return devinfo->gen >= 8 && (aspect_mask & VK_IMAGE_ASPECT_DEPTH_BIT) &&
-          samples == 1;
+   assert(devinfo && image && image->samples);
+   return devinfo->gen >= 8 && (image->aspects & VK_IMAGE_ASPECT_DEPTH_BIT) &&
+          image->samples == 1;
 }
 
 void
@@ -2421,19 +2511,21 @@ void
 anv_ccs_resolve(struct anv_cmd_buffer * const cmd_buffer,
                 const struct anv_state surface_state,
                 const struct anv_image * const image,
+                const uint32_t plane,
                 const uint8_t level, const uint32_t layer_count,
                 const enum blorp_fast_clear_op op);
 
 void
 anv_image_fast_clear(struct anv_cmd_buffer *cmd_buffer,
                      const struct anv_image *image,
+                     const uint32_t plane,
                      const uint32_t base_level, const uint32_t level_count,
                      const uint32_t base_layer, uint32_t layer_count);
 
 void
 anv_image_copy_to_shadow(struct anv_cmd_buffer *cmd_buffer,
                          const struct anv_image *image,
-                         VkImageAspectFlagBits aspect,
+                         uint32_t plane,
                          uint32_t base_level, uint32_t level_count,
                          uint32_t base_layer, uint32_t layer_count);
 
@@ -2458,37 +2550,71 @@ anv_get_levelCount(const struct anv_image *image,
           image->levels - range->baseMipLevel : range->levelCount;
 }
 
+static inline VkImageAspectFlags
+anv_image_color_aspects(struct anv_image *image, VkImageAspectFlags aspects)
+{
+   /* If the underlying image has color plane aspects and
+    * VK_IMAGE_ASPECT_COLOR_BIT has been requested, then return the aspects of
+    * the underlying image. */
+   if ((image->aspects & VK_IMAGE_ASPECT_PLANES_BITS) != 0 &&
+       aspects == VK_IMAGE_ASPECT_COLOR_BIT)
+      return image->aspects;
+
+   return aspects;
+}
+
+static inline bool
+anv_image_aspects_compatible(VkImageAspectFlags aspects1,
+                             VkImageAspectFlags aspects2)
+{
+   if (aspects1 == aspects2)
+      return true;
+
+   /* Only 1 color aspects are compatibles. */
+   if ((aspects1 & VK_IMAGE_ASPECT_ANY_COLOR_BIT) != 0 &&
+       (aspects2 & VK_IMAGE_ASPECT_ANY_COLOR_BIT) != 0 &&
+       _mesa_bitcount(aspects1) == _mesa_bitcount(aspects2))
+      return true;
+
+   return false;
+}
 
 struct anv_image_view {
    const struct anv_image *image; /**< VkImageViewCreateInfo::image */
 
-   struct isl_view isl;
-
    VkImageAspectFlags aspect_mask;
    VkFormat vk_format;
    VkExtent3D extent; /**< Extent of VkImageViewCreateInfo::baseMipLevel. */
 
-   /**
-    * RENDER_SURFACE_STATE when using image as a sampler surface with an image
-    * layout of SHADER_READ_ONLY_OPTIMAL or DEPTH_STENCIL_READ_ONLY_OPTIMAL.
-    */
-   struct anv_surface_state optimal_sampler_surface_state;
+   unsigned n_planes;
+   struct {
+      uint32_t image_plane;
 
-   /**
-    * RENDER_SURFACE_STATE when using image as a sampler surface with an image
-    * layout of GENERAL.
-    */
-   struct anv_surface_state general_sampler_surface_state;
+      struct isl_view isl;
 
-   /**
-    * RENDER_SURFACE_STATE when using image as a storage image. Separate states
-    * for write-only and readable, using the real format for write-only and the
-    * lowered format for readable.
-    */
-   struct anv_surface_state storage_surface_state;
-   struct anv_surface_state writeonly_storage_surface_state;
+      /**
+       * RENDER_SURFACE_STATE when using image as a sampler surface with an
+       * image layout of SHADER_READ_ONLY_OPTIMAL or
+       * DEPTH_STENCIL_READ_ONLY_OPTIMAL.
+       */
+      struct anv_surface_state optimal_sampler_surface_state;
 
-   struct brw_image_param storage_image_param;
+      /**
+       * RENDER_SURFACE_STATE when using image as a sampler surface with an
+       * image layout of GENERAL.
+       */
+      struct anv_surface_state general_sampler_surface_state;
+
+      /**
+       * RENDER_SURFACE_STATE when using image as a storage image. Separate
+       * states for write-only and readable, using the real format for
+       * write-only and the lowered format for readable.
+       */
+      struct anv_surface_state storage_surface_state;
+      struct anv_surface_state writeonly_storage_surface_state;
+
+      struct brw_image_param storage_image_param;
+   } planes[3];
 };
 
 enum anv_image_view_state_flags {
@@ -2498,7 +2624,7 @@ enum anv_image_view_state_flags {
 
 void anv_image_fill_surface_state(struct anv_device *device,
                                   const struct anv_image *image,
-                                  VkImageAspectFlagBits aspect,
+                                  uint32_t plane,
                                   const struct isl_view *view,
                                   isl_surf_usage_flags_t view_usage,
                                   enum isl_aux_usage aux_usage,
diff --git a/src/intel/vulkan/anv_wsi.c b/src/intel/vulkan/anv_wsi.c
index c3e5dc1870b..3fca076894c 100644
--- a/src/intel/vulkan/anv_wsi.c
+++ b/src/intel/vulkan/anv_wsi.c
@@ -221,7 +221,7 @@ anv_wsi_image_create(VkDevice device_h,
    result = anv_AllocateMemory(anv_device_to_handle(device),
       &(VkMemoryAllocateInfo) {
          .sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
-         .allocationSize = image->size,
+         .allocationSize = image->planes[0].surface.isl.size,
          .memoryTypeIndex = 0,
       },
       NULL /* XXX: pAllocator */,
@@ -240,7 +240,7 @@ anv_wsi_image_create(VkDevice device_h,
 
    anv_BindImageMemory(device_h, image_h, memory_h, 0);
 
-   struct anv_surface *surface = &image->color_surface;
+   struct anv_surface *surface = &image->planes[0].surface;
    assert(surface->isl.tiling == ISL_TILING_X);
 
    *row_pitch = surface->isl.row_pitch;
@@ -266,8 +266,8 @@ anv_wsi_image_create(VkDevice device_h,
    *image_p = image_h;
    *memory_p = memory_h;
    *fd_p = fd;
-   *size = image->size;
-   *offset = image->offset;
+   *size = image->planes[0].surface.isl.size;
+   *offset = image->planes[0].surface.offset;
    return VK_SUCCESS;
 fail_alloc_memory:
    anv_FreeMemory(device_h, memory_h, pAllocator);
diff --git a/src/intel/vulkan/gen8_cmd_buffer.c b/src/intel/vulkan/gen8_cmd_buffer.c
index 7bea231ea76..f19867ca320 100644
--- a/src/intel/vulkan/gen8_cmd_buffer.c
+++ b/src/intel/vulkan/gen8_cmd_buffer.c
@@ -323,7 +323,7 @@ want_stencil_pma_fix(struct anv_cmd_buffer *cmd_buffer)
    /* HiZ is enabled so we had better have a depth buffer with HiZ */
    const struct anv_image_view *ds_iview =
       anv_cmd_buffer_get_depth_stencil_view(cmd_buffer);
-   assert(ds_iview && ds_iview->image->aux_usage == ISL_AUX_USAGE_HIZ);
+   assert(ds_iview && ds_iview->image->planes[0].aux_usage == ISL_AUX_USAGE_HIZ);
 
    /* 3DSTATE_PS_EXTRA::PixelShaderValid */
    struct anv_pipeline *pipeline = cmd_buffer->state.pipeline;
diff --git a/src/intel/vulkan/genX_cmd_buffer.c b/src/intel/vulkan/genX_cmd_buffer.c
index dc5cf687dc6..71e46acfe4d 100644
--- a/src/intel/vulkan/genX_cmd_buffer.c
+++ b/src/intel/vulkan/genX_cmd_buffer.c
@@ -181,18 +181,45 @@ add_surface_state_reloc(struct anv_cmd_buffer *cmd_buffer,
 static void
 add_image_relocs(struct anv_cmd_buffer *cmd_buffer,
                  const struct anv_image *image,
+                 const uint32_t plane,
                  struct anv_surface_state state)
 {
    const struct isl_device *isl_dev = &cmd_buffer->device->isl_dev;
 
-   add_surface_state_reloc(cmd_buffer, state.state, image->bo, state.address);
+   add_surface_state_reloc(cmd_buffer, state.state,
+                           image->planes[plane].bo, state.address);
 
    if (state.aux_address) {
       VkResult result =
          anv_reloc_list_add(&cmd_buffer->surface_relocs,
                             &cmd_buffer->pool->alloc,
                             state.state.offset + isl_dev->ss.aux_addr_offset,
-                            image->bo, state.aux_address);
+                            image->planes[plane].bo,
+                            state.aux_address);
+      if (result != VK_SUCCESS)
+         anv_batch_set_error(&cmd_buffer->batch, result);
+   }
+}
+
+static void
+add_image_view_relocs(struct anv_cmd_buffer *cmd_buffer,
+                      const struct anv_image_view *image_view,
+                      const uint32_t plane,
+                      struct anv_surface_state state)
+{
+   const struct isl_device *isl_dev = &cmd_buffer->device->isl_dev;
+   const struct anv_image *image = image_view->image;
+   uint32_t image_plane = image_view->planes[plane].image_plane;
+
+   add_surface_state_reloc(cmd_buffer, state.state,
+                           image->planes[image_plane].bo, state.address);
+
+   if (state.aux_address) {
+      VkResult result =
+         anv_reloc_list_add(&cmd_buffer->surface_relocs,
+                            &cmd_buffer->pool->alloc,
+                            state.state.offset + isl_dev->ss.aux_addr_offset,
+                            image->planes[image_plane].bo, state.aux_address);
       if (result != VK_SUCCESS)
          anv_batch_set_error(&cmd_buffer->batch, result);
    }
@@ -225,8 +252,11 @@ color_attachment_compute_aux_usage(struct anv_device * device,
    struct anv_attachment_state *att_state = &cmd_state->attachments[att];
    struct anv_image_view *iview = cmd_state->framebuffer->attachments[att];
 
-   if (iview->isl.base_array_layer >=
-       anv_image_aux_layers(iview->image, iview->isl.base_level)) {
+   assert(iview->n_planes == 1);
+
+   if (iview->planes[0].isl.base_array_layer >=
+       anv_image_aux_layers(iview->image, 0,
+                            iview->planes[0].isl.base_level)) {
       /* There is no aux buffer which corresponds to the level and layer(s)
        * being accessed.
        */
@@ -234,12 +264,12 @@ color_attachment_compute_aux_usage(struct anv_device * device,
       att_state->input_aux_usage = ISL_AUX_USAGE_NONE;
       att_state->fast_clear = false;
       return;
-   } else if (iview->image->aux_usage == ISL_AUX_USAGE_MCS) {
+   } else if (iview->image->planes[0].aux_usage == ISL_AUX_USAGE_MCS) {
       att_state->aux_usage = ISL_AUX_USAGE_MCS;
       att_state->input_aux_usage = ISL_AUX_USAGE_MCS;
       att_state->fast_clear = false;
       return;
-   } else if (iview->image->aux_usage == ISL_AUX_USAGE_CCS_E) {
+   } else if (iview->image->planes[0].aux_usage == ISL_AUX_USAGE_CCS_E) {
       att_state->aux_usage = ISL_AUX_USAGE_CCS_E;
       att_state->input_aux_usage = ISL_AUX_USAGE_CCS_E;
    } else {
@@ -255,7 +285,7 @@ color_attachment_compute_aux_usage(struct anv_device * device,
        * In other words, we can only sample from a fast-cleared image if it
        * also supports color compression.
        */
-      if (isl_format_supports_ccs_e(&device->info, iview->isl.format)) {
+      if (isl_format_supports_ccs_e(&device->info, iview->planes[0].isl.format)) {
          att_state->input_aux_usage = ISL_AUX_USAGE_CCS_D;
 
          /* While fast-clear resolves and partial resolves are fairly cheap in the
@@ -274,10 +304,10 @@ color_attachment_compute_aux_usage(struct anv_device * device,
       }
    }
 
-   assert(iview->image->aux_surface.isl.usage & ISL_SURF_USAGE_CCS_BIT);
+   assert(iview->image->planes[0].aux_surface.isl.usage & ISL_SURF_USAGE_CCS_BIT);
 
    att_state->clear_color_is_zero_one =
-      color_is_zero_one(att_state->clear_value.color, iview->isl.format);
+      color_is_zero_one(att_state->clear_value.color, iview->planes[0].isl.format);
    att_state->clear_color_is_zero =
       att_state->clear_value.color.uint32[0] == 0 &&
       att_state->clear_value.color.uint32[1] == 0 &&
@@ -309,7 +339,8 @@ color_attachment_compute_aux_usage(struct anv_device * device,
        * layers.
        */
       if (cmd_state->framebuffer->layers !=
-          anv_image_aux_layers(iview->image, iview->isl.base_level)) {
+          anv_image_aux_layers(iview->image, 0,
+                               iview->planes[0].isl.base_level)) {
          att_state->fast_clear = false;
          if (GEN_GEN == 7) {
             anv_perf_warn(device->instance, iview->image,
@@ -325,7 +356,7 @@ color_attachment_compute_aux_usage(struct anv_device * device,
       if (cmd_state->pass->attachments[att].first_subpass_layout ==
           VK_IMAGE_LAYOUT_GENERAL &&
           (!att_state->clear_color_is_zero ||
-           iview->image->aux_usage == ISL_AUX_USAGE_NONE)) {
+           iview->image->planes[0].aux_usage == ISL_AUX_USAGE_NONE)) {
          att_state->fast_clear = false;
       }
 
@@ -370,14 +401,14 @@ transition_depth_buffer(struct anv_cmd_buffer *cmd_buffer,
     * that's currently in the buffer. Therefore, a data-preserving resolve
     * operation is not needed.
     */
-   if (image->aux_usage != ISL_AUX_USAGE_HIZ || initial_layout == final_layout)
+   if (image->planes[0].aux_usage != ISL_AUX_USAGE_HIZ || initial_layout == final_layout)
       return;
 
    const bool hiz_enabled = ISL_AUX_USAGE_HIZ ==
-      anv_layout_to_aux_usage(&cmd_buffer->device->info, image, image->aspects,
+      anv_layout_to_aux_usage(&cmd_buffer->device->info, image, 0,
                               initial_layout);
    const bool enable_hiz = ISL_AUX_USAGE_HIZ ==
-      anv_layout_to_aux_usage(&cmd_buffer->device->info, image, image->aspects,
+      anv_layout_to_aux_usage(&cmd_buffer->device->info, image, 0,
                               final_layout);
 
    enum blorp_hiz_op hiz_op;
@@ -403,14 +434,16 @@ enum fast_clear_state_field {
 static inline uint32_t
 get_fast_clear_state_offset(const struct anv_device *device,
                             const struct anv_image *image,
-                            unsigned level, enum fast_clear_state_field field)
+                            unsigned plane, unsigned level,
+                            enum fast_clear_state_field field)
 {
    assert(device && image);
-   assert(image->aspects == VK_IMAGE_ASPECT_COLOR_BIT);
-   assert(level < anv_image_aux_levels(image));
-   uint32_t offset = image->offset + image->aux_surface.offset +
-                     image->aux_surface.isl.size +
-                     anv_fast_clear_state_entry_size(device) * level;
+   assert(image->aspects & VK_IMAGE_ASPECT_ANY_COLOR_BIT);
+   assert(level < anv_image_aux_levels(image, plane));
+   /* Refer to the definition of anv_image for the memory layout. */
+   uint32_t offset = image->planes[plane].fast_clear_state_offset;
+
+   offset += anv_fast_clear_state_entry_size(device) * level;
 
    switch (field) {
    case FAST_CLEAR_STATE_FIELD_NEEDS_RESOLVE:
@@ -420,7 +453,8 @@ get_fast_clear_state_offset(const struct anv_device *device,
       break;
    }
 
-   assert(offset < image->offset + image->size);
+   assert(offset < image->planes[plane].surface.offset + image->planes[plane].size);
+
    return offset;
 }
 
@@ -433,14 +467,14 @@ get_fast_clear_state_offset(const struct anv_device *device,
 static void
 genX(set_image_needs_resolve)(struct anv_cmd_buffer *cmd_buffer,
                         const struct anv_image *image,
-                        unsigned level, bool needs_resolve)
+                        unsigned plane, unsigned level, bool needs_resolve)
 {
    assert(cmd_buffer && image);
-   assert(image->aspects == VK_IMAGE_ASPECT_COLOR_BIT);
-   assert(level < anv_image_aux_levels(image));
+   assert(image->aspects & VK_IMAGE_ASPECT_ANY_COLOR_BIT);
+   assert(level < anv_image_aux_levels(image, plane));
 
    const uint32_t resolve_flag_offset =
-      get_fast_clear_state_offset(cmd_buffer->device, image, level,
+      get_fast_clear_state_offset(cmd_buffer->device, image, plane, level,
                                   FAST_CLEAR_STATE_FIELD_NEEDS_RESOLVE);
 
    /* The HW docs say that there is no way to guarantee the completion of
@@ -448,7 +482,8 @@ genX(set_image_needs_resolve)(struct anv_cmd_buffer *cmd_buffer,
     * issues in testing is currently being used in the GL driver.
     */
    anv_batch_emit(&cmd_buffer->batch, GENX(MI_STORE_DATA_IMM), sdi) {
-      sdi.Address = (struct anv_address) { image->bo, resolve_flag_offset };
+      sdi.Address = (struct anv_address) { image->planes[plane].bo,
+                                           resolve_flag_offset };
       sdi.ImmediateData = needs_resolve;
    }
 }
@@ -456,14 +491,14 @@ genX(set_image_needs_resolve)(struct anv_cmd_buffer *cmd_buffer,
 static void
 genX(load_needs_resolve_predicate)(struct anv_cmd_buffer *cmd_buffer,
                                    const struct anv_image *image,
-                                   unsigned level)
+                                   unsigned plane, unsigned level)
 {
    assert(cmd_buffer && image);
-   assert(image->aspects == VK_IMAGE_ASPECT_COLOR_BIT);
-   assert(level < anv_image_aux_levels(image));
+   assert(image->aspects & VK_IMAGE_ASPECT_ANY_COLOR_BIT);
+   assert(level < anv_image_aux_levels(image, plane));
 
    const uint32_t resolve_flag_offset =
-      get_fast_clear_state_offset(cmd_buffer->device, image, level,
+      get_fast_clear_state_offset(cmd_buffer->device, image, plane, level,
                                   FAST_CLEAR_STATE_FIELD_NEEDS_RESOLVE);
 
    /* Make the pending predicated resolve a no-op if one is not needed.
@@ -473,7 +508,7 @@ genX(load_needs_resolve_predicate)(struct anv_cmd_buffer *cmd_buffer,
    emit_lri(&cmd_buffer->batch, MI_PREDICATE_SRC1 + 4, 0);
    emit_lri(&cmd_buffer->batch, MI_PREDICATE_SRC0    , 0);
    emit_lrm(&cmd_buffer->batch, MI_PREDICATE_SRC0 + 4,
-            image->bo, resolve_flag_offset);
+            image->planes[plane].bo, resolve_flag_offset);
    anv_batch_emit(&cmd_buffer->batch, GENX(MI_PREDICATE), mip) {
       mip.LoadOperation    = LOAD_LOADINV;
       mip.CombineOperation = COMBINE_SET;
@@ -484,11 +519,11 @@ genX(load_needs_resolve_predicate)(struct anv_cmd_buffer *cmd_buffer,
 static void
 init_fast_clear_state_entry(struct anv_cmd_buffer *cmd_buffer,
                             const struct anv_image *image,
-                            unsigned level)
+                            unsigned plane, unsigned level)
 {
    assert(cmd_buffer && image);
-   assert(image->aspects == VK_IMAGE_ASPECT_COLOR_BIT);
-   assert(level < anv_image_aux_levels(image));
+   assert(image->aspects & VK_IMAGE_ASPECT_ANY_COLOR_BIT);
+   assert(level < anv_image_aux_levels(image, plane));
 
    /* The resolve flag should updated to signify that fast-clear/compression
     * data needs to be removed when leaving the undefined layout. Such data
@@ -496,8 +531,8 @@ init_fast_clear_state_entry(struct anv_cmd_buffer *cmd_buffer,
     * to return incorrect data. The fast clear data in CCS_D buffers should
     * be removed because CCS_D isn't enabled all the time.
     */
-   genX(set_image_needs_resolve)(cmd_buffer, image, level,
-                                 image->aux_usage == ISL_AUX_USAGE_NONE);
+   genX(set_image_needs_resolve)(cmd_buffer, image, plane, level,
+                                 image->planes[plane].aux_usage == ISL_AUX_USAGE_NONE);
 
    /* The fast clear value dword(s) will be copied into a surface state object.
     * Ensure that the restrictions of the fields in the dword(s) are followed.
@@ -514,13 +549,14 @@ init_fast_clear_state_entry(struct anv_cmd_buffer *cmd_buffer,
    for (; i < cmd_buffer->device->isl_dev.ss.clear_value_size; i += 4) {
       anv_batch_emit(&cmd_buffer->batch, GENX(MI_STORE_DATA_IMM), sdi) {
          const uint32_t entry_offset =
-            get_fast_clear_state_offset(cmd_buffer->device, image, level,
+            get_fast_clear_state_offset(cmd_buffer->device, image, plane, level,
                                         FAST_CLEAR_STATE_FIELD_CLEAR_COLOR);
-         sdi.Address = (struct anv_address) { image->bo, entry_offset + i };
+         sdi.Address = (struct anv_address) { image->planes[plane].bo,
+                                              entry_offset + i };
 
          if (GEN_GEN >= 9) {
             /* MCS buffers on SKL+ can only have 1/0 clear colors. */
-            assert(image->aux_usage == ISL_AUX_USAGE_MCS);
+            assert(image->planes[plane].aux_usage == ISL_AUX_USAGE_MCS);
             sdi.ImmediateData = 0;
          } else if (GEN_VERSIONx10 >= 75) {
             /* Pre-SKL, the dword containing the clear values also contains
@@ -550,28 +586,31 @@ static void
 genX(copy_fast_clear_dwords)(struct anv_cmd_buffer *cmd_buffer,
                              struct anv_state surface_state,
                              const struct anv_image *image,
-                             unsigned level,
+                             unsigned plane, unsigned level,
                              bool copy_from_surface_state)
 {
    assert(cmd_buffer && image);
-   assert(image->aspects == VK_IMAGE_ASPECT_COLOR_BIT);
-   assert(level < anv_image_aux_levels(image));
+   assert(image->aspects & VK_IMAGE_ASPECT_ANY_COLOR_BIT);
+   assert(level < anv_image_aux_levels(image, plane));
 
    struct anv_bo *ss_bo =
       &cmd_buffer->device->surface_state_pool.block_pool.bo;
    uint32_t ss_clear_offset = surface_state.offset +
       cmd_buffer->device->isl_dev.ss.clear_value_offset;
    uint32_t entry_offset =
-      get_fast_clear_state_offset(cmd_buffer->device, image, level,
+      get_fast_clear_state_offset(cmd_buffer->device, image, plane, level,
                                   FAST_CLEAR_STATE_FIELD_CLEAR_COLOR);
    unsigned copy_size = cmd_buffer->device->isl_dev.ss.clear_value_size;
 
    if (copy_from_surface_state) {
-      genX(cmd_buffer_mi_memcpy)(cmd_buffer, image->bo, entry_offset,
+      genX(cmd_buffer_mi_memcpy)(cmd_buffer,
+                                 image->planes[plane].bo,
+                                 entry_offset,
                                  ss_bo, ss_clear_offset, copy_size);
    } else {
       genX(cmd_buffer_mi_memcpy)(cmd_buffer, ss_bo, ss_clear_offset,
-                                 image->bo, entry_offset, copy_size);
+                                 image->planes[plane].bo,
+                                 entry_offset, copy_size);
 
       /* Updating a surface state object may require that the state cache be
        * invalidated. From the SKL PRM, Shared Functions -> State -> State
@@ -603,6 +642,7 @@ genX(copy_fast_clear_dwords)(struct anv_cmd_buffer *cmd_buffer,
 static void
 transition_color_buffer(struct anv_cmd_buffer *cmd_buffer,
                         const struct anv_image *image,
+                        const uint32_t plane,
                         const uint32_t base_level, uint32_t level_count,
                         uint32_t base_layer, uint32_t layer_count,
                         VkImageLayout initial_layout,
@@ -610,7 +650,7 @@ transition_color_buffer(struct anv_cmd_buffer *cmd_buffer,
 {
    /* Validate the inputs. */
    assert(cmd_buffer);
-   assert(image && image->aspects == VK_IMAGE_ASPECT_COLOR_BIT);
+   assert(image && image->aspects & VK_IMAGE_ASPECT_ANY_COLOR_BIT);
    /* These values aren't supported for simplicity's sake. */
    assert(level_count != VK_REMAINING_MIP_LEVELS &&
           layer_count != VK_REMAINING_ARRAY_LAYERS);
@@ -630,22 +670,22 @@ transition_color_buffer(struct anv_cmd_buffer *cmd_buffer,
    if (initial_layout == final_layout)
       return;
 
-   if (image->shadow_surface.isl.size > 0 &&
+   if (image->planes[plane].shadow_surface.isl.size > 0 &&
        final_layout == VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL) {
       /* This surface is a linear compressed image with a tiled shadow surface
        * for texturing.  The client is about to use it in READ_ONLY_OPTIMAL so
        * we need to ensure the shadow copy is up-to-date.
        */
       assert(image->aspects == VK_IMAGE_ASPECT_COLOR_BIT);
-      assert(image->color_surface.isl.tiling == ISL_TILING_LINEAR);
-      assert(image->shadow_surface.isl.tiling != ISL_TILING_LINEAR);
-      assert(isl_format_is_compressed(image->color_surface.isl.format));
-      anv_image_copy_to_shadow(cmd_buffer, image, VK_IMAGE_ASPECT_COLOR_BIT,
+      assert(image->planes[plane].surface.isl.tiling == ISL_TILING_LINEAR);
+      assert(image->planes[plane].shadow_surface.isl.tiling != ISL_TILING_LINEAR);
+      assert(isl_format_is_compressed(image->planes[plane].surface.isl.format));
+      anv_image_copy_to_shadow(cmd_buffer, image, plane,
                                base_level, level_count,
                                base_layer, layer_count);
    }
 
-   if (base_layer >= anv_image_aux_layers(image, base_level))
+   if (base_layer >= anv_image_aux_layers(image, plane, base_level))
       return;
 
    /* A transition of a 3D subresource works on all slices at a time. */
@@ -655,9 +695,9 @@ transition_color_buffer(struct anv_cmd_buffer *cmd_buffer,
    }
 
    /* We're interested in the subresource range subset that has aux data. */
-   level_count = MIN2(level_count, anv_image_aux_levels(image) - base_level);
+   level_count = MIN2(level_count, anv_image_aux_levels(image, plane) - base_level);
    layer_count = MIN2(layer_count,
-                      anv_image_aux_layers(image, base_level) - base_layer);
+                      anv_image_aux_layers(image, plane, base_level) - base_layer);
    last_level_num = base_level + level_count;
 
    /* Record whether or not the layout is undefined. Pre-initialized images
@@ -678,7 +718,7 @@ transition_color_buffer(struct anv_cmd_buffer *cmd_buffer,
        * Initialize the relevant clear buffer entries.
        */
       for (unsigned level = base_level; level < last_level_num; level++)
-         init_fast_clear_state_entry(cmd_buffer, image, level);
+         init_fast_clear_state_entry(cmd_buffer, image, plane, level);
 
       /* Initialize the aux buffers to enable correct rendering. This operation
        * requires up to two steps: one to rid the aux buffer of data that may
@@ -702,7 +742,8 @@ transition_color_buffer(struct anv_cmd_buffer *cmd_buffer,
                           "define an MCS buffer.");
          }
 
-         anv_image_fast_clear(cmd_buffer, image, base_level, level_count,
+         anv_image_fast_clear(cmd_buffer, image, plane,
+                              base_level, level_count,
                               base_layer, layer_count);
       }
       /* At this point, some elements of the CCS buffer may have the fast-clear
@@ -714,7 +755,8 @@ transition_color_buffer(struct anv_cmd_buffer *cmd_buffer,
        * enabled. In this case, we must force the associated CCS buffers of the
        * specified range to enter the ambiguated state in advance.
        */
-      if (image->samples == 1 && image->aux_usage != ISL_AUX_USAGE_CCS_E &&
+      if (image->samples == 1 &&
+          image->planes[plane].aux_usage != ISL_AUX_USAGE_CCS_E &&
           final_layout != VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL) {
          /* The CCS_D buffer may not be enabled in the final layout. Continue
           * executing this function to perform a resolve.
@@ -766,13 +808,14 @@ transition_color_buffer(struct anv_cmd_buffer *cmd_buffer,
 
       /* The number of layers changes at each 3D miplevel. */
       if (image->type == VK_IMAGE_TYPE_3D) {
-         layer_count = MIN2(layer_count, anv_image_aux_layers(image, level));
+         layer_count = MIN2(layer_count, anv_image_aux_layers(image, plane, level));
       }
 
-      genX(load_needs_resolve_predicate)(cmd_buffer, image, level);
+      genX(load_needs_resolve_predicate)(cmd_buffer, image, plane, level);
 
-      enum isl_aux_usage aux_usage = image->aux_usage == ISL_AUX_USAGE_NONE ?
-                                     ISL_AUX_USAGE_CCS_D : image->aux_usage;
+      enum isl_aux_usage aux_usage =
+         image->planes[plane].aux_usage == ISL_AUX_USAGE_NONE ?
+         ISL_AUX_USAGE_CCS_D : image->planes[plane].aux_usage;
 
       /* Create a surface state with the right clear color and perform the
        * resolve.
@@ -780,9 +823,9 @@ transition_color_buffer(struct anv_cmd_buffer *cmd_buffer,
       struct anv_surface_state surface_state;
       surface_state.state = anv_cmd_buffer_alloc_surface_state(cmd_buffer);
       anv_image_fill_surface_state(cmd_buffer->device,
-                                   image, VK_IMAGE_ASPECT_COLOR_BIT,
+                                   image, plane,
                                    &(struct isl_view) {
-                                      .format = image->color_surface.isl.format,
+                                      .format = image->planes[plane].surface.isl.format,
                                       .swizzle = ISL_SWIZZLE_IDENTITY,
                                       .base_level = level,
                                       .levels = 1,
@@ -792,16 +835,16 @@ transition_color_buffer(struct anv_cmd_buffer *cmd_buffer,
                                    ISL_SURF_USAGE_RENDER_TARGET_BIT,
                                    aux_usage, NULL, 0,
                                    &surface_state, NULL);
-      add_image_relocs(cmd_buffer, image, surface_state);
+      add_image_relocs(cmd_buffer, image, 0, surface_state);
       genX(copy_fast_clear_dwords)(cmd_buffer, surface_state.state, image,
-                                   level, false /* copy to ss */);
+                                   plane, level, false /* copy to ss */);
       anv_ccs_resolve(cmd_buffer, surface_state.state, image,
-                      level, layer_count,
-                      image->aux_usage == ISL_AUX_USAGE_CCS_E ?
+                      plane, level, layer_count,
+                      image->planes[plane].aux_usage == ISL_AUX_USAGE_CCS_E ?
                       BLORP_FAST_CLEAR_OP_RESOLVE_PARTIAL :
                       BLORP_FAST_CLEAR_OP_RESOLVE_FULL);
 
-      genX(set_image_needs_resolve)(cmd_buffer, image, level, false);
+      genX(set_image_needs_resolve)(cmd_buffer, image, plane, level, false);
    }
 
    cmd_buffer->state.pending_pipe_bits |=
@@ -887,7 +930,7 @@ genX(cmd_buffer_setup_attachments)(struct anv_cmd_buffer *cmd_buffer,
          VkImageAspectFlags att_aspects = vk_format_aspects(att->format);
          VkImageAspectFlags clear_aspects = 0;
 
-         if (att_aspects == VK_IMAGE_ASPECT_COLOR_BIT) {
+         if (att_aspects & VK_IMAGE_ASPECT_ANY_COLOR_BIT) {
             /* color attachment */
             if (att->load_op == VK_ATTACHMENT_LOAD_OP_CLEAR) {
                clear_aspects |= VK_IMAGE_ASPECT_COLOR_BIT;
@@ -911,17 +954,17 @@ genX(cmd_buffer_setup_attachments)(struct anv_cmd_buffer *cmd_buffer,
 
          struct anv_image_view *iview = framebuffer->attachments[i];
          anv_assert(iview->vk_format == att->format);
+         anv_assert(iview->n_planes == 1);
 
          union isl_color_value clear_color = { .u32 = { 0, } };
-         if (att_aspects == VK_IMAGE_ASPECT_COLOR_BIT) {
+         if (att_aspects & VK_IMAGE_ASPECT_ANY_COLOR_BIT) {
             color_attachment_compute_aux_usage(cmd_buffer->device,
                                                state, i, begin->renderArea,
                                                &clear_color);
 
             anv_image_fill_surface_state(cmd_buffer->device,
-                                         iview->image,
-                                         VK_IMAGE_ASPECT_COLOR_BIT,
-                                         &iview->isl,
+                                         iview->image, 0,
+                                         &iview->planes[0].isl,
                                          ISL_SURF_USAGE_RENDER_TARGET_BIT,
                                          state->attachments[i].aux_usage,
                                          &clear_color,
@@ -929,8 +972,8 @@ genX(cmd_buffer_setup_attachments)(struct anv_cmd_buffer *cmd_buffer,
                                          &state->attachments[i].color,
                                          NULL);
 
-            add_image_relocs(cmd_buffer, iview->image,
-                             state->attachments[i].color);
+            add_image_view_relocs(cmd_buffer, iview, 0,
+                                  state->attachments[i].color);
          } else {
             /* This field will be initialized after the first subpass
              * transition.
@@ -942,9 +985,8 @@ genX(cmd_buffer_setup_attachments)(struct anv_cmd_buffer *cmd_buffer,
 
          if (need_input_attachment_state(&pass->attachments[i])) {
             anv_image_fill_surface_state(cmd_buffer->device,
-                                         iview->image,
-                                         VK_IMAGE_ASPECT_COLOR_BIT,
-                                         &iview->isl,
+                                         iview->image, 0,
+                                         &iview->planes[0].isl,
                                          ISL_SURF_USAGE_TEXTURE_BIT,
                                          state->attachments[i].input_aux_usage,
                                          &clear_color,
@@ -952,8 +994,8 @@ genX(cmd_buffer_setup_attachments)(struct anv_cmd_buffer *cmd_buffer,
                                          &state->attachments[i].input,
                                          NULL);
 
-            add_image_relocs(cmd_buffer, iview->image,
-                             state->attachments[i].input);
+            add_image_view_relocs(cmd_buffer, iview, 0,
+                                  state->attachments[i].input);
          }
       }
    }
@@ -1378,14 +1420,21 @@ void genX(CmdPipelineBarrier)(
          transition_depth_buffer(cmd_buffer, image,
                                  pImageMemoryBarriers[i].oldLayout,
                                  pImageMemoryBarriers[i].newLayout);
-      } else if (range->aspectMask == VK_IMAGE_ASPECT_COLOR_BIT) {
-         transition_color_buffer(cmd_buffer, image,
-                                 range->baseMipLevel,
-                                 anv_get_levelCount(image, range),
-                                 range->baseArrayLayer,
-                                 anv_get_layerCount(image, range),
-                                 pImageMemoryBarriers[i].oldLayout,
-                                 pImageMemoryBarriers[i].newLayout);
+      } else if (range->aspectMask & VK_IMAGE_ASPECT_ANY_COLOR_BIT) {
+         VkImageAspectFlags color_aspects =
+            anv_image_color_aspects(image, range->aspectMask);
+         uint32_t aspect_bit, plane;
+
+         anv_foreach_plane_aspect_bit(plane, aspect_bit, color_aspects,
+                                      image->aspects) {
+            transition_color_buffer(cmd_buffer, image, plane,
+                                    range->baseMipLevel,
+                                    anv_get_levelCount(image, range),
+                                    range->baseArrayLayer,
+                                    anv_get_layerCount(image, range),
+                                    pImageMemoryBarriers[i].oldLayout,
+                                    pImageMemoryBarriers[i].newLayout);
+         }
       }
    }
 
@@ -1574,26 +1623,28 @@ emit_binding_table(struct anv_cmd_buffer *cmd_buffer,
       case VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE: {
          struct anv_surface_state sstate =
             (desc->layout == VK_IMAGE_LAYOUT_GENERAL) ?
-            desc->image_view->general_sampler_surface_state :
-            desc->image_view->optimal_sampler_surface_state;
+            desc->image_view->planes[binding->plane].general_sampler_surface_state :
+            desc->image_view->planes[binding->plane].optimal_sampler_surface_state;
          surface_state = sstate.state;
          assert(surface_state.alloc_size);
-         add_image_relocs(cmd_buffer, desc->image_view->image, sstate);
+         add_image_view_relocs(cmd_buffer, desc->image_view,
+                               binding->plane, sstate);
          break;
       }
       case VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT:
          assert(stage == MESA_SHADER_FRAGMENT);
-         if (desc->image_view->aspect_mask != VK_IMAGE_ASPECT_COLOR_BIT) {
+         if ((desc->image_view->aspect_mask & VK_IMAGE_ASPECT_ANY_COLOR_BIT) == 0) {
             /* For depth and stencil input attachments, we treat it like any
              * old texture that a user may have bound.
              */
             struct anv_surface_state sstate =
                (desc->layout == VK_IMAGE_LAYOUT_GENERAL) ?
-               desc->image_view->general_sampler_surface_state :
-               desc->image_view->optimal_sampler_surface_state;
+               desc->image_view->planes[binding->plane].general_sampler_surface_state :
+               desc->image_view->planes[binding->plane].optimal_sampler_surface_state;
             surface_state = sstate.state;
             assert(surface_state.alloc_size);
-            add_image_relocs(cmd_buffer, desc->image_view->image, sstate);
+            add_image_view_relocs(cmd_buffer, desc->image_view,
+                                  binding->plane, sstate);
          } else {
             /* For color input attachments, we create the surface state at
              * vkBeginRenderPass time so that we can include aux and clear
@@ -1608,16 +1659,17 @@ emit_binding_table(struct anv_cmd_buffer *cmd_buffer,
 
       case VK_DESCRIPTOR_TYPE_STORAGE_IMAGE: {
          struct anv_surface_state sstate = (binding->write_only)
-            ? desc->image_view->writeonly_storage_surface_state
-            : desc->image_view->storage_surface_state;
+            ? desc->image_view->planes[binding->plane].writeonly_storage_surface_state
+            : desc->image_view->planes[binding->plane].storage_surface_state;
          surface_state = sstate.state;
          assert(surface_state.alloc_size);
-         add_image_relocs(cmd_buffer, desc->image_view->image, sstate);
+         add_image_view_relocs(cmd_buffer, desc->image_view,
+                               binding->plane, sstate);
 
          struct brw_image_param *image_param =
             &cmd_buffer->state.push_constants[stage]->images[image++];
 
-         *image_param = desc->image_view->storage_image_param;
+         *image_param = desc->image_view->planes[binding->plane].storage_image_param;
          image_param->surface_idx = bias + s;
          break;
       }
@@ -2753,41 +2805,51 @@ cmd_buffer_emit_depth_stencil(struct anv_cmd_buffer *cmd_buffer)
    };
 
    if (iview)
-      info.view = &iview->isl;
+      info.view = &iview->planes[0].isl;
 
    if (image && (image->aspects & VK_IMAGE_ASPECT_DEPTH_BIT)) {
-      info.depth_surf = &image->depth_surface.isl;
+      uint32_t depth_plane =
+         anv_image_aspect_to_plane(image->aspects, VK_IMAGE_ASPECT_DEPTH_BIT);
+      const struct anv_surface *surface = &image->planes[depth_plane].surface;
+
+      info.depth_surf = &surface->isl;
 
       info.depth_address =
          anv_batch_emit_reloc(&cmd_buffer->batch,
                               dw + device->isl_dev.ds.depth_offset / 4,
-                              image->bo,
-                              image->offset + image->depth_surface.offset);
+                              image->planes[depth_plane].bo,
+                              image->planes[depth_plane].bo_offset +
+                              surface->offset);
 
       const uint32_t ds =
          cmd_buffer->state.subpass->depth_stencil_attachment.attachment;
       info.hiz_usage = cmd_buffer->state.attachments[ds].aux_usage;
       if (info.hiz_usage == ISL_AUX_USAGE_HIZ) {
-         info.hiz_surf = &image->aux_surface.isl;
+         info.hiz_surf = &image->planes[depth_plane].aux_surface.isl;
 
          info.hiz_address =
             anv_batch_emit_reloc(&cmd_buffer->batch,
                                  dw + device->isl_dev.ds.hiz_offset / 4,
-                                 image->bo,
-                                 image->offset + image->aux_surface.offset);
+                                 image->planes[depth_plane].bo,
+                                 image->planes[depth_plane].bo_offset +
+                                 image->planes[depth_plane].aux_surface.offset);
 
          info.depth_clear_value = ANV_HZ_FC_VAL;
       }
    }
 
    if (image && (image->aspects & VK_IMAGE_ASPECT_STENCIL_BIT)) {
-      info.stencil_surf = &image->stencil_surface.isl;
+      uint32_t stencil_plane =
+         anv_image_aspect_to_plane(image->aspects, VK_IMAGE_ASPECT_STENCIL_BIT);
+      const struct anv_surface *surface = &image->planes[stencil_plane].surface;
+
+      info.stencil_surf = &surface->isl;
 
       info.stencil_address =
          anv_batch_emit_reloc(&cmd_buffer->batch,
                               dw + device->isl_dev.ds.stencil_offset / 4,
-                              image->bo,
-                              image->offset + image->stencil_surface.offset);
+                              image->planes[stencil_plane].bo,
+                              image->planes[stencil_plane].bo_offset + surface->offset);
    }
 
    isl_emit_depth_stencil_hiz_s(&device->isl_dev, dw, &info);
@@ -2877,7 +2939,7 @@ cmd_buffer_subpass_transition_layouts(struct anv_cmd_buffer * const cmd_buffer,
             att_state->input_aux_usage != att_state->aux_usage;
       if (subpass_end) {
          target_layout = att_desc->final_layout;
-      } else if (iview->aspect_mask == VK_IMAGE_ASPECT_COLOR_BIT &&
+      } else if (iview->aspect_mask & VK_IMAGE_ASPECT_ANY_COLOR_BIT &&
                  !input_needs_resolve) {
          /* Layout transitions before the final only help to enable sampling as
           * an input attachment. If the input attachment supports sampling
@@ -2895,12 +2957,12 @@ cmd_buffer_subpass_transition_layouts(struct anv_cmd_buffer * const cmd_buffer,
                                  att_state->current_layout, target_layout);
          att_state->aux_usage =
             anv_layout_to_aux_usage(&cmd_buffer->device->info, image,
-                                    image->aspects, target_layout);
-      } else if (image->aspects == VK_IMAGE_ASPECT_COLOR_BIT) {
-         transition_color_buffer(cmd_buffer, image,
-                                 iview->isl.base_level, 1,
-                                 iview->isl.base_array_layer,
-                                 iview->isl.array_len,
+                                    0, target_layout);
+      } else if (image->aspects & VK_IMAGE_ASPECT_ANY_COLOR_BIT) {
+         transition_color_buffer(cmd_buffer, image, 0,
+                                 iview->planes[0].isl.base_level, 1,
+                                 iview->planes[0].isl.base_array_layer,
+                                 iview->planes[0].isl.array_len,
                                  att_state->current_layout, target_layout);
       }
 
@@ -2945,22 +3007,24 @@ cmd_buffer_subpass_sync_fast_clear_values(struct anv_cmd_buffer *cmd_buffer)
       if (att_state->pending_clear_aspects && att_state->fast_clear) {
          /* Update the fast clear state entry. */
          genX(copy_fast_clear_dwords)(cmd_buffer, att_state->color.state,
-                                      iview->image, iview->isl.base_level,
+                                      iview->image, 0, iview->planes[0].isl.base_level,
                                       true /* copy from ss */);
 
          /* Fast-clears impact whether or not a resolve will be necessary. */
-         if (iview->image->aux_usage == ISL_AUX_USAGE_CCS_E &&
+         if (iview->image->planes[0].aux_usage == ISL_AUX_USAGE_CCS_E &&
              att_state->clear_color_is_zero) {
             /* This image always has the auxiliary buffer enabled. We can mark
              * the subresource as not needing a resolve because the clear color
              * will match what's in every RENDER_SURFACE_STATE object when it's
              * being used for sampling.
              */
-            genX(set_image_needs_resolve)(cmd_buffer, iview->image,
-                                          iview->isl.base_level, false);
+            genX(set_image_needs_resolve)(cmd_buffer, iview->image, 0,
+                                          iview->planes[0].isl.base_level,
+                                          false);
          } else {
-            genX(set_image_needs_resolve)(cmd_buffer, iview->image,
-                                          iview->isl.base_level, true);
+            genX(set_image_needs_resolve)(cmd_buffer, iview->image, 0,
+                                          iview->planes[0].isl.base_level,
+                                          true);
          }
       } else if (rp_att->load_op == VK_ATTACHMENT_LOAD_OP_LOAD) {
          /* The attachment may have been fast-cleared in a previous render
@@ -2969,13 +3033,13 @@ cmd_buffer_subpass_sync_fast_clear_values(struct anv_cmd_buffer *cmd_buffer)
           * TODO: Do this only once per render pass instead of every subpass.
           */
          genX(copy_fast_clear_dwords)(cmd_buffer, att_state->color.state,
-                                      iview->image, iview->isl.base_level,
+                                      iview->image, 0, iview->planes[0].isl.base_level,
                                       false /* copy to ss */);
 
          if (need_input_attachment_state(rp_att) &&
              att_state->input_aux_usage != ISL_AUX_USAGE_NONE) {
             genX(copy_fast_clear_dwords)(cmd_buffer, att_state->input.state,
-                                         iview->image, iview->isl.base_level,
+                                         iview->image, 0, iview->planes[0].isl.base_level,
                                          false /* copy to ss */);
          }
       }
-- 
2.14.2



More information about the mesa-dev mailing list