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

Parse scaling lists properly. #90

Merged

Conversation

jerzywilczek
Copy link
Contributor

Scaling lists were skipped previously, I implemented proper parsing, which was mostly coming up with the types to store them in.

I hope the API with const-sized arrays is ok, it seemed whoever implemented skipping the lists wanted to use Vecs initially. I found the generic const more appealing, but can change back to Vecs if needed.

Copy link

github-actions bot commented Nov 13, 2024

🐰 Bencher Report

Branch90/merge
Testbedlocalhost

⚠️ WARNING: No Threshold found!

Without a Threshold, no Alerts will ever be generated.

Click here to create a new Threshold
For more information, see the Threshold documentation.
To only post results if a Threshold exists, set the --ci-only-thresholds CLI flag.

Click to view all benchmark results
BenchmarkEstimated Cyclesestimated cyclesInstructionsinstructionsL1 HitshitsL2 HitshitsRAM HitshitsTotal read+writereads/writes
ci_bench::ci::reader read:setup_video("big_buck_bunny_1080p_24f...📈 view plot
⚠️ NO THRESHOLD
16,755,068.00📈 view plot
⚠️ NO THRESHOLD
8,286,933.00📈 view plot
⚠️ NO THRESHOLD
10,881,698.00📈 view plot
⚠️ NO THRESHOLD
15,565.00📈 view plot
⚠️ NO THRESHOLD
165,587.00📈 view plot
⚠️ NO THRESHOLD
11,062,850.00
🐰 View full continuous benchmarking report in Bencher

@jerzywilczek
Copy link
Contributor Author

jerzywilczek commented Dec 10, 2024

@dholroyd I really don't want to be annoying, but any chance of looking at this at some point? It's ok if the answer is no, I'd just like to know if it makes sense to wait for this. Once again, I really don't want to be annoying or bother you, sorry.

@jerzywilczek jerzywilczek force-pushed the scaling-lists-optional branch from ccfc26e to 54d98a7 Compare December 12, 2024 10:10
src/nal/sps.rs Outdated
pub enum ScalingList<const S: usize> {
NotPresent,
UseDefault,
List(Box<[i32; S]>),
Copy link
Collaborator

Choose a reason for hiding this comment

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

It looks like the possible bounds of scalingList[ j ] are [1, 256); is that right? If so, perhaps a NonZeroU8 would be appropriate rather than an i32?

Copy link
Owner

Choose a reason for hiding this comment

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

I've had a look at "Scaling list syntax" in the spec, and agree it seems to make sense to use NonZeroU8 here 👍

src/nal/pps.rs Outdated
@@ -142,7 +142,8 @@ impl SliceGroup {

#[derive(Debug, Clone)]
pub struct PicScalingMatrix {
// TODO
pub scaling_list4x4: Vec<ScalingList<16>>,
pub scaling_list8x8: Vec<ScalingList<64>>,
Copy link
Collaborator

Choose a reason for hiding this comment

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

Do you think it'd be more clear to make this Option<Vec<ScalingList<64>> based on the transform_8x8_mode_flag, as opposed to representing this by scaling_list8x8.empty()? I could go either way; probably should have a comment in the latter case though.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

After some thinking, representing it as Option<...> makes more sense, it seems more intuitive if someone wants to extract the flag.

let mut scaling_list = vec![];

impl<const S: usize> ScalingList<S> {
pub fn read<R: BitRead>(
Copy link
Collaborator

Choose a reason for hiding this comment

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

Regarding this comment in your PR description:

I hope the API with const-sized arrays is ok, it seemed whoever implemented skipping the lists wanted to use Vecs initially. I found the generic const more appealing, but can change back to Vecs if needed.

I think the only concern I have about the const-size array approach is doubling the code size of this read method, in combination with the existing multiplicative factor of the BitRead monomorphization. What would you say about avoiding that via delegation, something like the following:

/// Returns `use_default_scaling_matrix_flag`
fn fill_scaling_list<R: BitRead>(r: &mut R, scaling_list: &mut [NonZeroU8]) -> Result<bool, ScalingMatrixError> {
    // ...
}

impl<const S: usize> ScalingList<S> {
    pub fn read<R: BitRead>(r: &mut R, present: bool) -> Result<ScalingList<S>, ScalingMatrixError> {
        if !present { /* ... */ }
        let mut scaling_list = Box::new([0; S]);
        let use_default_scaling_matrix_flag = fill_scaling_list(r, &mut scaling_list[..])?;
        if use_default_scaling_matrix_flag {
            Ok(ScalingList::UseDefault)
        } else {
            Ok(ScalingList::List(scaling_list))
        }
    }
}

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Oh, that's a very nice catch. Super cool you noticed that.

ScalingList::NotPresent,
ScalingList::NotPresent,
]
}),
..ChromaInfo::default()
Copy link
Collaborator

Choose a reason for hiding this comment

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

It'd be nice to have a test case that exercises the new code paths more fully.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I modified the test case a bit, are there things I forgot to include there that you wish were covered?

pub struct ScalingList {
// TODO
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum ScalingList<const S: usize> {
Copy link
Collaborator

Choose a reason for hiding this comment

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

I had another thought about representation, and @dholroyd I'd love to get your take also.

Is it important to convey exactly how the file specified the scaling matrices, or are the callers always going to want to apply the relevant rules to come up with a matrix? The rules appear to be something like the following:

  • seq_scaling_matrix_present_flag == 0 => use Flat_...
  • seq_scaling_list_present_flag[ i ] == 0 => use fall-back rule set A
  • pic_scaling_matrix_present_flag == 0 => match the sps
  • pic_scaling_list_present_flag[ i ] == 0 => use fall-back rule set B
  • UseDefaultScalingMatrix...Flag[ i ] == 0 => use Default_..._Intra

If the latter, I think it's better to just represent a dense matrix of integers, rather than a vec of enums to these defaults or a box of integers.

Copy link
Owner

Choose a reason for hiding this comment

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

It's always been my ambition to use this code to produce a diagnostic dump of the bitstream content, so I lean toward the former, bitstream-oriented representation. I think there is also a place for utility functions to produce the latter representation on demand, so that callers don't all duplicate the logic you've outlined.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Thanks; that makes the existing structure clearly the right way to go.

@dholroyd
Copy link
Owner

@jerzywilczek thanks for this, bardzo ładne! Also, sorry for being slow getting to the PR.

@jerzywilczek jerzywilczek force-pushed the scaling-lists-optional branch 2 times, most recently from 41010bb to f988174 Compare January 27, 2025 11:37
Copy link

github-actions bot commented Jan 27, 2025

🐰 Bencher Report

Branchscaling-lists-optional
Testbedlocalhost

⚠️ WARNING: No Threshold found!

Without a Threshold, no Alerts will ever be generated.

Click here to create a new Threshold
For more information, see the Threshold documentation.
To only post results if a Threshold exists, set the --ci-only-thresholds CLI flag.

Click to view all benchmark results
BenchmarkEstimated CyclesBenchmark Result
1e6 x estimated cycles
(Result Δ%)
Upper Boundary
1e6 x estimated cycles
(Limit %)
Instructions1e6 x instructionsL1 Hits1e6 x hitsL2 Hits1e3 x hitsRAM Hits1e3 x hitsTotal read+write1e6 x reads/writes
ci_bench::ci::reader read:setup_video("big_buck_bunny_1080p_24fps_h264.h264"...📈 view plot
🚷 view threshold
16.73
(+0.52%)
18.31
(91.38%)
📈 view plot
⚠️ NO THRESHOLD
8.27📈 view plot
⚠️ NO THRESHOLD
10.86📈 view plot
⚠️ NO THRESHOLD
15.37📈 view plot
⚠️ NO THRESHOLD
165.59📈 view plot
⚠️ NO THRESHOLD
11.04
🐰 View full continuous benchmarking report in Bencher

@jerzywilczek
Copy link
Contributor Author

@dholroyd No problem! Thank you for the library.

@dholroyd
Copy link
Owner

I am happy with the state of this - any further comments @scottlamb ?

@jerzywilczek
Copy link
Contributor Author

One more thing - can we have a new release after merging this?

src/nal/sps.rs Outdated
} else {
next_scale
};
// unwrap is ok here, because:
Copy link
Collaborator

Choose a reason for hiding this comment

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

I agree with the argument in this comment, but I think it's possible to avoid the .unwrap() altogether:

  • keep last_scale as a NonZeroU8, starting from const { NonZeroU8::new(8).unwrap() } (msrp 1.83, the alternative of match { ..., None => panic!() } works a few versions before)
  • update via new_value = NonZeroU8::new(next_scale as u8).unwrap_or(last_value)

Copy link
Owner

@dholroyd dholroyd left a comment

Choose a reason for hiding this comment

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

Looking good

@scottlamb scottlamb merged commit 98048f0 into dholroyd:master Jan 28, 2025
2 checks passed
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