[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
Sat Jul 16 16:21:23 UTC 2016
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