Skip to content

Conversation

bmdhacks
Copy link

ASTC is a modern texture compression format that is ubiquitously supported in almost every mobile processor. It provides better quality, better compression, and more options than all other formats and has emerged as the industry standard for compressed textures in mobile devices.

This patch adds and extends ASTC support in OSG by allowing ASTC blocks to be wrapped in a DDS container. This is nonstandard, as ASTC is more of an OpenGL technology and DDS is a DirectX container, so I had to take some liberties. First, I straight up made up my own DXGI format numbers for all the ASTC block sizes. I think Apple's Metal API might use these same numbers in their DDS/ASTC support but I'm not sure. Secondly, I wrap the ASTC image data with DDS using OpenGL addressing of bottom-left first. This means that all textures will be displayed inverted in OpenMW unless you encode them flipped to begin with. ASTC block data is to complex to flip at runtime like we do with DXT1 and DXT5 so you just have to encode the texture inverted.

Because this is nonstandard, I hacked up some random tool I found on github called kram to do the encoding for me. You can find this here:
https://github.com/bmdhacks/kram
It would be nice to have better tooling for creation and conversion of DDS textures in OpenMW but this is good enough for me to get something out the door.

@bmdhacks
Copy link
Author

bmdhacks commented Aug 24, 2025

Here's a 1440p screenshot running 12x12 ASTC textures recompressed from the morrowind-starter-pack. These are 1/37th the size of RGBA8888 and like an eighth the size of DXT5:
Screenshot from 2025-08-24 06-41-33

@Capostrophic
Copy link
Collaborator

Yeah, I don't like that wrapping it into DDS. We and you shouldn't want to effectively maintain not just an extension of DDS but also its relevant tooling. Maybe add it as a separate astc format? Like whatever is done here? That way existing tooling for ASTC should be possible to be used.

@bmdhacks
Copy link
Author

bmdhacks commented Aug 24, 2025

The raw astc file format does not support mipmaps which is why I wrap it in DDS.
Also, OSG seems to do the right thing with DDS wrapped textures (and even had some stubs for ASTC before I got here) so I just went with it. I agree it's kinda gross though.

@bmdhacks
Copy link
Author

It's possible I'm wrong about the DXGI codes for ASTC btw, I just couldn't find any official documentation about it.

@bmdhacks
Copy link
Author

I can look at doing this in KTX textures if that would be preferable. I haven't seen KTX textures used anywhere in OpenMW, are they endorsed?

I removed some vestiges of ASTC support in DDS, and fixed a few math bugs regarding pixel sizes and compressed size for astc.
Also, we don't try to invert ASTC KTX textures becaue they're fed to opengl correctly as long as the origin is set accurately.
@bmdhacks bmdhacks force-pushed the bmd-openmw-astc-support branch from 40eea4d to e47ab9d Compare August 26, 2025 19:51
@bmdhacks
Copy link
Author

OK, I've updated my PR to only include ASTC support inside of KTX textures. This actually works out-of-the-box in openmw if you refer to ktx texture files. IE: OpenMW needs no changes. There were just a few tiny bugs regarding size and orientation math to get this working.

@bmdhacks
Copy link
Author

bmdhacks commented Sep 5, 2025

Ok, I'm parsing the ktx orientation metadata and warning if it is incorrect. I've also modified all my conversion utils (and have a PR to astcenc) to set this metadata field. I think this is committable now.

@AnyOldName3
Copy link
Member

I can absolutely see the desire for a warning for a missing/incompatible orientation, but I have mixed feelings about having it in this PR. I guess part of this is because it should really be OpenMW's job to decide it doesn't support certain orientations (or is unlikely to support things with unspecified orientation) rather than OSG's, but as the orientation enum doesn't have an unknown value, we can't really pass this information to the engine, so it's kind of a moot point. I've sat on the problem for a day without thinking of anything better than what this PR does, so maybe it's already as good as it's going to get.

@bmdhacks
Copy link
Author

bmdhacks commented Sep 7, 2025

Yeah, the way I see it, we have three options. I'm not passionate about any of them:

  1. Allow all KTX textures and let the user figure out that it's OpenGL oriented, and thus probably needs to be inverted.
  2. Warn if the KTX texture isn't OpenGL oriented with proper metadata, which incentivizes the user to use one of our encoding tools (although hopefully astcenc will accept my PR) but if they really want to use Photoshop and just flip the image that'll at least work for testing.
  3. Fail to display any texture that doesn't have the correct metadata. As far as I can tell, this will mean they must use our tools because most ktx encoders don't set the ktxOrientation flag, and nobody seems to read it either (despite it being in the spec).

I went with (2) in this PR, but I'm happy to change it if y'all would prefer. All the portmaster tools will add the metadata to our textures so at least we'll be compliant with any option.

@Capostrophic
Copy link
Collaborator

Capostrophic commented Sep 8, 2025

Is it totally unreasonable to tentatively consider any top left origin KTX unsupported (and vice versa) and move the warning to OpenMW code?

@bmdhacks
Copy link
Author

bmdhacks commented Sep 8, 2025 via email

@Capostrophic
Copy link
Collaborator

We already have some format-specific logic e.g. for TGA. The image manager will know if we used the KTX plugin to parse the image, and then we can try and do something with the result image.

@bmdhacks
Copy link
Author

bmdhacks commented Sep 8, 2025 via email

@Capostrophic
Copy link
Collaborator

Capostrophic commented Sep 8, 2025

It's impossible for OSG to properly adjust the image to match the expected orientation in every single case. It's not unreasonable for it to provide the raw data in the osg::Image with no modifications and no warnings. The problem is with providing the orientation for the application to be able to properly interpret the osg::Image. Right now extending osg::Image to provide more orientations comes with complications. Whether or not the extra orientations are added, it would be the responsibility of the application to warn the user it cannot properly handle the supplied orientation.

We have full info about the state of KTX images by the point they are converted because OSG is not maintained, so we would be able to know for sure a top left origin osg::Image given by the KTX plugin does not have the orientation we expect.

@AnyOldName3
Copy link
Member

The TGA logic isn't something we want to have, it's something we're forced to have because Morrowind handles TGA files weirdly and some mods don't work properly unless we inject the same weirdness.

Maybe something we could do is put the key/value data from the file into the osg::Image's userdata. In principle, application-specific userdata in a KTX file could do other application-specific things and this seems to me like something that's potentially sensible for other reasons.

@Capostrophic
Copy link
Collaborator

Capostrophic commented Sep 8, 2025

Using the user data seems sane. Although we'd still need to distinguish supported non-KTX images with no user data from unsupported KTX images with no user data (including those provided by stock OSG, though on a second thought this sounds difficult).

@AnyOldName3
Copy link
Member

I think if the loader is the KTX reader and there's no orientation userdata or it's incompatible, emitting a warning is fine. We can't know if a texture's orientation is right if it's got no orientation data and we can't know if it's right if using the upstream KTX loader, so it makes a degree of sense to warn in both cases and just tell downstream packagers that that's expected if they don't use our fork.

@AnyOldName3
Copy link
Member

There's a lot of stuff that you don't need to bother learning to make this work, so I'll leave a hint that the magic function is osg::Object::setUserValue(const std::string& name, const T& value). If you call it on the osg::Image instance and pass it a std::string as the value (not a C string), then it should handle everything under the hood, so you can just put the keys and values from the KTX file in that way.

@bmdhacks
Copy link
Author

Ok, I'm storing ktx metadata in the image user store now with the "KTX:" prefix. No warnings are emitted if the texture doesn't have the correct orientation, I'll do that in openmw.

}
else
{
// No key-value data, skip
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this branch doesn't do anything useful.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants