| name | resource-management-audit |
| description | Audit resource management including IDisposable pattern implementation, proper cleanup of OpenGL resources (buffers, textures, shaders, framebuffers), memory leak detection, resource lifetime management, and GPU resource tracking. Use when investigating memory leaks, GPU resource exhaustion, or implementing new resource types. |
Resource Management Audit
Overview
This skill audits resource management to ensure proper cleanup of OpenGL resources, correct IDisposable implementation, and prevention of memory leaks. Focus on engine-specific concerns like OpenGL context safety, factory ownership patterns, and GPU resource tracking.
When to Use
Invoke this skill when:
- Investigating memory leaks or growing memory usage
- GPU resources (textures, buffers) are not being cleaned up
- Adding new resource types (textures, shaders, meshes, framebuffers)
- Refactoring resource lifetime management
- Application crashes on shutdown (disposal issues)
- Out-of-memory errors or GPU resource exhaustion
- Reviewing code for proper disposal patterns
Critical Engine-Specific Rules
1. Never Call OpenGL in Finalizers
Why: OpenGL context is NOT available on finalizer thread. Calling GL functions in finalizers causes crashes.
~Texture()
{
if (_rendererID != 0)
{
Logger.Error($"Texture {_path} not disposed! GPU leak.");
}
}
public void Dispose()
{
if (_rendererID != 0)
{
GL.DeleteTexture(_rendererID);
_rendererID = 0;
}
GC.SuppressFinalize(this);
}
2. Factory-Managed Resources
Rule: Factory owns cached resources. Consumers get references but don't dispose them.
var texture = _textureFactory.Load("sprite.png");
texture.Dispose();
Check ownership documentation in resource classes (see XML comments).
3. Shared vs. Owned Resources
Owned: Component creates resource exclusively for itself → Component disposes it
Shared: Resource comes from factory/cache → Factory disposes it
public class MeshRenderer : IDisposable
{
private Mesh _mesh;
private uint _instanceVBO;
public void Dispose()
{
if (_instanceVBO != 0)
{
GL.DeleteBuffer(_instanceVBO);
_instanceVBO = 0;
}
}
}
4. Disposal Guards
Always implement these safeguards:
public class Texture : IDisposable
{
private uint _rendererID;
private bool _disposed = false;
public void Dispose()
{
if (_disposed)
return;
if (_rendererID != 0)
{
GL.DeleteTexture(_rendererID);
_rendererID = 0;
}
_disposed = true;
GC.SuppressFinalize(this);
}
}
Audit Checklist
When reviewing resource-owning classes, verify:
IDisposable Implementation
Ownership Clarity
Resource Creation Sites
Testing
Common Anti-Patterns
See references/anti-patterns.cs for detailed examples. Quick reference:
- Missing Disposal: Resource created but never disposed → Add disposal path
- Double Disposal: No
_disposed guard → Crashes on second Dispose()
- Disposing Shared Resources: Multiple owners unclear → Document ownership
- OpenGL in Finalizers: Crashes due to missing context → Log warning instead
- Forgetting GC.SuppressFinalize: Unnecessary finalizer runs → Always call it
- Wrong Disposal Order: FBO before attachments → Delete in reverse order of creation
- Null Checks Missing:
_mesh.Dispose() crashes if null → Use _mesh?.Dispose()
- No Disposal Path: Resources in loops → Cache or use
using statement
Disposal Patterns Reference
Standard IDisposable patterns with engine-specific notes: references/disposal-patterns.cs
Pattern Selection:
- Basic Pattern: Sealed classes, OpenGL-only resources
- Full Pattern: Inheritable classes, mixed managed/unmanaged resources
- Factory Pattern: Cached/shared resources with centralized lifetime management
Output Format
When reporting audit findings:
**Issue**: [Resource management problem]
**Location**: [File:line]
**Resource Type**: [OpenGL buffer/texture/shader/etc.]
**Problem**: [Specific issue - leak, double disposal, missing cleanup]
**Recommendation**:
[Code example showing fix]
**Priority**: [Critical/High/Medium/Low]
Example:
**Issue**: Mesh resources not disposed when entity destroyed
**Location**: Engine/Scene/Entity.cs:89
**Resource Type**: OpenGL VBO, EBO, VAO
**Problem**: Entity.Destroy() doesn't dispose MeshComponent.Mesh,
causing GPU memory leak (20MB per load/unload cycle)
**Recommendation**:
public void Destroy()
{
if (HasComponent<MeshComponent>())
{
var meshComp = GetComponent<MeshComponent>();
// Only dispose if component owns the mesh (not factory-managed)
if (meshComp.OwnsMesh)
{
meshComp.Mesh?.Dispose();
}
meshComp.Mesh = null;
}
_scene.RemoveEntity(this);
}
**Priority**: High (GPU memory leak)
Key Files Reference
references/disposal-patterns.cs: Standard IDisposable patterns (basic, full, factory)
references/anti-patterns.cs: 8 common mistakes with ❌/✅ examples
Summary
Core Principles:
- Never call OpenGL in finalizers (no GL context available)
- Document ownership explicitly (owned vs. shared resources)
- Factory-managed resources: factory disposes, consumers reference
- Always guard double-disposal with
_disposed flag
- Always call
GC.SuppressFinalize(this) in Dispose()
- Use ResourceTracker for leak detection in DEBUG builds
- Test cleanup with load/unload cycles and shutdown checks