alecazam / kram

Encode/decode/info to KTX/KTX2/DDS files with LDR/HDR and BC/ASTC/ETC2. Mac/Win C++11 too, Mac viewer, and scripts for batch processing textures.
MIT License
153 stars 9 forks source link

DDS to KTX conversion #8

Closed Ben-Mack closed 2 years ago

Ben-Mack commented 2 years ago

Currently no tool on the market provide DDS to KTX conversion but still keep the compressed format in DDS as is. Do you think it is possible? It'd great if kram has such feature.

alecazam commented 2 years ago

I've written a lot of DDS importers/exporters and have been trying to avoid it, but it is a container format with more support than KTX/KTX2 at least on Windows. It's a messy format since there are two versions of DDS. The new macOS has DDS support in Finder.

I think you are suggesting that most importers first decode the blocks, and then they get re-encoded. I think if it was already BC1-7 or ASTC, then it would be pretty trivial to convert and keep them as blocks as a pass-through. It would just be a memcpy of the blocks and meeting the alignment of KTX/KTX2. Also mips, arrays, cube faces, etc may lie in a different ordering than KTX/KTX2. The problem is many exporters export to the old format, but ASTC I believe needs the new DDS format.

Ben-Mack commented 2 years ago

Thanks for your responding. My use case is the simplest form, input is a BC7 encoded DDS without any mips, arrays, cube... Just align the blocks from DDS to match KTX format is perfect.

alecazam commented 2 years ago

I've tried not to introduce holes like that. If anything, it would need to support all types. The only thing kram doesn't support are 1D textures.

alecazam commented 2 years ago

I have DDS load/save support on the way. I need dds export for some of the Windows-based tools out there that only support DDS.

Note that most of the pipeline assumes uncompressed data when saving out which is why many tools decompress first. Will need a flag to bypass that after the dds -> ktx conversion, and then just save the ktx directly.

alecazam commented 2 years ago

Okay, the DDS load/save support is in. But you can't yet go from DDS to KTX/KTX2. That save logic is all entangled in the encode call right now. But DDS can now be used as input/output from kram, and kramv will view it.

Just need a simple save for KTXImage that runs logic on existing blocks/mips from a file. For example, ktx2 would need to supercompress the levels if that's requested. But you can load non-compressed DDS files and build mips and KTX/KTX2 off them.

I mostly needed DDS export to interface with Windows tools. But DDS is a pretty outdated format with Microsoft not updating it since DX10 era.

alecazam commented 2 years ago

Okay Ben, I added DDS load to KTX save. Try it out. This should do a pass-through where the dds loads, and a ktx is written out with the same encoded blocks. This has to re-order the blocks from DDS to KTX.

I probably need to add a case when KTXImage is aliasing the data from the file. Right now it's assuming image.imageData().data() isn't empty.

Example of encoding to png -> dds encode -v -f bc7 -srgb -premul -type 2d -i kram/tests/src/Toof-a.png -o kram/tests/out/mac/Toof-a.dds

Example of pass-through dds -> ktx (using dds output from above) kram encode -f bc7 -v -type 2d -i kram/tests/out/mac/Toof-a.dds -o kram/tests/out/mac/Toof-a-test.ktx

Format doesn't matter here, since it's just pass-through. I'll close this item soon, but will leave open until the code is tested and a little more robust. The code should allow inputs from dds, ktx, png, ktx2, and then encode them if they're uncompressed. If DDS is compressed, then it' pass-through. Could probably have a similar path for block-compressed ktx/ktx2 files too. That way custom mips would pass-through.

Ben-Mack commented 2 years ago

Thank you for implemented this! I tried to open the ktx result with Renderdoc but got following error: image

alecazam commented 2 years ago

That is the magic for ktx. See the first 4 bytes, although it's printed out in the wrong order.

const uint8_t kKTXIdentifier[kKTXIdentifierSize] = { 0xAB, 0x4B, 0x54, 0x58, 0x20, 0x31, 0x31, 0xBB, 0x0D, 0x0A, 0x1A, 0x0A //'«', 'K', 'T', 'X', ' ', '1', '1', '»', '\r', '\n', '\x1A', '\n' };

With that said, I can't open the ktx that results in kram. It's reporting that there is a level size mismatch. All the code is in saveKTX1.

alecazam commented 2 years ago

Okay, I just pushed fixes for saveKTX1, and I can open the files in kramv. Give it a shot once the github actions build the executables.

alecazam commented 2 years ago

It's not setting array count or pixelDepth to 0, so the code thinks it's a 2D array currently. Should be fixed now.

alecazam commented 2 years ago

Since I didn't hear anything back, going to close this as fixed.