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

Common: Rework Movies and Game color rendering #575

Merged

Conversation

ChthonVII
Copy link
Contributor

@ChthonVII ChthonVII commented May 30, 2023

Summary

  • Documentation: Add extensive best-practices video encoding guide to documentation. (Because the guide describes the new video playback behavior in detail, starting here might be helpful in reviewing this pull request.)
  • Logging: Improve FMV-related logging.
  • Logging: Add trace_all to trace calls in movies.cpp.
  • FMV Playback: Fix "double color range expansion" bug (by moving color range expansion from ffmpeg's swscale component (which sometimes ignores explicit parameters in favor of buggy auto-detection) to our own shader code).
  • FMV Playback: Chose color range (TV or PC/full-range) based on FMV metadata (or sensible guesswork).
  • FMV Playback: Convert to YUV444 at the swscale stage, using lanczos scaling for chroma. This should avoid needlessly downsampling YUV444 (or YUV422) input, and should provide superior upsampling for YUV420 (or YUV422) input.
  • FMV Playback: Remove improper Y shift in full-range YUV->RGB conversion.
  • FMV Playback: Use high-precision shift constants for YUV->RGB conversion.
  • FMV Playback: Use correct, high-precision values for BT601 YUV->RGB conversion matrix.
  • FMV Playback: Choose BT601 or BT709 YUV->RGB conversion based on FMV metadata (or sensible guesswork).
  • FMV Playback: Avoid unnecessary RGB -> YUV -> RGB conversion for the original FF7 PC edition videos by converting BRG24 to planar RGB and passing it through the YUV plumbing.
  • FMV Playback: Attempt conversion of anything other than BT601, BT709, or BGR24 to BT601 using swscale.
  • FMV Playback: Implement bespoke "toeless sRGB" gamma function that gives superior results on FF7 videos.
  • FMV Playback: Choose gamma function based on FMV metadata (or sensible guesswork) from among sRGB, SMPTE-C, 2.2, 2.8, and "toeless sRGB."
  • FMV Playback: Choose color gamut based on FMV metadata (or sensible guesswork), from among sRGB, NTSC-J, SMPTE-C, and EBU.
  • FMV Playback: Absent metadata, assume sRGB color gamut for eidoslogo.avi and sqlogo.avi because they were added for PC release and clearly aren't NTSC-J like the others.
  • FMV Playback: Detect FF8 Steam edition video files (based on dimensions and total lack of metadata) and use appropriate setting for color range, YUV->RGB conversion matrix, gamma function, and color gamut.
  • General Rendering: Add a number of saturate() calls to ensure colors always stay in bounds (0.0 to 1.0).
  • General Rendering: Refactor, moving things into functions in FFNx.common.sh.
  • General Rendering: Implement Martin Roberts' quasirandom dithering algorithm. Dithering is applied in the following situations: (1) Before doing a FMV color range expansion. (2) After applying a color gamut LUT. (3) For HDR, before converting to rec2020 and applying the rec2084 gamma function. (Dithering is not supported by the D3D9 backend.)
  • General Rendering: Replace conceptually wrong auto-detection for HDR max nits. The desired value is the HDR monitor's "brightness level of SDR white," rather than the HDR monitor's maximum possible nits. Use default of 200 nits (borrowed from Google Chrome) if auto-detection fails. Update comments in FFNx.toml accordingly.
  • General Rendering: Use high-precision values for sRGB->rec2020 gamut conversion matrix.
  • General Rendering: Do alpha blending in gamma space for HDR too. (Because that's what the original games do.)
  • General Rendering: Add option for "NTSC-J mode." In this mode, the rendering output is assumed to use the NTSC-J color gamut (used by the 1990s Japanese CRT television sets that FF7 and FF8 were designed for), and is converted to sRGB (for SDR) or rec2020 (for HDR) in post processing. Results in more faithful and generally more vibrant colors. This mode is appropriate for (1) vanilla FF7/FF8, (2) mod assets that are upscales of the original assets, and (3) mod assets made from scratch where the modder used the original assets as a color reference without doing a gamut conversion. (Try this out. It's a big deal.)
  • General Rendering: Do color gamut conversions using LUTs constructed using state-of-the-art gamut mapping algorithm that mostly preserves details outside the destination gamut with minimal loss of colorimetric fidelity. (Do not use LUTs for conversions to rec2020, since no GMA is needed in that case.)
  • General Rendering: Put plumbing in place for an override to draw an asset using the opposite color gamut from the overall gamut mode. (I.e., draw asset using sRGB gamut in NTSC-J mode, or vice versa.) This feature presently does nothing because there is no way to set the override to true. This feature is implemented in the hopeful expectation that one day FFNx/7H will offer a way either (a) differentiate general asset classes (e.g., spell textures, battle model textures, etc.), or (b) to set gamut metadata for assets individually, and then the override could be set accordingly. (In the meantime, the gamutthingy program can be used to convert an asset for drawing in the opposite mode.)
  • General Rendering: Exempt FFNx logo from NTSC-J mode. (It's an sRGB asset.)

Motivation

Fix FMV playback and colorimetry. Ended up with a general purpose NTSC-J mode along the way.

ACKs

  • I have updated the Changelog.md file
    • I have NOT updated the changelog. I'm unsure what level of detail is expected, and I fear the above summary may be too verbose.
  • [1/2] I did test my code on FF7
    • I HAVE tested FF7 with a SDR monitor.
    • I have NOT tested FF7 with a HDR monitor (because I don't own one). HDR testing is needed.
  • [1/2] I did test my code on FF8
    • I have NOT tested on FF8.
    • I HAVE tested FF8 Steam videos in FF7 by substituting them for FF7's videos.

@julianxhokaxhiu julianxhokaxhiu changed the title Moviesandgamutsfinal FF7: Rework Movies and Game color rendering May 30, 2023
@julianxhokaxhiu
Copy link
Owner

Thanks for the PR, I'll try to look at it more in detail this weekend, in the meantime, please drop this commit as it's unrelated to this PR, thank you: 0ca84be (#575)

@ChthonVII
Copy link
Contributor Author

OK, I reverted the revert. If you end up reverting the revert of the revert, please take care that the two calls to setOverallColorGamut() relating to the logo don't get lost along the way. Git gave me merge conflicts in both directions with them.

@julianxhokaxhiu
Copy link
Owner

Please drop the commit, do not "revert the revert". Consider using interactive rebase and drop the commit. Then add the calls you need to make sure the PR is fully functional. Thank you.

@ChthonVII
Copy link
Contributor Author

The first revert incorporates resolution of a merge conflict. If I delete it from the git history, the history will no longer include a record of the fully working state the PR started in. You might want to be able to recover that if the problems with 3437968 prove to be unfixable. Are you sure you want me to delete that?

The current state of the PR is identical to 2 commits back, so a git reset would suffice. It should be in a working state except for the black screen bug with the logo.

@julianxhokaxhiu
Copy link
Owner

Yes please, I don't want to mix things here. This PR is very valuable but I'd like to keep it focused on the color resolution. Put the logo part aside for now. Thank you

@julianxhokaxhiu
Copy link
Owner

I finally found some time to look at this PR more carefully and first of all thank you for the massive work you've done. Is really appreciated.

On top of it, I have only these three main concerns:

  1. The PR is atm tackling everything: movies, in-game and whatnot. I would have been more happy if we could have this split in two. First let's tackle movies ( so I can understand how much code impact it would produce ) and we could have tackled the game part. Having them mixed atm makes this PR extremely difficult to merge.
  2. For the lighting part I would need the help of @CosmosXIII as he would know better the impact your PR would have on top of his work. I saw too many changes on the shader so I wouldn't be comfortable saying if it looks good or not.
  3. I'm not really happy about the glut png files, I know we could ship whatever with FFNx but it feels a little bit "too meta". Is there a way we could implement this better? Maybe you could consider using the path I did for the initial logo part and embed it inside the DLL

Atm I would consider focusing on 1 and 3 on your own side, and let's wait for Cosmos to come back for 2.

Thank you very much in advance in the meantime, looking forward to it :)

@ChthonVII
Copy link
Contributor Author

The PR is atm tackling everything: movies, in-game and whatnot. I would have been more happy if we could have this split in two. First let's tackle movies ( so I can understand how much code impact it would produce ) and we could have tackled the game part. Having them mixed atm makes this PR extremely difficult to merge.

I've said before that this PR is not severable. The movie stuff depends on the general-purpose gamut stuff and general shader clean-up. This is ALL movie stuff, some of it just happens to have broader applicability. I know this isn't the answer you want. But I can't divide the indivisible.

For the lighting part I would need the help of @CosmosXIII as he would know better the impact your PR would have on top of his work. I saw too many changes on the shader so I wouldn't be comfortable saying if it looks good or not.

I think I was unclear earlier. This PR makes zero changes to the lighting code and zero changes that should impact the lighting code. There is an additional change that I would like to make if it could be done without breaking lighting. But I don't believe that's possible, so that additional change is not in this PR. I do want @CosmosXIII's input on whether that potential change would be possible, but it's not necessary for merging this PR.

I'm not really happy about the glut png files, I know we could ship whatever with FFNx but it feels a little bit "too meta". Is there a way we could implement this better? Maybe you could consider using the path I did for the initial logo part and embed it inside the DLL

I'm not clear on whether you're unhappy with the LUT approach in general, or with the LUTs being independent files.

As to the former, I don't see any other alternative. The math is just too heavy. Gamutthingy takes several minutes to convert a single HD image on CPU, with the use of a 400MB memo table for repeated colors. I don't believe it's possible to implement that for GPU with real-time frame rates. Also, it would require binding ~50MB of gamut boundary descriptor data as a uniform, which is pretty much the same thing as binding a LUT. I'm not aware of any other solution to "the math is too heavy for real-time" besides pre-computation via a LUT.

As to the latter, I don't see why it matters. I don't see any downside to having them as independent files. (And I don't understand what "too meta" means.) The only downside I see to embedding them into the binary is that then the users couldn't customize them. So, I don't really care. I wouldn't object to you embedding them in the binary, if you want to, but I'm not inclined to do it.

@julianxhokaxhiu
Copy link
Owner

julianxhokaxhiu commented Jun 19, 2023

Thanks for the reply. Regarding the first part I'm still trying to analyze how much complexity I'm bringing into the code, also considering I might have to maintain it if you might happen to not be around even though I'd appreciate having you onboard like other core members are contributing to the project, but I'm calculating the worst case scenario here. I hope you do understand why I'm asking so many questions and why I'm trying to simplify every contribution.

Regarding the second part, I see you're touching the lighting shader nevertheless so for me it's enough to wait for Cosmos to give me his opinion, as it's his code being touched there.

About the third part:

The only downside I see to embedding them into the binary is that then the users couldn't customize them.

How much do you expect users replacing them for something else? Isn't this a one-stop solution? Like merge, enable and enjoy? What would be the benefit of allowing users replacing these files? It's just for me to understand.

Once I understand this part I may think about which would be the best way. There are ways we could allow users replace them, and we looking for the file, but if not found on the disk we could use the embedded resource. I know for you it doesn't matter, but it does for me as I'd like to keep the distribution as simple as possible and having those files inside the shaders folder feels like an abuse to me ( even though it's kinda related, I know ).

So, I don't really care. I wouldn't object to you embedding them in the binary, if you want to, but I'm not inclined to do it.

This although is not the attitude I'm looking forward for when working with someone. I appreciate your idea and I'm open on working on it, but the work has to be mutual. If you saw first Cosmos PRs I asked him to rewrite the entire code three times until it made sense for the current FFNx structure. I hope you understand I'm not the only one working on this code, and I have to accomodate many needs. I hope this will make your reconsider being collaborative. There are still minor topics to be fixed which I spotted that are about indentation for eg. so I hope if I rise them you'd be open to work on them.

@julianxhokaxhiu julianxhokaxhiu self-requested a review June 19, 2023 21:32
@julianxhokaxhiu julianxhokaxhiu added the enhancement New feature or request label Jun 19, 2023
Copy link
Owner

@julianxhokaxhiu julianxhokaxhiu left a comment

Choose a reason for hiding this comment

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

Left some comments on various parts. I'll try to test the code later today and I'll let you know. I've seen you've touched the renderer part too and that one is a little bit delicate as it impacts also FF8. I'll keep you posted.

misc/FFNx.common.sh Outdated Show resolved Hide resolved
misc/FFNx.post.frag Outdated Show resolved Hide resolved
misc/FFNx.post.frag Outdated Show resolved Hide resolved
src/cfg.cpp Outdated Show resolved Hide resolved
src/video/movies.cpp Outdated Show resolved Hide resolved
src/renderer.h Show resolved Hide resolved
@julianxhokaxhiu
Copy link
Owner

julianxhokaxhiu commented Jun 24, 2023

I tested this PR on both FF7 and FF8 and these are the results:

FF7:

  • SDR: OK ( looks a bit oversaturated in colors here but the comparison is with the flag off, you can notice this on Cloud's skin using Ninostyle models )
  • HDR: OK ( looks identical to SDR when HDR is enabled, so colors are rendering perfectly )

FF8:

  • SDR: OK ( same as FF7 )
  • HDR: OK ( same as FF7 )

I tested both movies and gameplay and I didn't notice anything strange, including animated textures :) Great job so far. LMK if you want to address the indentation issues or if I shall. Additionally, I'd like to know if there's margin to improve the glut texture loading part. Once all of that is done and adressed, we can proceed merging this PR.

The only part I missed to test is the Lighting part, so any feedback from @CosmosXIII is really appreciated.

@julianxhokaxhiu julianxhokaxhiu changed the title FF7: Rework Movies and Game color rendering Common: Rework Movies and Game color rendering Jun 24, 2023
@CosmosXIII
Copy link
Contributor

@julianxhokaxhiu Tested with lighting and looks fine to me. IMO the shader movie code is now so complex that it would be better to make it it's own shader for both performance and maintenance reasons. But maybe that is better left out for another refactor PR.

About the color improvement, I can see the difference in the sense that it is more saturated but for me the biggest improvement is that now the movies have no longer crushed blacks whether you use the NTSC-J option or not.

@ChthonVII
Copy link
Contributor Author

ChthonVII commented Jun 26, 2023

Do we need to load all the PNGs at the same time? Can't we just load one based on the case we're on in the game?

I did it this way because it was simple and I figured the RAM cost was negligible. It should be possible to do better. In general, we want to have the required LUT loaded before we need it to be confident in avoiding stuttering. However, unless you're using movies with color gamuts that are unusual for our use case, you're likely to only use a maximum of 2 LUTs per session. So, I could rework things to pre-load the LUTs you're likely to need based on HDR and gamut mode settings, then lazy load the others in the unlikely event you need them. Please give me a few days.

LMK if you want to address the indentation issues or if I shall.

Please address the indentation issues. My editor "solves" the "tabs vs spaces" indent problem by displaying them exactly the same, but for a barely visible mark on the tabs. (With my eyesight, basically invisible.) In one sense, this is good because I don't even notice when viewing a file with mixed indents. However, it makes it really hard to fix a file with mixed indents, and easy to generate one accidentally.

Ninostyle models

Most from-scratch mods inhabit a no-man's land where neither gamut mode will be quite right. To the extent the modder stuck closely to the original palette, NTSC-J mode should be correct. To the extent the modder livened up the colors, sRGB mode should be correct. Unfortunately, most from-scratch mods mostly stuck the original palette, but with some departures that are brighter/more saturated. The ninostyle mods stick surprisingly close to the original palette (see here), so I think NTSC-J mode comes closest to "what the game was originally supposed to look like, but with better models." Though, ultimately, that's a really subjective judgment.

IMO the shader movie code is now so complex that it would be better to make it it's own shader for both performance and maintenance reasons. But maybe that is better left out for another refactor PR.

I'm probably not the right person for that job, since I know so little about BGFX that I'd probably break things trying to add a new shader pass. Anyway, I agree that's best left for a refactor.

HDR: OK ( looks identical to SDR when HDR is enabled, so colors are rendering perfectly )

There should be some colors that the HDR monitor can produce and the SDR monitor can't. Try comparing a really saturated green and it should look "greener" on the HDR monitor. (Though I must say that I'm really proud that the LUT is doing a good enough job in SDR that you couldn't tell the difference.)

Somewhat related to that: Is the autodetection for "this HDR monitor's nits for SDR white" working correctly? That's not something I was able to fully test, since I have neither Win10/11 nor a HDR monitor. The log file should say what happened. (This code starts around line 800 in renderer.cpp)

@julianxhokaxhiu
Copy link
Owner

Please give me a few days.

Sure thing, thank you :)

Anyway, I agree that's best left for a refactor.

Agree as well.

Is the autodetection for "this HDR monitor's nits for SDR white" working correctly?

The intro movie with stars was rendered correctly here when using HDR so I'd say yes. Well done :)

@ChthonVII
Copy link
Contributor Author

I just pushed a commit for lazy loading LUTs. I gave it a very brief test and it seems to be working correctly.

* Add SMPTE170M linearization function

FMVs are likely bt601 or bt709, so we need this to properly convert them to linear RGB.

* Use SMPTE170M linearization function if YUV texture is a movie

* Use SMPTE170M linearization function if YUV texture is a movie
… saturate() calls to make sure matrix operations stay in bounds
… 10-bit correct by lying about range, but only if file has no range metadata. Instead telling the truth about range gets 8-bit files correct. Needs more work to find workaround for swscale doing unwanted extra range conversions.
(1) Prevent unwanted, incorrect color range conversions by swscale
(2) Check colorspace and do conversion if needed (as yet untested)
(3) Use YUV444 for swscale output
(4) Use better scaling for chroma planes
…d use SMPTE170M when appropriate.

(Works great with 10-bit formats. Some posterization near black with 8-bit formats. Not sure if this is an encoder issue or a swscale issue.)
…on this HDR monitor" rather than "maximum brightness of this HDR monitor." Revised documentation in FFNx.toml accordingly. Replaced autodectection code with new implementation using DISPLAYCONFIG_DEVICE_INFO_GET_SDR_WHITE_LEVEL DisplayConfigGetDeviceInfo() query. Untested, since I don't have a HDR monitor or Win10.
…white point on Japanese television sets was actually 9300K+27mpcd, and (b) to use higher precision D65 white point for sRGB. Also added NTSCJ-to-rec2020 gamut conversion matrix
… parameter to to dithering in order avoid using the same dither pattern twice in the case in the case of a TV-range video played on HDR.
2. Move HDR gamut conversions up as early as possible.
3. Implement plumbing for gamut conversions for textures controlled by metadata, pending support for said metadata.
4. Apply NTSC-J to sRGB (or rec2020) gamut conversion for rendering polygons of solid color/gradients.
…n to enable "NTSC-J Gamut Mode." This isn't ideal because it necessitates double gamut conversions for videos in some cases, but it seems to be the only way to do gamut conversions on gameplay without breaking animated textures. (Also some cleanup and reorganizing.)
…ils among colors that are out-of-bounds for sRGB. Also, fix dithering to protect values near 0 and 1.
@julianxhokaxhiu julianxhokaxhiu merged commit 0759030 into julianxhokaxhiu:master Jul 8, 2023
@julianxhokaxhiu
Copy link
Owner

We did it! Thanks a lot for the amazing work and energy you put on this one, really appreciated!

@julianxhokaxhiu julianxhokaxhiu linked an issue Jul 8, 2023 that may be closed by this pull request
@julianxhokaxhiu julianxhokaxhiu added this to the 1.16.0 milestone Jul 9, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[ Common ] Improve the FFMpeg video decoding
3 participants