L3DT development blog
Large 3D terrain generator

Wringing more speed out of the plugin API

Hi Everyone,

[This post is aimed at plugin developers. It probably has no relevance to other audiences.]

Ever since the Zeolite plugin API was introduced in 2006, there has been a slight problem. It's slow. Well…not exactly slow, but slower. The same calculation written in a plugin runs more slowly than the same calculation written into the L3DT executable. This is because the plugin API - which plugins must use to access map data - performs some additional translation steps and safety checks that aren't required for code in the executable.

But no more…

In the latest update to the Zeolite plugin API (v2.9.6), I have included a new direct interface for maps and buffers that can potentially increase the speed of calculations in plugins by as much as 5x. The direct interface, which is implemented in the CzMap, CzMapTile (new!), and CzBuffer classes allows plugin-side code to directly access map and buffer memory without the overhead of calling plugin API functions.

The upshot is that, for suitable well-written algorithms, calculations performed by plugins can be as fast (or faster) than the same code running in the L3DT executable.

What it doesn't do

However, there is a downside to this interface; in fact there are several. Firstly, the direct interface does not do many useful housekeeping actions that are automagically performed by the conventional API functions, including:

  1. Updating mipmaps,
  2. Updating minimum / maximum altitudes in heightfields, and;
  3. Marking mosiac tiles as changed after setting pixels (thereby forcing the tile to be saved when released).

Consequently, if plugin developers are changing pixel values using the direct interface they must:

  1. Clear or update mipmaps,
  2. Update minimum / maximum altitudes for heightfields, water maps and design maps, and;
  3. Mark tiles as changed before releasing mosaic tile handles.

Note that these limitations do not affect reading pixel values; these interfaces are generally safe for reading, but more tricky for writing. Of course, you can use the direct interface to read quickly from one map, and the conventional API functions to write safely to another map, all at the same time.

Safety checking

The speed improvement of this interface is in part due to the fact that it skips all safety checks. Thus, it is extraordinarily easy to crash the application when improperly using this interface.

For example, range checking is omitted when getting or setting pixel values. The conventional API functions check the x and y coordinates given to a SetPixel / GetPixel call to ensure they're valid, and throw error messages if they're not. However, in the direct access interface there is no range checking, so invalid x or y coordinated will overrun the data buffer, dance a fandango on core, and crash the application. The risk is nil, of course, if the algorithm is well written and doesn't pass invalid coordinates in the first place. Thus, the direct access interface is only recommended for optimising rock-solid code that has already been thouroughly tested.


To use the direct interface, plugin developers must re-compile thier plugins and insert calls to the OpenDirectInterface member functions of the relevant CzMap, CzMapTile and/or CzBuffer objects. After calling OpenDirectInterface, the interface lasts only for the lifetime of the CzMap, CzMapTile or CzBuffer object, or until you call the CloseDirectInterface member function. Thus your calculation may use the direct interface when safe to do so, but not use it on other calculations where it might not be safe.

Roll-out plans

This interface will initially be rolled-out to select calculation plugins, as these are easiest to modify and will benefit most from such optimisations.

The first plugin to be updated was the atConvertWaterToHeight plugin. The calculation in this plugin gets input water map pixels and sets output heightmap pixels, with very little calculation in between, and thus is a good test of the pixel access speed. For this plugin, the effect of using the direct access interface was a speed-up of in-RAM maps of about 5x, and for mosaic maps of about 2x. The lesser improvement for mosaic map calculations is due the fact that mosaic calculations must perform disk paging, which is not affected by these optimisations.

The atCalc_AM_Image plugin has also been updated. This plugin is principally used for updating the terrain colour when painting with the attributes map brush in Sapphire. In this case the speed improvement was 30-40%. This improvement is less dramatic than with the atConvertWaterToHeight plugin because this calculation is less bound by simple pixel read/write speeds, and also because the optimisation applied to reading the the input values but not writing the output values. Furthert optimisation is possible here.

More complex plugins like the Sapphire 3D renderer may eventually be optimised thusly, but that may be some time away.

Implementation tips

These changes are available Zeolite v2.9.6, which is available here. Please note that this API version is compatible with L3DT v2.9 build 12 or later.


If you would like to provide feedback on these changes, or any other features of the new plugin API, please feel free to reply to this forum thread.

Over and out, Aaron.

l3dt/2011/mar/10.txt · Last modified: 2013/08/21 17:25 (external edit)
Except where otherwise noted, content on this wiki is licensed under the following license:CC Attribution-Share Alike 3.0 Unported
L3DT Development Blog RSS Donate Powered by PHP Valid XHTML 1.0 Valid CSS Driven by DokuWiki