AB4D Forum
Performance with multiple views - Printable Version

+- AB4D Forum (https://forum.ab4d.com)
+-- Forum: Products Forums (https://forum.ab4d.com/forumdisplay.php?fid=4)
+--- Forum: Ab4d.SharpEngine (https://forum.ab4d.com/forumdisplay.php?fid=12)
+--- Thread: Performance with multiple views (/showthread.php?tid=4503)



Performance with multiple views - matt@reefmaster.com.au - 01-13-2026

Hi,

I am writing an Avalonia MDI application where it is possible to have many windows created at one time, each of which uses it's own SharpEngine view control.
I notice that after say 5 windows are opened, performance in subsequent windows degrades sharply. This is not too surprising given the amount of resources that needs to be allocated to each view

So far: I am sharing a single VulkanDevice across all renderers and have already set supersampling to 1 - the latter certainly improves performance over multiple screens but at some quality cost of course.

Given that most of these views won't actually be *visible* at the same time, I am looking forguidance on what the best strategy is for trying to reduce resource use in my scenario. I do need to be able to let the user open multiple views, but of course the total screen area (even if all are visible) will never exceed the screen dimensions. Is there a recommended way to release the heaviest GPU assets whilst views are not visible that will allow quick resumption when the user reverts to the view?

thanks!


RE: Performance with multiple views - abenedik - 01-14-2026

When you are optimizing the memory usage of the engine, it is best to use the DumpFullMemoryUsage on the Scene object (or DumpMemoryReport method on the VulkanDevice object). This can be done by stopping at a breakpoint and then executing the following in the Immediate window (replace the scene with the name of your Scene instance):
Code:
scene.DumpFullMemoryUsage()
// or gpuDevice.DumpMemoryReport()

If you are using the Diagnostics Window (as in the samples project), then you can also just select the "Dump Memory" command from the menu (it is recommended to add the Diagnostics Window to your app in the debug build).

This will give you the details about all the GPU allocations. For example, when opening the "Advanced HeightMap" sample, the following is the memory dump:
Code:
Memory budget:
Heap[0]: Budget:  11.05 GB - Usage: 301.58 MB = Available:  10.76 GB  MemoryPropertyFlags: DeviceLocal, HostVisible, HostCoherent
Heap[1]: Budget:  35.56 GB - Usage:  25.77 MB = Available:  35.54 GB  MemoryPropertyFlags: HostVisible, HostCoherent, HostCached

Memory types:
[0]: heap index: 1; properties: None
[1]: heap index: 0; properties: DeviceLocal
[2]: heap index: 0; properties: DeviceLocal
[3]: heap index: 1; properties: HostVisible, HostCoherent
[4]: heap index: 1; properties: HostVisible, HostCoherent, HostCached
[5]: heap index: 0; properties: DeviceLocal, HostVisible, HostCoherent


VulkanMemoryAllocator report:
Allocated 196.53 MB; Allocations count: 10 / (unlimited);  PeakAllocatedMemory: 211.53 MB

Dedicated allocations (Count: 4; Total size: 164.53 MB):
[0] 'StandardStagingBuffer_1MB'                                  1 MB  MemType: 5 (heap: 0; flags: DeviceLocal, HostVisible, HostCoherent) Handle: 0x22B0BB7CBB0
[1] 'SharpEngineSceneView_SceneView_MSAA-DepthStencilImage'  72.75 MB  MemType: 1 (heap: 0; flags: DeviceLocal) ForImages Handle: 0x22B0C8D4D30
[2] 'SharpEngineSceneView_SceneView_MSAA-Image'              72.63 MB  MemType: 1 (heap: 0; flags: DeviceLocal) ForImages Handle: 0x22B0C8D5470
[3] 'SharpEngineSceneView_SceneView_SSAA-Image'              18.16 MB  MemType: 1 (heap: 0; flags: DeviceLocal) ForImages Handle: 0x22B0C8D5640


Shared memory blocks (Count: 6; Total size: 32 MB):
[0] 'Heap-memType5_0'   1 MB  ( 1% full)  MemType: 5 (heap: 0; flags: DeviceLocal, HostVisible, HostCoherent) Handle: 0x22B0BB7CF50
[1] 'Heap-memType1_1'  16 MB  (28% full)  MemType: 1 (heap: 0; flags: DeviceLocal) ForImages Handle: 0x22B0C8D45F0
[2] 'Heap-memType1_2'   1 MB  (80% full)  MemType: 1 (heap: 0; flags: DeviceLocal) Handle: 0x22B1652F9E0
[4] 'Heap-memType1_4'   2 MB  (99% full)  MemType: 1 (heap: 0; flags: DeviceLocal) Handle: 0x22B1652FD80
[6] 'Heap-memType1_6'   4 MB  (86% full)  MemType: 1 (heap: 0; flags: DeviceLocal) Handle: 0x22B165DA160
[8] 'Heap-memType1_8'   8 MB  (28% full)  MemType: 1 (heap: 0; flags: DeviceLocal) Handle: 0x22B165DA330


Shared block details:
[0] 'Heap-memType5_0'  1 MB  (1% full)  MemType: 5 (heap: 0; flags: DeviceLocal, HostVisible, HostCoherent) Handle: 0x22B0BB7CF50
  HeapAllocator 'Heap-memType5_0'; Alignment: 256;
  Used: 12.5 KB / 1 MB (1.2% full); MaxFreeBlockSize: 1011.5 KB;

  Blocks usage: 5 used + 1 free = 6;  (one char size: 2 KB):
  ******+.........................................................
  ................................................................
  ................................................................
  ................................................................
  ................................................................
  ................................................................
  ................................................................
  ................................................................
 
  All blocks (offset, size, name):
  0x00000000     4 KB 'MatricesMemoryBlocksBuffer-0_1'
  0x00001000   256  B 'SceneUniformBuffer_1'
  0x00001100   256  B 'AllLightsUniformBuffer-1_1'
  0x00001200     4 KB 'MaterialMemoryBlocksBuffer-0_1'
  0x00002200     4 KB 'LineInfoMemoryBlocksBuffer-0_1'
  0x00003200 1011.5 KB FREE


[1] 'Heap-memType1_1'  16 MB  (28% full)  MemType: 1 (heap: 0; flags: DeviceLocal) ForImages Handle: 0x22B0C8D45F0
  HeapAllocator 'Heap-memType1_1'; Alignment: 1024;
  Used: 4.55 MB / 16 MB (28.4% full); MaxFreeBlockSize: 11.45 MB;

  Blocks usage: 2 used + 1 free = 3;  (one char size: 32 KB):
  ****************************************************************
  ****************************************************************
  *****************+..............................................
  ................................................................
  ................................................................
  ................................................................
  ................................................................
  ................................................................
 
  All blocks (offset, size, name):
  0x00000000  4.54 MB 'SharpEngineSceneView_SceneView_SwapChainBackBuffer_01'
  0x0048A000     8 KB
  0x0048C000 11.45 MB FREE


[2] 'Heap-memType1_2'  1 MB  (80% full)  MemType: 1 (heap: 0; flags: DeviceLocal) Handle: 0x22B1652F9E0
  HeapAllocator 'Heap-memType1_2'; Alignment: 256;
  Used: 822 KB / 1 MB (80.3% full); MaxFreeBlockSize: 202 KB;

  Blocks usage: 5 used + 1 free = 6;  (one char size: 2 KB):
  ****************************************************************
  ****************************************************************
  ****************************************************************
  ****************************************************************
  ****************************************************************
  ****************************************************************
  ***************************.....................................
  ................................................................
 
  All blocks (offset, size, name):
  0x00000000   256  B 'VertexBuffer-ViewportQuad'
  0x00000100   256  B 'IndexBuffer-ViewportQuad'
  0x00000200 607.3 KB 'VertexBuffer-HeightMapWireframe-mesh'
  0x00097F00   512  B 'VertexBuffer-PositionsMesh3D'
  0x00098100 213.8 KB 'VertexBuffer-Minor contour lines-mesh'
  0x000CD800   202 KB FREE


[4] 'Heap-memType1_4'  2 MB  (99% full)  MemType: 1 (heap: 0; flags: DeviceLocal) Handle: 0x22B1652FD80
  HeapAllocator 'Heap-memType1_4'; Alignment: 256;
  Used: 1.98 MB / 2 MB (99.2% full); MaxFreeBlockSize: 15.8 KB;

  Blocks usage: 1 used + 1 free = 2;  (one char size: 4 KB):
  ****************************************************************
  ****************************************************************
  ****************************************************************
  ****************************************************************
  ****************************************************************
  ****************************************************************
  ****************************************************************
  ************************************************************+...
 
  All blocks (offset, size, name):
  0x00000000  1.98 MB 'VertexBuffer-TriangleMesh'
  0x001FC100  15.8 KB FREE


[6] 'Heap-memType1_6'  4 MB  (86% full)  MemType: 1 (heap: 0; flags: DeviceLocal) Handle: 0x22B165DA160
  HeapAllocator 'Heap-memType1_6'; Alignment: 256;
  Used: 3.47 MB / 4 MB (86.8% full); MaxFreeBlockSize: 539.5 KB;

  Blocks usage: 2 used + 1 free = 3;  (one char size: 8 KB):
  ****************************************************************
  ****************************************************************
  ****************************************************************
  ****************************************************************
  ****************************************************************
  ****************************************************************
  ************************************************************+...
  ................................................................
 
  All blocks (offset, size, name):
  0x00000000  1.49 MB 'IndexBuffer-TriangleMesh'
  0x0017D100  1.98 MB 'VertexBuffer-TriangleMesh'
  0x00379200 539.5 KB FREE


[8] 'Heap-memType1_8'  8 MB  (28% full)  MemType: 1 (heap: 0; flags: DeviceLocal) Handle: 0x22B165DA330
  HeapAllocator 'Heap-memType1_8'; Alignment: 256;
  Used: 2.31 MB / 8 MB (28.9% full); MaxFreeBlockSize: 5.69 MB;

  Blocks usage: 2 used + 1 free = 3;  (one char size: 16 KB):
  ****************************************************************
  ****************************************************************
  ********************+...........................................
  ................................................................
  ................................................................
  ................................................................
  ................................................................
  ................................................................
 
  All blocks (offset, size, name):
  0x00000000  1.49 MB 'IndexBuffer-TriangleMesh'
  0x0017D100 844.3 KB 'VertexBuffer-Minor contour lines-mesh'
  0x00250200  5.69 MB FREE




Scene:
MatricesMemoryBlocks[0] is DynamicMemoryBlock<Matrix4x4>; Handles: 0x22B0CA9ED60:
Capacity: 64; MemorySize: 64 * 64 = 4,096 bytes; Used blocks: 2; LastUsedIndex: 1
    0 **..............................................................



StandardEffect:
MaterialMemoryBlocks[0] is DynamicMemoryBlock<StandardMaterialUniformBuffer>; Handles: 0x22B163AF820:
Capacity: 64; MemorySize: 64 * 64 = 4,096 bytes; Used blocks: 3; LastUsedIndex: 2; PreventZeroBlockIndex
    0 ***.............................................................



ThickLineEffect:
LineInfoMemoryBlocks[0] is DynamicMemoryBlock<LineInfoUniformBuffer>; Handles: 0x22B15CD54D0:
Capacity: 85; MemorySize: 85 * 48 = 4,080 bytes; Used blocks: 4; LastUsedIndex: 7
    0 **....**........................................................
   64 .....................




DescriptorSetsCache 'CommonFragmentShaderStorageWithTextureDescriptorSet':
- Data GpuBuffer[0].Id: 116; Image: Id: 103 (ImageView: 0x22B163ED150); Sampler: 0x22B16531010)  => DescriptorSets: 0x22B165F6CE0

First, there are some GPU budgets that show how much memory is available. Then, different memory types are displayed.

After that, the total allocated memory is displayed - in my case 196.53 MB.

Then the "Dedicated allocations" show the Vulkan allocations that are used by one buffer. These are also used for the back buffers. In my case, because 4x MSAA and 4x SSAA (super-sampling is used), there are 3 allocated back buffers: 2 super-sampled (4x size) for depth-stencil and rendering back buffer and one final downsampled buffer that is actually shown ('SharpEngineSceneView_SceneView_SSAA-Image'). In this case, a single back-buffer takes 75 MB for the 1326 x 884 image with 4x SSAA and 4x MSAA.

If there were no super-sampling, then there would be only two 18 MB buffers for depth-stencil and back-buffer (saving 2 * 75MB of memory).

Then the "Shared memory blocks" shows memory blocks that can be shared by multiple GpuBuffers (for vertex / index buffers, matrix buffers, and textures).

Below that are individual memory blocks that show which GpuBuffers or GpuImages are stored there.

Note that all that needs to be manually managed by SharpEngine because Vulkan API requires that (resource management in DirectX 11 or OpenGL is significantly simpler)

Inspecting memory dump gives you the best data about where you can save the memory and which objects are worth disposing.


If memory is a concern and if you are intending to show your app in 4K monitors, then disabling supersampling saves a significant amount of memory. But maybe the memory dump will show you that most of the memory is consumed by some GpuBuffers (meshes) or textures.

By default, the SharpEngineSceneView's DisposeBackBuffersWhenHidden property is set to true. This means that when the SharpEngineSceneView is hidden, it will automatically dispose its back-buffers. For example, this happens when SharpEngineSceneView is used in a tab control. But when you use it in windows, then the SharpEngineSceneView may still be visible despite the fact that its parent window is behind some other window.

In this case, one idea to optimize the resources is to render the current 3D scene to a bitmap when the window is deactivated. Then hide the  SharpEngineSceneView to dispose its back-buffers. Then show the rendered bitmap instead of the SharpEngineSceneView.

Based on the memory dump for your use case, you may also decide to dispose some big textures and meshes. This will release the memory. Then, when the SharpEngineSceneView is active again, you load the textures from the disk and generate (load) the big meshes again.

You may also need to consider the current GPU memory budget. You can query it by calling any of the following methods:
Code:
gpuDevice.GetMemoryBudget();
gpuDevice.GetDeviceLocalMemoryBudget();

I hope that this will help you improve the resource usage of your app.


RE: Performance with multiple views - matt@reefmaster.com.au - 01-15-2026

Thank you as ever Andrej for comprehesive and useful reply.
I have the auto dispose backbuffer working now which is making a big difference, I will continue to look into the other areas you mentioned.
thanks!
Matt