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

Fixed issue with image buffers not being displayed properly #1795

Merged
merged 3 commits into from
Jan 29, 2025

Conversation

skibu
Copy link
Contributor

@skibu skibu commented Aug 8, 2024

Video is worth a thousand words so here is what it is supposed to look like: https://youtu.be/81JUmu6EaWg

And here is what it looks like using an image buffer. Quite disappointing. https://youtu.be/W6i9Ffs2YzA

Please provide feedback if you have additional suggestions.

I tried to implement the dynamic scrolling of some pretty pictures in my Norns script that is under development. In order to do some interesting things graphically I tried reading in a PNG file into an image buffer, and then just writing the image buffer to the screen using screen.display_image(). The images showed up, but maybe a quarter of the time I just got a blank screen or just the lower part of the image. It was quite disappointing and not very pretty at all.

I did lots of exploring and testing and found that there is a timing issue. Screen commands are not executed fully immediately. Instead, they are queued and then processed by Cairo. It means that after doing a screen.clear() and then immediately afterwards a screen.display_image(png_buffer, x, y) that the clearing of the screen hadn't yet been completed. This resulted sometimes in the image buffer being overwritten by the clearing of the screen that was still in process. Likely, this is also occurring for other graphics operations, but just not yet noticed by anyone.

The solution was to change weaver.c _screen_clear() so that it doesn't return until the clear has been processed. I found that putting in a delay after the screen_clear() fixed the problem, but a delay is of course not a desirable solution. Since don't have good access to the workings of the screen queue, nor to Cairo, I found that needed to use a screen command that waits for results. This of course makes sure that the previous clear event is done. I chose the simplest screen function that returns a value, which is _screen_current_point().

The difference is like night and day. With the simple change to weaver.c the image buffer is always drawn correctly.

I created two scripts to illustrate the problem and the solution. Scripts are at https://github.com/skibu/nornsFun and are scrollImageFile.lua and scrollImageBuffer.lua .

scrollImageFile.lua shows what it is supposed to look like and doesn't actually use an image buffer. But scrollImageBuffer.lua will show the problem. And you can try the simple change to weaver.c to see how scrollImageBuffer.lua works with the change.

And lastly, I made screen.c consistent and added surface_may_have_color = true; to screen_surface_display(). I think it was previously just an oversight from when other screen changes were made a few months ago.

Since screen_event_clear() only puts item a request into a queue it turns
out that the clearing of the screen can actually not be finished until
after the next screen command has started. This is especially apparent
if the next screen command is screen.display_image() for displaying
a buffer. Frequently the end result is that the image buffer is not
displayed at all or only part of it becomes visible. Therefore need to
execute a command that gets a return value to make sure that the screen
has finished being cleared before this function returns. Using
_screen_current_point() here completely fixes the problem.

Wanted to just look at the screen queue to see if there was a way to
wait until the queue was cleared but there did not appear to be such
a solution.
It appears that during previous round of screen changes that
screen_surface_display() was modified to include
surface_may_have_color=true. But screen_surface_display() does
the exact same thing, and therefore should be consistent.
Therefore made the code consistent in order to avoid problems
from cropping up.
@dndrks
Copy link
Member

dndrks commented Aug 8, 2024

i'm not confident enough to speak on the screen redraw backend, but wanted to add that @skibu 's changes test well for me -- seems like a very sensible solution!

i was able to get scrollImageBuffer.lua drawing appropriately without those changes, through @skibu's suggestion of just calling screen.current_point() after the screen.clear() in the Lua layer -- @tlubke, do you remember if the necessity of the screen.current_point() call is by design? if not, then it seems like the proposed changes are a good fix!

@tlubke
Copy link
Collaborator

tlubke commented Aug 8, 2024

... -- @tlubke, do you remember if the necessity of the screen.current_point() call is by design? if not, then it seems like the proposed changes are a good fix!

I wouldn't say the necessity is by design, but rather a consequence of moving Cairo operations into its own thread and some of the functions were required to be blocking asynchronous. Some of the open GH issues about screen seem related.

I'll need to get back into the code a little bit before I can say whether or not it's a good idea to call _screen_current_point() inside _screen_clear(). I'm curious if just screen_results_wait() would be enough to sync, or if there has to be an asynchronous event like current_point in order for that to work. (this definitely deadlocks the on the results semaphore if there are no results to wait on.

There are definitely some subtle timing things to work out with the screen that are probably beyond the scope of this particular PR. I'll check in here again soon.

@tlubke
Copy link
Collaborator

tlubke commented Aug 8, 2024

The solution might be to give all screen functions in weaver an event so that screen operations happen sequentially. Specifically screen_load_png(), screen_display_image(), and screen_display_image_region() for this example. I'll try that out quick.

EDIT: Yeah, that seems to do the trick. screen_load_png() already has a screen event. Not sure why screen_display_image() and screen_display_image_region() didn't get one. Is there any reason why they should exist outside of the event loop?

@tlubke
Copy link
Collaborator

tlubke commented Aug 8, 2024

2024-08-08_norns_pr_1795_d.patch

Here's a patch that adds two more screen events, one for display_surface() ("display_image" in Lua) and the other for display_surface_region(). This seems to fix https://github.com/skibu/nornsFun/blob/main/scrollImageBuffer.lua for me.

@skibu
Copy link
Contributor Author

skibu commented Aug 8, 2024

I’ll try out @tlubke ’s patch in a bit. I’m pretty confident that adding the new screen events is the proper solution. I can see how making functions like screen_display_image() event driven because the event queue would sequentially process the events as desired. Thanks for coming up with a solid solution!

Btw, I had also tried just using screen_results_wait(). It deadlocking meant that I needed a command that retrieved data in order to sync the commands.

This code change was purely done by @tlubke. It addresses the issue
of making sure a display image buffer command doesn't happen until
the screen clear command has been executed. This was done by
making the display image buffer commands events, which means they
are processed by the event queue sequentially. Tested this change
for PR monome#1795 and it works great. Reverted my original proposed
change in weaver.c.
@skibu
Copy link
Contributor Author

skibu commented Aug 9, 2024

I tested out @tlubke 's change and took out my original change. It works great! Therefore I checked in @tlubke 's changes into the branch for everyone to easily see.

I think that this PR can now be re-reviewed by folks and accepted if others agree.

@catfact
Copy link
Collaborator

catfact commented Aug 9, 2024

Thanks awesome

Yes, queing everything is better than blocking the caller (main thread)

@tlubke
Copy link
Collaborator

tlubke commented Aug 11, 2024

Probably a good time to cross off #1747 too. I got started on it and modified scrollImageBuffer.lua to use screen.draw_to() on png_buffer. I occasionally see a flicker on the scrolling image, so not sure if I implemented everything correctly, or if I went too far with how many functions I made into events.

I'll push a branch and see what people think. tlubke@a995249

EDIT: I removed screen.update() from the function inside screen.draw_to() and the flicker went away. Could be a hint to what the issue might be.

@catfact
Copy link
Collaborator

catfact commented Jan 11, 2025

@tlubke

so, i really haven't been able to get dug in to norns code for a while, i think you are the expert on the screen modules at this point. you now have write access; feel free to approve and merge if you see fit.

@tlubke
Copy link
Collaborator

tlubke commented Jan 13, 2025

Sounds good, I'll go over these changes again soon and see if I can remember where my train of thought was on my last edit.

If there is an upcoming norns version release, it probably makes sense to get this fix merged in there.

Copy link
Collaborator

@tlubke tlubke left a comment

Choose a reason for hiding this comment

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

I spent a little while tracing back through this code and some of that paths it effects. I think this is good to merge on a functional basis.

I realize these are mostly my changes and I had forgotten about the coding style in the wiki here. Looking at some more recent commits in matron though, they don't seem to bet strictly adhered to.

@skibu I'll leave it up to you if you want to adjust the formatting to match the wiki.

I've approved the PR, and will merge it in a week or two if there is no opposition from other maintainers on formatting.

ev->buf = NULL; // Didn't make a copy of the pointer, nullify to avoid double free.
break;
case SCREEN_EVENT_DISPLAY_SURFACE_REGION:
screen_surface_display_region(ev->buf, ev->payload.d.d1, ev->payload.d.d2, ev->payload.d.d3, ev->payload.d.d4, ev->payload.d.d5, ev->payload.d.d6);
Copy link
Collaborator

Choose a reason for hiding this comment

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

minor: coding style (c), column length.

screen_event_data_push(&ev);
}

void screen_event_display_surface_region(void* surface, double left, double top, double width, double height, double x, double y) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

minor: coding style (c), column length + '*' alignment.

@@ -33,6 +33,8 @@ extern void screen_event_close_path(void);
extern void screen_event_text_extents(const char *s);
extern void screen_event_export_png(const char *s);
extern void screen_event_display_png(const char *filename, double x, double y);
extern void screen_event_display_surface(void* surface, double x, double y);
extern void screen_event_display_surface_region(void* surface, double left, double top, double width, double height, double x, double y);
Copy link
Collaborator

Choose a reason for hiding this comment

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

minor: coding style (c), column length + '*' alignment.

@tehn
Copy link
Member

tehn commented Jan 22, 2025

for what it's worth, the entire repo might benefit from having a clang-format pass (there is a rule, i think it should match the code style)

@skibu
Copy link
Contributor Author

skibu commented Jan 25, 2025

Since I was asked, I don't see formatting issues as critical since they are currently only loosely adhered to. If folks want formatting to be consistent I think it is far better to enforce it automatically every time a file is checked into github.

Therefore I agree that would be good to go ahead and merge.

I don't think an official release should be done at this time though. After this PR I found another screen function that needs to be a screen event in order to prevent problems. Plus I found some other problematic bugs as well. I'll try to create issues for these soon so that they an be dealt with.

@skibu
Copy link
Contributor Author

skibu commented Jan 27, 2025

I finally was able to create a separate issue for two other functions that also need to be queue functions. The issue is #1808 . This PR should be merged though instead of waiting for the new issue to also be resolved.

@tlubke
Copy link
Collaborator

tlubke commented Jan 28, 2025

@skibu Thanks for opening those other issues. I'm going to use #1615 to track/group progress as a whole for all of the remaining screen fixes.

I think you're correct about merging this one instead of waiting.

EDIT: Need to think through the merge process to not lose track of the right authorship.

@tlubke tlubke merged commit 0a15d4d into monome:main Jan 29, 2025
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.

6 participants