texconv: Added support for DXT5nm and RXGB DXT5 format variants (#362)

This commit is contained in:
Chuck Walbourn 2023-06-12 14:34:32 -07:00 коммит произвёл GitHub
Родитель 2395b804ff
Коммит a2a4ffd33c
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
3 изменённых файлов: 224 добавлений и 91 удалений

Просмотреть файл

@ -221,6 +221,9 @@ namespace DirectX
DDS_FLAGS_FORCE_DX9_LEGACY = 0x40000,
// Force use of legacy header for DDS writer (will fail if unable to write as such)
DDS_FLAGS_FORCE_DXT5_RXGB = 0x80000,
// Force use of 'RXGB' instead of 'DXT5' for DDS write of BC3_UNORM data
DDS_FLAGS_ALLOW_LARGE_FILES = 0x1000000,
// Enables the loader to read large dimension .dds files (i.e. greater than known hardware requirements)
};

Просмотреть файл

@ -66,6 +66,19 @@ namespace
{ DXGI_FORMAT_BC2_UNORM, CONV_FLAGS_PMALPHA, DDSPF_DXT2 }, // D3DFMT_DXT2
{ DXGI_FORMAT_BC3_UNORM, CONV_FLAGS_PMALPHA, DDSPF_DXT4 }, // D3DFMT_DXT4
// These DXT5 variants have various swizzled channels. They are returned 'as is' to the client as BC3.
{ DXGI_FORMAT_BC3_UNORM, CONV_FLAGS_NONE, { sizeof(DDS_PIXELFORMAT), DDS_FOURCC, MAKEFOURCC('A', '2', 'D', '5'), 0, 0, 0, 0, 0 } },
{ DXGI_FORMAT_BC3_UNORM, CONV_FLAGS_NONE, { sizeof(DDS_PIXELFORMAT), DDS_FOURCC, MAKEFOURCC('x', 'G', 'B', 'R'), 0, 0, 0, 0, 0 } },
{ DXGI_FORMAT_BC3_UNORM, CONV_FLAGS_NONE, { sizeof(DDS_PIXELFORMAT), DDS_FOURCC, MAKEFOURCC('R', 'x', 'B', 'G'), 0, 0, 0, 0, 0 } },
{ DXGI_FORMAT_BC3_UNORM, CONV_FLAGS_NONE, { sizeof(DDS_PIXELFORMAT), DDS_FOURCC, MAKEFOURCC('R', 'B', 'x', 'G'), 0, 0, 0, 0, 0 } },
{ DXGI_FORMAT_BC3_UNORM, CONV_FLAGS_NONE, { sizeof(DDS_PIXELFORMAT), DDS_FOURCC, MAKEFOURCC('x', 'R', 'B', 'G'), 0, 0, 0, 0, 0 } },
{ DXGI_FORMAT_BC3_UNORM, CONV_FLAGS_NONE, { sizeof(DDS_PIXELFORMAT), DDS_FOURCC, MAKEFOURCC('R', 'G', 'x', 'B'), 0, 0, 0, 0, 0 } },
{ DXGI_FORMAT_BC3_UNORM, CONV_FLAGS_NONE, { sizeof(DDS_PIXELFORMAT), DDS_FOURCC, MAKEFOURCC('x', 'G', 'x', 'R'), 0, 0, 0, 0, 0 } },
{ DXGI_FORMAT_BC3_UNORM, CONV_FLAGS_NONE, { sizeof(DDS_PIXELFORMAT), DDS_FOURCC, MAKEFOURCC('G', 'X', 'R', 'B'), 0, 0, 0, 0, 0 } },
{ DXGI_FORMAT_BC3_UNORM, CONV_FLAGS_NONE, { sizeof(DDS_PIXELFORMAT), DDS_FOURCC, MAKEFOURCC('G', 'R', 'X', 'B'), 0, 0, 0, 0, 0 } },
{ DXGI_FORMAT_BC3_UNORM, CONV_FLAGS_NONE, { sizeof(DDS_PIXELFORMAT), DDS_FOURCC, MAKEFOURCC('R', 'X', 'G', 'B'), 0, 0, 0, 0, 0 } },
{ DXGI_FORMAT_BC3_UNORM, CONV_FLAGS_NONE, { sizeof(DDS_PIXELFORMAT), DDS_FOURCC, MAKEFOURCC('B', 'R', 'G', 'X'), 0, 0, 0, 0, 0 } },
{ DXGI_FORMAT_BC4_UNORM, CONV_FLAGS_NONE, DDSPF_BC4_UNORM },
{ DXGI_FORMAT_BC4_SNORM, CONV_FLAGS_NONE, DDSPF_BC4_SNORM },
{ DXGI_FORMAT_BC5_UNORM, CONV_FLAGS_NONE, DDSPF_BC5_UNORM },
@ -73,6 +86,7 @@ namespace
{ DXGI_FORMAT_BC4_UNORM, CONV_FLAGS_NONE, { sizeof(DDS_PIXELFORMAT), DDS_FOURCC, MAKEFOURCC('A', 'T', 'I', '1'), 0, 0, 0, 0, 0 } },
{ DXGI_FORMAT_BC5_UNORM, CONV_FLAGS_NONE, { sizeof(DDS_PIXELFORMAT), DDS_FOURCC, MAKEFOURCC('A', 'T', 'I', '2'), 0, 0, 0, 0, 0 } },
{ DXGI_FORMAT_BC5_UNORM, CONV_FLAGS_NONE, { sizeof(DDS_PIXELFORMAT), DDS_FOURCC, MAKEFOURCC('A', '2', 'X', 'Y'), 0, 0, 0, 0, 0 } },
{ DXGI_FORMAT_BC6H_UF16, CONV_FLAGS_NONE, { sizeof(DDS_PIXELFORMAT), DDS_FOURCC, MAKEFOURCC('B', 'C', '6', 'H'), 0, 0, 0, 0, 0 } },
{ DXGI_FORMAT_BC7_UNORM, CONV_FLAGS_NONE, { sizeof(DDS_PIXELFORMAT), DDS_FOURCC, MAKEFOURCC('B', 'C', '7', 'L'), 0, 0, 0, 0, 0 } },
@ -614,7 +628,6 @@ HRESULT DirectX::EncodeDDSHeader(
case DXGI_FORMAT_G8R8_G8B8_UNORM: memcpy(&ddpf, &DDSPF_G8R8_G8B8, sizeof(DDS_PIXELFORMAT)); break;
case DXGI_FORMAT_BC1_UNORM: memcpy(&ddpf, &DDSPF_DXT1, sizeof(DDS_PIXELFORMAT)); break;
case DXGI_FORMAT_BC2_UNORM: memcpy(&ddpf, metadata.IsPMAlpha() ? (&DDSPF_DXT2) : (&DDSPF_DXT3), sizeof(DDS_PIXELFORMAT)); break;
case DXGI_FORMAT_BC3_UNORM: memcpy(&ddpf, metadata.IsPMAlpha() ? (&DDSPF_DXT4) : (&DDSPF_DXT5), sizeof(DDS_PIXELFORMAT)); break;
case DXGI_FORMAT_BC4_SNORM: memcpy(&ddpf, &DDSPF_BC4_SNORM, sizeof(DDS_PIXELFORMAT)); break;
case DXGI_FORMAT_BC5_SNORM: memcpy(&ddpf, &DDSPF_BC5_SNORM, sizeof(DDS_PIXELFORMAT)); break;
case DXGI_FORMAT_B5G6R5_UNORM: memcpy(&ddpf, &DDSPF_R5G6B5, sizeof(DDS_PIXELFORMAT)); break;
@ -627,6 +640,14 @@ HRESULT DirectX::EncodeDDSHeader(
case DXGI_FORMAT_B4G4R4A4_UNORM: memcpy(&ddpf, &DDSPF_A4R4G4B4, sizeof(DDS_PIXELFORMAT)); break; // DXGI 1.2
case DXGI_FORMAT_YUY2: memcpy(&ddpf, &DDSPF_YUY2, sizeof(DDS_PIXELFORMAT)); break; // DXGI 1.2
case DXGI_FORMAT_BC3_UNORM:
memcpy(&ddpf, metadata.IsPMAlpha() ? (&DDSPF_DXT4) : (&DDSPF_DXT5), sizeof(DDS_PIXELFORMAT));
if (flags & DDS_FLAGS_FORCE_DXT5_RXGB)
{
ddpf.fourCC = MAKEFOURCC('R', 'X', 'G', 'B');
}
break;
// Legacy D3DX formats using D3DFMT enum value as FourCC
case DXGI_FORMAT_R32G32B32A32_FLOAT:
ddpf.size = sizeof(DDS_PIXELFORMAT); ddpf.flags = DDS_FOURCC; ddpf.fourCC = 116; // D3DFMT_A32B32G32R32F

Просмотреть файл

@ -147,6 +147,12 @@ namespace
ROTATE_P3D65_TO_709,
};
enum
{
FORMAT_DXT5_NM = 1,
FORMAT_DXT5_RXGB,
};
static_assert(OPT_MAX <= 64, "dwOptions is a unsigned int bitfield");
struct SConversion
@ -338,6 +344,15 @@ namespace
{ nullptr, DXGI_FORMAT_UNKNOWN }
};
const SValue<uint32_t> g_pSpecialFormats[] =
{
{ L"BC3n", FORMAT_DXT5_NM },
{ L"DXT5nm", FORMAT_DXT5_NM },
{ L"RXGB", FORMAT_DXT5_RXGB },
{ nullptr, 0 }
};
const SValue<uint32_t> g_pReadOnlyFormats[] =
{
DEFFMT(R32G32B32A32_TYPELESS),
@ -997,6 +1012,8 @@ namespace
PrintList(13, g_pFormats);
wprintf(L" ");
PrintList(13, g_pFormatAliases);
wprintf(L" ");
PrintList(13, g_pSpecialFormats);
wprintf(L"\n <filter>: ");
PrintList(13, g_pFilters);
@ -1428,6 +1445,8 @@ int __cdecl wmain(_In_ int argc, _In_z_count_(argc) wchar_t* argv[])
float paperWhiteNits = 200.f;
float preserveAlphaCoverageRef = 0.0f;
bool keepRecursiveDirs = false;
bool dxt5nm = false;
bool dxt5rxgb = false;
uint32_t swizzleElements[4] = { 0, 1, 2, 3 };
uint32_t zeroElements[4] = {};
uint32_t oneElements[4] = {};
@ -1581,10 +1600,24 @@ int __cdecl wmain(_In_ int argc, _In_z_count_(argc) wchar_t* argv[])
format = static_cast<DXGI_FORMAT>(LookupByName(pValue, g_pFormatAliases));
if (!format)
{
wprintf(L"Invalid value specified with -f (%ls)\n", pValue);
wprintf(L"\n");
PrintUsage();
return 1;
switch (LookupByName(pValue, g_pSpecialFormats))
{
case FORMAT_DXT5_NM:
format = DXGI_FORMAT_BC3_UNORM;
dxt5nm = true;
break;
case FORMAT_DXT5_RXGB:
format = DXGI_FORMAT_BC3_UNORM;
dxt5rxgb = true;
break;
default:
wprintf(L"Invalid value specified with -f (%ls)\n", pValue);
wprintf(L"\n");
PrintUsage();
return 1;
}
}
}
break;
@ -3454,36 +3487,12 @@ int __cdecl wmain(_In_ int argc, _In_z_count_(argc) wchar_t* argv[])
}
// --- Compress ----------------------------------------------------------------
if (IsCompressed(tformat) && (FileType == CODEC_DDS))
if (FileType == CODEC_DDS)
{
if (cimage && (cimage->GetMetadata().format == tformat))
if (dxt5nm || dxt5rxgb)
{
// We never changed the image and it was already compressed in our desired format, use original data
image.reset(cimage.release());
auto& tinfo = image->GetMetadata();
if ((tinfo.width % 4) != 0 || (tinfo.height % 4) != 0)
{
non4bc = true;
}
info.format = tinfo.format;
assert(info.width == tinfo.width);
assert(info.height == tinfo.height);
assert(info.depth == tinfo.depth);
assert(info.arraySize == tinfo.arraySize);
assert(info.mipLevels == tinfo.mipLevels);
assert(info.miscFlags == tinfo.miscFlags);
assert(info.dimension == tinfo.dimension);
}
else
{
cimage.reset();
auto img = image->GetImage(0, 0, 0);
assert(img);
const size_t nimg = image->GetImageCount();
// Prepare for DXT5nm/RXGB
assert(tformat == DXGI_FORMAT_BC3_UNORM);
std::unique_ptr<ScratchImage> timage(new (std::nothrow) ScratchImage);
if (!timage)
@ -3492,93 +3501,188 @@ int __cdecl wmain(_In_ int argc, _In_z_count_(argc) wchar_t* argv[])
return 1;
}
bool bc6hbc7 = false;
switch (tformat)
if (dxt5nm)
{
case DXGI_FORMAT_BC6H_TYPELESS:
case DXGI_FORMAT_BC6H_UF16:
case DXGI_FORMAT_BC6H_SF16:
case DXGI_FORMAT_BC7_TYPELESS:
case DXGI_FORMAT_BC7_UNORM:
case DXGI_FORMAT_BC7_UNORM_SRGB:
bc6hbc7 = true;
{
static bool s_tryonce = false;
if (!s_tryonce)
hr = TransformImage(image->GetImages(), image->GetImageCount(), image->GetMetadata(),
[=](XMVECTOR* outPixels, const XMVECTOR* inPixels, size_t w, size_t y)
{
s_tryonce = true;
UNREFERENCED_PARAMETER(y);
if (!(dwOptions & (uint64_t(1) << OPT_NOGPU)))
for (size_t j = 0; j < w; ++j)
{
if (!CreateDevice(adapter, pDevice.GetAddressOf()))
wprintf(L"\nWARNING: DirectCompute is not available, using BC6H / BC7 CPU codec\n");
outPixels[j] = XMVectorPermute<4, 1, 5, 0>(inPixels[j], g_XMIdentityR0);
}
else
{
wprintf(L"\nWARNING: using BC6H / BC7 CPU codec\n");
}
}
}, *timage);
if (FAILED(hr))
{
wprintf(L" FAILED [DXT5nm] (%08X%ls)\n", static_cast<unsigned int>(hr), GetErrorDesc(hr));
return 1;
}
break;
default:
break;
}
TEX_COMPRESS_FLAGS cflags = dwCompress;
#ifdef _OPENMP
if (!(dwOptions & (uint64_t(1) << OPT_FORCE_SINGLEPROC)))
{
cflags |= TEX_COMPRESS_PARALLEL;
}
#endif
if ((img->width % 4) != 0 || (img->height % 4) != 0)
{
non4bc = true;
}
if (bc6hbc7 && pDevice)
{
hr = Compress(pDevice.Get(), img, nimg, info, tformat, dwCompress | dwSRGB, alphaWeight, *timage);
}
else
{
hr = Compress(img, nimg, info, tformat, cflags | dwSRGB, alphaThreshold, *timage);
}
if (FAILED(hr))
{
wprintf(L" FAILED [compress] (%08X%ls)\n", static_cast<unsigned int>(hr), GetErrorDesc(hr));
retVal = 1;
continue;
hr = TransformImage(image->GetImages(), image->GetImageCount(), image->GetMetadata(),
[=](XMVECTOR* outPixels, const XMVECTOR* inPixels, size_t w, size_t y)
{
UNREFERENCED_PARAMETER(y);
for (size_t j = 0; j < w; ++j)
{
outPixels[j] = XMVectorSwizzle<3, 1, 2, 0>(inPixels[j]);
}
}, *timage);
if (FAILED(hr))
{
wprintf(L" FAILED [DXT5 RXGB] (%08X%ls)\n", static_cast<unsigned int>(hr), GetErrorDesc(hr));
return 1;
}
}
#ifndef NDEBUG
auto& tinfo = timage->GetMetadata();
#endif
info.format = tinfo.format;
assert(info.width == tinfo.width);
assert(info.height == tinfo.height);
assert(info.depth == tinfo.depth);
assert(info.arraySize == tinfo.arraySize);
assert(info.mipLevels == tinfo.mipLevels);
assert(info.miscFlags == tinfo.miscFlags);
assert(info.format == tinfo.format);
assert(info.dimension == tinfo.dimension);
image.swap(timage);
cimage.reset();
}
if (IsCompressed(tformat))
{
if (cimage && (cimage->GetMetadata().format == tformat))
{
// We never changed the image and it was already compressed in our desired format, use original data
image.reset(cimage.release());
auto& tinfo = image->GetMetadata();
if ((tinfo.width % 4) != 0 || (tinfo.height % 4) != 0)
{
non4bc = true;
}
info.format = tinfo.format;
assert(info.width == tinfo.width);
assert(info.height == tinfo.height);
assert(info.depth == tinfo.depth);
assert(info.arraySize == tinfo.arraySize);
assert(info.mipLevels == tinfo.mipLevels);
assert(info.miscFlags == tinfo.miscFlags);
assert(info.dimension == tinfo.dimension);
}
else
{
cimage.reset();
auto img = image->GetImage(0, 0, 0);
assert(img);
const size_t nimg = image->GetImageCount();
std::unique_ptr<ScratchImage> timage(new (std::nothrow) ScratchImage);
if (!timage)
{
wprintf(L"\nERROR: Memory allocation failed\n");
return 1;
}
bool bc6hbc7 = false;
switch (tformat)
{
case DXGI_FORMAT_BC6H_TYPELESS:
case DXGI_FORMAT_BC6H_UF16:
case DXGI_FORMAT_BC6H_SF16:
case DXGI_FORMAT_BC7_TYPELESS:
case DXGI_FORMAT_BC7_UNORM:
case DXGI_FORMAT_BC7_UNORM_SRGB:
bc6hbc7 = true;
{
static bool s_tryonce = false;
if (!s_tryonce)
{
s_tryonce = true;
if (!(dwOptions & (uint64_t(1) << OPT_NOGPU)))
{
if (!CreateDevice(adapter, pDevice.GetAddressOf()))
wprintf(L"\nWARNING: DirectCompute is not available, using BC6H / BC7 CPU codec\n");
}
else
{
wprintf(L"\nWARNING: using BC6H / BC7 CPU codec\n");
}
}
}
break;
default:
break;
}
TEX_COMPRESS_FLAGS cflags = dwCompress;
#ifdef _OPENMP
if (!(dwOptions & (uint64_t(1) << OPT_FORCE_SINGLEPROC)))
{
cflags |= TEX_COMPRESS_PARALLEL;
}
#endif
if ((img->width % 4) != 0 || (img->height % 4) != 0)
{
non4bc = true;
}
if (bc6hbc7 && pDevice)
{
hr = Compress(pDevice.Get(), img, nimg, info, tformat, dwCompress | dwSRGB, alphaWeight, *timage);
}
else
{
hr = Compress(img, nimg, info, tformat, cflags | dwSRGB, alphaThreshold, *timage);
}
if (FAILED(hr))
{
wprintf(L" FAILED [compress] (%08X%ls)\n", static_cast<unsigned int>(hr), GetErrorDesc(hr));
retVal = 1;
continue;
}
auto& tinfo = timage->GetMetadata();
info.format = tinfo.format;
assert(info.width == tinfo.width);
assert(info.height == tinfo.height);
assert(info.depth == tinfo.depth);
assert(info.arraySize == tinfo.arraySize);
assert(info.mipLevels == tinfo.mipLevels);
assert(info.miscFlags == tinfo.miscFlags);
assert(info.dimension == tinfo.dimension);
image.swap(timage);
}
}
}
else
{
cimage.reset();
}
cimage.reset();
// --- Set alpha mode ----------------------------------------------------------
if (HasAlpha(info.format)
&& info.format != DXGI_FORMAT_A8_UNORM)
{
if (image->IsAlphaAllOpaque())
if (dxt5nm || dxt5rxgb)
{
info.SetAlphaMode(TEX_ALPHA_MODE_CUSTOM);
}
else if (image->IsAlphaAllOpaque())
{
info.SetAlphaMode(TEX_ALPHA_MODE_OPAQUE);
}
@ -3693,6 +3797,11 @@ int __cdecl wmain(_In_ int argc, _In_z_count_(argc) wchar_t* argv[])
}
else if (dwOptions & (uint64_t(1) << OPT_USE_DX9))
{
if (dxt5rxgb)
{
ddsFlags |= DDS_FLAGS_FORCE_DXT5_RXGB;
}
ddsFlags |= DDS_FLAGS_FORCE_DX9_LEGACY;
}