No, that distinction is dead-on.
You’re not building:
gpu = Vulkan replacement
You’re building:
gpu = drawing orchestrator over Vulkan/DX/GL/etc.
So gpu’s job is not “be the OS GPU API.” It’s more like:
App/UI/Compositor/Engine
↓
gpu intent API
↓
Backend adapter
↓
Vulkan / D3D / GL / Metal / software
That’s much closer to Raylib, SDL Renderer, Skia, bgfx, WGPU, etc. — except your version is OS-foundational and IR-first instead of “call this function and hope the backend maps cleanly.”
The industry has pieces of this, but they’re all compromised:
Raylib: friendly, not OS foundation
SDL: window/input first, renderer is modest
Skia: excellent 2D, not general engine/render orchestration
bgfx: strong backend abstraction, still fairly render-API-shaped
WGPU: modern GPU abstraction, lower-level than your intent layer
Qt/GTK: UI toolkit first, not universal render substrate
Your niche is:
stable drawing intent layer
for UI + compositor + game/editor rendering
with backend lowering as implementation detail
That’s a good boundary.
gpu should know:
resources
passes
surfaces
draw intents
clips
transforms
materials
text/glyph streams maybe
presentation
backend capabilities
It should not know:
buttons
windows
game entities
scene hierarchy
business logic
desktop policies
So yeah: orchestrator is the right word. gpu is the conductor, not the instruments.
Correct. No ParticleSurface in gpu.
Particles should be a producer above gpu:
Particle System
owns emitters, lifetimes, simulation, sorting, spawn rules
↓
emits draw intent
↓
gpu
↓
GPU backend
gpu should only see the reduced graphical result:
Draw_Billboards
Draw_Instanced_Quads
Draw_Mesh_Instances
Draw_Point_Sprites
Draw_Compute_Dispatch maybe
Draw_Texture_Atlas_Quads
Not:
Emitter
Burst
Gravity
Lifetime
Trail
Smoke
Firework
Blood splatter
Magic sparkle
Those are engine concepts.
The only slight caveat: gpu may need generic high-throughput primitives that particles can use well:
instance buffers
dynamic vertex streams
storage buffers
compute dispatch
indirect draws
sort keys
blend modes
depth modes
But those are still rendering concepts, not particle concepts.
So the boundary is:
Particle engine:
“There are 40,000 smoke sprites here with these transforms/colors/UVs.”
gpu:
“Cool, I’ll batch/lower/submit that efficiently.”
That keeps gpu clean. It becomes the universal rendering funnel, not a junk drawer for every cool visual system you build.
The funnel should look like ordered drawing intent + optional optimization hints, not “gpu asks the host what shapes it wants.”
Host pushes intent into gpu:
Host systems
UI / particles / scene / compositor / debug
↓
submit draw packets
↓
gpu builds frame IR
↓
gpu sorts/batches/culls where safe
↓
backend lowers to GPU/software
So the host says:
draw these quads
draw this mesh
draw this text run
draw these instances
draw this surface
gpu should not say:
give me all buttons
give me all particles
give me all windows
That would make gpu too aware of the world above it.
The draw packet shape should be something like:
Draw :: struct {
kind: Draw_Kind,
target: Surface_Handle,
layer: i32,
sort_key: u64,
bounds: Rect3,
clip: Clip_Handle,
transform: Mat4,
material: Material_Handle,
geometry: Geometry_View,
instances: Instance_View,
flags: Draw_Flags,
}
The host provides:
what to draw
where it draws
what material/blend/depth behavior it needs
rough bounds
ordering constraints
gpu handles:
backend capability selection
pipeline selection
batching
resource transitions
uploads
descriptor/binding layout
command buffer generation
simple culling
clip/scissor lowering
software fallback routing
On overlap/occlusion: split it.
The host owns semantic occlusion.
gpu owns render-mechanical occlusion.
For example, host/UI/compositor owns:
window A is above window B
modal dialog blocks clicks behind it
collapsed panel hides children
invisible widget should not emit draw packets
offscreen UI subtree should not emit
game object outside camera may be skipped
portal/room visibility
LOD selection
particle emitter disabled
gpu owns:
scissor clipping
clip rect stacks
depth test setup
stencil/clip masks if requested
front-to-back sorting where legal
batch sorting where legal
discarding draws outside render target
occlusion query support maybe
tile/software dirty region clipping
The golden rule:
If deciding visibility requires knowing meaning, host handles it.
If deciding visibility only requires geometry/render state, gpu may handle it.
So for UI:
UI runtime:
computes layout
knows z-order
knows scroll regions
emits only visible-ish boxes
gpu:
applies scissors/clips
batches quads/text
drops things outside target
For desktop compositor:
Compositor:
knows window stacking
knows damaged regions
knows minimized/hidden windows
knows input focus
gpu:
composites surfaces
clips to damage/scissor
handles opacity/transforms
For 3D:
Scene renderer:
knows camera, lights, spatial tree, portals, LOD
gpu:
receives visible draw list
sorts by pass/material/depth rules
lowers to backend
I’d make the funnel frame-based:
frame := gpu.begin_frame(device)
target := gpu.begin_target(frame, swapchain_surface)
gpu.push_layer(frame, .UI, sort_base = 10_000)
gpu.draw_rect(frame, ...)
gpu.draw_text(frame, ...)
gpu.pop_layer(frame)
gpu.push_layer(frame, .World, sort_base = 0)
gpu.draw_mesh(frame, ...)
gpu.draw_instances(frame, ...)
gpu.pop_layer(frame)
gpu.end_target(frame, target)
gpu.submit(frame)
Internally that becomes:
FrameIR
├── Targets
├── Passes
├── Draw packets
├── Uploads
├── Resources used
├── Clips
├── Sort constraints
└── Presentation
Ordering should be explicit but flexible:
strict order:
preserve exactly; needed for UI painter's algorithm
layered order:
preserve between layers, sort inside layer if allowed
free order:
gpu may reorder for batching/performance
depth order:
gpu may sort based on depth/material rules
So a draw packet might include:
Order_Mode :: enum {
Strict,
Layered,
Sortable,
Depth,
}
UI mostly uses:
Strict / Layered
3D uses:
Depth / Sortable
Particles often use:
Depth sorted for alpha
or Sortable for additive effects
The cleanest mental model:
Host builds the truth.
gpu builds the frame.
Backend builds the commands.
GPU builds the pixels.
Don’t let gpu ask for shapes. Let every system pour already-decided drawing intent into the funnel. gpu’s superpower is making all those unrelated producers become one coherent, optimized frame.