NeoVeldrid is a low-level, high-performance graphics library for .NET. Build 2D and 3D games, simulations, and tools with a single portable API across Vulkan, Direct3D 11, OpenGL, and OpenGL ES.
A maintained fork of Veldrid, powered by Silk.NET. Open source under the MIT license.
Write your rendering code once. It runs on Vulkan, Direct3D 11, OpenGL, and OpenGL ES without any changes.
Windows, Linux, and macOS from a single codebase. All native dependencies ship as NuGet packages.
A thin, low-cost abstraction close to the metal. Allocation-free rendering loop. Multi-threaded command recording.
Programmable shaders, compute, structured buffers, array textures, multisampling. Write GLSL once, cross-compile via SPIR-V.
Install a NuGet package and start coding. No native SDKs, no manual library copying, no build scripts.
Headless rendering, GPU compute, multi-window, ImGui integration. Games, tools, simulations, or offline processing.
Create a window and a GPU device in two lines. NeoVeldrid picks the best available backend for the platform automatically.
var window = NeoVeldridStartup.CreateWindow(new WindowCreateInfo
{
WindowWidth = 960,
WindowHeight = 540,
WindowTitle = "My App"
});
var device = NeoVeldridStartup.CreateGraphicsDevice(window);
var factory = device.ResourceFactory;
var commandList = factory.CreateCommandList();Create vertex buffers, index buffers, and uniforms with a clean, typed API. Upload data directly from arrays and structs.
float[] quadVertices = {
// X Y U V
-0.5f, 0.5f, 0.0f, 0.0f, // top-left
0.5f, 0.5f, 1.0f, 0.0f, // top-right
0.5f, -0.5f, 1.0f, 1.0f, // bottom-right
-0.5f, -0.5f, 0.0f, 1.0f, // bottom-left
};
ushort[] quadIndices = { 0, 1, 2, 0, 2, 3 };
var vertexBuffer = factory.CreateBuffer(
new BufferDescription(quadVertices.SizeInBytes(), BufferUsage.VertexBuffer)
);
var indexBuffer = factory.CreateBuffer(
new BufferDescription(quadIndices.SizeInBytes(), BufferUsage.IndexBuffer)
);
device.UpdateBuffer(vertexBuffer, 0, quadVertices);
device.UpdateBuffer(indexBuffer, 0, quadIndices);Write your shaders in GLSL once. NeoVeldrid.SPIRV handles the rest - compiling to SPIR-V and cross-compiling to whatever the current backend needs. No separate shader files per platform.
var vertexCode = File.ReadAllText("my-shader.vert.glsl"); var fragmentCode = File.ReadAllText("my-shader.frag.glsl"); var shaders = factory.CreateFromSpirv( new ShaderDescription(ShaderStages.Vertex, Encoding.UTF8.GetBytes(vertexCode), "main"), new ShaderDescription(ShaderStages.Fragment, Encoding.UTF8.GetBytes(fragmentCode), "main") );
Blend state, depth testing, rasterization, shaders - everything is explicit and declarative. No hidden state machines, no surprises.
var pipeline = factory.CreateGraphicsPipeline(new GraphicsPipelineDescription
{
BlendState = BlendStateDescription.SingleOverrideBlend,
DepthStencilState = DepthStencilStateDescription.DepthOnlyLessEqual,
RasterizerState = RasterizerStateDescription.Default,
PrimitiveTopology = PrimitiveTopology.TriangleList,
ShaderSet = new ShaderSetDescription(new[] { vertexLayout }, shaders),
Outputs = device.SwapchainFramebuffer.OutputDescription
});The core rendering loop allocates zero garbage-collected memory. Record commands, submit, swap. That's it.
while (window.Exists)
{
window.PumpEvents();
commandList.Begin();
commandList.SetFramebuffer(device.SwapchainFramebuffer);
commandList.ClearColorTarget(0, RgbaFloat.CornflowerBlue);
commandList.SetPipeline(pipeline);
commandList.SetVertexBuffer(0, vertexBuffer);
commandList.SetIndexBuffer(indexBuffer, IndexFormat.UInt16);
commandList.DrawIndexed(6);
commandList.End();
device.SubmitCommands(commandList);
device.SwapBuffers();
}Run compute shaders alongside your graphics work. Same command list, same resource model, same submission pipeline.
var computePipeline = factory.CreateComputePipeline(
new ComputePipelineDescription(computeShader, resourceLayout, 16, 16, 1)
);
commandList.Begin();
commandList.SetPipeline(computePipeline);
commandList.SetComputeResourceSet(0, resourceSet);
commandList.Dispatch(groupCountX, groupCountY, 1);
commandList.End();
device.SubmitCommands(commandList);Create 2D textures, upload pixel data, sample them in shaders. Supports compressed formats, mipmaps, array textures, and multisampling.
var texture = factory.CreateTexture(TextureDescription.Texture2D(
width, height, mipLevels: 1, arrayLayers: 1,
PixelFormat.R8_G8_B8_A8_UNorm, TextureUsage.Sampled)
);
device.UpdateTexture(texture, pixelData, 0, 0, 0, width, height, 1, 0, 0);
var textureView = factory.CreateTextureView(texture);
var sampler = factory.CreateSampler(SamplerDescription.Linear);NeoVeldrid preserves full API compatibility. Update your NuGet packages, rename the namespace, and you're done. No code changes needed.
Migration GuideFollow the tutorial to create your first NeoVeldrid application in minutes.
Get Started