diff options
-rw-r--r-- | src/gallium/auxiliary/util/u_surface.c | 115 |
1 files changed, 95 insertions, 20 deletions
diff --git a/src/gallium/auxiliary/util/u_surface.c b/src/gallium/auxiliary/util/u_surface.c index 4ef81c041c4..e48e47c2ec2 100644 --- a/src/gallium/auxiliary/util/u_surface.c +++ b/src/gallium/auxiliary/util/u_surface.c @@ -238,8 +238,21 @@ util_fill_box(ubyte * dst, } +/** Mipmap level size computation, with minimum block size */ +static inline unsigned +minify(unsigned value, unsigned levels, unsigned blocksize) +{ + return MAX2(blocksize, value >> levels); +} + + /** * Fallback function for pipe->resource_copy_region(). + * We support copying between different formats (including compressed/ + * uncompressed) if the bytes per block or pixel matches. If copying + * compressed -> uncompressed, the dst region is reduced by the block + * width, height. If copying uncompressed -> compressed, the dest region + * is expanded by the block width, height. See GL_ARB_copy_image. * Note: (X,Y)=(0,0) is always the upper-left corner. */ void @@ -249,14 +262,15 @@ util_resource_copy_region(struct pipe_context *pipe, unsigned dst_x, unsigned dst_y, unsigned dst_z, struct pipe_resource *src, unsigned src_level, - const struct pipe_box *src_box) + const struct pipe_box *src_box_in) { struct pipe_transfer *src_trans, *dst_trans; uint8_t *dst_map; const uint8_t *src_map; MAYBE_UNUSED enum pipe_format src_format; enum pipe_format dst_format; - struct pipe_box dst_box; + struct pipe_box src_box, dst_box; + unsigned src_bs, dst_bs, src_bw, dst_bw, src_bh, dst_bh; assert(src && dst); if (!src || !dst) @@ -268,47 +282,108 @@ util_resource_copy_region(struct pipe_context *pipe, src_format = src->format; dst_format = dst->format; - assert(util_format_get_blocksize(dst_format) == util_format_get_blocksize(src_format)); - assert(util_format_get_blockwidth(dst_format) == util_format_get_blockwidth(src_format)); - assert(util_format_get_blockheight(dst_format) == util_format_get_blockheight(src_format)); + /* init src box */ + src_box = *src_box_in; + + /* init dst box */ + dst_box.x = dst_x; + dst_box.y = dst_y; + dst_box.z = dst_z; + dst_box.width = src_box.width; + dst_box.height = src_box.height; + dst_box.depth = src_box.depth; + + src_bs = util_format_get_blocksize(src_format); + src_bw = util_format_get_blockwidth(src_format); + src_bh = util_format_get_blockheight(src_format); + dst_bs = util_format_get_blocksize(dst_format); + dst_bw = util_format_get_blockwidth(dst_format); + dst_bh = util_format_get_blockheight(dst_format); + + /* Note: all box positions and sizes are in pixels */ + if (src_bw > 1 && dst_bw == 1) { + /* Copy from compressed to uncompressed. + * Shrink dest box by the src block size. + */ + dst_box.width /= src_bw; + dst_box.height /= src_bh; + } + else if (src_bw == 1 && dst_bw > 1) { + /* Copy from uncompressed to compressed. + * Expand dest box by the dest block size. + */ + dst_box.width *= dst_bw; + dst_box.height *= dst_bh; + } + else { + /* compressed -> compressed or uncompressed -> uncompressed copy */ + assert(src_bw == dst_bw); + assert(src_bh == dst_bh); + } + + assert(src_bs == dst_bs); + if (src_bs != dst_bs) { + /* This can happen if we fail to do format checking before hand. + * Don't crash below. + */ + return; + } + + /* check that region boxes are block aligned */ + assert(src_box.x % src_bw == 0); + assert(src_box.y % src_bh == 0); + assert(src_box.width % src_bw == 0 || src_box.width < src_bw); + assert(src_box.height % src_bh == 0 || src_box.height < src_bh); + assert(dst_box.x % dst_bw == 0); + assert(dst_box.y % dst_bh == 0); + assert(dst_box.width % dst_bw == 0 || dst_box.width < dst_bw); + assert(dst_box.height % dst_bh == 0 || dst_box.height < src_bh); + + /* check that region boxes are not out of bounds */ + assert(src_box.x + src_box.width <= + minify(src->width0, src_level, src_bw)); + assert(src_box.y + src_box.height <= + minify(src->height0, src_level, src_bh)); + assert(dst_box.x + dst_box.width <= + minify(dst->width0, dst_level, dst_bw)); + assert(dst_box.y + dst_box.height <= + minify(dst->height0, dst_level, dst_bh)); + + /* check that total number of src, dest bytes match */ + assert((src_box.width / src_bw) * (src_box.height / src_bh) * src_bs == + (dst_box.width / dst_bw) * (dst_box.height / dst_bh) * dst_bs); src_map = pipe->transfer_map(pipe, src, src_level, PIPE_TRANSFER_READ, - src_box, &src_trans); + &src_box, &src_trans); assert(src_map); if (!src_map) { goto no_src_map; } - dst_box.x = dst_x; - dst_box.y = dst_y; - dst_box.z = dst_z; - dst_box.width = src_box->width; - dst_box.height = src_box->height; - dst_box.depth = src_box->depth; - dst_map = pipe->transfer_map(pipe, dst, dst_level, - PIPE_TRANSFER_WRITE | PIPE_TRANSFER_DISCARD_RANGE, - &dst_box, &dst_trans); + PIPE_TRANSFER_WRITE | + PIPE_TRANSFER_DISCARD_RANGE, &dst_box, + &dst_trans); assert(dst_map); if (!dst_map) { goto no_dst_map; } if (dst->target == PIPE_BUFFER && src->target == PIPE_BUFFER) { - assert(src_box->height == 1); - assert(src_box->depth == 1); - memcpy(dst_map, src_map, src_box->width); + assert(src_box.height == 1); + assert(src_box.depth == 1); + memcpy(dst_map, src_map, src_box.width); } else { util_copy_box(dst_map, - dst_format, + src_format, dst_trans->stride, dst_trans->layer_stride, 0, 0, 0, - src_box->width, src_box->height, src_box->depth, + src_box.width, src_box.height, src_box.depth, src_map, src_trans->stride, src_trans->layer_stride, 0, 0, 0); |