[Mesa-dev] [PATCH v2 2/2] st/mesa: provide GL_OES_copy_image support by caching the original ETC data
Ilia Mirkin
imirkin at alum.mit.edu
Tue Jul 26 01:32:18 UTC 2016
ping
On Sat, Jul 16, 2016 at 12:21 PM, Ilia Mirkin <imirkin at alum.mit.edu> wrote:
> The additional provision of GL_OES_copy_image is that it work for ETC.
> However many desktop GPUs don't have native ETC support, so st/mesa does
> the decoding by hand. Instead of discarding the compressed data, keep it
> around in CPU memory. Use it when performing image copies.
>
> Signed-off-by: Ilia Mirkin <imirkin at alum.mit.edu>
> ---
> docs/GL3.txt | 2 +-
> docs/relnotes/12.1.0.html | 1 +
> src/mesa/state_tracker/st_cb_copyimage.c | 96 +++++++++++++++++++++++++++++++-
> src/mesa/state_tracker/st_cb_texture.c | 77 +++++++++++++++++--------
> src/mesa/state_tracker/st_extensions.c | 12 +---
> src/mesa/state_tracker/st_texture.h | 7 ++-
> 6 files changed, 156 insertions(+), 39 deletions(-)
>
> diff --git a/docs/GL3.txt b/docs/GL3.txt
> index ce34869..6da5225 100644
> --- a/docs/GL3.txt
> +++ b/docs/GL3.txt
> @@ -255,7 +255,7 @@ GLES3.2, GLSL ES 3.2
> GL_KHR_debug DONE (all drivers)
> GL_KHR_robustness DONE (i965)
> GL_KHR_texture_compression_astc_ldr DONE (i965/gen9+)
> - GL_OES_copy_image DONE (i965)
> + GL_OES_copy_image DONE (all drivers)
> GL_OES_draw_buffers_indexed DONE (all drivers that support GL_ARB_draw_buffers_blend)
> GL_OES_draw_elements_base_vertex DONE (all drivers)
> GL_OES_geometry_shader started (idr)
> diff --git a/docs/relnotes/12.1.0.html b/docs/relnotes/12.1.0.html
> index 096f551..abdd83af 100644
> --- a/docs/relnotes/12.1.0.html
> +++ b/docs/relnotes/12.1.0.html
> @@ -47,6 +47,7 @@ Note: some of the new features are only available with certain drivers.
> <li>GL_ARB_shader_group_vote on nvc0</li>
> <li>GL_ARB_ES3_1_compatibility on i965</li>
> <li>GL_EXT_window_rectangles on nv50, nvc0</li>
> +<li>GL_OES_copy_image on nv50, nvc0, r600, radeonsi, softpipe, llvmpipe</li>
> </ul>
>
> <h2>Bug fixes</h2>
> diff --git a/src/mesa/state_tracker/st_cb_copyimage.c b/src/mesa/state_tracker/st_cb_copyimage.c
> index f670bd9..d160c8c 100644
> --- a/src/mesa/state_tracker/st_cb_copyimage.c
> +++ b/src/mesa/state_tracker/st_cb_copyimage.c
> @@ -532,6 +532,90 @@ copy_image(struct pipe_context *pipe,
> src_box);
> }
>
> +/* Note, the only allowable compressed format for this function is ETC */
> +static void
> +fallback_copy_image(struct st_context *st,
> + struct gl_texture_image *dst_image,
> + struct pipe_resource *dst_res,
> + int dst_x, int dst_y, int dst_z,
> + struct gl_texture_image *src_image,
> + struct pipe_resource *src_res,
> + int src_x, int src_y, int src_z,
> + int src_w, int src_h)
> +{
> + uint8_t *dst, *src;
> + int dst_stride, src_stride;
> + struct pipe_transfer *dst_transfer, *src_transfer;
> + unsigned line_bytes;
> +
> + bool dst_is_compressed = dst_image && _mesa_is_format_compressed(dst_image->TexFormat);
> + bool src_is_compressed = src_image && _mesa_is_format_compressed(src_image->TexFormat);
> +
> + unsigned dst_w = src_w;
> + unsigned dst_h = src_h;
> + unsigned lines = src_h;
> +
> + if (src_is_compressed && !dst_is_compressed) {
> + dst_w = DIV_ROUND_UP(dst_w, 4);
> + dst_h = DIV_ROUND_UP(dst_h, 4);
> + } else if (!src_is_compressed && dst_is_compressed) {
> + dst_w *= 4;
> + dst_h *= 4;
> + }
> + if (src_is_compressed) {
> + lines = DIV_ROUND_UP(lines, 4);
> + }
> +
> + if (src_image)
> + line_bytes = _mesa_format_row_stride(src_image->TexFormat, src_w);
> + else
> + line_bytes = _mesa_format_row_stride(dst_image->TexFormat, dst_w);
> +
> + if (dst_image) {
> + st->ctx->Driver.MapTextureImage(
> + st->ctx, dst_image, dst_z,
> + dst_x, dst_y, dst_w, dst_h,
> + GL_MAP_WRITE_BIT, &dst, &dst_stride);
> + } else {
> + dst = pipe_transfer_map(st->pipe, dst_res, 0, dst_z,
> + PIPE_TRANSFER_WRITE,
> + dst_x, dst_y, dst_w, dst_h,
> + &dst_transfer);
> + dst_stride = dst_transfer->stride;
> + }
> +
> + if (src_image) {
> + st->ctx->Driver.MapTextureImage(
> + st->ctx, src_image, src_z,
> + src_x, src_y, src_w, src_h,
> + GL_MAP_READ_BIT, &src, &src_stride);
> + } else {
> + src = pipe_transfer_map(st->pipe, src_res, 0, src_z,
> + PIPE_TRANSFER_READ,
> + src_x, src_y, src_w, src_h,
> + &src_transfer);
> + src_stride = src_transfer->stride;
> + }
> +
> + for (int y = 0; y < lines; y++) {
> + memcpy(dst, src, line_bytes);
> + dst += dst_stride;
> + src += src_stride;
> + }
> +
> + if (dst_image) {
> + st->ctx->Driver.UnmapTextureImage(st->ctx, dst_image, dst_z);
> + } else {
> + pipe_transfer_unmap(st->pipe, dst_transfer);
> + }
> +
> + if (src_image) {
> + st->ctx->Driver.UnmapTextureImage(st->ctx, src_image, src_z);
> + } else {
> + pipe_transfer_unmap(st->pipe, src_transfer);
> + }
> +}
> +
> static void
> st_CopyImageSubData(struct gl_context *ctx,
> struct gl_texture_image *src_image,
> @@ -547,6 +631,7 @@ st_CopyImageSubData(struct gl_context *ctx,
> struct pipe_resource *src_res, *dst_res;
> struct pipe_box box;
> int src_level, dst_level;
> + int orig_src_z = src_z, orig_dst_z = dst_z;
>
> st_flush_bitmap_cache(st);
> st_invalidate_readpix_cache(st);
> @@ -583,8 +668,15 @@ st_CopyImageSubData(struct gl_context *ctx,
>
> 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);
> + if ((src_image && st_etc_fallback(st, src_image)) ||
> + (dst_image && st_etc_fallback(st, dst_image))) {
> + fallback_copy_image(st, dst_image, dst_res, dst_x, dst_y, orig_dst_z,
> + src_image, src_res, src_x, src_y, orig_src_z,
> + src_width, src_height);
> + } else {
> + copy_image(pipe, dst_res, dst_level, dst_x, dst_y, dst_z,
> + src_res, src_level, &box);
> + }
> }
>
> void
> diff --git a/src/mesa/state_tracker/st_cb_texture.c b/src/mesa/state_tracker/st_cb_texture.c
> index 54f338f..8db11ed 100644
> --- a/src/mesa/state_tracker/st_cb_texture.c
> +++ b/src/mesa/state_tracker/st_cb_texture.c
> @@ -187,6 +187,11 @@ st_FreeTextureImageBuffer(struct gl_context *ctx,
> free(stImage->transfer);
> stImage->transfer = NULL;
> stImage->num_transfers = 0;
> +
> + if (stImage->etc_data) {
> + free(stImage->etc_data);
> + stImage->etc_data = NULL;
> + }
> }
>
> bool
> @@ -196,6 +201,26 @@ st_etc_fallback(struct st_context *st, struct gl_texture_image *texImage)
> (texImage->TexFormat == MESA_FORMAT_ETC1_RGB8 && !st->has_etc1);
> }
>
> +static void
> +etc_fallback_allocate(struct st_context *st, struct st_texture_image *stImage)
> +{
> + struct gl_texture_image *texImage = &stImage->base;
> +
> + if (!st_etc_fallback(st, texImage))
> + return;
> +
> + if (stImage->etc_data)
> + free(stImage->etc_data);
> +
> + unsigned data_size = _mesa_format_image_size(texImage->TexFormat,
> + texImage->Width2,
> + texImage->Height2,
> + texImage->Depth2);
> +
> + stImage->etc_data =
> + malloc(data_size * _mesa_num_tex_faces(texImage->TexObject->Target));
> +}
> +
> /** called via ctx->Driver.MapTextureImage() */
> static void
> st_MapTextureImage(struct gl_context *ctx,
> @@ -222,24 +247,23 @@ st_MapTextureImage(struct gl_context *ctx,
> &transfer);
> if (map) {
> if (st_etc_fallback(st, texImage)) {
> - /* ETC isn't supported by gallium and it's represented
> - * by uncompressed formats. Only write transfers with precompressed
> - * data are supported by ES3, which makes this really simple.
> + /* ETC isn't supported by all gallium drivers, where it's represented
> + * by uncompressed formats. We store the compressed data (as it's
> + * needed for image copies in OES_copy_image), and decompress as
> + * necessary in Unmap.
> *
> - * Just create a temporary storage where the ETC texture will
> - * be stored. It will be decompressed in the Unmap function.
> + * Note: all ETC1/ETC2 formats have 4x4 block sizes.
> */
> unsigned z = transfer->box.z;
> struct st_texture_image_transfer *itransfer = &stImage->transfer[z];
>
> - itransfer->temp_data =
> - malloc(_mesa_format_image_size(texImage->TexFormat, w, h, 1));
> - itransfer->temp_stride =
> - _mesa_format_row_stride(texImage->TexFormat, w);
> + unsigned bytes = _mesa_get_format_bytes(texImage->TexFormat);
> + unsigned stride = *rowStrideOut = itransfer->temp_stride =
> + _mesa_format_row_stride(texImage->TexFormat, texImage->Width2);
> + *mapOut = itransfer->temp_data =
> + stImage->etc_data + ((x / 4) * bytes + (y / 4) * stride) +
> + z * stride * texImage->Height2 / 4;
> itransfer->map = map;
> -
> - *mapOut = itransfer->temp_data;
> - *rowStrideOut = itransfer->temp_stride;
> }
> else {
> /* supported mapping */
> @@ -271,20 +295,21 @@ st_UnmapTextureImage(struct gl_context *ctx,
>
> assert(z == transfer->box.z);
>
> - if (texImage->TexFormat == MESA_FORMAT_ETC1_RGB8) {
> - _mesa_etc1_unpack_rgba8888(itransfer->map, transfer->stride,
> - itransfer->temp_data,
> - itransfer->temp_stride,
> - transfer->box.width, transfer->box.height);
> - }
> - else {
> - _mesa_unpack_etc2_format(itransfer->map, transfer->stride,
> - itransfer->temp_data, itransfer->temp_stride,
> - transfer->box.width, transfer->box.height,
> - texImage->TexFormat);
> + if (transfer->usage & PIPE_TRANSFER_WRITE) {
> + if (texImage->TexFormat == MESA_FORMAT_ETC1_RGB8) {
> + _mesa_etc1_unpack_rgba8888(itransfer->map, transfer->stride,
> + itransfer->temp_data,
> + itransfer->temp_stride,
> + transfer->box.width, transfer->box.height);
> + }
> + else {
> + _mesa_unpack_etc2_format(itransfer->map, transfer->stride,
> + itransfer->temp_data, itransfer->temp_stride,
> + transfer->box.width, transfer->box.height,
> + texImage->TexFormat);
> + }
> }
>
> - free(itransfer->temp_data);
> itransfer->temp_data = NULL;
> itransfer->temp_stride = 0;
> itransfer->map = 0;
> @@ -572,6 +597,8 @@ st_AllocTextureImageBuffer(struct gl_context *ctx,
>
> assert(!stImage->pt); /* xxx this might be wrong */
>
> + etc_fallback_allocate(st, stImage);
> +
> /* Look if the parent texture object has space for this image */
> if (stObj->pt &&
> level <= stObj->pt->last_level &&
> @@ -2680,6 +2707,8 @@ st_AllocTextureStorage(struct gl_context *ctx,
> struct st_texture_image *stImage =
> st_texture_image(texObj->Image[face][level]);
> pipe_resource_reference(&stImage->pt, stObj->pt);
> +
> + etc_fallback_allocate(st, stImage);
> }
> }
>
> diff --git a/src/mesa/state_tracker/st_extensions.c b/src/mesa/state_tracker/st_extensions.c
> index 4033452..493db12 100644
> --- a/src/mesa/state_tracker/st_extensions.c
> +++ b/src/mesa/state_tracker/st_extensions.c
> @@ -579,6 +579,7 @@ void st_init_extensions(struct pipe_screen *screen,
> { o(ARB_color_buffer_float), PIPE_CAP_VERTEX_COLOR_UNCLAMPED },
> { o(ARB_conditional_render_inverted), PIPE_CAP_CONDITIONAL_RENDER_INVERTED },
> { o(ARB_copy_image), PIPE_CAP_COPY_BETWEEN_COMPRESSED_AND_PLAIN_FORMATS },
> + { o(OES_copy_image), PIPE_CAP_COPY_BETWEEN_COMPRESSED_AND_PLAIN_FORMATS },
> { o(ARB_cull_distance), PIPE_CAP_CULL_DISTANCE },
> { o(ARB_depth_clamp), PIPE_CAP_DEPTH_CLIP_DISABLE },
> { o(ARB_depth_texture), PIPE_CAP_TEXTURE_SHADOW_MAP },
> @@ -951,17 +952,6 @@ void st_init_extensions(struct pipe_screen *screen,
> extensions->OES_sample_variables = extensions->ARB_sample_shading &&
> extensions->ARB_gpu_shader5;
>
> - /* If we don't have native ETC2 support, we don't keep track of the
> - * original ETC2 data. This is necessary to be able to copy images between
> - * compatible view classes.
> - */
> - if (extensions->ARB_copy_image && screen->is_format_supported(
> - screen, PIPE_FORMAT_ETC2_RGB8,
> - PIPE_TEXTURE_2D, 0,
> - PIPE_BIND_SAMPLER_VIEW)) {
> - extensions->OES_copy_image = GL_TRUE;
> - }
> -
> /* Maximum sample count. */
> {
> enum pipe_format color_formats[] = {
> diff --git a/src/mesa/state_tracker/st_texture.h b/src/mesa/state_tracker/st_texture.h
> index d5e8281..b2ddc14 100644
> --- a/src/mesa/state_tracker/st_texture.h
> +++ b/src/mesa/state_tracker/st_texture.h
> @@ -65,7 +65,12 @@ struct st_texture_image
> */
> struct st_texture_image_transfer *transfer;
> unsigned num_transfers;
> -};
> +
> + /* For ETC images, keep track of the original data. This is necessary for
> + * mapping/unmapping, as well as image copies.
> + */
> + GLubyte *etc_data;
> + };
>
>
> /**
> --
> 2.7.3
>
More information about the mesa-dev
mailing list