Principles
- Every allocation has exactly one owner.
- Ownership never transfers implicitly.
- Every
createhas a matchingdestroy. - The application never frees internal pointers.
Ownership Table
| Resource | Owner | Created by | Freed by |
|---|---|---|---|
MopViewport | Application | mop_viewport_create | mop_viewport_destroy |
MopMesh (handle) | Viewport | mop_viewport_add_mesh | mop_viewport_remove_mesh or viewport destroy |
| RHI Device | Viewport | rhi->device_create | rhi->device_destroy |
| RHI Framebuffer | Viewport | rhi->framebuffer_create | rhi->framebuffer_destroy |
| RHI Buffer | Viewport | rhi->buffer_create | rhi->buffer_destroy |
| Vertex/index arrays | Application | Application code | Application code |
| Color buffer pointer | Backend | Backend internal | Backend internal |
Copy Semantics
When the application calls mop_viewport_add_mesh, the engine copies vertex and index data into RHI buffers. The application may free its arrays immediately.
Application Engine
│ │
│ mop_viewport_add_mesh() │
│ ─────────────────────────> │
│ (vertices[], indices[]) │
│ │── rhi->buffer_create (copies data)
│ returns MopMesh* │
│ <───────────────────────── │
│ │
│ free(vertices) │ (safe: engine has its own copy)
Readback Pointers
mop_viewport_read_color returns a borrowed pointer. The application must not free it. Invalidated by:
mop_viewport_render(next frame overwrites)mop_viewport_resize(framebuffer reallocated)mop_viewport_destroy(everything freed)
Leak Prevention
mop_viewport_destroy walks the mesh array and destroys every active mesh's buffers before destroying the framebuffer and device. No leaks even if the application skips mop_viewport_remove_mesh.
Pointer Stability
MopMesh * and MopInstancedMesh * returned by mop_viewport_add_mesh / mop_viewport_add_instanced_mesh are stable for the entire lifetime of the mesh. The internal scene pool heap-allocates each mesh struct individually and stores pointers in a growable array; growing the array never moves the structs themselves. Freed slots go on an O(1) free list and are reused on the next add.
Consequences for application code:
- Cache
MopMesh *across frames and across add/remove of other meshes. No need to look up byobject_idevery frame. - The pointer is only invalidated by the matching
mop_viewport_remove_meshor bymop_viewport_destroy. - Every mesh stores its own
slot_indexand a back-pointer to its viewport, somop_mesh_set_position/set_rotation/ etc. can take the scene lock internally without the caller passing the viewport handle.