st/mesa: provide GL_OES_copy_image support by caching the original ETC data

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@alum.mit.edu>
Acked-by: Marek Olšák <marek.olsak@amd.com>
This commit is contained in:
Ilia Mirkin 2016-07-08 02:44:57 -04:00
parent 7727e6f67c
commit d816a51b81
6 changed files with 156 additions and 39 deletions

View File

@ -257,7 +257,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)

View File

@ -52,6 +52,7 @@ Note: some of the new features are only available with certain drivers.
<li>GL_ARB_ES3_1_compatibility on i965</li>
<li>GL_EXT_window_rectangles on nv50, nvc0</li>
<li>GL_KHR_texture_compression_astc_sliced_3d on i965</li>
<li>GL_OES_copy_image on nv50, nvc0, r600, radeonsi, softpipe, llvmpipe</li>
</ul>
<h2>Bug fixes</h2>

View File

@ -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

View File

@ -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);
}
}

View File

@ -582,6 +582,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 },
@ -955,17 +956,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[] = {

View File

@ -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;
};
/**