Sometimes a subclass of ClutterOffscreenEffect wants to paint with a
completely custom material. In that case it is awkward to modify the
material returned owned by ClutterOffscreenEffect so it makes more
sense to just get the texture and manage its own material.
Reviewed-by: Emmanuele Bassi <ebassi@linux.intel.com>
The OffscreenEffect class needs to expose a way for sub-classes to
track the size of FBO it creates, in case it has to do some geometry
deformations like the DeformEffect sub-classes.
Let's move the private symbol we used internally in 1.6 to fix
DeformEffect to the list of public symbols of OffscreenEffect.
http://bugzilla.clutter-project.org/show_bug.cgi?id=2570
*** This is an API change ***
The create_target() virtual function should return a CoglHandle to a
texture; clutter_offscreen_effect_get_target(), instead, returns a
CoglMaterial to be painted in the implementation of the paint_target()
virtual function.
Instead of equating textures with materials, and confusing the user of
the API, we should mark the difference more prominently.
First of all, we should return a CoglMaterial* (now that we have that
as a public type) in get_target(); having handles all over the place
does not make it easier to distinguish the semantics of the virtual
functions.
Then we should rename create_target() to create_texture(), to make it
clear that what should be returned is a texture that is used as the
backing for the offscreen framebuffer.
The OffscreenEffect should set up the off screen draw buffer so that it
has the same projection and modelview as if it where on screen; we
achieve that by setting up the viewport to be the same size of the stage
but with an initial offset given by the left-most vertex of the actor.
When we paint the texture attached to the FBO we then set up the
modelview matrix of the on screen draw buffer so that it's the same as
the stage one: this way, the texture will be painted in screen
coordinates and it will occupy the same area as the actor would have
had.
The OffscreenEffect class is meant to be used to implement Effect
sub-classes that create an offscreen framebuffer and redirect the
actor's paint sequence there. The OffscreenEffect is useful for
effects using fragment shaders.
Any shader-based effect being applied to an actor through an offscreen
buffer should be used before painting the resulting target material and
not for every actor. This means that doing:
pre_paint: cogl_program_use(program)
set up offscreen buffer
paint: [ actors ] → offscreen buffer → target material
post_paint: paint target material
cogl_program_use(null)
Is not correct. Unfortunately, we cannot really do:
post_paint: cogl_program_use(program)
paint target material
cogl_program_use(null)
Because the OffscreenEffect::post_paint() implementation also pops the
offscreen buffer and re-instates the previous framebuffer:
post_paint: cogl_program_use(program)
change frame buffer ← ouch!
paint target material
cogl_program_use(null)
One way to fix it is to allow using the shader right before painting
the target material - which means adding a new virtual inside the
OffscreenEffect class vtable in additions to the ones defined by the
parent Effect class.
The newly-added paint_target() virtual allows the correct sequence of
actions by adding an entry point for sub-classes to wrap the "paint
target material" operation with custom code, in order to implement the
case above correctly as:
post_paint: change frame buffer
cogl_program_use(program)
paint target material
cogl_program_use(null)
The added upside is that sub-classes of OffscreenEffect involving
shaders really just need to override the prepare() and paint_target()
virtuals, since the pre_paint() and post_paint() do all that's needed.