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.
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 anILcdGXYLayer
interface that is safe to use.
The reference also supports and implements |
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.
//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);
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 {
}
}
//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:
-
TLcyGXYAsynchronousLayerCustomizerPanelFactory
ensures synchronized access when setting a layer object and applying changes on it. Testing if an object is supported is done using an instant invocation; -
TLcyGXYAsynchronousLayerEncoder
automatically turns encoded layers into asynchronously painted layers; -
TLcyGXYAsynchronousLayerDecoder
ensures synchronized access when decoding a layer; -
TLcyGXYAsynchronousLayerFactory
automatically turns encoded layers into asynchronously painted layers; -
TLcyGXYAsynchronousLayerTypeProvider
uses instant access when determining the layer type; -
TLcyGXYAsynchronousLayerPaintHintProvider
uses instant access when determining the layer paint hint; -
TLcyGXYAsynchronousLayerWorkspaceCodec
ensures synchronized access and transformation into asynchronously painted layers.
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
.