Controlling Memory Usage

The HOOPS Memory Manager

The dynamic allocation and releasing of memory in HOOPS/3DGS is controlled by an internal memory manager. The memory manager may allocate memory in numerous situations, such as when objects are inserted into the HOOPS database, an Update_Display is performed or files are loaded into the HOOPS database. As items are removed from the HOOPS database, the memory manager takes care of the freeing of the memory associated with them. HOOPS allocates memory and chunks and attempts to retain/reuse them after items are deleted.

HOOPS does not monitor the available system memory or make any attempts to limit its memory usage. For example, it will never refuse to honor an API call as a result of it requiring a lot of memory. HOOPS simply allocates (or will attempt to allocate) memory that it requires, based on the HOOPS/3DGS calls made by the developer. Any tracking and management of HOOPS memory usage must be handled in your application logic, and is facilitated by the function Show_Memory_Usage. This function returns the amount of memory that HOOPS has allocated, and how much memory is currently in-use.

Relinquishing Allocated Memory

If you find that there is a significant gap between ‘allocated’ and ‘in-use’, and you wish to reclaim some of the memory that HOOPS has allocated but is not currently using, you can consider calling Relinquish_Memory. This forces HOOPS to release unused memory back to the system, thereby defeating the benefits of HOOPS memory management system. This function should only be used in very specific situations, and also has major downsides on 32-bit systems. See its Reference Manual entry for more detailed notes.

Raw Memory and Memory Debugging

If your HOOPS-based application is experiencing memory problems and are trying to determine where they are occurring, you can perform accurate HOOPS memory debugging by enabling the system option “raw memory” via the Define_System_Options function. When this setting is turned on, HOOPS no longer uses its internal memory manager for dynamic memory allocation, but instead calls directly to the operating system for the allocation and freeing of memory. The direct calls to malloc and free allows a developer to easily track memory leaks using Valgrind, Visual Studio or any other memory debugging tool. Note that turning “raw memory” on can significantly affect HOOPS/3DGS performance, and therefore this option should NOT be set for shipping versions of HOOPS-based application. Thus, by default, “raw memory” is turned off.

Handling Out-Of-Memory Conditions

The dynamic allocation and releasing of memory in Visualize is controlled by an internal memory manager. The memory manager may allocate memory in numerous situations, such as when objects are inserted into the database, an update is performed, or files are loaded. As items are removed from the database, the memory manager takes care of the freeing of the memory associated with them. Visualize allocates memory using its own memory pool and attempts to reuse memory chunks after entities are deleted.

Tracking Visualize memory usage should be handled in your application logic, and this is facilitated by calling Show_Memory_Usage, which lets you query the amount of memory that Visualize currently has allocated, along with how much is currently being used.

Soft Memory Limit

Visualize gives you the option to specify a “soft memory limit”, specified in megabytes:

    // set to one gigabyte
    HC_Define_System_Options("soft memory limit=1024");

When Visualize allocates more memory than this value, it will issue an information event of the following form:

  • category: HEC_MEMORY

  • specific: HES_OUT_OF_MEMORY

  • severity: 0 - “informational”

  • message: “Soft memory limit breached.”

Aside from generating the information event, Visualize will not respond to this situation. It is the responsibility of your application to catch this event using a previously defined event handler, take steps to recover memory, and limit new memory allocation. For example, you could choose to show the user a warning, close open files, or disallow opening of new files. Event handlers are defined using Define_Error_Handler.

Hard Memory Limit

When HOOPS Visualize attempts to allocate system memory and fails, it has run into the “hard memory limit”. This means all available physical memory has been exhausted. This is a serious situation which threatens application stability. Upon reaching the hard memory limit, Visualize will perform the following steps:

  • Report a warning event. Your application should have an error handler previously defined with Define_Error_Handler

    • category: HEC_MEMORY

    • specific: HES_OUT_OF_MEMORY

    • severity: 1 - “warning”

    One possible continuation you could implement would be to free a previously reserved memory buffer, and then attempt a recovery.

  • Visualize will automatically call Relinquish_Memory to try and free up any memory that is allocated but unused (as reported by Show_Memory_Usage)

  • Make a second attempt to allocate the desired memory

If the second attempt at memory allocation succeeds, Visualize continues and the application behaves normally. If the second attempt at memory allocation fails, it is a FATAL error.

  • category: HEC_MEMORY

  • specific: HES_OUT_OF_MEMORY

  • severity: 3 - fatal error

An application CANNOT successfully return from a fatal error - the application will will behave unpredictably if you try. The most likely situation is either a crash or a hang. The best option at this point is for the application to exit as gracefully as possible (and perhaps display a dialog box showing what happened). This requires the application to either longjmp (akin to a throw/exception in C++). This is true of any fatal errors in HOOPS Visualize, of which there are few. This HES_OUT_OF_MEMORY fatal error is the most common fatal error that could occur during typical usage.

Memory Manager Overloading

If you wish to use your own memory manager, HOOPS provides functionality for more direct control of the internal memory usage via the “malloc” and “free” options in Define_System_Options. To use “malloc” and “free”, you must make Define_System_Options your first call to HOOPS and set these options at that time. The “malloc” option accepts a function pointer which will be used by HOOPS to request memory for new objects. The “free” option accepts a function pointer that HOOPS will use to release memory. “Malloc” and “free” must be set in conjunction with one another. Note that these options are persistent through a call to Reset_System.

If “malloc” and “free” options are set as well as “raw memory”, the “malloc” and “free” options will supersede the “raw memory”. Note that even when these memory options are used, HOOPS still uses the internal memory manager to control the allocation of small internal data structures.

Geometry Memory Optimizations

HOOPS/3DGS automatically performs a considerable amount of memory optimization internally for the storage and rendering of geometry. However, there a few special options and notes related to optimizing memory usage for faces.

Shell Facelists

HOOPS/3DGS retains the face list passed into Insert_Shell by default. The number of bytes allocated for each shell face list can be quite high. However, if the shell is never edited, local shell attributes are not edited after the first update, and the original face list does not need to be accessed via Show_Shell, then the face list need not be retained. Setting the “conserve memory” option to ‘face list’ in a call to Define_System_Options instructs HOOPS/3DGS to discard the face list after the shell has been drawn for the first time.

    HC_Define_System_Options("conserve memory = face list");

If the face list has been discarded, then Show_Shell will return a face list based on the internally-calculated tri-strips, which means that each face will consist of a single triangle.

Software Culling Optimizations

HOOPS/3DGS tries to perform backplane culling using a technique called ‘screen-plane facings’ when display lists are turned off. This technique can improve rendering performance by up to 35%, but at the cost of additional memory usage for shell primitives. The number of bytes allocated for each shell’s ‘facings’ can be up to twenty times the number of faces. For example, HOOPS/3DGS will use an additional 20 MB of memory to perform the SSE-based software culling for a shell with one million faces. This technique is only used if Intel x86 SSE extensions are available. If the extensions are not available, then culling will be performed as usual; specifically, it will be performed by the underlying 3D hardware if a 3D driver is being used, or via a regular software path if a 2D driver or software z-buffer is being used.

This functionality can be disabled by calling Define_System_Options and setting the “conserve memory” option to ‘facings’:

    HC_Define_System_Options("conserve memory = facings");