Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

tr_image: do not use R_FindImageLoader with cube maps, test multifile cubemaps first (or preview may be loaded as cubemap instead) #1394

Merged
merged 1 commit into from
Nov 2, 2024

Conversation

illwieckz
Copy link
Member

@illwieckz illwieckz commented Oct 26, 2024

Deep rewrite of image loading code.

  • do not walk .shader files in tr_image.cpp
  • do not use R_FindImageLoader with cube maps
  • only use home read for KTX with IF_HOMEPATH
  • load home KTX with IF_HOMEPATH before doing any lookup
  • do not do any other home loading
  • test multifile cubemaps before singlefile ones (or preview may be loaded as cubemap instead)
  • fix a crash when singlefile cubemap is freed because it was loaded but not created
  • look for cubemap file with explicit extension
  • add many debug logs and factorize them

Fixes #1393:

- do not walk .shader files in tr_image.cpp
- do not use R_FindImageLoader with cube maps
- only use home read for KTX with IF_HOMEPATH
- load home KTX with IF_HOMEPATH before doing any lookup
- do not do any other home loading
- test multifile cubemaps before singlefile ones (or preview may be loaded as cubemap instead)
- fix a crash when singlefile cubemap is freed because it was loaded but not created
- look for cubemap file with explicit extension
- add many debug logs and factorize them
foundLoader = true;
}

if ( pak == nullptr ) {
Copy link
Member

Choose a reason for hiding this comment

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

We don't want this homepath thing (yes it should be deleted from the other one too)

Copy link
Member Author

Choose a reason for hiding this comment

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

@VReaperV Is it used to load cached reflection cubemaps?

Copy link
Contributor

Choose a reason for hiding this comment

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

The one in R_FindImageLoader() is.

Copy link
Member Author

Choose a reason for hiding this comment

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

Well, something is wrong, R_FindImageLoader() is not expected to be used to load a cached cubemap.

For now I'll keep the home loader in both, then later we can decide what is to be kept when things are not weird.

Copy link
Member Author

@illwieckz illwieckz Oct 27, 2024

Choose a reason for hiding this comment

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

🔹️ xreaperx: Well, I thought you were asking about the homepath usage in the loader?
🔹️ xreaperx: I added it so it would look for .ktx under homepath
🔹️ illwieckz: yes
🔹️ illwieckz: but you said only R_FindImageLoader requires to read home path
🔹️ xreaperx: Well yeah otherwise it doesn't find the files
🔹️ illwieckz: but this is a cubemap
🔹️ illwieckz: R_FindImageLoader is for non-cubemap files
🔹️ xreaperx: Yeah there's some weirdness with that

Copy link
Member Author

@illwieckz illwieckz Oct 27, 2024

Choose a reason for hiding this comment

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

🔹️ xreaperx: @illwieckz R_FindCubeImage() use R_FindImageLoader(), not R_FindCubeImageLoader()
🔹️ xreaperx: So if that's not what it should do, then the error is in that

I say: we merge this that fixes the skybox, then later we fix the reflection cubemap code.

Copy link
Contributor

Choose a reason for hiding this comment

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

Sounds reasonable to me. I don't know if there's supposed to be some other way to load images from homepath, but other functionality that I looked at (log, glsl cache) used FS::HomePath.

Copy link
Member

Choose a reason for hiding this comment

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

This is not the code that's used for reflection maps. The reflection map search sets an IF_HOMEPATH flag instructing to search the homepath instead of the VFS. It's bad to search the homepath for every random image.

Copy link
Contributor

Choose a reason for hiding this comment

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

Yes and no. loader.ImageLoader() is the only call that uses IF_HOMEPATH, but R_FindImageLoader(), which preceeds it, doesn't use that, and is used to know if the file exists in the first place. I suppose a proper solution might be to pass imageParams.bits to it and then use IF_HOMEPATH.

Copy link
Member Author

Choose a reason for hiding this comment

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

The purpose of this PR is to fix skybox loading, other things like “attempting to remove some code and see if that breaks reflections“ later.

char baseName[ MAX_QPATH ];
COM_StripExtension3( buffer, baseName, sizeof( baseName ) );

const FS::PakInfo* bestPak = nullptr;
Copy link
Member

Choose a reason for hiding this comment

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

Could use at least a TODO comment about respecting explicit extensions.

Copy link
Member Author

Choose a reason for hiding this comment

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

Done.

@illwieckz
Copy link
Member Author

I finally reworded the thing in a better way, including the non-cubemap code.

@illwieckz
Copy link
Member Author

I added some TODO comments saying the removal of homepath reading have to be investigated later.


if ( *ext && bestLoader == -1 )
if ( *ext && loader )
Copy link
Contributor

Choose a reason for hiding this comment

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

This should be !loader.

@illwieckz illwieckz force-pushed the illwieckz/skyboxes branch 6 times, most recently from 13cb6ae to e958009 Compare October 31, 2024 16:58
@illwieckz
Copy link
Member Author

So I did a deeper rewrite.

First, I refactored much code and added more logs. It makes me able to check how and if the cached KTX loading worked.

So I dropped every home loading but cached KTX: now only the cached KTX is loaded from home, nothing else is loaded from home. It even doesn't iterate loaders for them: it does a KTX home dir straight and return.

I actually found a crash that happens when loading a singlefile cubmemap succeeds but image creation fails (I had to simulate this case to verify it would crash).

I also implemented the look-up of singlefile cubemap with explicit extension.

The first post was updated accordingly.

@illwieckz illwieckz force-pushed the illwieckz/skyboxes branch 2 times, most recently from 7ef328f to eab6a6a Compare October 31, 2024 17:18
@illwieckz
Copy link
Member Author

illwieckz commented Oct 31, 2024

I noticed some functions were copying with Q_strncpyz strings received as argument before using them. I guess it's some security check in case some user-provided data is intentionally wrong (missing null terminator). I don't know if such copy is needed but I renamed the symbols to make them more obvious.

// We found a file and its pak is better than the best pak we have
// this relies on how the filesystem works internally and should be moved
// to a more explicit interface once there is one. (FIXME)
// FIXME: Move to a more explicit interface once there is one.
Copy link
Member

Choose a reason for hiding this comment

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

The preceding sentence is important context for this comment. Without it written together, it would be difficult to guess what it's talking about.

COM_StripExtension3( buffer, cubeMapBaseName, sizeof( cubeMapBaseName ) );

for ( const cubeMapLoader_t &loader : cubeMapLoaders )
// Look for cached 6-face skybox file.
Copy link
Member

Choose a reason for hiding this comment

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

The reflection cubemaps aren't "skyboxes"

Copy link
Member Author

Choose a reason for hiding this comment

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

Rewritten.

return bestLoader;
}

static image_t *R_LoadAndCreateCubeImage( const char *imageName, const char* altName, const imageExtLoader_t *loader, imageParams_t& imageParams )
Copy link
Member

Choose a reason for hiding this comment

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

This could use a comment (or function name change) indicating it is only used for single-file variants

Copy link
Member Author

Choose a reason for hiding this comment

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

Done.

Q_strncpyz( buffer, imageName, sizeof( buffer ) );
unsigned hash = GenerateImageHashValue( buffer );
char imageName[ 1024 ];
Q_strncpyz( imageName, untrustedName, sizeof( imageName ) );
Copy link
Member

Choose a reason for hiding this comment

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

We don't need to make a copy of the function argument. The one concern like that which should be taken care of is that when you use COM_Parse* it returns a pointer to a temporary buffer, which you need to either copy, or finish using immediately.

Copy link
Member Author

Choose a reason for hiding this comment

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

In fact all the multiple copies (the thing I first thought it was a security check) and this one ware a VERY UGLY workaround for a bug where the .shader file was iterating the multiple images of lines like AnimMap 1.5 models/weapons/lgun/display1 models/weapons/lgun/display2 models/weapons/lgun/display3 models/weapons/lgun/display4 twice: once in tr_shader.cpp, once in tr_image.cpp… So ParseStage from tr_shader.cpp was reading next token, passed it to R_FindImageFile, then R_FindImageFile backuped the token position, passed the token to R_LoadImage that was moving to the next token in the back or ParseStage, etc. And with multi-file cubemaps it meant the image code was iterating next .shader` file token 6 time, requiring to reset the state 6 times… This was all crazy.

if ( IsImageCompressed( imageParams.bits ) )
{
Log::Warn("Cube map face '%s' has DXTn compression, cube map unusable", fileName );
R_FreeCubePics( pic, i );
Copy link
Member

Choose a reason for hiding this comment

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

Should be i + 1

Copy link
Member Author

Choose a reason for hiding this comment

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

Done, also done in next free.

Log::Warn("cubemap face '%s' is a multilayer image with %d layer(s), cube map unusable", filename, numLayers);
break;
}
R_LoadImage( &fileName[ 0 ], &pic[ i ], &width, &height, &numLayers, &numMips, &imageParams.bits );
Copy link
Member

Choose a reason for hiding this comment

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

Just fileName

Copy link
Member Author

Choose a reason for hiding this comment

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

Done. The various &name[0] were in fact a workaround for the many type discrepancies of the various string copies to workaround the double shader keyword iteration… What a mess!

src/engine/renderer/tr_image.cpp Show resolved Hide resolved
}
}

/* If the image path is “some.thing” for a file named “some.thing.crn”,
Copy link
Member

Choose a reason for hiding this comment

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

These comments are written in a confusing way. You could say If we are give the name "some.thing", first we look for some.thing.webp, some.thing.tga etc... Then, some.webp, some.tga, etc.

Copy link
Member Author

Choose a reason for hiding this comment

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

Done.

try loading with “something” as a basename. */
if ( *ext && !loader )
{
COM_StripExtension3( token, fileName, sizeof(fileName) );
Copy link
Member

Choose a reason for hiding this comment

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

The token temporary pointer shouldn't be used after calling a bunch of other functions.

Copy link
Member Author

Choose a reason for hiding this comment

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

Removed. In fact the whole token wording was a because of this code parsing the .shader file, which was wrong.

@illwieckz illwieckz force-pushed the illwieckz/skyboxes branch 2 times, most recently from c40fc19 to 58c3631 Compare November 1, 2024 06:50
Copy link
Member Author

@illwieckz illwieckz left a comment

Choose a reason for hiding this comment

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

OK, so, lot of that madness code with many copies and pointers were very convoluted workarounds to the fact the image code was walking the .shader file while the shader code did the same and then there was some crazy code done to make this wrongness work.

Q_strncpyz( buffer, imageName, sizeof( buffer ) );
unsigned hash = GenerateImageHashValue( buffer );
char imageName[ 1024 ];
Q_strncpyz( imageName, untrustedName, sizeof( imageName ) );
Copy link
Member Author

Choose a reason for hiding this comment

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

In fact all the multiple copies (the thing I first thought it was a security check) and this one ware a VERY UGLY workaround for a bug where the .shader file was iterating the multiple images of lines like AnimMap 1.5 models/weapons/lgun/display1 models/weapons/lgun/display2 models/weapons/lgun/display3 models/weapons/lgun/display4 twice: once in tr_shader.cpp, once in tr_image.cpp… So ParseStage from tr_shader.cpp was reading next token, passed it to R_FindImageFile, then R_FindImageFile backuped the token position, passed the token to R_LoadImage that was moving to the next token in the back or ParseStage, etc. And with multi-file cubemaps it meant the image code was iterating next .shader` file token 6 time, requiring to reset the state 6 times… This was all crazy.

return bestLoader;
}

static image_t *R_LoadAndCreateCubeImage( const char *imageName, const char* altName, const imageExtLoader_t *loader, imageParams_t& imageParams )
Copy link
Member Author

Choose a reason for hiding this comment

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

Done.

COM_StripExtension3( buffer, cubeMapBaseName, sizeof( cubeMapBaseName ) );

for ( const cubeMapLoader_t &loader : cubeMapLoaders )
// Look for cached 6-face skybox file.
Copy link
Member Author

Choose a reason for hiding this comment

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

Rewritten.

Log::Warn("cubemap face '%s' is a multilayer image with %d layer(s), cube map unusable", filename, numLayers);
break;
}
R_LoadImage( &fileName[ 0 ], &pic[ i ], &width, &height, &numLayers, &numMips, &imageParams.bits );
Copy link
Member Author

Choose a reason for hiding this comment

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

Done. The various &name[0] were in fact a workaround for the many type discrepancies of the various string copies to workaround the double shader keyword iteration… What a mess!

src/engine/renderer/tr_image.cpp Show resolved Hide resolved
if ( IsImageCompressed( imageParams.bits ) )
{
Log::Warn("Cube map face '%s' has DXTn compression, cube map unusable", fileName );
R_FreeCubePics( pic, i );
Copy link
Member Author

Choose a reason for hiding this comment

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

Done, also done in next free.

try loading with “something” as a basename. */
if ( *ext && !loader )
{
COM_StripExtension3( token, fileName, sizeof(fileName) );
Copy link
Member Author

Choose a reason for hiding this comment

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

Removed. In fact the whole token wording was a because of this code parsing the .shader file, which was wrong.

@slipher
Copy link
Member

slipher commented Nov 2, 2024

LGTM

@illwieckz illwieckz merged commit ef89e9b into master Nov 2, 2024
9 checks passed
@illwieckz illwieckz deleted the illwieckz/skyboxes branch November 2, 2024 20:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Broken wasteland skybox
3 participants