diff options
author | Marek Olšák <[email protected]> | 2015-08-24 02:55:20 +0200 |
---|---|---|
committer | Marek Olšák <[email protected]> | 2015-10-28 11:52:17 +0100 |
commit | f04f13622f3e71bee057d60a6be9c53b92b56cc9 (patch) | |
tree | 8543fdb40ed4dfce18fbe4e2e3887633c7361391 /src/mesa/state_tracker/st_cb_copyimage.c | |
parent | ce9db16e1c101204cdb6b2482aa1b4c7d0c59d41 (diff) |
st/mesa: implement ARB_copy_image
I wonder if the craziness was worth it.
Reviewed-by: Brian Paul <[email protected]>
Diffstat (limited to 'src/mesa/state_tracker/st_cb_copyimage.c')
-rw-r--r-- | src/mesa/state_tracker/st_cb_copyimage.c | 578 |
1 files changed, 578 insertions, 0 deletions
diff --git a/src/mesa/state_tracker/st_cb_copyimage.c b/src/mesa/state_tracker/st_cb_copyimage.c new file mode 100644 index 00000000000..c94a0b22ea8 --- /dev/null +++ b/src/mesa/state_tracker/st_cb_copyimage.c @@ -0,0 +1,578 @@ +/* + * Copyright 2015 Advanced Micro Devices, 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 + * on the rights to use, copy, modify, merge, publish, distribute, sub + * license, 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 (including the next + * paragraph) 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 NON-INFRINGEMENT. IN NO EVENT SHALL + * THE AUTHOR(S) AND/OR THEIR SUPPLIERS 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. + * + */ + +#include "state_tracker/st_context.h" +#include "state_tracker/st_cb_copyimage.h" +#include "state_tracker/st_cb_fbo.h" +#include "state_tracker/st_texture.h" + +#include "util/u_box.h" +#include "util/u_format.h" +#include "util/u_inlines.h" + + +/** + * Return an equivalent canonical format without "X" channels. + * + * Copying between incompatible formats is easier when the format is + * canonicalized, meaning that it is in a standard form. + * + * The returned format has the same component sizes and swizzles as + * the source format, the type is changed to UINT or UNORM, depending on + * which one has the most swizzle combinations in their group. + * + * If it's not an array format, return a memcpy-equivalent array format. + * + * The key feature is that swizzled versions of formats of the same + * component size always return the same component type. + * + * X returns A. + * Luminance, intensity, alpha, depth, stencil, and 8-bit and 16-bit packed + * formats are not supported. (same as ARB_copy_image) + */ +static enum pipe_format +get_canonical_format(enum pipe_format format) +{ + const struct util_format_description *desc = + util_format_description(format); + + /* Packed formats. Return the equivalent array format. */ + if (format == PIPE_FORMAT_R11G11B10_FLOAT || + format == PIPE_FORMAT_R9G9B9E5_FLOAT) + return get_canonical_format(PIPE_FORMAT_R8G8B8A8_UINT); + + if (desc->nr_channels == 4 && + desc->channel[0].size == 10 && + desc->channel[1].size == 10 && + desc->channel[2].size == 10 && + desc->channel[3].size == 2) { + if (desc->swizzle[0] == UTIL_FORMAT_SWIZZLE_X && + desc->swizzle[1] == UTIL_FORMAT_SWIZZLE_Y && + desc->swizzle[2] == UTIL_FORMAT_SWIZZLE_Z) + return get_canonical_format(PIPE_FORMAT_R8G8B8A8_UINT); + + return PIPE_FORMAT_NONE; + } + +#define RETURN_FOR_SWIZZLE1(x, format) \ + if (desc->swizzle[0] == UTIL_FORMAT_SWIZZLE_##x) \ + return format + +#define RETURN_FOR_SWIZZLE2(x, y, format) \ + if (desc->swizzle[0] == UTIL_FORMAT_SWIZZLE_##x && \ + desc->swizzle[1] == UTIL_FORMAT_SWIZZLE_##y) \ + return format + +#define RETURN_FOR_SWIZZLE3(x, y, z, format) \ + if (desc->swizzle[0] == UTIL_FORMAT_SWIZZLE_##x && \ + desc->swizzle[1] == UTIL_FORMAT_SWIZZLE_##y && \ + desc->swizzle[2] == UTIL_FORMAT_SWIZZLE_##z) \ + return format + +#define RETURN_FOR_SWIZZLE4(x, y, z, w, format) \ + if (desc->swizzle[0] == UTIL_FORMAT_SWIZZLE_##x && \ + desc->swizzle[1] == UTIL_FORMAT_SWIZZLE_##y && \ + desc->swizzle[2] == UTIL_FORMAT_SWIZZLE_##z && \ + desc->swizzle[3] == UTIL_FORMAT_SWIZZLE_##w) \ + return format + + /* Array formats. */ + if (desc->is_array) { + switch (desc->nr_channels) { + case 1: + switch (desc->channel[0].size) { + case 8: + RETURN_FOR_SWIZZLE1(X, PIPE_FORMAT_R8_UINT); + break; + + case 16: + RETURN_FOR_SWIZZLE1(X, PIPE_FORMAT_R16_UINT); + break; + + case 32: + RETURN_FOR_SWIZZLE1(X, PIPE_FORMAT_R32_UINT); + break; + } + break; + + case 2: + switch (desc->channel[0].size) { + case 8: + /* All formats in each group must be of the same type. + * We can't use UINT for R8G8 while using UNORM for G8R8. + */ + RETURN_FOR_SWIZZLE2(X, Y, PIPE_FORMAT_R8G8_UNORM); + RETURN_FOR_SWIZZLE2(Y, X, PIPE_FORMAT_G8R8_UNORM); + break; + + case 16: + RETURN_FOR_SWIZZLE2(X, Y, PIPE_FORMAT_R16G16_UNORM); + RETURN_FOR_SWIZZLE2(Y, X, PIPE_FORMAT_G16R16_UNORM); + break; + + case 32: + RETURN_FOR_SWIZZLE2(X, Y, PIPE_FORMAT_R32G32_UINT); + break; + } + break; + + case 3: + switch (desc->channel[0].size) { + case 8: + RETURN_FOR_SWIZZLE3(X, Y, Z, PIPE_FORMAT_R8G8B8_UINT); + break; + + case 16: + RETURN_FOR_SWIZZLE3(X, Y, Z, PIPE_FORMAT_R16G16B16_UINT); + break; + + case 32: + RETURN_FOR_SWIZZLE3(X, Y, Z, PIPE_FORMAT_R32G32B32_UINT); + break; + } + break; + + case 4: + switch (desc->channel[0].size) { + case 8: + RETURN_FOR_SWIZZLE4(X, Y, Z, W, PIPE_FORMAT_R8G8B8A8_UNORM); + RETURN_FOR_SWIZZLE4(X, Y, Z, 1, PIPE_FORMAT_R8G8B8A8_UNORM); + RETURN_FOR_SWIZZLE4(Z, Y, X, W, PIPE_FORMAT_B8G8R8A8_UNORM); + RETURN_FOR_SWIZZLE4(Z, Y, X, 1, PIPE_FORMAT_B8G8R8A8_UNORM); + RETURN_FOR_SWIZZLE4(W, Z, Y, X, PIPE_FORMAT_A8B8G8R8_UNORM); + RETURN_FOR_SWIZZLE4(1, Z, Y, X, PIPE_FORMAT_A8B8G8R8_UNORM); + RETURN_FOR_SWIZZLE4(W, X, Y, Z, PIPE_FORMAT_A8R8G8B8_UNORM); + RETURN_FOR_SWIZZLE4(1, X, Y, Z, PIPE_FORMAT_A8R8G8B8_UNORM); + break; + + case 16: + RETURN_FOR_SWIZZLE4(X, Y, Z, W, PIPE_FORMAT_R16G16B16A16_UINT); + RETURN_FOR_SWIZZLE4(X, Y, Z, 1, PIPE_FORMAT_R16G16B16A16_UINT); + break; + + case 32: + RETURN_FOR_SWIZZLE4(X, Y, Z, W, PIPE_FORMAT_R32G32B32A32_UINT); + RETURN_FOR_SWIZZLE4(X, Y, Z, 1, PIPE_FORMAT_R32G32B32A32_UINT); + break; + } + } + + assert(!"unknown array format"); + return PIPE_FORMAT_NONE; + } + + assert(!"unknown packed format"); + return PIPE_FORMAT_NONE; +} + +/** + * Return true if the swizzle is XYZW in case of a 4-channel format, + * XY in case of a 2-channel format, or X in case of a 1-channel format. + */ +static bool +has_identity_swizzle(const struct util_format_description *desc) +{ + int i; + + for (i = 0; i < desc->nr_channels; i++) + if (desc->swizzle[i] != UTIL_FORMAT_SWIZZLE_X + i) + return false; + + return true; +} + +/** + * Return a canonical format for the given bits and channel size. + */ +static enum pipe_format +canonical_format_from_bits(unsigned bits, unsigned channel_size) +{ + switch (bits) { + case 8: + if (channel_size == 8) + return get_canonical_format(PIPE_FORMAT_R8_UINT); + break; + + case 16: + if (channel_size == 8) + return get_canonical_format(PIPE_FORMAT_R8G8_UINT); + if (channel_size == 16) + return get_canonical_format(PIPE_FORMAT_R16_UINT); + break; + + case 32: + if (channel_size == 8) + return get_canonical_format(PIPE_FORMAT_R8G8B8A8_UINT); + if (channel_size == 16) + return get_canonical_format(PIPE_FORMAT_R16G16_UINT); + if (channel_size == 32) + return get_canonical_format(PIPE_FORMAT_R32_UINT); + break; + + case 64: + if (channel_size == 16) + return get_canonical_format(PIPE_FORMAT_R16G16B16A16_UINT); + if (channel_size == 32) + return get_canonical_format(PIPE_FORMAT_R32G32_UINT); + break; + + case 128: + if (channel_size == 32) + return get_canonical_format(PIPE_FORMAT_R32G32B32A32_UINT); + break; + } + + assert(!"impossible format"); + return PIPE_FORMAT_NONE; +} + +static void +blit(struct pipe_context *pipe, + struct pipe_resource *dst, + enum pipe_format dst_format, + unsigned dst_level, + unsigned dstx, unsigned dsty, unsigned dstz, + struct pipe_resource *src, + enum pipe_format src_format, + unsigned src_level, + const struct pipe_box *src_box) +{ + struct pipe_blit_info blit = {{0}}; + + blit.src.resource = src; + blit.dst.resource = dst; + blit.src.format = src_format; + blit.dst.format = dst_format; + blit.src.level = src_level; + blit.dst.level = dst_level; + blit.src.box = *src_box; + u_box_3d(dstx, dsty, dstz, src_box->width, src_box->height, + src_box->depth, &blit.dst.box); + blit.mask = PIPE_MASK_RGBA; + blit.filter = PIPE_TEX_FILTER_NEAREST; + + pipe->blit(pipe, &blit); +} + +static void +swizzled_copy(struct pipe_context *pipe, + struct pipe_resource *dst, + unsigned dst_level, + unsigned dstx, unsigned dsty, unsigned dstz, + struct pipe_resource *src, + unsigned src_level, + const struct pipe_box *src_box) +{ + const struct util_format_description *src_desc, *dst_desc; + unsigned bits; + enum pipe_format blit_src_format, blit_dst_format; + + /* Get equivalent canonical formats. Those are always array formats and + * copying between compatible canonical formats behaves either like + * memcpy or like swizzled memcpy. The idea is that we won't have to care + * about the channel type from this point on. + * Only the swizzle and channel size. + */ + blit_src_format = get_canonical_format(src->format); + blit_dst_format = get_canonical_format(dst->format); + + assert(blit_src_format != PIPE_FORMAT_NONE); + assert(blit_dst_format != PIPE_FORMAT_NONE); + + src_desc = util_format_description(blit_src_format); + dst_desc = util_format_description(blit_dst_format); + + assert(src_desc->block.bits == dst_desc->block.bits); + bits = src_desc->block.bits; + + if (dst_desc->channel[0].size == src_desc->channel[0].size) { + /* Only the swizzle is different, which means we can just blit, + * e.g. RGBA -> BGRA. + */ + } else if (has_identity_swizzle(src_desc)) { + /* Src is unswizzled and dst can be swizzled, so src is typecast + * to an equivalent dst-compatible format. + * e.g. R32 -> BGRA8 is realized as RGBA8 -> BGRA8 + */ + blit_src_format = + canonical_format_from_bits(bits, dst_desc->channel[0].size); + } else if (has_identity_swizzle(dst_desc)) { + /* Dst is unswizzled and src can be swizzled, so dst is typecast + * to an equivalent src-compatible format. + * e.g. BGRA8 -> R32 is realized as BGRA8 -> RGBA8 + */ + blit_dst_format = + canonical_format_from_bits(bits, src_desc->channel[0].size); + } else { + assert(!"This should have been handled by handle_complex_copy."); + return; + } + + blit(pipe, dst, blit_dst_format, dst_level, dstx, dsty, dstz, + src, blit_src_format, src_level, src_box); +} + +static bool +same_size_and_swizzle(const struct util_format_description *d1, + const struct util_format_description *d2) +{ + int i; + + if (d1->layout != d2->layout || + d1->nr_channels != d2->nr_channels || + d1->is_array != d2->is_array) + return false; + + for (i = 0; i < d1->nr_channels; i++) { + if (d1->channel[i].size != d2->channel[i].size) + return false; + + if (d1->swizzle[i] <= UTIL_FORMAT_SWIZZLE_W && + d2->swizzle[i] <= UTIL_FORMAT_SWIZZLE_W && + d1->swizzle[i] != d2->swizzle[i]) + return false; + } + + return true; +} + +static struct pipe_resource * +create_texture(struct pipe_screen *screen, enum pipe_format format, + unsigned width, unsigned height, unsigned depth) +{ + struct pipe_resource templ; + + memset(&templ, 0, sizeof(templ)); + templ.format = format; + templ.width0 = width; + templ.height0 = height; + templ.depth0 = 1; + templ.array_size = depth; + templ.usage = PIPE_USAGE_DEFAULT; + templ.bind = PIPE_BIND_SAMPLER_VIEW | PIPE_BIND_RENDER_TARGET; + + if (depth > 1) + templ.target = PIPE_TEXTURE_2D_ARRAY; + else + templ.target = PIPE_TEXTURE_2D; + + return screen->resource_create(screen, &templ); +} + +/** + * Handle complex format conversions using 2 blits with a temporary texture + * in between, e.g. blitting from B10G10R10A2 to G16R16. + * + * This example is implemented this way: + * 1) First, blit from B10G10R10A2 to R10G10B10A2, which is canonical, so it + * can be reinterpreted as a different canonical format of the same bpp, + * such as R16G16. This blit only swaps R and B 10-bit components. + * 2) Finally, blit the result, which is R10G10B10A2, as R16G16 to G16R16. + * This blit only swaps R and G 16-bit components. + */ +static bool +handle_complex_copy(struct pipe_context *pipe, + struct pipe_resource *dst, + unsigned dst_level, + unsigned dstx, unsigned dsty, unsigned dstz, + struct pipe_resource *src, + unsigned src_level, + const struct pipe_box *src_box, + enum pipe_format noncanon_format, + enum pipe_format canon_format) +{ + struct pipe_box temp_box; + struct pipe_resource *temp = NULL; + const struct util_format_description *src_desc, *dst_desc; + const struct util_format_description *canon_desc, *noncanon_desc; + bool src_is_canon; + bool src_is_noncanon; + bool dst_is_canon; + bool dst_is_noncanon; + + src_desc = util_format_description(src->format); + dst_desc = util_format_description(dst->format); + canon_desc = util_format_description(canon_format); + noncanon_desc = util_format_description(noncanon_format); + + src_is_canon = same_size_and_swizzle(src_desc, canon_desc); + dst_is_canon = same_size_and_swizzle(dst_desc, canon_desc); + src_is_noncanon = same_size_and_swizzle(src_desc, noncanon_desc); + dst_is_noncanon = same_size_and_swizzle(dst_desc, noncanon_desc); + + if (src_is_noncanon) { + /* Simple case - only types differ (e.g. UNORM and UINT). */ + if (dst_is_noncanon) { + blit(pipe, dst, noncanon_format, dst_level, dstx, dsty, dstz, src, + noncanon_format, src_level, src_box); + return true; + } + + /* Simple case - only types and swizzles differ. */ + if (dst_is_canon) { + blit(pipe, dst, canon_format, dst_level, dstx, dsty, dstz, src, + noncanon_format, src_level, src_box); + return true; + } + + /* Use the temporary texture. Src is converted to a canonical format, + * then proceed the generic swizzled_copy. + */ + temp = create_texture(pipe->screen, canon_format, src_box->width, + src_box->height, src_box->depth); + + u_box_3d(0, 0, 0, src_box->width, src_box->height, src_box->depth, + &temp_box); + + blit(pipe, temp, canon_format, 0, 0, 0, 0, src, noncanon_format, + src_level, src_box); + swizzled_copy(pipe, dst, dst_level, dstx, dsty, dstz, temp, 0, + &temp_box); + pipe_resource_reference(&temp, NULL); + return true; + } + + if (dst_is_noncanon) { + /* Simple case - only types and swizzles differ. */ + if (src_is_canon) { + blit(pipe, dst, noncanon_format, dst_level, dstx, dsty, dstz, src, + canon_format, src_level, src_box); + return true; + } + + /* Use the temporary texture. First, use the generic copy, but use + * a canonical format in the destination. Then convert */ + temp = create_texture(pipe->screen, canon_format, src_box->width, + src_box->height, src_box->depth); + + u_box_3d(0, 0, 0, src_box->width, src_box->height, src_box->depth, + &temp_box); + + swizzled_copy(pipe, temp, 0, 0, 0, 0, src, src_level, src_box); + blit(pipe, dst, noncanon_format, dst_level, dstx, dsty, dstz, temp, + canon_format, 0, &temp_box); + pipe_resource_reference(&temp, NULL); + return true; + } + + return false; +} + +static void +copy_image(struct pipe_context *pipe, + struct pipe_resource *dst, + unsigned dst_level, + unsigned dstx, unsigned dsty, unsigned dstz, + struct pipe_resource *src, + unsigned src_level, + const struct pipe_box *src_box) +{ + if (src->format == dst->format || + util_format_is_compressed(src->format) || + util_format_is_compressed(dst->format)) { + pipe->resource_copy_region(pipe, dst, dst_level, dstx, dsty, dstz, + src, src_level, src_box); + return; + } + + /* Copying to/from B10G10R10*2 needs 2 blits with R10G10B10A2 + * as a temporary texture in between. + */ + if (handle_complex_copy(pipe, dst, dst_level, dstx, dsty, dstz, src, + src_level, src_box, PIPE_FORMAT_B10G10R10A2_UINT, + PIPE_FORMAT_R10G10B10A2_UINT)) + return; + + /* Copying to/from G8R8 needs 2 blits with R8G8 as a temporary texture + * in between. + */ + if (handle_complex_copy(pipe, dst, dst_level, dstx, dsty, dstz, src, + src_level, src_box, PIPE_FORMAT_G8R8_UNORM, + PIPE_FORMAT_R8G8_UNORM)) + return; + + /* Copying to/from G16R16 needs 2 blits with R16G16 as a temporary texture + * in between. + */ + if (handle_complex_copy(pipe, dst, dst_level, dstx, dsty, dstz, src, + src_level, src_box, PIPE_FORMAT_G16R16_UNORM, + PIPE_FORMAT_R16G16_UNORM)) + return; + + /* Only allow non-identity swizzling on RGBA8 formats. */ + + /* Simple copy, memcpy with swizzling, no format conversion. */ + swizzled_copy(pipe, dst, dst_level, dstx, dsty, dstz, src, src_level, + src_box); +} + +static void +st_CopyImageSubData(struct gl_context *ctx, + struct gl_texture_image *src_image, + struct gl_renderbuffer *src_renderbuffer, + int src_x, int src_y, int src_z, + struct gl_texture_image *dst_image, + struct gl_renderbuffer *dst_renderbuffer, + int dst_x, int dst_y, int dst_z, + int src_width, int src_height) +{ + struct st_context *st = st_context(ctx); + struct pipe_context *pipe = st->pipe; + struct pipe_resource *src_res, *dst_res; + struct pipe_box box; + int src_level, dst_level; + + if (src_image) { + struct st_texture_image *src = st_texture_image(src_image); + src_res = src->pt; + src_level = src_image->Level; + src_z += src_image->Face; + } else { + struct st_renderbuffer *src = st_renderbuffer(src_renderbuffer); + src_res = src->texture; + src_level = 0; + } + + if (dst_image) { + struct st_texture_image *dst = st_texture_image(dst_image); + dst_res = dst->pt; + dst_level = dst_image->Level; + dst_z += dst_image->Face; + } else { + struct st_renderbuffer *dst = st_renderbuffer(dst_renderbuffer); + dst_res = dst->texture; + dst_level = 0; + } + + u_box_2d_zslice(src_x, src_y, src_z, src_width, src_height, &box); + + copy_image(pipe, dst_res, dst_level, dst_x, dst_y, dst_z, + src_res, src_level, &box); +} + +void +st_init_copy_image_functions(struct dd_function_table *functions) +{ + functions->CopyImageSubData = st_CopyImageSubData; +} |