texdiag support for diff

This commit is contained in:
Chuck Walbourn 2016-09-27 00:24:19 -07:00
parent 9fb5335cac
commit 9ea3ed1b4a

View File

@ -37,6 +37,7 @@ enum COMMANDS
CMD_INFO = 1,
CMD_ANALYZE,
CMD_COMPARE,
CMD_DIFF,
CMD_DUMPBC,
CMD_MAX
};
@ -44,9 +45,12 @@ enum COMMANDS
enum OPTIONS
{
OPT_RECURSIVE = 1,
OPT_FORMAT,
OPT_FILTER,
OPT_DDS_DWORD_ALIGN,
OPT_DDS_BAD_DXTN_TAILS,
OPT_OUTPUTFILE,
OPT_OVERWRITE,
OPT_NOLOGO,
OPT_TYPELESS_UNORM,
OPT_TYPELESS_FLOAT,
@ -78,6 +82,7 @@ const SValue g_pCommands[] =
{ L"info", CMD_INFO },
{ L"analyze", CMD_ANALYZE },
{ L"compare", CMD_COMPARE },
{ L"diff", CMD_DIFF },
{ L"dumpbc", CMD_DUMPBC },
{ nullptr, 0 }
};
@ -85,10 +90,13 @@ const SValue g_pCommands[] =
const SValue g_pOptions[] =
{
{ L"r", OPT_RECURSIVE },
{ L"f", OPT_FORMAT },
{ L"if", OPT_FILTER },
{ L"dword", OPT_DDS_DWORD_ALIGN },
{ L"badtails", OPT_DDS_BAD_DXTN_TAILS },
{ L"nologo", OPT_NOLOGO },
{ L"o", OPT_OUTPUTFILE },
{ L"y", OPT_OVERWRITE },
{ L"tu", OPT_TYPELESS_UNORM },
{ L"tf", OPT_TYPELESS_FLOAT },
{ L"xlum", OPT_EXPAND_LUMINANCE },
@ -101,7 +109,7 @@ const SValue g_pOptions[] =
const SValue g_pFormats[] =
{
// List does not include _TYPELESS or depth/stencil formats
// List does not include _TYPELESS, depth/stencil, or BC formats
DEFFMT(R32G32B32A32_FLOAT),
DEFFMT(R32G32B32A32_UINT),
DEFFMT(R32G32B32A32_SINT),
@ -146,20 +154,9 @@ const SValue g_pFormats[] =
DEFFMT(R8_SNORM),
DEFFMT(R8_SINT),
DEFFMT(A8_UNORM),
//DEFFMT(R1_UNORM)
DEFFMT(R9G9B9E5_SHAREDEXP),
DEFFMT(R8G8_B8G8_UNORM),
DEFFMT(G8R8_G8B8_UNORM),
DEFFMT(BC1_UNORM),
DEFFMT(BC1_UNORM_SRGB),
DEFFMT(BC2_UNORM),
DEFFMT(BC2_UNORM_SRGB),
DEFFMT(BC3_UNORM),
DEFFMT(BC3_UNORM_SRGB),
DEFFMT(BC4_UNORM),
DEFFMT(BC4_SNORM),
DEFFMT(BC5_UNORM),
DEFFMT(BC5_SNORM),
DEFFMT(B5G6R5_UNORM),
DEFFMT(B5G5R5A1_UNORM),
@ -169,10 +166,6 @@ const SValue g_pFormats[] =
DEFFMT(R10G10B10_XR_BIAS_A2_UNORM),
DEFFMT(B8G8R8A8_UNORM_SRGB),
DEFFMT(B8G8R8X8_UNORM_SRGB),
DEFFMT(BC6H_UF16),
DEFFMT(BC6H_SF16),
DEFFMT(BC7_UNORM),
DEFFMT(BC7_UNORM_SRGB),
// DXGI 1.2 formats
DEFFMT(AYUV),
@ -181,25 +174,83 @@ const SValue g_pFormats[] =
DEFFMT(YUY2),
DEFFMT(Y210),
DEFFMT(Y216),
DEFFMT(B4G4R4A4_UNORM),
{ nullptr, DXGI_FORMAT_UNKNOWN }
};
const SValue g_pReadOnlyFormats[] =
{
DEFFMT(R32G32B32A32_TYPELESS),
DEFFMT(R32G32B32_TYPELESS),
DEFFMT(R16G16B16A16_TYPELESS),
DEFFMT(R32G32_TYPELESS),
DEFFMT(R32G8X24_TYPELESS),
DEFFMT(D32_FLOAT_S8X24_UINT),
DEFFMT(R32_FLOAT_X8X24_TYPELESS),
DEFFMT(X32_TYPELESS_G8X24_UINT),
DEFFMT(R10G10B10A2_TYPELESS),
DEFFMT(R8G8B8A8_TYPELESS),
DEFFMT(R16G16_TYPELESS),
DEFFMT(R32_TYPELESS),
DEFFMT(D32_FLOAT),
DEFFMT(R24G8_TYPELESS),
DEFFMT(D24_UNORM_S8_UINT),
DEFFMT(R24_UNORM_X8_TYPELESS),
DEFFMT(X24_TYPELESS_G8_UINT),
DEFFMT(R8G8_TYPELESS),
DEFFMT(R16_TYPELESS),
DEFFMT(R8_TYPELESS),
DEFFMT(BC1_TYPELESS),
DEFFMT(BC1_UNORM),
DEFFMT(BC1_UNORM_SRGB),
DEFFMT(BC2_TYPELESS),
DEFFMT(BC2_UNORM),
DEFFMT(BC2_UNORM_SRGB),
DEFFMT(BC3_TYPELESS),
DEFFMT(BC3_UNORM),
DEFFMT(BC3_UNORM_SRGB),
DEFFMT(BC4_TYPELESS),
DEFFMT(BC4_UNORM),
DEFFMT(BC4_SNORM),
DEFFMT(BC5_TYPELESS),
DEFFMT(BC5_UNORM),
DEFFMT(BC5_SNORM),
// DXGI 1.1 formats
DEFFMT(B8G8R8A8_TYPELESS),
DEFFMT(B8G8R8X8_TYPELESS),
DEFFMT(BC6H_TYPELESS),
DEFFMT(BC6H_UF16),
DEFFMT(BC6H_SF16),
DEFFMT(BC7_TYPELESS),
DEFFMT(BC7_UNORM),
DEFFMT(BC7_UNORM_SRGB),
// DXGI 1.2 formats
DEFFMT(AI44),
DEFFMT(IA44),
DEFFMT(P8),
DEFFMT(A8P8),
DEFFMT(B4G4R4A4_UNORM),
DEFFMT(NV12),
DEFFMT(P010),
DEFFMT(P016),
DEFFMT(420_OPAQUE),
DEFFMT(NV11),
// DXGI 1.3 formats
{ L"DXGI_FORMAT_P208", DXGI_FORMAT(130) },
{ L"DXGI_FORMAT_V208", DXGI_FORMAT(131) },
{ L"DXGI_FORMAT_V408", DXGI_FORMAT(132) },
{ L"P208", DXGI_FORMAT(130) },
{ L"V208", DXGI_FORMAT(131) },
{ L"V408", DXGI_FORMAT(132) },
// Xbox-specific formats
{ L"DXGI_FORMAT_R10G10B10_7E3_A2_FLOAT (Xbox)", DXGI_FORMAT(116) },
{ L"DXGI_FORMAT_R10G10B10_6E4_A2_FLOAT (Xbox)", DXGI_FORMAT(117) },
{ L"DXGI_FORMAT_D16_UNORM_S8_UINT (Xbox)", DXGI_FORMAT(118) },
{ L"DXGI_FORMAT_R16_UNORM_X8_TYPELESS (Xbox)", DXGI_FORMAT(119) },
{ L"DXGI_FORMAT_X16_TYPELESS_G8_UINT (Xbox)", DXGI_FORMAT(120) },
{ L"DXGI_FORMAT_R10G10B10_SNORM_A2_UNORM (Xbox)", DXGI_FORMAT(189) },
{ L"DXGI_FORMAT_R4G4_UNORM (Xbox)", DXGI_FORMAT(190) },
{ L"R10G10B10_7E3_A2_FLOAT (Xbox)", DXGI_FORMAT(116) },
{ L"R10G10B10_6E4_A2_FLOAT (Xbox)", DXGI_FORMAT(117) },
{ L"D16_UNORM_S8_UINT (Xbox)", DXGI_FORMAT(118) },
{ L"R16_UNORM_X8_TYPELESS (Xbox)", DXGI_FORMAT(119) },
{ L"X16_TYPELESS_G8_UINT (Xbox)", DXGI_FORMAT(120) },
{ L"R10G10B10_SNORM_A2_UNORM (Xbox)", DXGI_FORMAT(189) },
{ L"R4G4_UNORM (Xbox)", DXGI_FORMAT(190) },
{ nullptr, DXGI_FORMAT_UNKNOWN }
};
@ -227,6 +278,26 @@ const SValue g_pFilters[] =
{ nullptr, TEX_FILTER_DEFAULT }
};
#define CODEC_DDS 0xFFFF0001
#define CODEC_TGA 0xFFFF0002
#define CODEC_HDR 0xFFFF0005
const SValue g_pExtFileTypes[] =
{
{ L".BMP", WIC_CODEC_BMP },
{ L".JPG", WIC_CODEC_JPEG },
{ L".JPEG", WIC_CODEC_JPEG },
{ L".PNG", WIC_CODEC_PNG },
{ L".DDS", CODEC_DDS },
{ L".TGA", CODEC_TGA },
{ L".HDR", CODEC_HDR },
{ L".TIF", WIC_CODEC_TIFF },
{ L".TIFF", WIC_CODEC_TIFF },
{ L".WDP", WIC_CODEC_WMP },
{ L".HDP", WIC_CODEC_WMP },
{ L".JXR", WIC_CODEC_WMP },
{ nullptr, CODEC_DDS }
};
//////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
@ -355,6 +426,17 @@ namespace
break;
}
}
for (const SValue *pFormat = g_pReadOnlyFormats; pFormat->pName; pFormat++)
{
if ((DXGI_FORMAT)pFormat->dwValue == Format)
{
wprintf(pFormat->pName);
return;
}
}
wprintf(L"*UNKNOWN*");
}
@ -398,6 +480,7 @@ namespace
wprintf(L" info Output image metadata\n");
wprintf(L" analyze Analyze and summarize image information\n");
wprintf(L" compare Compare two images with MSE error metric\n");
wprintf(L" diff Generate difference image from two images\n");
wprintf(L" dumpbc Dump out compressed blocks (DDS BC only)\n\n");
wprintf(L" -r wildcard filename search is recursive\n");
wprintf(L" -if <filter> image filtering\n");
@ -406,11 +489,18 @@ namespace
wprintf(L" -dword Use DWORD instead of BYTE alignment\n");
wprintf(L" -badtails Fix for older DXTn with bad mipchain tails\n");
wprintf(L" -xlum expand legacy L8, L16, and A8P8 formats\n");
wprintf(L"\n (diff only)\n");
wprintf(L" -f <format> format\n");
wprintf(L" -o <filename> output filename\n");
wprintf(L" -y overwrite existing output file (if any)\n");
wprintf(L"\n (dumpbc only)\n");
wprintf(L" -targetx <num> dump pixels at location x (defaults to all)\n");
wprintf(L" -targety <num> dump pixels at location y (defaults to all)\n");
wprintf(L"\n -nologo suppress copyright message\n");
wprintf(L"\n <format>: ");
PrintList(13, g_pFormats);
wprintf(L"\n <filter>: ");
PrintList(13, g_pFilters);
}
@ -482,6 +572,24 @@ namespace
}
}
HRESULT SaveImage(const Image* image, const wchar_t *fileName, DWORD codec)
{
switch (codec)
{
case CODEC_DDS:
return SaveToDDSFile(*image, DDS_FLAGS_NONE, fileName);
case CODEC_TGA:
return SaveToTGAFile(*image, fileName);
case CODEC_HDR:
return SaveToHDRFile(*image, fileName);
default:
return SaveToWICFile(*image, WIC_FLAGS_NONE, GetWICCodec(static_cast<WICCodecs>(codec)), fileName);
}
}
//--------------------------------------------------------------------------------------
struct AnalyzeData
{
@ -1039,6 +1147,80 @@ namespace
}
//--------------------------------------------------------------------------------------
HRESULT Difference(const Image& image1, const Image& image2, DWORD dwFilter, DXGI_FORMAT format, ScratchImage& result)
{
if (!image1.pixels || !image2.pixels)
return E_POINTER;
if (image1.width != image2.width
|| image1.height != image2.height)
return E_FAIL;
ScratchImage tempA;
const Image* imageA = &image1;
if (IsCompressed(image1.format))
{
HRESULT hr = Decompress(image1, DXGI_FORMAT_R32G32B32A32_FLOAT, tempA);
if (FAILED(hr))
return hr;
imageA = tempA.GetImage(0, 0, 0);
}
ScratchImage tempB;
const Image* imageB = &image2;
if (image2.format != DXGI_FORMAT_R32G32B32A32_FLOAT)
{
if (IsCompressed(image2.format))
{
HRESULT hr = Decompress(image2, DXGI_FORMAT_R32G32B32A32_FLOAT, tempB);
if (FAILED(hr))
return hr;
imageB = tempB.GetImage(0,0,0);
}
else
{
HRESULT hr = Convert(image2, DXGI_FORMAT_R32G32B32A32_FLOAT, dwFilter, TEX_THRESHOLD_DEFAULT, tempB);
if (FAILED(hr))
return hr;
imageB = tempB.GetImage(0, 0, 0);
}
}
if (!imageA || !imageB)
return E_POINTER;
ScratchImage diffImage;
HRESULT hr = TransformImage(*imageA, [&](XMVECTOR* outPixels, const XMVECTOR * inPixels, size_t width, size_t y)
{
auto *inPixelsB = reinterpret_cast<XMVECTOR*>(imageB->pixels + (y*imageB->rowPitch));
for (size_t x = 0; x < width; ++x)
{
XMVECTOR v1 = *inPixels++;
XMVECTOR v2 = *inPixelsB++;
v1 = XMVectorSubtract(v1, v2);
v1 = XMVectorAbs(v1);
v1 = XMVectorSelect( g_XMIdentityR3, v1, g_XMSelect1110);
*outPixels++ = v1;
}
}, (format == DXGI_FORMAT_R32G32B32A32_FLOAT) ? result : diffImage);
if (FAILED(hr))
return hr;
if (format == DXGI_FORMAT_R32G32B32A32_FLOAT)
return S_OK;
return Convert(diffImage.GetImages(), diffImage.GetImageCount(), diffImage.GetMetadata(), format, dwFilter, TEX_THRESHOLD_DEFAULT, result);
}
//--------------------------------------------------------------------------------------
// Partition, Shape, Fixup
const uint8_t g_aFixUp[3][64][3] =
@ -2764,6 +2946,9 @@ int __cdecl wmain(_In_ int argc, _In_z_count_(argc) wchar_t* argv[])
DWORD dwFilter = TEX_FILTER_DEFAULT;
int pixelx = -1;
int pixely = -1;
DXGI_FORMAT diffFormat = DXGI_FORMAT_B8G8R8A8_UNORM;
DWORD diffFileType = WIC_CODEC_BMP;
wchar_t szOutputFile[MAX_PATH] = { 0 };
// Initialize COM (needed for WIC)
HRESULT hr = hr = CoInitializeEx(nullptr, COINIT_MULTITHREADED);
@ -2786,11 +2971,12 @@ int __cdecl wmain(_In_ int argc, _In_z_count_(argc) wchar_t* argv[])
case CMD_INFO:
case CMD_ANALYZE:
case CMD_COMPARE:
case CMD_DIFF:
case CMD_DUMPBC:
break;
default:
wprintf(L"Must use one of: info, analyze, compare, or dumpbc\n\n");
wprintf(L"Must use one of: info, analyze, compare, diff, or dumpbc\n\n");
return 1;
}
@ -2825,6 +3011,8 @@ int __cdecl wmain(_In_ int argc, _In_z_count_(argc) wchar_t* argv[])
switch (dwOption)
{
case OPT_FILTER:
case OPT_FORMAT:
case OPT_OUTPUTFILE:
case OPT_TARGET_PIXELX:
case OPT_TARGET_PIXELY:
if (!*pValue)
@ -2843,6 +3031,23 @@ int __cdecl wmain(_In_ int argc, _In_z_count_(argc) wchar_t* argv[])
switch (dwOption)
{
case OPT_FORMAT:
if (dwCommand != CMD_DIFF)
{
wprintf(L"-f only valid for use with diff command\n");
return 1;
}
else
{
diffFormat = (DXGI_FORMAT)LookupByName(pValue, g_pFormats);
if (!diffFormat)
{
wprintf(L"Invalid value specified with -f (%ls)\n", pValue);
return 1;
}
}
break;
case OPT_FILTER:
dwFilter = LookupByName(pValue, g_pFilters);
if (!dwFilter)
@ -2852,8 +3057,30 @@ int __cdecl wmain(_In_ int argc, _In_z_count_(argc) wchar_t* argv[])
}
break;
case OPT_OUTPUTFILE:
if (dwCommand != CMD_DIFF)
{
wprintf(L"-o only valid for use with diff command\n");
return 1;
}
else
{
wcscpy_s(szOutputFile, MAX_PATH, pValue);
wchar_t ext[_MAX_EXT];
_wsplitpath_s(szOutputFile, nullptr, 0, nullptr, 0, nullptr, 0, ext, _MAX_EXT);
diffFileType = LookupByName(ext, g_pExtFileTypes);
}
break;
case OPT_TARGET_PIXELX:
if (swscanf_s(pValue, L"%d", &pixelx) != 1)
if (dwCommand != CMD_DUMPBC)
{
wprintf(L"-targetx only valid with dumpbc command\n");
return 1;
}
else if (swscanf_s(pValue, L"%d", &pixelx) != 1)
{
wprintf(L"Invalid value for pixel x location (%ls)\n", pValue);
return 1;
@ -2861,7 +3088,12 @@ int __cdecl wmain(_In_ int argc, _In_z_count_(argc) wchar_t* argv[])
break;
case OPT_TARGET_PIXELY:
if (swscanf_s(pValue, L"%d", &pixely) != 1)
if (dwCommand != CMD_DUMPBC)
{
wprintf(L"-targety only valid with dumpbc command\n");
return 1;
}
else if (swscanf_s(pValue, L"%d", &pixely) != 1)
{
wprintf(L"Invalid value for pixel y location (%ls)\n", pValue);
return 1;
@ -2900,10 +3132,11 @@ int __cdecl wmain(_In_ int argc, _In_z_count_(argc) wchar_t* argv[])
switch (dwCommand)
{
case CMD_COMPARE:
// --- Compare -----------------------------------------------------------------
case CMD_DIFF:
// --- Compare/Diff ------------------------------------------------------------
if (conversion.size() != 2)
{
wprintf(L"ERROR: compare needs excatly two images\n");
wprintf(L"ERROR: compare/diff needs exactly two images\n");
return 1;
}
else
@ -2943,11 +3176,56 @@ int __cdecl wmain(_In_ int argc, _In_z_count_(argc) wchar_t* argv[])
if (info1.height != info2.height
|| info1.width != info2.width)
{
wprintf(L"ERROR: Can only compare images of the same width & height\n");
wprintf(L"ERROR: Can only compare/diff images of the same width & height\n");
return 1;
}
if ((info1.depth == 1
if (dwCommand == CMD_DIFF)
{
if (!*szOutputFile)
{
wchar_t ext[_MAX_EXT];
wchar_t fname[_MAX_FNAME];
_wsplitpath_s(pImage1->szSrc, nullptr, 0, nullptr, 0, fname, _MAX_FNAME, ext, _MAX_EXT);
if (_wcsicmp(ext, L".bmp") == 0)
{
wprintf(L"ERROR: Need to specify output file via -o\n");
return 1;
}
_wmakepath_s(szOutputFile, nullptr, nullptr, fname, L".bmp");
}
if (image1->GetImageCount() > 1 || image2->GetImageCount() > 1)
wprintf(L"WARNING: ignoring all images but first one in each file\n");
ScratchImage diffImage;
hr = Difference(*image1->GetImage(0, 0, 0), *image2->GetImage(0, 0, 0), dwFilter, diffFormat, diffImage);
if (FAILED(hr))
{
wprintf(L"Failed diffing images (%08X)\n", hr);
return 1;
}
if (~dwOptions & (1 << OPT_OVERWRITE))
{
if (GetFileAttributesW(szOutputFile) != INVALID_FILE_ATTRIBUTES)
{
wprintf(L"\nERROR: Output file already exists, use -y to overwrite\n");
return 1;
}
}
hr = SaveImage(diffImage.GetImage(0, 0, 0), szOutputFile, diffFileType);
if (FAILED(hr))
{
wprintf(L" FAILED (%x)\n", hr);
return 1;
}
wprintf(L"Difference %ls\n", szOutputFile);
}
else if ((info1.depth == 1
&& info1.arraySize == 1
&& info1.mipLevels == 1)
|| info1.depth != info2.depth