Archicad C++ API
About Archicad add-on development using the C++ API.

ACAPI_Element_AttachObserver performance issues

Anonymous
Not applicable
Hi,

I have the following code that iterates over all elements we are are interested in and attaches an observer to the element if it isn't in a frozen layer:
   
    // it->first is the UUID string of the element
    std::map<std::string, CrsElement*>::iterator it = MapOfElements.begin();
    for (; it != MapOfElements.end(); ++it) {
        API_Elem_Head element_head;
        BNZeroMemory(&element_head, sizeof(API_Elem_Head));
        element_head.guid = APIGuidFromString(it->first.c_str());
        err = ACAPI_Element_GetHeader(&element_head);

        API_Attribute    layer;
        BNZeroMemory(&layer, sizeof(API_Attribute));
        layer.header.typeID = API_LayerID;
        layer.header.index = element_head.layer;
        ACAPI_Attribute_Get(&layer);
        if (!(layer.header.flags & APILay_Locked)) {
            ACAPI_Element_AttachObserver(&element_head, 0);
        }
    }
The problem is that it appears to take about 0.02 seconds to call ACAPI_Element_AttachObserver for each object. This becomes very problematic when we have 16,000 elements to observe (which is not uncommon) since it takes over 5 minutes to attach and Archicad is locked up during the process. I've verified that the time spent is inside ACAPI_Element_AttachObserver since if I lock every layer the process completes very quickly.

I've tried moving the ACAPI_Notify_InstallElementObserver call both before and after the loop but this doesn't change performance. I'm testing mainly in Archicad 21 but believe the issue exists from at least Archicad 19 onwards.

Is there any reason why this is so slow? Can something be done internally or by me to improve performance?

regards

Brendan Hack
4 REPLIES 4
Ralph Wessel
Mentor
I can't comment on the speed of this particular function because we usually use it on discrete element sets, e.g. in response to some user operation on them, rather than across the entire project.

Before you go too far down that track, are you sure you need to do this? What do you (roughly) intend to do when notified of a database event on an element?

I'm asking because it's a potential performance bottleneck. If the user edits element in large quantities, e.g. moving several thousand objects, any processing you do can easily cause a disruptive lag. I'm very cautious about attaching observers and aim to take another approach if possible.
Ralph Wessel BArch
Anonymous
Not applicable
The application here is an interactive renderer that updates in real time whenever scene elements change. So I have to listen to every element being rendered since any change may affect the rendered scene. We already have the layer freeze check to cut down on what we need to listen to. One of the main applications is as a design and feedback tool so being able to interactively change things is essential.
Ralph Wessel
Mentor
Keep a few things in mind with that approach:
1. Not all 3D changes are caused by directly editing elements – attributes and model view options, for example, can significantly modify a scene with no element editing. Even a simply view change through the navigator is enough.

2. Editing one element can indirectly change the geometry of other elements, e.g. via solid element operations. You won't be notified that the 3D geometry of the other elements has changed because it isn't a database change, i.e. 3D geometry isn't stored in the database.

3. Building and extracting 3D geometry in response to every edit could be seriously disruptive, i.e. the user sitting watching a spinning cursor after every edit. Note what sometimes happens when elements are edited live in a section view. ARCHICAD normally spares the user this pain when editing in the plan view by not immediately generating the resulting 3D geometry.

I'd suggest triggering a scene update by a keypress or some other action.
Ralph Wessel BArch
drjustice
Newcomer
I am encountering the same issue for the same use case as the Original Poster: I want to implement a live connection to an external viewer, and I need to know when things have changed on an element so that I can push that change to the external viewer.

I understand the following:

- The only way to get changes on an object is via an observer.
- Observers can't be installed on all objects for the reasons outlined above.
- Although there is a global notification for new elements, there is no global notification for changed/deleted elements (unless they have an observer)
- Even if we installed an observer on all elements, or if there was a global notification for all new/change/deletes, there is no guarantee we would get notified by non-user interaction events (eg: another plugin changes an element)

As a result of the above, Graphisoft support in this thread recommended that incremental updates to the external viewer should be pushed upon user action (eg: hotkey) -- by traversing the whole model, detecting changes for each object, and only re-push elements that have changed)

That is certainly one way to do it... but sigh.

But another way to do it would be to do this traversal "in the background". I don't mean doing it in a separate thread, I mean doing it "on-idle". The idea would be to traverse only a handful of elements per "on-idle" call -- a bit like cooperative multitasking, where after a few milliseconds, we yield back control to the main application.

However, I did not see anything in the API that would allow a plugin to work this way. I haven't seen a notification function that would be called when the app is idle. As entry points to the plugin, I see notification handlers for menu items, and the Notification Manager, I see no hook for being called by any other means.


Suggestions?

Didn't find the answer?

Check other topics in this Forum

Back to Forum

Read the latest accepted solutions!

Accepted Solutions

Start a new conversation!