HexaEngine / Hexa.NET.DirectXTex

A .NET wrapper for the DirectXTex library.
MIT License
2 stars 0 forks source link

Crash when saving a 2048x2048 image #2

Closed iMrShadow closed 2 months ago

iMrShadow commented 2 months ago

Hello! I am experiencing an interesting crash when working with large textures - in this case 2048x2048. I am using a function to restore the Z axis of normal maps when converting to PNG. It "works" for small images, but it doesn't for large. I am attaching the dds files I tested in various sizes. 4 of them should work (512x512, 1024x1024, 1024x2048, 2018x1024), the last one will crash (tile_roofShingles...dds)

Normal Maps.zip

Here's the code I used.

unsafe public static void SaveNormalMap(string ddsFilePath, string destinationDirectory, TextureType textureType, DDSConversionMode conversionMode = DDSConversionMode.DEFAULT)
    {
        GetDDSImage(ddsFilePath, out ScratchImage image, out TexMetadata metadata);

// Create path for new texture file
        var path = Path.Combine(destinationDirectory, Path.GetFileNameWithoutExtension(ddsFilePath) + Converter.GetExtension(textureType)[0]);

        ScratchImage rawImage = DirectXTex.CreateScratchImage();

// Decompress to R8G8B8A8
        if (DirectXTex.IsCompressed(metadata.Format))
        {
            DirectXTex.Decompress2(image.GetImages(), image.GetImageCount(), image.GetMetadata(), (int)DXGIFormat.R8G8B8A8_UNORM, rawImage);
        }
        else
        {
// Copy the Texture if it's not compressed
            Rect rect = new() { X = 0, Y = 0, W = metadata.Width, H = metadata.Height };

            DirectXTex.CopyRectangle(image.GetImage(0, 0, 0), rect, rawImage.GetImage(0, 0, 0), TexFilterFlags.Default, 0, 0);
        }

        image.Release();

// Select the TransformImage Function (in this case we use Restore_Z and we use the RestoreZUnorm function).
        TransformImageFunc transformFunction = null;

        if (conversionMode == DDSConversionMode.RESTORE_Z)
        {
            transformFunction = RestoreZ;

            if (DirectXTex.FormatDataType(metadata.Format) == FormatType.Unorm)
            {
                transformFunction = RestoreZUnorm;
            }
        }

        ScratchImage finalImage = DirectXTex.CreateScratchImage();

// Transform the image
        if (transformFunction != null)
        {
            DirectXTex.TransformImage(rawImage.GetImage(0, 0, 0), transformFunction, finalImage);
        }

        rawImage.Release();

// Save the new image
        Guid guid = DirectXTex.GetWICCodec(GetWicDecoder(textureType));
        DirectXTex.SaveToWICFile2(finalImage.GetImages(), finalImage.GetImageCount(), 0, guid, path, null, default);

        finalImage.Release();
    }
unsafe public static void RestoreZUnorm(Vector4* outPixels, Vector4* inPixels, ulong width, ulong y)
    {
        for (ulong j = 0; j < width; ++j)
        {

            Vector4 value = inPixels[j];

            Vector4 x2 = (value * 2.0f) - Vector4.One;
            x2 = Vector4.SquareRoot(Vector4.One - Vector4.Multiply(x2, x2));
            Vector4 z = Vector4.Multiply(x2, 0.5f) + Vector4.One * 0.5f;

            outPixels[j] = value;
            outPixels[j].Z = z.Z;
        }
    }
// Enums for the modes, ignore them.
public enum DDSConversionMode
    {
        DEFAULT = 0,
        RESTORE_Z = 1,
        SWIZZLE_MASK_RG00 = 2,
        SWIZZLE_ABGR = 3,
    }

public enum TextureType
{
    Unknown,
    D3DTX,
    DDS,
    KTX,
    KTX2,
    PNG,
    JPEG,
    BMP,
    TIFF,
    TGA
}
JunaMeinhold commented 2 months ago

could be an issue with mip maps, png doesn't support mip maps. I recommend adding this (see screenshot, HResult is already included in the package, in the namespace HexaGen.Runtime) image

JunaMeinhold commented 2 months ago

the solution to the png mip-map issue is this

DirectXTex.SaveToWICFile(mipChain.GetImages()[0], WICFlags.None, DirectXTex.GetWICCodec(WICCodecs.CodecPng), outputPath, null, default);
iMrShadow commented 2 months ago

the solution to the png mip-map issue is this

DirectXTex.SaveToWICFile(mipChain.GetImages()[0], WICFlags.None, DirectXTex.GetWICCodec(WICCodecs.CodecPng), outputPath, null, default);

This worked partially, but the situation is weird again. Here's the breakdown:

  1. Saving the smaller images works anywhere on the PC.
  2. I get an AccessViolationException when saving the 2048x2048 ones in some paths, while in others - I do not (I am getting a Deja Vu with the previous issue #1). Then the whole app crashes. Note: the folders access are the same

Edit: This might be only related when dealing with folders in /Downloads. I will investigate further. However, it wouldn't make sense when loading the files (Issue #1). Edit 2: Using SaveToWICMemory works, and saving the file as bytes works well. Same work-around as #1 but in the opposite direction.

JunaMeinhold commented 2 months ago

Also png doesn't support all Pixel formats, png only supports RGBA8 UNorm, RGBA16 UNorm, R8 UNorm, R16 UNorm

JunaMeinhold commented 2 months ago

Do you have by any chance an Anti-Virus, that could cause that? (The source code of the native lib is in the project you need cmake to build it)

iMrShadow commented 2 months ago

I only have Windows Defender and Malwarebytes, nothing too fancy

Also png doesn't support all Pixel formats, png only supports RGBA8 UNorm, RGBA16 UNorm, R8 UNorm, R16 UNorm

Yes, I am aware - that's why I have the uncompress function above. The function applies only to normal maps (which are commonly in BC3 and BC5 formats). Previously we used texconv.exe which automatically did that for us, but I want to get rid of external dependencies

JunaMeinhold commented 2 months ago

It could be a permission problem. Maybe try running it in admin mode, just for testing.

iMrShadow commented 2 months ago

It didn't work. Does the wrapper inherit the app's permissions, or it's standalone? It could be due to DirectXTex's way of doing things

JunaMeinhold commented 2 months ago

the wrapper is simply a c layer that forwards calls to the c++ lib, I didn't change anything, (DirectXTex is imported as git sub module from the official repo) see source code, (you have to do a C# -> C -> Cpp layer because C# can only interop with C and COM)

iMrShadow commented 2 months ago

I am closing the issue since there's a workaround for this and it may be related to DirectXTex's permissions, which this wrapper doesn't have control of.

Thanks for the help!