/* * 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 */ #include "genhw/genhw.h" #include "core/ilo_builder_mi.h" #include "core/ilo_builder_blt.h" #include "util/u_pack_color.h" #include "ilo_context.h" #include "ilo_cp.h" #include "ilo_blit.h" #include "ilo_resource.h" #include "ilo_blitter.h" static uint32_t ilo_blitter_blt_begin(struct ilo_blitter *blitter, int max_cmd_size, struct intel_bo *dst, enum gen_surface_tiling dst_tiling, struct intel_bo *src, enum gen_surface_tiling src_tiling) { struct ilo_cp *cp = blitter->ilo->cp; struct intel_bo *aper_check[2]; int count; uint32_t swctrl; /* change owner */ ilo_cp_set_owner(cp, INTEL_RING_BLT, NULL); /* check aperture space */ aper_check[0] = dst; count = 1; if (src) { aper_check[1] = src; count++; } if (!ilo_builder_validate(&cp->builder, count, aper_check)) ilo_cp_submit(cp, "out of aperture"); /* set BCS_SWCTRL */ swctrl = 0x0; assert(dst_tiling == GEN6_TILING_NONE || dst_tiling == GEN6_TILING_X || dst_tiling == GEN6_TILING_Y); assert(src_tiling == GEN6_TILING_NONE || src_tiling == GEN6_TILING_X || src_tiling == GEN6_TILING_Y); if (dst_tiling == GEN6_TILING_Y) { swctrl |= GEN6_REG_BCS_SWCTRL_DST_TILING_Y << 16 | GEN6_REG_BCS_SWCTRL_DST_TILING_Y; } if (src && src_tiling == GEN6_TILING_Y) { swctrl |= GEN6_REG_BCS_SWCTRL_SRC_TILING_Y << 16 | GEN6_REG_BCS_SWCTRL_SRC_TILING_Y; } /* * 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 (swctrl) max_cmd_size += (4 + 3) * 2; if (ilo_cp_space(cp) < max_cmd_size) { ilo_cp_submit(cp, "out of space"); assert(ilo_cp_space(cp) >= max_cmd_size); } if (swctrl) { /* * 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_MI_FLUSH_DW(&cp->builder); gen6_MI_LOAD_REGISTER_IMM(&cp->builder, GEN6_REG_BCS_SWCTRL, swctrl); swctrl &= ~(GEN6_REG_BCS_SWCTRL_DST_TILING_Y | GEN6_REG_BCS_SWCTRL_SRC_TILING_Y); } return swctrl; } static void ilo_blitter_blt_end(struct ilo_blitter *blitter, uint32_t swctrl) { struct ilo_builder *builder = &blitter->ilo->cp->builder; /* set BCS_SWCTRL back */ if (swctrl) { gen6_MI_FLUSH_DW(builder); gen6_MI_LOAD_REGISTER_IMM(builder, GEN6_REG_BCS_SWCTRL, swctrl); } } static bool buf_clear_region(struct ilo_blitter *blitter, struct ilo_buffer_resource *buf, unsigned offset, uint32_t val, unsigned size, enum gen6_blt_mask value_mask, enum gen6_blt_mask write_mask) { const uint8_t rop = 0xf0; /* PATCOPY */ const int cpp = gen6_blt_translate_value_cpp(value_mask); struct ilo_builder *builder = &blitter->ilo->cp->builder; struct gen6_blt_bo dst; if (offset % cpp || size % cpp) return false; dst.bo = buf->vma.bo; dst.offset = buf->vma.bo_offset + offset; ilo_blitter_blt_begin(blitter, GEN6_COLOR_BLT__SIZE * (1 + size / 32764 / gen6_blt_max_scanlines), dst.bo, GEN6_TILING_NONE, NULL, GEN6_TILING_NONE); while (size) { unsigned width, height; width = size; height = 1; if (width > gen6_blt_max_bytes_per_scanline) { /* less than INT16_MAX and dword-aligned */ width = 32764; height = size / width; if (height > gen6_blt_max_scanlines) height = gen6_blt_max_scanlines; dst.pitch = width; } else { dst.pitch = 0; } gen6_COLOR_BLT(builder, &dst, val, width, height, rop, value_mask, write_mask); dst.offset += dst.pitch * height; size -= width * height; } ilo_blitter_blt_end(blitter, 0); return true; } static bool buf_copy_region(struct ilo_blitter *blitter, struct ilo_buffer_resource *dst_buf, unsigned dst_offset, struct ilo_buffer_resource *src_buf, unsigned src_offset, unsigned size) { const uint8_t rop = 0xcc; /* SRCCOPY */ struct ilo_builder *builder = &blitter->ilo->cp->builder; struct gen6_blt_bo dst, src; dst.bo = dst_buf->vma.bo; dst.offset = dst_buf->vma.bo_offset + dst_offset; dst.pitch = 0; src.bo = src_buf->vma.bo; src.offset = src_buf->vma.bo_offset + src_offset; src.pitch = 0; ilo_blitter_blt_begin(blitter, GEN6_SRC_COPY_BLT__SIZE * (1 + size / 32764 / gen6_blt_max_scanlines), dst_buf->vma.bo, GEN6_TILING_NONE, src_buf->vma.bo, GEN6_TILING_NONE); while (size) { unsigned width, height; width = size; height = 1; if (width > gen6_blt_max_bytes_per_scanline) { /* less than INT16_MAX and dword-aligned */ width = 32764; height = size / width; if (height > gen6_blt_max_scanlines) height = gen6_blt_max_scanlines; dst.pitch = width; src.pitch = width; } else { dst.pitch = 0; src.pitch = 0; } gen6_SRC_COPY_BLT(builder, &dst, &src, width, height, rop, GEN6_BLT_MASK_8, GEN6_BLT_MASK_8); dst.offset += dst.pitch * height; src.offset += src.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_tex, 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_blt_translate_value_cpp(value_mask); const unsigned max_extent = 32767; /* INT16_MAX */ const uint8_t rop = 0xf0; /* PATCOPY */ struct ilo_builder *builder = &blitter->ilo->cp->builder; struct gen6_blt_xy_bo dst; uint32_t swctrl; int slice; /* no W-tiling nor separate stencil support */ if (dst_tex->image.tiling == GEN8_TILING_W || dst_tex->separate_s8) return false; if (dst_tex->image.bo_stride > max_extent) return false; if (dst_box->width * cpp > gen6_blt_max_bytes_per_scanline) return false; dst.bo = dst_tex->vma.bo; dst.offset = dst_tex->vma.bo_offset; dst.pitch = dst_tex->image.bo_stride; dst.tiling = dst_tex->image.tiling; swctrl = ilo_blitter_blt_begin(blitter, GEN6_XY_COLOR_BLT__SIZE * dst_box->depth, dst_tex->vma.bo, dst_tex->image.tiling, NULL, GEN6_TILING_NONE); for (slice = 0; slice < dst_box->depth; slice++) { unsigned x, y; ilo_image_get_slice_pos(&dst_tex->image, dst_level, dst_box->z + slice, &x, &y); dst.x = x + dst_box->x; dst.y = y + dst_box->y; if (dst.x + dst_box->width > max_extent || dst.y + dst_box->height > max_extent) break; gen6_XY_COLOR_BLT(builder, &dst, val, dst_box->width, dst_box->height, rop, value_mask, write_mask); } ilo_blitter_blt_end(blitter, swctrl); return (slice == dst_box->depth); } static bool tex_copy_region(struct ilo_blitter *blitter, struct ilo_texture *dst_tex, unsigned dst_level, unsigned dst_x, unsigned dst_y, unsigned dst_z, struct ilo_texture *src_tex, unsigned src_level, const struct pipe_box *src_box) { const struct util_format_description *desc = util_format_description(dst_tex->image_format); const unsigned max_extent = 32767; /* INT16_MAX */ const uint8_t rop = 0xcc; /* SRCCOPY */ struct ilo_builder *builder = &blitter->ilo->cp->builder; enum gen6_blt_mask mask; struct gen6_blt_xy_bo dst, src; uint32_t swctrl; int cpp, xscale, slice; /* no W-tiling nor separate stencil support */ if (dst_tex->image.tiling == GEN8_TILING_W || dst_tex->separate_s8 || src_tex->image.tiling == GEN8_TILING_W || src_tex->separate_s8) return false; if (dst_tex->image.bo_stride > max_extent || src_tex->image.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; } if (src_box->width * cpp * xscale > gen6_blt_max_bytes_per_scanline) return false; 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; } dst.bo = dst_tex->vma.bo; dst.offset = dst_tex->vma.bo_offset; dst.pitch = dst_tex->image.bo_stride; dst.tiling = dst_tex->image.tiling; src.bo = src_tex->vma.bo; src.offset = src_tex->vma.bo_offset; src.pitch = src_tex->image.bo_stride; src.tiling = src_tex->image.tiling; swctrl = ilo_blitter_blt_begin(blitter, GEN6_XY_SRC_COPY_BLT__SIZE * src_box->depth, dst.bo, dst.tiling, src.bo, src.tiling); for (slice = 0; slice < src_box->depth; slice++) { unsigned dx, dy, sx, sy, width, height; ilo_image_get_slice_pos(&dst_tex->image, dst_level, dst_z + slice, &dx, &dy); ilo_image_get_slice_pos(&src_tex->image, src_level, src_box->z + slice, &sx, &sy); dst.x = (dx + dst_x) * xscale; dst.y = dy + dst_y; src.x = (sx + src_box->x) * xscale; src.y = sy + src_box->y; width = src_box->width * xscale; height = src_box->height; /* in blocks */ dst.x /= desc->block.width; dst.y /= desc->block.height; src.x /= desc->block.width; src.y /= desc->block.height; width /= desc->block.width; height /= desc->block.height; if (src.x + width > max_extent || src.y + height > max_extent || dst.x + width > max_extent || dst.y + height > max_extent) break; gen6_XY_SRC_COPY_BLT(builder, &dst, &src, width, height, rop, mask, mask); } 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; ilo_blit_resolve_slices(blitter->ilo, src, src_level, src_box->z, src_box->depth, ILO_TEXTURE_BLT_READ); ilo_blit_resolve_slices(blitter->ilo, dst, dst_level, dst_z, src_box->depth, ILO_TEXTURE_BLT_WRITE); 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_resource(dst), dst_offset, ilo_buffer_resource(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_skip_rendering(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; ilo_blit_resolve_surface(blitter->ilo, rt, ILO_TEXTURE_BLT_WRITE); 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_resource(rt->texture), offset, packed.ui[0], size, 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[0], 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_skip_rendering(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; } ilo_blit_resolve_surface(blitter->ilo, zs, ILO_TEXTURE_BLT_WRITE); 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); }