Search Unity

Loading crunched textures at runtime in 2017.3+

Discussion in 'Scripting' started by Sneeza, Sep 16, 2018.

  1. Sneeza

    Sneeza

    Joined:
    Aug 6, 2017
    Posts:
    9
    I'm having an annoying problem that hopefully others can shed some light on.

    As part of my project I want to load textures from the web and parse them into Texture2D objects at runtime. I have an approach that works for DXT5, ETC2 and PVRTC formats which is great, however the filesize for these images is fairly large so I'm looking to use crunched textures which are a factor of 20 smaller in my tests.

    I'm compressing the images using Unity's updated branch of the Crunch tool available here: https://github.com/Unity-Technologies/crunch/tree/unity

    After parsing the CRN file in Unity to extract the header and image data, I run the following to generate the Texture2D:

    Code (CSharp):
    1. AssetImageData imageData = AssetDecoder.GetImageDataFromCRN(response.Data);
    2.  
    3. texture = new Texture2D(imageData.width, imageData.height, imageData.format, false, true);
    4. texture.LoadRawTextureData(imageData.dataPointer, imageData.dataLength);
    5. texture.Apply();
    This gives me the following error:

    Code (CSharp):
    1. The texture could not be loaded because it has been encoded with an older version of Crunch. This can happen when loading AssetBundles containing Crunch-compressed textures built with Unity version prior to 2017.3
    2. UnityEngine.Texture2D:Apply()
    I'm not sure how to approach this problem. I've tried re-compiling the Crunch binary from that repo and I get the same error. The last commit to that repo was in June and Unity 2017.3 came out in December 2017 so I don't think I'm using an outdated version of the Crunch tool.

    Any ideas would be appreciated!
     
  2. GXMark

    GXMark

    Joined:
    Oct 13, 2012
    Posts:
    515
    Your correct the "CRN" data is not compatible with the latest version of unity (of this writing). Your best bet is to put Extern "C" wrapper around the method calls you want inside the crnlib c++ project and create a DLL. You can then take the returned DDS data and remove the header and directly load the raw bytes into the texture 2D. I can confirm this works perfectly well.

    This also has the benefit of not breaking your crn data when unity decides to improve upon the forked crunch code. Or at the very least you have the opportunity to support multiple crunch versions if your game stores crn data external to the unity build app.

    I would be prepared to put my Runtime Crunch Utility and crnlib DLL onto the asset for a small $5 cost if there was a demand for it.
     
  3. GXMark

    GXMark

    Joined:
    Oct 13, 2012
    Posts:
    515
    Some additional info on crunch runtime. From unity crunch fork replace crnlib.h with the following and build to dll. The settings are comparable to the creation times of the unity crunch setup.

    Code (CSharp):
    1. // File: crnlib.h - Advanced DXTn texture compression library.
    2. // Copyright (c) 2010-2016 Richard Geldreich, Jr. and Binomial LLC
    3. // See copyright notice and license at the end of this file.
    4. //
    5. // This header file contains the public crnlib declarations for DXTn,
    6. // clustered DXTn, and CRN compression/decompression.
    7. //
    8. // Note: This library does NOT need to be linked into your game executable if
    9. // all you want to do is transcode .CRN files to raw DXTn bits at run-time.
    10. // The crn_decomp.h header file library contains all the code necessary for
    11. // decompression.
    12. //
    13. // Important: If compiling with gcc, be sure strict aliasing is disabled: -fno-strict-aliasing
    14. #ifndef CRNLIB_H
    15. #define CRNLIB_H
    16.  
    17. #ifdef _MSC_VER
    18. #pragma warning(disable : 4127)  //  conditional expression is constant
    19. #endif
    20.  
    21. #define CRNLIB_VERSION 104
    22.  
    23. #define CRNLIB_SUPPORT_ATI_COMPRESS 0
    24. #define CRNLIB_SUPPORT_SQUISH 0
    25.  
    26. typedef unsigned char crn_uint8;
    27. typedef unsigned short crn_uint16;
    28. typedef unsigned int crn_uint32;
    29. typedef signed char crn_int8;
    30. typedef signed short crn_int16;
    31. typedef signed int crn_int32;
    32. typedef unsigned int crn_bool;
    33.  
    34. #ifdef TESTFUNCDLL_EXPORT
    35. #define TESTFUNCDLL_API __declspec(dllexport)
    36. #else
    37. #define TESTFUNCDLL_API __declspec(dllimport)
    38. #endif
    39.  
    40. // crnlib can compress to these file types.
    41. enum crn_file_type {
    42.   // .CRN
    43.   cCRNFileTypeCRN = 0,
    44.  
    45.   // .DDS using regular DXT or clustered DXT
    46.   cCRNFileTypeDDS,
    47.  
    48.   cCRNFileTypeForceDWORD = 0xFFFFFFFF
    49. };
    50.  
    51. // Supported compressed pixel formats.
    52. // Basically all the standard DX9 formats, with some swizzled DXT5 formats
    53. // (most of them supported by ATI's Compressonator), along with some ATI/X360 GPU specific formats.
    54. enum crn_format {
    55.   cCRNFmtInvalid = -1,
    56.  
    57.   cCRNFmtDXT1 = 0,
    58.  
    59.   cCRNFmtFirstValid = cCRNFmtDXT1,
    60.  
    61.   // cCRNFmtDXT3 is not currently supported when writing to CRN - only DDS.
    62.   cCRNFmtDXT3,
    63.  
    64.   cCRNFmtDXT5,
    65.  
    66.   // Various DXT5 derivatives
    67.   cCRNFmtDXT5_CCxY,  // Luma-chroma
    68.   cCRNFmtDXT5_xGxR,  // Swizzled 2-component
    69.   cCRNFmtDXT5_xGBR,  // Swizzled 3-component
    70.   cCRNFmtDXT5_AGBR,  // Swizzled 4-component
    71.  
    72.   // ATI 3DC and X360 DXN
    73.   cCRNFmtDXN_XY,
    74.   cCRNFmtDXN_YX,
    75.  
    76.   // DXT5 alpha blocks only
    77.   cCRNFmtDXT5A,
    78.  
    79.   cCRNFmtETC1,
    80.   cCRNFmtETC2,
    81.   cCRNFmtETC2A,
    82.   cCRNFmtETC1S,
    83.   cCRNFmtETC2AS,
    84.  
    85.   cCRNFmtTotal,
    86.  
    87.   cCRNFmtForceDWORD = 0xFFFFFFFF
    88. };
    89.  
    90. // Various library/file format limits.
    91. enum crn_limits {
    92.   // Max. mipmap level resolution on any axis.
    93.   cCRNMaxLevelResolution = 4096,
    94.  
    95.   cCRNMinPaletteSize = 8,
    96.   cCRNMaxPaletteSize = 8192,
    97.  
    98.   cCRNMaxFaces = 6,
    99.   cCRNMaxLevels = 16,
    100.  
    101.   cCRNMaxHelperThreads = 16,
    102.  
    103.   cCRNMinQualityLevel = 0,
    104.   cCRNMaxQualityLevel = 255
    105. };
    106.  
    107. // CRN/DDS compression flags.
    108. // See the m_flags member in the crn_comp_params struct, below.
    109. enum crn_comp_flags {
    110.   // Enables perceptual colorspace distance metrics if set.
    111.   // Important: Be sure to disable this when compressing non-sRGB colorspace images, like normal maps!
    112.   // Default: Set
    113.   cCRNCompFlagPerceptual = 1,
    114.  
    115.   // Enables (up to) 8x8 macroblock usage if set. If disabled, only 4x4 blocks are allowed.
    116.   // Compression ratio will be lower when disabled, but may cut down on blocky artifacts because the process used to determine
    117.   // where large macroblocks can be used without artifacts isn't perfect.
    118.   // Default: Set.
    119.   cCRNCompFlagHierarchical = 2,
    120.  
    121.   // cCRNCompFlagQuick disables several output file optimizations - intended for things like quicker previews.
    122.   // Default: Not set.
    123.   cCRNCompFlagQuick = 4,
    124.  
    125.   // DXT1: OK to use DXT1 alpha blocks for better quality or DXT1A transparency.
    126.   // DXT5: OK to use both DXT5 block types.
    127.   // Currently only used when writing to .DDS files, as .CRN uses only a subset of the possible DXTn block types.
    128.   // Default: Set.
    129.   cCRNCompFlagUseBothBlockTypes = 8,
    130.  
    131.   // OK to use DXT1A transparent indices to encode black (assumes pixel shader ignores fetched alpha).
    132.   // Currently only used when writing to .DDS files, .CRN never uses alpha blocks.
    133.   // Default: Not set.
    134.   cCRNCompFlagUseTransparentIndicesForBlack = 16,
    135.  
    136.   // Disables endpoint caching, for more deterministic output.
    137.   // Currently only used when writing to .DDS files.
    138.   // Default: Not set.
    139.   cCRNCompFlagDisableEndpointCaching = 32,
    140.  
    141.   // If enabled, use the cCRNColorEndpointPaletteSize, etc. params to control the CRN palette sizes. Only useful when writing to .CRN files.
    142.   // Default: Not set.
    143.   cCRNCompFlagManualPaletteSizes = 64,
    144.  
    145.   // If enabled, DXT1A alpha blocks are used to encode single bit transparency.
    146.   // Default: Not set.
    147.   cCRNCompFlagDXT1AForTransparency = 128,
    148.  
    149.   // If enabled, the DXT1 compressor's color distance metric assumes the pixel shader will be converting the fetched RGB results to luma (Y part of YCbCr).
    150.   // This increases quality when compressing grayscale images, because the compressor can spread the luma error amoung all three channels (i.e. it can generate blocks
    151.   // with some chroma present if doing so will ultimately lead to lower luma error).
    152.   // Only enable on grayscale source images.
    153.   // Default: Not set.
    154.   cCRNCompFlagGrayscaleSampling = 256,
    155.  
    156.   // If enabled, debug information will be output during compression.
    157.   // Default: Not set.
    158.   cCRNCompFlagDebugging = 0x80000000,
    159.  
    160.   cCRNCompFlagForceDWORD = 0xFFFFFFFF
    161. };
    162.  
    163. // Controls DXTn quality vs. speed control - only used when compressing to .DDS.
    164. enum crn_dxt_quality {
    165.   cCRNDXTQualitySuperFast,
    166.   cCRNDXTQualityFast,
    167.   cCRNDXTQualityNormal,
    168.   cCRNDXTQualityBetter,
    169.   cCRNDXTQualityUber,
    170.  
    171.   cCRNDXTQualityTotal,
    172.  
    173.   cCRNDXTQualityForceDWORD = 0xFFFFFFFF
    174. };
    175.  
    176. // Which DXTn compressor to use when compressing to plain (non-clustered) .DDS.
    177. enum crn_dxt_compressor_type {
    178.   cCRNDXTCompressorCRN,   // Use crnlib's ETC1 or DXTc block compressor (default, highest quality, comparable or better than ati_compress or squish, and crnlib's ETC1 is a lot fasterw with similiar quality to Erricson's)
    179.   cCRNDXTCompressorCRNF,  // Use crnlib's "fast" DXTc block compressor
    180.   cCRNDXTCompressorRYG,   // Use RYG's DXTc block compressor (low quality, but very fast)
    181.  
    182. #if CRNLIB_SUPPORT_ATI_COMPRESS
    183.   cCRNDXTCompressorATI,
    184. #endif
    185.  
    186. #if CRNLIB_SUPPORT_SQUISH
    187.   cCRNDXTCompressorSquish,
    188. #endif
    189.  
    190.   cCRNTotalDXTCompressors,
    191.  
    192.   cCRNDXTCompressorForceDWORD = 0xFFFFFFFF
    193. };
    194.  
    195. // Progress callback function.
    196. // Processing will stop prematurely (and fail) if the callback returns false.
    197. // phase_index, total_phases - high level progress
    198. // subphase_index, total_subphases - progress within current phase
    199. typedef crn_bool (*crn_progress_callback_func)(crn_uint32 phase_index, crn_uint32 total_phases, crn_uint32 subphase_index, crn_uint32 total_subphases, void* pUser_data_ptr);
    200.  
    201. // CRN/DDS compression parameters struct.
    202. struct crn_comp_params {
    203.     inline crn_comp_params() { clear(); }
    204.  
    205.     // Clear struct to default parameters.
    206.     inline void clear() {
    207.         m_size_of_obj = sizeof(*this);
    208.         m_file_type = cCRNFileTypeCRN;
    209.         m_faces = 1;
    210.         m_width = 0;
    211.         m_height = 0;
    212.         m_levels = 1;
    213.         m_format = cCRNFmtDXT1;
    214.         m_flags = cCRNCompFlagPerceptual | cCRNCompFlagHierarchical | cCRNCompFlagUseBothBlockTypes;
    215.  
    216.         for (crn_uint32 f = 0; f < cCRNMaxFaces; f++)
    217.             for (crn_uint32 l = 0; l < cCRNMaxLevels; l++)
    218.                 m_pImages[f][l] = NULL;
    219.  
    220.         m_target_bitrate = 0.0f;
    221.         m_quality_level = cCRNMaxQualityLevel;
    222.         m_dxt1a_alpha_threshold = 128;
    223.         m_dxt_quality = cCRNDXTQualityNormal;
    224.         m_dxt_compressor_type = cCRNDXTCompressorCRN;
    225.         m_alpha_component = 3;
    226.  
    227.         m_crn_adaptive_tile_color_psnr_derating = 2.0f;
    228.         m_crn_adaptive_tile_alpha_psnr_derating = 2.0f;
    229.         m_crn_color_endpoint_palette_size = 0;
    230.         m_crn_color_selector_palette_size = 0;
    231.         m_crn_alpha_endpoint_palette_size = 0;
    232.         m_crn_alpha_selector_palette_size = 0;
    233.  
    234.         m_num_helper_threads = 10;
    235.         m_userdata0 = 0;
    236.         m_userdata1 = 0;
    237.         m_pProgress_func = NULL;
    238.         m_pProgress_func_data = NULL;
    239.     }
    240.  
    241.   inline bool operator==(const crn_comp_params& rhs) const {
    242. #define CRNLIB_COMP(x)  \
    243.   do {                  \
    244.     if ((x) != (rhs.x)) \
    245.       return false;     \
    246.   } while (0)
    247.     CRNLIB_COMP(m_size_of_obj);
    248.     CRNLIB_COMP(m_file_type);
    249.     CRNLIB_COMP(m_faces);
    250.     CRNLIB_COMP(m_width);
    251.     CRNLIB_COMP(m_height);
    252.     CRNLIB_COMP(m_levels);
    253.     CRNLIB_COMP(m_format);
    254.     CRNLIB_COMP(m_flags);
    255.     CRNLIB_COMP(m_target_bitrate);
    256.     CRNLIB_COMP(m_quality_level);
    257.     CRNLIB_COMP(m_dxt1a_alpha_threshold);
    258.     CRNLIB_COMP(m_dxt_quality);
    259.     CRNLIB_COMP(m_dxt_compressor_type);
    260.     CRNLIB_COMP(m_alpha_component);
    261.     CRNLIB_COMP(m_crn_adaptive_tile_color_psnr_derating);
    262.     CRNLIB_COMP(m_crn_adaptive_tile_alpha_psnr_derating);
    263.     CRNLIB_COMP(m_crn_color_endpoint_palette_size);
    264.     CRNLIB_COMP(m_crn_color_selector_palette_size);
    265.     CRNLIB_COMP(m_crn_alpha_endpoint_palette_size);
    266.     CRNLIB_COMP(m_crn_alpha_selector_palette_size);
    267.     CRNLIB_COMP(m_num_helper_threads);
    268.     CRNLIB_COMP(m_userdata0);
    269.     CRNLIB_COMP(m_userdata1);
    270.     CRNLIB_COMP(m_pProgress_func);
    271.     CRNLIB_COMP(m_pProgress_func_data);
    272.  
    273.     for (crn_uint32 f = 0; f < cCRNMaxFaces; f++)
    274.       for (crn_uint32 l = 0; l < cCRNMaxLevels; l++)
    275.         CRNLIB_COMP(m_pImages[f][l]);
    276.  
    277. #undef CRNLIB_COMP
    278.     return true;
    279.   }
    280.  
    281.   // Returns true if the input parameters are reasonable.
    282.   inline bool check() const {
    283.     if ((m_file_type > cCRNFileTypeDDS) ||
    284.         (((int)m_quality_level < (int)cCRNMinQualityLevel) || ((int)m_quality_level > (int)cCRNMaxQualityLevel)) ||
    285.         (m_dxt1a_alpha_threshold > 255) ||
    286.         ((m_faces != 1) && (m_faces != 6)) ||
    287.         ((m_width < 1) || (m_width > cCRNMaxLevelResolution)) ||
    288.         ((m_height < 1) || (m_height > cCRNMaxLevelResolution)) ||
    289.         ((m_levels < 1) || (m_levels > cCRNMaxLevels)) ||
    290.         ((m_format < cCRNFmtDXT1) || (m_format >= cCRNFmtTotal)) ||
    291.         ((m_crn_color_endpoint_palette_size) && ((m_crn_color_endpoint_palette_size < cCRNMinPaletteSize) || (m_crn_color_endpoint_palette_size > cCRNMaxPaletteSize))) ||
    292.         ((m_crn_color_selector_palette_size) && ((m_crn_color_selector_palette_size < cCRNMinPaletteSize) || (m_crn_color_selector_palette_size > cCRNMaxPaletteSize))) ||
    293.         ((m_crn_alpha_endpoint_palette_size) && ((m_crn_alpha_endpoint_palette_size < cCRNMinPaletteSize) || (m_crn_alpha_endpoint_palette_size > cCRNMaxPaletteSize))) ||
    294.         ((m_crn_alpha_selector_palette_size) && ((m_crn_alpha_selector_palette_size < cCRNMinPaletteSize) || (m_crn_alpha_selector_palette_size > cCRNMaxPaletteSize))) ||
    295.         (m_alpha_component > 3) ||
    296.         (m_num_helper_threads > cCRNMaxHelperThreads) ||
    297.         (m_dxt_quality > cCRNDXTQualityUber) ||
    298.         (m_dxt_compressor_type >= cCRNTotalDXTCompressors)) {
    299.       return false;
    300.     }
    301.     return true;
    302.   }
    303.  
    304.   // Helper to set/get flags from m_flags member.
    305.   inline bool get_flag(crn_comp_flags flag) const { return (m_flags & flag) != 0; }
    306.   inline void set_flag(crn_comp_flags flag, bool val) {
    307.     m_flags &= ~flag;
    308.     if (val)
    309.       m_flags |= flag;
    310.   }
    311.  
    312.   crn_uint32 m_size_of_obj;
    313.  
    314.   crn_file_type m_file_type;  // Output file type: cCRNFileTypeCRN or cCRNFileTypeDDS.
    315.  
    316.   crn_uint32 m_faces;   // 1 (2D map) or 6 (cubemap)
    317.   crn_uint32 m_width;   // [1,cCRNMaxLevelResolution], non-power of 2 OK, non-square OK
    318.   crn_uint32 m_height;  // [1,cCRNMaxLevelResolution], non-power of 2 OK, non-square OK
    319.   crn_uint32 m_levels;  // [1,cCRNMaxLevelResolution], non-power of 2 OK, non-square OK
    320.  
    321.   crn_format m_format;  // Output pixel format.
    322.  
    323.   crn_uint32 m_flags;  // see crn_comp_flags enum
    324.  
    325.   // Array of pointers to 32bpp input images.
    326.   const crn_uint32* m_pImages[cCRNMaxFaces][cCRNMaxLevels];
    327.  
    328.   // Target bitrate - if non-zero, the compressor will use an interpolative search to find the
    329.   // highest quality level that is <= the target bitrate. If it fails to find a bitrate high enough, it'll
    330.   // try disabling adaptive block sizes (cCRNCompFlagHierarchical flag) and redo the search. This process can be pretty slow.
    331.   float m_target_bitrate;
    332.  
    333.   // Desired quality level.
    334.   // Currently, CRN and DDS quality levels are not compatible with eachother from an image quality standpoint.
    335.   crn_uint32 m_quality_level;  // [cCRNMinQualityLevel, cCRNMaxQualityLevel]
    336.  
    337.   // DXTn compression parameters.
    338.   crn_uint32 m_dxt1a_alpha_threshold;
    339.   crn_dxt_quality m_dxt_quality;
    340.   crn_dxt_compressor_type m_dxt_compressor_type;
    341.  
    342.   // Alpha channel's component. Defaults to 3.
    343.   crn_uint32 m_alpha_component;
    344.  
    345.   // Various low-level CRN specific parameters.
    346.   float m_crn_adaptive_tile_color_psnr_derating;
    347.   float m_crn_adaptive_tile_alpha_psnr_derating;
    348.  
    349.   crn_uint32 m_crn_color_endpoint_palette_size;  // [cCRNMinPaletteSize,cCRNMaxPaletteSize]
    350.   crn_uint32 m_crn_color_selector_palette_size;  // [cCRNMinPaletteSize,cCRNMaxPaletteSize]
    351.  
    352.   crn_uint32 m_crn_alpha_endpoint_palette_size;  // [cCRNMinPaletteSize,cCRNMaxPaletteSize]
    353.   crn_uint32 m_crn_alpha_selector_palette_size;  // [cCRNMinPaletteSize,cCRNMaxPaletteSize]
    354.  
    355.   // Number of helper threads to create during compression. 0=no threading.
    356.   crn_uint32 m_num_helper_threads;
    357.  
    358.   // CRN userdata0 and userdata1 members, which are written directly to the header of the output file.
    359.   crn_uint32 m_userdata0;
    360.   crn_uint32 m_userdata1;
    361.  
    362.   // User provided progress callback.
    363.   crn_progress_callback_func m_pProgress_func;
    364.   void* m_pProgress_func_data;
    365. };
    366.  
    367. // Mipmap generator's mode.
    368. enum crn_mip_mode {
    369.   cCRNMipModeUseSourceOrGenerateMips,  // Use source texture's mipmaps if it has any, otherwise generate new mipmaps
    370.   cCRNMipModeUseSourceMips,            // Use source texture's mipmaps if it has any, otherwise the output has no mipmaps
    371.   cCRNMipModeGenerateMips,             // Always generate new mipmaps
    372.   cCRNMipModeNoMips,                   // Output texture has no mipmaps
    373.  
    374.   cCRNMipModeTotal,
    375.  
    376.   cCRNModeForceDWORD = 0xFFFFFFFF
    377. };
    378.  
    379. const char* crn_get_mip_mode_desc(crn_mip_mode m);
    380. const char* crn_get_mip_mode_name(crn_mip_mode m);
    381.  
    382. // Mipmap generator's filter kernel.
    383. enum crn_mip_filter {
    384.   cCRNMipFilterBox,
    385.   cCRNMipFilterTent,
    386.   cCRNMipFilterLanczos4,
    387.   cCRNMipFilterMitchell,
    388.   cCRNMipFilterKaiser,  // Kaiser=default mipmap filter
    389.  
    390.   cCRNMipFilterTotal,
    391.  
    392.   cCRNMipFilterForceDWORD = 0xFFFFFFFF
    393. };
    394.  
    395. const char* crn_get_mip_filter_name(crn_mip_filter f);
    396.  
    397. // Mipmap generator's scale mode.
    398. enum crn_scale_mode {
    399.   cCRNSMDisabled,
    400.   cCRNSMAbsolute,
    401.   cCRNSMRelative,
    402.   cCRNSMLowerPow2,
    403.   cCRNSMNearestPow2,
    404.   cCRNSMNextPow2,
    405.  
    406.   cCRNSMTotal,
    407.  
    408.   cCRNSMForceDWORD = 0xFFFFFFFF
    409. };
    410.  
    411. const char* crn_get_scale_mode_desc(crn_scale_mode sm);
    412.  
    413. // Mipmap generator parameters.
    414. struct crn_mipmap_params {
    415.   inline crn_mipmap_params() { clear(); }
    416.  
    417.   inline void clear() {
    418.     m_size_of_obj = sizeof(*this);
    419.     m_mode = cCRNMipModeUseSourceOrGenerateMips;
    420.     m_filter = cCRNMipFilterKaiser;
    421.     m_gamma_filtering = true;
    422.     m_gamma = 2.2f;
    423.     // Default "blurriness" factor of .9 actually sharpens the output a little.
    424.     m_blurriness = .9f;
    425.     m_renormalize = false;
    426.     m_tiled = false;
    427.     m_max_levels = cCRNMaxLevels;
    428.     m_min_mip_size = 1;
    429.  
    430.     m_scale_mode = cCRNSMDisabled;
    431.     m_scale_x = 1.0f;
    432.     m_scale_y = 1.0f;
    433.  
    434.     m_window_left = 0;
    435.     m_window_top = 0;
    436.     m_window_right = 0;
    437.     m_window_bottom = 0;
    438.  
    439.     m_clamp_scale = false;
    440.     m_clamp_width = 0;
    441.     m_clamp_height = 0;
    442.   }
    443.  
    444.   inline bool check() const { return true; }
    445.  
    446.   inline bool operator==(const crn_mipmap_params& rhs) const {
    447. #define CRNLIB_COMP(x)  \
    448.   do {                  \
    449.     if ((x) != (rhs.x)) \
    450.       return false;     \
    451.   } while (0)
    452.     CRNLIB_COMP(m_size_of_obj);
    453.     CRNLIB_COMP(m_mode);
    454.     CRNLIB_COMP(m_filter);
    455.     CRNLIB_COMP(m_gamma_filtering);
    456.     CRNLIB_COMP(m_gamma);
    457.     CRNLIB_COMP(m_blurriness);
    458.     CRNLIB_COMP(m_renormalize);
    459.     CRNLIB_COMP(m_tiled);
    460.     CRNLIB_COMP(m_max_levels);
    461.     CRNLIB_COMP(m_min_mip_size);
    462.     CRNLIB_COMP(m_scale_mode);
    463.     CRNLIB_COMP(m_scale_x);
    464.     CRNLIB_COMP(m_scale_y);
    465.     CRNLIB_COMP(m_window_left);
    466.     CRNLIB_COMP(m_window_top);
    467.     CRNLIB_COMP(m_window_right);
    468.     CRNLIB_COMP(m_window_bottom);
    469.     CRNLIB_COMP(m_clamp_scale);
    470.     CRNLIB_COMP(m_clamp_width);
    471.     CRNLIB_COMP(m_clamp_height);
    472.     return true;
    473. #undef CRNLIB_COMP
    474.   }
    475.   crn_uint32 m_size_of_obj;
    476.  
    477.   crn_mip_mode m_mode;
    478.   crn_mip_filter m_filter;
    479.  
    480.   crn_bool m_gamma_filtering;
    481.   float m_gamma;
    482.  
    483.   float m_blurriness;
    484.  
    485.   crn_uint32 m_max_levels;
    486.   crn_uint32 m_min_mip_size;
    487.  
    488.   crn_bool m_renormalize;
    489.   crn_bool m_tiled;
    490.  
    491.   crn_scale_mode m_scale_mode;
    492.   float m_scale_x;
    493.   float m_scale_y;
    494.  
    495.   crn_uint32 m_window_left;
    496.   crn_uint32 m_window_top;
    497.   crn_uint32 m_window_right;
    498.   crn_uint32 m_window_bottom;
    499.  
    500.   crn_bool m_clamp_scale;
    501.   crn_uint32 m_clamp_width;
    502.   crn_uint32 m_clamp_height;
    503. };
    504.  
    505. // -------- High-level helper function definitions for CDN/DDS compression.
    506.  
    507. #ifndef CRNLIB_MIN_ALLOC_ALIGNMENT
    508. #define CRNLIB_MIN_ALLOC_ALIGNMENT sizeof(size_t) * 2
    509. #endif
    510.  
    511. // Function to set an optional user provided memory allocation/reallocation/msize routines.
    512. // By default, crnlib just uses malloc(), free(), etc. for all allocations.
    513. typedef void* (*crn_realloc_func)(void* p, size_t size, size_t* pActual_size, bool movable, void* pUser_data);
    514. typedef size_t (*crn_msize_func)(void* p, void* pUser_data);
    515. void crn_set_memory_callbacks(crn_realloc_func pRealloc, crn_msize_func pMSize, void* pUser_data);
    516.  
    517. #ifdef TESTFUNCDLL_EXPORT
    518. #define TESTFUNCDLL_API __declspec(dllexport)
    519. #else
    520. #define TESTFUNCDLL_API __declspec(dllimport)
    521. #endif
    522.  
    523. extern "C"
    524. {
    525.  
    526. // Frees memory blocks allocated by crn_compress(), crn_decompress_crn_to_dds(), or crn_decompress_dds_to_images().
    527.     TESTFUNCDLL_API void crn_free_block(void* pBlock);
    528.  
    529. // Compresses a 32-bit/pixel texture to either: a regular DX9 DDS file, a "clustered" (or reduced entropy) DX9 DDS file, or a CRN file in memory.
    530. // Input parameters:
    531. //  comp_params is the compression parameters struct, defined above.
    532. //  compressed_size will be set to the size of the returned memory block containing the output file.
    533. //  The returned block must be freed by calling crn_free_block().
    534. //  *pActual_quality_level will be set to the actual quality level used to compress the image. May be NULL.
    535. //  *pActual_bitrate will be set to the output file's effective bitrate, possibly taking into account LZMA compression. May be NULL.
    536. // Return value:
    537. //  The compressed file data, or NULL on failure.
    538. //  compressed_size will be set to the size of the returned memory buffer.
    539. // Notes:
    540. //  A "regular" DDS file is compressed using normal DXTn compression at the specified DXT quality level.
    541. //  A "clustered" DDS file is compressed using clustered DXTn compression to either the target bitrate or the specified integer quality factor.
    542. //  The output file is a standard DX9 format DDS file, except the compressor assumes you will be later losslessly compressing the DDS output file using the LZMA algorithm.
    543. //  A texture is defined as an array of 1 or 6 "faces" (6 faces=cubemap), where each "face" consists of between [1,cCRNMaxLevels] mipmap levels.
    544. //  Mipmap levels are simple 32-bit 2D images with a pitch of width*sizeof(uint32), arranged in the usual raster order (top scanline first).
    545. //  The image pixels may be grayscale (YYYX bytes in memory), grayscale/alpha (YYYA in memory), 24-bit (RGBX in memory), or 32-bit (RGBA) colors (where "X"=don't care).
    546. //  RGB color data is generally assumed to be in the sRGB colorspace. If not, be sure to clear the "cCRNCompFlagPerceptual" in the crn_comp_params struct!
    547.  
    548.  
    549.     TESTFUNCDLL_API void* crn_compress(crn_uint32* imagedata,
    550.         crn_uint32 width,
    551.         crn_uint32 height,
    552.         crn_uint32 format,
    553.         crn_uint32 flags,
    554.         crn_uint32 quality_level,
    555.         crn_bool create_mipmaps,
    556.         crn_uint32& compressed_size,
    557.         crn_uint32* pActual_quality_level,
    558.         float* pActual_bitrate);
    559.  
    560.     TESTFUNCDLL_API void* crn_decompress(
    561.         const void* pCRN_file_data,
    562.         crn_uint32& file_size);
    563.  
    564.     TESTFUNCDLL_API bool crn_decompress_dds_to_images(
    565.         const void* pDDS_file_data,
    566.         crn_uint32 dds_file_size,
    567.         crn_uint32** ppImages,
    568.         crn_uint32& faces,
    569.         crn_uint32& levels,
    570.         crn_uint32& width,
    571.         crn_uint32& height);
    572.  
    573.     TESTFUNCDLL_API void crn_free_all_images(crn_uint32** ppImages, crn_uint32 faces, crn_uint32 levels);
    574. }
    575.  
    576.     void* crn_compress(const crn_comp_params& comp_params, crn_uint32& compressed_size, crn_uint32* pActual_quality_level = NULL, float* pActual_bitrate = NULL);
    577.  
    578.     // Like the above function, except this function can also do things like generate mipmaps, and resize or crop the input texture before compression.
    579.     // The actual operations performed are controlled by the crn_mipmap_params struct members.
    580.     // Be sure to set the "m_gamma_filtering" member of crn_mipmap_params to false if the input texture is not sRGB.
    581.     void* crn_compress(const crn_comp_params& comp_params, const crn_mipmap_params& mip_params, crn_uint32& compressed_size, crn_uint32* pActual_quality_level = NULL, float* pActual_bitrate = NULL);
    582.  
    583.     // Transcodes an entire CRN file to DDS using the crn_decomp.h header file library to do most of the heavy lifting.
    584.     // The output DDS file's format is guaranteed to be one of the DXTn formats in the crn_format enum.
    585.     // This is a fast operation, because the CRN format is explicitly designed to be efficiently transcodable to DXTn.
    586.     // For more control over decompression, see the lower-level helper functions in crn_decomp.h, which do not depend at all on crnlib.
    587.     void* crn_decompress_crn_to_dds(const void* pCRN_file_data, crn_uint32& file_size);
    588.  
    589.  
    590.     // Decompresses an entire DDS file in any supported format to uncompressed 32-bit/pixel image(s).
    591.     // See the crnlib::pixel_format enum in inc/dds_defs.h for a list of the supported DDS formats.
    592.     // You are responsible for freeing each image block, either by calling crn_free_all_images() or manually calling crn_free_block() on each image pointer.
    593.     struct crn_texture_desc {
    594.         crn_uint32 m_faces;
    595.         crn_uint32 m_width;
    596.         crn_uint32 m_height;
    597.         crn_uint32 m_levels;
    598.         crn_uint32 m_fmt_fourcc;  // Same as crnlib::pixel_format
    599.     };
    600.  
    601.     bool crn_decompress_dds_to_images(const void* pDDS_file_data, crn_uint32 dds_file_size, crn_uint32** ppImages, crn_texture_desc& tex_desc);
    602.  
    603.     // -------- crn_format related helpers functions.
    604.  
    605.     // Returns the FOURCC format equivalent to the specified crn_format.
    606.     crn_uint32 crn_get_format_fourcc(crn_format fmt);
    607.  
    608.     // Returns the crn_format's bits per texel.
    609.     crn_uint32 crn_get_format_bits_per_texel(crn_format fmt);
    610.  
    611.     // Returns the crn_format's number of bytes per block.
    612.     crn_uint32 crn_get_bytes_per_dxt_block(crn_format fmt);
    613.  
    614.     // Returns the non-swizzled, basic DXTn version of the specified crn_format.
    615.     // This is the format you would supply D3D or OpenGL.
    616.     crn_format crn_get_fundamental_dxt_format(crn_format fmt);
    617.  
    618.     // -------- String helpers.
    619.  
    620.     // Converts a crn_file_type to a string.
    621.     const char* crn_get_file_type_ext(crn_file_type file_type);
    622.  
    623.     // Converts a crn_format to a string.
    624.     const char* crn_get_format_string(crn_format fmt);
    625.  
    626.     // Converts a crn_dxt_quality to a string.
    627.     const char* crn_get_dxt_quality_string(crn_dxt_quality q);
    628.  
    629.     // -------- Low-level DXTn 4x4 block compressor API
    630.  
    631.     // crnlib's DXTn endpoint optimizer actually supports any number of source pixels (i.e. from 1 to thousands, not just 16),
    632.     // but for simplicity this API only supports 4x4 texel blocks.
    633.     typedef void* crn_block_compressor_context_t;
    634.  
    635.     // Create a DXTn block compressor.
    636.     // This function only supports the basic/nonswizzled "fundamental" formats: DXT1, DXT3, DXT5, DXT5A, DXN_XY and DXN_YX.
    637.     // Avoid calling this multiple times if you intend on compressing many blocks, because it allocates some memory.
    638.     crn_block_compressor_context_t crn_create_block_compressor(const crn_comp_params& params);
    639.  
    640.     // Compresses a block of 16 pixels to the destination DXTn block.
    641.     // pDst_block should be 8 (for DXT1/DXT5A) or 16 bytes (all the others).
    642.     // pPixels should be an array of 16 crn_uint32's. Each crn_uint32 must be r,g,b,a (r is always first) in memory.
    643.     void crn_compress_block(crn_block_compressor_context_t pContext, const crn_uint32* pPixels, void* pDst_block);
    644.  
    645.     // Frees a DXTn block compressor.
    646.     void crn_free_block_compressor(crn_block_compressor_context_t pContext);
    647.  
    648.     // Unpacks a compressed block to pDst_pixels.
    649.     // pSrc_block should be 8 (for DXT1/DXT5A) or 16 bytes (all the others).
    650.     // pDst_pixel should be an array of 16 crn_uint32's. Each uint32 will be r,g,b,a (r is always first) in memory.
    651.     // crn_fmt should be one of the "fundamental" formats: DXT1, DXT3, DXT5, DXT5A, DXN_XY and DXN_YX.
    652.     // The various swizzled DXT5 formats (such as cCRNFmtDXT5_xGBR, etc.) will be unpacked as if they where plain DXT5.
    653.     // Returns false if the crn_fmt is invalid.
    654.     bool crn_decompress_block(const void* pSrc_block, crn_uint32* pDst_pixels, crn_format crn_fmt);
    655.  
    656. #endif  // CRNLIB_H
    657.  
    658. //------------------------------------------------------------------------------
    659. //
    660. // crnlib uses the ZLIB license:
    661. // http://opensource.org/licenses/Zlib
    662. //
    663. // Copyright (c) 2010-2016 Richard Geldreich, Jr. and Binomial LLC
    664. //
    665. // This software is provided 'as-is', without any express or implied
    666. // warranty.  In no event will the authors be held liable for any damages
    667. // arising from the use of this software.
    668. //
    669. // Permission is granted to anyone to use this software for any purpose,
    670. // including commercial applications, and to alter it and redistribute it
    671. // freely, subject to the following restrictions:
    672. //
    673. // 1. The origin of this software must not be misrepresented; you must not
    674. // claim that you wrote the original software. If you use this software
    675. // in a product, an acknowledgment in the product documentation would be
    676. // appreciated but is not required.
    677. //
    678. // 2. Altered source versions must be plainly marked as such, and must not be
    679. // misrepresented as being the original software.
    680. //
    681. // 3. This notice may not be removed or altered from any source distribution.
    682. //
    683. //------------------------------------------------------------------------------
    684.  


    Here is my crunch utility script

    Code (CSharp):
    1. using System;
    2. using System.Runtime.InteropServices;
    3. using System.Text;
    4. using UnityEngine;
    5.  
    6. namespace VGS.WB
    7. {
    8.     public class VGS_WBCrunchUtility
    9.     {
    10.         /*
    11.         private uint width;                                 // Power 2 and Non-Square are both acceptable
    12.         private uint height;                                // Power 2 and Non-Square are both acceptable
    13.         private uint format;                                // Output pixel format  [ DXT1 = 0  DXT 5 = 4 ]
    14.         private uint flags;                                 // Bitwise OR
    15.                                                             // Perceptual  1  ( Disable for non-rgb color space textures like normal maps ) Default Set
    16.                                                             // Hierarchical 2 ( Compression ration lower when disbaled but can cut down on artifacts ) Default Set
    17.                                                             // Both Block Types ( Currently only used when writing DDS ) Default Set
    18.                                                             // Therefore
    19.                                                             // Albedo / Metallic / Specular   flags = 2
    20.                                                             // Normal Maps  flags = 3
    21.                                                             */
    22.  
    23.         // Straight From the c++ Dll (unmanaged)
    24.         [DllImport("crnlib")]
    25.         private static extern IntPtr crn_compress(UIntPtr image, uint width, uint height, uint format, uint flags, uint quality, uint create_mipmaps, out uint compressed_size, out uint quality_level, out float bitrate);
    26.  
    27.         [DllImport("crnlib")]
    28.         private static extern void crn_free_block(IntPtr ptr);
    29.  
    30.         [DllImport("crnlib")]
    31.         private static extern IntPtr crn_decompress(IntPtr ptr, out uint file_size);
    32.  
    33.         /// <summary>
    34.         /// Crunches DXT1, DXT5 texture formats to CRN
    35.         /// </summary>
    36.         /// <param name="tex">Image Data</param>
    37.         /// <param name="nonrgb">Normal Map Textures Set to False</param>
    38.         /// <param name="mipmaps">Generate mip maps</param>
    39.         /// <param name="quality">0 (Min) 255 (Max) Default 128</param>
    40.         /// <returns></returns>
    41.         static public byte[] Crunch(Texture2D tex, bool nonrgb, bool create_mipmaps, uint quality, bool verbose = false)
    42.         {
    43.             byte[] returnedResult = null;
    44.  
    45.             // Check the format
    46.             if (tex.format == TextureFormat.DXT1 || tex.format == TextureFormat.DXT5)
    47.             {
    48.                 uint width = (uint)tex.width;
    49.                 uint height = (uint)tex.height;
    50.                 uint format = (uint)(tex.format == TextureFormat.DXT1 ? 0 : 2);
    51.                 uint flags = (uint)(nonrgb ? 11 : 10);
    52.  
    53.                 // Create an array of uint32 from the pixel colors of the texture
    54.                 uint[] image = CreateImage(tex.GetPixels32());
    55.  
    56.                 // Calculate the data length
    57.                 int data_length = image.Length * sizeof(uint);
    58.  
    59.                 // Allocate the unmanaged array
    60.                 IntPtr unmanagedArray = Marshal.AllocHGlobal(data_length);
    61.  
    62.                 // Copy the image to a byte array
    63.                 byte[] result = new byte[data_length];
    64.                 Buffer.BlockCopy(image, 0, result, 0, data_length);
    65.  
    66.                 // Copy the byte array to an unmanaged array
    67.                 Marshal.Copy(result, 0, unmanagedArray, data_length);
    68.  
    69.                 // Define the output parameters
    70.                 uint compression_size;
    71.                 uint quality_level;
    72.                 float bitrate;
    73.  
    74.                 // Crunch the image data and return a pointer to the crunched result array
    75.                 IntPtr returnedPtr = crn_compress(IntPtrToUintPtr(unmanagedArray), width, height, format, flags, quality, (uint)(create_mipmaps ? 1 : 0), out compression_size, out quality_level, out bitrate);
    76.  
    77.                 // Allocate space for the crunched byte array
    78.                 returnedResult = new byte[compression_size];
    79.  
    80.                 // Copy the heap array block to the return result
    81.                 Marshal.Copy(returnedPtr, returnedResult, 0, (int)compression_size);
    82.  
    83.                 // Free the memory block
    84.                 crn_free_block(returnedPtr);
    85.  
    86.                 // Free the unmanaged memory
    87.                 Marshal.FreeHGlobal(unmanagedArray);
    88.  
    89.                 // Verbose the result
    90.                 if (verbose)
    91.                 {
    92.                     Debug.LogFormat("[Crunch] Format : {0}  Width : {1}  Height : {2}  Non RGB : {3}  Create MipMaps : {4}  CRN Size : {5} kb   Quality Level {6}   Bitrate {7}",
    93.                         tex.format.ToString(),
    94.                         width,
    95.                         height,
    96.                         nonrgb,
    97.                         create_mipmaps,
    98.                         compression_size / 1024,
    99.                         quality_level,
    100.                         bitrate);
    101.                 }
    102.             }
    103.  
    104.             return returnedResult;
    105.         }
    106.  
    107.         static public byte[] CRN_To_DDS(byte[] data, bool verbose = false)
    108.         {
    109.             // Allocate the unmanaged array
    110.             IntPtr unmanagedArray = Marshal.AllocHGlobal(data.Length);
    111.  
    112.             // Copy the data to a byte array
    113.             byte[] result = new byte[data.Length];
    114.             Buffer.BlockCopy(data, 0, result, 0, data.Length);
    115.  
    116.             // Copy the byte array to an unmanaged array
    117.             Marshal.Copy(result, 0, unmanagedArray, data.Length);
    118.  
    119.             uint file_size = (uint)data.Length;
    120.  
    121.             // Get the DDS data from the CRN data
    122.             IntPtr returnedPtr = crn_decompress(unmanagedArray, out file_size);
    123.  
    124.             // Allocate space for the decrunched byte array
    125.             byte[] returnedResult = new byte[file_size];
    126.  
    127.             // Copy the heap array block to the return result
    128.             Marshal.Copy(returnedPtr, returnedResult, 0, (int)file_size);
    129.  
    130.             // Free the memory block
    131.             crn_free_block(returnedPtr);
    132.  
    133.             // Free the unmanaged memory
    134.             Marshal.FreeHGlobal(unmanagedArray);
    135.  
    136.             if (verbose)
    137.                 Debug.LogFormat("Transcoded CRN Size : {0} kb to DDS Size : {1} kb", data.Length / 1024, file_size / 1024);
    138.  
    139.             return returnedResult;
    140.         }
    141.  
    142.         public static Texture2D LoadTextureDDS(byte[] ddsBytes, bool mipmaps, bool verbose = false)
    143.         {
    144.             // Discover the format from the DDS data
    145.             StringBuilder formatStr = new StringBuilder();
    146.             formatStr.Append(Convert.ToChar(ddsBytes[84]));
    147.             formatStr.Append(Convert.ToChar(ddsBytes[85]));
    148.             formatStr.Append(Convert.ToChar(ddsBytes[86]));
    149.             formatStr.Append(Convert.ToChar(ddsBytes[87]));
    150.  
    151.             // Check to ensure the DXT1 or DXT5 was found
    152.             if (formatStr.ToString() != "DXT1" && formatStr.ToString() != "DXT5")
    153.                 throw new Exception("Invalid TextureFormat. Only DXT1 and DXT5 formats are supported by this method.");
    154.  
    155.             // Set the texture format
    156.             TextureFormat textureFormat = (formatStr.ToString() == "DXT1" ? TextureFormat.DXT1 : TextureFormat.DXT5);
    157.  
    158.             byte ddsSizeCheck = ddsBytes[4];
    159.             if (ddsSizeCheck != 124)
    160.                 throw new Exception("Invalid DDS DXTn texture. Unable to read");  //this header byte should be 124 for DDS image files
    161.  
    162.             int height = ddsBytes[13] * 256 + ddsBytes[12];
    163.             int width = ddsBytes[17] * 256 + ddsBytes[16];
    164.  
    165.  
    166.             int DDS_HEADER_SIZE = 128;
    167.             byte[] dxtBytes = new byte[ddsBytes.Length - DDS_HEADER_SIZE];
    168.             Buffer.BlockCopy(ddsBytes, DDS_HEADER_SIZE, dxtBytes, 0, ddsBytes.Length - DDS_HEADER_SIZE);
    169.  
    170.             Texture2D texture = new Texture2D(width, height, textureFormat, mipmaps);
    171.  
    172.             texture.LoadRawTextureData(dxtBytes);
    173.             texture.Apply();
    174.  
    175.             if (verbose)
    176.                 Debug.LogFormat("Valid DDS Texture Format : {0}  Width : {1}  Height : {2}  MipMaps : {3}", formatStr.ToString(), width, height, texture.mipmapCount);
    177.  
    178.             return (texture);
    179.         }
    180.  
    181.  
    182.         public static UIntPtr IntPtrToUintPtr(IntPtr ptr)
    183.         {
    184.             if (IntPtr.Size == 4)
    185.                 return unchecked((UIntPtr)(uint)(int)ptr);
    186.  
    187.             return unchecked((UIntPtr)(ulong)(long)ptr);
    188.         }
    189.  
    190.         static private Color32[] ConvertImageLevelToColor32(uint[] image)
    191.         {
    192.             Color32[] colors = new Color32[image.Length];
    193.  
    194.             for (int i = 0; i < colors.Length; i++)
    195.                 colors[i] = ConvertUINTToColor32(image[i]);
    196.  
    197.             return colors;
    198.         }
    199.  
    200.         static private uint[] CreateImage(Color32[] colors)
    201.         {
    202.             uint[] image = new uint[colors.Length];
    203.  
    204.             for (int i = 0; i < colors.Length; i++)
    205.                 image[i] = ConvertColor32ToUINT(colors[i]);
    206.  
    207.             return image;
    208.         }
    209.  
    210.         static private Color32 ConvertUINTToColor32(uint aCol)
    211.         {
    212.             Color32 c = Color.white;
    213.             c.b = (byte)((aCol) & 0xFF);
    214.             c.g = (byte)((aCol >> 8) & 0xFF);
    215.             c.r = (byte)((aCol >> 16) & 0xFF);
    216.             c.a = (byte)((aCol >> 24) & 0xFF);
    217.             return c;
    218.         }
    219.  
    220.         static private uint ConvertColor32ToUINT(Color32 c)
    221.         {
    222.             return (uint)(c.r | (c.g << 8) | (c.b << 16) | (c.a << 24));
    223.         }
    224.     }
    225. }
    226.  
     
    Last edited: Jan 1, 2020
  4. daniel5johansson

    daniel5johansson

    Joined:
    Jun 21, 2018
    Posts:
    7
    Hi, I'd really like to try this out, to see if it's fast enough to do real-time decompression of a stream of 4K textures.

    @GXMark:
    Do I need VS2010 to build the crnlib dll? I tried importing it into VS2017 that came with Unity but I got over 600 errors,
    * cannot open source file "errno.h"
    * cannot open source file "float.h"
    * cannot open source file "stddef.h"
    etc etc etc..
    Thanks