SharpDX Interop for ILGPU

A SharpDX interop library for ILGPU

SharpDX Interop for ILGPU

A SharpDX interop library for ILGPU


Many 3D applications often rely on compute shaders to implement computer graphics or advanced simulation algorithms. Using ILGPU to implement advanced algorithms in 3D applications can simplify these tasks. This interop library provides access to native Direct3D (11.X) resources (compute buffers or textures) from ILGPU. The ILGPU SharpDX Interop library is released under the University of Illinois/NCSA Open Source License.

Please note that SharpDX is an independent project that is not related to the ILGPU project.

Lib architecture

ILGPU Kernels in your 3D application

Write ILGPU kernels that can access your Direct3D 11.X resources from SharpDX.

Buffer and Texture support

Buffers and Textures that are created via the provided interop accelerator can be accessed from your ILGPU kernels.

Full CPU Accelerator Support

As always, all kernels can be run and debugged with the CPU accelerator. Note that your Direct3D 11.X device does not need to be in software emulation mode.

Upcoming Changes in v0.3: .Net Standard 2.0 support

The interop library will feature .Net Standard 2.0 support in the next release. This simplifies integration into many different applications.

A Short Tutorial

Typically, you create a Direct3D device somewhere in your application. It usually looks like that:

using SharpDX;
using SharpDX.Direct3D11;
...

Device.CreateWithSwapChain(
    DriverType.Hardware,
    DeviceCreationFlags.Debug,
    swapChainDescription,
    out Device device,
    out SwapChain swapChain);
                    

After you have created the device, you can setup an Associated Accelerator in the ILGPU world. The instantiated accelerator "knows" the associated Direct3D device and activates general interop support. Currently, you can chose between AcceleratorType.Cuda or AcceleratorType.CPU.

using ILGPU;
using ILGPU.SharpDX;
...

ilgpuContext = new Context();
accelerator = device.CreateAssociatedAccelerator(ilgpuContext, AcceleratorType.Cuda);

To create and manage Direct3D objects, we need to create an Interop Accelerator Note that the Interop Accelerator will be removed in future releases to simplify programming. Instead, all operations will be performed on the Associated Accelerator in future versions.

interopAccelerator = accelerator.CreateDirectXInteropAccelerator(device);

// Create a buffer that stores vertex information with 6 elements.
vertexBuffer = interopAccelerator.CreateBuffer<MyVertexStruct>(6, DirectXBufferFlags.None);

// Create a texture with width = 640, height = 480 and format = Vector4
colorTexture = interopAccelerator.CreateTexture2D(640, 480, Format.R32G32B32A32_Float);
                    

Note that you have to dispose all buffers and textures before you can dispose the Interop Accelerator. You can then safely dispose the Associated Accelerator and the global ILGPU context.

Mapping & Unmapping of Resources

Before buffers and textures can be used from ILGPU kernels, they have to be mapped. After you have completed all operations on the ILGPU side, you must release the mapped resources. This ensures that all ILGPU operations are finished and that the graphics API can access the mapped buffers again.

using (var mapping = interopAccelerator.MapBuffer(
    dxIntermediateContext,
    vertexBuffer,
    colorTexture))
{
    // ILGPU operations...
}

// Graphics operations...

Although the returned DirectXBufferMapping object is a structure, it implements the IDisposable interface. This allows the programmer to use using statements to ensure that the required Unmap operation is called automatically. If you cannot (or do not want to) use a using statement, you have to call Unmap manually.

var mapping = interopAccelerator.MapBuffers(
    dxIntermediateContext,
    vertexBuffer,
    colorTexture);

// ILGPU operations...

mapping.Unmap();

// Graphics operations...

Note that the mapping operation is a time-consuming step and should only be called once per frame. It is time-consuming since it ensures that all calls to the graphics API are completed before a kernel launch is triggered. Try to batch all buffers you need and map them in a single step (for example, at the beginning of your simulation loop). Pay additional attention to the last parameter of the Map function. It is a parameter array that causes an additional array creation upon invocation. Try to store your arguments to a pre-allocated array in order to avoid additional heap allocations in your render loop.

Fork me on GitHub