mesa/st: break up st_GetTexSubImage

this makes the function more readable and enables a lot of the code
to be reused for other purposes

Reviewed-by: Marek Olšák <marek.olsak@amd.com>
Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/11982>
This commit is contained in:
Mike Blumenkrantz 2021-04-19 13:03:06 -04:00 committed by Marge Bot
parent 7a14484bca
commit 6276973a9a

View file

@ -129,6 +129,266 @@ gl_target_to_pipe(GLenum target)
}
}
static enum pipe_format
get_src_format(struct pipe_screen *screen, struct st_texture_object *stObj)
{
struct pipe_resource *src = stObj->pt;
enum pipe_format src_format;
/* Convert the source format to what is expected by GetTexImage
* and see if it's supported.
*
* This only applies to glGetTexImage:
* - Luminance must be returned as (L,0,0,1).
* - Luminance alpha must be returned as (L,0,0,A).
* - Intensity must be returned as (I,0,0,1)
*/
if (stObj->surface_based)
src_format = util_format_linear(stObj->surface_format);
else
src_format = util_format_linear(src->format);
src_format = util_format_luminance_to_red(src_format);
src_format = util_format_intensity_to_red(src_format);
if (!src_format ||
!screen->is_format_supported(screen, src_format, src->target,
src->nr_samples, src->nr_storage_samples,
PIPE_BIND_SAMPLER_VIEW)) {
return PIPE_FORMAT_NONE;
}
return src_format;
}
static struct pipe_resource *
create_dst_texture(struct gl_context *ctx,
enum pipe_format dst_format, enum pipe_texture_target pipe_target,
GLsizei width, GLsizei height, GLint depth,
GLenum gl_target, unsigned bind)
{
struct st_context *st = st_context(ctx);
struct pipe_screen *screen = st->screen;
struct pipe_resource dst_templ;
/* create the destination texture of size (width X height X depth) */
memset(&dst_templ, 0, sizeof(dst_templ));
dst_templ.target = pipe_target;
dst_templ.format = dst_format;
dst_templ.bind = bind;
dst_templ.usage = PIPE_USAGE_STAGING;
st_gl_texture_dims_to_pipe_dims(gl_target, width, height, depth,
&dst_templ.width0, &dst_templ.height0,
&dst_templ.depth0, &dst_templ.array_size);
return screen->resource_create(screen, &dst_templ);
}
static boolean
copy_to_staging_dest(struct gl_context * ctx, struct pipe_resource *dst,
GLint xoffset, GLint yoffset, GLint zoffset,
GLsizei width, GLsizei height, GLint depth,
GLenum format, GLenum type, void * pixels,
struct gl_texture_image *texImage)
{
struct st_context *st = st_context(ctx);
struct pipe_context *pipe = st->pipe;
struct st_texture_object *stObj = st_texture_object(texImage->TexObject);
struct pipe_resource *src = stObj->pt;
enum pipe_format dst_format = dst->format;
mesa_format mesa_format;
GLenum gl_target = texImage->TexObject->Target;
unsigned dims;
struct pipe_transfer *tex_xfer;
ubyte *map = NULL;
boolean done = FALSE;
pixels = _mesa_map_pbo_dest(ctx, &ctx->Pack, pixels);
map = pipe_texture_map_3d(pipe, dst, 0, PIPE_MAP_READ,
0, 0, 0, width, height, depth, &tex_xfer);
if (!map) {
goto end;
}
mesa_format = st_pipe_format_to_mesa_format(dst_format);
dims = _mesa_get_texture_dimensions(gl_target);
/* copy/pack data into user buffer */
if (_mesa_format_matches_format_and_type(mesa_format, format, type,
ctx->Pack.SwapBytes, NULL)) {
/* memcpy */
const uint bytesPerRow = width * util_format_get_blocksize(dst_format);
GLuint row, slice;
for (slice = 0; slice < depth; slice++) {
ubyte *slice_map = map;
for (row = 0; row < height; row++) {
void *dest = _mesa_image_address(dims, &ctx->Pack, pixels,
width, height, format, type,
slice, row, 0);
memcpy(dest, slice_map, bytesPerRow);
slice_map += tex_xfer->stride;
}
map += tex_xfer->layer_stride;
}
}
else {
/* format translation via floats */
GLuint slice;
GLfloat *rgba;
uint32_t dstMesaFormat;
int dstStride, srcStride;
assert(util_format_is_compressed(src->format));
rgba = malloc(width * height * 4 * sizeof(GLfloat));
if (!rgba) {
goto end;
}
if (ST_DEBUG & DEBUG_FALLBACK)
debug_printf("%s: fallback format translation\n", __func__);
dstMesaFormat = _mesa_format_from_format_and_type(format, type);
dstStride = _mesa_image_row_stride(&ctx->Pack, width, format, type);
srcStride = 4 * width * sizeof(GLfloat);
for (slice = 0; slice < depth; slice++) {
void *dest = _mesa_image_address(dims, &ctx->Pack, pixels,
width, height, format, type,
slice, 0, 0);
/* get float[4] rgba row from surface */
pipe_get_tile_rgba(tex_xfer, map, 0, 0, width, height, dst_format,
rgba);
_mesa_format_convert(dest, dstMesaFormat, dstStride,
rgba, RGBA32_FLOAT, srcStride,
width, height, NULL);
/* Handle byte swapping if required */
if (ctx->Pack.SwapBytes) {
_mesa_swap_bytes_2d_image(format, type, &ctx->Pack,
width, height, dest, dest);
}
map += tex_xfer->layer_stride;
}
free(rgba);
}
done = TRUE;
end:
if (map)
pipe_texture_unmap(pipe, tex_xfer);
_mesa_unmap_pbo_dest(ctx, &ctx->Pack);
return done;
}
static enum pipe_format
get_dst_format(struct gl_context *ctx, enum pipe_texture_target target,
enum pipe_format src_format, bool is_compressed,
GLenum format, GLenum type, unsigned bind)
{
struct st_context *st = st_context(ctx);
struct pipe_screen *screen = st->screen;
/* Choose the destination format by finding the best match
* for the format+type combo. */
enum pipe_format dst_format = st_choose_matching_format(st, bind, format, type,
ctx->Pack.SwapBytes);
if (dst_format == PIPE_FORMAT_NONE) {
GLenum dst_glformat;
/* Fall back to _mesa_GetTexImage_sw except for compressed formats,
* where decompression with a blit is always preferred. */
if (!is_compressed) {
return PIPE_FORMAT_NONE;
}
/* Set the appropriate format for the decompressed texture.
* Luminance and sRGB formats shouldn't appear here.*/
switch (src_format) {
case PIPE_FORMAT_DXT1_RGB:
case PIPE_FORMAT_DXT1_RGBA:
case PIPE_FORMAT_DXT3_RGBA:
case PIPE_FORMAT_DXT5_RGBA:
case PIPE_FORMAT_RGTC1_UNORM:
case PIPE_FORMAT_RGTC2_UNORM:
case PIPE_FORMAT_ETC1_RGB8:
case PIPE_FORMAT_ETC2_RGB8:
case PIPE_FORMAT_ETC2_RGB8A1:
case PIPE_FORMAT_ETC2_RGBA8:
case PIPE_FORMAT_ASTC_4x4:
case PIPE_FORMAT_ASTC_5x4:
case PIPE_FORMAT_ASTC_5x5:
case PIPE_FORMAT_ASTC_6x5:
case PIPE_FORMAT_ASTC_6x6:
case PIPE_FORMAT_ASTC_8x5:
case PIPE_FORMAT_ASTC_8x6:
case PIPE_FORMAT_ASTC_8x8:
case PIPE_FORMAT_ASTC_10x5:
case PIPE_FORMAT_ASTC_10x6:
case PIPE_FORMAT_ASTC_10x8:
case PIPE_FORMAT_ASTC_10x10:
case PIPE_FORMAT_ASTC_12x10:
case PIPE_FORMAT_ASTC_12x12:
case PIPE_FORMAT_BPTC_RGBA_UNORM:
case PIPE_FORMAT_FXT1_RGB:
case PIPE_FORMAT_FXT1_RGBA:
dst_glformat = GL_RGBA8;
break;
case PIPE_FORMAT_RGTC1_SNORM:
case PIPE_FORMAT_RGTC2_SNORM:
if (!ctx->Extensions.EXT_texture_snorm)
return PIPE_FORMAT_NONE;
dst_glformat = GL_RGBA8_SNORM;
break;
case PIPE_FORMAT_BPTC_RGB_FLOAT:
case PIPE_FORMAT_BPTC_RGB_UFLOAT:
if (!ctx->Extensions.ARB_texture_float)
return PIPE_FORMAT_NONE;
dst_glformat = GL_RGBA32F;
break;
case PIPE_FORMAT_ETC2_R11_UNORM:
if (!screen->is_format_supported(screen, PIPE_FORMAT_R16_UNORM,
target, 0, 0, bind))
return PIPE_FORMAT_NONE;
dst_glformat = GL_R16;
break;
case PIPE_FORMAT_ETC2_R11_SNORM:
if (!screen->is_format_supported(screen, PIPE_FORMAT_R16_SNORM,
target, 0, 0, bind))
return PIPE_FORMAT_NONE;
dst_glformat = GL_R16_SNORM;
break;
case PIPE_FORMAT_ETC2_RG11_UNORM:
if (!screen->is_format_supported(screen, PIPE_FORMAT_R16G16_UNORM,
target, 0, 0, bind))
return PIPE_FORMAT_NONE;
dst_glformat = GL_RG16;
break;
case PIPE_FORMAT_ETC2_RG11_SNORM:
if (!screen->is_format_supported(screen, PIPE_FORMAT_R16G16_SNORM,
target, 0, 0, bind))
return PIPE_FORMAT_NONE;
dst_glformat = GL_RG16_SNORM;
break;
default:
assert(0);
return PIPE_FORMAT_NONE;
}
dst_format = st_choose_format(st, dst_glformat, format, type,
target, 0, 0, bind,
false, false);
}
return dst_format;
}
/** called via ctx->Driver.NewTextureImage() */
static struct gl_texture_image *
@ -2028,22 +2288,16 @@ st_GetTexSubImage(struct gl_context * ctx,
struct gl_texture_image *texImage)
{
struct st_context *st = st_context(ctx);
struct pipe_context *pipe = st->pipe;
struct pipe_screen *screen = st->screen;
struct st_texture_image *stImage = st_texture_image(texImage);
struct st_texture_object *stObj = st_texture_object(texImage->TexObject);
struct pipe_resource *src = stObj->pt;
struct pipe_resource *dst = NULL;
struct pipe_resource dst_templ;
enum pipe_format dst_format, src_format;
mesa_format mesa_format;
GLenum gl_target = texImage->TexObject->Target;
enum pipe_texture_target pipe_target;
unsigned dims;
struct pipe_blit_info blit;
unsigned bind;
struct pipe_transfer *tex_xfer;
ubyte *map = NULL;
boolean done = FALSE;
assert(!_mesa_is_format_etc2(texImage->TexFormat) &&
@ -2052,6 +2306,12 @@ st_GetTexSubImage(struct gl_context * ctx,
st_flush_bitmap_cache(st);
/* GetTexImage only returns a single face for cubemaps. */
if (gl_target == GL_TEXTURE_CUBE_MAP) {
gl_target = GL_TEXTURE_2D;
}
pipe_target = gl_target_to_pipe(gl_target);
if (!st->prefer_blit_based_texture_transfer &&
!_mesa_is_format_compressed(texImage->TexFormat)) {
/* Try to avoid the fallback if we're doing texture decompression here */
@ -2083,151 +2343,23 @@ st_GetTexSubImage(struct gl_context * ctx,
goto fallback;
}
/* Convert the source format to what is expected by GetTexImage
* and see if it's supported.
*
* This only applies to glGetTexImage:
* - Luminance must be returned as (L,0,0,1).
* - Luminance alpha must be returned as (L,0,0,A).
* - Intensity must be returned as (I,0,0,1)
*/
if (stObj->surface_based)
src_format = util_format_linear(stObj->surface_format);
else
src_format = util_format_linear(src->format);
src_format = util_format_luminance_to_red(src_format);
src_format = util_format_intensity_to_red(src_format);
if (!src_format ||
!screen->is_format_supported(screen, src_format, src->target,
src->nr_samples, src->nr_storage_samples,
PIPE_BIND_SAMPLER_VIEW)) {
src_format = get_src_format(screen, stObj);
if (src_format == PIPE_FORMAT_NONE)
goto fallback;
}
if (format == GL_DEPTH_COMPONENT || format == GL_DEPTH_STENCIL)
bind = PIPE_BIND_DEPTH_STENCIL;
else
bind = PIPE_BIND_RENDER_TARGET;
/* GetTexImage only returns a single face for cubemaps. */
if (gl_target == GL_TEXTURE_CUBE_MAP) {
gl_target = GL_TEXTURE_2D;
}
pipe_target = gl_target_to_pipe(gl_target);
/* Choose the destination format by finding the best match
* for the format+type combo. */
dst_format = st_choose_matching_format(st, bind, format, type,
ctx->Pack.SwapBytes);
if (dst_format == PIPE_FORMAT_NONE) {
GLenum dst_glformat;
/* Fall back to _mesa_GetTexImage_sw except for compressed formats,
* where decompression with a blit is always preferred. */
if (!util_format_is_compressed(src->format)) {
goto fallback;
}
/* Set the appropriate format for the decompressed texture.
* Luminance and sRGB formats shouldn't appear here.*/
switch (src_format) {
case PIPE_FORMAT_DXT1_RGB:
case PIPE_FORMAT_DXT1_RGBA:
case PIPE_FORMAT_DXT3_RGBA:
case PIPE_FORMAT_DXT5_RGBA:
case PIPE_FORMAT_RGTC1_UNORM:
case PIPE_FORMAT_RGTC2_UNORM:
case PIPE_FORMAT_ETC1_RGB8:
case PIPE_FORMAT_ETC2_RGB8:
case PIPE_FORMAT_ETC2_RGB8A1:
case PIPE_FORMAT_ETC2_RGBA8:
case PIPE_FORMAT_ASTC_4x4:
case PIPE_FORMAT_ASTC_5x4:
case PIPE_FORMAT_ASTC_5x5:
case PIPE_FORMAT_ASTC_6x5:
case PIPE_FORMAT_ASTC_6x6:
case PIPE_FORMAT_ASTC_8x5:
case PIPE_FORMAT_ASTC_8x6:
case PIPE_FORMAT_ASTC_8x8:
case PIPE_FORMAT_ASTC_10x5:
case PIPE_FORMAT_ASTC_10x6:
case PIPE_FORMAT_ASTC_10x8:
case PIPE_FORMAT_ASTC_10x10:
case PIPE_FORMAT_ASTC_12x10:
case PIPE_FORMAT_ASTC_12x12:
case PIPE_FORMAT_BPTC_RGBA_UNORM:
case PIPE_FORMAT_FXT1_RGB:
case PIPE_FORMAT_FXT1_RGBA:
dst_glformat = GL_RGBA8;
break;
case PIPE_FORMAT_RGTC1_SNORM:
case PIPE_FORMAT_RGTC2_SNORM:
if (!ctx->Extensions.EXT_texture_snorm)
goto fallback;
dst_glformat = GL_RGBA8_SNORM;
break;
case PIPE_FORMAT_BPTC_RGB_FLOAT:
case PIPE_FORMAT_BPTC_RGB_UFLOAT:
if (!ctx->Extensions.ARB_texture_float)
goto fallback;
dst_glformat = GL_RGBA32F;
break;
case PIPE_FORMAT_ETC2_R11_UNORM:
if (!screen->is_format_supported(screen, PIPE_FORMAT_R16_UNORM,
pipe_target, 0, 0, bind))
goto fallback;
dst_glformat = GL_R16;
break;
case PIPE_FORMAT_ETC2_R11_SNORM:
if (!screen->is_format_supported(screen, PIPE_FORMAT_R16_SNORM,
pipe_target, 0, 0, bind))
goto fallback;
dst_glformat = GL_R16_SNORM;
break;
case PIPE_FORMAT_ETC2_RG11_UNORM:
if (!screen->is_format_supported(screen, PIPE_FORMAT_R16G16_UNORM,
pipe_target, 0, 0, bind))
goto fallback;
dst_glformat = GL_RG16;
break;
case PIPE_FORMAT_ETC2_RG11_SNORM:
if (!screen->is_format_supported(screen, PIPE_FORMAT_R16G16_SNORM,
pipe_target, 0, 0, bind))
goto fallback;
dst_glformat = GL_RG16_SNORM;
break;
default:
assert(0);
goto fallback;
}
dst_format = st_choose_format(st, dst_glformat, format, type,
pipe_target, 0, 0, bind,
false, false);
if (dst_format == PIPE_FORMAT_NONE) {
/* unable to get an rgba format!?! */
goto fallback;
}
}
/* create the destination texture of size (width X height X depth) */
memset(&dst_templ, 0, sizeof(dst_templ));
dst_templ.target = pipe_target;
dst_templ.format = dst_format;
dst_templ.bind = bind;
dst_templ.usage = PIPE_USAGE_STAGING;
st_gl_texture_dims_to_pipe_dims(gl_target, width, height, depth,
&dst_templ.width0, &dst_templ.height0,
&dst_templ.depth0, &dst_templ.array_size);
dst = screen->resource_create(screen, &dst_templ);
if (!dst) {
dst_format = get_dst_format(ctx, pipe_target, src_format, util_format_is_compressed(src->format),
format, type, bind);
if (dst_format == PIPE_FORMAT_NONE)
goto fallback;
dst = create_dst_texture(ctx, dst_format, pipe_target, width, height, depth, gl_target, bind);
if (!dst)
goto fallback;
}
/* From now on, we need the gallium representation of dimensions. */
if (gl_target == GL_TEXTURE_1D_ARRAY) {
@ -2264,91 +2396,8 @@ st_GetTexSubImage(struct gl_context * ctx,
/* blit/render/decompress */
st->pipe->blit(st->pipe, &blit);
pixels = _mesa_map_pbo_dest(ctx, &ctx->Pack, pixels);
map = pipe_texture_map_3d(pipe, dst, 0, PIPE_MAP_READ,
0, 0, 0, width, height, depth, &tex_xfer);
if (!map) {
goto end;
}
mesa_format = st_pipe_format_to_mesa_format(dst_format);
dims = _mesa_get_texture_dimensions(gl_target);
/* copy/pack data into user buffer */
if (_mesa_format_matches_format_and_type(mesa_format, format, type,
ctx->Pack.SwapBytes, NULL)) {
/* memcpy */
const uint bytesPerRow = width * util_format_get_blocksize(dst_format);
GLuint row, slice;
for (slice = 0; slice < depth; slice++) {
ubyte *slice_map = map;
for (row = 0; row < height; row++) {
void *dest = _mesa_image_address(dims, &ctx->Pack, pixels,
width, height, format, type,
slice, row, 0);
memcpy(dest, slice_map, bytesPerRow);
slice_map += tex_xfer->stride;
}
map += tex_xfer->layer_stride;
}
}
else {
/* format translation via floats */
GLuint slice;
GLfloat *rgba;
uint32_t dstMesaFormat;
int dstStride, srcStride;
assert(util_format_is_compressed(src->format));
rgba = malloc(width * height * 4 * sizeof(GLfloat));
if (!rgba) {
goto end;
}
if (ST_DEBUG & DEBUG_FALLBACK)
debug_printf("%s: fallback format translation\n", __func__);
dstMesaFormat = _mesa_format_from_format_and_type(format, type);
dstStride = _mesa_image_row_stride(&ctx->Pack, width, format, type);
srcStride = 4 * width * sizeof(GLfloat);
for (slice = 0; slice < depth; slice++) {
void *dest = _mesa_image_address(dims, &ctx->Pack, pixels,
width, height, format, type,
slice, 0, 0);
/* get float[4] rgba row from surface */
pipe_get_tile_rgba(tex_xfer, map, 0, 0, width, height, dst_format,
rgba);
_mesa_format_convert(dest, dstMesaFormat, dstStride,
rgba, RGBA32_FLOAT, srcStride,
width, height, NULL);
/* Handle byte swapping if required */
if (ctx->Pack.SwapBytes) {
_mesa_swap_bytes_2d_image(format, type, &ctx->Pack,
width, height, dest, dest);
}
map += tex_xfer->layer_stride;
}
free(rgba);
}
done = TRUE;
end:
if (map)
pipe_texture_unmap(pipe, tex_xfer);
_mesa_unmap_pbo_dest(ctx, &ctx->Pack);
done = copy_to_staging_dest(ctx, dst, xoffset, yoffset, zoffset, width, height,
depth, format, type, pixels, texImage);
pipe_resource_reference(&dst, NULL);
fallback: