From: Connor McAdams conmanx360@gmail.com
Signed-off-by: Connor McAdams conmanx360@gmail.com Signed-off-by: Henri Verbeet hverbeet@codeweavers.com --- dlls/wined3d/resource.c | 9 ++ dlls/wined3d/texture.c | 40 ++++-- dlls/wined3d/utils.c | 286 +++++++++++++++++++++++++++++++++++++---- dlls/wined3d/wined3d_private.h | 6 + 4 files changed, 304 insertions(+), 37 deletions(-)
diff --git a/dlls/wined3d/resource.c b/dlls/wined3d/resource.c index b5dcdf012db..bf54245276a 100644 --- a/dlls/wined3d/resource.c +++ b/dlls/wined3d/resource.c @@ -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); +} diff --git a/dlls/wined3d/texture.c b/dlls/wined3d/texture.c index c316906a957..ebdacfa2ee0 100644 --- a/dlls/wined3d/texture.c +++ b/dlls/wined3d/texture.c @@ -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; diff --git a/dlls/wined3d/utils.c b/dlls/wined3d/utils.c index 937c1bc0df6..bac52022510 100644 --- a/dlls/wined3d/utils.c +++ b/dlls/wined3d/utils.c @@ -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⁸ - 1) / (2⁶ - 1) ≈ 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⁸) */ + 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; diff --git a/dlls/wined3d/wined3d_private.h b/dlls/wined3d/wined3d_private.h index ece41ebd06c..25f7cb2416e 100644 --- a/dlls/wined3d/wined3d_private.h +++ b/dlls/wined3d/wined3d_private.h @@ -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;