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

Set effect render visibility based on token vision #290

Closed
wants to merge 2 commits into from

Conversation

Codas
Copy link
Contributor

@Codas Codas commented Aug 15, 2024

Currently, masking is used exclusively to hide effects that are occluded from token vision by walls etc.
This means currently every effect on the game canvas is still rendered and then cut from the canvas, even though it might not be visible at all.

This PR calculates visibility of effects based on the 4 corners and the center (+tolerance) of the effects bounding box with the vision polygon and sets the PIXI.Containers visibility to false in case it is not visible.

With this PR I also want to open a discussion about effect visibility masking:
Masking of effects completely removes every possibility that the drawing of effects can be batched which introduces a significant performance bottleneck.
Since now effects that are not inside the players vision (based on the effects boundin box) are not shown at all, I think it can make sense to move the masking operation of effects behind a performance setting.
Maybe by default only apply masking if foundry performance is set to high or above with a separate override in the Sequencer settings?

@Haxxer
Copy link
Collaborator

Haxxer commented Aug 16, 2024

What are the optimizations that masking prevents?

@Codas
Copy link
Contributor Author

Codas commented Aug 16, 2024

That's a bit more complicated. The answer is "draw call batching" but that alone is probably not helpful.

Essentially, when drawing something with WebGL, many operations have to be performed. Shader set that is to be executed, geometry and textures registered etc but that is akin to just setting some flags in an object in JavaScript.
the real expensive operations happens with the actual "draw call" which essentially tells the driver or abstraction layer (webGL in our case) "please execute all these instructions and draw the stuff I told you to do". This steps involves a lot of translations to actual GPU code, uploading of data to the GPU, essentially a lot of overhead.
Mond important optimization therefore is to reduce the number of draw calls to render each frame.

Good news is that very often a bunch of stuff can happen in one draw call even. Moden GPUs Fire example can upload and reference up to 16 textures in one draw call, which in the case of sequencer means that potentially up to 16 effects could be rendered at once. In case of sprite sheats (which are one texture) this even increases to 16 unique effects, meaning 50 times the same token animation for example could still be just one texture in one draw call.

the problem is that in order to mask one effect, this effect has to be rendered in its own to then be masked or cut into shape. This is one draw call to draw the effect to a separate texture and one draw call to mask the effect.
this has to happen even if the effect in the end is fully shown, as long as the mask is present.

Which means that instead of one draw call for 16 effects (the one performance heavy operation) with masking 16 effects would introduce 32 draw calls. One each for rendering the effect, one for masking.

In absolute performance numbers this means that one animation im working on and something like aura effects could tank my performance to unplayable frame rates whereas without masking there is hardly any performance impact at all.

@Haxxer
Copy link
Collaborator

Haxxer commented Aug 18, 2024

Interesting - does this draw call consolidation require a new PR then? I'm open to adding an option to have some levels of detail in this regard, so that we can optimize it a bit. Is there any way we could consolidate draw calls of non-masked effects separately from masked effects?

@Codas
Copy link
Contributor Author

Codas commented Aug 18, 2024

No, nothing has to be changed. Batching is done automatically.

Currently each effect also has additional filters applied (which work the same as masks with regards to breaking batching and how they are applied in PIXI), but this will be taken care of in my other PR #275. Though that one has to be updated with this code and to re-enable masking if it is enabled.

With regards to batching: All effects that are rendered in sequence that have neither masks nor filters applied should be batched automatically up to a certain limit (8 or most often 16 textures for modern GPUs).
A batch group would be broken as soon as one effect with masks or filter has to be rendered or in case of foundry v12 a token with a dynamic ring is to be rendered.

For example:

Effect1(with Filter), Effect2, Effect3, Effect4, Token1(regular), Token2(dynamic ring), Token3(dynamic ring), Effect5, Effect 6

could be rendered in batches of:
Effect 1 separately because it has a Filter (2 draw calls)
Effect 2, 3, 4 and Token 1 in one draw call
Token 2 and Token 3 will be rendered in one batch as the dynamic ring shader is batchable.
Finally, Effect 5 and 6 can be drawn in one call again.

All in all this could be rendered in 5 draw calls.
With maskets enabled for the effects this increases to 14 draw calls.

I really think just introducing a setting, something like "enable effect vision masking" that is enabled by default or something like this would be best. That way people with slower hardware could just disable masking for a large performance increase and everyone else just gets the best visuals.

This PR really does one thing, not rendering effects that are occluded by walls etc from the vision of tokens.
This archives two things:

  1. It improves performance in the general case since effects that don't have to be rendered simiply are not. No draw calls etc for those!
  2. It would provide a fallback to masking the effects in case pixel-perfect effect masking becomes optional in the future.

@Codas
Copy link
Contributor Author

Codas commented Aug 19, 2024

I'll close this (for now) in favor of the vision mask batching PR. This might still be a nice performance improvement in certain cases, but it also has certain problematic edge cases I want to examine in more detail

@Codas Codas closed this Aug 19, 2024
@Codas Codas deleted the effect-visibility-culling branch September 23, 2024 18:08
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.

2 participants