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

initial implementation #1

Merged
merged 13 commits into from
Jun 14, 2024
Merged

initial implementation #1

merged 13 commits into from
Jun 14, 2024

Conversation

scottlamb
Copy link
Collaborator

No description provided.

@scottlamb scottlamb requested review from ccbrown and Xaeroxe June 7, 2024 17:26
Copy link

@ccbrown ccbrown left a comment

Choose a reason for hiding this comment

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

1 blocking request. Overall lgtm, but I haven't gotten deep into the code yet.

LICENSE-MIT.txt Outdated Show resolved Hide resolved
Cargo.toml Outdated Show resolved Hide resolved
* use "Infinite Athlete, Inc." everywhere
* get rid of unneeded `Cargo.toml` release profile tweaking
@scottlamb scottlamb requested a review from ccbrown June 7, 2024 19:33
src/uyvy_to_i420.rs Outdated Show resolved Hide resolved
/// `uyvy_in` is of the shape `(height, width / 2, 4)` and channels are (U, Y, V, Y).
/// Guaranteed to initialize and fill the supplied output planes on success.
/// `y_out` is of the shape `(height, width)`; `u_out` and `v_out` are each of
/// the shape `(height / 2, width / 2)`.
Copy link

Choose a reason for hiding this comment

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

I would suggest adding a # Panics section here to make it clear what happens when the args aren't the correct shape. Or make the function return an error in those cases instead.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

With the new frame abstraction, the only panics are here:

    let [uyvy_in] = &uyvy_planes[..] else {
        panic!("uyvy_in must have one plane");
    };
    let mut yuv_planes = yuv_out.planes();
    let [y_out, u_out, v_out] = &mut yuv_planes[..] else {
        panic!("yuv_out must have three planes");
    };
    // ...
    assert_eq!(uyvy_in.data.len(), pixels * 2);
    // ...
    assert_eq!(y_out.data.len(), pixels);
    assert_eq!(u_out.data.len(), pixels / 4);
    assert_eq!(v_out.data.len(), pixels / 4);

...which can only happen if you supply a buggy implementation of Frame/FrameBuf that returns a number/size of planes inconsistent with its pixel_format(). To me that's kind of a duh, of course it will panic situation, not worth calling out. Would you agree?

src/uyvy_to_i420.rs Outdated Show resolved Hide resolved
src/uyvy_to_i420.rs Outdated Show resolved Hide resolved
src/uyvy_to_i420.rs Show resolved Hide resolved
src/uyvy_to_i420.rs Outdated Show resolved Hide resolved
src/uyvy_to_i420.rs Outdated Show resolved Hide resolved
* add `Frame`/`FrameBuf` abstraction to allow caller to avoid
  preinitialization without any use of `unsafe`. This also offers
  an interface for padded lines, although the conversion currently
  returns unsupported if it's used.

* return a `ConversionError` rather than the more specific
  `Unsupported`. The error is currently opaque; this could be changed
  later without breaking compatibility.
@scottlamb scottlamb requested review from ccbrown and Xaeroxe June 11, 2024 17:34
src/uyvy_to_i420.rs Outdated Show resolved Hide resolved
src/frame.rs Outdated Show resolved Hide resolved
@Xaeroxe
Copy link

Xaeroxe commented Jun 11, 2024

For the sake of making my stance clear I'm really close to giving an approval. My outstanding concerns are #1 (comment) and #1 (comment)

#1 (comment) is a very minor opinion based thing but I recognize there are drawbacks to my proposal so I'm not firm on it.

src/frame.rs Outdated
}

pub struct FrameBufPlane<'a> {
pub data: &'a mut [MaybeUninit<u8>],
Copy link

@ccbrown ccbrown Jun 11, 2024

Choose a reason for hiding this comment

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

Since FrameBuf is unsafe anyway, is there any reason not to use pointer / length here? As-is, I still don't think it'd be possible to fill an arbitrary &mut [u8] not backed by a Vec (or something convertible to &mut [MaybeUninit<u8>]), right?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I like &'a mut [MaybeUninit<u8>] as a nice readable way of bundling the lifetime, pointer, and length. It also offers safe writing and thus can limit the size of the unsafe blocks required / ways a writer is likely to screw up even if they ultimately still need an unsafe { buf.finish() } at the end to assert they're initialized everything.

On the other hand, If filling uninitialized data will be mostly done by the conversion stuff here—that uses raw pointers anyway for vendor SIMD intrinsics—yeah, there's not a huge advantage. We could expose raw pointers instead of MaybeUninit. Yes, that'd let you use a &mut [u8] as a target of conversion which yes I think my current FrameBuf would not allow. There also wouldn't have to be a separate FrameBuf trait; it could just be a boolean tracking if someone has pretty-promised it's initialized yet. You could always get a raw pointer; you could only get a &[u8] or &mut [u8] post-initialization.

I'm on the fence. What do you think?

As-is, I still don't think it'd be possible to fill an arbitrary &mut [u8] not backed by a Vec (or something convertible to &mut [MaybeUninit<u8>]), right?

I said no before...and as I think about it some more, I think probably no? The thread I linked was talking about how a MaybeUninit<T> could fill in an invalid bit pattern for T. There's no invalid bit pattern for u8. It's still forbidden to to make an uninitialized u8 though. I think it comes down to this: MaybeUninit<T> implements Copy, so if you do *this = that; where that is uninitialized, do you "taint" a previously-initialized this with uninitialized status? I guess so?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

There also wouldn't have to be a separate FrameBuf trait; it could just be a boolean tracking if someone has pretty-promised it's initialized yet.

That said I'd probably still provide two separate structs implementing the one trait. One specifically for a Vec so it can do the set_len call on finish. One for borrowed buffers; you could get an initialized one from a &mut [u8] or an uninitialized one from a &mut [MaybeUninit<u8>].

Copy link

Choose a reason for hiding this comment

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

I think the risk of the user doing something wrong as a result of needing to do something the library doesn't support might be the bigger risk here. And I would expect the goal to be for us to provide implementations of the trait to cover nearly all use-cases, right? I think that could be done with potentially just two implementations: one that takes a &mut [MaybeUninit<u8>] and one that can take in a DerefMut<Target=[u8]>.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

And I would expect the goal to be for us to provide implementations of the trait to cover nearly all use-cases, right?

I think that there will be Frame impls in other libraries that implement capture/decoding/encoding/sending, to avoid unnecessary copies. But hopefully they're not too crazy to write in either case.

I redid this abstraction with a Frame and additive FrameMut trait. And then the concrete implementation...there's one, but also uses Storage/StorageMut traits with a few implementations. I didn't do it based on Deref/DerefMut because Vec is special, and there's no stable/sound specialization.

Comment on lines +80 to +83
data: *const u8,
stride: usize,
len: usize,
initialized: bool,
Copy link

Choose a reason for hiding this comment

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

Should there be a pub way to construct a FramePlaneRef and FramePlaneMut?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Yes, we'll need those for the outside-the-crate frame impls, and I added them now. I'm not totally sure I have the right signature for them; I could add in width also, as in how many bytes each row uses without padding; width <= stride. This would allow the PartialEq/Eq impls to skip the padding bytes as well as just exposing the width to callers.

@scottlamb scottlamb requested review from ccbrown and Xaeroxe June 13, 2024 20:57
Copy link

@Xaeroxe Xaeroxe left a comment

Choose a reason for hiding this comment

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

Minor issues. I don't consider these worth blocking if you're feeling pressure to get this merged.

src/frame.rs Outdated Show resolved Hide resolved
src/frame.rs Outdated Show resolved Hide resolved
src/frame.rs Outdated Show resolved Hide resolved
Xaeroxe
Xaeroxe previously approved these changes Jun 13, 2024
Copy link

@Xaeroxe Xaeroxe left a comment

Choose a reason for hiding this comment

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

LGTM!

ccbrown
ccbrown previously approved these changes Jun 14, 2024
Copy link

@ccbrown ccbrown left a comment

Choose a reason for hiding this comment

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

One very minor question, but lgtm!

src/lib.rs Outdated

pub use uyvy_to_i420::convert as convert_uyvy_to_i420;

pub use arrayvec::ArrayVec;
Copy link

Choose a reason for hiding this comment

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

Would it be better to re-export arrayvec? With only arrayvec::ArrayVec, there are certain things users won't be able to do, like reference arrayvec::CapacityError by name.

@scottlamb scottlamb dismissed stale reviews from ccbrown and Xaeroxe via f0f0528 June 14, 2024 18:47
@scottlamb scottlamb merged commit dd6d4c3 into main Jun 14, 2024
2 checks passed
@scottlamb scottlamb deleted the initial-impl branch June 14, 2024 19:05
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