Skip to content

Render Backends

MeshRenderData2D stores vertices as tightly packed floats:

x, y, u, v

Each vertex uses MeshRenderData2D.FloatsPerVertex, which is currently 4.

var data = new MeshRenderData2D();
MeshRenderExtractor.Extract(mesh, deformer: null, data);
ReadOnlySpan<float> vertices = data.Vertices;
ReadOnlySpan<int> indices = data.Indices;

The index buffer contains triangle indices from Mesh2D.Faces, three indices per face.

MeshRenderData2D tracks independent flags:

Flag Meaning
VerticesDirty Vertex buffer contents changed.
IndicesDirty Index buffer contents changed.

After uploading buffers to your renderer, clear the flags:

if (data.VerticesDirty)
{
// upload vertex buffer
}
if (data.IndicesDirty)
{
// upload index buffer
}
data.ClearDirtyFlags();

Some APIs or debug paths are simpler with expanded triangle vertices.

float[] triangles = data.ExpandIndexedTriangles();

ExpandIndexedTriangles() returns a new float array where every triangle index has been resolved into x, y, u, v vertex data.

Implement IMeshRenderBackend2D when integrating with an engine or graphics API.

public interface IMeshRenderBackend2D : IDisposable
{
RenderResourceHandle CreateResource(MeshRenderData2D data);
void UpdateResource(RenderResourceHandle handle, MeshRenderData2D data);
void BindTexture(RenderResourceHandle handle, Texture2D? texture);
void Draw(RenderResourceHandle handle, in RenderTransform2D transform);
void DestroyResource(RenderResourceHandle handle);
}

The core library does not own GPU resources. Your backend maps RenderResourceHandle to API-specific objects such as buffers, vertex arrays, descriptor sets, or textures.

You do not have to use the reference backend classes. If your application already uses Veldrid or another renderer, keep Vergeo2D.Mesh as the mesh/deformation layer and map MeshRenderData2D.Vertices plus MeshRenderData2D.Indices into that renderer’s own vertex and index buffers.

RenderResourceHandle mirrors the generation-checked handle pattern used by MeshHandle.

if (handle.IsValid)
{
backend.Draw(handle, RenderTransform2D.Identity);
}

Use the generation value to detect stale GPU resource references after a backend resource is destroyed and a slot is reused.

RenderTransform2D stores:

  • Position
  • RotationRadians
  • Scale
var transform = new RenderTransform2D(
position: new Vector2(100, 80),
rotationRadians: 0f,
scale: Vector2.One);
var matrix = transform.ToMatrix();

ToMatrix() applies scale, then rotation, then translation.

Reference backend implementations are opt-in and guarded by compilation symbols.

Backend NuGet package Define constant
OpenGLMeshRenderBackend2D Silk.NET.OpenGL VERGEO_OPENGL
Direct3D11MeshRenderBackend2D Vortice.Direct3D11 VERGEO_DIRECTX
VulkanMeshRenderBackend2D Silk.NET.Vulkan VERGEO_VULKAN
<PropertyGroup>
<DefineConstants>$(DefineConstants);VERGEO_OPENGL</DefineConstants>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Silk.NET.OpenGL" Version="x.x.x" />
</ItemGroup>

Without the matching define constant, that backend class is not compiled into the package.

Each reference backend expects the native API objects owned by your application:

var backend = new OpenGLMeshRenderBackend2D(gl);
var backend = new Direct3D11MeshRenderBackend2D(device, context);
var backend = new VulkanMeshRenderBackend2D(vk, physicalDevice, device, commandBuffer);

The backend does not create a window, swapchain, command loop, shader program, or pipeline state for you.

Reference texture binding is intentionally small:

  • OpenGL tracks a texture ID on the resource, but BindTexture(handle, null) only clears it.
  • Direct3D11 and Vulkan currently leave BindTexture() empty.

In a real renderer, use Texture2D.Path, Texture2D.Width, and Texture2D.Height to load or look up your engine texture, then bind your own shader resource, descriptor set, or API texture object before drawing.

The extracted vertex layout is:

Attribute Components Offset
Position float2 0
UV float2 2 * sizeof(float)

Reference OpenGL backend enables attribute 0 for position and attribute 1 for UV.