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

Add a function for retrieving the window contents #104

Merged
merged 9 commits into from
Jun 2, 2023
Merged

Conversation

notgull
Copy link
Member

@notgull notgull commented May 13, 2023

Closes #36 by adding a fetch() method for the Buffer method. Might need a rework in the face of #98 or #99.

WIP for now, with only implementations for X11 and Win32. I would appreciate guidance from the platform maintainers on how this would be implemented on other platforms.

@ids1024
Copy link
Member

ids1024 commented May 13, 2023

On Wayland, if we want to read from the back buffer we can just do that. If we want to get window contents that this softbuffer Surface didn't present, there's no way to do so. For macOS and WASM presenting currently still copies, so this could just be a no-op. On Redox, presumably mapping the window and reading will work.

I'm not sure exactly how this API would be used though. It doesn't seem ideal to use every frame. If we have make a request to the display server and copy all the pixels to do this, would to be better for the application that wants to retain graphics to just have it's own persistent buffer and copy into the softbuffer::Buffer when it draws?

@daxpedda
Copy link
Member

Web implementation: #105.

If we want to get window contents that this softbuffer Surface didn't present, there's no way to do so.

I assumed that this is the purpose of this API. I implemented the Web implementation accordingly, otherwise it would be no-op like you suggested.

@daxpedda
Copy link
Member

Additionally, I'm unsure how other platforms then Web handle this, but what if the surface was resized, what does fetch do in this case, error? Or just get part of the contents that fit into the buffer? Does it zero out the rest if the fetched content is smaller then the current size?

@daxpedda daxpedda mentioned this pull request May 13, 2023
@ids1024
Copy link
Member

ids1024 commented May 13, 2023

I assumed that this is the purpose of this API.

If so, this isn't possible on Wayland. I'm guessing it's not possible on macOS given how the APIs work, but I would be less confident there.

It's not necessarily bad to expose features that only work on some backends, but I'd like to see more clearly what the actual application for this would be. And whether or not its the best way to achieve that.

@notgull
Copy link
Member Author

notgull commented May 13, 2023

The goal of this API is to get the currently presented contents of the buffer and download it to the client. This is nice for testing, since it allows you to get the image created by the user. It would be nice for testing not only softbuffer but also drawing frameworks like theo.

@ids1024
Copy link
Member

ids1024 commented May 13, 2023

Ah. For debugging uses it makes more sense. Then the performance concerns that would otherwise apply here aren't so much of a problem.

It should be clearly documented what this is and is not suitable for though.

I also wonder whether a method on Buffer like this is best. For debugging purposes, a Surface method returning Result<Vec<u32>, _> could be more suitable. If it's updating the contents of the Buffer that would clash with incremental rendering using buffer age when #99 is merged.

@notgull
Copy link
Member Author

notgull commented May 14, 2023

I also wonder whether a method on Buffer like this is best. For debugging purposes, a Surface method returning Result<Vec<u32>, _> could be more suitable. If it's updating the contents of the Buffer that would clash with incremental rendering using buffer age when #99 is merged.

That's probably a good idea. I've changed this PR to use this design.


// Set all pixels to red.
let mut buffer = surface.buffer_mut().unwrap();
buffer.fill(0xFF0000FF);
Copy link
Member

Choose a reason for hiding this comment

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

That looks like blue to me.

/// 00000000RRRRRRRRGGGGGGGGBBBBBBBB

Suggested change
buffer.fill(0xFF0000FF);
buffer.fill(0x00FF0000);

Copy link
Member

Choose a reason for hiding this comment

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

0x00FF0000 won't be the same color on big-endian systems, right? If we care about those.

Something like u32::from_ne_bytes could be used for consistent behavior.

Copy link
Member

Choose a reason for hiding this comment

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

I don't think so? I went down a rabbit hole for a bit there and confirmed two things:

  1. Literals are always written in big-endian in Rust. I couldn't find any documentation on that, so I tried it out: https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=c1a0820eba008226bae7771c1f510fe8.
  2. Bitshift operators always act in big-endian (except on some old PowerPC architectures I read somewhere?).

So I think what we are doing in Rust right now, seems to be pretty much cross-endianness compatible.
What I don't know is if the APIs expect us to turn things around on big-endian targets.
I also assume nobody here actually has a big-endian system lying around to try it out?

That said, on Web, my purview, this isn't a concern.

Copy link
Member

Choose a reason for hiding this comment

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

I think the use of bitshifts in the examples would also be wrong on big endian. We represent color with u32, but it's really interpreted more like [u8; 4] but with 4 byte alignment. Which isn't dependent on endianess.

I don't have a big endian system to test on, though I fixed a similar issue in tiny-skia that someone reported: linebender/tiny-skia#69. That one is easy to test with virtualization, but this is a bit more complicated since one may want a full OS with a display server. Maybe X11 backend over TCP could work...

Copy link
Member

@daxpedda daxpedda May 14, 2023

Choose a reason for hiding this comment

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

Maybe X11 backend over TCP could work...

Sounds like fun, please share the results when you get them 🤣.

Copy link
Member

Choose a reason for hiding this comment

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

It can be faster to work with 32-bit values than individual bytes. And things like tiny-skia work on &[u32]. And you can safely cast &[u32] to &[u8] but not the other way around due to alignment.

Copy link
Member

Choose a reason for hiding this comment

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

I'm not sure what to think of this, there isn't actually a safe method to cast &[u32] to &[u8] I know of. I would argue that u32 is definitely a worse user interface and the improved speed is speculation (I actually agree with that speculation).

Just an idea: what do you think of &[[u8; 4]]?

Copy link
Member

Choose a reason for hiding this comment

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

[u32] has alignment 4 (on typical architectures), while [u8; 4] has alignment 1. So there's not currently a standard library method to cast &[u32] to &[u8], but it's always sound while the reverse is not true. Likewise it is potentially unsound to cast &[[u8; 4]] to &[u32].

bytemuck is popular for this sort of thing.

Copy link
Member

Choose a reason for hiding this comment

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

while [u8; 4] has alignment 1.

Ah, apologies, was thinking that this does have an alignment of 4 actually, for some reason (I think I played around with too much shaders lately ...).


Alright, w/e, I think I'm still in favor of &[u8] until we can show a reasonable performance degradation, but this is an issue for another time.

In the meantime, your original problem still needs to be addressed. I think we should also document this somewhere.

/// 00000000RRRRRRRRGGGGGGGGBBBBBBBB

This could be a bit confusing, because it's showing big-endian order, which most platforms aren't, but it's also representative of how literals are written in Rust.

Copy link
Member

Choose a reason for hiding this comment

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

I say we handle it afterwards: #109.

@daxpedda
Copy link
Member

daxpedda commented May 14, 2023

The goal of this API is to get the currently presented contents of the buffer and download it to the client.

So should this account for stuff not presented by softbuffer? Because if not, backends that don't have a buffer age can just return their buffer (if I understand this correctly).

@notgull
Copy link
Member Author

notgull commented May 14, 2023

The goal of this API is to get the currently presented contents of the buffer and download it to the client.

So should this account for stuff not presented by softbuffer? Because if not, backends that don't have a buffer age can just return their buffer (if I understand this correctly).

Yes, this is what I wanted. Right now, my goal is to have a reproducible test harness; not only in softbuffer, but in other crates too. For theo, what I want to do is:

  • Render an image to the window.
  • Use softbuffer to fetch that image.
  • Compare that image against an existing image to detect regressions.

@ids1024
Copy link
Member

ids1024 commented May 14, 2023

We'll need to consider how this interacts with #98. Does the method fetch accept a format argument? Would the supported formats be the same as for presenting? Or does it return a value tagged with the format the surface contains? I'm not sure exactly how it works in the backends here.

Conversion is not necessarily lossless, if we support pixel formats that aren't just orderings RGB (though again, things like 10 bit color won't work with u32 pixels).

#98 is probably the most important feature missing in softbuffer currently, though I'm still not sure how best to design that, including conversion functionality.

@daxpedda
Copy link
Member

  • Render an image to the window.
  • Use softbuffer to fetch that image.

On Web, the Context created has to be the same, if the original drawer used "webgl2", softbuffer will fail, because it's using "2d". Not sure if we care about this case, considering we are "softbuffer" and all.

Same if transferControlToOffscreen() was used, though this can be worked around with #103.

This was referenced May 19, 2023
@notgull notgull marked this pull request as ready for review May 19, 2023 18:53
@notgull
Copy link
Member Author

notgull commented May 19, 2023

I think this is ready now. macOS, Wayland and Redox are unimplemented for now; I'm hoping that the platform maintainers will be able to take care of that.

notgull and others added 3 commits May 19, 2023 12:00
Fix X11 bugs

- Vec not being filled all the way
- Tests need to be run with Xvfb
- Add note about visibility
Use winit-test as a test harness

Redo testing harness for WASM

Make sure the wayland runner doesn't use xvfb
Adjust Web code style
Adjust `fetch()` test for Web

Test fixes
Add line to docs about unsupported platforms

Test fixes
@notgull
Copy link
Member Author

notgull commented May 19, 2023

I'm unsure why WASM is failing by returning an empty buffer, nor am I sure why Windows seems to spurious fail in the same way.

@daxpedda
Copy link
Member

daxpedda commented May 19, 2023

Web fails because #104 (comment) wasn't applied. Currently fails on X11 because of that too.

I confirmed that both succeed when applying #104 (comment).

src/lib.rs Show resolved Hide resolved
@notgull
Copy link
Member Author

notgull commented Jun 2, 2023

Looks like adding GdiFlush fixed the Win32 backend. Hopefully this means that all of the outstanding issues are resolved.

@notgull notgull merged commit 4424847 into master Jun 2, 2023
@notgull notgull deleted the notgull/fetch branch June 2, 2023 03:09
@notgull notgull mentioned this pull request Jun 4, 2023
notgull added a commit that referenced this pull request Jun 4, 2023
* On MacOS, the contents scale is updated when set_buffer() is called, to adapt when the window is on a new screen (#68).
* **Breaking:** Split the `GraphicsContext` type into `Context` and `Surface` (#64).
* On Web, cache the document in the `Context` type (#66).
* **Breaking:** Introduce a new "owned buffer" for no-copy presentation (#65).
* Enable support for multi-threaded WASM (#77).
* Fix buffer resizing on X11 (#69).
* Add a set of functions for handling buffer damage (#99).
* Add a `fetch()` function for getting the window contents (#104).
* Bump MSRV to 1.64 (#81).
@daxpedda daxpedda mentioned this pull request Jun 4, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

Successfully merging this pull request may close these issues.

Add a method for getting pixels from a window
3 participants