arkavo-org / arkavo-org/VRMMetalKit
Optimize outline rendering with Indirect Command Buffers
- Dominant language
- Swift
- Stars
- 6
- Forks
- 2
- Avg merge
- 18h 51m
- Merged PRs (30d)
- 26
Description
## Summary
MToon outlines currently use a two-pass approach (render geometry, then render inverted/extruded geometry for outlines). This can be optimized using Metal's Indirect Command Buffers (ICB) to reduce CPU overhead and draw call latency.
## Current Implementation
In `VRMRenderer`, outlines are rendered as a separate pass:
1. First pass: Render front faces with MToon shading
2. Second pass: Render back faces with vertex extrusion for outlines
Each mesh with outlines requires separate draw call encoding.
## Optimization Opportunity
### Indirect Command Buffers
ICBs allow the GPU to encode draw commands, eliminating per-draw CPU overhead:
```swift
// Create ICB once
let icbDescriptor = MTLIndirectCommandBufferDescriptor()
icbDescriptor.commandTypes = [.draw, .drawIndexed]
icbDescriptor.inheritBuffers = false
icbDescriptor.maxVertexBufferBindCount = 8
icbDescriptor.maxFragmentBufferBindCount = 4
let indirectBuffer = device.makeIndirectCommandBuffer(
descriptor: icbDescriptor,
maxCommandCount: maxMeshCount * 2, // base + outline per mesh
options: []
)
```
### GPU-Driven Encoding
A compute shader can populate the ICB based on visibility and material properties:
```metal
kernel void encodeOutlineCommands(
device MTLIndirectRenderCommand* commands [[buffer(0)]],
constant MeshInfo* meshes [[buffer(1)]],
constant uint& meshCount [[buffer(2)]],
uint id [[thread_position_in_grid]]
) {
if (id >= meshCount) return;
MeshInfo mesh = meshes[id];
if (mesh.outlineWidth <= 0) return; // Skip meshes without outlines
// Encode outline draw command
commands[id].setVertexBuffer(mesh.vertexBuffer, 0);
commands[id].setFragmentBuffer(mesh.materialBuffer, 0);
commands[id].drawIndexedPrimitives(
primitiveType: triangle,
indexCount: mesh.indexCount,
indexType: uint16,
indexBuffer: mesh.indexBuffer,
indexBufferOffset: 0,
instanceCount: 1,
baseVertex: 0,
baseInstance: id
);
}
```
### Execute with Single Call
```swift
renderEncoder.executeCommandsInBuffer(
indirectBuffer,
range: 0..20)
- Complex VRM models
- Lower-end devices where CPU is the bottleneck
Contributor guide
Research direction
Start by locating VRMRenderer and tracing the existing two-pass MToon outline rendering. Review the ICB and compute-kernel requirements against the repository's current Metal targets, then benchmark the existing and proposed paths. Done means the outline pass uses ICBs where supported, has the stated older-device fallback, and includes before-and-after CPU/GPU measurements.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- swift
- Domain
- computer-graphics, performance
- Issue type
- Refactor
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 32/100