maptool icon indicating copy to clipboard operation
maptool copied to clipboard

[Maintenance]: Split rendering responsibility from deciding what to render

Open kwvanderlinde opened this issue 3 months ago • 3 comments

Describe the problem

The renderers (ZoneRenderer et al for Swing, GdxRenderer for LibGDX) are big and complicated, doing too much and they duplicate a bunch of logic between each other. They also depend directly on the model and other state, which means they are hampered by the model's limitations such as lack of thread safety.

The improvement you'd like to see

Introduce the concept of "render instructions". A render instruction is a cohesive bit of data that represents something to render. E.g.:

  • a ClearScreen instruction contains a colour that the screen should be blanked with.
  • a BoxedString instruction contains a position and some text to draw things like the player visibility note at the top of the string.

The render instructions are produced by the ZoneCompositor. The compositor will run on the Swing thread (so threading isn't a concern), and based on the model and view state will build the list of render instructions that are needed to render the zone. E.g., if a zone has fog of war enabled, the compositor output instructions for rendering fog of war, but if the zone does not have a fog of war enabled then those instructions will be absent. Taken together, the list of render instructions are essentially a thread-safe snapshot of model and view state that needs to be rendered.

The Swing and LibGDX renderers will consume the render instructions, actioning them in whatever way is appropriate for that renderer.

Expected Benefits

  • Cleaner renderer code, including less duplication between Swing and LibGDX renderers
  • Ability to render off the Swing thread (important for certain LibGDX backends)
  • Decoupling of the renderers from the model.
  • Removal of often redundant rendering logic from the model code.

Additional Context

No response

kwvanderlinde avatar Oct 08 '25 23:10 kwvanderlinde

As I go, I'm trying to make sure that the new approach works well enough for both the ZoneRenderer and the to-be-resurrected GdxRenderer.

One major issue I'm running into with GdxRenderer is that it is really expensive to triangulate geometry every frame, especially circular geometry as that requires a lot of triangles to approximate well enough. In some cases, we can handle this kind of geometry specially to get faster rendering, such as dot states that we know are just going to be circles. But in general, it would be better if we could avoid meshing every frame.

Solving that is going to take some work. I may make some concessions in the short term that could seem odd without this context, and might leave other parts of the solution for the future. Depends how inspired I get.

But if anyone has ideas for solutions, I'm all ears.

kwvanderlinde avatar Nov 19 '25 23:11 kwvanderlinde

My plan was to cache the triangles in the renderer and invalidate it if the object changes.

thelsing avatar Nov 20 '25 09:11 thelsing

As I go, I'm trying to make sure that the new approach works well enough for both the ZoneRenderer and the to-be-resurrected GdxRenderer.

One major issue I'm running into with GdxRenderer is that it is really expensive to triangulate geometry every frame, especially circular geometry as that requires a lot of triangles to approximate well enough. In some cases, we can handle this kind of geometry specially to get faster rendering, such as dot states that we know are just going to be circles. But in general, it would be better if we could avoid meshing every frame.

Solving that is going to take some work. I may make some concessions in the short term that could seem odd without this context, and might leave other parts of the solution for the future. Depends how inspired I get.

But if anyone has ideas for solutions, I'm all ears.

Probably a first step, which may also be a "quickish win" (might be a pain with the swing renderer though, in which case you could have the swing renderer ignore the "nothing has changed" flag and rebuild), is to track if anything has changed, and only rebuild the geometry if that is the case, because it's unlikely that players are updating lights/vision/vbl/zooming or dropping tokens or moving tokens, etc 60 times a second 😆 Then see what is causing the most pain from there.

cwisniew avatar Nov 20 '25 12:11 cwisniew