Supporting Depth Testing
This has been discussed idly for some time now, but it feels like we are settling on a vocabulary and set of core concepts that can hopefully support our requirements, so it seems like we should get something written down and see if we can get some wider input too.
- ClutterDepthGroup
- This is a special container that defines a depth testing environment for all of its descendants. Actors will be notified when they enter or leave a depth group. Separate overlapping depth groups will be combined with painters algorithm semantics so for example if you implement a carousel container using depth testing and have multiple such containers overlapping then the children of one carousel will not interact with children of the other.
- Depth Masks
- Depth masks are actor silhouettes/outlines that can be used to prepare the depth buffer. Composite actors may sometimes derive their depth mask by combining the depth masks of children.
- ClutterDepthAware Interface
- Actors that understand how to interact with a ClutterDepthGroup will implement this interface. This will provide the mechanism by which an actor can be notified of entering/leaving a depth group and a mechanism to query an actors depth mask.
Handling 2D Composite Actors
One of the tricky things with supporting depth testing with predominantly 2D composite actors is that we need to avoid z fighting of all the component actors which are all going to be on the same z plane. The current idea is that while drawing the component actors, depth writing should be disabled and only depth testing should be used. When finished drawing the component actors then the container can write a single mask representing itself and those children to the depth buffer so that other actors can now test against that mask. This avoids having overlapping component actors that are drawing on the same plane testing against the masks of siblings. (Note: it is not enough to just use a > test vs a >= to avoid fighting since the precision of the depth buffer and accuracy of the transforms aren't typically high enough for that to work.
Outline of the control flow
- Create a DepthGroup
- Create a bunch of depth aware 2d composite actors and parent them in the DepthGroup
- The 2d actors are notified of entering a depth group since they implement the depth-aware interface.
- All notified actors ensure they have materials with depth testing enabled.
- Start painting the scenegraph, and come to paint the depth group
- The depth group uses its bounding paint-box to clear part of the depth buffer
- Start painting the first 2d composite actor
- All descendant actors paint with depth testing enabled, depth writing disabled.
- The composite actor writes a depth mask (need more details about handling masks derived from child masks)
- Repeat painting steps for remaining 2d composite actors.
Issues
- What are the semantics of nested depth groups?
- If we allow nested depth groups how does this interact with notify entering/leaving of depth groups?
- There needs to be a mechanism to disambiguate who is responsible for writing depth masks. To just say "composite actors must do this" isn't enough since composite actors can be contained within each other, and it's typically only the top-level one (the one directly inside a DepthGroup) that should write the masks.
"Correct" opacity
Every now and then we get complaints that the current model for dealing with transparent actors in clutter doesn't give the desired visual results. The current approach while simple and works ok for leaf node actors can look ugly for composite actors. The result you get for composite actors with overlapping primitives is that the primitives all independently become glassy and as they become transparent you start to see the internal structure of the different primitives. What is usually desired though is that the actor taken as a whole, if considered as a flat 2D image, should have a uniform transparency that doesn't reveal the component primitives of that actors.
After discussing this a bit we have outlined one possible solution:
- We will internally add the ability to give ClutterEffects a priority so it is possible to control the order of effects beyond simply the order they are added.
- We will implement an internal ClutterOpacityEffect
- This will look at a source actor and determine if it has children and if so it will redirect the rendering of the actor offscreen, before applying a transparency to the final composition of the offscreen framebuffer.
- For leaf nodes we need something like the set_opacity_parent mechanism we have internally in clutter-actor.c for explicitly setting the opacity value of the actor that should be referenced in its paint function for handling opacity.
- Since it's still possible that a leaf node could require redirection in some cases there will also be an override flag that allows an actor to explicitly request redirection by the opacity effect.
- We will add a mechanism for effects to queue a re-run of the effect which is distinct from queuing a redraw of the actor. This is so that changes to the opacity factor don't require us to redraw the contents of the offscreen framebuffer, instead it just requires us to repeat the final composition with a different factor.
- Noteably this mechanism should consider when there are effects being chained together so that queuing a re-run of one effect should cascade outwards triggering re-runs of other effects that composite after that effect. While preparing for the next frame the "effect re-run reference" should only ever move inwards towards the actors paint function.
- We will add a "transparency" property ranging from 1-0 (1 being opaque) that will internally manage the creation and addition of a ClutterOpacityEffect to the actor with priority that means the effect will composited last.
- After evaluating the performance, and considering if we want to bump Clutter's GL requirements to include framebuffer_objects then we may do away with the transparency property and instead change the semantics of the "opacity" property.