/* * Mesa 3-D graphics library * * Copyright (C) 2012-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 "util/u_surface.h" #include "util/u_transfer.h" #include "util/u_format_etc.h" #include "ilo_blit.h" #include "ilo_blitter.h" #include "ilo_cp.h" #include "ilo_context.h" #include "ilo_resource.h" #include "ilo_state.h" #include "ilo_transfer.h" /* * For buffers that are not busy, we want to map/unmap them directly. For * those that are busy, we have to worry about synchronization. We could wait * for GPU to finish, but there are cases where we could avoid waiting. * * - When PIPE_TRANSFER_DISCARD_WHOLE_RESOURCE is set, the contents of the * buffer can be discarded. We can replace the backing bo by a new one of * the same size (renaming). * - When PIPE_TRANSFER_DISCARD_RANGE is set, the contents of the mapped * range can be discarded. We can allocate and map a staging bo on * mapping, and (pipelined-)copy it over to the real bo on unmapping. * - When PIPE_TRANSFER_FLUSH_EXPLICIT is set, there is no reading and only * flushed regions need to be written. We can still allocate and map a * staging bo, but should copy only the flushed regions over. * * However, there are other flags to consider. * * - When PIPE_TRANSFER_UNSYNCHRONIZED is set, we do not need to worry about * synchronization at all on mapping. * - When PIPE_TRANSFER_MAP_DIRECTLY is set, no staging area is allowed. * - When PIPE_TRANSFER_DONTBLOCK is set, we should fail if we have to block. * - When PIPE_TRANSFER_PERSISTENT is set, GPU may access the buffer while it * is mapped. Synchronization is done by defining memory barriers, * explicitly via memory_barrier() or implicitly via * transfer_flush_region(), as well as GPU fences. * - When PIPE_TRANSFER_COHERENT is set, updates by either CPU or GPU should * be made visible to the other side immediately. Since the kernel flushes * GPU caches at the end of each batch buffer, CPU always sees GPU updates. * We could use a coherent mapping to make all persistent mappings * coherent. * * These also apply to textures, except that we may additionally need to do * format conversion or tiling/untiling. */ /** * Return a transfer method suitable for the usage. The returned method will * correctly block when the resource is busy. */ static bool resource_get_transfer_method(struct pipe_resource *res, unsigned usage, enum ilo_transfer_map_method *method) { const struct ilo_screen *is = ilo_screen(res->screen); enum ilo_transfer_map_method m; bool tiled; if (res->target == PIPE_BUFFER) { tiled = false; } else { struct ilo_texture *tex = ilo_texture(res); bool need_convert = true; /* we may need to convert on the fly */ if (tex->separate_s8 || tex->layout.format == PIPE_FORMAT_S8_UINT) m = ILO_TRANSFER_MAP_SW_ZS; else if (tex->layout.format != tex->base.format) m = ILO_TRANSFER_MAP_SW_CONVERT; else need_convert = false; if (need_convert) { if (usage & (PIPE_TRANSFER_MAP_DIRECTLY | PIPE_TRANSFER_PERSISTENT)) return false; *method = m; return true; } tiled = (tex->layout.tiling != INTEL_TILING_NONE); } if (tiled) m = ILO_TRANSFER_MAP_GTT; /* to have a linear view */ else if (is->dev.has_llc) m = ILO_TRANSFER_MAP_CPU; /* fast and mostly coherent */ else if (usage & PIPE_TRANSFER_PERSISTENT) m = ILO_TRANSFER_MAP_GTT; /* for coherency */ else if (usage & PIPE_TRANSFER_READ) m = ILO_TRANSFER_MAP_CPU; /* gtt read is too slow */ else m = ILO_TRANSFER_MAP_GTT; *method = m; return true; } /** * Rename the bo of the resource. */ static bool resource_rename_bo(struct pipe_resource *res) { return (res->target == PIPE_BUFFER) ? ilo_buffer_rename_bo(ilo_buffer(res)) : ilo_texture_rename_bo(ilo_texture(res)); } /** * Return true if usage allows the use of staging bo to avoid blocking. */ static bool usage_allows_staging_bo(unsigned usage) { /* do we know how to write the data back to the resource? */ const unsigned can_writeback = (PIPE_TRANSFER_DISCARD_WHOLE_RESOURCE | PIPE_TRANSFER_DISCARD_RANGE | PIPE_TRANSFER_FLUSH_EXPLICIT); const unsigned reasons_against = (PIPE_TRANSFER_READ | PIPE_TRANSFER_MAP_DIRECTLY | PIPE_TRANSFER_PERSISTENT); return (usage & can_writeback) && !(usage & reasons_against); } /** * Allocate the staging resource. It is always linear and its size matches * the transfer box, with proper paddings. */ static bool xfer_alloc_staging_res(struct ilo_transfer *xfer) { const struct pipe_resource *res = xfer->base.resource; const struct pipe_box *box = &xfer->base.box; struct pipe_resource templ; memset(&templ, 0, sizeof(templ)); templ.format = res->format; if (res->target == PIPE_BUFFER) { templ.target = PIPE_BUFFER; templ.width0 = (box->x % ILO_TRANSFER_MAP_BUFFER_ALIGNMENT) + box->width; } else { /* use 2D array for any texture target */ templ.target = PIPE_TEXTURE_2D_ARRAY; templ.width0 = box->width; } templ.height0 = box->height; templ.depth0 = 1; templ.array_size = box->depth; templ.nr_samples = 1; templ.usage = PIPE_USAGE_STAGING; templ.bind = PIPE_BIND_TRANSFER_WRITE; if (xfer->base.usage & PIPE_TRANSFER_FLUSH_EXPLICIT) { templ.flags = PIPE_RESOURCE_FLAG_MAP_PERSISTENT | PIPE_RESOURCE_FLAG_MAP_COHERENT; } xfer->staging.res = res->screen->resource_create(res->screen, &templ); if (xfer->staging.res && xfer->staging.res->target != PIPE_BUFFER) { assert(ilo_texture(xfer->staging.res)->layout.tiling == INTEL_TILING_NONE); } return (xfer->staging.res != NULL); } /** * Use an alternative transfer method or rename the resource to unblock an * otherwise blocking transfer. */ static bool xfer_unblock(struct ilo_transfer *xfer, bool *resource_renamed) { struct pipe_resource *res = xfer->base.resource; bool unblocked = false, renamed = false; switch (xfer->method) { case ILO_TRANSFER_MAP_CPU: case ILO_TRANSFER_MAP_GTT: if (xfer->base.usage & PIPE_TRANSFER_UNSYNCHRONIZED) { xfer->method = ILO_TRANSFER_MAP_GTT_UNSYNC; unblocked = true; } else if ((xfer->base.usage & PIPE_TRANSFER_DISCARD_WHOLE_RESOURCE) && resource_rename_bo(res)) { renamed = true; unblocked = true; } else if (usage_allows_staging_bo(xfer->base.usage) && xfer_alloc_staging_res(xfer)) { xfer->method = ILO_TRANSFER_MAP_STAGING; unblocked = true; } break; case ILO_TRANSFER_MAP_GTT_UNSYNC: case ILO_TRANSFER_MAP_STAGING: unblocked = true; break; default: break; } *resource_renamed = renamed; return unblocked; } /** * Allocate the staging system buffer based on the resource format and the * transfer box. */ static bool xfer_alloc_staging_sys(struct ilo_transfer *xfer) { const enum pipe_format format = xfer->base.resource->format; const struct pipe_box *box = &xfer->base.box; const unsigned alignment = 64; /* need to tell the world the layout */ xfer->base.stride = align(util_format_get_stride(format, box->width), alignment); xfer->base.layer_stride = util_format_get_2d_size(format, xfer->base.stride, box->height); xfer->staging.sys = align_malloc(xfer->base.layer_stride * box->depth, alignment); return (xfer->staging.sys != NULL); } /** * Map according to the method. The staging system buffer should have been * allocated if the method requires it. */ static void * xfer_map(struct ilo_transfer *xfer) { void *ptr; switch (xfer->method) { case ILO_TRANSFER_MAP_CPU: ptr = intel_bo_map(ilo_resource_get_bo(xfer->base.resource), xfer->base.usage & PIPE_TRANSFER_WRITE); break; case ILO_TRANSFER_MAP_GTT: ptr = intel_bo_map_gtt(ilo_resource_get_bo(xfer->base.resource)); break; case ILO_TRANSFER_MAP_GTT_UNSYNC: ptr = intel_bo_map_unsynchronized( ilo_resource_get_bo(xfer->base.resource)); break; case ILO_TRANSFER_MAP_STAGING: { const struct ilo_screen *is = ilo_screen(xfer->staging.res->screen); struct intel_bo *bo = ilo_resource_get_bo(xfer->staging.res); /* * We want a writable, optionally persistent and coherent, mapping * for a linear bo. We can call resource_get_transfer_method(), but * this turns out to be fairly simple. */ if (is->dev.has_llc) ptr = intel_bo_map(bo, true); else ptr = intel_bo_map_gtt(bo); if (ptr && xfer->staging.res->target == PIPE_BUFFER) ptr += (xfer->base.box.x % ILO_TRANSFER_MAP_BUFFER_ALIGNMENT); } break; case ILO_TRANSFER_MAP_SW_CONVERT: case ILO_TRANSFER_MAP_SW_ZS: ptr = xfer->staging.sys; break; default: assert(!"unknown mapping method"); ptr = NULL; break; } return ptr; } /** * Unmap a transfer. */ static void xfer_unmap(struct ilo_transfer *xfer) { switch (xfer->method) { case ILO_TRANSFER_MAP_CPU: case ILO_TRANSFER_MAP_GTT: case ILO_TRANSFER_MAP_GTT_UNSYNC: intel_bo_unmap(ilo_resource_get_bo(xfer->base.resource)); break; case ILO_TRANSFER_MAP_STAGING: intel_bo_unmap(ilo_resource_get_bo(xfer->staging.res)); break; default: break; } } static void tex_get_box_origin(const struct ilo_texture *tex, unsigned level, unsigned slice, const struct pipe_box *box, unsigned *mem_x, unsigned *mem_y) { unsigned x, y; ilo_layout_get_slice_pos(&tex->layout, level, box->z + slice, &x, &y); x += box->x; y += box->y; ilo_layout_pos_to_mem(&tex->layout, x, y, mem_x, mem_y); } static unsigned tex_get_box_offset(const struct ilo_texture *tex, unsigned level, const struct pipe_box *box) { unsigned mem_x, mem_y; tex_get_box_origin(tex, level, 0, box, &mem_x, &mem_y); return ilo_layout_mem_to_linear(&tex->layout, mem_x, mem_y); } static unsigned tex_get_slice_stride(const struct ilo_texture *tex, unsigned level) { return ilo_layout_get_slice_stride(&tex->layout, level); } static unsigned tex_tile_x_swizzle(unsigned addr) { /* * From the Ivy Bridge PRM, volume 1 part 2, page 24: * * "As shown in the tiling algorithm, the new address bit[6] should be: * * Address bit[6] <= TiledAddr bit[6] XOR * TiledAddr bit[9] XOR * TiledAddr bit[10]" */ return addr ^ (((addr >> 3) ^ (addr >> 4)) & 0x40); } static unsigned tex_tile_y_swizzle(unsigned addr) { /* * From the Ivy Bridge PRM, volume 1 part 2, page 24: * * "As shown in the tiling algorithm, The new address bit[6] becomes: * * Address bit[6] <= TiledAddr bit[6] XOR * TiledAddr bit[9]" */ return addr ^ ((addr >> 3) & 0x40); } static unsigned tex_tile_x_offset(unsigned mem_x, unsigned mem_y, unsigned tiles_per_row, bool swizzle) { /* * From the Sandy Bridge PRM, volume 1 part 2, page 21, we know that a * X-major tile has 8 rows and 32 OWord columns (512 bytes). Tiles in the * tiled region are numbered in row-major order, starting from zero. The * tile number can thus be calculated as follows: * * tile = (mem_y / 8) * tiles_per_row + (mem_x / 512) * * OWords in that tile are also numbered in row-major order, starting from * zero. The OWord number can thus be calculated as follows: * * oword = (mem_y % 8) * 32 + ((mem_x % 512) / 16) * * and the tiled offset is * * offset = tile * 4096 + oword * 16 + (mem_x % 16) * = tile * 4096 + (mem_y % 8) * 512 + (mem_x % 512) */ unsigned tile, offset; tile = (mem_y >> 3) * tiles_per_row + (mem_x >> 9); offset = tile << 12 | (mem_y & 0x7) << 9 | (mem_x & 0x1ff); return (swizzle) ? tex_tile_x_swizzle(offset) : offset; } static unsigned tex_tile_y_offset(unsigned mem_x, unsigned mem_y, unsigned tiles_per_row, bool swizzle) { /* * From the Sandy Bridge PRM, volume 1 part 2, page 22, we know that a * Y-major tile has 32 rows and 8 OWord columns (128 bytes). Tiles in the * tiled region are numbered in row-major order, starting from zero. The * tile number can thus be calculated as follows: * * tile = (mem_y / 32) * tiles_per_row + (mem_x / 128) * * OWords in that tile are numbered in column-major order, starting from * zero. The OWord number can thus be calculated as follows: * * oword = ((mem_x % 128) / 16) * 32 + (mem_y % 32) * * and the tiled offset is * * offset = tile * 4096 + oword * 16 + (mem_x % 16) */ unsigned tile, oword, offset; tile = (mem_y >> 5) * tiles_per_row + (mem_x >> 7); oword = (mem_x & 0x70) << 1 | (mem_y & 0x1f); offset = tile << 12 | oword << 4 | (mem_x & 0xf); return (swizzle) ? tex_tile_y_swizzle(offset) : offset; } static unsigned tex_tile_w_offset(unsigned mem_x, unsigned mem_y, unsigned tiles_per_row, bool swizzle) { /* * From the Sandy Bridge PRM, volume 1 part 2, page 23, we know that a * W-major tile has 8 8x8-block rows and 8 8x8-block columns. Tiles in the * tiled region are numbered in row-major order, starting from zero. The * tile number can thus be calculated as follows: * * tile = (mem_y / 64) * tiles_per_row + (mem_x / 64) * * 8x8-blocks in that tile are numbered in column-major order, starting * from zero. The 8x8-block number can thus be calculated as follows: * * blk8 = ((mem_x % 64) / 8) * 8 + ((mem_y % 64) / 8) * * Each 8x8-block is divided into 4 4x4-blocks, in row-major order. Each * 4x4-block is further divided into 4 2x2-blocks, also in row-major order. * We have * * blk4 = (((mem_y % 64) / 4) & 1) * 2 + (((mem_x % 64) / 4) & 1) * blk2 = (((mem_y % 64) / 2) & 1) * 2 + (((mem_x % 64) / 2) & 1) * blk1 = (((mem_y % 64) ) & 1) * 2 + (((mem_x % 64) ) & 1) * * and the tiled offset is * * offset = tile * 4096 + blk8 * 64 + blk4 * 16 + blk2 * 4 + blk1 */ unsigned tile, blk8, blk4, blk2, blk1, offset; tile = (mem_y >> 6) * tiles_per_row + (mem_x >> 6); blk8 = ((mem_x >> 3) & 0x7) << 3 | ((mem_y >> 3) & 0x7); blk4 = ((mem_y >> 2) & 0x1) << 1 | ((mem_x >> 2) & 0x1); blk2 = ((mem_y >> 1) & 0x1) << 1 | ((mem_x >> 1) & 0x1); blk1 = ((mem_y ) & 0x1) << 1 | ((mem_x ) & 0x1); offset = tile << 12 | blk8 << 6 | blk4 << 4 | blk2 << 2 | blk1; return (swizzle) ? tex_tile_y_swizzle(offset) : offset; } static unsigned tex_tile_none_offset(unsigned mem_x, unsigned mem_y, unsigned tiles_per_row, bool swizzle) { return mem_y * tiles_per_row + mem_x; } typedef unsigned (*tex_tile_offset_func)(unsigned mem_x, unsigned mem_y, unsigned tiles_per_row, bool swizzle); static tex_tile_offset_func tex_tile_choose_offset_func(const struct ilo_texture *tex, unsigned *tiles_per_row) { switch (tex->layout.tiling) { case INTEL_TILING_X: *tiles_per_row = tex->layout.bo_stride / 512; return tex_tile_x_offset; case INTEL_TILING_Y: *tiles_per_row = tex->layout.bo_stride / 128; return tex_tile_y_offset; case INTEL_TILING_NONE: default: /* W-tiling */ if (tex->layout.format == PIPE_FORMAT_S8_UINT) { *tiles_per_row = tex->layout.bo_stride / 64; return tex_tile_w_offset; } else { *tiles_per_row = tex->layout.bo_stride; return tex_tile_none_offset; } } } static void * tex_staging_sys_map_bo(struct ilo_texture *tex, bool for_read_back, bool linear_view) { const struct ilo_screen *is = ilo_screen(tex->base.screen); const bool prefer_cpu = (is->dev.has_llc || for_read_back); void *ptr; if (prefer_cpu && (tex->layout.tiling == INTEL_TILING_NONE || !linear_view)) ptr = intel_bo_map(tex->bo, !for_read_back); else ptr = intel_bo_map_gtt(tex->bo); return ptr; } static void tex_staging_sys_unmap_bo(struct ilo_texture *tex) { intel_bo_unmap(tex->bo); } static bool tex_staging_sys_zs_read(struct ilo_texture *tex, const struct ilo_transfer *xfer) { const struct ilo_screen *is = ilo_screen(tex->base.screen); const bool swizzle = is->dev.has_address_swizzling; const struct pipe_box *box = &xfer->base.box; const uint8_t *src; tex_tile_offset_func tile_offset; unsigned tiles_per_row; int slice; src = tex_staging_sys_map_bo(tex, true, false); if (!src) return false; tile_offset = tex_tile_choose_offset_func(tex, &tiles_per_row); assert(tex->layout.block_width == 1 && tex->layout.block_height == 1); if (tex->separate_s8) { struct ilo_texture *s8_tex = tex->separate_s8; const uint8_t *s8_src; tex_tile_offset_func s8_tile_offset; unsigned s8_tiles_per_row; int dst_cpp, dst_s8_pos, src_cpp_used; s8_src = tex_staging_sys_map_bo(s8_tex, true, false); if (!s8_src) { tex_staging_sys_unmap_bo(tex); return false; } s8_tile_offset = tex_tile_choose_offset_func(s8_tex, &s8_tiles_per_row); if (tex->base.format == PIPE_FORMAT_Z24_UNORM_S8_UINT) { assert(tex->layout.format == PIPE_FORMAT_Z24X8_UNORM); dst_cpp = 4; dst_s8_pos = 3; src_cpp_used = 3; } else { assert(tex->base.format == PIPE_FORMAT_Z32_FLOAT_S8X24_UINT); assert(tex->layout.format == PIPE_FORMAT_Z32_FLOAT); dst_cpp = 8; dst_s8_pos = 4; src_cpp_used = 4; } for (slice = 0; slice < box->depth; slice++) { unsigned mem_x, mem_y, s8_mem_x, s8_mem_y; uint8_t *dst; int i, j; tex_get_box_origin(tex, xfer->base.level, slice, box, &mem_x, &mem_y); tex_get_box_origin(s8_tex, xfer->base.level, slice, box, &s8_mem_x, &s8_mem_y); dst = xfer->staging.sys + xfer->base.layer_stride * slice; for (i = 0; i < box->height; i++) { unsigned x = mem_x, s8_x = s8_mem_x; uint8_t *d = dst; for (j = 0; j < box->width; j++) { const unsigned offset = tile_offset(x, mem_y, tiles_per_row, swizzle); const unsigned s8_offset = s8_tile_offset(s8_x, s8_mem_y, s8_tiles_per_row, swizzle); memcpy(d, src + offset, src_cpp_used); d[dst_s8_pos] = s8_src[s8_offset]; d += dst_cpp; x += tex->layout.block_size; s8_x++; } dst += xfer->base.stride; mem_y++; s8_mem_y++; } } tex_staging_sys_unmap_bo(s8_tex); } else { assert(tex->layout.format == PIPE_FORMAT_S8_UINT); for (slice = 0; slice < box->depth; slice++) { unsigned mem_x, mem_y; uint8_t *dst; int i, j; tex_get_box_origin(tex, xfer->base.level, slice, box, &mem_x, &mem_y); dst = xfer->staging.sys + xfer->base.layer_stride * slice; for (i = 0; i < box->height; i++) { unsigned x = mem_x; uint8_t *d = dst; for (j = 0; j < box->width; j++) { const unsigned offset = tile_offset(x, mem_y, tiles_per_row, swizzle); *d = src[offset]; d++; x++; } dst += xfer->base.stride; mem_y++; } } } tex_staging_sys_unmap_bo(tex); return true; } static bool tex_staging_sys_zs_write(struct ilo_texture *tex, const struct ilo_transfer *xfer) { const struct ilo_screen *is = ilo_screen(tex->base.screen); const bool swizzle = is->dev.has_address_swizzling; const struct pipe_box *box = &xfer->base.box; uint8_t *dst; tex_tile_offset_func tile_offset; unsigned tiles_per_row; int slice; dst = tex_staging_sys_map_bo(tex, false, false); if (!dst) return false; tile_offset = tex_tile_choose_offset_func(tex, &tiles_per_row); assert(tex->layout.block_width == 1 && tex->layout.block_height == 1); if (tex->separate_s8) { struct ilo_texture *s8_tex = tex->separate_s8; uint8_t *s8_dst; tex_tile_offset_func s8_tile_offset; unsigned s8_tiles_per_row; int src_cpp, src_s8_pos, dst_cpp_used; s8_dst = tex_staging_sys_map_bo(s8_tex, false, false); if (!s8_dst) { tex_staging_sys_unmap_bo(s8_tex); return false; } s8_tile_offset = tex_tile_choose_offset_func(s8_tex, &s8_tiles_per_row); if (tex->base.format == PIPE_FORMAT_Z24_UNORM_S8_UINT) { assert(tex->layout.format == PIPE_FORMAT_Z24X8_UNORM); src_cpp = 4; src_s8_pos = 3; dst_cpp_used = 3; } else { assert(tex->base.format == PIPE_FORMAT_Z32_FLOAT_S8X24_UINT); assert(tex->layout.format == PIPE_FORMAT_Z32_FLOAT); src_cpp = 8; src_s8_pos = 4; dst_cpp_used = 4; } for (slice = 0; slice < box->depth; slice++) { unsigned mem_x, mem_y, s8_mem_x, s8_mem_y; const uint8_t *src; int i, j; tex_get_box_origin(tex, xfer->base.level, slice, box, &mem_x, &mem_y); tex_get_box_origin(s8_tex, xfer->base.level, slice, box, &s8_mem_x, &s8_mem_y); src = xfer->staging.sys + xfer->base.layer_stride * slice; for (i = 0; i < box->height; i++) { unsigned x = mem_x, s8_x = s8_mem_x; const uint8_t *s = src; for (j = 0; j < box->width; j++) { const unsigned offset = tile_offset(x, mem_y, tiles_per_row, swizzle); const unsigned s8_offset = s8_tile_offset(s8_x, s8_mem_y, s8_tiles_per_row, swizzle); memcpy(dst + offset, s, dst_cpp_used); s8_dst[s8_offset] = s[src_s8_pos]; s += src_cpp; x += tex->layout.block_size; s8_x++; } src += xfer->base.stride; mem_y++; s8_mem_y++; } } tex_staging_sys_unmap_bo(s8_tex); } else { assert(tex->layout.format == PIPE_FORMAT_S8_UINT); for (slice = 0; slice < box->depth; slice++) { unsigned mem_x, mem_y; const uint8_t *src; int i, j; tex_get_box_origin(tex, xfer->base.level, slice, box, &mem_x, &mem_y); src = xfer->staging.sys + xfer->base.layer_stride * slice; for (i = 0; i < box->height; i++) { unsigned x = mem_x; const uint8_t *s = src; for (j = 0; j < box->width; j++) { const unsigned offset = tile_offset(x, mem_y, tiles_per_row, swizzle); dst[offset] = *s; s++; x++; } src += xfer->base.stride; mem_y++; } } } tex_staging_sys_unmap_bo(tex); return true; } static bool tex_staging_sys_convert_write(struct ilo_texture *tex, const struct ilo_transfer *xfer) { const struct pipe_box *box = &xfer->base.box; unsigned dst_slice_stride; void *dst; int slice; dst = tex_staging_sys_map_bo(tex, false, true); if (!dst) return false; dst += tex_get_box_offset(tex, xfer->base.level, box); /* slice stride is not always available */ if (box->depth > 1) dst_slice_stride = tex_get_slice_stride(tex, xfer->base.level); else dst_slice_stride = 0; if (unlikely(tex->layout.format == tex->base.format)) { util_copy_box(dst, tex->layout.format, tex->layout.bo_stride, dst_slice_stride, 0, 0, 0, box->width, box->height, box->depth, xfer->staging.sys, xfer->base.stride, xfer->base.layer_stride, 0, 0, 0); tex_staging_sys_unmap_bo(tex); return true; } switch (tex->base.format) { case PIPE_FORMAT_ETC1_RGB8: assert(tex->layout.format == PIPE_FORMAT_R8G8B8X8_UNORM); for (slice = 0; slice < box->depth; slice++) { const void *src = xfer->staging.sys + xfer->base.layer_stride * slice; util_format_etc1_rgb8_unpack_rgba_8unorm(dst, tex->layout.bo_stride, src, xfer->base.stride, box->width, box->height); dst += dst_slice_stride; } break; default: assert(!"unable to convert the staging data"); break; } tex_staging_sys_unmap_bo(tex); return true; } static void tex_staging_sys_writeback(struct ilo_transfer *xfer) { struct ilo_texture *tex = ilo_texture(xfer->base.resource); bool success; if (!(xfer->base.usage & PIPE_TRANSFER_WRITE)) return; switch (xfer->method) { case ILO_TRANSFER_MAP_SW_CONVERT: success = tex_staging_sys_convert_write(tex, xfer); break; case ILO_TRANSFER_MAP_SW_ZS: success = tex_staging_sys_zs_write(tex, xfer); break; default: assert(!"unknown mapping method"); success = false; break; } if (!success) ilo_err("failed to map resource for moving staging data\n"); } static bool tex_staging_sys_readback(struct ilo_transfer *xfer) { struct ilo_texture *tex = ilo_texture(xfer->base.resource); bool read_back = false, success; /* see if we need to read the resource back */ if (xfer->base.usage & PIPE_TRANSFER_READ) { read_back = true; } else if (xfer->base.usage & PIPE_TRANSFER_WRITE) { const unsigned discard_flags = (PIPE_TRANSFER_DISCARD_RANGE | PIPE_TRANSFER_DISCARD_WHOLE_RESOURCE); if (!(xfer->base.usage & discard_flags)) read_back = true; } if (!read_back) return true; switch (xfer->method) { case ILO_TRANSFER_MAP_SW_CONVERT: assert(!"no on-the-fly format conversion for mapping"); success = false; break; case ILO_TRANSFER_MAP_SW_ZS: success = tex_staging_sys_zs_read(tex, xfer); break; default: assert(!"unknown mapping method"); success = false; break; } return success; } static void * tex_map(struct ilo_transfer *xfer) { void *ptr; switch (xfer->method) { case ILO_TRANSFER_MAP_CPU: case ILO_TRANSFER_MAP_GTT: case ILO_TRANSFER_MAP_GTT_UNSYNC: ptr = xfer_map(xfer); if (ptr) { const struct ilo_texture *tex = ilo_texture(xfer->base.resource); ptr += tex_get_box_offset(tex, xfer->base.level, &xfer->base.box); /* stride is for a block row, not a texel row */ xfer->base.stride = tex->layout.bo_stride; /* note that slice stride is not always available */ xfer->base.layer_stride = (xfer->base.box.depth > 1) ? tex_get_slice_stride(tex, xfer->base.level) : 0; } break; case ILO_TRANSFER_MAP_STAGING: ptr = xfer_map(xfer); if (ptr) { const struct ilo_texture *staging = ilo_texture(xfer->staging.res); xfer->base.stride = staging->layout.bo_stride; xfer->base.layer_stride = tex_get_slice_stride(staging, 0); } break; case ILO_TRANSFER_MAP_SW_CONVERT: case ILO_TRANSFER_MAP_SW_ZS: if (xfer_alloc_staging_sys(xfer) && tex_staging_sys_readback(xfer)) ptr = xfer_map(xfer); else ptr = NULL; break; default: assert(!"unknown mapping method"); ptr = NULL; break; } return ptr; } static void * buf_map(struct ilo_transfer *xfer) { void *ptr; ptr = xfer_map(xfer); if (!ptr) return NULL; if (xfer->method != ILO_TRANSFER_MAP_STAGING) ptr += xfer->base.box.x; xfer->base.stride = 0; xfer->base.layer_stride = 0; assert(xfer->base.level == 0); assert(xfer->base.box.y == 0); assert(xfer->base.box.z == 0); assert(xfer->base.box.height == 1); assert(xfer->base.box.depth == 1); return ptr; } static void copy_staging_resource(struct ilo_context *ilo, struct ilo_transfer *xfer, const struct pipe_box *box) { const unsigned pad_x = (xfer->staging.res->target == PIPE_BUFFER) ? xfer->base.box.x % ILO_TRANSFER_MAP_BUFFER_ALIGNMENT : 0; struct pipe_box modified_box; assert(xfer->method == ILO_TRANSFER_MAP_STAGING && xfer->staging.res); if (!box) { u_box_3d(pad_x, 0, 0, xfer->base.box.width, xfer->base.box.height, xfer->base.box.depth, &modified_box); box = &modified_box; } else if (pad_x) { modified_box = *box; modified_box.x += pad_x; box = &modified_box; } ilo_blitter_blt_copy_resource(ilo->blitter, xfer->base.resource, xfer->base.level, xfer->base.box.x, xfer->base.box.y, xfer->base.box.z, xfer->staging.res, 0, box); } static bool is_bo_busy(struct ilo_context *ilo, struct intel_bo *bo, bool *need_flush) { const bool referenced = intel_bo_has_reloc(ilo->cp->bo, bo); if (need_flush) *need_flush = referenced; if (referenced) return true; return intel_bo_is_busy(bo); } /** * Choose the best mapping method, depending on the transfer usage and whether * the bo is busy. */ static bool choose_transfer_method(struct ilo_context *ilo, struct ilo_transfer *xfer) { struct pipe_resource *res = xfer->base.resource; bool need_flush; if (!resource_get_transfer_method(res, xfer->base.usage, &xfer->method)) return false; /* see if we can avoid blocking */ if (is_bo_busy(ilo, ilo_resource_get_bo(res), &need_flush)) { bool resource_renamed; if (!xfer_unblock(xfer, &resource_renamed)) { if (xfer->base.usage & PIPE_TRANSFER_DONTBLOCK) return false; /* flush to make bo really busy so that map() correctly blocks */ if (need_flush) ilo_cp_flush(ilo->cp, "syncing for transfers"); } if (resource_renamed) ilo_mark_states_with_resource_renamed(ilo, res); } return true; } static void buf_pwrite(struct ilo_context *ilo, struct ilo_buffer *buf, unsigned usage, int offset, int size, const void *data) { bool need_flush; /* see if we can avoid blocking */ if (is_bo_busy(ilo, buf->bo, &need_flush)) { bool unblocked = false; if ((usage & PIPE_TRANSFER_DISCARD_WHOLE_RESOURCE) && ilo_buffer_rename_bo(buf)) { ilo_mark_states_with_resource_renamed(ilo, &buf->base); unblocked = true; } else { struct pipe_resource templ, *staging; /* * allocate a staging buffer to hold the data and pipelined copy it * over */ templ = buf->base; templ.width0 = size; templ.usage = PIPE_USAGE_STAGING; templ.bind = PIPE_BIND_TRANSFER_WRITE; staging = ilo->base.screen->resource_create(ilo->base.screen, &templ); if (staging) { struct pipe_box staging_box; intel_bo_pwrite(ilo_buffer(staging)->bo, 0, size, data); u_box_1d(0, size, &staging_box); ilo_blitter_blt_copy_resource(ilo->blitter, &buf->base, 0, offset, 0, 0, staging, 0, &staging_box); pipe_resource_reference(&staging, NULL); return; } } /* flush to make bo really busy so that pwrite() correctly blocks */ if (!unblocked && need_flush) ilo_cp_flush(ilo->cp, "syncing for pwrites"); } intel_bo_pwrite(buf->bo, offset, size, data); } static void ilo_transfer_flush_region(struct pipe_context *pipe, struct pipe_transfer *transfer, const struct pipe_box *box) { struct ilo_context *ilo = ilo_context(pipe); struct ilo_transfer *xfer = ilo_transfer(transfer); /* * The staging resource is mapped persistently and coherently. We can copy * without unmapping. */ if (xfer->method == ILO_TRANSFER_MAP_STAGING && (xfer->base.usage & PIPE_TRANSFER_FLUSH_EXPLICIT)) copy_staging_resource(ilo, xfer, box); } static void ilo_transfer_unmap(struct pipe_context *pipe, struct pipe_transfer *transfer) { struct ilo_context *ilo = ilo_context(pipe); struct ilo_transfer *xfer = ilo_transfer(transfer); xfer_unmap(xfer); switch (xfer->method) { case ILO_TRANSFER_MAP_STAGING: if (!(xfer->base.usage & PIPE_TRANSFER_FLUSH_EXPLICIT)) copy_staging_resource(ilo, xfer, NULL); pipe_resource_reference(&xfer->staging.res, NULL); break; case ILO_TRANSFER_MAP_SW_CONVERT: case ILO_TRANSFER_MAP_SW_ZS: tex_staging_sys_writeback(xfer); align_free(xfer->staging.sys); break; default: break; } pipe_resource_reference(&xfer->base.resource, NULL); util_slab_free(&ilo->transfer_mempool, xfer); } static void * ilo_transfer_map(struct pipe_context *pipe, struct pipe_resource *res, unsigned level, unsigned usage, const struct pipe_box *box, struct pipe_transfer **transfer) { struct ilo_context *ilo = ilo_context(pipe); struct ilo_transfer *xfer; void *ptr; /* note that xfer is not zero'd */ xfer = util_slab_alloc(&ilo->transfer_mempool); if (!xfer) { *transfer = NULL; return NULL; } xfer->base.resource = NULL; pipe_resource_reference(&xfer->base.resource, res); xfer->base.level = level; xfer->base.usage = usage; xfer->base.box = *box; ilo_blit_resolve_transfer(ilo, &xfer->base); if (choose_transfer_method(ilo, xfer)) { if (res->target == PIPE_BUFFER) ptr = buf_map(xfer); else ptr = tex_map(xfer); } else { ptr = NULL; } if (!ptr) { pipe_resource_reference(&xfer->base.resource, NULL); util_slab_free(&ilo->transfer_mempool, xfer); *transfer = NULL; return NULL; } *transfer = &xfer->base; return ptr; } static void ilo_transfer_inline_write(struct pipe_context *pipe, struct pipe_resource *res, unsigned level, unsigned usage, const struct pipe_box *box, const void *data, unsigned stride, unsigned layer_stride) { if (likely(res->target == PIPE_BUFFER) && !(usage & PIPE_TRANSFER_UNSYNCHRONIZED)) { /* they should specify just an offset and a size */ assert(level == 0); assert(box->y == 0); assert(box->z == 0); assert(box->height == 1); assert(box->depth == 1); buf_pwrite(ilo_context(pipe), ilo_buffer(res), usage, box->x, box->width, data); } else { u_default_transfer_inline_write(pipe, res, level, usage, box, data, stride, layer_stride); } } /** * Initialize transfer-related functions. */ void ilo_init_transfer_functions(struct ilo_context *ilo) { ilo->base.transfer_map = ilo_transfer_map; ilo->base.transfer_flush_region = ilo_transfer_flush_region; ilo->base.transfer_unmap = ilo_transfer_unmap; ilo->base.transfer_inline_write = ilo_transfer_inline_write; }