-
Notifications
You must be signed in to change notification settings - Fork 675
Description
Hi. I'm the author of the DDS decoder PR and I wanted to talk about the future of DDS support in the image crate. I would like to work together to add support for reading and writing various DDS image formats to the image crate.
What's DDS
This section is meant as a refresher to make sure we're all on the same page.
DDS is container format for various compressed and uncompressed image formats. Its main purpose is to store textures for graphics applications (e.g. video games) as modern GPUs support DDS natively, allowing for the use of compressed textures directly on the GPU.
DDS also supports more than just simple images. Cube maps, 1D arrays, 2D images, 3D volumes, and arrays of all of the above (except volumes) are supported. And everything can have any number of mip maps. So a single DDS file can contain between 0 (empty array) and >2^32 images (if mip maps are included).
Furthermore, DDS supports a large number of image formats. The DXGI_FORMAT enum contains roughly 40 image formats that are relevant, but there are around 10 to 20 more that are used in old DDS files (D3DFormat). The complexity of these formats varies widely. Formats like R8G8B8A8_UNORM that can be "decoded" by just memcpy-ing bytes from disk, but formats like BC6 and BC7 require around 500-1000 LOC to decode a single block.
The present
Right now, DDS support is severely lacking. Only reading DDS files is supported, and only 3 (BC1/2/3) out the over 40 relevant DXGI formats are supported. Furthermore, those 3 formats are implemented incorrectly (*), causing noticeable discolorations for certain images.
(*) I'm saying "incorrectly", but they are technically spec-compliant. The spec allows +-3% error. Such an error is permissible for real-time graphics, where textures are samples millions of times per frame, but I believe that it is not suitable to allow such errors in a general-purpose image library.
Due to these short-comings, the usefulness of the DDS support in the image crate is currently very limited.
Given the comments on my old PR, there clearly seems to be interest in having the current situation improved, and I would like to help do just that.
What I have to offer
Over the last 2~3 months I've been working on a DDS de/encoder written in safe Rust. It currently supports 58 formats for decoding and 55 for encoding. The library currently has the following features:
- Parsing: It can parse the header of a DDS into a high-level representation of a DDS header (
Headerenum) and interpret that header to determine the exact layout of the data section (DataLayoutenum). It can also detect and auto fix errors in the header, so old slightly incorrect DDS files can be read without issue. - Decoding: Any of the supported formats can be decoded into a selected color format. So you can decode e.g. BC1_UNORM as RGBA_U8 or GRAY_F32 or similar. The library supports 12 color formats and will handle any necessary conversions. Of course, the native/original color format of the image format should be preferred. Decoding rects is also supported for all formats.
- Encoding: It's supported for all formats except BC6 and BC7. Images of any color format are supported for encoding (again, the library handles any necessary conversions). Error diffusion dithering is supported for some low-bit-depth formats. BC{1,2,3} also support a perceptual error metric based on Oklab for high-quality encoding.
- Speed: I benchmark and I optimize. Decoding a 4k texture takes around 100-200ms. The slowest format is BC6 with around 400ms. The other BCn formats are all around 50-100ms. Encoding is generally slower. Uncompressed formats take around 200-400ms for a 4k texture, but BCn formats are really slow. Encoding a 1k texture takes around 1-2sec for BC1 and around 300ms for BC4 on a single thread and at high quality settings.
- Correctness: There's lots of new code, so I have lots of tests. All formats are tested with snapshots, leading to around 400 snapshot images + many more snapshots, ensuring that all formats are correctly decoded and encoded. I also extensively compared the snapshots to DirectXTex by Microsoft. While my outputs aren't bit-by-bit equivalent for various reasons (mostly because DirectXTex likes to use fast approximations, while I don't), results are generally within 1 bit (u8) of DirectXTex with no visual difference. Of course, BC6/7 are bit-exact, as required by the spec.
- Few dependencies: I only use
bitflags,zerocopy, andglam. Onlybitflagsis a public dependency. (Also,glamis only used for encoding. I just needed a small vector library, so it can be replaced if need be.)
I also want to say that I wrote everything from scratch. Honestly, I didn't think it would so much code, but it currently stands at ~12k LOC (not including tests and comments).
Note that everything is still under development, so please expect things to change and improve.
Collaborate
My goal is to use this library as a dependency for the image crate to power its DDS support. So I wish to collaborate to make it happen.
In particular, this is my first image library and my first crate to publish. So I would like to get feedback to 1) make the library more suitable for the image crate and 2) improve the library in general (I'm sure I made some suboptimal design decisions...).
I also want to say that I view the library in its current format more as a low-level primitive than a high-level DDS library. It can de/encoded single images with a DDS files, yes, but that's it. There's no high-level API de/encoding whole DDS files yet, and I haven't even thought about mipmap generation yet. So some pointers for making a high-level API would be appreciated.
Honestly, my ideal outcome would be to have the DDS library under the image-rs organization, so that it can be maintained and improved even when I, one day, don't have the time to do so anymore. You guys seem to know a little about maintaining image libraries :)
Please tell me what you think.
@HeroicKatora @fintelia