The TLcyAsynchronousPaintAddOn offers support for painting layers in a thread that differs from the event dispatch thread. This paint thread draws layers in a special temporary buffer. The image produced in the buffer is then painted onto the map using the normal drawing routines.

Because a layer is not necessarily thread-safe, some precautions are needed to ensure that the layer is not simultaneously accessed in different threads. The asynchronous layer add-on handles most of this work by offering an API for creating and accessing asynchronously painted layers. The API is encapsulated in a single facade that implements the abstract class ALcyAsynchronousPaintFacade.

Enabling asynchronous painting

To enable asynchronous painting for an existing layer, the createGXYAsynchronousLayer method must be called with the existing layer as argument. If the resulting ILcdGXYLayer is inserted into a map component, the original layer will be automatically painted asynchronously. Take care not to insert the original layer into any map component. If you do, the layer will be accessed simultaneously, leading to visual and data corruption. To protect the state of the layer, it is also recommended to discard the reference to the original layer after calling createGXYAsynchronousLayer.

Program: Enabling asynchronous painting shows an example of a layer factory wrapper method. It delegates the actual layer creation to another factory and enables asynchronous painting of the layer.

Program: Enabling asynchronous painting
public ILcdGXYLayer createGXYLayer(ILcdModel aModel) {
  ILcdGXYLayer layer = fLayerFactory.createGXYLayer(aModel);
  if (layer != null) {
    return fLucyEnv.getMapManager().getAsynchronousPaintFacade().createGXYAsynchronousLayer(layer);
  }
  return layer;
}

The asynchronous behavior of a layer is controlled using information encapsulated into TLcyAsynchronousPaintHint instances. A Lucy paint hint is a convenience extension of a TLcdGXYAsynchronousPaintHint with the following extra properties:

merge:

If false, the layer will get its own paint buffer. This can be useful for very slow or very fast layers.

mergeGroup:

If possible, layers from the same merge group are made to share the same paint queue. Most background formats in Lucy use a default merge group, to save on memory and CPU requirements.

To provide paint hints for your own layers, you can register an ILcyAsynchronousPaintHintProvider as a service. See the Lucy Services Mechanism documentation and TLcyGXYAsynchronousLayerPaintHintProvider.

If a layer tree node is made to paint asynchronously, only the node itself will be painted asynchronously, not its children. This way, you can decide for each individual child layer if you want it painted asynchronously or not. Similarly, the recommended way to make layer lists paint asynchronously is to make their individual leaf layers paint asynchronously. This way the layer list is kept synchronous and can be accessed at all times. An alternative is to make the layer list itself paint asynchronously, but by doing this the layer list will be considered as a single layer: Lucy will not recognize it as a layer list anymore. Do not make a layer list asynchronous when one of its child layers is already asynchronous: asynchronously painted layers cannot paint asynchronously painted layers.

Accessing asynchronously painted layers

To avoid concurrency problems, asynchronously painted layers must be accessed and modified with care. An asynchronously painted layer can be accessed in five ways: using protected access and using synchronized, instant and two types of deferred invocations.

Protected access

uses the layer reference that was obtained from the createGXYAsynchronousLayer method of the asynchronous paint facade. It is this reference that you will receive from Lucy when it passes a layer, for example when you are querying a map or when asked to encode a layer. The reference is protected in that it exposes an ILcdGXYLayer interface that is safe to use.

The reference also supports and implements ILcdGXYEditableLabelsLayer, if necessary.

This means that painters and editors obtained through the interface will not interfere with asynchronous painting and neither will calling getBounds, applyOnInteract, or any property-related methods.

+ However, because the layer’s model is not protected, a model write lock must be acquired before changing the model. Note that LuciadLightspeed’s controllers take write locks when editing models, and are thus safe to use in combination with asynchronously painted layers. See Threads and locks for more information about read-write locks in LuciadLightspeed.

+ The deferred invocation:: is encapsulated into the run method of an ILcdGXYAsynchronousLayerRunnable. This method is executed in the paint thread at a time when the original layer is safe to access. The invocation is scheduled using the invokeLaterOnGXYLayer method in the ALcyAsynchronousPaintFacade. If your runnable needs to execute code in the Event Dispatch Thread, use the invokeLaterOnGXYLayerInEDT method. Never combine invokeLaterOnGXYLayer with a runnable containing EventQueue’s `invokeAndWait calls, as this is prone to deadlocks.

+ Program: Accessing an asynchronously painted layer using a deferred invocation shows an example of a deferred invocation calling a TLcdLayer's setModel method.

+ Synchronized invocations:: work much like the deferred ones, but they are executed in the caller’s thread. This type of invocation is scheduled by the invokeAndWaitOnGXYLayer method. Calling this method causes the caller thread to block until the asynchronous painting stops and subsequently executes the invocation. Typically, you would use the synchronized access if you need to retrieve specific layer properties that are not exposed by using the standard ILcdGXYLayer interface. As an example, Program: Accessing an asynchronously painted layer using a synchronous invocation shows a synchronous invocation that sets the scale range of a TLcdGXYLayer. The synchronous invocation ensures the scale range is set before returning the layer.

+ Instant invocations:: form the only type of access that may interfere with the asynchronous painting. An instant invocation is scheduled by the invokeNowOnGXYLayer method. The invocation is executed almost immediately, but has to deal with a layer that is possible being read and/or changed by a paint method in another thread. Typically, you would use the instant access if you need to determine if a layer implements an interface or if you want to access a simple boolean property: actions that should not be affected by asynchronous painting.

+ Program: Accessing an asynchronously painted layer using an instant invocation shows an instant invocation that determines the layer’s type.

Program: Accessing an asynchronously painted layer using a deferred invocation
//Create the runnable which will update the inner layer
ILcdGXYAsynchronousLayerRunnable asyncLayerRunnable = aSafeGXYLayer -> ((TLcdLayer) aSafeGXYLayer).setModel(model);

//Invoke the runnable on the inner layer of the async layer by using the facade
ALcyAsynchronousPaintFacade asynchronousPaintFacade = fLucyEnv.getMapManager().getAsynchronousPaintFacade();
asynchronousPaintFacade.invokeLaterOnGXYLayer(
    layer, asyncLayerRunnable);
Program: Accessing an asynchronously painted layer using a synchronous invocation
    ILcdGXYLayer layer = fLayerFactory.createGXYLayer(aModel);

    //Create the runnable which will update the inner layer
    ILcdGXYAsynchronousLayerRunnable asyncLayerRunnable = aSafeGXYLayer -> {
      if (aSafeGXYLayer instanceof TLcdGXYLayer) {
        ((TLcdGXYLayer) aSafeGXYLayer).setScaleRange(myScaleRange);
      }
    };

    //Invoke the runnable on the inner layer of the async layer by using the facade
    ALcyAsynchronousPaintFacade asynchronousPaintFacade = fLucyEnv.getMapManager().getAsynchronousPaintFacade();
    asynchronousPaintFacade.invokeAndWaitOnGXYLayer(layer,
                                                    asyncLayerRunnable);
    return layer;
  }

  private static class MyEditableLayer extends TLcdGXYLayer {

  }
}
Program: Accessing an asynchronously painted layer using an instant invocation
//Create the runnable which will check the inner layer
int[] result = {0};
ILcdGXYAsynchronousLayerRunnable asyncLayerRunnable = aSafeGXYLayer -> {
  result[0] = aSafeGXYLayer instanceof MyEditableLayer ?
              ILcyGXYLayerType.EDITABLE :
              ILcyGXYLayerType.UNKNOWN;
};

//Invoke the runnable on the inner layer of the async layer by using the facade
ALcyAsynchronousPaintFacade asynchronousPaintFacade = fLucyEnv.getMapManager().getAsynchronousPaintFacade();
asynchronousPaintFacade.invokeNowOnGXYLayer(aGXYLayer,
                                            asyncLayerRunnable);

The recommended way to access layer lists with asynchronously painted leaf layers is by using the above-mentioned access mechanisms for each leaf layer. TLcdGXYLayerList, for example, just relies on the protected access of all asynchronous layers. Note that using invocation methods with a layer list does not automatically enable you to access non-ILcdGXYLayer specific layer properties of the leaf layers.

Customizing paint queue management

Lucy’s map components come with a paint queue manager to automatically create and manage paint buffers for you. The manager is created using the factory method in the ALcyAsynchronousPaintFacade. If, for some reason, the paint hint providers do not offer sufficient customization of paint queue management, you can override the setupGXYAsynchronousPaintQueueManager method and create your own paint queue manager. See Asynchronous painting in a GXY view for more information on writing paint queue managers.

Utility wrappers

A set of utility wrappers are available in the package com.luciad.lucy.map.asynchronous for common layer related classes. These classes ensure layer access on the original layer for the following tasks:

Disabling asynchronous behavior

Asynchronous painting is disabled by simply removing the add-on from the Lucy add-on configuration file. Lucy will then default to a synchronous version of the ALcyAsynchronousPaintFacade: you need not modify your code.

Note that by default, most add-ons of Lucy will be configured for asynchronous painting. For instance, the controllers created by the map add-on continuously repaint the view when they are used. If you disable the asynchronous behavior, this continuous repainting can be detrimental to the user experience. The 'Asynchronous behavior considerations' sections in the documentation of the various add-ons contain instructions on how to adapt the add-on when the asynchronous behavior is disabled.

Further information

To learn more about the details of the asynchronous painting mechanism in LuciadLightspeed, see Asynchronous painting in a GXY view. For more Lucy-specific configuration options, we refer to the file config/lucy/asynchronouspaint/asynchronouspaint_addon.cfg.