Skip to content

Add support for invalidation on OpenGL renderer #20073

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

Merged
merged 29 commits into from
Apr 25, 2025

Conversation

ZehMatt
Copy link
Member

@ZehMatt ZehMatt commented Apr 28, 2023

There are some minor quirks remaining, the rain is currently not drawing correctly and sometimes when panning a few stray pixels will appear from the toolbar somehow. Other than that its mostly fine for testing, this dramatically increases the performance on large parks where nothing is really going on.
Here is an example:
develop:
openrct2_2023-07-04_20-33-35c660add360c7647d9
PR:
openrct2_2023-07-04_20-33-52dc4401a9d2488f7b4

I'll have it in draft until I fix the remaining things, there might be other broken things so this is a good opportunity to start testing early.

@ZehMatt ZehMatt force-pushed the render-cleanup branch 2 times, most recently from d7af8ef to e386258 Compare April 28, 2023 16:26
@ZehMatt
Copy link
Member Author

ZehMatt commented May 1, 2023

Things to do:

  • On zooms bigger than default objects seem to be shaking, probably some integer scaling stuff.
  • Rain is currently not working correctly, software just restores the old pixels but doing this in OpenGL would be probably expensive.
  • On zooms bigger than default there are sometimes stray pixels coming from the UI especially the top/bottom toolbar when panning.
  • Bad invalidation in some cases, the first title sequence has some area that is not invalidated for some reasons, switching back to the title menu also causes issues.

@Gymnasiast
Copy link
Member

I ran into a bug as well:

  1. Open a save or scenario.
  2. Quit to menu
  3. Screen is not invalidated correctly.

@ZehMatt
Copy link
Member Author

ZehMatt commented May 3, 2023

Thanks, updated my comment, noticed this before but forgot to put it there.

@ZehMatt ZehMatt force-pushed the render-cleanup branch 2 times, most recently from 0ee7f57 to 134e413 Compare May 21, 2023 23:44
@Gymnasiast

This comment was marked as duplicate.

@Broxzier
Copy link
Member

The issue with the options window is unrelated: #20312

@ZehMatt ZehMatt force-pushed the render-cleanup branch 2 times, most recently from fd3944a to 8557de1 Compare July 4, 2023 17:20
@ZehMatt
Copy link
Member Author

ZehMatt commented Jul 4, 2023

I think I've got all working now, even software will gain quite a major improvement here. On develop I get about 170 FPS on bpb.sv6 at the default zoom and on this PR it is 350 and without the rain it nearly hits 400.

@ZehMatt

This comment was marked as outdated.

@ZehMatt ZehMatt marked this pull request as ready for review July 4, 2023 21:05
@ZehMatt ZehMatt marked this pull request as draft July 9, 2023 13:15
@ZehMatt
Copy link
Member Author

ZehMatt commented Jul 9, 2023

There are still some oddities that needs fixing, IntelOrca and others found some issues.

@github-actions
Copy link

This pull request has been marked as stale and will be closed in 14 days if no action is taken. To keep it open, leave a comment or remove the stale-pr label. If you're awaiting feedback from a developer, please send us a reminder (either here or on Discord).

@tupaschoal
Copy link
Member

Did you manage to fix the remaining issues @ZehMatt ?

@ZehMatt
Copy link
Member Author

ZehMatt commented Oct 14, 2023

Did you manage to fix the remaining issues @ZehMatt ?

Sort of, there is however a regression in performance with OpenGL which is odd considering it has to draw less 🤷‍♂️ I will get back to this eventually. There needs to be some general cleanup around the rendering code tbh.

@karst
Copy link
Member

karst commented Apr 25, 2025

From my testing (Win10, RTX 4070, Ryzen 5800x, 64GB DDR4 RAM, dual 1440p) this increases the framerate from 11fps to 130 fps when zoomed out. While keeping the framerate around 430 avg on both before/after when fully zoomed in.
image
(Left: Develop / Right: PR)
image
(Left: Develop / Right: PR)

@mixiate
Copy link
Contributor

mixiate commented Apr 25, 2025

The title screen positioning has been fixed now. I think that this invalidation is now working correctly. I do have a couple of things I wanted to mention though:

I hope that a fix for the clipped sprites will go in soon after this, otherwise the game is considerably more glitchy (depending on the park) now with no workaround.

Performance is worse now in large complicated parks which are probably invalidating the majority of the screen on every frame. This was the case for software too when I tested it a while ago. I believe that invalidation can sometimes cause many more column draws than just redrawing the entire screen.

A park like The Screameasy is a good example of both. All the signs and many other parts trigger the clipping bug, and perf is down too.

@ZehMatt
Copy link
Member Author

ZehMatt commented Apr 25, 2025

The title screen positioning has been fixed now. I think that this invalidation is now working correctly. I do have a couple of things I wanted to mention though:

I hope that a fix for the clipped sprites will go in soon after this, otherwise the game is considerably more glitchy (depending on the park) now with no workaround.

Performance is worse now in large complicated parks which are probably invalidating the majority of the screen on every frame. This was the case for software too when I tested it a while ago. I believe that invalidation can sometimes cause many more column draws than just redrawing the entire screen.

A park like The Screameasy is a good example of both. All the signs and many other parts trigger the clipping bug, and perf is down too.

The minor performance regression is a bit odd but I also get the same here, turns out that OpenGL is faster sometimes when it has to redraw everything which feels a bit illogical but the goal here first is to have parity with Software, also the abstraction how this currently works is quite bad, most of it is duplicate code and way to OOP oriented. Also the performance regression is very small the more complex the park. I will do some more PRs after this one now that it is at least fully functional I can focus on making it better. As for the clipping I will have to also experiment around with it, I think the way it culls is too primitive and should best consider the entire hierarchy as in all attached child nodes, the reason it flickers is probably that when the root node is out of bounds it picks one of the next children as the new root so it will potentially draw in a different order.

@mixiate
Copy link
Contributor

mixiate commented Apr 25, 2025

I'm still a bit confused about what you mean with the hierarchy and the nodes. The culling is happening at the point where a sprite is painted, if it gets culled, it doesn't get added in the first place. Am I missing something?

@ZehMatt
Copy link
Member Author

ZehMatt commented Apr 25, 2025

I'm still a bit confused about what you mean with the hierarchy and the nodes. The culling is happening at the point where a sprite is painted, if it gets culled, it doesn't get added in the first place. Am I missing something?

This is not quite correct, it is culled when it creates the graph before its sorted and is then rendered. Anyway I've improved the InvalidationGrid quite a bit and now OpenGL outperforms develop in majority of cases on my end. I will look into the clipping of sprites afterwards but ideally not in this PR.

What I mean by the nodes, have a look at the following code:

PaintStruct* PaintAddImageAsChild(
    PaintSession& session, const ImageId image_id, const CoordsXYZ& offset, const BoundBoxXYZ& boundBox)
{
    PaintStruct* parentPS = session.LastPS;
    if (parentPS == nullptr)
    {
        return PaintAddImageAsParent(session, image_id, offset, boundBox);
    }
   ...
}

LastPS is perhaps a bit of a bad name, this should be LastRootPS/LastParentPS as that is always the last node (PaintStruct) created by PaintAddImageAsParent and in case PaintAddImageAsParent is culling it away it will be set to null making the next child the parent instead. The way it partially chips away things isn't good.

It is probably best to move the culling stage after the entire graph is built, right now it only orders it but it doesn't remove anything, there are probably cases where it would still draw something even when it would be fully covered by lets say a wall.

@mixiate
Copy link
Contributor

mixiate commented Apr 25, 2025

Ah okay I get you, I knew about the child sprite issue but in my head I had separated the two things even though I know it's part of the same thing.

And yeah I wasn't meaning for you to do this in this PR, I just wanted to check if it was intended at all.

@Fuyukai
Copy link
Contributor

Fuyukai commented Apr 25, 2025

At maximum zoom out this seems to cause a lot of things to stop rendering for multiple frames at a time. It's the same as on the software renderer.

2025-04-25.20-28-35.mp4

Apologises for the low quality, but you should still be able to see the issue here.

@ZehMatt
Copy link
Member Author

ZehMatt commented Apr 25, 2025

At maximum zoom out this seems to cause a lot of things to stop rendering for multiple frames at a time. It's the same as on the software renderer.

2025-04-25.20-28-35.mp4
Apologises for the low quality, but you should still be able to see the issue here.

Does this also happen on the develop branch?

Edit: Just tested and that is indeed an issue already in develop, rides probably don't invalidate at this zoom level but the game sometimes refreshes the entire screen which renders everything again for that frame. This is a separate issue, we could check for some rides that are larger like ferris wheels and also invalidate for that zoom, but this should be all done in a separate PR.

@ZehMatt ZehMatt merged commit 0d96235 into OpenRCT2:develop Apr 25, 2025
23 checks passed
@ZehMatt ZehMatt deleted the render-cleanup branch April 25, 2025 21:14
@cheweytoo
Copy link
Contributor

This has made the (known) issues with invisible supports over water way worse in OpenRCT2-v0.4.21-151-g0d96235f32-linux-x86_64.AppImage:

flickering_paths.mp4

I did some bisecting with older dev builds, and this is the first with this issue.

@mixiate
Copy link
Contributor

mixiate commented Apr 26, 2025

waterglitch.mp4

It looks like it is not drawing the same sprites when the paint area gets clipped. Similar to the previous glitching i've been going on about.

Will try and fix this later.

AaronVanGeffen added a commit that referenced this pull request May 4, 2025
- Feature: [#24206] [Plugin] Add APIs for breaking down rides, reading the current breakdown, and for fixing broken down rides.
- Improved: [#20073] The OpenGL drawing engine now supports screen invalidation which avoids the redrawing of unchanged regions.
- Improved: [#21767] RCT Classic for macOS can now be used as the source game.
- Improved: [#23590] Title bars are now drawn bigger when “Enlarged UI” is enabled.
- Improved: [#23626] Add small, medium and large flat and sloped turns, S-bends and diagonal track to the Go-Karts.
- Improved: [#23982] The scenario objective window has been merged into the scenario options window.
- Improved: [#24233] RCT Classic+ from Apple Arcade can now be used as the source game, and is detected automatically.
- Improved: [#24260] Better performance on parks that have a lot of Guests and Entertainers.
- Improved: [#24319] RCT Classic installs via Steam are now detected automatically on Windows.
- Change: [#23803] Lightning strikes and thunder now happen at the same frequency independently of the game speed.
- Change: [#23857] Replace display options tab with custom sprites.
- Change: [#24069] [Plugin] Plugins are now available in the scenario editor and track designer.
- Change: [#24135] Compress Emscripten js/wasm files.
- Change: [#24194] The advanced options tab has been reworked to make it easier to find the RCT1 path setting.
- Change: [#24235] Small changes to RCT1 theme.
- Change: [#24236] Controls and Interface options now both have their own tabs in the Options window.
- Change: [#24308] “Software” and “Software (hardware display)” renderers have been merged into a single “Software” renderer.
- Change: [#24317] The scenery window now shows at least one row of scenery objects, even if there are multiple rows of tabs.
- Fix: [#18479] Tile elements ordered beneath water do not draw correctly.
- Fix: [#19782] Game stops counting inversions and golf holes after 31 (original bug).
- Fix: [#21207] Track List window gets positioned incorrectly.
- Fix: [#21919] Non-recolourable cars still show colour picker (original bug).
- Fix: [#22182] [Plugin] Crash when using map.getAllEntities("car").
- Fix: [#22634] Asset packs with sound effect overrides are not loaded correctly at startup.
- Fix: [#23108] Missing pieces on Hypercoaster and Hyper-Twister, even with the ‘all drawable track pieces’ cheat enabled.
- Fix: [#24013] Failure to load a scenario preview image (minimap) could lead to an uncaught exception error message.
- Fix: [#24045] [Plugin] Data storage is not cleared when converting save game to scenario.
- Fix: [#24121] Checkbox labels run beyond the edge of the window if they’re too long to fit.
- Fix: [#24142] [Plugin] Track origin is miscalculated on downward slopes.
- Fix: [#24220] Narrow station platforms have missing sides on certain rotations.
- Fix: [#24286] Steam installs of RCT1 and RCT2 are not autodetected on macOS.
- Fix: [#24310] [Plugin] Missing invalidation on various plugin api setters for entities.
@LRFLEW
Copy link
Contributor

LRFLEW commented Jul 16, 2025

I'm catching up on some of the changes made when I wasn't active on the project, and came across this PR (after discussing it with @ZehMatt on Discord). Honestly, I'm of the opinion that this was probably a mistake. GPU rendering is based heavily on parallelism, and adding any sort of data dependency (where one operation relies on the result of another) reduces that parallelism. This change causes an inter-frame data dependency, where the GPU can't start rendering the next frame until the previous frame has completely finished rendering. Considering that this already has some pretty heavy intra-frame data dependencies for transparency drawing, I'm concerned by how much this affects throughput. I was testing a fairly busy park across v0.4.21 (the last release before this change) and v0.4.22 (the first release with this change), and found that the framerate using OpenGL seemed to drop from around 30-36 fps to more like 24-30 fps. Yes, invalidation can make more static scenes render faster, but at the cost of making the more involved scenes, which needs the framerate more, have notably worse performance.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
changelog This issue/PR deserves a changelog entry.
Projects
None yet
Development

Successfully merging this pull request may close these issues.