Supporting GPU Buffers for FMX

 

The FMXUtils repository now implements support for GPU Buffers for Delphi FMX (with DX11 or WebGPU).

Adding GPU buffers required some trickery and hackery, but the speedups can be massive: 2x to 3x speedups on low-end integrated GPUs, with reports of 20x (twenty times) speedups on discrete GPUs!

I tried to make the support as easy and backward compatible as possible, while dancing around missing private fields and non-virtual methods.

The GPU buffers are “exposed” through new drivers & interposer classes. They are supported by a new DX11 drop-in replacement FMX driver, as well as a brand new experimental WebGPU driver for FMX, based on Delphi-WebGPU.

They also have graceful fallback for legacy and unsupported FMX drivers, meaning that code using the new GPU buffers will run on FMX drivers that do not support GPU buffers (though without any speedup).

Enabling the GPU Buffers

To enable the new DX11 driver, you need to add “FMXU.Context.DX11” to the project “uses” clause and then call RegisterDX11ContextU in the project code (the .dpr). This will replace the default DX11 FMX driver.

The GPU buffers are exposed in the FMXU.Buffers unit. There are two buffers:

  • TGPUVertexBuffer for vertices, wraps a TVertexBuffer
  • TGPUIndexBuffer for indices, wraps a TIndexBuffer

The wrapped buffer can be accessed with Lock/Unlock methods.

There are currently two modes for the GPU buffers:

  • dynamic uses mapped memory, it’s intended for data that will be used only once, or modified piecemeal
  • static: transfers the data to GPU memory, it’s the best option for static meshes and data that will be used multiple times

Since TVertexBuffer & TIndexBuffer were not made to be sub-classed, a limitation is that you should NOT resize them. If you need to resize a buffer, you must do so via the GPU buffer class that wraps them.

Rendering the GPU Buffers

Add the FMXU.Context class to the uses clause of the unit where you’re rendering. It will expose via a class helper the following methods

  • ApplyMaterial: call this to explicitly apply a material and opacity
  • DrawGPUPrimitive: draw primitives with the applied material
  • ResetMaterial : call this when you’re done drawing GPU Primitives

Alternatively there are overloads for DrawPrimitives, DrawTriangles, DrawLines & DrawPoints, with the classic FMX signature, except they will take GPUBuffers instead of classic buffers.

An extended enumeration TPrimitiveKindU is also introduced, which adds support for triangle strips.

Aside from support for GPU buffers, the new DX11 driver has a somewhat different internal approach to shaders. It should be compatible – and eventually more efficient – but the abstraction may be leaking…

Note that all the vertex elements are supported, including all ColorF1 etc. (unlike classic DX11 driver).