Skip to content

Wrong calculation of DDS texture mipmap sizes #3375

@d1str4ught

Description

@d1str4ught

I try to load DDS files with mipmaps, but when I create textures from them, it fails because of the mipmaps.
I used the bimg loader first, did not work, then I used https://github.com/septag/dds-ktx library.
My implementation is below.
So when I call createTexture2D, the assertion below fails:

		if (BX_ENABLED(BGFX_CONFIG_DEBUG)
		&&  NULL != _mem)
		{
			TextureInfo ti;
			calcTextureSize(ti, _width, _height, 1, false, _hasMips, _numLayers, _format);
			BX_ASSERT(ti.storageSize == _mem->size
				, "createTexture2D: Texture storage size doesn't match passed memory size (storage size: %d, memory size: %d)"
				, ti.storageSize
				, _mem->size
				);
		}

storageSize is grater then _mem->size.
I tried to debug it, and I found out that that calcTextureSize will yield a different size for around the 6th mipmap.
bimg::calcTextureSize: 37888 + 9728 + 2560 + 640 + 192 + 64
my calculation: 37888 + 9728 + 2560 + 640 + 192 + 32

Because of this, mipmaps are misaligned in memory causing strange looking textures in the game.

Here is my implementation:

#include <bgfx/platform.h>
#include <bx/file.h>
#include <bx/readerwriter.h>
#include <bx/math.h>
#include <bx/os.h>

#define DDSKTX_IMPLEMENT
#include <dds-ktx.h>

bool LoadDDSFromMemory(const uint8_t* ddsData, size_t ddsDataSize, bgfx::TextureHandle& texture, bgfx::TextureInfo& info)
{
    ddsktx_texture_info textureInfo = { 0 };
    if (!ddsktx_parse(&textureInfo, ddsData, ddsDataSize, NULL)) {
        return false;
    }

    info.format = bgfx::TextureFormat::Unknown;
    info.width = textureInfo.width;
    info.height = textureInfo.height;

    switch (textureInfo.format)
    {
        case DDSKTX_FORMAT_BC1:
            info.format = bgfx::TextureFormat::BC1;
            break;
        case DDSKTX_FORMAT_BC2:
            info.format = bgfx::TextureFormat::BC2;
            break;
        case DDSKTX_FORMAT_BC3:
            info.format = bgfx::TextureFormat::BC3;
            break;
        case DDSKTX_FORMAT_BC6H:
            info.format = bgfx::TextureFormat::BC6H;
            break;
        case DDSKTX_FORMAT_BC7:
            info.format = bgfx::TextureFormat::BC7;
            break;
        case DDSKTX_FORMAT_RGBA8:
            info.format = bgfx::TextureFormat::RGBA8;
            break;

        default:
            return false;
    }

    // Calculate total size for all mip levels
    uint32_t totalSize = 0;
    for (uint32_t mip = 0; mip < textureInfo.num_mips; mip++)
    {
        ddsktx_sub_data sub_data;
        ddsktx_get_sub(&textureInfo, &sub_data, ddsData, ddsDataSize, 0, 0, mip);
        totalSize += static_cast<uint32_t>(sub_data.size_bytes);
    }

    // Allocate memory
    const bgfx::Memory* mem = bgfx::alloc(totalSize);

    // Copy mip levels
    uint8_t* dest = mem->data;
    for (uint32_t mip = 0; mip < textureInfo.num_mips; mip++)
    {
        ddsktx_sub_data sub_data;
        ddsktx_get_sub(&textureInfo, &sub_data, ddsData, ddsDataSize, 0, 0, mip);
        memcpy(dest, sub_data.buff, sub_data.size_bytes);
        dest += sub_data.size_bytes;
    }

    // Create texture
    texture = bgfx::createTexture2D(
        static_cast<uint16_t>(textureInfo.width),
        static_cast<uint16_t>(textureInfo.height),
        textureInfo.num_mips > 1,
        textureInfo.num_layers,
        info.format,
        BGFX_TEXTURE_NONE,
        mem
    );

    return bgfx::isValid(texture);
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions