No, that distinction is dead-on. You’re not building: ```text gpu = Vulkan replacement ``` You’re building: ```text gpu = drawing orchestrator over Vulkan/DX/GL/etc. ``` So gpu’s job is not “be the OS GPU API.” It’s more like: ```text 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: ```text 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: ```text stable drawing intent layer for UI + compositor + game/editor rendering with backend lowering as implementation detail ``` That’s a good boundary. gpu should know: ```text resources passes surfaces draw intents clips transforms materials text/glyph streams maybe presentation backend capabilities ``` It should not know: ```text 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: ```text Particle System owns emitters, lifetimes, simulation, sorting, spawn rules ↓ emits draw intent ↓ gpu ↓ GPU backend ``` gpu should only see the reduced graphical result: ```text Draw_Billboards Draw_Instanced_Quads Draw_Mesh_Instances Draw_Point_Sprites Draw_Compute_Dispatch maybe Draw_Texture_Atlas_Quads ``` Not: ```text 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: ```text 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: ```text 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: ```text 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: ```text draw these quads draw this mesh draw this text run draw these instances draw this surface ``` gpu should **not** say: ```text 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: ```odin 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: ```text what to draw where it draws what material/blend/depth behavior it needs rough bounds ordering constraints ``` gpu handles: ```text 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: ```text 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: ```text 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: ```text If deciding visibility requires knowing meaning, host handles it. If deciding visibility only requires geometry/render state, gpu may handle it. ``` So for UI: ```text 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: ```text 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: ```text 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: ```odin 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: ```text FrameIR ├── Targets ├── Passes ├── Draw packets ├── Uploads ├── Resources used ├── Clips ├── Sort constraints └── Presentation ``` Ordering should be explicit but flexible: ```text 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: ```odin Order_Mode :: enum { Strict, Layered, Sortable, Depth, } ``` UI mostly uses: ```text Strict / Layered ``` 3D uses: ```text Depth / Sortable ``` Particles often use: ```text Depth sorted for alpha or Sortable for additive effects ``` The cleanest mental model: ```text 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.