/*
 * Mesa 3-D graphics library
 *
 * Copyright (C) 2013 LunarG, Inc.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included
 * in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 *
 * Authors:
 *    Chia-I Wu <olv@lunarg.com>
 */

#include "util/u_pack_color.h"
#include "intel_reg.h"

#include "ilo_3d.h"
#include "ilo_context.h"
#include "ilo_cp.h"
#include "ilo_resource.h"
#include "ilo_blitter.h"

#ifndef COLOR_BLT_CMD
#define COLOR_BLT_CMD (CMD_2D | (0x40 << 22))
#endif

#ifndef SRC_COPY_BLT_CMD
#define SRC_COPY_BLT_CMD (CMD_2D | (0x43 << 22))
#endif

enum gen6_blt_mask {
   GEN6_BLT_MASK_8,
   GEN6_BLT_MASK_16,
   GEN6_BLT_MASK_32,
   GEN6_BLT_MASK_32_LO,
   GEN6_BLT_MASK_32_HI,
};

/*
 * From the Sandy Bridge PRM, volume 1 part 5, page 7:
 *
 *     "The BLT engine is capable of transferring very large quantities of
 *      graphics data. Any graphics data read from and written to the
 *      destination is permitted to represent a number of pixels that occupies
 *      up to 65,536 scan lines and up to 32,768 bytes per scan line at the
 *      destination. The maximum number of pixels that may be represented per
 *      scan line's worth of graphics data depends on the color depth."
 */
static const int gen6_max_bytes_per_scanline = 32768;
static const int gen6_max_scanlines = 65536;

static void
gen6_emit_MI_FLUSH_DW(struct ilo_dev_info *dev, struct ilo_cp *cp)
{
   const uint8_t cmd_len = 4;

   ilo_cp_begin(cp, cmd_len);
   ilo_cp_write(cp, MI_FLUSH_DW | (cmd_len - 2));
   ilo_cp_write(cp, 0);
   ilo_cp_write(cp, 0);
   ilo_cp_write(cp, 0);
   ilo_cp_end(cp);
}

static void
gen6_emit_MI_LOAD_REGISTER_IMM(struct ilo_dev_info *dev,
                               uint32_t reg, uint32_t val,
                               struct ilo_cp *cp)
{
   const uint8_t cmd_len = 3;

   ilo_cp_begin(cp, cmd_len);
   ilo_cp_write(cp, MI_LOAD_REGISTER_IMM | (cmd_len - 2));
   ilo_cp_write(cp, reg);
   ilo_cp_write(cp, val);
   ilo_cp_end(cp);
}

static uint32_t
gen6_translate_blt_value_mask(enum gen6_blt_mask value_mask)
{
   switch (value_mask) {
   case GEN6_BLT_MASK_8:  return BR13_8;
   case GEN6_BLT_MASK_16: return BR13_565;
   default:               return BR13_8888;
   }
}

static uint32_t
gen6_translate_blt_write_mask(enum gen6_blt_mask write_mask)
{
   switch (write_mask) {
   case GEN6_BLT_MASK_32:    return XY_BLT_WRITE_RGB |
                                    XY_BLT_WRITE_ALPHA;
   case GEN6_BLT_MASK_32_LO: return XY_BLT_WRITE_RGB;
   case GEN6_BLT_MASK_32_HI: return XY_BLT_WRITE_ALPHA;
   default:                  return 0;
   }
}

static uint32_t
gen6_translate_blt_cpp(enum gen6_blt_mask mask)
{
   switch (mask) {
   case GEN6_BLT_MASK_8:  return 1;
   case GEN6_BLT_MASK_16: return 2;
   default:               return 4;
   }
}

static void
gen6_emit_COLOR_BLT(struct ilo_dev_info *dev,
                    struct intel_bo *dst_bo,
                    int16_t dst_pitch, uint32_t dst_offset,
                    uint16_t width, uint16_t height,
                    uint32_t pattern, uint8_t rop,
                    enum gen6_blt_mask value_mask,
                    enum gen6_blt_mask write_mask,
                    struct ilo_cp *cp)
{
   const uint8_t cmd_len = 5;
   const int cpp = gen6_translate_blt_cpp(value_mask);
   uint32_t dw0, dw1;

   dw0 = COLOR_BLT_CMD |
         gen6_translate_blt_write_mask(write_mask) |
         (cmd_len - 2);

   assert(width < gen6_max_bytes_per_scanline);
   assert(height < gen6_max_scanlines);
   /* offsets are naturally aligned and pitches are dword-aligned */
   assert(dst_offset % cpp == 0 && dst_pitch % 4 == 0);

   dw1 = rop << 16 |
         gen6_translate_blt_value_mask(value_mask) |
         dst_pitch;

   ilo_cp_begin(cp, cmd_len);
   ilo_cp_write(cp, dw0);
   ilo_cp_write(cp, dw1);
   ilo_cp_write(cp, height << 16 | width);
   ilo_cp_write_bo(cp, dst_offset, dst_bo, INTEL_DOMAIN_RENDER,
                                           INTEL_DOMAIN_RENDER);
   ilo_cp_write(cp, pattern);
   ilo_cp_end(cp);
}

static void
gen6_emit_XY_COLOR_BLT(struct ilo_dev_info *dev,
                       struct intel_bo *dst_bo,
                       enum intel_tiling_mode dst_tiling,
                       int16_t dst_pitch, uint32_t dst_offset,
                       int16_t x1, int16_t y1, int16_t x2, int16_t y2,
                       uint32_t pattern, uint8_t rop,
                       enum gen6_blt_mask value_mask,
                       enum gen6_blt_mask write_mask,
                       struct ilo_cp *cp)
{
   const uint8_t cmd_len = 6;
   const int cpp = gen6_translate_blt_cpp(value_mask);
   int dst_align, dst_pitch_shift;
   uint32_t dw0, dw1;

   dw0 = XY_COLOR_BLT_CMD |
         gen6_translate_blt_write_mask(write_mask) |
         (cmd_len - 2);

   if (dst_tiling == INTEL_TILING_NONE) {
      dst_align = 4;
      dst_pitch_shift = 0;
   }
   else {
      dw0 |= XY_DST_TILED;

      dst_align = (dst_tiling == INTEL_TILING_Y) ? 128 : 512;
      /* in dwords when tiled */
      dst_pitch_shift = 2;
   }

   assert((x2 - x1) * cpp < gen6_max_bytes_per_scanline);
   assert(y2 - y1 < gen6_max_scanlines);
   assert(dst_offset % dst_align == 0 && dst_pitch % dst_align == 0);

   dw1 = rop << 16 |
         gen6_translate_blt_value_mask(value_mask) |
         dst_pitch >> dst_pitch_shift;

   ilo_cp_begin(cp, cmd_len);
   ilo_cp_write(cp, dw0);
   ilo_cp_write(cp, dw1);
   ilo_cp_write(cp, y1 << 16 | x1);
   ilo_cp_write(cp, y2 << 16 | x2);
   ilo_cp_write_bo(cp, dst_offset, dst_bo,
                   INTEL_DOMAIN_RENDER, INTEL_DOMAIN_RENDER);
   ilo_cp_write(cp, pattern);
   ilo_cp_end(cp);
}

static void
gen6_emit_SRC_COPY_BLT(struct ilo_dev_info *dev,
                       struct intel_bo *dst_bo,
                       int16_t dst_pitch, uint32_t dst_offset,
                       uint16_t width, uint16_t height,
                       struct intel_bo *src_bo,
                       int16_t src_pitch, uint32_t src_offset,
                       bool dir_rtl, uint8_t rop,
                       enum gen6_blt_mask value_mask,
                       enum gen6_blt_mask write_mask,
                       struct ilo_cp *cp)
{
   const uint8_t cmd_len = 6;
   const int cpp = gen6_translate_blt_cpp(value_mask);
   uint32_t dw0, dw1;

   dw0 = SRC_COPY_BLT_CMD |
         gen6_translate_blt_write_mask(write_mask) |
         (cmd_len - 2);

   assert(width < gen6_max_bytes_per_scanline);
   assert(height < gen6_max_scanlines);
   /* offsets are naturally aligned and pitches are dword-aligned */
   assert(dst_offset % cpp == 0 && dst_pitch % 4 == 0);
   assert(src_offset % cpp == 0 && src_pitch % 4 == 0);

   dw1 = rop << 16 |
         gen6_translate_blt_value_mask(value_mask) |
         dst_pitch;

   if (dir_rtl)
      dw1 |= 1 << 30;

   ilo_cp_begin(cp, cmd_len);
   ilo_cp_write(cp, dw0);
   ilo_cp_write(cp, dw1);
   ilo_cp_write(cp, height << 16 | width);
   ilo_cp_write_bo(cp, dst_offset, dst_bo, INTEL_DOMAIN_RENDER,
                                           INTEL_DOMAIN_RENDER);
   ilo_cp_write(cp, src_pitch);
   ilo_cp_write_bo(cp, src_offset, src_bo, INTEL_DOMAIN_RENDER, 0);
   ilo_cp_end(cp);
}

static void
gen6_emit_XY_SRC_COPY_BLT(struct ilo_dev_info *dev,
                          struct intel_bo *dst_bo,
                          enum intel_tiling_mode dst_tiling,
                          int16_t dst_pitch, uint32_t dst_offset,
                          int16_t x1, int16_t y1, int16_t x2, int16_t y2,
                          struct intel_bo *src_bo,
                          enum intel_tiling_mode src_tiling,
                          int16_t src_pitch, uint32_t src_offset,
                          int16_t src_x, int16_t src_y, uint8_t rop,
                          enum gen6_blt_mask value_mask,
                          enum gen6_blt_mask write_mask,
                          struct ilo_cp *cp)
{
   const uint8_t cmd_len = 8;
   const int cpp = gen6_translate_blt_cpp(value_mask);
   int dst_align, dst_pitch_shift;
   int src_align, src_pitch_shift;
   uint32_t dw0, dw1;

   dw0 = XY_SRC_COPY_BLT_CMD |
         gen6_translate_blt_write_mask(write_mask) |
         (cmd_len - 2);

   if (dst_tiling == INTEL_TILING_NONE) {
      dst_align = 4;
      dst_pitch_shift = 0;
   }
   else {
      dw0 |= XY_DST_TILED;

      dst_align = (dst_tiling == INTEL_TILING_Y) ? 128 : 512;
      /* in dwords when tiled */
      dst_pitch_shift = 2;
   }

   if (src_tiling == INTEL_TILING_NONE) {
      src_align = 4;
      src_pitch_shift = 0;
   }
   else {
      dw0 |= XY_SRC_TILED;

      src_align = (src_tiling == INTEL_TILING_Y) ? 128 : 512;
      /* in dwords when tiled */
      src_pitch_shift = 2;
   }

   assert((x2 - x1) * cpp < gen6_max_bytes_per_scanline);
   assert(y2 - y1 < gen6_max_scanlines);
   assert(dst_offset % dst_align == 0 && dst_pitch % dst_align == 0);
   assert(src_offset % src_align == 0 && src_pitch % src_align == 0);

   dw1 = rop << 16 |
         gen6_translate_blt_value_mask(value_mask) |
         dst_pitch >> dst_pitch_shift;

   ilo_cp_begin(cp, cmd_len);
   ilo_cp_write(cp, dw0);
   ilo_cp_write(cp, dw1);
   ilo_cp_write(cp, y1 << 16 | x1);
   ilo_cp_write(cp, y2 << 16 | x2);
   ilo_cp_write_bo(cp, dst_offset, dst_bo, INTEL_DOMAIN_RENDER,
                                           INTEL_DOMAIN_RENDER);
   ilo_cp_write(cp, src_y << 16 | src_x);
   ilo_cp_write(cp, src_pitch >> src_pitch_shift);
   ilo_cp_write_bo(cp, src_offset, src_bo, INTEL_DOMAIN_RENDER, 0);
   ilo_cp_end(cp);
}

static uint32_t
ilo_blitter_blt_begin(struct ilo_blitter *blitter, int max_cmd_size,
                      struct intel_bo *dst, enum intel_tiling_mode dst_tiling,
                      struct intel_bo *src, enum intel_tiling_mode src_tiling)
{
   struct ilo_context *ilo = blitter->ilo;
   struct intel_bo *aper_check[3];
   int count;
   uint32_t swctrl;

   /* change ring */
   ilo_cp_set_ring(ilo->cp, ILO_CP_RING_BLT);
   ilo_cp_set_owner(ilo->cp, NULL, 0);

   /* check aperture space */
   aper_check[0] = ilo->cp->bo;
   aper_check[1] = dst;
   count = 2;

   if (src) {
      aper_check[2] = src;
      count++;
   }

   if (intel_winsys_check_aperture_space(ilo->winsys, aper_check, count))
      ilo_cp_flush(ilo->cp);

   /* set BCS_SWCTRL */
   swctrl = 0x0;

   if (dst_tiling == INTEL_TILING_Y) {
      swctrl |= BCS_SWCTRL_DST_Y << 16 |
                BCS_SWCTRL_DST_Y;
   }

   if (src && src_tiling == INTEL_TILING_Y) {
      swctrl |= BCS_SWCTRL_SRC_Y << 16 |
                BCS_SWCTRL_SRC_Y;
   }

   if (swctrl) {
      /*
       * Most clients expect BLT engine to be stateless.  If we have to set
       * BCS_SWCTRL to a non-default value, we have to set it back in the same
       * batch buffer.
       */
      if (ilo_cp_space(ilo->cp) < (4 + 3) * 2 + max_cmd_size)
         ilo_cp_flush(ilo->cp);

      ilo_cp_assert_no_implicit_flush(ilo->cp, true);

      /*
       * From the Ivy Bridge PRM, volume 1 part 4, page 133:
       *
       *     "SW is required to flush the HW before changing the polarity of
       *      this bit (Tile Y Destination/Source)."
       */
      gen6_emit_MI_FLUSH_DW(ilo->dev, ilo->cp);
      gen6_emit_MI_LOAD_REGISTER_IMM(ilo->dev, BCS_SWCTRL, swctrl, ilo->cp);

      swctrl &= ~(BCS_SWCTRL_DST_Y | BCS_SWCTRL_SRC_Y);
   }

   return swctrl;
}

static void
ilo_blitter_blt_end(struct ilo_blitter *blitter, uint32_t swctrl)
{
   struct ilo_context *ilo = blitter->ilo;

   /* set BCS_SWCTRL back */
   if (swctrl) {
      gen6_emit_MI_FLUSH_DW(ilo->dev, ilo->cp);
      gen6_emit_MI_LOAD_REGISTER_IMM(ilo->dev, BCS_SWCTRL, swctrl, ilo->cp);

      ilo_cp_assert_no_implicit_flush(ilo->cp, false);
   }
}

static bool
buf_clear_region(struct ilo_blitter *blitter,
                 struct ilo_buffer *dst,
                 unsigned dst_offset, unsigned dst_size,
                 uint32_t val,
                 enum gen6_blt_mask value_mask,
                 enum gen6_blt_mask write_mask)
{
   const uint8_t rop = 0xf0; /* PATCOPY */
   const int cpp = gen6_translate_blt_cpp(value_mask);
   struct ilo_context *ilo = blitter->ilo;
   unsigned offset = 0;

   if (dst_offset % cpp || dst_size % cpp)
      return false;

   ilo_blitter_blt_begin(blitter, 0,
         dst->bo, INTEL_TILING_NONE, NULL, INTEL_TILING_NONE);

   while (dst_size) {
      unsigned width, height;
      int16_t pitch;

      width = dst_size;
      height = 1;
      pitch = 0;

      if (width > gen6_max_bytes_per_scanline) {
         /* less than INT16_MAX and dword-aligned */
         pitch = 32764;

         width = pitch;
         height = dst_size / width;
         if (height > gen6_max_scanlines)
            height = gen6_max_scanlines;
      }

      gen6_emit_COLOR_BLT(ilo->dev, dst->bo, pitch, dst_offset + offset,
            width, height, val, rop, value_mask, write_mask, ilo->cp);

      offset += pitch * height;
      dst_size -= width * height;
   }

   ilo_blitter_blt_end(blitter, 0);

   return true;
}

static bool
buf_copy_region(struct ilo_blitter *blitter,
                struct ilo_buffer *dst, unsigned dst_offset,
                struct ilo_buffer *src, unsigned src_offset,
                unsigned size)
{
   const uint8_t rop = 0xcc; /* SRCCOPY */
   struct ilo_context *ilo = blitter->ilo;
   unsigned offset = 0;

   ilo_blitter_blt_begin(blitter, 0,
         dst->bo, INTEL_TILING_NONE, src->bo, INTEL_TILING_NONE);

   while (size) {
      unsigned width, height;
      int16_t pitch;

      width = size;
      height = 1;
      pitch = 0;

      if (width > gen6_max_bytes_per_scanline) {
         /* less than INT16_MAX and dword-aligned */
         pitch = 32764;

         width = pitch;
         height = size / width;
         if (height > gen6_max_scanlines)
            height = gen6_max_scanlines;
      }

      gen6_emit_SRC_COPY_BLT(ilo->dev,
            dst->bo, pitch, dst_offset + offset,
            width, height,
            src->bo, pitch, src_offset + offset,
            false, rop, GEN6_BLT_MASK_8, GEN6_BLT_MASK_8,
            ilo->cp);

      offset += pitch * height;
      size -= width * height;
   }

   ilo_blitter_blt_end(blitter, 0);

   return true;
}

static bool
tex_clear_region(struct ilo_blitter *blitter,
                 struct ilo_texture *dst, unsigned dst_level,
                 const struct pipe_box *dst_box,
                 uint32_t val,
                 enum gen6_blt_mask value_mask,
                 enum gen6_blt_mask write_mask)
{
   const int cpp = gen6_translate_blt_cpp(value_mask);
   const unsigned max_extent = 32767; /* INT16_MAX */
   const uint8_t rop = 0xf0; /* PATCOPY */
   struct ilo_context *ilo = blitter->ilo;
   uint32_t swctrl;
   int slice;

   /* no W-tiling support */
   if (dst->separate_s8)
      return false;

   if (dst->bo_stride > max_extent)
      return false;

   swctrl = ilo_blitter_blt_begin(blitter, dst_box->depth * 6,
         dst->bo, dst->tiling, NULL, INTEL_TILING_NONE);

   for (slice = 0; slice < dst_box->depth; slice++) {
      const struct ilo_texture_slice *dst_slice =
         &dst->slice_offsets[dst_level][dst_box->z + slice];
      unsigned x1, y1, x2, y2;

      x1 = dst_slice->x + dst_box->x;
      y1 = dst_slice->y + dst_box->y;
      x2 = x1 + dst_box->width;
      y2 = y1 + dst_box->height;

      if (x2 > max_extent || y2 > max_extent ||
          (x2 - x1) * cpp > gen6_max_bytes_per_scanline)
         break;

      gen6_emit_XY_COLOR_BLT(ilo->dev,
            dst->bo, dst->tiling, dst->bo_stride, 0,
            x1, y1, x2, y2, val, rop, value_mask, write_mask,
            ilo->cp);
   }

   ilo_blitter_blt_end(blitter, swctrl);

   return (slice == dst_box->depth);
}

static bool
tex_copy_region(struct ilo_blitter *blitter,
                struct ilo_texture *dst,
                unsigned dst_level,
                unsigned dst_x, unsigned dst_y, unsigned dst_z,
                struct ilo_texture *src,
                unsigned src_level,
                const struct pipe_box *src_box)
{
   const struct util_format_description *desc =
      util_format_description(dst->bo_format);
   const unsigned max_extent = 32767; /* INT16_MAX */
   const uint8_t rop = 0xcc; /* SRCCOPY */
   struct ilo_context *ilo = blitter->ilo;
   enum gen6_blt_mask mask;
   uint32_t swctrl;
   int cpp, xscale, slice;

   /* no W-tiling support */
   if (dst->separate_s8 || src->separate_s8)
      return false;

   if (dst->bo_stride > max_extent || src->bo_stride > max_extent)
      return false;

   cpp = desc->block.bits / 8;
   xscale = 1;

   /* accommodate for larger cpp */
   if (cpp > 4) {
      if (cpp % 2 == 1)
         return false;

      cpp = (cpp % 4 == 0) ? 4 : 2;
      xscale = (desc->block.bits / 8) / cpp;
   }

   switch (cpp) {
   case 1:
      mask = GEN6_BLT_MASK_8;
      break;
   case 2:
      mask = GEN6_BLT_MASK_16;
      break;
   case 4:
      mask = GEN6_BLT_MASK_32;
      break;
   default:
      return false;
      break;
   }

   swctrl = ilo_blitter_blt_begin(blitter, src_box->depth * 8,
         dst->bo, dst->tiling, src->bo, src->tiling);

   for (slice = 0; slice < src_box->depth; slice++) {
      const struct ilo_texture_slice *dst_slice =
         &dst->slice_offsets[dst_level][dst_z + slice];
      const struct ilo_texture_slice *src_slice =
         &src->slice_offsets[src_level][src_box->z + slice];
      unsigned x1, y1, x2, y2, src_x, src_y;

      x1 = (dst_slice->x + dst_x) * xscale;
      y1 = dst_slice->y + dst_y;
      x2 = (x1 + src_box->width) * xscale;
      y2 = y1 + src_box->height;
      src_x = (src_slice->x + src_box->x) * xscale;
      src_y = src_slice->y + src_box->y;

      /* in blocks */
      x1 /= desc->block.width;
      y1 /= desc->block.height;
      x2 = (x2 + desc->block.width - 1) / desc->block.width;
      y2 = (y2 + desc->block.height - 1) / desc->block.height;
      src_x /= desc->block.width;
      src_y /= desc->block.height;

      if (x2 > max_extent || y2 > max_extent ||
          src_x > max_extent || src_y > max_extent ||
          (x2 - x1) * cpp > gen6_max_bytes_per_scanline)
         break;

      gen6_emit_XY_SRC_COPY_BLT(ilo->dev,
            dst->bo, dst->tiling, dst->bo_stride, 0,
            x1, y1, x2, y2,
            src->bo, src->tiling, src->bo_stride, 0,
            src_x, src_y, rop, mask, mask,
            ilo->cp);
   }

   ilo_blitter_blt_end(blitter, swctrl);

   return (slice == src_box->depth);
}

bool
ilo_blitter_blt_copy_resource(struct ilo_blitter *blitter,
                              struct pipe_resource *dst, unsigned dst_level,
                              unsigned dst_x, unsigned dst_y, unsigned dst_z,
                              struct pipe_resource *src, unsigned src_level,
                              const struct pipe_box *src_box)
{
   bool success;

   if (dst->target == PIPE_BUFFER && src->target == PIPE_BUFFER) {
      const unsigned dst_offset = dst_x;
      const unsigned src_offset = src_box->x;
      const unsigned size = src_box->width;

      assert(dst_level == 0 && dst_y == 0 && dst_z == 0);
      assert(src_level == 0 &&
             src_box->y == 0 &&
             src_box->z == 0 &&
             src_box->height == 1 &&
             src_box->depth == 1);

      success = buf_copy_region(blitter,
            ilo_buffer(dst), dst_offset, ilo_buffer(src), src_offset, size);
   }
   else if (dst->target != PIPE_BUFFER && src->target != PIPE_BUFFER) {
      success = tex_copy_region(blitter,
            ilo_texture(dst), dst_level, dst_x, dst_y, dst_z,
            ilo_texture(src), src_level, src_box);
   }
   else {
      success = false;
   }

   return success;
}

bool
ilo_blitter_blt_clear_rt(struct ilo_blitter *blitter,
                         struct pipe_surface *rt,
                         const union pipe_color_union *color,
                         unsigned x, unsigned y,
                         unsigned width, unsigned height)
{
   const int cpp = util_format_get_blocksize(rt->format);
   enum gen6_blt_mask mask;
   union util_color packed;
   bool success;

   if (!ilo_3d_pass_render_condition(blitter->ilo))
      return true;

   switch (cpp) {
   case 1:
      mask = GEN6_BLT_MASK_8;
      break;
   case 2:
      mask = GEN6_BLT_MASK_16;
      break;
   case 4:
      mask = GEN6_BLT_MASK_32;
      break;
   default:
      return false;
      break;
   }

   if (util_format_is_pure_integer(rt->format) ||
       util_format_is_compressed(rt->format))
      return false;

   util_pack_color(color->f, rt->format, &packed);

   if (rt->texture->target == PIPE_BUFFER) {
      unsigned offset, end, size;

      assert(y == 0 && height == 1);

      offset = (rt->u.buf.first_element + x) * cpp;
      end = (rt->u.buf.last_element + 1) * cpp;

      size = width * cpp;
      if (offset + size > end)
         size = end - offset;

      success = buf_clear_region(blitter, ilo_buffer(rt->texture),
            offset, size, packed.ui, mask, mask);
   }
   else {
      struct pipe_box box;

      u_box_3d(x, y, rt->u.tex.first_layer, width, height,
            rt->u.tex.last_layer - rt->u.tex.first_layer + 1, &box);

      success = tex_clear_region(blitter, ilo_texture(rt->texture),
            rt->u.tex.level, &box, packed.ui, mask, mask);
   }

   return success;
}

bool
ilo_blitter_blt_clear_zs(struct ilo_blitter *blitter,
                         struct pipe_surface *zs,
                         unsigned clear_flags,
                         double depth, unsigned stencil,
                         unsigned x, unsigned y,
                         unsigned width, unsigned height)
{
   enum gen6_blt_mask value_mask, write_mask;
   struct pipe_box box;
   uint32_t val;

   if (!ilo_3d_pass_render_condition(blitter->ilo))
      return true;

   switch (zs->format) {
   case PIPE_FORMAT_Z16_UNORM:
      if (!(clear_flags & PIPE_CLEAR_DEPTH))
         return true;

      value_mask = GEN6_BLT_MASK_16;
      write_mask = GEN6_BLT_MASK_16;
      break;
   case PIPE_FORMAT_Z32_FLOAT:
      if (!(clear_flags & PIPE_CLEAR_DEPTH))
         return true;

      value_mask = GEN6_BLT_MASK_32;
      write_mask = GEN6_BLT_MASK_32;
      break;
   case PIPE_FORMAT_Z24X8_UNORM:
      if (!(clear_flags & PIPE_CLEAR_DEPTH))
         return true;

      value_mask = GEN6_BLT_MASK_32;
      write_mask = GEN6_BLT_MASK_32_LO;
      break;
   case PIPE_FORMAT_Z24_UNORM_S8_UINT:
      if (!(clear_flags & PIPE_CLEAR_DEPTHSTENCIL))
         return true;

      value_mask = GEN6_BLT_MASK_32;

      if ((clear_flags & PIPE_CLEAR_DEPTHSTENCIL) == PIPE_CLEAR_DEPTHSTENCIL)
         write_mask = GEN6_BLT_MASK_32;
      else if (clear_flags & PIPE_CLEAR_DEPTH)
         write_mask = GEN6_BLT_MASK_32_LO;
      else
         write_mask = GEN6_BLT_MASK_32_HI;
      break;
   default:
      return false;
      break;
   }

   val = util_pack_z_stencil(zs->format, depth, stencil);

   u_box_3d(x, y, zs->u.tex.first_layer, width, height,
         zs->u.tex.last_layer - zs->u.tex.first_layer + 1, &box);

   assert(zs->texture->target != PIPE_BUFFER);

   return tex_clear_region(blitter, ilo_texture(zs->texture),
         zs->u.tex.level, &box, val, value_mask, write_mask);
}
