Dev:2.5/Source/Architecture/Notifiers/Data

提供: wiki
移動先: 案内検索

Overview

Important note: notifiers are for the UI to work, not for Blender's animation system to work. A notifier gets generated by tools or events, and are only signals that get handled optionally. Some notifications might be needed to trigger stuff on data level (like update frame, but that's more of visualization, not data edit).

The whole dependency graph should be strictly separated from the notifiers for that reason. Optionally, depsgraph can send out the proper notifications of course. An exporting script that relies on fine-grained data export should not use notifiers, but register itself in depsgraph... for that we better make a new depsgraph design.

Implementation

Adding a note:

   WM_event_add_notifier(bContext *C, unsigned int flags, void *reference);

The flags indicate data changes on four levels;

  • 1) Category, which is Window, Screen, Scene, Object, Material, etc
  • 2) Data, like Markers, Frame, Transformation, Shading
  • 3) Data sub types, not filled in now but you might want to pass on more detail
  • 4) Action, only four: Edited, Evaluated, Added, Removed

Here is a notifier for a frame change:

  WM_event_add_notifier(C, NC_SCENE|ND_FRAME, scene);

(You can skip SubTypes or the Action, when not relevant.)

As prefixes for the above categories I've picked NC, ND, NS, NA respectively.

  • Reference pointer

Let's agree on never reading from the reference pointer, only to compare. Notifiers can not guarantee data to be valid. We also should only pass on the pointer from the "Category", not from other (sub levels of) data. That way a Material category notifier can verify if this is the material itself.


Reading the notifier can go as follows:

    static void action_main_area_listener(ARegion *ar, wmNotifier *note)
    {    
        /* context changes */
        switch(note->category) {
                case NC_SCENE:
                switch(note->data) {
                        case ND_FRAME:
                        case ND_MARKERS:
                                ED_region_tag_redraw(ar);
                                break;
                        }
                break;
        }
    }

Currently the NC_WINDOW category has no data, actions or subtypes. Use this notifier to generate a full-window redraw:

  WM_event_add_notifier(C, NC_WINDOW, NULL);

Which Type to Send?

Here are some rough guidelines:

  • Datablocks: when you're editing a datablock, there will typically be a corresponding category (if there isn't one, it may be time to add it). From there you should be able to find the right ND_* value within that datablock.
  • Space/Region Data: send an NC_SPACE notifier. This can have an ND_* value, or work with a note->reference check for the space type in the listener. Only use the NC_SPACE category when changing the space or region data, it should not become a replacement for allqueue.
  • User Preferences: for user preferences an NC_WINDOW notifier can be sent, to redraw the whole screen.

Data subtypes and action classifications are not used much currently. TODO guidelines for these.

Original Design Doc

Motivation

Blender 2.4 has calls like allqueue(REDRAW..), reshadeall_displist(), BIF_preview_changed() to ensure windows redraw and caches are cleared on changes. However this is not a system that is extensible. When adding a spacetype, tools have to be updated for this new spacetype, because there is no system for it to get notified on changes. Similarly python scripts cannot track data changes well, and the ones that do try this have to resort to polling.

As a solution, we can to turn around this system and instead of tools queuing redraws, they can say which data they changed to notify areas, regions, preview renders, and anything interested in tracking data changes, and these can then decide themselves if they want to update.

Types of Changes

There are three types of changes we can distinguish:

  • Context changes: areas, regions, .. get different data in their context.
  • Data edits: data is added, removed or changed by a tool.
  • Data evaluation: data was (re-)evaluated due to frame changes or the dependency graph.

Context changes are the easiest to handle. When a screen, area or region changes, it can require everything affected by that context to be updated. So a screen requires all its areas to be redrawn for example. This is relatively coarse but should not lead to many unnecessary redraws.

Next, data edits and evaluations are more complicated to get good updates for, different windows display different kinds of data and it is desirable to avoid unnecessary redraws here. This could be done very fine grained tracking exactly which data changes and which properties of that data, but this is not very practical. More details on how to handle that in the next section.

Further there is the distinction between data edits and evaluation. For redraws there isn't really an important distinction here. However for things like exporters that are doing exports on the fly to a game engine for example, this distinction is useful since evaluations don't always require re-export (you don't want to re-export everything on frame changes for example). Edits always imply evaluation, but not the other way around.

Data Changes

Deciding how coarse or fine we need to notify of and listen to data changes is not trivial. Finer changes are interesting but may require too much code, and may even cause too much overhead. I propose that we report changes for specific datablock instances, and code that needs to track it this precise can plug in here, but for redraws we can just track changes per datablock type.

We can categorize changes as a (non-overlapping) hierarchy, with a very coarse datablock changed at the top, per type datablock changes in the middle, and changes per group of datablock type properties at the bottom. Reporting a change at any of these three levels also implies a change to be reported for its children and parents in the hierarchy. For example a generic "datablock changed" implies all datablocks types and their properties changes. A change to a material property group implies a material change and a generic datablock change.