[PATCH v6 1/2] wined3d: convert 3D DXTn textures

Connor McAdams conmanx360 at gmail.com
Wed Jul 18 17:20:53 CDT 2018


This patch adds the ability to decompress DXTn textures, and is meant to
be used for DXTn 3D textures because they are not supported properly in
drivers.

Signed-off-by: Connor McAdams <conmanx360 at gmail.com>
---
 dlls/wined3d/texture.c         |  51 +++++++-
 dlls/wined3d/utils.c           | 275 +++++++++++++++++++++++++++++++++++++----
 dlls/wined3d/wined3d_private.h |   4 +
 3 files changed, 303 insertions(+), 27 deletions(-)

diff --git a/dlls/wined3d/texture.c b/dlls/wined3d/texture.c
index c316906..34e9e56 100644
--- a/dlls/wined3d/texture.c
+++ b/dlls/wined3d/texture.c
@@ -567,6 +567,8 @@ static void wined3d_texture_allocate_gl_mutable_storage(struct wined3d_texture *
     unsigned int level, level_count, layer, layer_count;
     GLsizei width, height, depth;
     GLenum target;
+    GLint gl_format;
+    GLint gl_type;
 
     level_count = texture->level_count;
     if (texture->target == GL_TEXTURE_1D_ARRAY || texture->target == GL_TEXTURE_2D_ARRAY)
@@ -578,6 +580,18 @@ static void wined3d_texture_allocate_gl_mutable_storage(struct wined3d_texture *
     {
         target = wined3d_texture_get_sub_resource_target(texture, layer * level_count);
 
+        if (texture->resource.format_flags & WINED3DFMT_FLAG_DECOMPRESS)
+        {
+            gl_internal_format = GL_RGBA8;
+            gl_format = GL_BGRA;
+            gl_type = GL_UNSIGNED_INT_8_8_8_8_REV;
+        }
+        else
+        {
+            gl_format = format->glFormat;
+            gl_type = format->glType;
+        }
+
         for (level = 0; level < level_count; ++level)
         {
             width = wined3d_texture_get_level_pow2_width(texture, level);
@@ -596,19 +610,19 @@ static void wined3d_texture_allocate_gl_mutable_storage(struct wined3d_texture *
                 depth = wined3d_texture_get_level_depth(texture, level);
                 GL_EXTCALL(glTexImage3D(target, level, gl_internal_format, width, height,
                         target == GL_TEXTURE_2D_ARRAY ? texture->layer_count : depth, 0,
-                        format->glFormat, format->glType, NULL));
+                        gl_format, gl_type, NULL));
                 checkGLcall("glTexImage3D");
             }
             else if (target == GL_TEXTURE_1D)
             {
                 gl_info->gl_ops.gl.p_glTexImage1D(target, level, gl_internal_format,
-                        width, 0, format->glFormat, format->glType, NULL);
+                        width, 0, gl_format, gl_type, NULL);
             }
             else
             {
                 gl_info->gl_ops.gl.p_glTexImage2D(target, level, gl_internal_format, width,
                         target == GL_TEXTURE_1D_ARRAY ? texture->layer_count : height, 0,
-                        format->glFormat, format->glType, NULL);
+                        gl_format, gl_type, NULL);
                 checkGLcall("glTexImage2D");
             }
         }
@@ -624,6 +638,9 @@ static void wined3d_texture_allocate_gl_immutable_storage(struct wined3d_texture
     GLsizei height = wined3d_texture_get_level_pow2_height(texture, 0);
     GLsizei width = wined3d_texture_get_level_pow2_width(texture, 0);
 
+    if (texture->resource.format_flags & WINED3DFMT_FLAG_DECOMPRESS)
+        gl_internal_format = GL_RGBA8;
+
     switch (texture->target)
     {
         case GL_TEXTURE_3D:
@@ -1948,6 +1965,34 @@ void wined3d_texture_upload_data(struct wined3d_texture *texture, unsigned int s
         bo.addr += src_box->left * format->byte_count;
     }
 
+    if (texture->resource.format_flags & WINED3DFMT_FLAG_DECOMPRESS)
+    {
+        unsigned int dst_row_pitch, dst_slice_pitch;
+        void *src_mem;
+
+        f = *format;
+        format = wined3d_get_format(gl_info, WINED3DFMT_B8G8R8A8_UNORM, WINED3DUSAGE_TEXTURE);
+
+        wined3d_format_calculate_pitch(format, 1, update_w, update_h, &dst_row_pitch, &dst_slice_pitch);
+
+        if (!(converted_mem = heap_calloc(update_d, dst_slice_pitch)))
+        {
+            ERR("Failed to allocate upload buffer.\n");
+            return;
+        }
+        src_mem = context_map_bo_address(context, &bo, src_slice_pitch,
+                GL_PIXEL_UNPACK_BUFFER, WINED3D_MAP_READ);
+        f.decompress(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;
+        bo.addr = converted_mem;
+        src_row_pitch = dst_row_pitch;
+        src_slice_pitch = dst_slice_pitch;
+        texture->resource.format_flags &= ~WINED3DFMT_FLAG_BLOCKS;
+    }
+
     if (format->upload)
     {
         unsigned int dst_row_pitch, dst_slice_pitch;
diff --git a/dlls/wined3d/utils.c b/dlls/wined3d/utils.c
index 937c1bc..04128d4 100644
--- a/dlls/wined3d/utils.c
+++ b/dlls/wined3d/utils.c
@@ -339,6 +339,34 @@ static const struct wined3d_format_base_flags format_base_flags[] =
     {WINED3DFMT_RESZ,                 WINED3DFMT_FLAG_EXTENSION},
 };
 
+static void convert_dxt1_b8g8r8a8_unorm(const BYTE *src, BYTE *dst, UINT src_row_pitch, UINT src_slice_pitch,
+        UINT dst_row_pitch, UINT dst_slice_pitch, UINT width, UINT height, UINT depth);
+static void convert_dxt3_b8g8r8a8_unorm(const BYTE *src, BYTE *dst, UINT src_row_pitch, UINT src_slice_pitch,
+        UINT dst_row_pitch, UINT dst_slice_pitch, UINT width, UINT height, UINT depth);
+static void convert_dxt5_b8g8r8a8_unorm(const BYTE *src, BYTE *dst, UINT src_row_pitch, UINT src_slice_pitch,
+        UINT dst_row_pitch, UINT dst_slice_pitch, UINT width, UINT height, UINT depth);
+
+struct wined3d_format_decompress_info
+{
+    enum wined3d_format_id id;
+    DWORD flags;
+    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 const struct wined3d_format_decompress_info format_decompress_info[] =
+{
+    {WINED3DFMT_DXT1,      WINED3DFMT_FLAG_DECOMPRESS, convert_dxt1_b8g8r8a8_unorm},
+    {WINED3DFMT_DXT2,      WINED3DFMT_FLAG_DECOMPRESS, convert_dxt3_b8g8r8a8_unorm},
+    {WINED3DFMT_DXT3,      WINED3DFMT_FLAG_DECOMPRESS, convert_dxt3_b8g8r8a8_unorm},
+    {WINED3DFMT_DXT4,      WINED3DFMT_FLAG_DECOMPRESS, convert_dxt5_b8g8r8a8_unorm},
+    {WINED3DFMT_DXT5,      WINED3DFMT_FLAG_DECOMPRESS, convert_dxt5_b8g8r8a8_unorm},
+    {WINED3DFMT_BC1_UNORM, WINED3DFMT_FLAG_DECOMPRESS, convert_dxt1_b8g8r8a8_unorm},
+    {WINED3DFMT_BC2_UNORM, WINED3DFMT_FLAG_DECOMPRESS, convert_dxt3_b8g8r8a8_unorm},
+    {WINED3DFMT_BC3_UNORM, WINED3DFMT_FLAG_DECOMPRESS, convert_dxt5_b8g8r8a8_unorm},
+};
+
 struct wined3d_format_block_info
 {
     enum wined3d_format_id id;
@@ -437,6 +465,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,
@@ -840,6 +871,206 @@ static void convert_s8_uint_d24_float(const BYTE *src, BYTE *dst, UINT src_row_p
     }
 }
 
+static void dxtn_decompress_block(const BYTE *src, BYTE *dst, UINT width, UINT height, UINT depth,
+        UINT x_pos, UINT y_pos, UINT z_pos, UINT dst_slice_pitch, UINT64 cur_block, UINT fmt, BYTE block_offset)
+{
+    UINT64 alpha_block, alpha_index, color_block, color_index;
+    const UINT64 *source;
+    DWORD *dest;
+    DWORD alpha_lookup;
+    DWORD bgra;
+    DWORD temp;
+    DWORD i, x, y;
+    WORD color[2];
+    BYTE alpha_val, color_val;
+    BYTE alpha[8];
+    BYTE r[4];
+    BYTE g[4];
+    BYTE b[4];
+
+    source = (const UINT64 *)(src + cur_block * block_offset);
+    if (fmt == WINED3DFMT_DXT1)
+    {
+        color_block = source[0];
+        alpha_block = 0;
+    }
+    else
+    {
+        alpha_block = source[0];
+        color_block = source[1];
+    }
+
+    color[0] = color_block & 0xffff;
+    color[1] = (color_block >> 16) & 0xffff;
+    for (i = 0; i < 2; ++i)
+    {
+        temp = (color[i] >> 11) * 255 + 16;
+        r[i] = (temp / 32 + temp) / 32;
+        temp = ((color[i] >> 5) & 0x3f) * 255 + 32;
+        g[i] = (temp / 64 + temp) / 64;
+        temp = (color[i] & 0x1f) * 255 + 16;
+        b[i] = (temp / 32 + temp) / 32;
+    }
+
+    if (fmt == WINED3DFMT_DXT3 || fmt == WINED3DFMT_DXT5 || color[0] > color[1])
+    {
+        for (i = 0; i < 2; ++i)
+        {
+            r[2 + i] = (2 * r[0 + i] + r[1 - i]) / 3;
+            g[2 + i] = (2 * g[0 + i] + g[1 - i]) / 3;
+            b[2 + i] = (2 * b[0 + i] + b[1 - i]) / 3;
+        }
+    }
+    else if (fmt == WINED3DFMT_DXT1 && color[0] <= color[1])
+    {
+        r[2] = (r[0] + r[1]) / 2;
+        g[2] = (g[0] + g[1]) / 2;
+        b[2] = (b[0] + b[1]) / 2;
+
+        r[3] = 0;
+        g[3] = 0;
+        b[3] = 0;
+    }
+
+    switch (fmt)
+    {
+        case WINED3DFMT_DXT1:
+            alpha_index = 0;
+            for (i = 0; i < ARRAY_SIZE(alpha); ++i)
+                alpha[i] = 255;
+            if (color[0] <= color[1])
+                alpha[3] = 0;
+            break;
+        case WINED3DFMT_DXT3:
+            alpha_index = alpha_block;
+            for (i = 0; i < ARRAY_SIZE(alpha); ++i)
+                alpha[i] = 0;
+            break;
+        case WINED3DFMT_DXT5:
+            alpha_index = (alpha_block >> 16);
+            alpha[0] = alpha_block & 0xff;
+            alpha[1] = (alpha_block >> 8) & 0xff;
+            if (alpha[0] > alpha[1])
+            {
+                for (i = 0; i < 6; ++i)
+                    alpha[2 + i] = (((6 - i) * alpha[0]) + ((1 + i) * alpha[1])) / 7;
+            }
+            else if (alpha[0] <= alpha[1])
+            {
+                for (i = 0; i < 4; ++i)
+                    alpha[2 + i] = (((4 - i) * alpha[0]) + ((1 + i) * alpha[1])) / 5;
+                alpha[6] = 0;
+                alpha[7] = 255;
+            }
+            break;
+        default:
+            alpha_index = 0;
+            break;
+    }
+
+    color_index = (color_block >> 32) & 0xffffffff;
+    dest = (DWORD *)(dst + z_pos * dst_slice_pitch);
+    for (y = 0; y < 4; ++y)
+    {
+        if (y_pos + y >= height)
+            break;
+        for (x = 0; x < 4; ++x)
+        {
+            if (x_pos + x >= width)
+                break;
+
+            color_val = 0;
+            alpha_val = 0;
+            bgra = 0;
+
+            color_val = (color_index >> (y * 8));
+            color_val = (color_val >> (x * 2)) & 0x3;
+            switch (fmt)
+            {
+                case WINED3DFMT_DXT1:
+                    alpha_val = color_val;
+                    break;
+                case WINED3DFMT_DXT3:
+                    alpha_lookup = (alpha_index >> (y * 16)) & 0xffff;
+                    alpha_val = (alpha_lookup >> (x * 4)) & 0xf;
+                    temp = alpha_val * 255 + 8;
+                    alpha[0] = (temp / 16 + temp) / 16;
+                    alpha_val = 0;
+                    break;
+                case WINED3DFMT_DXT5:
+                    alpha_lookup = (alpha_index >> (y * 12)) & 0xfff;
+                    alpha_val = (alpha_lookup >> (x * 3)) & 0x7;
+                    break;
+            }
+            bgra = ((alpha[alpha_val] << 24) | (r[color_val] << 16) | (g[color_val] << 8) | b[color_val]);
+            dest[(y_pos + y) * width + (x_pos + x)] = bgra;
+        }
+    }
+}
+
+static void convert_dxt5_b8g8r8a8_unorm(const BYTE *src, BYTE *dst, UINT src_row_pitch, UINT src_slice_pitch,
+        UINT dst_row_pitch, UINT dst_slice_pitch, UINT width, UINT height, UINT depth)
+{
+    UINT64 current_block;
+    DWORD x, y, z;
+
+    current_block = 0;
+
+    for (z = 0; z < depth; ++z)
+    {
+        for (y = 0; y < height; y += 4)
+        {
+            for (x = 0; x < width; x += 4)
+            {
+                dxtn_decompress_block(src, dst, width, height, depth, x, y, z, dst_slice_pitch, current_block, WINED3DFMT_DXT5, 16);
+                current_block++;
+            }
+        }
+    }
+}
+
+static void convert_dxt3_b8g8r8a8_unorm(const BYTE *src, BYTE *dst, UINT src_row_pitch, UINT src_slice_pitch,
+        UINT dst_row_pitch, UINT dst_slice_pitch, UINT width, UINT height, UINT depth)
+{
+    UINT64 current_block;
+    DWORD x, y, z;
+
+    current_block = 0;
+
+    for (z = 0; z < depth; ++z)
+    {
+        for (y = 0; y < height; y += 4)
+        {
+            for (x = 0; x < width; x += 4)
+            {
+                dxtn_decompress_block(src, dst, width, height, depth, x, y, z, dst_slice_pitch, current_block, WINED3DFMT_DXT3, 16);
+                current_block++;
+            }
+        }
+    }
+}
+
+static void convert_dxt1_b8g8r8a8_unorm(const BYTE *src, BYTE *dst, UINT src_row_pitch, UINT src_slice_pitch,
+        UINT dst_row_pitch, UINT dst_slice_pitch, UINT width, UINT height, UINT depth)
+{
+    UINT64 current_block;
+    DWORD x, y, z;
+
+    current_block = 0;
+
+    for (z = 0; z < depth; ++z)
+    {
+        for (y = 0; y < height; y += 4)
+        {
+            for (x = 0; x < width; x += 4)
+            {
+                dxtn_decompress_block(src, dst, width, height, depth, x, y, z, dst_slice_pitch, current_block, WINED3DFMT_DXT1, 8);
+                current_block++;
+            }
+        }
+    }
+}
+
 static void x8_d24_unorm_upload(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,
@@ -1066,7 +1297,7 @@ static const struct wined3d_format_texture_info format_texture_info[] =
     /* format id                        gl_internal                       gl_srgb_internal                      gl_rt_internal
             gl_format                   gl_type                           conv_byte_count
             flags
-            extension                   upload                            download */
+            extension                   upload                            download                              decompress */
     /* FourCC formats */
     /* GL_APPLE_ycbcr_422 claims that its '2YUV' format, which is supported via the UNSIGNED_SHORT_8_8_REV_APPLE type
      * is equivalent to 'UYVY' format on Windows, and the 'YUVS' via UNSIGNED_SHORT_8_8_APPLE equates to 'YUY2'. The
@@ -1897,6 +2128,23 @@ static BOOL init_format_block_info(struct wined3d_gl_info *gl_info)
     return TRUE;
 }
 
+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] |= format_decompress_info[i].flags;
+        format->decompress = format_decompress_info[i].decompress;
+    }
+
+    return TRUE;
+}
+
 static GLenum wined3d_gl_type_to_enum(enum wined3d_gl_resource_type type)
 {
     switch (type)
@@ -3394,29 +3642,7 @@ static void apply_format_fixups(struct wined3d_adapter *adapter, struct wined3d_
      * 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;
+     * block layout is not compatible with the one used by d3d. See volume_dxtn_test. */
     /* Similarly with ATI1N / ATI2N and GL_ARB_texture_compression_rgtc. */
     idx = get_format_idx(WINED3DFMT_ATI1N);
     gl_info->formats[idx].flags[WINED3D_GL_RES_TYPE_TEX_3D] &= ~WINED3DFMT_FLAG_TEXTURE;
@@ -3742,6 +3968,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 9aa7081..b1d8b24 100644
--- a/dlls/wined3d/wined3d_private.h
+++ b/dlls/wined3d/wined3d_private.h
@@ -4247,6 +4247,7 @@ extern enum wined3d_format_id pixelformat_for_depth(DWORD depth) DECLSPEC_HIDDEN
 #define WINED3DFMT_FLAG_BLOCKS_NO_VERIFY            0x00100000
 #define WINED3DFMT_FLAG_INTEGER                     0x00200000
 #define WINED3DFMT_FLAG_GEN_MIPMAP                  0x00400000
+#define WINED3DFMT_FLAG_DECOMPRESS                  0x00800000
 
 struct wined3d_rational
 {
@@ -4306,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;
-- 
2.7.4




More information about the wine-devel mailing list