wined3d: Decompress DXTn 3D textures on upload.

Signed-off-by: Connor McAdams <conmanx360@gmail.com>
Signed-off-by: Henri Verbeet <hverbeet@codeweavers.com>
Signed-off-by: Matteo Bruni <mbruni@codeweavers.com>
Signed-off-by: Alexandre Julliard <julliard@winehq.org>
This commit is contained in:
Connor McAdams 2018-08-07 01:34:58 +04:30 committed by Alexandre Julliard
parent 8a321bba69
commit 3d4001f4fc
4 changed files with 304 additions and 37 deletions

View File

@ -477,3 +477,12 @@ void wined3d_resource_update_draw_binding(struct wined3d_resource *resource)
resource->draw_binding = WINED3D_LOCATION_TEXTURE_RGB;
}
}
const struct wined3d_format *wined3d_resource_get_decompress_format(struct wined3d_resource *resource,
const struct wined3d_context *context)
{
if (resource->format_flags & (WINED3DFMT_FLAG_SRGB_READ | WINED3DFMT_FLAG_SRGB_WRITE)
&& !(context->d3d_info->wined3d_creation_flags & WINED3D_SRGB_READ_WRITE_CONTROL))
return wined3d_get_format(context->gl_info, WINED3DFMT_B8G8R8A8_UNORM_SRGB, resource->usage);
return wined3d_get_format(context->gl_info, WINED3DFMT_B8G8R8A8_UNORM, resource->usage);
}

View File

@ -1727,7 +1727,13 @@ void wined3d_texture_prepare_texture(struct wined3d_texture *texture, struct win
if (texture->flags & alloc_flag)
return;
if (format->conv_byte_count)
if (texture->resource.format_flags & WINED3DFMT_FLAG_DECOMPRESS)
{
TRACE("WINED3DFMT_FLAG_DECOMPRESS set.\n");
texture->flags |= WINED3D_TEXTURE_CONVERTED;
format = wined3d_resource_get_decompress_format(&texture->resource, context);
}
else if (format->conv_byte_count)
{
texture->flags |= WINED3D_TEXTURE_CONVERTED;
}
@ -1897,6 +1903,7 @@ void wined3d_texture_upload_data(struct wined3d_texture *texture, unsigned int s
void *converted_mem = NULL;
struct wined3d_format f;
unsigned int level;
BOOL decompress;
GLenum target;
TRACE("texture %p, sub_resource_idx %u, context %p, format %s, src_box %s, data {%#x:%p}, "
@ -1948,20 +1955,31 @@ void wined3d_texture_upload_data(struct wined3d_texture *texture, unsigned int s
bo.addr += src_box->left * format->byte_count;
}
if (format->upload)
decompress = texture->resource.format_flags & WINED3DFMT_FLAG_DECOMPRESS;
if (format->upload || decompress)
{
const struct wined3d_format *compressed_format = format;
unsigned int dst_row_pitch, dst_slice_pitch;
void *src_mem;
if (texture->resource.format_flags & WINED3DFMT_FLAG_BLOCKS)
ERR("Converting a block-based format.\n");
if (decompress)
{
format = wined3d_resource_get_decompress_format(&texture->resource, context);
}
else
{
if (texture->resource.format_flags & WINED3DFMT_FLAG_BLOCKS)
ERR("Converting a block-based format.\n");
f = *format;
f.byte_count = format->conv_byte_count;
format = &f;
f = *format;
f.byte_count = format->conv_byte_count;
format = &f;
}
wined3d_format_calculate_pitch(format, 1, update_w, update_h, &dst_row_pitch, &dst_slice_pitch);
/* Note that uploading 3D textures may require quite some address
* space; it may make sense to upload them per-slice instead. */
if (!(converted_mem = heap_calloc(update_d, dst_slice_pitch)))
{
ERR("Failed to allocate upload buffer.\n");
@ -1970,8 +1988,12 @@ void wined3d_texture_upload_data(struct wined3d_texture *texture, unsigned int s
src_mem = context_map_bo_address(context, &bo, src_slice_pitch,
GL_PIXEL_UNPACK_BUFFER, WINED3D_MAP_READ);
format->upload(src_mem, converted_mem, src_row_pitch, src_slice_pitch,
dst_row_pitch, dst_slice_pitch, update_w, update_h, update_d);
if (decompress)
compressed_format->decompress(src_mem, converted_mem, src_row_pitch, src_slice_pitch,
dst_row_pitch, dst_slice_pitch, update_w, update_h, update_d);
else
format->upload(src_mem, converted_mem, src_row_pitch, src_slice_pitch,
dst_row_pitch, dst_slice_pitch, update_w, update_h, update_d);
context_unmap_bo_address(context, &bo, GL_PIXEL_UNPACK_BUFFER);
bo.buffer_object = 0;

View File

@ -339,6 +339,230 @@ static const struct wined3d_format_base_flags format_base_flags[] =
{WINED3DFMT_RESZ, WINED3DFMT_FLAG_EXTENSION},
};
static void rgb888_from_rgb565(WORD rgb565, BYTE *r, BYTE *g, BYTE *b)
{
BYTE c;
/* (2⁸ - 1) / (2⁵ - 1) ≈ 2⁸ / 2⁵ + 2⁸ / 2¹⁰
* (2 - 1) / (2 - 1) 2 / 2 + 2 / 2¹² */
c = rgb565 >> 11;
*r = (c << 3) + (c >> 2);
c = (rgb565 >> 5) & 0x3f;
*g = (c << 2) + (c >> 4);
c = rgb565 & 0x1f;
*b = (c << 3) + (c >> 2);
}
static void build_dxtn_colour_table(WORD colour0, WORD colour1,
DWORD colour_table[4], enum wined3d_format_id format_id)
{
unsigned int i;
struct
{
BYTE r, g, b;
} c[4];
rgb888_from_rgb565(colour0, &c[0].r, &c[0].g, &c[0].b);
rgb888_from_rgb565(colour1, &c[1].r, &c[1].g, &c[1].b);
if (format_id == WINED3DFMT_BC1_UNORM && colour0 <= colour1)
{
c[2].r = (c[0].r + c[1].r) / 2;
c[2].g = (c[0].g + c[1].g) / 2;
c[2].b = (c[0].b + c[1].b) / 2;
c[3].r = 0;
c[3].g = 0;
c[3].b = 0;
}
else
{
for (i = 0; i < 2; ++i)
{
c[i + 2].r = (2 * c[i].r + c[1 - i].r) / 3;
c[i + 2].g = (2 * c[i].g + c[1 - i].g) / 3;
c[i + 2].b = (2 * c[i].b + c[1 - i].b) / 3;
}
}
for (i = 0; i < 4; ++i)
{
colour_table[i] = (c[i].r << 16) | (c[i].g << 8) | c[i].b;
}
}
static void build_dxtn_alpha_table(BYTE alpha0, BYTE alpha1, BYTE alpha_table[8])
{
unsigned int i;
alpha_table[0] = alpha0;
alpha_table[1] = alpha1;
if (alpha0 > alpha1)
{
for (i = 0; i < 6; ++i)
{
alpha_table[2 + i] = ((6 - i) * alpha0 + (i + 1) * alpha1) / 7;
}
return;
}
else
{
for (i = 0; i < 4; ++i)
{
alpha_table[2 + i] = ((4 - i) * alpha0 + (i + 1) * alpha1) / 5;
}
alpha_table[6] = 0x00;
alpha_table[7] = 0xff;
}
}
static void decompress_dxtn_block(const BYTE *src, BYTE *dst, unsigned int width,
unsigned int height, unsigned int dst_row_pitch, enum wined3d_format_id format_id)
{
const UINT64 *s = (const UINT64 *)src;
BOOL bc1_alpha = FALSE;
DWORD colour_table[4];
BYTE alpha_table[8];
UINT64 alpha_bits;
DWORD colour_bits;
unsigned int x, y;
BYTE colour_idx;
DWORD *dst_row;
BYTE alpha;
if (format_id == WINED3DFMT_BC1_UNORM)
{
WORD colour0, colour1;
alpha_bits = 0;
colour0 = s[0] & 0xffff;
colour1 = (s[0] >> 16) & 0xffff;
colour_bits = (s[0] >> 32) & 0xffffffff;
build_dxtn_colour_table(colour0, colour1, colour_table, format_id);
if (colour0 <= colour1)
bc1_alpha = TRUE;
}
else
{
alpha_bits = s[0];
if (format_id == WINED3DFMT_BC3_UNORM)
{
build_dxtn_alpha_table(alpha_bits & 0xff, (alpha_bits >> 8) & 0xff, alpha_table);
alpha_bits >>= 16;
}
colour_bits = (s[1] >> 32) & 0xffffffff;
build_dxtn_colour_table(s[1] & 0xffff, (s[1] >> 16) & 0xffff, colour_table, format_id);
}
for (y = 0; y < height; ++y)
{
dst_row = (DWORD *)&dst[y * dst_row_pitch];
for (x = 0; x < width; ++x)
{
colour_idx = (colour_bits >> (y * 8 + x * 2)) & 0x3;
switch (format_id)
{
case WINED3DFMT_BC1_UNORM:
alpha = bc1_alpha && colour_idx == 3 ? 0x00 : 0xff;
break;
case WINED3DFMT_BC2_UNORM:
alpha = (alpha_bits >> (y * 16 + x * 4)) & 0xf;
/* (2⁸ - 1) / (2⁴ - 1) ≈ 2⁸ / 2⁴ + 2⁸ / 2⁸ */
alpha |= alpha << 4;
break;
case WINED3DFMT_BC3_UNORM:
alpha = alpha_table[(alpha_bits >> (y * 12 + x * 3)) & 0x7];
break;
default:
alpha = 0xff;
break;
}
dst_row[x] = (alpha << 24) | colour_table[colour_idx];
}
}
}
static void decompress_dxtn(const BYTE *src, BYTE *dst, unsigned int src_row_pitch,
unsigned int src_slice_pitch, unsigned int dst_row_pitch, unsigned int dst_slice_pitch,
unsigned int width, unsigned int height, unsigned int depth, enum wined3d_format_id format_id)
{
unsigned int block_byte_count, block_w, block_h;
const BYTE *src_row, *src_slice = src;
BYTE *dst_row, *dst_slice = dst;
unsigned int x, y, z;
block_byte_count = format_id == WINED3DFMT_BC1_UNORM ? 8 : 16;
for (z = 0; z < depth; ++z)
{
src_row = src_slice;
dst_row = dst_slice;
for (y = 0; y < height; y += 4)
{
for (x = 0; x < width; x += 4)
{
block_w = min(width - x, 4);
block_h = min(height - y, 4);
decompress_dxtn_block(&src_row[x * (block_byte_count / 4)],
&dst_row[x * 4], block_w, block_h, dst_row_pitch, format_id);
}
src_row += src_row_pitch;
dst_row += dst_row_pitch * 4;
}
src_slice += src_slice_pitch;
dst_slice += dst_slice_pitch;
}
}
static void decompress_bc3(const BYTE *src, BYTE *dst, unsigned int src_row_pitch,
unsigned int src_slice_pitch, unsigned int dst_row_pitch, unsigned int dst_slice_pitch,
unsigned int width, unsigned int height, unsigned int depth)
{
decompress_dxtn(src, dst, src_row_pitch, src_slice_pitch, dst_row_pitch,
dst_slice_pitch, width, height, depth, WINED3DFMT_BC3_UNORM);
}
static void decompress_bc2(const BYTE *src, BYTE *dst, unsigned int src_row_pitch,
unsigned int src_slice_pitch, unsigned int dst_row_pitch, unsigned int dst_slice_pitch,
unsigned int width, unsigned int height, unsigned int depth)
{
decompress_dxtn(src, dst, src_row_pitch, src_slice_pitch, dst_row_pitch,
dst_slice_pitch, width, height, depth, WINED3DFMT_BC2_UNORM);
}
static void decompress_bc1(const BYTE *src, BYTE *dst, unsigned int src_row_pitch,
unsigned int src_slice_pitch, unsigned int dst_row_pitch, unsigned int dst_slice_pitch,
unsigned int width, unsigned int height, unsigned int depth)
{
decompress_dxtn(src, dst, src_row_pitch, src_slice_pitch, dst_row_pitch,
dst_slice_pitch, width, height, depth, WINED3DFMT_BC1_UNORM);
}
static const struct wined3d_format_decompress_info
{
enum wined3d_format_id id;
void (*decompress)(const BYTE *src, BYTE *dst, unsigned int src_row_pitch, unsigned int src_slice_pitch,
unsigned int dst_row_pitch, unsigned int dst_slice_pitch,
unsigned int width, unsigned int height, unsigned int depth);
}
format_decompress_info[] =
{
{WINED3DFMT_DXT1, decompress_bc1},
{WINED3DFMT_DXT2, decompress_bc2},
{WINED3DFMT_DXT3, decompress_bc2},
{WINED3DFMT_DXT4, decompress_bc3},
{WINED3DFMT_DXT5, decompress_bc3},
{WINED3DFMT_BC1_UNORM, decompress_bc1},
{WINED3DFMT_BC2_UNORM, decompress_bc2},
{WINED3DFMT_BC3_UNORM, decompress_bc3},
};
struct wined3d_format_block_info
{
enum wined3d_format_id id;
@ -437,6 +661,9 @@ struct wined3d_format_texture_info
void (*download)(const BYTE *src, BYTE *dst, unsigned int src_row_pitch, unsigned int src_slice_pitch,
unsigned int dst_row_pitch, unsigned int dst_slice_pitch,
unsigned int width, unsigned int height, unsigned int depth);
void (*decompress)(const BYTE *src, BYTE *dst, unsigned int src_row_pitch, unsigned int src_slice_pitch,
unsigned int dst_row_pitch, unsigned int dst_slice_pitch,
unsigned int width, unsigned int height, unsigned int depth);
};
static void convert_l4a4_unorm(const BYTE *src, BYTE *dst, UINT src_row_pitch, UINT src_slice_pitch,
@ -1897,6 +2124,34 @@ static BOOL init_format_block_info(struct wined3d_gl_info *gl_info)
return TRUE;
}
/* Most compressed formats are not supported for 3D textures by OpenGL.
*
* In the case of the S3TC/DXTn formats, NV_texture_compression_vtc provides
* these formats for 3D textures, but unfortunately the block layout is
* different from the one used by Direct3D.
*
* Since applications either don't check format availability at all before
* using these, or refuse to run without them, we decompress them on upload.
*
* Affected applications include "Heroes VI", "From Dust", "Halo Online" and
* "Eldorado". */
static BOOL init_format_decompress_info(struct wined3d_gl_info *gl_info)
{
struct wined3d_format *format;
unsigned int i;
for (i = 0; i < ARRAY_SIZE(format_decompress_info); ++i)
{
if (!(format = get_format_internal(gl_info, format_decompress_info[i].id)))
return FALSE;
format->flags[WINED3D_GL_RES_TYPE_TEX_3D] |= WINED3DFMT_FLAG_DECOMPRESS;
format->decompress = format_decompress_info[i].decompress;
}
return TRUE;
}
static GLenum wined3d_gl_type_to_enum(enum wined3d_gl_resource_type type)
{
switch (type)
@ -3390,34 +3645,8 @@ static void apply_format_fixups(struct wined3d_adapter *adapter, struct wined3d_
}
}
/* GL_EXT_texture_compression_s3tc does not support 3D textures. Some Windows drivers
* for dx9 GPUs support it, some do not, so not supporting DXTn volumes is OK for d3d9.
*
* Note that GL_NV_texture_compression_vtc adds this functionality to OpenGL, but the
* block layout is not compatible with the one used by d3d. See volume_dxt5_test. */
idx = get_format_idx(WINED3DFMT_DXT1);
gl_info->formats[idx].flags[WINED3D_GL_RES_TYPE_TEX_3D] &= ~WINED3DFMT_FLAG_TEXTURE;
idx = get_format_idx(WINED3DFMT_DXT2);
gl_info->formats[idx].flags[WINED3D_GL_RES_TYPE_TEX_3D] &= ~WINED3DFMT_FLAG_TEXTURE;
idx = get_format_idx(WINED3DFMT_DXT3);
gl_info->formats[idx].flags[WINED3D_GL_RES_TYPE_TEX_3D] &= ~WINED3DFMT_FLAG_TEXTURE;
idx = get_format_idx(WINED3DFMT_DXT4);
gl_info->formats[idx].flags[WINED3D_GL_RES_TYPE_TEX_3D] &= ~WINED3DFMT_FLAG_TEXTURE;
idx = get_format_idx(WINED3DFMT_DXT5);
gl_info->formats[idx].flags[WINED3D_GL_RES_TYPE_TEX_3D] &= ~WINED3DFMT_FLAG_TEXTURE;
idx = get_format_idx(WINED3DFMT_BC1_UNORM);
gl_info->formats[idx].flags[WINED3D_GL_RES_TYPE_TEX_3D] &= ~WINED3DFMT_FLAG_TEXTURE;
idx = get_format_idx(WINED3DFMT_BC1_UNORM_SRGB);
gl_info->formats[idx].flags[WINED3D_GL_RES_TYPE_TEX_3D] &= ~WINED3DFMT_FLAG_TEXTURE;
idx = get_format_idx(WINED3DFMT_BC2_UNORM);
gl_info->formats[idx].flags[WINED3D_GL_RES_TYPE_TEX_3D] &= ~WINED3DFMT_FLAG_TEXTURE;
idx = get_format_idx(WINED3DFMT_BC2_UNORM_SRGB);
gl_info->formats[idx].flags[WINED3D_GL_RES_TYPE_TEX_3D] &= ~WINED3DFMT_FLAG_TEXTURE;
idx = get_format_idx(WINED3DFMT_BC3_UNORM);
gl_info->formats[idx].flags[WINED3D_GL_RES_TYPE_TEX_3D] &= ~WINED3DFMT_FLAG_TEXTURE;
idx = get_format_idx(WINED3DFMT_BC3_UNORM_SRGB);
gl_info->formats[idx].flags[WINED3D_GL_RES_TYPE_TEX_3D] &= ~WINED3DFMT_FLAG_TEXTURE;
/* Similarly with ATI1N / ATI2N and GL_ARB_texture_compression_rgtc. */
/* These formats are not supported for 3D textures. See also
* WINED3DFMT_FLAG_DECOMPRESS. */
idx = get_format_idx(WINED3DFMT_ATI1N);
gl_info->formats[idx].flags[WINED3D_GL_RES_TYPE_TEX_3D] &= ~WINED3DFMT_FLAG_TEXTURE;
idx = get_format_idx(WINED3DFMT_ATI2N);
@ -3742,6 +3971,7 @@ BOOL wined3d_adapter_init_format_info(struct wined3d_adapter *adapter, struct wi
if (!init_format_base_info(gl_info)) return FALSE;
if (!init_format_block_info(gl_info)) goto fail;
if (!init_format_decompress_info(gl_info)) goto fail;
if (!ctx) /* WINED3D_NO3D */
return TRUE;

View File

@ -3081,6 +3081,8 @@ HRESULT resource_init(struct wined3d_resource *resource, struct wined3d_device *
void resource_unload(struct wined3d_resource *resource) DECLSPEC_HIDDEN;
BOOL wined3d_resource_allocate_sysmem(struct wined3d_resource *resource) DECLSPEC_HIDDEN;
void wined3d_resource_free_sysmem(struct wined3d_resource *resource) DECLSPEC_HIDDEN;
const struct wined3d_format *wined3d_resource_get_decompress_format(struct wined3d_resource *resource,
const struct wined3d_context *context) DECLSPEC_HIDDEN;
GLbitfield wined3d_resource_gl_map_flags(DWORD d3d_flags) DECLSPEC_HIDDEN;
GLenum wined3d_resource_gl_legacy_map_flags(DWORD d3d_flags) DECLSPEC_HIDDEN;
BOOL wined3d_resource_is_offscreen(struct wined3d_resource *resource) DECLSPEC_HIDDEN;
@ -4231,6 +4233,7 @@ extern enum wined3d_format_id pixelformat_for_depth(DWORD depth) DECLSPEC_HIDDEN
#define WINED3DFMT_FLAG_EXTENSION 0x00000020
#define WINED3DFMT_FLAG_FBO_ATTACHABLE 0x00000040
#define WINED3DFMT_FLAG_FBO_ATTACHABLE_SRGB 0x00000080
#define WINED3DFMT_FLAG_DECOMPRESS 0x00000100
#define WINED3DFMT_FLAG_FLOAT 0x00000200
#define WINED3DFMT_FLAG_BUMPMAP 0x00000400
#define WINED3DFMT_FLAG_SRGB_READ 0x00000800
@ -4304,6 +4307,9 @@ struct wined3d_format
void (*download)(const BYTE *src, BYTE *dst, unsigned int src_row_pitch, unsigned int src_slice_pitch,
unsigned int dst_row_pitch, unsigned dst_slice_pitch,
unsigned int width, unsigned int height, unsigned int depth);
void (*decompress)(const BYTE *src, BYTE *dst, unsigned int src_row_pitch, unsigned int src_slice_pitch,
unsigned int dst_row_pitch, unsigned dst_slice_pitch,
unsigned int width, unsigned int height, unsigned int depth);
enum wined3d_format_id typeless_id;
GLenum gl_view_class;